byollm 0.1.0 → 0.1.2
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.
- package/README.md +45 -126
- package/dist/bin.js +1 -1
- package/dist/{chunk-FUKJDD7M.js → chunk-K7QYAF7V.js} +262 -58
- package/dist/chunk-K7QYAF7V.js.map +1 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-FUKJDD7M.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/memory.ts","../src/origins.ts","../src/local-server.ts","../src/memory-gate.ts","../src/cli.ts","../src/emphasis.ts","../src/model.ts","../src/config.ts","../src/ssrf.ts","../src/known-models.ts","../src/login.ts","../src/service-line.ts","../src/preflight.ts","../src/update.ts","../src/provenance.ts","../src/update-deps.ts","../src/backends/index.ts","../src/backends/claude-cli.ts","../src/backends/process-backend.ts","../src/backends/quota.ts","../src/backends/codex-cli.ts","../src/backends/openai-http.ts","../src/backends/types.ts","../src/health.ts","../src/heartbeat.ts","../src/setup.ts","../src/services-manage.ts","../src/cli-models.ts","../src/probe-local.ts","../src/supervised.ts","../src/spend.ts","../src/ledger.ts","../src/budgets.ts","../src/client.ts","../src/revoked.ts","../src/test-your-device.ts","../src/diagnose.ts","../src/connect.ts","../src/ingress.ts","../src/model-memory.ts","../src/unused-models.ts","../src/stop-remedy.ts","../src/identity.ts","../src/console-agent.ts","../src/pty-shell.ts","../src/console-agent-main.ts","../src/console-agent-spec.ts","../src/console-listen.ts","../src/pairings.ts","../src/spent-grants.ts","../src/paths.ts","../src/site-outcome.ts","../src/runner.ts","../src/compose.ts","../src/install.ts","../src/service.ts","../src/service-states.ts"],"sourcesContent":["/**\n * `byollm` — what end users run.\n *\n * The CLI (`byollm connect`, `status`, `log`, `services`, `offer`) is the\n * product surface; this module is the same machinery as a library, so the\n * conformance kit can drive a real daemon in-process instead of shelling out.\n *\n * @packageDocumentation\n */\n\nimport { PROTOCOL_VERSION } from \"@byollm/protocol\";\n\nexport {\n readMemory,\n readPressure,\n parsePressureLevel,\n parsePsi,\n parseMemInfo,\n parseVmStat,\n parseSwapUsage,\n type MemoryReading,\n type MemoryPressure,\n type ReadCommand,\n} from \"./memory.js\";\nexport { normalizeOrigin, UnusableOrigin } from \"./origins.js\";\n\nexport { main, runCli, type CliIo, type ExitCode } from \"./cli.js\";\n\nexport {\n ClaudeCliBackend,\n OpenAiHttpBackend,\n childEnv,\n claudeArgv,\n createBackend,\n type Backend,\n type BackendErrorCode,\n type BackendHealth,\n type BackendInit,\n type BackendRequest,\n type BackendResult,\n} from \"./backends/index.js\";\n\nexport { Budgets, type BudgetDecision, type BudgetRefusal } from \"./budgets.js\";\n\nexport { DeviceIdentity } from \"./identity.js\";\n\nexport {\n ClientError,\n ProtocolClient,\n type ClientErrorKind,\n type ClientOptions,\n} from \"./client.js\";\n\nexport { composePrompt } from \"./compose.js\";\n\nexport {\n DEFAULT_CONFIG,\n DaemonConfig,\n loadConfig,\n resolveConfig,\n type ServiceConfig,\n type CommunityBudget,\n type ConfigProblem,\n type IngressRetention,\n type LoadedConfig,\n type Limits,\n type ResolvedRoute,\n} from \"./config.js\";\n\nexport {\n connect,\n currentPlatform,\n type ConnectOptions,\n type ConnectResult,\n} from \"./connect.js\";\n\nexport {\n IngressLog,\n hashText,\n stripControlChars,\n type IngressEntry,\n type IngressOptions,\n type OutcomeEntry,\n type PromptEntry,\n} from \"./ingress.js\";\n\nexport { daemonPaths, defaultRoot, type DaemonPaths } from \"./paths.js\";\n\nexport { SpendLedger, estimateCents } from \"./spend.js\";\n\nexport { Pairings, type Pairing } from \"./pairings.js\";\n\nexport {\n Runner,\n type RunnerEvent,\n type RunnerOptions,\n type RunnerStatus,\n} from \"./runner.js\";\n\nexport {\n BASE_URL_REFUSAL_MESSAGES,\n checkBaseUrl,\n type BaseUrlCheck,\n type BaseUrlRefusal,\n} from \"./ssrf.js\";\n\n/**\n * This daemon's version, reported on pairing and on every heartbeat.\n *\n * Kept in step with `package.json` by a test rather than by a build-time\n * define: an app's runner list shows this string, so a stale one is a lie\n * told to every user, and a literal that a test pins cannot drift quietly.\n */\nexport const DAEMON_VERSION = \"0.1.2\";\n\n/**\n * Everything needed to reason about one daemon — byollm_010 §5.\n *\n * `--version` used to print a package version and nothing else, which is the\n * least useful version string a distributed daemon can have. \"It doesn't work\n * on Windows\" with no platform, no Node version and no protocol version is\n * the most expensive sentence an open-source project receives, and every\n * later capability — deprecation warnings, a minimum-supported-version\n * policy, \"your daemon is N releases behind\" — needs these facts to exist.\n *\n * The same tuple goes in the handshake (byollm_009 §4), deliberately: a\n * support conversation and a version policy should be arguing about the same\n * numbers.\n */\nexport interface DaemonVersion {\n readonly daemon: string;\n readonly protocol: string;\n readonly platform: NodeJS.Platform;\n readonly arch: string;\n readonly node: string;\n}\n\nexport function daemonVersion(): DaemonVersion {\n return {\n daemon: DAEMON_VERSION,\n protocol: PROTOCOL_VERSION,\n platform: process.platform,\n arch: process.arch,\n node: process.versions.node,\n };\n}\n\n/** One line, for `--version` and for pasting into an issue. */\nexport function formatVersion(v: DaemonVersion = daemonVersion()): string {\n return (\n `byollm ${v.daemon} (protocol ${v.protocol})\\n` +\n `${v.platform}-${v.arch}, node ${v.node}\\n`\n );\n}\n","import { execFile } from \"node:child_process\";\nimport { readFile } from \"node:fs/promises\";\nimport { totalmem, freemem, platform as hostPlatform } from \"node:os\";\nimport { promisify } from \"node:util\";\n\n/**\n * How much memory this machine can actually give a job — byollm_022, B080.\n *\n * Built because nothing in this daemon reads memory at all, and neither does\n * Ollama: its own scheduler logged 17 GB into 5.2 GiB free with zero swap and\n * loaded anyway. On a 36 GB machine that was a wedge, about thirty daemons\n * restarting at once, and a person watching their laptop stop.\n *\n * ## `available`, not `free`, and the difference is the whole reader\n *\n * `os.freemem()` on macOS reports pages that are free *right now* — 0.28 GB\n * on a machine with 6.41 GB it could hand over on demand. A gate built on it\n * is not conservative, it is **broken closed**: it refuses every job forever\n * on a machine that is working perfectly, and that failure looks exactly like\n * the product deciding it can never serve. Available is free plus what the\n * kernel can reclaim without asking anybody.\n *\n * ## Zero dependencies, and the macOS spawn named rather than skipped\n *\n * `systeminformation` computes exactly this and would be one import. It is\n * also large, per-platform, and shells out — into a daemon whose security\n * story is a small fixed audited spawn surface, against a `docs/deps.md` that\n * says dependency minimalism and means it.\n *\n * So the method is taken and the package is not. **The dependency would not\n * avoid the `vm_stat` spawn; it would hide it behind a code path per\n * platform.** Doing it here means one known command, with fixed argv, no\n * shell, and nothing derived from a job, a payload or a config — the same\n * discipline as the process backends, and the same shape as B056's PATH check\n * that looks rather than executes.\n */\n/**\n * The OS's own answer to \"is memory tight right now\" — byollm_022.\n *\n * Separate from how many bytes are available, because the two disagree in the\n * direction that matters. This Mac reads `warn` while holding 6.6 GB\n * available and serving jobs perfectly, so a gate that refused on pressure\n * alone would refuse on an ordinary evening.\n */\nexport type MemoryPressure = \"normal\" | \"warn\" | \"critical\" | \"unknown\";\n\nexport type MemoryReading =\n | {\n readonly kind: \"read\";\n /** Free plus reclaimable, in bytes. */\n readonly availableBytes: number;\n readonly totalBytes: number;\n /** Absent where the platform does not report it — Windows, for now. */\n readonly swapFreeBytes?: number;\n readonly swapTotalBytes?: number;\n /**\n * Whether the swap store grows on demand — and it decides whether\n * \"free\" here is a headroom figure at all.\n *\n * On Linux swap is a partition or a file of fixed size, so `SwapFree`\n * near zero means the machine has nowhere left to page: a real signal.\n * **On macOS the swap file is grown by the kernel as it is needed**, so\n * `vm.swapusage` free near zero means the CURRENT file is full and\n * about to be enlarged — not that the machine is out of room.\n *\n * Measured, not assumed. Todd's Mac read a 15.0 GB swap total one\n * night and 24.0 GB the next morning: the same machine, the file\n * grown, no reinstall. A refusal rule reading that as \"headroom gone\"\n * would fire on a laptop that is fine — `os.freemem()`'s mistake with\n * a different number, which is the failure this whole row exists to\n * avoid.\n *\n * byollm_022 rules it directly: keep `SwapFree` on Linux, \"drop swap\n * as a refusal condition on macOS entirely\". This is how — as a\n * property of the reading, so the gate stays platform-agnostic and\n * cannot grow a `process.platform` branch of its own.\n */\n readonly swapGrows?: boolean;\n }\n | {\n /**\n * We could not measure, which is not the same as \"there is no room\".\n *\n * `stopReasons`' `unavailable` one layer down: we looked, there is\n * nothing readable, and the honest move is to say so rather than let an\n * absence read as a pass — or as a refusal. The gate admits normally\n * here and `byollm status` says the guard is not active on this\n * machine, because a guard nobody knows is off is worse than no guard.\n */\n readonly kind: \"unknown\";\n readonly why: string;\n };\n\n/** Runs a fixed argv and returns its stdout, or undefined. */\nexport type ReadCommand = (\n command: readonly [string, ...string[]],\n) => Promise<string | undefined>;\n\n/**\n * Linux: one file read, no spawn.\n *\n * `MemAvailable` is the kernel's own answer to this exact question, which is\n * why it exists — confirmed on a live box at 3,707,608 kB against a `MemFree`\n * of 3,381,836 kB, with `os.freemem()` agreeing with the latter.\n */\nexport function parseMemInfo(text: string): MemoryReading {\n const kb = (key: string): number | undefined => {\n const found = new RegExp(`^${key}:\\\\s+(\\\\d+) kB$`, \"m\").exec(text);\n return found === null ? undefined : Number(found[1]) * 1024;\n };\n const available = kb(\"MemAvailable\");\n const total = kb(\"MemTotal\");\n if (available === undefined || total === undefined) {\n return { kind: \"unknown\", why: \"/proc/meminfo carried no MemAvailable\" };\n }\n const swapTotal = kb(\"SwapTotal\");\n const swapFree = kb(\"SwapFree\");\n return {\n kind: \"read\",\n availableBytes: available,\n totalBytes: total,\n ...(swapFree === undefined ? {} : { swapFreeBytes: swapFree }),\n ...(swapTotal === undefined ? {} : { swapTotalBytes: swapTotal }),\n };\n}\n\n/**\n * macOS: `vm_stat`, because no Node API exposes inactive and purgeable pages.\n *\n * Available is free + inactive + speculative + purgeable — the pages the\n * kernel will hand over without anybody being asked. The page size is read\n * from `vm_stat`'s own header rather than assumed 4096: this machine reports\n * 16384, and assuming would have understated available memory fourfold, which\n * is the broken-closed failure arriving through a constant instead.\n */\nexport function parseVmStat(text: string, total: number): MemoryReading {\n const page = /page size of (\\d+) bytes/.exec(text);\n if (page === null) {\n return { kind: \"unknown\", why: \"vm_stat did not report its page size\" };\n }\n const pageSize = Number(page[1]);\n const pages = (label: string): number | undefined => {\n const found = new RegExp(`^Pages ${label}:\\\\s+(\\\\d+)\\\\.`, \"m\").exec(text);\n return found === null ? undefined : Number(found[1]);\n };\n const free = pages(\"free\");\n const inactive = pages(\"inactive\");\n if (free === undefined || inactive === undefined) {\n return {\n kind: \"unknown\",\n why: \"vm_stat carried no free or inactive count\",\n };\n }\n const reclaimable =\n free + inactive + (pages(\"speculative\") ?? 0) + (pages(\"purgeable\") ?? 0);\n return {\n kind: \"read\",\n availableBytes: reclaimable * pageSize,\n totalBytes: total,\n };\n}\n\n/** macOS swap, from `sysctl vm.swapusage`. */\nexport function parseSwapUsage(text: string): {\n swapFreeBytes?: number;\n swapTotalBytes?: number;\n} {\n const mb = (key: string): number | undefined => {\n const found = new RegExp(`${key} = ([\\\\d.]+)M`).exec(text);\n return found === null ? undefined : Number(found[1]) * 1024 * 1024;\n };\n const free = mb(\"free\");\n const total = mb(\"total\");\n return {\n ...(free === undefined ? {} : { swapFreeBytes: free }),\n ...(total === undefined ? {} : { swapTotalBytes: total }),\n };\n}\n\n/**\n * What this machine can give, per platform.\n *\n * Windows is `os.freemem()` and that is not the macOS mistake repeated: on\n * Windows that call already reports available physical memory rather than a\n * near-zero free count. Same function, different meaning, and the difference\n * is exactly why this is a per-platform table rather than one call.\n */\nexport async function readMemory(\n run: ReadCommand,\n readFile: (path: string) => Promise<string | undefined>,\n platform: NodeJS.Platform = hostPlatform(),\n): Promise<MemoryReading> {\n if (platform === \"linux\") {\n const text = await readFile(\"/proc/meminfo\");\n return text === undefined\n ? { kind: \"unknown\", why: \"/proc/meminfo could not be read\" }\n : parseMemInfo(text);\n }\n if (platform === \"darwin\") {\n const text = await run([\"vm_stat\"]);\n if (text === undefined) {\n return { kind: \"unknown\", why: \"vm_stat could not be run\" };\n }\n const reading = parseVmStat(text, totalmem());\n if (reading.kind !== \"read\") return reading;\n const swap = await run([\"sysctl\", \"vm.swapusage\"]);\n /* Read and reported, but flagged as growable — it belongs in the log\n where a person is tuning a default, and not in a refusal. */\n return {\n ...reading,\n ...(swap === undefined\n ? {}\n : { ...parseSwapUsage(swap), swapGrows: true }),\n };\n }\n if (platform === \"win32\") {\n return { kind: \"read\", availableBytes: freemem(), totalBytes: totalmem() };\n }\n return { kind: \"unknown\", why: `no memory reader for ${platform}` };\n}\n\n/**\n * macOS pressure, from the kernel's own level.\n *\n * `kern.memorystatus_vm_pressure_level`: 1 normal, 2 warn, 4 critical — the\n * number Activity Monitor's pressure graph draws.\n *\n * **Verified on the Mac, and it corrects the assumption this was designed\n * on.** The spec expected `normal` here; it reads **2, warn**, stably across\n * samples, on a machine with 6.6 GB available that is serving jobs fine. That\n * is not a problem with the signal — it is the reason the threshold is\n * CRITICAL and not warn. Refusing at warn would refuse tonight, on a laptop\n * whose owner would rightly call that broken.\n */\nexport function parsePressureLevel(text: string): MemoryPressure {\n const found = /(\\d+)\\s*$/.exec(text.trim());\n if (found === null) return \"unknown\";\n const levels: Readonly<Record<number, MemoryPressure>> = {\n 1: \"normal\",\n 2: \"warn\",\n 4: \"critical\",\n };\n return levels[Number(found[1])] ?? \"unknown\";\n}\n\n/**\n * Linux pressure, from PSI.\n *\n * `full avg10` is the share of the last ten seconds in which EVERY task was\n * stalled on memory. Anything sustained there is a machine already thrashing,\n * which is the same \"already in trouble\" the macOS critical level means.\n */\nexport function parsePsi(text: string): MemoryPressure {\n const full = /^full .*avg10=([\\d.]+)/m.exec(text);\n if (full === null) return \"unknown\";\n return Number(full[1]) >= 10 ? \"critical\" : \"normal\";\n}\n\n/** What the OS says about pressure, per platform. */\nexport async function readPressure(\n run: ReadCommand,\n readFile: (path: string) => Promise<string | undefined>,\n platform: NodeJS.Platform = hostPlatform(),\n): Promise<MemoryPressure> {\n if (platform === \"darwin\") {\n const text = await run([\n \"sysctl\",\n \"-n\",\n \"kern.memorystatus_vm_pressure_level\",\n ]);\n return text === undefined ? \"unknown\" : parsePressureLevel(text);\n }\n if (platform === \"linux\") {\n const text = await readFile(\"/proc/pressure/memory\");\n return text === undefined ? \"unknown\" : parsePsi(text);\n }\n return \"unknown\";\n}\n\n/**\n * The reader the daemon actually runs — B080.\n *\n * Everything above this line is injected and tested against captured output.\n * This is the one place that touches the host, and it exists so that the\n * injection point has something to inject: a guard nobody wires is dead code\n * wearing an API, which is a thing this codebase has already shipped once.\n *\n * Failures are swallowed into `unknown` rather than thrown. A machine where\n * `vm_stat` is missing is a machine where the guard cannot run, and that is\n * the third state the reader already models — it must not be a crashed\n * daemon, and it must not be silence either. {@link readMemory} says which,\n * and `byollm status` says it out loud.\n */\nexport async function readHostMemory(): Promise<{\n memory: MemoryReading;\n pressure: MemoryPressure;\n}> {\n const run: ReadCommand = async ([command, ...args]) => {\n try {\n const { stdout } = await promisify(execFile)(command, args, {\n // Fixed argv, no shell — the security caveat byollm_022 names for\n // spawning at all, answered rather than skipped.\n shell: false,\n timeout: 2000,\n });\n return stdout;\n } catch {\n return undefined;\n }\n };\n const read = async (path: string): Promise<string | undefined> => {\n try {\n return await readFile(path, \"utf8\");\n } catch {\n return undefined;\n }\n };\n return {\n memory: await readMemory(run, read),\n pressure: await readPressure(run, read),\n };\n}\n","/**\n * One spelling of one server.\n *\n * An origin is this daemon's primary key for a paired relay and for every\n * site under it. Two spellings of the same server must produce the same key\n * and two different servers must never produce the same one — and the\n * function that used to do this job did neither.\n *\n * It was `new URL(input).origin` with the raw string as a fallback, and it\n * cost us a stop-ship on 2026-08-26. `byollm allow hub.byollm.cloud …`\n * normalized to `hub.byollm.cloud`; the pairing had been stored as\n * `https://hub.byollm.cloud`; the lookup missed, and the guard that should\n * have refused read the miss as \"no control plane here\" and ran the flow it\n * was written to prevent. Nothing warned, because a fallback that returns its\n * input cannot tell a caller it failed.\n *\n * The collision was worse than the miss. `new URL()` accepts far more than\n * URLs, and `.origin` serializes anything it cannot place as the **string**\n * `\"null\"` — so `localhost:8080`, `example.com:8443`, `javascript:alert(1)`\n * and `data:text/html,x` all normalized to one identity. Four unrelated\n * inputs sharing a primary key is not a near miss; on the old allowlist it\n * was one entry granting four things.\n *\n * So: parse or refuse, and never guess quietly. The only guess left is the\n * scheme, it is made only when the input supplied none, and it is made the\n * way a person means it (see {@link normalizeOrigin}).\n */\n\n/** Input that does not name a server this daemon could talk to. */\nexport class UnusableOrigin extends Error {\n /** What was offered. Origins are not secrets; this is safe to print. */\n readonly input: string;\n /** Why, in a form a person can act on. */\n readonly reason: string;\n\n constructor(input: string, reason: string) {\n super(`not a usable origin: ${reason}`);\n this.name = \"UnusableOrigin\";\n this.input = input;\n this.reason = reason;\n }\n}\n\n/**\n * Hosts where a scheme-less spelling means `http`.\n *\n * Everywhere else the guess is `https`, because everywhere else it is 2026.\n * Loopback is the exception because a development server on this machine is\n * overwhelmingly plain http, and the rule that matters — a scheme-less\n * spelling resolves to the same key as the schemed one a person would have\n * typed — is only satisfied by guessing the way they meant it. Somebody\n * running TLS on localhost writes the scheme, and is then believed.\n */\nconst LOOPBACK = /^(localhost|127(\\.\\d{1,3}){3}|\\[::1\\])$/i;\n\n/**\n * The origin — scheme, host and port — of a server named by `input`.\n *\n * Accepts a full URL (`https://app.test/anything`) or an authority on its own\n * (`app.test`, `app.test:8443`, `localhost:8080`). Idempotent: normalizing an\n * already-normalized origin returns it unchanged, which is what lets callers\n * normalize at the door and compare with `===` afterwards.\n *\n * @throws {UnusableOrigin} for anything else — including a scheme this daemon\n * does not speak. A refusal is the whole point: the previous fallback turned\n * unparseable input into a plausible-looking key and every caller downstream\n * believed it.\n */\nexport function normalizeOrigin(input: string): string {\n const trimmed = input.trim();\n if (trimmed === \"\") throw new UnusableOrigin(input, \"it is empty\");\n\n const direct = asHttpOrigin(trimmed);\n if (direct !== undefined) return direct;\n\n // Checked before the authority branch, so `ftp://x.test` refuses instead of\n // being read as a host called `ftp`. An input that named a scheme was not\n // trying to be a hostname, and treating it as one is exactly the silent\n // reinterpretation this module exists to stop.\n //\n // Split by remedy, because `http://` and `ftp://x.test` both land here and\n // want opposite advice: one supplied a scheme we speak and no host, the\n // other a host and a scheme we do not. A single message would have told\n // somebody typing `http://` to use http.\n const schemed = trimmed.indexOf(\"://\");\n if (schemed !== -1) {\n const scheme = trimmed.slice(0, schemed).toLowerCase();\n throw new UnusableOrigin(\n input,\n scheme === \"http\" || scheme === \"https\"\n ? \"it names a scheme but no host\"\n : \"it names a scheme this daemon does not speak — use http or https\",\n );\n }\n\n const secure = asHttpOrigin(`https://${trimmed}`);\n if (secure === undefined) {\n throw new UnusableOrigin(input, \"it does not name a host and port\");\n }\n // Re-read the host from the parse that succeeded rather than the raw text:\n // `HUB.Test:8443` and `[::1]:8080` are both hosts, and only the parser\n // knows where each one ends.\n const host = new URL(`https://${trimmed}`).hostname;\n if (LOOPBACK.test(host)) {\n const local = asHttpOrigin(`http://${trimmed}`);\n if (local !== undefined) return local;\n }\n return secure;\n}\n\n/**\n * `candidate`'s origin, if it is an http(s) URL.\n *\n * There is no host check here, and its absence is load-bearing rather than an\n * oversight. `http` and `https` are WHATWG *special schemes*: the parser\n * requires a host for them and throws without one, so a URL that reaches the\n * return has a host by construction. A first draft of this function guarded\n * `hostname === \"\"` anyway, with a comment asserting `new URL(\"http://\")`\n * parses to the origin `\"http:\"`. It does not — it throws — and the guard\n * could never fire. A mutation run found it in the only way an unreachable\n * branch is ever found: by surviving.\n *\n * The invariant it pretended to enforce is real and is now tested instead of\n * guarded, over generated input — see \"every accepted origin names a server\"\n * in origins.test.ts. A property that holds is worth more than a branch that\n * cannot run, and it cannot rot into a false comment.\n */\nfunction asHttpOrigin(candidate: string): string | undefined {\n let url: URL;\n try {\n url = new URL(candidate);\n } catch {\n return undefined;\n }\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return undefined;\n return url.origin;\n}\n","import { spawn } from \"node:child_process\";\nimport type { BackendId } from \"@byollm/protocol\";\n\n/**\n * Starting a local model server when a job needs it — B050.\n *\n * Ruled by Todd on the third pass, and the ruling is smaller than the two\n * drafts before it: \"they should call the models to start them as needed, not\n * keep them running when they aren't.\" Nothing is pre-warmed, nothing is kept\n * resident for byollm's sake, and cleanup is the runtime's own business —\n * ollama unloads models after `keep_alive` without being asked, and the\n * server process left behind is light.\n *\n * ## Only loopback, and the reason is not tidiness\n *\n * A configured `baseUrl` can point anywhere. Starting a process because a\n * *remote* endpoint did not answer is nonsense at best: the thing that is\n * down is on another machine, and the local command would either fail or —\n * worse — succeed and serve a different model than the one the owner\n * configured, on a port that happens to match. So the start is gated on the\n * url being loopback, which is the only case where \"this server is not\n * running\" and \"this machine can start it\" are the same sentence.\n *\n * ## One command, and the rest say nothing\n *\n * `ollama serve` is here because it is stable and well known. The other\n * local servers each have a start command and this module does not guess\n * them: `login.ts` set the precedent — the commands there were checked by\n * running them, and guessing one would have produced a gate that always\n * failed on the path a new person meets first. An id with no entry simply\n * does not get started, which is exactly the behaviour before this existed.\n */\n\n/** How a local server is started, for the ones we can say. */\nexport function startCommandFor(\n id: BackendId,\n): readonly [string, ...string[]] | undefined {\n switch (id) {\n case \"ollama\":\n return [\"ollama\", \"serve\"];\n default:\n /* Not \"cannot be started\" — \"this module has nothing to spawn\". The\n caller falls back to the behaviour it had before, which is to report\n the server as down. Filling these in wants a machine with each of\n them on it, one at a time, the way the login commands were done. */\n return undefined;\n }\n}\n\n/**\n * Is this url on this machine?\n *\n * Hostname only, and deliberately not a DNS lookup: a name that resolves to\n * a loopback address today is a name somebody else controls tomorrow, and\n * this decides whether to run a program.\n */\nexport function isLoopback(baseUrl: string): boolean {\n let host: string;\n try {\n host = new URL(baseUrl).hostname;\n } catch {\n return false;\n }\n /* IPv6 loopback arrives bracketed from `URL`, which strips the brackets\n into `[::1]` -> `::1` on some runtimes and not others. Both spellings. */\n return (\n host === \"127.0.0.1\" ||\n host === \"localhost\" ||\n host === \"::1\" ||\n host === \"[::1]\"\n );\n}\n\nexport interface StartLocalInput {\n readonly id: BackendId;\n readonly baseUrl: string | undefined;\n /** Is it answering now? Asked again after the start, to know if it worked. */\n readonly answers: () => Promise<boolean>;\n /** Spawns and returns immediately — the server outlives this call. */\n readonly spawn: (command: readonly string[]) => void;\n readonly wait: (ms: number) => Promise<void>;\n readonly report: (line: string) => void;\n /** How long to give it before giving up. */\n readonly withinMs?: number;\n readonly pollMs?: number;\n}\n\nexport type StartOutcome =\n \"already-running\" | \"started\" | \"not-startable\" | \"gave-up\";\n\n/**\n * Make sure the local server behind this service is up, if we can.\n *\n * Returns what happened rather than a boolean, because the four cases lead to\n * four different sentences and a caller that only knew \"false\" would have to\n * invent one.\n */\nexport async function ensureLocalServer(\n input: StartLocalInput,\n): Promise<StartOutcome> {\n /**\n * Startability first, and health only if there is something we could do.\n *\n * The other order reads better and costs a request per job on every\n * backend in the product — including the ones this module has no command\n * for and the remote endpoints it would never touch. Asking a question\n * whose answer cannot change what happens next is a request nobody\n * needed, on the hot path.\n */\n const command = startCommandFor(input.id);\n if (command === undefined) return \"not-startable\";\n if (input.baseUrl === undefined || !isLoopback(input.baseUrl)) {\n return \"not-startable\";\n }\n\n if (await input.answers()) return \"already-running\";\n\n input.report(`${input.id} is not answering — starting it`);\n input.spawn(command);\n\n const until = Date.now() + (input.withinMs ?? 20_000);\n /* Asked before the first wait as well as after: a server that was already\n coming up when the job arrived should not cost the job a full poll\n interval it did not need. */\n while (Date.now() < until) {\n await input.wait(input.pollMs ?? 250);\n if (await input.answers()) return \"started\";\n }\n /**\n * Given up, and the job proceeds to fail on its own terms.\n *\n * Deliberately not throwing: the caller was about to try the backend\n * anyway, and the backend's own failure sentence is better than ours — it\n * knows what it asked for and what came back. This adds a line saying we\n * tried, which is the part the backend cannot know.\n */\n input.report(`${input.id} did not come up in time`);\n return \"gave-up\";\n}\n\n/**\n * Can this machine start the server behind a service — B056 / D4.\n *\n * The advertising ruling: a configured-but-stopped local server should\n * advertise as available, because it IS available, one spawn away. First-job\n * latency pays the model load and the job's deadline bounds it.\n *\n * **\"Installed and startable\" is not the same as \"configured\".** A config\n * naming a server nobody ever installed must NOT advertise: the fleet would\n * claim work it cannot serve, and a site's job would go from a clean\n * no-runner silence to a claimed-then-failed job, which is strictly worse for\n * them. So the question is asked of the machine, not of the file.\n *\n * Three things have to hold, and each rules out a real configuration:\n * · we know a start command for this backend (not every local server has\n * one this module can say)\n * · the url is on this machine (a remote endpoint that is\n * down is not something a local spawn fixes)\n * · the binary is actually on PATH (the half that separates\n * \"installed\" from \"written in a config file\")\n */\n/**\n * Why a service cannot be started, when it cannot — B098.\n *\n * Three different facts, and the boolean collapsed them into one so the\n * owner surface asserted whichever it had a sentence for. On Todd's machine\n * that produced a false instruction: his services are `openai-http` at\n * loopback addresses, this module has no start command for that id, and\n * `byollm status` told him to **install** Ollama and MLX — both of which are\n * installed and running.\n *\n * Same shape as B105 one row earlier: one bucket for two facts, and the\n * bucket names the wrong one. Here it costs somebody an afternoon\n * reinstalling software that was never missing.\n */\nexport type Startability =\n | { readonly startable: true }\n /** This module knows no command for that backend — not that it is absent. */\n | { readonly startable: false; readonly why: \"no-start-command\" }\n /** Somebody else's machine. A local spawn cannot fix a remote server. */\n | { readonly startable: false; readonly why: \"not-local\" }\n /** The one case where \"install it\" is the right thing to say. */\n | { readonly startable: false; readonly why: \"not-installed\" };\n\nexport async function startability(input: {\n readonly id: BackendId;\n readonly baseUrl: string | undefined;\n readonly onPath?: (binary: string) => Promise<boolean>;\n}): Promise<Startability> {\n const command = startCommandFor(input.id);\n if (command === undefined)\n return { startable: false, why: \"no-start-command\" };\n if (input.baseUrl === undefined || !isLoopback(input.baseUrl)) {\n return { startable: false, why: \"not-local\" };\n }\n return (await (input.onPath ?? binaryOnPath)(command[0]))\n ? { startable: true }\n : { startable: false, why: \"not-installed\" };\n}\n\n/**\n * Can this be started at all — the answer without the reason.\n *\n * Kept because the advertising decision genuinely only needs the boolean, and\n * threading a reason through it would invite somebody to branch on one there.\n * B098 changed what the SURFACE says, not what the fleet offers.\n */\nexport async function isStartable(input: {\n readonly id: BackendId;\n readonly baseUrl: string | undefined;\n readonly onPath?: (binary: string) => Promise<boolean>;\n}): Promise<boolean> {\n return (await startability(input)).startable;\n}\n\n/**\n * Start a local model server, and let it go — B092.\n *\n * The production half of {@link ensureLocalServer}'s `spawn` seam. Extracted\n * from the daemon's Runner options so it can be run in a test: an inline\n * closure in `cli.ts` is a line nothing can exercise, and B092 exists because\n * a seam nobody passed sat unnoticed for four releases while its own comment\n * described the problem.\n *\n * **Detached, unreferenced, and no stdio.** A model server is not this\n * daemon's child in any sense that matters — it should outlive a daemon\n * restart exactly as it would if its owner had started it, and holding its\n * pipes would let a full output buffer block the process that is meant to be\n * serving jobs.\n *\n * **No shell, and nothing from a job reaches the argv.**\n * {@link startCommandFor} returns a hardcoded literal for a known backend id,\n * so there is nothing here to quote and nothing to smuggle.\n *\n * `onError` rather than a throw: an unhandled `error` on a child process\n * takes the whole daemon down, and \"the binary went away between the PATH\n * check and now\" has to be a job that fails, not a daemon that dies.\n */\nexport function spawnLocalServer(\n command: readonly string[],\n onError: (message: string) => void,\n spawnImpl: typeof spawn = spawn,\n): void {\n const [program, ...args] = command;\n if (program === undefined) return;\n const child = spawnImpl(program, args, {\n detached: true,\n stdio: \"ignore\",\n shell: false,\n });\n child.on(\"error\", (error: Error) => {\n onError(`could not start ${program}: ${error.message}`);\n });\n child.unref();\n}\n\n/**\n * Is this program on PATH?\n *\n * Resolved by looking, not by running it. `ollama --version` would answer the\n * question and would also start work on somebody's machine as a side effect\n * of an advertising decision — and this runs on a heartbeat.\n *\n * Windows executables carry their extension, so PATHEXT is consulted there;\n * everywhere else the file simply has to be executable by us.\n */\nexport async function binaryOnPath(\n binary: string,\n env: NodeJS.ProcessEnv = process.env,\n platform: NodeJS.Platform = process.platform,\n): Promise<boolean> {\n const { access } = await import(\"node:fs/promises\");\n const { join, delimiter } = await import(\"node:path\");\n const { constants } = await import(\"node:fs\");\n\n const dirs = (env[\"PATH\"] ?? \"\").split(delimiter).filter((d) => d !== \"\");\n const suffixes =\n platform === \"win32\"\n ? (env[\"PATHEXT\"] ?? \".COM;.EXE;.BAT;.CMD\").split(\";\")\n : [\"\"];\n\n for (const dir of dirs) {\n for (const suffix of suffixes) {\n try {\n await access(join(dir, `${binary}${suffix}`), constants.X_OK);\n return true;\n } catch {\n // Not here, or not executable by us. Both mean keep looking.\n }\n }\n }\n return false;\n}\n","import { type BackendId, resolveCost } from \"@byollm/protocol\";\nimport type { MemoryPressure, MemoryReading } from \"./memory.js\";\n\n/**\n * Whether this machine should take a job that loads a model — B080.\n *\n * Kevin's Qwen on one local server, an 18 GB Gemma on another, a 36 GB Mac\n * with swap at 97%, and about thirty system daemons restarting at once.\n * Ollama loaded 17 GB into 5.2 GiB free because nothing asked whether there\n * was room — not Ollama, and not us.\n *\n * ## It gates on the RESOLVED cost, and it must ASK\n *\n * Only a service that resolves `free` serves a model out of local memory.\n * Everything `metered` or `subscription` is a proxy to somebody else's\n * hardware, and there is nothing for a memory gate to protect.\n *\n * **Which service is free is a question, not a field.** The first draft of\n * this gate read `BACKENDS[id].cost` — and `openai-http` declares `null`,\n * because its cost is classified from where the request goes rather than\n * declared by the registry. So `cost !== \"free\"` was true, and the gate\n * admitted the generic backend without ever looking at memory: 100 MB\n * available, `{\"admit\": true, \"why\": \"openai-http is a proxy (detected)\"}`.\n *\n * That is precisely the backend this row exists for. `openai-http` at a\n * loopback address is the documented way to reach a local model server — the\n * site says *\"Not listed? openai-http reaches anything OpenAI-compatible\"* —\n * and it is what Todd's own MLX server on port 6999 is configured as. The\n * gate called his local model a proxy and skipped the check.\n *\n * The law was already written, in the function being bypassed:\n * {@link resolveCost}'s signature was hardened after `byollm offer` passed\n * two of three arguments, and its comment records that *\"the\n * no-re-derivation law was breached through the gap rather than by anybody\n * copying the logic.\"* Then it was the wrong arguments; here it was reading\n * the raw field instead of asking. Same law, same shape, one turn later —\n * so this asks, and takes the same no-partial-askers discipline: `baseUrl`\n * and `model` are required keys that may hold `undefined`, never optional\n * ones a caller can forget.\n *\n * Hosted boxes stay inert by construction, since 018 forbids local serving\n * on one and every service there resolves to a proxy.\n *\n * ## It refuses rarely, on purpose\n *\n * The owner already consented by configuring the service, and Todd's ruling\n * is that this consent is enough: a memory floor is second-guessing somebody\n * about their own machine. So this exists for the fleet nobody is watching,\n * not for the laptop whose owner is in the room — and the acceptance test is\n * his: **none of his four authorised services is ever refused in ordinary\n * use.** If it fires there, the default is wrong, not his machine.\n */\nexport interface GateInput {\n readonly backendId: BackendId;\n /**\n * The service's configured address and model — required keys, so that\n * omitting either is a decision at the call site rather than a default\n * nobody notices. See {@link resolveCost}, whose signature this mirrors and\n * whose classification this defers to.\n */\n readonly baseUrl: string | undefined;\n readonly model: string | undefined;\n readonly memory: MemoryReading;\n readonly pressure: MemoryPressure;\n /** Bytes. Deliberately low — see {@link DEFAULT_FLOOR_BYTES}. */\n readonly floorBytes?: number;\n}\n\n/**\n * 2 GB, and low on purpose.\n *\n * An 8 GB floor would refuse on the machine that prompted this row, tonight,\n * while it holds 6.6 GB and serves jobs perfectly. That is the `os.freemem()`\n * failure wearing a different number: broken closed on a machine that is\n * fine. Below 2 GB is dire by any reading and does not happen in ordinary\n * use — which is the point, because this is a backstop under the pressure\n * signal rather than the primary judgement.\n */\nexport const DEFAULT_FLOOR_BYTES = 2 * 1024 * 1024 * 1024;\n\nexport type GateDecision =\n | { readonly admit: true; readonly why: string }\n | { readonly admit: false; readonly why: string };\n\n/**\n * Does this route hold a model on this machine at all?\n *\n * Exported so the caller can decide whether reading memory is worth a\n * `vm_stat` spawn per job, and defined here so that decision is the SAME\n * decision the gate makes rather than a second one that agrees today. That\n * is the whole lesson of B085, which arrived because a second place answered\n * a question this file already answers.\n */\nexport function guardApplies(input: {\n readonly backendId: BackendId;\n readonly baseUrl: string | undefined;\n readonly model: string | undefined;\n}): boolean {\n return resolveCost(input.backendId, input.baseUrl, input.model) === \"free\";\n}\n\nexport function memoryGate(input: GateInput): GateDecision {\n /**\n * Ask, never read the field.\n *\n * The one unknown-shaped answer this can return — `metered` because the\n * address is absent or unreadable — would make the gate skip a check it\n * should run, which is the wrong failure direction for a guard even though\n * it is the right one for a bill. It is unreachable rather than handled:\n * `validateConfig` refuses an HTTP-class service whose `baseUrl` is missing\n * or unparseable, so no such service is ever dispatched. That reachability\n * claim is a prediction, so it ships with the test that catches it — see\n * \"an unreachable premise\" in the suite.\n */\n if (!guardApplies(input)) {\n return {\n admit: true,\n why:\n `${input.backendId} resolves to ` +\n `${resolveCost(input.backendId, input.baseUrl, input.model)}, ` +\n `so it holds no model here`,\n };\n }\n\n /**\n * Unknown admits, and says so somewhere a person will read it.\n *\n * Refusing where we cannot measure bricks the daemon on a platform nobody\n * has visited; admitting silently means the guard does not exist and\n * nobody knows. This is `stopReasons`' `unavailable` one layer down.\n */\n if (input.memory.kind !== \"read\") {\n return {\n admit: true,\n why: `memory could not be read (${input.memory.why}), so the guard is not active here`,\n };\n }\n\n /**\n * Critical only, never warn — verified rather than assumed.\n *\n * `kern.memorystatus_vm_pressure_level` reads **2, warn** on the machine\n * this row exists for, stably, while it holds 6.6 GB available and serves\n * jobs. Critical is the state where the kernel is already killing things,\n * which is the only state where refusing a job is the kinder answer.\n */\n if (input.pressure === \"critical\") {\n return { admit: false, why: \"the kernel reports critical memory pressure\" };\n }\n\n const floor = input.floorBytes ?? DEFAULT_FLOOR_BYTES;\n if (input.memory.availableBytes < floor) {\n return {\n admit: false,\n why:\n `${bytes(input.memory.availableBytes)} available, under the ` +\n `${bytes(floor)} floor`,\n };\n }\n\n /**\n * Swap, and the landmine that lives here.\n *\n * **`swapTotalBytes === 0` means \"no swap is configured\", never \"swap\n * headroom is exhausted.\"** A ratio would be 0/0. Kubernetes nodes run\n * with swap disabled, so every hosted box reads zero — and a careless\n * \"refuse when swap headroom is gone\" refuses every job on every box\n * forever, which is broken-closed again on machines that are fine.\n *\n * Third time this week the same distinction decided a design: absent is\n * not empty, the way `unavailable` is not `unverified` and `\"unknown\"` is\n * not `\"end\"`.\n */\n const { swapTotalBytes, swapFreeBytes, swapGrows } = input.memory;\n if (\n swapTotalBytes !== undefined &&\n swapTotalBytes > 0 &&\n /* A store that grows on demand cannot be exhausted by being full. macOS\n enlarges its swap file as it needs to — Todd's read 15 GB one night\n and 24 GB the next morning — so \"free near zero\" there is the current\n file filling, not the machine running out. Second landmine in the same\n three lines: absent is not empty, and full is not exhausted. */\n swapGrows !== true &&\n swapFreeBytes !== undefined &&\n swapFreeBytes < 64 * 1024 * 1024\n ) {\n return {\n admit: false,\n why: `swap is configured and effectively exhausted (${bytes(swapFreeBytes)} free)`,\n };\n }\n\n return {\n admit: true,\n why: `${bytes(input.memory.availableBytes)} available, pressure ${input.pressure}`,\n };\n}\n\n/**\n * Memory, in GB, rounded DOWN — B167, and the direction is the whole point.\n *\n * Four copies of this lived in the daemon and `cli.ts` held the same three\n * lines twice, in two functions. That is B072's shape on a different quantity,\n * and it is a different quantity in the way that matters: **the rounding goes\n * the other way.**\n *\n * `describeBytes` in the protocol rounds UP, because it answers *\"how much do\n * I have to lose\"* and an answer smaller than the truth sends somebody to trim\n * a hundred bytes off something that needs to lose a megabyte.\n *\n * Everything here answers *\"how much do I have\"*, and **an answer larger than\n * the truth is the dangerous one**: 2147483647 bytes available against a 2 GB\n * floor is a refusal, and `toFixed` prints it as `2.0 GB` — a screen saying a\n * device has exactly the memory it was just refused for. Floor, not nearest,\n * so the number shown is never more than the number measured.\n *\n * All four call sites render available or total memory, which is why one\n * helper is right here and would not have been if one of them rendered a\n * requirement.\n */\nexport function gigabytes(n: number): string {\n return `${(Math.floor((n / 1024 ** 3) * 10) / 10).toFixed(1)} GB`;\n}\n\nfunction bytes(n: number): string {\n return gigabytes(n);\n}\n","import { spawnLocalServer } from \"./local-server.js\";\nimport { gigabytes } from \"./memory-gate.js\";\nimport { access } from \"node:fs/promises\";\nimport { emphasise, terminalContext } from \"./emphasis.js\";\nimport { backendVerifier, setModel, showModel } from \"./model.js\";\nimport { preflight } from \"./preflight.js\";\nimport { offerInbox, update } from \"./update.js\";\nimport { realUpdateDeps } from \"./update-deps.js\";\nimport type { Provenance } from \"./provenance.js\";\nimport { runLogin, type LoginCommand } from \"./login.js\";\nimport { createBackend } from \"./backends/index.js\";\nimport type { Backend } from \"./backends/types.js\";\nimport { mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { hostname, userInfo } from \"node:os\";\nimport { dirname } from \"node:path\";\nimport { createInterface } from \"node:readline/promises\";\nimport { FAILURES_BEFORE_ALARM, readHealth } from \"./health.js\";\nimport {\n beatWriterIsGone,\n readHeartbeat,\n type DaemonHeartbeat,\n} from \"./heartbeat.js\";\nimport { InputEnded, runSetup, terminalIo, type TerminalIo } from \"./setup.js\";\nimport {\n manageServices,\n misTypedReport,\n misTypedServices,\n readExistingConfig,\n summarise,\n writeManaged,\n} from \"./services-manage.js\";\nimport type { BackendId } from \"@byollm/protocol\";\nimport { backendDescriptor, backendName, classifyCost } from \"@byollm/protocol\";\nimport { fingerprint } from \"@byollm/protocol\";\nimport { normalizeOrigin, UnusableOrigin } from \"./origins.js\";\nimport { Budgets } from \"./budgets.js\";\nimport { ClientError, ProtocolClient } from \"./client.js\";\nimport {\n REVOKED_SENTENCE,\n clearRevokedMark,\n revokedMark,\n revokedRemedy,\n} from \"./revoked.js\";\nimport { TEST_YOUR_DEVICE } from \"./test-your-device.js\";\nimport { diagnoseRoute } from \"./diagnose.js\";\nimport { DaemonConfig, loadConfig, writeConfig } from \"./config.js\";\nimport { connect } from \"./connect.js\";\nimport { IngressLog, stripControlChars } from \"./ingress.js\";\nimport { readHostMemory } from \"./memory.js\";\nimport { modelLoadQuestion } from \"./model-memory.js\";\nimport { probeLocalServers } from \"./probe-local.js\";\nimport { unusedModelsReport } from \"./unused-models.js\";\nimport { stopLine } from \"./stop-remedy.js\";\nimport type { MemoryPressure, MemoryReading } from \"./memory.js\";\nimport { DeviceIdentity } from \"./identity.js\";\nimport {\n consoleAgentUnavailable,\n runConsoleAgent,\n CONSOLE_BOX_ENDPOINT,\n} from \"./console-agent-main.js\";\nimport { ConsoleAgentSpec } from \"./console-agent-spec.js\";\nimport { CONSOLE_DEVICE_ENDPOINT, consoleListener } from \"./console-listen.js\";\nimport { Pairings, recordSites } from \"./pairings.js\";\nimport { dollars, SpendLedger } from \"./spend.js\";\nimport { SpentGrants } from \"./spent-grants.js\";\nimport {\n daemonPaths,\n overriddenRootNotice,\n type DaemonPaths,\n} from \"./paths.js\";\nimport { howItRuns, supervisorPid, tellSupervisor } from \"./supervised.js\";\nimport { Runner, type RunnerEvent } from \"./runner.js\";\nimport {\n installedProgram,\n installService,\n serviceState,\n spawnCommand,\n uninstallService,\n type CommandRunner,\n type ServiceState,\n} from \"./install.js\";\nimport {\n serviceIsInstalled,\n servicePlan,\n servicePlatform,\n type ServicePlan,\n type ServicePlatform,\n type ServiceTarget,\n} from \"./service.js\";\nimport { authNote, renderServices } from \"./service-line.js\";\nimport { readServiceStates, writeServiceStates } from \"./service-states.js\";\nimport { DAEMON_VERSION, formatVersion } from \"./index.js\";\n\nconst USAGE = `byollm — run an app's LLM jobs on your own models.\n\n byollm setup answer three questions instead of editing JSON\n byollm connect [<url>] pair with an app and start running its jobs\n byollm forget <url> drop a pairing\n byollm name [<name>] what this device calls itself when it pairs\n byollm start run in the background, across restarts\n byollm stop stop running in the background\n byollm run run here in the foreground, and watch the log\n byollm status what is connected, what is running, what it cost\n byollm log [--full] [-n N] every prompt that has run on this device\n byollm services each service: health, who it is offered to, model\n byollm services manage turn services on and off, and share them\n byollm model <svc> <name> check a model answers, then use it\n byollm offer <service> <scope> who a service is offered to (private|team)\n byollm sites which sites this device serves\n\nConfig lives in ~/.byollm/config.json. Everything this daemon has ever run is\nin ~/.byollm/ingress.log — it is yours to read and yours to delete.\n\nminAvailableMemoryBytes in that config is the memory this device keeps free: a\njob that would load a model is refused below it. It does not know how big your\nmodels are, so set it to fit your largest one.\n`;\n\n/**\n * Commands that moved, and what they moved to — byollm_020.\n *\n * Kept working for a window rather than deleted, because Kevin, Casul and\n * Rob have these in their shells and their notes today. A rename that\n * answers \"unknown command\" is a rename that costs somebody their afternoon\n * for no reason: the old word still does the right thing, and says once what\n * to type next time.\n *\n * `pause`/`resume` are not in here, because they were not renamed. They were\n * removed — {@link REMOVED} — and the difference is the point of the map:\n * this one promises the old word still does the old thing.\n */\n/**\n * How long a drain waits for running work before an update proceeds — B053.\n *\n * A job that outlives this holds a lease the hub will reclaim anyway, and a\n * machine cannot be held out of service indefinitely by one slow prompt.\n */\nconst UPDATE_DRAIN_MS = 120_000;\n\n/** How often the update watcher looks for an offer. Nothing is waiting on a person. */\nconst UPDATE_POLL_MS = 1_000;\n\n/**\n * Verbs that are gone, and say so — B043, ruled by Todd 2026-09-04.\n *\n * `pause` did not do what its own status screen claimed. It wrote a flag,\n * and the running daemon never read it: the flag reached exactly two places,\n * a probe heartbeat sent during `connect` and the `status` headline, which\n * printed `PAUSED` in the top line while the daemon underneath went on\n * claiming work and spending money. Somebody who paused a machine and\n * checked on it was told the thing they wanted to hear by a screen that had\n * not asked the daemon anything.\n *\n * So this is not a feature being retired for tidiness. Removing it is how\n * the lie stops, and it is why these do not become aliases for `stop`: an\n * alias must do the same thing under a new name, and `stop` unregisters\n * supervision — more destructive than what the person typed, arriving\n * through the fix for it.\n *\n * Idling without giving up startup is now `byollm stop` and `byollm start`,\n * which costs one command each since B036 made re-registration free.\n */\nconst REMOVED = {\n pause: \"stop\",\n resume: \"start\",\n} as const;\n\n/**\n * What a removed verb says. Deliberately not the shape of a rename notice:\n * that one ends \"still works for now\", and this one has to end the opposite\n * way or it reads as a nudge somebody can ignore.\n */\nfunction removedNotice(was: keyof typeof REMOVED): string {\n return (\n `\\`byollm ${was}\\` has been removed — use \\`byollm ${REMOVED[was]}\\`.\\n\\n` +\n ` byollm stop stops the daemon and unregisters it from startup\\n` +\n ` byollm start brings it back\\n\\n` +\n `It did not do what it said: the flag it wrote was never read by the ` +\n `running\\ndaemon, so a machine you had \"paused\" went on claiming work.\\n`\n );\n}\n\n/** \"one app\" / \"3 apps\", so the line reads as a sentence either way. */\nfunction describeOrigins(origins: readonly string[]): string {\n return origins.length === 1\n ? `paired with ${origins[0] ?? \"one app\"}`\n : `paired with ${String(origins.length)} apps`;\n}\n\n/** Exit codes: 0 fine, 1 a real failure, 2 the user asked for something wrong. */\nexport type ExitCode = 0 | 1 | 2;\n\n/**\n * Everything the CLI touches outside itself.\n *\n * byollm_002 calls the meter the product's soul, which means it has to be\n * testable rather than merely observable by a human at a terminal. Injecting\n * the streams, the state directory and the confirmation prompt lets the tests\n * drive the real commands against a temporary `BYOLLM_HOME`.\n */\nexport interface CliIo {\n readonly out: (text: string) => void;\n readonly err: (text: string) => void;\n /** Answers the scarier-confirmation prompt when widening access. */\n readonly confirm: (question: string) => Promise<boolean>;\n}\n\nconst defaultIo: CliIo = {\n out: (text) => process.stdout.write(text),\n err: (text) => process.stderr.write(text),\n confirm: confirmInteractively,\n};\n\n/** Run one command. Exported so the tests drive the same code a user does. */\n/**\n * One service's backend, built the way the daemon builds it.\n *\n * `baseUrl` is not optional decoration for an HTTP-class transport — the\n * constructor throws without it. Building backends from the id alone crashed\n * `byollm run` for every Ollama user, and the test that caught it was one\n * about revocation, which is the kind of luck not to rely on twice.\n */\nexport function backendFor(config: {\n readonly type: BackendId;\n readonly baseUrl?: string | undefined;\n}): Backend {\n return createBackend(\n config.type,\n config.baseUrl === undefined ? {} : { baseUrl: config.baseUrl },\n );\n}\n\n/**\n * The preflight's dependencies, defaulted to the real ones — B047.\n *\n * Built here rather than inside each command so that `start` and `run` ask\n * the same question the same way. Two implementations of one rule is the\n * defect this whole audit keeps finding.\n */\nasync function preflightFor(\n paths: DaemonPaths,\n io: CliIo,\n interactive: boolean,\n options: {\n platform?: NodeJS.Platform;\n verify?: (config: {\n readonly type: BackendId;\n readonly model: string;\n readonly baseUrl?: string | undefined;\n }) => Promise<{\n readonly answers: boolean | undefined;\n readonly detail?: string | undefined;\n }>;\n login?: (command: LoginCommand) => Promise<boolean>;\n ask?: (question: string) => Promise<string>;\n },\n): Promise<void> {\n const loaded = await loadConfig(paths.config);\n await preflight({\n loaded,\n device: await labelFor(paths, undefined),\n io,\n interactive,\n ask:\n options.ask ??\n (async (question) => {\n const rl = createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n try {\n return await rl.question(question);\n } finally {\n rl.close();\n }\n }),\n platform: options.platform ?? process.platform,\n verify:\n options.verify ??\n ((config) =>\n backendVerifier(() => backendFor(config))(config.type, config.model)),\n login:\n options.login ??\n ((command) =>\n runLogin(command, (text) => {\n io.err(text);\n })),\n signInFor: (config) => backendFor(config).signIn,\n });\n}\n\nexport async function runCli(\n argv: readonly string[],\n options: {\n paths?: DaemonPaths;\n io?: Partial<CliIo>;\n /**\n * Stops the polling loop. The executable wires this to SIGINT/SIGTERM;\n * anything embedding the CLI (including its tests) can stop it the same\n * way rather than by killing the process.\n */\n signal?: AbortSignal;\n /**\n * The machine's service supervisor. Injected by the tests so that neither\n * `install` nor `status` ever spawns a real `launchctl` — a unit test that\n * shells out to the host's init system is a unit test that installs\n * something on whoever runs it.\n */\n service?: ServiceIo;\n /**\n * How long a parked daemon waits between asking whether it has been\n * re-paired. Injected by the tests for the same reason `service` is: the\n * real interval is tuned for a person watching a machine, and a test that\n * waited it out would spend that time doing nothing.\n */\n parkPollMs?: number;\n /**\n * Is something restarting us if we exit? Defaults to \"no TTY, so yes\".\n * Injected because the parked-daemon behaviour only exists under a\n * supervisor, and a test runner attached to a terminal looks like a\n * person at a prompt — which is the other branch entirely.\n */\n supervised?: boolean;\n /**\n * The environment to read the supervisor's pid from — B241.\n *\n * **Not the same seam as `supervised` above, and the difference is the\n * whole reason this exists.** `supervised` states the answer; this states\n * the INPUT the answer is derived from, so a test can exercise the\n * derivation itself — which is what `start-says-what-is-signed-out.test.ts`\n * is for, and why it could not use `supervised`.\n *\n * Before this, saying \"there is a supervisor\" meant setting\n * `process.env` on the whole process, and `process.env` is shared across\n * vitest's worker threads. Two files did it, and their paired runs failed\n * 8% of the time: each hung waiting on a supervisor the other had\n * announced. A global is not a fixture.\n */\n env?: NodeJS.ProcessEnv;\n /**\n * Which machine this is, and how a backend is checked and signed in —\n * B047's seams.\n *\n * The preflight spends a real canary and can hand the terminal to a\n * vendor CLI. Both are injected so a test can drive the whole shape\n * without spending money or opening a browser, and so the Windows branch\n * is reachable from the machines we own.\n */\n platform?: NodeJS.Platform;\n /**\n * Is a person here to answer? Defaults to \"is stdout a terminal\".\n *\n * Separate from `supervised`, which asks whether something restarts us.\n * They usually agree and they are not the same question, and the\n * preflight turns on this one: a canary costs money and a login prompt\n * needs somebody to type.\n */\n interactive?: boolean;\n verify?: (config: {\n readonly type: BackendId;\n readonly model: string;\n readonly baseUrl?: string | undefined;\n }) => Promise<{\n readonly answers: boolean | undefined;\n readonly detail?: string | undefined;\n }>;\n login?: (command: LoginCommand) => Promise<boolean>;\n /** How the preflight asks. Injected so tests are not a TTY. */\n ask?: (question: string) => Promise<string>;\n } = {},\n): Promise<ExitCode> {\n const [command, ...rest] = argv;\n const paths = options.paths ?? daemonPaths();\n const io: CliIo = { ...defaultIo, ...options.io };\n const signal = options.signal;\n // Resolved once: three call sites each falling back separately is three\n // chances to pass the wrong one, and the object is a description of this\n // process, not a connection to anything.\n const service = options.service ?? defaultServiceIo();\n\n switch (command) {\n case undefined:\n case \"help\":\n case \"--help\":\n case \"-h\":\n io.out(USAGE);\n return 0;\n case \"--version\":\n case \"version\":\n // The full tuple, not a bare version. byollm_010 §5: an issue that\n // arrives without a platform costs a round trip to learn one.\n io.out(formatVersion());\n return 0;\n case \"setup\":\n return commandSetup(paths, io, signal);\n case \"connect\":\n return commandConnect(paths, rest, io, signal);\n case \"name\":\n return commandName(paths, rest, io);\n case \"console-agent\":\n return commandConsoleAgent(paths, rest, io);\n case \"run\":\n return commandRun(\n paths,\n rest,\n io,\n signal,\n options.parkPollMs,\n options.supervised,\n options.interactive,\n {\n ...(options.platform === undefined\n ? {}\n : { platform: options.platform }),\n ...(options.verify === undefined ? {} : { verify: options.verify }),\n ...(options.login === undefined ? {} : { login: options.login }),\n ...(options.ask === undefined ? {} : { ask: options.ask }),\n },\n );\n case \"status\":\n return commandStatus(paths, io, service);\n case \"log\":\n return commandLog(paths, rest, io);\n case \"start\":\n return commandInstall(paths, io, service, options.interactive, {\n ...(options.platform === undefined\n ? {}\n : { platform: options.platform }),\n ...(options.verify === undefined ? {} : { verify: options.verify }),\n ...(options.login === undefined ? {} : { login: options.login }),\n ...(options.ask === undefined ? {} : { ask: options.ask }),\n });\n case \"stop\":\n return commandUninstall(paths, io, service);\n case \"allow\":\n return commandRetiredAdmission(\"allow\", io);\n case \"disallow\":\n return commandRetiredAdmission(\"disallow\", io);\n case \"offer\":\n return commandOffer(paths, rest, io);\n case \"sites\":\n return commandSites(paths, io);\n case \"approve\":\n return commandRetiredApprove(io);\n case \"forget\":\n return commandForget(paths, rest, io);\n case \"services\":\n /**\n * One sub-verb, and everything else is refused rather than ignored.\n *\n * `byollm models claude fake` once listed every service and exited zero,\n * because the arguments were dropped on the floor — a command asked to\n * SET something answered by LISTING, and reported success for work it\n * never did. The same trap is one typo away here (`byollm services\n * mange`), and the same rule closes it: a verb handed arguments it has\n * no use for says so and names the one it has.\n */\n if (rest[0] === \"manage\") {\n return commandServicesManage(\n paths,\n io,\n rest.slice(1),\n signal,\n options.env,\n );\n }\n if (rest.length > 0) {\n io.err(\n `byollm services takes no arguments, and got ` +\n `${rest.map((arg) => JSON.stringify(arg)).join(\" \")}.\\n\\n` +\n ` byollm services every service, and the model it runs\\n` +\n ` byollm services manage turn services on and off\\n`,\n );\n return 2;\n }\n return commandServices(paths, io, service);\n case \"model\":\n return commandModel(paths, rest, io);\n /* Gone, and saying so rather than doing something adjacent — see\n {@link REMOVED} for why these are not aliases. */\n case \"pause\":\n case \"resume\":\n io.err(removedNotice(command));\n return 2;\n default:\n io.err(`unknown command: ${command}\\n\\n${USAGE}`);\n return 2;\n }\n}\n\n// -- install ------------------------------------------------------------------\n\n/**\n * Everything the install commands touch outside themselves.\n *\n * Injected so the tests drive the real code with a fake `launchctl`: the\n * alternative is a command nobody can test on the machine they are writing it\n * on, which for three operating systems means two of them are checked by\n * hoping.\n */\nexport interface ServiceIo {\n readonly platform: ServicePlatform;\n readonly execPath: string;\n readonly scriptPath: string;\n readonly run: CommandRunner;\n readonly home?: string;\n readonly uid?: number;\n /** Windows only: who the scheduled task belongs to, so it needs no admin. */\n readonly user?: string;\n /**\n * Windows only: where the Startup fallback is written.\n *\n * Here so a test can point it somewhere disposable. See\n * {@link ServiceTarget.appData} — the suite was writing into the real one.\n */\n readonly appData?: string;\n /**\n * How install waits between probes while confirming the daemon came up.\n *\n * Injected for the same reason `run` is: the confirmation is a real poll\n * with real delays, and a suite that spends six seconds proving it is a\n * suite somebody starts skipping.\n */\n readonly wait?: (ms: number) => Promise<void>;\n}\n\n/**\n * How this process was started, as something a service file can point at.\n *\n * `process.argv[1]` is the CLI's own entry script. It is the right answer for\n * a global install and the wrong one for `npx`, which is why the plan refuses\n * the second — see `refuseToSupervise`.\n */\nexport function defaultServiceIo(): ServiceIo {\n return {\n /* `servicePlatform`, not a ternary — B103. The same three-way\n classification was written out here while the function holding its\n reasoning had no callers at all: *\"everything that is not macOS or\n Windows is treated as systemd… guessing wrong loudly beats refusing to\n run on a platform somebody actually has.\"* That paragraph is the\n decision, and a copy of the branches carries none of it. */\n platform: servicePlatform(process.platform),\n execPath: process.execPath,\n scriptPath: process.argv[1] ?? \"\",\n run: spawnCommand,\n // `getuid` is absent on Windows, where nothing reads it.\n uid: process.getuid?.() ?? 0,\n /* Who the Windows task belongs to — B036. `USERDOMAIN` is absent on a\n machine that is not domain-joined, where the bare name is what\n `schtasks` wants anyway. Only Windows reads this. */\n ...(process.env[\"USERNAME\"] === undefined\n ? {}\n : {\n user: process.env[\"USERDOMAIN\"]\n ? `${process.env[\"USERDOMAIN\"]}\\\\${process.env[\"USERNAME\"]}`\n : process.env[\"USERNAME\"],\n }),\n };\n}\n\nfunction serviceTarget(paths: DaemonPaths, service: ServiceIo): ServiceTarget {\n return {\n platform: service.platform,\n execPath: service.execPath,\n scriptPath: service.scriptPath,\n ...(service.home === undefined ? {} : { home: service.home }),\n ...(service.uid === undefined ? {} : { uid: service.uid }),\n ...(service.user === undefined ? {} : { user: service.user }),\n ...(service.appData === undefined ? {} : { appData: service.appData }),\n root: paths.root,\n };\n}\n\n/**\n * Run in the background from now on — cloud_002.\n *\n * The command that makes a machine keep its promise. Everything a roster\n * assumes — that this machine is there, that work sent to it runs — depends on\n * a process that outlives the window somebody typed in.\n */\nasync function commandInstall(\n paths: DaemonPaths,\n io: CliIo,\n service: ServiceIo,\n interactive = process.stdout.isTTY,\n preflightOptions: {\n platform?: NodeJS.Platform;\n verify?: (config: {\n readonly type: BackendId;\n readonly model: string;\n readonly baseUrl?: string | undefined;\n }) => Promise<{\n readonly answers: boolean | undefined;\n readonly detail?: string | undefined;\n }>;\n login?: (command: LoginCommand) => Promise<boolean>;\n ask?: (question: string) => Promise<string>;\n } = {},\n): Promise<ExitCode> {\n const result = await installService(\n serviceTarget(paths, service),\n service.run,\n service.wait ?? ((ms) => new Promise((r) => setTimeout(r, ms))),\n (await revokedMark(paths.health)) !== undefined,\n );\n for (const line of result.lines) (result.ok ? io.out : io.err)(`${line}\\n`);\n /**\n * The same moment, reached the other way.\n *\n * `byollm setup` ends here for most people, but somebody who paired earlier\n * and ran `byollm start` on its own has arrived at exactly the point where\n * the device is running and the promise becomes true — and had nothing\n * telling them so. One sentence, one definition, both callers.\n */\n if (result.ok) {\n /**\n * A service that cannot sign in is named here, where somebody is looking\n * — B036.\n *\n * Kevin's daemon started cleanly and served nothing, because `claude` was\n * signed out. Everything was working: the task registered, the process\n * ran, `status` was honest, and the one screen he was actually watching\n * said the install had succeeded. He found out by noticing that no work\n * arrived.\n *\n * That line read services.json — what the daemon's last probe recorded —\n * and Kevin then signed out and ran `start` and got nothing. B047: the\n * install waits for the daemon to be ALIVE, not for its first probe to\n * have landed, so the file is from before the sign-out on a used machine\n * and absent on a new one, and \"absent is not signed-out\" correctly says\n * nothing about either.\n *\n * So it asks now, and REPLACES the read-back rather than joining it. Two\n * answers to one question on one screen is how they come to disagree.\n */\n await preflightFor(paths, io, interactive, preflightOptions);\n io.out(`\\n${TEST_YOUR_DEVICE}\\n`);\n }\n return result.ok ? 0 : 1;\n}\n\nasync function commandUninstall(\n paths: DaemonPaths,\n io: CliIo,\n service: ServiceIo,\n): Promise<ExitCode> {\n const result = await uninstallService(\n serviceTarget(paths, service),\n service.run,\n );\n for (const line of result.lines) io.out(`${line}\\n`);\n return 0;\n}\n\n/**\n * One line about supervision, for `status`.\n *\n * \"It says it is paired\" and \"it is actually running\" are different facts, and\n * the second is the one that matters at 2 a.m. The line is deliberately\n * blunt about the third state — installed but stopped — because that is the\n * one that looks fine from the dashboard and serves nothing.\n */\nasync function supervisionLine(\n plan: ServicePlan,\n state: ServiceState,\n revoked: boolean,\n): Promise<string> {\n switch (state.state) {\n case \"running\":\n return `service: running under ${plan.supervisor}\\n`;\n case \"installed\": {\n /**\n * And say *why*, when the file on disk can say it.\n *\n * \"(last exit 2)\" is a number somebody has to interpret, and the walk\n * showed what that costs: a device on rosters, serving nothing, and a\n * log full of routine lines because the failure was never in the log —\n * launchd could not start the program at all.\n *\n * The unit records absolute paths to the node that installed it. Under\n * a version manager those belong to one node version, so an ordinary\n * node upgrade leaves a service pointing at a binary that is gone. That\n * is invisible from every surface here, and it is one `stat` away from\n * being the first thing this line says.\n */\n /**\n * A revoked device is not a broken install — ruled 2026-09-03.\n *\n * \"on rosters and serving nothing. See <log>\" is the right sentence for\n * a daemon that will not start, and the wrong one for a daemon that has\n * been told to stop: it sends somebody to read a log that says exactly\n * what the line above already said, and it implies something on this\n * machine needs fixing. Nothing here does. The headline has named the\n * cause and the remedy, so this line stops competing with it.\n */\n if (revoked) {\n return `service: installed, not serving — ${REVOKED_SENTENCE}.\\n`;\n }\n const program = await installedProgram(plan);\n const gone =\n program === null || program.exists\n ? \"\"\n : `\\n the program it runs is missing: ${program.path}\\n` +\n ` (the node it was installed with was removed or upgraded — ` +\n `\\`byollm start\\` records the current one)\\n`;\n return (\n `service: installed but NOT running (${state.detail}) — ` +\n `this device is on rosters and serving nothing. See ${plan.logPath}\\n` +\n gone\n );\n }\n case \"absent\": {\n /**\n * \"Not installed\" is wrong on a machine that autostarts — 2026-09-02.\n *\n * `serviceState` asks the supervisor. On Windows the supervisor is\n * Task Scheduler, and every install had fallen to the Startup folder\n * instead — so `schtasks` truthfully reported nothing, and this line\n * told somebody whose daemon starts at every logon that jobs only run\n * while a terminal is open.\n *\n * The file is the evidence the supervisor cannot give. It says less\n * than a running check — a Startup entry means it will start, not that\n * it is up — and saying less accurately beats saying more wrongly.\n */\n const fallback = plan.fallback;\n if (fallback !== undefined && (await exists(fallback.unitPath))) {\n return (\n `service: starting from ${fallback.supervisor} at logon, not ` +\n `under ${plan.supervisor}\\n` +\n ` ${fallback.caveat}\\n` +\n ` startup: ${fallback.unitPath}\\n`\n );\n }\n return `service: not installed — jobs only run while \\`byollm run\\` is open (\\`byollm start\\` fixes that)\\n`;\n }\n }\n}\n\n// -- name ---------------------------------------------------------------------\n\n/**\n * Read or set what this machine calls itself.\n *\n * The name is shown on the approval screen — the one moment somebody is\n * deciding whether to trust this machine, and the one moment \"which of my\n * three laptops is this\" is a question with consequences. A hostname answers\n * it badly and a person answers it well.\n *\n * It changes nothing already paired. A name is what a machine *offered* when\n * it asked, and rewriting an app's record of that afterwards would be this\n * daemon editing somebody else's memory of a decision they made.\n */\nasync function commandName(\n paths: DaemonPaths,\n args: readonly string[],\n io: CliIo,\n): Promise<ExitCode> {\n const next = args[0];\n if (next === undefined) {\n io.out(`${await labelFor(paths, undefined)}\\n`);\n return 0;\n }\n\n await mkdir(dirname(paths.label), { recursive: true });\n await writeFile(paths.label, `${next.slice(0, 120)}\\n`, { mode: 0o600 });\n io.out(\n `This device will pair as ${next.slice(0, 120)}.\\n` +\n \"Anything already paired keeps the name it introduced itself with.\\n\",\n );\n return 0;\n}\n\n// -- console-agent -----------------------------------------------------------\n\n/**\n * `byollm console-agent` — the box side of a browser console.\n *\n * **Not reachable from `byollm run`, and that is the design.** A capability\n * that lets a remote broker drive a pty should be absent from a laptop\n * daemon's behaviour rather than disabled in it, so it is started explicitly,\n * by the box's supervisor, and by nothing else. The pty it needs is not\n * installed outside the box image at all (Todd's ruling, 2026-09-16: node-pty\n * goes in the container build, with no dependency entry anywhere), so on an\n * ordinary machine this command has nothing to run and says so in one line.\n *\n * The session's arguments come from the hub, which is the party that decided\n * an owner may open this console. This command does not second-guess that —\n * it verifies what it can (every frame signed by the announced browser key,\n * and the sealed `hello` naming the same one) and writes every session to the\n * owner's feed, which is where the ruling puts the accountability.\n */\n/**\n * Which shell a console lands in — the one thing this command will not guess.\n *\n * It used to read `BYOLLM_CONSOLE_SHELL` and fall back to `/bin/sh`. That\n * variable was set NOWHERE: not in the box's Dockerfile, not in its first\n * boot, not by the supervisor that starts this command. So every hosted\n * console since the feature shipped landed in a bare `/bin/sh`, while the\n * page's greeting told the operator it ran \"a fixed list of commands\" and\n * `box/test/restricted-shell.test.ts` went green about a fence that nothing\n * ever spawned. `ls` answered with the filesystem. B237.\n *\n * **A default reached by forgetting is not a default, it is the behaviour.**\n * So there is no default. The shell is named, or no console opens. The\n * unrestricted one is still reachable for local work, by asking for it in\n * words that cannot be typed by accident — which is the whole difference\n * between an escape hatch and a hole.\n */\n/**\n * The session description, if one was given — the first argument that is\n * neither a flag nor a flag's value.\n *\n * `--shell` takes a path, and a naive `args[0]` would read that path as a\n * session spec and try to join a console with it. Exported so it can be\n * asserted directly: the listening form never returns, so a test that drove\n * the command to find out would hang instead of failing.\n */\nexport function consoleSpecArg(args: readonly string[]): string | undefined {\n const shellAt = args.indexOf(\"--shell\");\n return args.find(\n (arg, at) =>\n !arg.startsWith(\"--\") && !(shellAt !== -1 && at === shellAt + 1),\n );\n}\n\ntype ConsoleShellChoice =\n | { readonly ok: true; readonly command: string; readonly fenced: boolean }\n | { readonly ok: false; readonly why: string };\n\nconst UNRESTRICTED = \"--unrestricted-shell\";\n\nexport function consoleShellChoice(\n args: readonly string[],\n): ConsoleShellChoice {\n const at = args.indexOf(\"--shell\");\n const named = at === -1 ? undefined : args[at + 1];\n const escaped = args.includes(UNRESTRICTED);\n\n if (at !== -1 && (named === undefined || named.startsWith(\"--\"))) {\n return { ok: false, why: \"`--shell` needs a path to the shell to run.\" };\n }\n if (named !== undefined && escaped) {\n return {\n ok: false,\n why: `\\`--shell\\` and \\`${UNRESTRICTED}\\` disagree about what to run.`,\n };\n }\n if (named !== undefined) return { ok: true, command: named, fenced: true };\n if (escaped) return { ok: true, command: \"/bin/sh\", fenced: false };\n return {\n ok: false,\n /* Names the wiring that is missing, because the box is where this goes\n wrong and a person reading this line is standing in front of one. */\n why:\n \"no console shell configured.\\n\" +\n \"Pass `--shell <path>` — on a hosted box that is `/opt/byollm-box/shell`,\\n\" +\n \"the console fence. For an unfenced shell on your own machine, ask for\\n\" +\n `it: \\`byollm console-agent ${UNRESTRICTED}\\`.`,\n };\n}\n\nasync function commandConsoleAgent(\n paths: DaemonPaths,\n args: readonly string[],\n io: CliIo,\n): Promise<ExitCode> {\n const keys = await new DeviceIdentity(paths.keys).load(Date.now());\n\n const spec = consoleSpecArg(args);\n if (spec === undefined) {\n /**\n * No argument means LISTEN — B018c hole 2.\n *\n * The box holds a control socket and waits to be told a console is\n * wanted. Before this the command took a session description and nothing\n * ever gave it one: a console could be opened by a browser and never\n * joined, because the box did not know.\n *\n * The one-argument form stays, and is what this mode invokes per\n * announcement — so the path a real console takes is the same one a\n * person can drive by hand when something has gone wrong.\n */\n const pairings = new Pairings(paths.pairings);\n await pairings.load();\n const paired = pairings.list()[0];\n if (paired === undefined) {\n io.err(\n \"byollm console-agent has nothing to listen to: this device is not paired.\\n\" +\n \"Pair it first with `byollm connect`.\\n\",\n );\n return 1;\n }\n\n const shell = consoleShellChoice(args);\n if (!shell.ok) {\n io.err(`byollm console-agent will not open a console: ${shell.why}\\n`);\n return 1;\n }\n\n const url = `${paired.origin.replace(/^http/, \"ws\")}${CONSOLE_DEVICE_ENDPOINT}`;\n io.out(`listening for consoles on ${paired.origin}\\n`);\n /* Said at startup, every time, because the failure this replaces was a\n silent one: nothing anywhere named the shell a console would land in,\n so nothing could disagree with it out loud. */\n io.out(\n shell.fenced\n ? `console shell: ${shell.command}\\n`\n : `console shell: ${shell.command} — UNRESTRICTED, the console fence is off\\n`,\n );\n\n /* One writer for both halves — the listener's and the agent's. It was\n two identical lambdas, which is how the two ends of a console came to\n disagree about an alphabet in the first place. `fields` needs no\n default: spreading `undefined` spreads nothing. */\n const consoleLog = (\n message: string,\n fields?: Record<string, unknown>,\n ): void => {\n io.out(`${JSON.stringify({ console: message, ...fields })}\\n`);\n };\n\n consoleListener({\n url,\n runnerId: paired.runnerId,\n keys,\n run: (announcement) =>\n runConsoleAgent({\n /* The endpoint from one definition, shared with the signer that\n has to name it identically — a URL built here and a signature\n built there, disagreeing by a character, is a 401 nobody can\n read. */\n url: `${paired.origin.replace(/^http/, \"ws\")}${CONSOLE_BOX_ENDPOINT}?session=${announcement.sessionId}`,\n sessionId: announcement.sessionId,\n deadlineAt: announcement.deadlineAt,\n keys,\n /* The data door is authenticated too, and signs over the SESSION —\n a signature captured from one cannot be replayed to join\n another. Without this the dial is refused 401 before the\n handshake, which is what attempt six hit. */\n runnerId: paired.runnerId,\n browser: announcement.browser,\n command: shell.command,\n args: [],\n cwd: process.env[\"HOME\"] ?? \"/\",\n env: { ...process.env } as Record<string, string>,\n record: (entry) => {\n io.out(`${JSON.stringify({ consoleSession: entry })}\\n`);\n return Promise.resolve();\n },\n /* The agent's own half of the picture, on the same stream as the\n listener's — one place to read when a console goes quiet. */\n log: consoleLog,\n }).then(() => undefined),\n log: consoleLog,\n });\n\n /* Returns only when the supervisor stops it: a listener that exited\n immediately would be a box that is never reachable, which is the state\n this command exists to end. */\n await new Promise<void>(() => undefined);\n return 0;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(spec);\n } catch {\n io.err(\"That session description is not JSON.\\n\");\n return 2;\n }\n\n const session = ConsoleAgentSpec.safeParse(parsed);\n if (!session.success) {\n io.err(\"That session description is not one this agent understands.\\n\");\n return 2;\n }\n\n /**\n * The by-hand form needs the pairing too, for the same reason listen mode\n * does: the data door wants a signature, and a signature needs the runner\n * this box is known by.\n *\n * This path exists so \"the path a real console takes is the same one a\n * person can drive by hand when something has gone wrong\" — which is only\n * true if it authenticates the same way. A version that dialled unsigned\n * would fail differently from production and be worse than useless for\n * debugging it.\n */\n const agentPairings = new Pairings(paths.pairings);\n await agentPairings.load();\n const agentPaired = agentPairings.list()[0];\n if (agentPaired === undefined) {\n io.err(\n \"byollm console-agent cannot join a console: this device is not paired.\\n\" +\n \"The hub will not accept an unsigned connection. Pair it first with `byollm connect`.\\n\",\n );\n return 1;\n }\n\n try {\n const why = await runConsoleAgent({\n url: session.data.url,\n sessionId: session.data.sessionId,\n deadlineAt: session.data.deadlineAt,\n keys,\n runnerId: agentPaired.runnerId,\n browser: session.data.browser,\n command: session.data.shell.command,\n args: session.data.shell.args,\n cwd: session.data.shell.cwd,\n env: session.data.shell.env,\n record: (entry) => {\n // The feed is the ruling's item 4, and it is deliberately the plainest\n // thing that cannot fail: a line on stdout, which the box's supervisor\n // already collects. A session that could not be recorded because a\n // writer was unavailable is exactly the session somebody would want.\n io.out(`${JSON.stringify({ consoleSession: entry })}\\n`);\n return Promise.resolve();\n },\n });\n io.out(`${why}\\n`);\n return 0;\n } catch (error) {\n const unavailable = consoleAgentUnavailable(error);\n if (unavailable !== undefined) {\n io.err(`${unavailable}\\n`);\n return 1;\n }\n throw error;\n }\n}\n\n// -- connect -----------------------------------------------------------------\n\n/**\n * Where `byollm connect` goes when nobody says — cloud_010's overnight brief.\n *\n * The reference hub, named once. A daemon that made somebody paste a URL\n * before it would do anything is a daemon whose first minute is a\n * copy-and-paste from a page they have not opened yet — and every one of them\n * would paste the same string.\n *\n * It is a default and not a lock: `byollm connect <url>` still goes wherever\n * it is pointed, which is what keeps the protocol something other people can\n * host. Stated here rather than fetched, because a default that arrives over\n * the network is a default somebody else can move.\n */\nexport const DEFAULT_ORIGIN = \"https://hub.byollm.cloud\";\n\n/**\n * Which upstream `connect` was pointed at — the argument, or the default.\n *\n * Its own function so it can be checked without a network. The test that\n * covered \"no argument means the reference hub\" did it by *running* connect\n * with no argument, which sent a real pair request to the real hub on every\n * run — tolerable while the hub refused that shape in 400ms, and not\n * tolerable once it started answering: the run hung for a full poll and left\n * a pending code in production behind it.\n *\n * The `--name` guard is the whole of the filter and the reason this is\n * fiddly: without checking `named !== -1`, `named + 1` is `0` when there is no\n * `--name`, so the *URL* gets filtered out and every `connect <url>` quietly\n * goes to the default instead. That shipped once and was caught by the\n * integration test connecting to the hub when it had been told otherwise.\n */\nexport function connectTarget(args: readonly string[]): string {\n const named = args.indexOf(\"--name\");\n const positional = args.filter(\n (arg, at) =>\n !arg.startsWith(\"-\") &&\n (named === -1 || (at !== named && at !== named + 1)),\n );\n return positional[0] ?? DEFAULT_ORIGIN;\n}\n\n/**\n * What this machine calls itself, in the order the answers were given.\n *\n * A flag beats a saved name beats the environment beats the hostname. Every\n * layer is somebody being more specific than the last, and the hostname is\n * what a machine says when nobody has said anything — `todd@Todds-Mac-Studio`\n * is a fine answer and a poor label on an approval screen with three of them.\n */\nasync function labelFor(\n paths: DaemonPaths,\n flag: string | undefined,\n): Promise<string> {\n if (flag !== undefined && flag !== \"\") return flag.slice(0, 120);\n try {\n const saved = (await readFile(paths.label, \"utf8\")).trim();\n if (saved !== \"\") return saved.slice(0, 120);\n } catch {\n // No saved name is the ordinary case, not a problem to report.\n }\n return hostLabel();\n}\n\n/**\n * `byollm setup` — byollm_015 Phase 1.\n *\n * Thin on purpose: the conversation lives in `setup.ts` so it can be driven by\n * a test that is not a terminal, and so this file stays a router.\n */\nasync function commandSetup(\n paths: DaemonPaths,\n io: CliIo,\n signal?: AbortSignal,\n): Promise<ExitCode> {\n const terminal = terminalIo(\n (text) => {\n io.out(text);\n },\n (text) => {\n io.err(text);\n },\n );\n try {\n return await setupWith(paths, io, terminal, signal);\n } catch (error) {\n return endedOrThrow(error, io);\n } finally {\n /* An open readline holds stdin, so a command that does not close it does\n not exit — B114. */\n terminal.close();\n }\n}\n\n/**\n * Input that ran out, reported once rather than as a stack trace — B114.\n *\n * Both screens read a blank line as \"keep the default\", so an ended stdin must\n * not arrive as `\"\"`: `byollm setup < /dev/null` would then answer every\n * question with its default, pair the device and install a service, on the\n * strength of a file with nothing in it. It stops instead, and says that is\n * what happened.\n */\nfunction endedOrThrow(error: unknown, io: CliIo): ExitCode {\n if (!(error instanceof InputEnded)) throw error;\n io.err(\n \"\\nStopped: the input ran out before the questions did.\\n\" +\n \" Nothing further was written. Run this again on a terminal, or\\n\" +\n \" edit ~/.byollm/config.json by hand.\\n\",\n );\n return 1;\n}\n\nasync function setupWith(\n paths: DaemonPaths,\n io: CliIo,\n terminal: TerminalIo,\n signal?: AbortSignal,\n): Promise<ExitCode> {\n const result = await runSetup(\n paths,\n terminal,\n undefined,\n undefined,\n undefined,\n undefined,\n /**\n * `connect` and `install`, run in this process — 2026-09-01.\n *\n * Through `runCli` rather than by spawning `byollm` again: a spawn would\n * find whichever binary is on PATH, which on a machine mid-upgrade is not\n * necessarily this one. The wizard finishing the job means *this* build\n * doing it.\n */\n (argv) => runCli([...argv], { paths, io, ...(signal ? { signal } : {}) }),\n );\n return result.wrote || result.services.length > 0 ? 0 : 1;\n}\n\n/**\n * `byollm model` — byollm_017 Phase 1.\n *\n * Three shapes, one verb: no service lists nothing useful and says so; a\n * service alone reports what it runs and what its CLI is known to accept; a\n * service and a name probes, then writes.\n */\nasync function commandModel(\n paths: DaemonPaths,\n args: readonly string[],\n io: CliIo,\n): Promise<ExitCode> {\n const [service, model] = args;\n if (service === undefined) {\n io.err(\n \"usage:\\n\" +\n \" byollm services every service and the model it runs\\n\" +\n \" byollm model <service> one service, and what it accepts\\n\" +\n \" byollm model <service> <name> check that model, then use it\\n\",\n );\n return 2;\n }\n if (model === undefined) {\n const shown = await showModel(paths.config, service, io);\n return shown.code;\n }\n /**\n * The service's own backend, built the way the daemon builds it.\n *\n * This passed `createBackend(id, {})` — the id alone — and an HTTP-class\n * transport throws without its `baseUrl`, so `byollm model <svc> <name>`\n * failed for every Ollama, LM Studio and vLLM service with \"openai-http\n * backend requires a baseUrl\" before the probe could run. The same hole\n * `backendFor` was written to close for `run`; `setup` already goes through\n * it. Read once here, for the verifier and the memory guard both.\n */\n const loaded = await loadConfig(paths.config).catch(() => undefined);\n const configured = loaded?.config.services[service];\n const set = await setModel(\n { configPath: paths.config, service, model },\n io,\n backendVerifier((id) =>\n backendFor({ type: id, baseUrl: configured?.baseUrl }),\n ),\n /**\n * The memory guard on the owner's own command — B106.\n *\n * Wired here for the same reason `readMemory` and `spawnServer` are wired\n * on the Runner and nowhere else: this is the process that is about to\n * load a model, and a seam nobody passes is a guard that does not exist.\n *\n * The configured floor comes from the same config the gate reads for a\n * job — `minAvailableMemoryBytes`, one value, one meaning, whoever asked.\n */\n async (backendId) => {\n if (loaded === undefined) return { ask: false };\n const { memory, pressure } = await readHostMemory();\n return modelLoadQuestion({\n backendId,\n baseUrl: configured?.baseUrl,\n model,\n memory,\n pressure,\n floorBytes: loaded.config.minAvailableMemoryBytes,\n });\n },\n io.confirm,\n );\n return set.code;\n}\n\nasync function commandConnect(\n paths: DaemonPaths,\n args: readonly string[],\n io: CliIo,\n signal?: AbortSignal,\n): Promise<ExitCode> {\n // `--name` may appear before or after the URL: somebody typing this for the\n // first time should not have to learn an argument order.\n const named = args.indexOf(\"--name\");\n const name = named === -1 ? undefined : args[named + 1];\n if (named !== -1 && (name === undefined || name.startsWith(\"-\"))) {\n io.err(\"usage: byollm connect [<url>] [--name <name>]\\n\");\n return 2;\n }\n // Guarded on `named !== -1`, and the guard is the whole of this comment.\n // Without it `named + 1` is `0` when there is no `--name`, so the *URL* is\n // filtered out and every `connect <url>` quietly goes to the default\n // instead. The integration test — the only one that runs the real binary —\n // is what caught it, by connecting to the hub when it had been told\n // otherwise.\n const origin = normalizeOrigin(connectTarget(args));\n\n /**\n * The stored credential IS the device — ruled 2026-09-02 (byollm_016).\n *\n * This used to say \"already paired with …\" and then ask `Pair again?`, and\n * a yes ran the whole ceremony: new code, new approval, new fingerprint\n * compare. Two things were wrong with it.\n *\n * The first is that the ceremony could not succeed. The dashboard inserts a\n * device row keyed on the fingerprint of this machine's identity key, and\n * that key is stable — `DeviceIdentity` mints one only when the file is\n * absent. So re-pairing the same machine hits the unique index, comes back\n * `23505`, and the screen says \"that device's keys are already registered\n * on an account\". The daemon, meanwhile, polls until the code expires. The\n * flow's happy path was a dead end.\n *\n * The second is worse and is why this is ruled rather than tidied: **a\n * ceremony repeated becomes a habit, and a habit is not a comparison.** The\n * fingerprint check is load-bearing precisely because it is rare. Asking\n * for it on every routine re-run trains the person to approve without\n * looking, which is a security regression wearing a UX complaint.\n *\n * So the credential is presented first. Found is not works, so it is\n * *probed* rather than assumed — one real signed heartbeat, which is the\n * same call this daemon makes every ten seconds anyway. The hub recognising\n * it is a session, not a ceremony.\n */\n const existing = await (async () => {\n const pairings = new Pairings(paths.pairings);\n await pairings.load();\n return pairings.get(origin);\n })();\n\n const { loaded, ingress, budgets, spend, spentGrants } = await context(paths);\n\n for (const problem of loaded.problems) {\n io.err(`config: ${problem.where}: ${problem.message}\\n`);\n }\n\n const client = new ProtocolClient({ origin });\n const runner = new Runner({\n client,\n runnerId: \"pending\",\n owner: \"pending\",\n daemonVersion: DAEMON_VERSION,\n loaded,\n budgets,\n spend,\n spentGrants,\n ingress,\n });\n\n // Daemon start: the one place a canary runs. `#tick()` calls this with no\n // options, so the polling loop never spends a call.\n const capabilities = await runner.detectCapabilities({ canary: true });\n // The daemon is the process that probes; `byollm status` is the process that\n // reports. This is the only thing that connects them.\n await writeServiceStates(paths.serviceStates, runner.serviceStates);\n\n /**\n * Zero healthy backends is a warning, not a refusal — cloud_002, ruled\n * 2026-08-21.\n *\n * This used to return 1. The stated reason was that \"pairing while\n * advertising nothing would produce a runner that silently never receives\n * work\", and the invariant does not hold: a paired daemon whose model server\n * dies an hour later is already in exactly that state, and the daemon\n * handles it by not advertising. The refusal guarded t=0 only.\n *\n * The real requirement is never to *silently* receive no work, and the\n * answer to silence is loudness. So identity first, capability second: the\n * fingerprint comparison is the moment that matters and the machine\n * appearing on the page is the reward; the model server is the follow-up.\n * The runtime already declines to claim at zero capabilities, so nothing\n * routes here until a route goes healthy — and then it starts on its own,\n * with no second pairing.\n */\n /**\n * What the probe learned, not how many survived it.\n *\n * \"0 backends are healthy\" is true of a machine with no CLI installed and\n * of a machine whose subscription token expired last week, and those want\n * opposite actions from the person reading it. The canary already knew\n * which — it ran, it failed, the route was dropped — and this sentence\n * threw the answer away, so somebody paired a machine, watched it advertise\n * nothing, and had to go and find out why from a job that failed later.\n */\n {\n const lines = renderServices(\n runner.serviceStates,\n await labelFor(paths, name),\n );\n if (lines.length > 0) io.out(`\\nservices\\n${lines.join(\"\\n\")}\\n`);\n }\n // Kept for `byollm status`, which is a different process and must not spend\n // a model call of its own to answer \"how are my services\".\n await writeServiceStates(paths.serviceStates, runner.serviceStates);\n\n if (capabilities.length === 0) {\n io.err(\n \"\\nNothing will route to this device yet.\\n\" +\n \"Pairing anyway — work starts arriving on its own once a service can\\n\" +\n \"answer. `byollm status` says where each one stands.\\n\",\n );\n }\n\n /**\n * Present the stored credential, and see whether the hub still knows it.\n *\n * Placed after detection so the heartbeat carries this device's real\n * capabilities: a reconnect that also refreshes what the hub knows is worth\n * more than one that only says hello, and `connect` has already paid for\n * the probe by this point.\n *\n * The three outcomes are deliberately not two.\n *\n * - Accepted: a session. Print what it reconnected as and stop. Nothing on\n * the dashboard changes, and no fingerprint is compared, because nothing\n * about this device's identity is new.\n * - Refused by name — revoked, or a runner this hub does not know: the\n * credential is genuinely spent, and pairing is the right next step. Said\n * out loud first, because \"we are pairing again\" needs a reason.\n * - Could not ask: **not a refusal.** An unreachable hub, a timeout, a\n * clock too far off. Falling through to a ceremony here would ask\n * somebody to re-approve a machine over a wifi problem, and would mint a\n * pairing attempt that cannot land anyway. This is the same law as the\n * control plane's polls and the release gate's exit 4: an unreadable\n * answer is not a negative answer.\n */\n if (existing !== undefined) {\n const identity = new DeviceIdentity(paths.keys);\n const asStored = client.withIdentity({\n runnerId: existing.runnerId,\n sign: (input) => identity.signRequest(input),\n });\n io.out(`\\nChecking this device's existing connection to ${origin}\\n`);\n try {\n await asStored.heartbeat({\n runnerId: existing.runnerId,\n daemonVersion: DAEMON_VERSION,\n capabilities,\n activeLeases: [],\n /* Required by HeartbeatRequest, which is `.strict()`, so it is sent\n and not dropped: a daemon that stops sending a required field is\n rejected by every hub that has not been upgraded in lockstep. The\n field itself dies at the 0.1.0 protocol cut with the aliases —\n B044 — where one publish moves both sides at once. */\n });\n const when = new Date(existing.pairedAt).toISOString().slice(0, 10);\n io.out(\n `\\n Connected as ${await labelFor(paths, name)} (paired ${when}).\\n` +\n ` Nothing to approve — this device was already known here.\\n`,\n );\n return 0;\n } catch (error) {\n const spent =\n error instanceof ClientError &&\n (error.kind === \"revoked\" || error.kind === \"unauthorized\");\n if (!spent) {\n const detail = error instanceof Error ? error.message : String(error);\n io.err(\n `\\n Could not check this device's connection to ${origin}.\\n` +\n ` ${detail}\\n\\n` +\n `${wrap(\n \" This device is still paired and nothing was changed. That is \" +\n \"not the same as being refused, so it has not been re-paired: \" +\n \"try again when the connection is back.\",\n )}\\n`,\n );\n return 1;\n }\n io.out(\n `\\n${wrap(\n ` This device's pairing with ${origin} is no longer accepted ` +\n `(${error instanceof ClientError ? error.kind : \"refused\"}), so ` +\n `it needs approving again.`,\n )}\\n`,\n );\n }\n }\n\n io.out(`\\nConnecting to ${origin}\\n`);\n\n /**\n * An unreachable hub is a sentence, not a stack trace.\n *\n * `connect` used to return before this line whenever no backend was healthy,\n * so the unreachable-upstream path was mostly unreached — and it throws a\n * `ClientError` that nothing caught. Now that pairing proceeds at zero\n * capabilities, this is the ordinary failure for somebody offline, on a\n * captive-portal wifi, or pointed at a hub that is down.\n */\n /**\n * This machine's own fingerprint, printed *with* the code.\n *\n * The approval screen says \"this must match the fingerprint `byollm connect`\n * printed on that machine\" — and it did not print one. Todd found it the\n * only way anybody could: standing at the screen with nothing to compare\n * against.\n *\n * A comparison with one side missing is not a weaker ceremony, it is\n * theatre: the person clicks approve because the flow expects them to, and\n * the check the whole trust model rests on has quietly become a formality.\n * The keys are already in hand here — this is the moment to say so.\n */\n const deviceIdentity = await new DeviceIdentity(paths.keys).publicIdentity(\n Date.now(),\n );\n\n let result: Awaited<ReturnType<typeof connect>>;\n try {\n result = await connect({\n client,\n daemonVersion: DAEMON_VERSION,\n label: await labelFor(paths, name),\n capabilities,\n device: deviceIdentity,\n onCode: (info) => {\n const minutes = Math.max(\n 1,\n Math.round((info.expiresAt - Date.now()) / 60_000),\n );\n /**\n * Numbered steps, because this is the one moment the daemon needs\n * somebody to go and do something.\n *\n * It printed a URL and a code as two labelled values, which reads as\n * status rather than as instruction — Todd, who designed the flow,\n * watched his own code expire waiting for the terminal to do\n * something. If the author sits still, everybody sits still.\n *\n * \"Enter\" rather than \"find the button\": where the code goes is the\n * dashboard's problem, and the approval URL carries whatever it needs\n * to open on the right screen. A step here naming a button would be a\n * step that goes stale the first time the page is redesigned.\n *\n * The code itself is highlighted — ux 09-03. Numbering the steps was\n * not enough on its own: step 2 holds the only value somebody has to\n * carry to another window, and it sat in a line that looked exactly\n * like the two around it. The emphasis wraps the code and not the\n * label, and it is absent the moment this is not a terminal, so a\n * piped transcript still holds a code somebody can paste.\n */\n io.out(\n `\\n Your steps:\\n` +\n ` 1) Open: ${info.verificationUrl}\\n` +\n ` 2) Enter code: ${emphasise(info.userCode, terminalContext())} ` +\n `(expires in ${String(minutes)} minutes)\\n` +\n ` 3) Check the screen shows this device's fingerprint, ` +\n `then approve:\\n\\n` +\n ` ${fingerprint(deviceIdentity.identity)}\\n\\n` +\n ` waiting for approval…`,\n );\n },\n onPoll: () => {\n io.out(\".\");\n },\n ...(signal === undefined ? {} : { signal }),\n });\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n io.err(\n `\\n Could not pair with ${origin}.\\n ${detail}\\n\\n` +\n ` Check the URL and your connection, then try again. Nothing was ` +\n `changed on this device.\\n`,\n );\n return 1;\n }\n\n if (!result.ok) {\n io.out(`\\n\\n ${result.message}\\n`);\n return 1;\n }\n\n const pairings = new Pairings(paths.pairings);\n await pairings.load();\n reportSkipped(pairings, io);\n await pairings.put(result.pairing);\n\n /**\n * And the supervisor hears about it too — B207.\n *\n * Pairing is the fact the box supervisor now keys its retry on: an unpaired\n * daemon is left idle rather than respawned every minute. So `connect` has\n * to speak as loudly as `setup` does, or a device paired this way would sit\n * idle until something else happened to signal.\n */\n tellSupervisor();\n\n /**\n * The mark is cleared by the thing that fixes it.\n *\n * A revoked device that re-pairs is not revoked, and a mark that outlived\n * its own remedy would be a machine told it cannot serve while it serves —\n * the same class of lie as a status line that outlives the consent it\n * reports.\n *\n * Written through the health file's own writer so the rest of the record\n * survives: this clears one field, it does not reset what the daemon knows\n * about its upstream.\n */\n await clearRevokedMark(paths.health);\n\n io.out(` paired as ${result.pairing.ownerLabel ?? result.pairing.owner}\\n`);\n // The keys this machine just pinned, printed where somebody can still\n // compare them. The approval happened in a browser, so this is the first\n // moment the two ends of that decision are visible on the same screen —\n // and a pin nobody can see is a pin nobody checks. Sites that arrive\n // *later* get the same treatment through `byollm sites`, where they wait\n // for an answer rather than being pinned (V1-1).\n // One line, not two. A site's id in this file *is* its fingerprint —\n // `keyId` and `fingerprint` are the same function, and `runner.ts` refuses\n // any entry where they disagree — so printing both stuttered the same\n // string twice and read as two facts to check against each other.\n //\n // Derived from the pinned key rather than taken from the map key: the value\n // on screen should be computed from the material it describes, so what\n // somebody compares by eye is the key itself and not a label beside it.\n /**\n * Labelled, and labelled as *not this device* — ruled 2026-09-03.\n *\n * These printed bare, indented, immediately under \"paired as <id>\". Todd\n * read them as a second fingerprint for this machine and asked what he was\n * meant to compare them against.\n *\n * Nothing, is the answer: they are the keys of the sites this pairing\n * covers. But the question was the right one to ask, on the one screen\n * where a fingerprint comparison is the whole security model. An unlabelled\n * fingerprint there invites a comparison nobody defined, and a person who\n * compares the wrong pair once and finds no match learns that the\n * comparison is noise.\n *\n * `pinned:` is the word `byollm status` already uses for the same list —\n * one vocabulary — and the heading says whose keys these are.\n */\n const pinned = Object.values(result.pairing.sites);\n if (pinned.length > 0) {\n io.out(\n ` sites this pairing covers — site keys, not this device's ` +\n `fingerprint:\\n`,\n );\n for (const site of pinned) {\n io.out(` pinned: ${fingerprint(site.identity)}\\n`);\n }\n }\n /* Nothing pinned yet is the normal first install: you pair before you have\n connected anything, because there is nothing to connect a site to\n beforehand. Said out loud so the empty list reads as a step remaining\n rather than as something having gone wrong — and so nobody waits for work\n that has no reason to arrive. */\n if (Object.keys(result.pairing.sites).length === 0) {\n io.out(\n ` no sites yet — connect one in your dashboard and its first job\\n` +\n ` will arrive here, with its fingerprint, for you to see.\\n`,\n );\n }\n /**\n * Pairing is a ceremony, not a service — ruled 2026-09-01.\n *\n * This ended by calling `runLoop`, so `byollm connect` pinned the keys and\n * then sat in the foreground forever running jobs. Two costs, and the\n * second is the one that mattered on a walk:\n *\n * Somebody who ran it in a terminal they then closed had a device that was\n * paired and not running, with nothing on screen having said the two were\n * different things. And somebody who left it open had a \"daemon\" that\n * survived exactly as long as that window — no supervisor, no restart, no\n * log — which looks identical to a healthy install until the laptop sleeps.\n *\n * Running is `run`'s job in the foreground and `install`'s as a service.\n * So this ends, and says which of the two to do next.\n */\n io.out(\n `\\nPaired. Nothing is running yet — pairing and running are separate:\\n` +\n ` byollm start keep it running in the background (recommended)\\n` +\n ` byollm run run in this terminal, Ctrl-C to stop\\n`,\n );\n\n return 0;\n}\n\n// -- run ---------------------------------------------------------------------\n\nasync function commandRun(\n paths: DaemonPaths,\n args: readonly string[],\n io: CliIo,\n signal?: AbortSignal,\n pollMs = PARK_POLL_MS,\n /* Both from one answer — B213. `isTTY` is true inside a box because the Pod\n gives the console a tty, so these two expressions had the supervised\n daemon asking a human who was not there and reporting itself unsupervised.\n See {@link howItRuns}. */\n supervised = howItRuns().supervised,\n interactive = howItRuns().interactive,\n preflightOptions: {\n platform?: NodeJS.Platform;\n verify?: (config: {\n readonly type: BackendId;\n readonly model: string;\n readonly baseUrl?: string | undefined;\n }) => Promise<{\n readonly answers: boolean | undefined;\n readonly detail?: string | undefined;\n }>;\n login?: (command: LoginCommand) => Promise<boolean>;\n ask?: (question: string) => Promise<string>;\n } = {},\n): Promise<ExitCode> {\n /**\n * No url — byollm_020.\n *\n * `run <url>` meant \"serve only this one app\", which nobody wanted and\n * everybody misread: the url in `connect <url>` is where you pair, and the\n * same shape here read as \"point the daemon at this address\". Two commands\n * taking a url, one of which does not mean what it looks like, is the\n * confusion this audit is for. Serving every paired app is the whole job.\n */\n if (args.length > 0) {\n io.err(\n `byollm run takes no arguments, and got ` +\n `${args.map((arg) => JSON.stringify(arg)).join(\" \")}.\\n\\n` +\n ` byollm run serve every app this device is paired with\\n` +\n ` byollm connect ${args[0] ?? \"<url>\"} pair with an app\\n` +\n ` byollm sites which sites this device serves\\n`,\n );\n return 2;\n }\n\n /**\n * Asked before anything serves — B047, the half `run` did not have at all.\n *\n * `signedOutLines` had exactly one caller and it was `start`, so somebody\n * running the foreground daemon — which on Windows is the path that\n * reliably works — got no signed-out surface whatsoever. Kevin ran it\n * signed out and watched a serving line for services that could not serve.\n *\n * Outside the loop: the loop re-enters on re-pairing, and asking again\n * there would be asking a question nobody's answer had changed.\n */\n await preflightFor(paths, io, interactive, preflightOptions);\n\n /*\n * Re-entered, not run once, so that re-pairing can end a park.\n *\n * The pairings are read inside the loop for the same reason: the file is\n * how another process hands this one the news, and a parked daemon that\n * kept its first reading would come back to serve exactly the nothing it\n * parked over.\n */\n for (;;) {\n const pairings = new Pairings(paths.pairings);\n await pairings.load();\n reportSkipped(pairings, io);\n\n const origins = pairings.list().map((pairing) => pairing.origin);\n\n const outcome =\n origins.length === 0\n ? // The branch Todd's service log repeated all evening, for a machine\n // that was paired and had been revoked. It is the *first* of two\n // that can find nothing to serve, and it was the one being reached\n // — which is why the decision lives in one function now rather than\n // beside each `return`.\n await nothingToServe(paths, io, signal, supervised, [], pollMs)\n : await runLoop(paths, origins, io, signal, supervised, pollMs);\n\n if (outcome !== \"repaired\") return outcome;\n io.err(\"re-paired — this device is returning to service.\\n\");\n }\n}\n\n/**\n * Say so when a pairing row could not be read — cloud_008 §2.3a.\n *\n * `load` skips what it cannot parse instead of discarding the whole file, and\n * a skip nobody mentions is the same silence in a smaller box: the user would\n * see one connection missing and no reason for it. Named on stderr, by origin\n * and failing field, never by value — a pairing row holds pinned keys.\n */\nfunction reportSkipped(pairings: Pairings, io: CliIo): void {\n for (const row of pairings.skipped) {\n io.err(\n `could not read the pairing for ${row.origin} (${row.problem}) — ` +\n \"it was skipped; re-pair with `byollm connect` to restore it\\n\",\n );\n }\n // Ours to explain, not theirs to fix — so `out`, not `err`.\n for (const notice of pairings.retired) {\n io.out(`note: ${notice}\\n`);\n }\n}\n\n/**\n * The allowlist file, retired out loud — Amendment I.\n *\n * A file full of names this device used to honour, that it now ignores, is\n * the worst possible state to leave silently: the entries stay on disk\n * reading like grants, and the person who wrote them has no way to learn they\n * stopped meaning anything. Pre-1.0 gives us the liberty to delete the\n * machinery; it does not give us the liberty to delete it quietly.\n *\n * So this reads what is there, says whose access ended and where that\n * decision lives now, and removes the file — once. Reported at `status` and\n * at the start of a run, which are the two places somebody is looking.\n *\n * It names the people. \"3 entries were retired\" is a count; the point of the\n * notice is that somebody can recognise a name and go re-add them in the one\n * place that can now authorise it.\n */\nasync function retireAllowlist(paths: DaemonPaths, io: CliIo): Promise<void> {\n let raw: string;\n try {\n raw = await readFile(paths.allowlist, \"utf8\");\n } catch {\n return;\n }\n\n const names = new Set<string>();\n try {\n const parsed: unknown = JSON.parse(raw);\n const entries = (parsed as { entries?: unknown }).entries;\n if (Array.isArray(entries)) {\n for (const entry of entries as { owner?: unknown; origin?: unknown }[]) {\n if (\n typeof entry.owner === \"string\" &&\n typeof entry.origin === \"string\"\n ) {\n names.add(\n `${stripControlChars(entry.owner)} on ${stripControlChars(entry.origin)}`,\n );\n }\n }\n }\n } catch {\n // Unreadable is still retired. The file goes either way, and saying so\n // without a list is better than saying nothing because a parse failed.\n }\n\n io.out(\n `\\n${wrap(\n \"note: this device used to keep its own list of people allowed to use \" +\n \"it. That list is gone — it could never check the names on it, so it \" +\n \"only ever agreed with whoever was asking.\",\n )}\\n`,\n );\n for (const name of [...names].sort()) {\n io.out(` no longer allowed here: ${name}\\n`);\n }\n io.out(\n `${wrap(\n names.size > 0\n ? \"Add them again from your team page, where a relay can actually \" +\n \"verify who they are.\"\n : \"Membership lives with your relay now, and arrives one signed grant \" +\n \"at a time.\",\n )}\\n`,\n );\n await rm(paths.allowlist, { force: true });\n}\n\n/**\n * What a run ended as: an exit code, or the news that it should start again.\n *\n * `\"repaired\"` is not an exit. A parked daemon that sees its revocation\n * undone has to go back and build the runners it never built, and the only\n * honest way to say that in a return type is a value that is not a code.\n */\ntype ServeOutcome = ExitCode | \"repaired\";\n\n/**\n * There is nothing to serve, and why that is decides everything after it.\n *\n * Two call sites reach this — no pairings at all, and pairings that all\n * skipped — and before tonight each wrote its own sentence. One of them said\n * \"No apps are paired yet\" to a machine that was paired and revoked. Todd read\n * it on repeat in his service log while launchd restarted the daemon behind\n * it.\n *\n * ## Three decisions, and they are separable\n *\n * **What is true.** Empty-because-revoked and empty-because-never-paired are\n * different facts. Both remedies happen to be `connect`, which is exactly why\n * the wrong sentence survived so long: it sent people somewhere useful by\n * luck, and read as \"your setup never finished\" to somebody whose setup\n * finished weeks ago.\n *\n * **What to say.** The ruled sentence for the first, the connect instructions\n * for the second.\n *\n * **Whether to exit.** Exiting invites a supervisor to start us again, and\n * launchd obliges every ten seconds: boot, refused, exit, boot — each turn a\n * request the hub answers only to say no again. Revocation is not transient\n * and no backoff makes hammering it correct, so under a supervisor the\n * process stays up, marked, serving nothing. Interactively it exits, because\n * somebody who typed `byollm run` wants their prompt back and a command that\n * hangs after explaining itself is its own small cruelty.\n */\nasync function nothingToServe(\n paths: DaemonPaths,\n io: CliIo,\n signal?: AbortSignal,\n supervised = !process.stdout.isTTY,\n /** What we were asked to serve; empty means \"whatever is paired\". */\n origins: readonly string[] = [],\n pollMs = PARK_POLL_MS,\n): Promise<ServeOutcome> {\n const mark = await revokedMark(paths.health);\n if (mark === undefined) {\n io.err(\n \"No app is paired, so there is nothing to serve — this device will \" +\n \"appear on rosters and answer nothing.\\n\" +\n \" byollm connect <url> pair this device\\n\" +\n \" byollm status what this device believes right now\\n\",\n );\n return 2;\n }\n\n io.err(\n `${REVOKED_SENTENCE}.\\n` +\n ` ${mark.origin} no longer accepts this device's credential, so ` +\n `there is nothing to serve.\\n` +\n revokedRemedy(mark.origin, supervised),\n );\n if (!supervised) return 2;\n return (await parkedUntilRepaired(paths, origins, signal, pollMs))\n ? \"repaired\"\n : 0;\n}\n\n/**\n * How often a parked daemon asks whether the remedy has been applied.\n *\n * Slow is about not spinning, and nothing else: this reads two local files\n * and never speaks to the hub, so the reason the rest of this daemon backs\n * off — do not hammer somebody else's server — has no force here. What sets\n * the number is the person: they run `byollm connect`, they are told the\n * service comes back on its own, and this is how long they watch a dark\n * machine before that sentence starts to look like a lie.\n */\nconst PARK_POLL_MS = 15_000;\n\n/** Resolves `true` if the wait was cut short by the signal. */\nfunction sleepOrAbort(ms: number, signal?: AbortSignal): Promise<boolean> {\n return new Promise((resolve) => {\n if (signal?.aborted === true) {\n resolve(true);\n return;\n }\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve(false);\n }, ms);\n // Declared after the timer it clears, and used only from a callback that\n // cannot run before this line — the alternative is a `let` that lint\n // rightly objects to.\n const onAbort = (): void => {\n clearTimeout(timer);\n resolve(true);\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\n/**\n * Wait, parked, until the revocation is undone — or until we are stopped.\n *\n * The gap this closes: a revoked daemon under a supervisor stayed up holding\n * nothing but its abort signal. `byollm connect` runs in a *different*\n * process; it clears the mark and writes the pairing, says \"paired\", and had\n * no way at all to reach the waiter. The machine stayed dark until somebody\n * restarted it by hand, having been given no reason to think they needed to.\n *\n * **A poll may promote, never demote** — the rule the hub's pollers already\n * follow, turned on the daemon's own state. This only ever moves parked to\n * serving. It cannot park a serving daemon, and a read that fails is not an\n * answer: `revokedMark` returning `undefined` on an unreadable health file\n * would be indistinguishable from a cleared mark, so promotion also requires\n * a pairing to be present and readable. Two facts have to agree before this\n * wakes anything up.\n *\n * Returns `true` when the caller should go back and serve.\n */\nasync function parkedUntilRepaired(\n paths: DaemonPaths,\n origins: readonly string[],\n signal: AbortSignal | undefined,\n pollMs: number,\n): Promise<boolean> {\n for (;;) {\n if (await sleepOrAbort(pollMs, signal)) return false;\n if ((await revokedMark(paths.health)) !== undefined) continue;\n\n const pairings = new Pairings(paths.pairings);\n await pairings.load();\n // An empty `origins` is `byollm run` with no argument — any pairing will\n // do, because any pairing is what it would have served.\n const served = pairings\n .list()\n .some(\n (pairing) => origins.length === 0 || origins.includes(pairing.origin),\n );\n if (served) return true;\n }\n}\n\nasync function runLoop(\n paths: DaemonPaths,\n origins: readonly string[],\n io: CliIo,\n signal?: AbortSignal,\n /**\n * Is something restarting us if we exit?\n *\n * A TTY, because that is the difference that matters and it needs no\n * configuration to be true on machines installed before this existed:\n * launchd, systemd and Task Scheduler all run us without one, and a person\n * at a prompt always has one. The cost is that `byollm run | tee log`\n * interactively looks supervised — it would wait rather than return, on a\n * revoked machine, which is the same thing the supervisor case wants and a\n * Ctrl-C away from over.\n *\n * Injected so the tests can be either.\n */\n supervised = !process.stdout.isTTY,\n pollMs = PARK_POLL_MS,\n): Promise<ServeOutcome> {\n /* One line, only when the state directory has actually moved — B205 item 2.\n Silent on a box (the supervisor states BYOLLM_HOME to this same default)\n and on any normal machine, so it speaks only in the case that is a bug. */\n const moved = overriddenRootNotice(paths.root);\n if (moved !== undefined) io.out(`${moved}\\n`);\n\n const { loaded, ingress, budgets, spend, spentGrants } = await context(paths);\n const identity = new DeviceIdentity(paths.keys);\n const pairings = new Pairings(paths.pairings);\n await pairings.load();\n reportSkipped(pairings, io);\n await retireAllowlist(paths, io);\n\n const controller = new AbortController();\n signal?.addEventListener(\n \"abort\",\n () => {\n controller.abort();\n },\n { once: true },\n );\n /*\n * An abort that already happened fires no listener.\n *\n * Registering was the whole of the wiring, so a signal aborted before this\n * line — a caller that stops us during startup, or the re-entry after a\n * park, where the stop can land between the decision to serve and the\n * setup that serves — started a full polling loop that nothing could ever\n * stop. `AbortSignal` is a latch, not an event: it has to be read as well\n * as subscribed to.\n */\n if (signal?.aborted === true) controller.abort();\n const runners: Runner[] = [];\n /**\n * The newest version the update authority has named — B053, B360.\n *\n * Only built when the owner turned updates on: an offer to a machine that\n * will not take one is not worth a line in its log.\n */\n const inbox = loaded.config.autoUpdate\n ? offerInbox({\n authority: normalizeOrigin(\n loaded.config.updateAuthority ?? DEFAULT_ORIGIN,\n ),\n hasControlPlane: (origin) =>\n pairings.get(origin)?.controlPlanePublic !== undefined,\n report: (line) => {\n io.err(`update: ${line}\\n`);\n },\n })\n : undefined;\n\n for (const origin of origins) {\n const pairing = pairings.get(origin);\n if (!pairing) {\n io.err(`not paired with ${origin}\\n`);\n continue;\n }\n const runner = new Runner({\n client: new ProtocolClient({\n origin,\n identity: {\n runnerId: pairing.runnerId,\n sign: (input) => identity.signRequest(input),\n },\n }),\n runnerId: pairing.runnerId,\n owner: pairing.owner,\n // What this pairing pinned, if it pinned one — Amendment G. A pairing\n // made before roster sync existed has none, so it holds no roster and\n // says so; re-pairing is what installs it.\n ...(pairing.controlPlanePublic === undefined\n ? {}\n : { controlPlanePublic: pairing.controlPlanePublic }),\n // Only the long-running daemon records health. The short-lived commands\n // that also build a Runner — `connect`, `services` — would otherwise\n // write a count from one attempt, which says nothing about how the\n // daemon that actually runs is getting on.\n healthPath: paths.health,\n heartbeatPath: paths.heartbeat,\n // Written whenever it changes, not only at start — S2. `byollm status`\n // is a different process and this file is the whole of what reaches it.\n onServiceStates: (states) =>\n writeServiceStates(paths.serviceStates, states),\n identity: {\n keys: () => identity.load(Date.now()),\n // What was on disk. The runner replaces it from each heartbeat and\n // the loop below writes changes back — cloud_009 §5.\n sites: new Map(Object.entries(pairing.sites)),\n // And what was ever approved, which is what a re-offered id is\n // compared against — V1-1.\n known: new Map(Object.entries(pairing.known ?? {})),\n },\n daemonVersion: DAEMON_VERSION,\n loaded,\n budgets,\n spend,\n spentGrants,\n ingress,\n /**\n * The memory guard, wired here and nowhere else — B080.\n *\n * Same rule as `spawnServer`: only the daemon that runs jobs gets one,\n * because `connect`, `services` and `status` build runners to ask\n * questions and none of them should be spawning `vm_stat` to do it.\n *\n * This is the line that makes the guard exist. Without it `memoryGate`\n * is a function with tests and no reader — which is the state B080 sat\n * in for two commits, and the state `spawnServer` was still in until\n * B092 wired it directly below.\n */\n readMemory: readHostMemory,\n /**\n * On-demand start, finally connected to something — B092.\n *\n * `ensureLocalServer` has had no production caller since B050 built\n * it: the seam existed, the start command existed, and nothing ever\n * passed this. So a configured-but-stopped server was advertised\n * (B056) and then never started, which is the claimed-then-failed job\n * B087 stopped by refusing to advertise it at all.\n *\n * **The seam being absent by default is right and stays.** `status`,\n * `connect` and `services` each build a Runner to ANSWER something,\n * and none of them may launch a process as a side effect of being\n * asked. What was missing was passing it in the one place that should\n * have it: the long-running daemon, here.\n *\n * Three things had to be true before this was safe, and now are.\n * The memory guard refuses BEFORE the start rather than after, which\n * is the check that was missing on 09-08. B087 makes advertising\n * self-heal, so this one line restores B056's ruling with nothing\n * else to remember. And B079's headline — \"byollm now starts a local\n * model server when a job needs one\" — becomes true here, which is\n * why the sentence and this line ship in the same release.\n *\n * **Detached and unreferenced, with no stdio.** A model server is not\n * this daemon's child in any sense that matters: it outlives a daemon\n * restart the way it would if its owner had started it, and holding\n * its pipes would mean a full output buffer could block the process\n * that is supposed to be serving jobs. `startCommandFor` supplies a\n * hardcoded argv and nothing from a job reaches it\n * ({@link MUSTS.NO_PAYLOAD_ROUTING}), so there is no shell here and\n * nothing to quote.\n */\n spawnServer: (command) => {\n spawnLocalServer(command, (message) => {\n io.err(`${message}\\n`);\n });\n },\n onEvent: (event) => {\n report(origin, event, io);\n /* B360: which site said it decides whether it counts — see\n `offerInbox`. It used to be whichever site spoke last. */\n if (event.type === \"update-offered\")\n inbox?.receive(origin, event.version);\n // The set follows consent, and the file follows the set — cloud_009\n // §5. Not awaited, for the reason the revocation branch below gives:\n // an event handler that throws takes the runner with it, and a file\n // that cannot be written is worth a message rather than a crash.\n if (event.type === \"heartbeat\") {\n void recordSites(pairings, origin, runner.sites, {\n known: runner.known,\n })\n .then(async () => {\n // Then read the file back, because a re-pair may have happened\n // in another terminal. `byollm connect` cannot call into a\n // running loop, so it arrives the only way one process can hand\n // something to another here — through the file both share.\n //\n // This used to carry approvals through the same door. There are\n // no approvals any more (Amendment K); the door stays for the\n // one thing pairing still produces.\n await pairings.load();\n const fresh = pairings.get(origin);\n if (fresh?.controlPlanePublic !== undefined) {\n runner.adoptControlPlaneKey(fresh.controlPlanePublic);\n }\n })\n .catch((error: unknown) => {\n io.err(\n `could not record the site list for ${origin}: ` +\n `${error instanceof Error ? error.message : \"unknown error\"}\\n`,\n );\n });\n }\n\n // Revocation drops the pairing — cloud_008 §2.3, finding 24.\n //\n // The daemon stopped and said so, and left the pinned site key on\n // disk. So `byollm run` came back tomorrow and tried to reconnect to\n // a site that had withdrawn consent: refused, correctly, but the\n // machine still held a key for a relationship that had ended, and the\n // user's own `byollm list` still showed the pairing.\n //\n // Revocation is the site saying the relationship is over. Making that\n // durable on this side is the daemon's half of\n // `REVOCATION_IMMEDIATE`, and re-connecting is a re-pair — which is a\n // consent screen, which is the point.\n //\n // Not awaited: an event handler that throws would take down a runner\n // that has already stopped, and a pairing file that cannot be written\n // is worth a message rather than a crash.\n /**\n * Mark, never destroy — ruled 2026-09-03.\n *\n * This deleted the pairing from disk. That turned a wrong server\n * answer into local data loss, and the bug that produced this ruling\n * is the proof that the answer can be wrong: an owner-scoped guard\n * refused devices that had never been revoked, and each one deleted\n * its own pairing on the way down. A device paired thirty seconds\n * earlier lost its pins and its owner lost the evidence.\n *\n * Deleting bought no safety. Enforcement lives where the authority\n * is: the hub refuses a revoked device whatever this file remembers,\n * so the local copy protects nothing and its absence explains\n * nothing. `byollm forget` still exists for somebody who means it.\n *\n * The runner has already stopped serving by the time this fires —\n * `#revokedBy` sets `#stopped` and cancels everything — so what is\n * left to do is say so.\n */\n if (event.type === \"revoked\") {\n io.out(\n `${new URL(origin).host} says this device was revoked. ` +\n `It has stopped serving.\\n` +\n ` The pairing is kept, not deleted — re-pair to return:\\n` +\n ` byollm connect ${origin}\\n`,\n );\n }\n },\n });\n runners.push(runner);\n }\n\n if (runners.length === 0) {\n // Every origin that got here was skipped for a reason already said out\n // loud above; `nothingToServe` supplies the consequence and decides\n // whether exiting would hand the supervisor a loop.\n return nothingToServe(paths, io, signal, supervised, origins, pollMs);\n }\n\n // Leases are released on the way out, so the app sees work return to the\n // queue at once instead of waiting for a lease to lapse.\n // Only take over the process's signals when nobody handed us one of their\n // own — an embedder (or a test) that passed a signal owns its own lifecycle.\n if (signal === undefined) {\n const stop = (): void => {\n controller.abort();\n // Ctrl-C must end in an exit, whatever the release path does. Without a\n // rejection handler a failing `shutdown` left `process.exit` unreached\n // and killed the daemon on an unhandled rejection instead — the same\n // exit, with a stack trace and a misleading code.\n Promise.all(runners.map((runner) => runner.shutdown(\"shutdown\"))).then(\n () => {\n process.exit(0);\n },\n (error: unknown) => {\n // Leases will lapse on their own; say what happened and leave a\n // non-zero code so a supervisor can tell this apart from a clean\n // stop.\n io.err(`shutdown did not complete cleanly: ${String(error)}\\n`);\n process.exit(1);\n },\n );\n };\n process.on(\"SIGINT\", stop);\n process.on(\"SIGTERM\", stop);\n }\n\n /**\n * Say it is working, on the surface where nobody else will — B037.\n *\n * `byollm run` printed nothing at all on start. On Windows especially,\n * where it is the path that reliably works, a person ran it and watched an\n * empty terminal: no way to tell \"serving\" from \"hung\" from \"silently\n * failed\", so the honest reaction was to assume the worst and stop.\n *\n * The supervised daemon has always logged its state; this is the same\n * sentence on the foreground path, which is the one somebody is actually\n * looking at. It names the device because that is the word the dashboard\n * uses, and says where the enabling happens, because the next question\n * after \"is it running\" is always \"then why is nothing arriving\".\n */\n io.out(\n `serving as ${await labelFor(paths, undefined)} — ` +\n `${describeOrigins(origins)}.\\n` +\n `Which sites may send work is enabled from your dashboard. ` +\n `Ctrl-C to stop.\\n`,\n );\n\n await ingress.applyRetention(Date.now());\n\n /**\n * Take an offered update, if this machine was told to — B053.\n *\n * Raced against the runners rather than checked between ticks: `run()` is\n * a long-lived promise and there is no between. The watcher resolves only\n * when an update has been taken, so on every other path this is the same\n * `await` it always was.\n */\n const updated = inbox\n ? watchForUpdate({\n runners,\n io,\n signal: controller.signal,\n offered: inbox.current,\n })\n : new Promise<boolean>(() => undefined);\n\n const tookUpdate = await Promise.race([\n Promise.all(runners.map((runner) => runner.run(controller.signal))).then(\n () => false,\n ),\n updated,\n ]);\n controller.abort();\n await Promise.all(runners.map((runner) => runner.shutdown(\"shutdown\")));\n if (tookUpdate) {\n /* Exit so the supervisor starts the new binary. This process is the old\n one — it cannot become the new one, and a daemon that installed an\n update and went on running would report a version it is not. */\n io.err(\"updated — exiting so the supervisor starts the new version.\\n\");\n }\n return 0;\n}\n\n/**\n * Wait for an offer, then take it — B053.\n *\n * Resolves `true` only when the machine actually moved, so the caller can\n * exit and let the supervisor start the new binary. Every other outcome —\n * refused, rolled back, stranded — leaves the daemon serving and never\n * resolves, because none of them is a reason to stop working.\n *\n * Exported for the reason {@link spawnCommand} is: a private closure inside\n * the loop is a seam nothing can exercise, and the paths worth exercising\n * here are the ones where an update does not happen.\n */\nexport async function watchForUpdate(input: {\n readonly runners: readonly Runner[];\n readonly io: CliIo;\n readonly signal: AbortSignal;\n readonly offered: () => string | undefined;\n readonly wait?: (ms: number) => Promise<void>;\n readonly run?: CommandRunner;\n readonly drainMs?: number;\n /** The provenance check, stubbed in tests; the registry's otherwise. */\n readonly verify?: (version: string) => Promise<Provenance>;\n}): Promise<boolean> {\n const wait =\n input.wait ?? ((ms: number) => new Promise<void>((w) => setTimeout(w, ms)));\n /**\n * Versions this process has already attempted.\n *\n * The offer keeps arriving until the machine takes it, so without this a\n * failed update becomes a loop that reinstalls the same broken version\n * every second. With it, a machine that rolled back sits on the version it\n * has and waits for a different answer — which is what a rollback means.\n */\n const tried = new Set<string>();\n for (;;) {\n if (input.signal.aborted) return false;\n const version = input.offered();\n if (version !== undefined && !tried.has(version)) {\n tried.add(version);\n const outcome = await update(DAEMON_VERSION, version, {\n ...realUpdateDeps({\n run: input.run ?? spawnCommand,\n ...(input.verify === undefined ? {} : { verify: input.verify }),\n drain: async () => {\n await Promise.all(\n input.runners.map((runner) =>\n runner.drain(input.drainMs ?? UPDATE_DRAIN_MS),\n ),\n );\n },\n /* `byollm start` re-registers with the supervisor, which is what\n the new entry point needs. It is not this process's own restart\n — that is the supervisor's job, after we exit. */\n reregister: async () =>\n (await (input.run ?? spawnCommand)([\"byollm\", \"start\"])).code === 0,\n report: (line) => {\n input.io.err(`update: ${line}\\n`);\n },\n }),\n });\n if (outcome.kind === \"updated\") return true;\n /* Nothing moved, or it moved back. Either way this machine is still\n the one it was, so it goes back to work rather than sitting drained\n waiting for a restart nobody is going to perform. */\n for (const runner of input.runners) {\n runner.resumeClaiming();\n }\n /* And it keeps watching. Returning here — which it did — meant a\n machine that failed one update never took another until it was\n restarted, so the fix for a bad release could not reach exactly the\n machines the bad release had landed on. */\n }\n await wait(UPDATE_POLL_MS);\n }\n}\n\n/**\n * One line per event.\n *\n * Every interpolated string that could contain remote text goes through\n * {@link stripControlChars} first: a refusal reason or an error message may\n * quote a payload, and text that can repaint a terminal can forge output\n * (byollm_004 §5's ANSI/log-injection row).\n */\nfunction report(origin: string, event: RunnerEvent, io: CliIo): void {\n const at = new Date().toISOString().slice(11, 19);\n const host = new URL(origin).host;\n\n switch (event.type) {\n case \"claimed\":\n io.out(`${at} ${host} claimed ${event.kind} ${event.jobId}\\n`);\n break;\n case \"finished\":\n io.out(\n `${at} ${host} ${event.outcome} ${event.jobId} ` +\n `(${String(event.durationMs)}ms)\\n`,\n );\n break;\n case \"no-free-slot\":\n /**\n * The line Todd needed and did not get — B196.\n *\n * Named as \"taking no new work\" rather than \"busy\", because busy is what\n * an owner assumes anyway and the point of the line is that the\n * assumption may be wrong: the same state is reached by two jobs running\n * and by two slots lost to the leak `.88` still carries.\n *\n * The remedy is not offered, deliberately. On `.88` it is a restart; on\n * `.89` a leaked slot cannot happen; and telling somebody to restart a\n * device that is legitimately serving two long jobs would be the worse\n * error. What the line owes them is the fact and the numbers.\n */\n io.out(\n `${at} ${host} taking no new work — ${String(event.active)} of ` +\n `${String(event.concurrency)} slots held\\n`,\n );\n break;\n case \"free-slot-again\":\n io.out(\n `${at} ${host} taking work again — ${String(event.free)} slot(s) free\\n`,\n );\n break;\n case \"refused\":\n io.out(\n `${at} ${host} refused ${event.jobId}: ` +\n `${stripControlChars(event.reason)}\\n`,\n );\n break;\n case \"revoked\":\n io.out(`${at} ${host} this runner was revoked. Stopping.\\n`);\n break;\n // Not a fault and not a revocation: the terms this machine runs under\n // changed, and the person who has to read them is the one at this\n // keyboard. The daemon keeps running and keeps its pairing.\n case \"awaiting-consent\":\n // Names the sites, because \"something changed\" sends somebody hunting.\n // A pairing covers a set now (cloud_009 §5), and one site's terms\n // moving leaves every other site running.\n io.out(\n `${at} ${host} paused for ${event.sites.join(\", \")} — the terms ` +\n `changed and are waiting for you. Open your connections page to ` +\n `read them; nothing runs for those sites until you do.\\n`,\n );\n break;\n case \"site-key-changed\":\n // Refused, not applied. The map is keyed by identity key id, so this is\n // an encryption key moving under an identity whose fingerprint somebody\n // already compared — which is the substitution pinning exists to stop.\n io.err(\n `${at} ${host} refused a changed key for site ${event.site}. ` +\n `Nothing was re-pinned. If this site rotated its keys on purpose, ` +\n `re-pair with \\`byollm connect\\`.\\n`,\n );\n break;\n case \"site-rotated\":\n // Loud, and on stdout rather than stderr: this is not a fault. It is a\n // key movement that verified, which is the one thing that must never\n // happen quietly — C.5. Both fingerprints are printed because the whole\n // point is that somebody can compare the new one against what the site\n // now shows, and the old one against what they remember approving.\n io.out(\n `${at} ${host} site ${event.site} rotated its key.\\n` +\n ` was: ${event.fromFingerprint}\\n` +\n ` now: ${event.fingerprint}\\n` +\n ` Accepted because the previous key signed for this one. ` +\n `Nothing was re-approved by hand.\\n` +\n (event.path.length > 2\n ? ` Through ${String(event.path.length - 1)} rotations since ` +\n `the key this device approved.\\n`\n : \"\"),\n );\n break;\n case \"service-not-signed-in\":\n // Says what to do, because a notice about a credential that names no\n // command is a notice somebody has to go and research.\n io.err(\n `${at} ${host} ${event.service} is not signed in — it has stopped ` +\n `taking work.\\n` +\n ` ${event.detail}\\n` +\n ` Sign in with that tool, then restart: ` +\n `\\`byollm run\\` re-checks on start.\\n`,\n );\n break;\n case \"service-signed-in\":\n /* The other half of the sign-in notice. Somebody was sent to a\n terminal; this is the daemon saying it noticed they went. Without it\n the only evidence the remedy worked is work quietly starting to\n flow, which is not evidence anybody can see. */\n io.err(\n `${at} ${host} ${event.service} is signed in again — it is taking ` +\n `work.\\n`,\n );\n break;\n case \"service-out-of-quota\":\n /*\n * Names no command, because there is not one — 019 §3.2.\n *\n * Every other notice on this stream ends in something to run. Ending\n * this one that way would be inventing a remedy: the account is fine,\n * the credentials are fine, and the only thing that fixes it is a\n * clock. So it says when, and says the machine handles the rest, which\n * is true — the service is re-advertised on its own.\n */\n io.err(\n `${at} ${host} ${event.service} is out of quota — it has stopped ` +\n `taking work.\\n` +\n (event.until === undefined\n ? ` Nothing to fix and nothing to sign in to. It will be ` +\n `offered again once it answers.\\n`\n : ` Back around ${new Date(event.until).toLocaleTimeString()}` +\n `. It will be offered again on its own.\\n`),\n );\n break;\n case \"now-serving\":\n /**\n * The mitigation for site policy moving to the account — Amendment K.\n *\n * This line used to ask a question (\"run `byollm approve`\"); it now\n * reports a fact, and that difference is the trade. A device owner no\n * longer decides which sites this machine serves. What they keep is\n * knowing, at the machine, the first time each one asks for anything —\n * and the levers that bound it, which the message names because a\n * notice with nothing to do about it is just noise.\n */\n io.out(\n `${at} ${host} now serving ${event.site}, enabled from your ` +\n `dashboard.\\n fingerprint: ${event.fingerprint}\\n` +\n ` Not expected? \\`byollm stop\\` stops all work, and ` +\n `\\`byollm forget\\` drops the pairing.\\n`,\n );\n break;\n case \"site-refused\":\n io.err(\n `${at} ${host} refused site ${event.site}: ` +\n `${stripControlChars(event.reason)}. Nothing was pinned.\\n`,\n );\n break;\n case \"serving-nothing\":\n // Said once, and said as the ordinary thing it is. This used to print\n // \"this runner was revoked\" and delete the pairing — a sentence that\n // was true only when it happened to be, and destructive when it was\n // not.\n io.out(\n `${at} ${host} nothing is consented for this device right now. ` +\n `The pairing stands; work resumes when a site is connected again.\\n`,\n );\n break;\n case \"consent-resumed\":\n io.out(`${at} ${host} resumed — thank you.\\n`);\n break;\n case \"error\":\n io.err(`${at} ${host} ${stripControlChars(event.message)}\\n`);\n break;\n case \"heartbeat\":\n break;\n }\n}\n\n// -- status ------------------------------------------------------------------\n\n/**\n * What `byollm status` says about the memory guard.\n *\n * Separate from the command so it can be tested against readings nobody can\n * arrange on a laptop — a machine with 1 GB free, a platform with no reader.\n * The command reads the host; this decides what that means.\n */\nexport function memoryGuardLines(input: {\n memory: MemoryReading;\n pressure: MemoryPressure;\n /**\n * The owner's floor, not the default — B090.\n *\n * This printed {@link DEFAULT_FLOOR_BYTES} unconditionally, which was\n * right for exactly as long as the floor was not configurable. An owner\n * who raises it to 8 GB and then reads \"refused below 2.0 GB\" is being\n * told their setting did not take, on the one screen that exists to say\n * what this device is doing.\n */\n floorBytes: number;\n}): string {\n const gb = gigabytes;\n if (input.memory.kind !== \"read\") {\n return (\n `memory guard: NOT ACTIVE\\n` +\n ` ${input.memory.why}, so jobs are admitted without checking whether\\n` +\n ` this machine has room to load a model. Nothing here is refusing on\\n` +\n ` memory — which is not the same as nothing needing to be refused.\\n`\n );\n }\n const floor = gb(input.floorBytes);\n return (\n `memory guard: active\\n` +\n ` ${gb(input.memory.availableBytes)} available of ` +\n `${gb(input.memory.totalBytes)}, pressure ${input.pressure}\\n` +\n ` a job that loads a model is refused below ${floor} available, or at\\n` +\n ` critical pressure.\\n`\n );\n}\n\n/**\n * How stale the beat may be before `status` calls the daemon dead — B201,\n * reworked under B202.\n *\n * A live daemon rewrites it every heartbeat, and `DEFAULT_HEARTBEAT_MS` is ten\n * seconds. **Six missed writes**, which is past any single slow tick — a box\n * throttled to its 50m request spends ~2 s on probes alone (B198) — and still\n * inside the dashboard's own 90 s \"online\" window, so the machine's own\n * `status` is not the last surface to notice its daemon is gone.\n *\n * Erring long on purpose: a false NOT RUNNING sends somebody to restart a\n * working device, and this headline is exactly the one that has to be believed.\n */\nconst HEARTBEAT_STALE_AFTER_MS = 60_000;\n\n/**\n * Which sentence this beat has earned — B228.\n *\n * Asked again here rather than carried down from the decision above, because\n * the two call sites want different things: the decision wants \"is it stale at\n * all\", and the message wants \"which fact am I explaining\".\n *\n * **My first version suppressed this whenever the beat was also old**, on the\n * theory that the age was the better thing to tell somebody. A mutation went\n * straight through it, and the reason is that the theory was wrong: a beat\n * twenty minutes old whose process is GONE and one whose process is still\n * there are different situations — stopped versus wedged — and only one of\n * them is fixed by restarting. Suppressing the specific fact because a vaguer\n * one was also true is the wrong way round.\n */\nconst beatIsGone = (beat: DaemonHeartbeat): boolean => beatWriterIsGone(beat);\n\n/** An age in the plainest words — the shape `describeDrift` uses for a skew. */\nfunction describeAge(ms: number): string {\n const seconds = Math.round(Math.abs(ms) / 1000);\n return seconds < 120\n ? `${String(seconds)} seconds`\n : `${String(Math.round(seconds / 60))} minutes`;\n}\n\nasync function commandStatus(\n paths: DaemonPaths,\n io: CliIo,\n service: ServiceIo,\n): Promise<ExitCode> {\n const { loaded, configured, ingress, budgets, spend, spentGrants } =\n await context(paths);\n const pairings = new Pairings(paths.pairings);\n await pairings.load();\n reportSkipped(pairings, io);\n await retireAllowlist(paths, io);\n\n // Whether anything here could admit a stranger at all. A `team` offer on a\n // device paired with nothing serves one person, and both surfaces say so\n // rather than printing the request.\n const hasRelay = pairings\n .list()\n .some((pairing) => pairing.controlPlanePublic !== undefined);\n\n const now = Date.now();\n\n io.out(formatVersion());\n // This machine's fingerprint, so an owner can compare it against what a\n // site shows them (byollm_009 §3). A fingerprint nobody can find is a\n // fingerprint nobody compares.\n io.out(\n `identity: ${await new DeviceIdentity(paths.keys).fingerprint(now)}\\n`,\n );\n // **A persistent rejection is a state, and it leads.**\n //\n // This line said `running` for hours while every heartbeat the daemon sent\n // was refused. True, and useless: the daemon was running, reporting\n // nothing, invisible to the hub, and the device's page showed frozen data.\n // The only surface that knew was a log line nobody tails.\n //\n // So the headline answers \"is this device working\" rather than \"is the\n // process alive\", and those turned out to be different questions.\n const health = await readHealth(paths.health);\n const failing =\n health !== undefined && health.consecutiveFailures >= FAILURES_BEFORE_ALARM;\n\n /**\n * A daemon that has stopped beating has stopped running — B201, reworked\n * under B202.\n *\n * **The first version of this read `health.json`'s `at` as a per-beat stamp.\n * It is not**, and the line three above its call site says so: health is\n * *\"written on the transition rather than every beat, so a healthy daemon is\n * not rewriting a file every ten seconds to say nothing changed.\"*\n *\n * That was wrong in both directions at once — an absent file left a dead\n * daemon reading `running`, and a daemon that failed once and recovered\n * froze `at` at the recovery and read `NOT RUNNING` a minute later while\n * serving perfectly. I read `#recordHealth`'s body and never its callers.\n *\n * `heartbeat.json` is written every beat and overwritten in place, so its\n * age means exactly what this arm needs it to mean.\n *\n * `#recordHealth` stamps `at` on **every heartbeat**, so a live daemon\n * refreshes this file every ten seconds. A dead one freezes `at` — and\n * leaves `consecutiveFailures` at whatever it was, which after a healthy run\n * is **zero**. So `failing` never trips, `supervision.state` on a box is\n * `absent` because there is no systemd to ask, and the headline fell all the\n * way through to **`running`** for a process that did not exist.\n *\n * That happened. On 09-15 box-1's daemon was gone — a `/proc` walk found\n * only the console — while `status` reported a working device, and the only\n * truthful instrument anybody had was typing the walk by hand.\n *\n * **The honest signal was already on disk and this surface never asked** —\n * the same shape as the provenance model B197 found, two rows apart.\n */\n const beat = await readHeartbeat(paths.heartbeat);\n /**\n * Absent counts, and only for a device that is meant to be serving.\n *\n * box-1's shape: the container restarted, the PVC carried `~/.byollm`\n * across, and the daemon was gone — so its beat file is there and stale. A\n * daemon that has never run since this version wrote none at all, and for a\n * PAIRED device that is still \"not running\"; for a machine nobody has set\n * up it is just that, which the screen says better further down.\n */\n const serving = pairings.list().length > 0;\n /**\n * Two ways to know, and the second closes a full minute of lying — B228.\n *\n * The age arm alone leaves a window the width of the staleness threshold: a\n * daemon killed five seconds ago has a fresh file, `consecutiveFailures` is\n * zero because it was working when it died, `supervision.state` is `absent`\n * for a daemon run in a terminal, and the headline falls through to\n * `running` for a process that does not exist. **That is exactly the minute\n * in which somebody who just typed `byollm stop` types `byollm status`** —\n * which is what Todd did on 09-16, on `.89`, before the beat file existed\n * at all.\n *\n * The pid has been on the record since B202 with a comment saying what it\n * is for, and nothing asked it for three releases. Asking it can only\n * demote: `ESRCH` is proof of death, a successful signal proves nothing\n * because pids are reused. See {@link beatWriterIsGone}.\n */\n const aged = beat !== undefined && now - beat.at > HEARTBEAT_STALE_AFTER_MS;\n const stale = beat === undefined ? serving : aged || beatWriterIsGone(beat);\n\n /**\n * The supervisor's answer, asked once and used twice — ruled 2026-09-03.\n *\n * `state:` printed `running` two lines above `service: installed but NOT\n * running`. One display holding two truths, and a status that disagrees\n * with itself teaches the reader to trust neither line.\n *\n * The cause was that this headline consulted a local flag and the health\n * file and never the supervisor, so it was answering a narrower question\n * than the word `state` promises. It is meant to answer \"is this device\n * working\", and a device whose service is dead is not working.\n *\n * `PAUSED` is gone from this list (B043) because it was the worst version\n * of that same bug: it outranked every other state and was set by a flag\n * the daemon did not read, so the one line people check said the machine\n * was idle while it claimed work.\n *\n * `absent` does not demote it: no service registered is the ordinary shape\n * of `byollm run` in a terminal, which is working fine.\n *\n * **A machine nobody has set up is not running either.** No config, no\n * heartbeat: nothing has ever run here, and this line said `running` — the\n * headline for \"working\" on a device with nothing to work with, above a\n * service list showing the built-in default as if somebody had chosen it.\n * Zero and unknown never look alike (`docs/standards.md`), and \"never set\n * up\" is a zero this screen can know. A beat outranks it: `byollm run` on\n * the defaults is a daemon that is running, whatever the file says.\n */\n const plan = servicePlan(serviceTarget(paths, service));\n const supervision = await serviceState(plan, service.run);\n const revoked = await revokedMark(paths.health);\n const notSetUp = !configured && beat === undefined;\n\n io.out(\n `state: ${\n revoked !== undefined\n ? \"REVOKED\"\n : supervision.state === \"installed\" || stale\n ? \"NOT RUNNING\"\n : failing\n ? \"NOT REPORTING\"\n : notSetUp\n ? \"NOT SET UP\"\n : \"running\"\n }\\n`,\n );\n if (revoked === undefined && !stale && !failing && notSetUp) {\n io.out(\n ` no config at ${paths.config}, and nothing has run here yet.\\n` +\n ` ${NOT_SET_UP_HINT}`,\n );\n }\n if (revoked !== undefined) {\n /* The ruled sentence, as the headline's own explanation. Everything below\n it on this screen describes a device that is not going to serve\n anything until it is paired again. */\n io.out(\n ` ${REVOKED_SENTENCE}.\\n` +\n ` ${revoked.origin} no longer accepts this device's credential.\\n` +\n revokedRemedy(revoked.origin, supervision.state !== \"absent\"),\n );\n }\n if (stale) {\n /**\n * Said with the evidence, because \"NOT RUNNING\" from a surface that was\n * wrong about exactly this an hour ago has to show its working — and the\n * three ways of being not-running have DIFFERENT working.\n *\n * Printing the age when the argument was the missing process would show a\n * reader a number that does not support the verdict, on the one screen\n * that has to be believed. And the never-beat case used to print the age\n * too: `now - (beat?.at ?? now)` is zero when there is no beat, so a\n * paired machine whose daemon has never run was told \"nothing has written\n * this device's heartbeat for 0 seconds\", which is not a sentence about\n * anything.\n */\n const why =\n beat === undefined\n ? ` nothing has ever written this device's heartbeat, and this ` +\n `device is paired — so a daemon was meant to be running here and ` +\n `has not.\\n`\n : beatIsGone(beat)\n ? /* No \"only\": this arm is reached for an old beat too, and \"only\n 20 minutes old\" would be the sentence being wrong out loud. */\n ` the process that wrote this device's heartbeat (pid ` +\n `${String(beat.pid)}) is gone, and the beat is ` +\n `${describeAge(now - beat.at)} old — it was stopped rather than ` +\n `wedged.\\n`\n : ` nothing has written this device's heartbeat for ` +\n `${describeAge(now - beat.at)} — a running daemon rewrites it ` +\n `every heartbeat, so it is not running.\\n`;\n io.out(\n why +\n ` anything below is the last thing it believed, not what is true ` +\n `now.\\n`,\n );\n }\n if (failing) {\n io.out(\n ` the hub has rejected this device's last ` +\n `${String(health.consecutiveFailures)} messages — it is running and ` +\n `invisible.\\n` +\n (health.origin === undefined ? \"\" : ` upstream: ${health.origin}\\n`) +\n (health.lastError === undefined\n ? \"\"\n : ` it said: ${health.lastError}\\n`) +\n ` anything below is what this device believes, not what the hub has ` +\n `been told.\\n`,\n );\n }\n io.out(await supervisionLine(plan, supervision, revoked !== undefined));\n io.out(\"\\n\");\n\n /**\n * The memory guard, and whether it is actually guarding — B080.\n *\n * byollm_022 requires this line for the unknown case specifically: if the\n * reader cannot answer, the gate admits normally, and **an absent guard\n * that stays quiet is indistinguishable from one that is passing\n * everything.** That is `stopReasons`' `unavailable` one layer down — we\n * looked, there was nothing to read, and the honest move is to say so.\n *\n * Read directly here rather than through a Runner. The rule that `status`\n * must not spawn is about starting a model server as a side effect of\n * asking a question; this IS the question, asked once, on the screen whose\n * job is to answer it.\n */\n io.out(\n memoryGuardLines({\n ...(await readHostMemory()),\n floorBytes: loaded.config.minAvailableMemoryBytes,\n }),\n );\n io.out(\"\\n\");\n\n io.out(\"paired apps\\n\");\n const list = pairings.list();\n if (list.length === 0) {\n io.out(\" (none — run `byollm connect <url>`)\\n\");\n }\n for (const pairing of list) {\n io.out(` ${pairing.origin} as ${pairing.ownerLabel ?? pairing.owner}\\n`);\n // The pinned key, so an owner can check it against what the app shows.\n // A pin nobody can see is a pin nobody can verify.\n // Every pinned key, so an owner can check each against what the site\n // shows. A pin nobody can see is a pin nobody can verify, and a pairing\n // covering several sites hides several of them behind one line.\n for (const site of Object.values(pairing.sites)) {\n io.out(` pinned: ${fingerprint(site.identity)}\\n`);\n }\n }\n\n // **Services and defaults. No routes section** — ruled 2026-08-25.\n //\n // `routes` was the old shape's ghost. It listed one line per resolved kind,\n // which in Phase A *was* the service list, and by Phase B it was a third\n // section describing facts the first two already carry: a route is a\n // (service, kind) pair plus which one is the default, and both of those are\n // here. Three displays of two facts is how they drift apart, which is this\n // morning's lesson pointed at our own output.\n //\n // The two facts a service can have about a kind are said apart. Every\n // declared service **answers** the kinds it declares — a mapping may point\n // at any of them — and one of them may additionally be the owner's own\n // **default**, which decides only where a job nothing resolved goes.\n //\n // \"selectable for\" lived here until Amendment L, meaning \"a site may name\n // this one\". No site names anything now, so the word described a power\n // nobody has; the kinds a service answers is the fact that survived.\n //\n // There is no separate `defaults` section, and there was one for about an\n // hour. It listed `llm.chat → claude` beside a service line already reading\n // `claude — default for llm.chat`: the same fact twice, which is the\n // criticism that removed `routes` the same afternoon. A display that\n // restates itself is two things to keep in step, and only one of them gets\n // updated.\n const byService = new Map<\n string,\n { answers: string[]; defaults: string[] }\n >();\n for (const route of loaded.routes) {\n const entry = byService.get(route.service) ?? {\n answers: [],\n defaults: [],\n };\n entry.answers.push(route.kind);\n if (route.isDefault) entry.defaults.push(route.kind);\n byService.set(route.service, entry);\n }\n\n io.out(\"\\nservices\\n\");\n // What the last probe recorded, if anything has probed. Read rather than\n // re-run — see the comment beside `authLine` below.\n const recorded = await readServiceStates(paths.serviceStates);\n const deviceName = await labelFor(paths, undefined);\n // Nothing written is not the default: the default is what the daemon falls\n // back to, and listing it here told a fresh install it had an Ollama.\n const declared = configured ? Object.entries(loaded.config.services) : [];\n if (!configured) {\n io.out(\" (none — run `byollm setup`)\\n\");\n } else if (declared.length === 0) {\n io.out(\" (none configured)\\n\");\n }\n for (const [name, service] of declared) {\n const entry = byService.get(name) ?? { answers: [], defaults: [] };\n const route = loaded.routes.find((r) => r.service === name);\n const shown =\n route === undefined\n ? service.model\n : `${route.model} (${route.backendId})`;\n // \"private (only you)\" rather than \"offered to private\" — the config's\n // word, with the consequence beside it, so nobody has to already know\n // what the word means to read the line.\n //\n // **The effective scope, not the configured one.** This read\n // `service.offer` and so answered from the file rather than from the\n // daemon: a metered service configured `team` but narrowed to `private`\n // pending spend consent printed \"team (you and people you allow)\" while\n // refusing every one of them. The config is a request; `route.offerScope`\n // is what happened to it.\n const summary = offerSummary({\n effective: route?.offerScope ?? service.offer,\n configured: service.offer,\n hasRelay,\n });\n const scope =\n `${summary.scope} (${summary.audience})` +\n (summary.narrowedBy === undefined ? \"\" : ` — ${summary.narrowedBy}`);\n\n /**\n * What the last probe found, if anything has probed.\n *\n * Read rather than re-run: this is a different process, and the canary is\n * a real model call — on a metered backend, real money, on a command\n * people run repeatedly while something is wrong.\n *\n * Absent is not signed-out. A machine that has never probed prints what it\n * always printed, because \"nothing has asked yet\" is not a finding.\n */\n const authLine = authNote({\n service: name,\n device: deviceName,\n report: recorded.get(name),\n });\n // Three short lines rather than one long one. `openai-http:` prefixed\n // onto `mlx-community/Qwen2.5-14B-Instruct-4bit` with a scope after it\n // wrapped at any sane terminal width, and a wrapped line in a column\n // layout stops looking like a column at all.\n //\n // The backend id goes with the scope rather than the model: it is the\n // same *kind* of fact — how this service behaves — while the model is the\n // thing a person recognises and the only part that is genuinely long.\n io.out(` ${name}\\n`);\n io.out(` ${shown}\\n`);\n const says: string[] = [];\n if (entry.answers.length > 0) {\n says.push(`answers ${entry.answers.join(\", \")}`);\n }\n if (entry.defaults.length > 0) {\n // Named as *yours*, because that is the whole of what a default is now:\n // where your own work goes when nothing resolved it. A relayed job\n // arrives already resolved and never consults it.\n says.push(`your default for ${entry.defaults.join(\", \")}`);\n }\n io.out(\n ` ${scope} · ${says.length === 0 ? \"not offered — see the problems below\" : says.join(\" · \")}\\n`,\n );\n /* The auth line, when the probe found something to say. Same template as\n `byollm connect` and the daemon's output, so one machine cannot\n describe itself differently depending on where you look. */\n if (authLine !== undefined) {\n io.out(` ${authLine.line}\\n`);\n if (authLine.detail !== undefined) {\n io.out(` ${authLine.detail}\\n`);\n }\n }\n }\n\n // **Withheld is shown, never merely absent.**\n //\n // A kind two services answer is not advertised until the owner says which\n // wins — correct, and silent in every surface that only lists what *is*\n // advertised. An owner adds a second `llm.generate`, their team's jobs stop\n // matching, and nothing anywhere says why. So it is listed here, by name,\n // with the fix.\n for (const held of loaded.withheld) {\n io.out(\n ` … ${held.kind.padEnd(14)} no default — ${String(held.services.length)} services ` +\n `answer it (${held.services.join(\", \")})\\n` +\n ` a job naming one of them runs; a job naming none has nowhere ` +\n `to go\\n` +\n ` set defaults.${held.kind} in ~/.byollm/config.json\\n`,\n );\n }\n for (const problem of loaded.problems) {\n io.out(` ! ${problem.where}: ${problem.message}\\n`);\n }\n\n /**\n * Who can use this device — and the honest admission that this device does\n * not know.\n *\n * It used to print a list, because it held one: first a per-person\n * allowlist, then a signed roster. Amendment J removed both. Membership now\n * arrives one grant at a time, at claim, so there is no moment at which\n * this device is told the set — and a status surface declares whose\n * knowledge it shows.\n *\n * Saying so is the point rather than an apology. A screen that quietly\n * stopped listing people would read as \"nobody\", which is the flattering\n * lie in the sentence about who may use somebody's computer.\n */\n io.out(\"\\nwho can use this device\\n\");\n io.out(\" you, always\\n\");\n const paired = pairings.list();\n if (paired.length === 0) {\n io.out(\" nobody else — this device is not paired with anything\\n\");\n }\n for (const pairing of paired) {\n if (pairing.controlPlanePublic === undefined) {\n // Direct mode, and the reason is worth one line: there is no control\n // plane here, so nothing could ever sign a statement that a stranger\n // may use this machine. Owner-only is not a setting somebody forgot to\n // change (ruled 2026-08-26).\n io.out(\n ` nobody else, through ${pairing.origin} — it has no control plane, ` +\n `so nothing\\n can tell this device who anybody else is\\n`,\n );\n continue;\n }\n io.out(\n ` whoever ${pairing.origin} admits, one job at a time\\n` +\n ` (this device is not told the list — it checks a signature per ` +\n `job.\\n Manage who is on it from your team page.)\\n`,\n );\n }\n\n /*\n * A ledger that could not be read, named — byollm_016, 2026-09-03.\n *\n * The brakes below go on by themselves when the bookkeeping is\n * unreadable, and a brake nobody can explain is indistinguishable from a\n * broken daemon: the machine simply stops taking community work and every\n * number on this screen says it should be taking it. The file is named,\n * never quoted — it is a record of what this machine did for other people,\n * and that does not belong in a screenshot or a support thread.\n */\n const grantsBlocked = spentGrants.blockedReason(now);\n const untrusted = [\n [\"community work\", budgets.untrustedReason()],\n [\"metered spend\", spend.untrustedReason()],\n [\"used grants\", grantsBlocked],\n ].filter((row): row is [string, string] => row[1] !== undefined);\n if (untrusted.length > 0) {\n io.out(\"\\nbookkeeping this device cannot read\\n\");\n for (const [what, why] of untrusted) {\n io.out(` ${what}: ${why}\\n`);\n }\n /*\n * Two different brakes, two different sentences — CW's rolling review.\n *\n * The first draft said \"your own work is unaffected\" for whatever was\n * unreadable, which is true of the two ledgers that count what this\n * machine did for other people and false of the third. The used-grants\n * record guards the wire: while it is unreadable this device refuses\n * every relayed job, the owner's own site work included, because a\n * duplicate of your own metered job is still your money.\n *\n * Saying the reassuring one over the strict one would have somebody\n * reading \"your own work is unaffected\" on the exact screen explaining\n * why their own work had stopped.\n */\n if (grantsBlocked === undefined) {\n io.out(\n \" it is refusing work for other people until this is fixed. Your \" +\n \"own work is unaffected.\\n\",\n );\n } else {\n io.out(\n \" it is refusing every job that arrives through a site, including \" +\n \"your own, until this is fixed.\\n\" +\n \" that lasts about two minutes from the last start — long enough \" +\n \"that nothing it forgot could still be replayed.\\n\",\n );\n }\n io.out(\" move the named file aside and this clears on the next start.\\n\");\n }\n\n const spentToday = spend.summary(now);\n /**\n * One row per SERVICE, and it used to be one per route — B113, found by\n * running it.\n *\n * `loaded.routes` carries a row for every (service, kind) pair, so a\n * service answering both `llm.generate` and `llm.chat` printed its ceiling\n * twice, identically, in a block whose subject is services and whose\n * numbers are per service. `spend.summary` is keyed by service too, so the\n * second line was the same money counted again on screen.\n *\n * **It was latent until this hour.** Two kinds on one service was rare\n * enough to miss while people hand-wrote configs; `byollm services manage`\n * writes both kinds on every service it creates, which makes the doubling\n * the normal case from today. The queued row and the live text it\n * falsifies, meeting — instruction 7's converse, arriving from the other\n * direction.\n */\n const metered = [\n ...new Map(\n loaded.routes\n .filter((r) => r.cost === \"metered\")\n .map((route) => [route.service, route]),\n ).values(),\n ];\n if (metered.length > 0) {\n /**\n * Whose spending this bounds, said in the heading — B113.\n *\n * It read *\"metered services — your money\"* over\n * *\"glm-5.2: $0.00 spent today of $25.00\"*, **and Todd read that as a\n * personal budget.** That is the evidence: it is a ceiling on OTHER\n * PEOPLE'S work, and his own jobs neither count toward it nor stop at it.\n *\n * The code is explicit in both directions and this line contradicted\n * both. `runner.ts` records a charge only when the job is `community` —\n * *\"Own work is not counted: their machine, their key, their call\"* — and\n * `spend.ts` says of the ceiling *\"Only community metered work consults\n * this — the owner's own jobs never reach it, so a bookkeeping failure\n * cannot brake the machine's own work.\"*\n *\n * \"Your money\" was not wrong about who pays. It was wrong about who\n * spends, on a screen whose whole job is to say what this machine is\n * doing for other people.\n */\n io.out(\"\\nmetered services — your account, spent by other people\\n\");\n for (const route of metered) {\n const spent = spentToday[route.service] ?? 0;\n /**\n * Bounded is `offerScope`, not `spendAcknowledged` — found writing the\n * line above.\n *\n * The two travel together for a widened service, which is why this\n * survived: `resolveConfig` refuses a shared metered service that has\n * an acknowledgement without a ceiling. They come apart for a service\n * that acknowledged and stayed **private** — which printed\n * *\"$0.00 spent today of $0.00\"*, a zero cap on a service nobody else\n * can reach, i.e. two false claims in one line.\n *\n * The ceiling is consulted for community work on a widened service and\n * nothing else, so that is the condition this reports.\n */\n const bounded =\n route.offerScope !== \"private\" &&\n route.spendDailyCapCents !== undefined;\n io.out(\n bounded\n ? ` ${route.service}: ${dollars(spent)} of ` +\n `${dollars(route.spendDailyCapCents)} spent today by ` +\n `people you shared it with\\n`\n : ` ${route.service}: not shared, so nobody else can spend on it\\n`,\n );\n }\n io.out(\" your own jobs on these are not counted here and have no cap.\\n\");\n }\n\n const usage = budgets.usage(now);\n io.out(\"\\ncommunity work done for others\\n\");\n io.out(\n ` ${String(usage.hour)} in the last hour (cap ${String(usage.limits.maxJobsPerHour)}), ` +\n `${String(usage.day)} today (cap ${String(usage.limits.maxJobsPerDay)})\\n`,\n );\n\n const entries = await ingress.read();\n const prompts = entries.filter((entry) => entry.type === \"prompt\");\n const outcomes = entries.filter((entry) => entry.type === \"outcome\");\n io.out(\"\\nthis device has run\\n\");\n io.out(\n ` ${String(prompts.length)} prompts, ` +\n `${String(outcomes.filter((o) => o.outcome === \"ok\").length)} ok, ` +\n `${String(outcomes.filter((o) => o.outcome === \"error\").length)} failed, ` +\n `${String(outcomes.filter((o) => o.outcome === \"refused\").length)} refused\\n`,\n );\n io.out(\n ` full log: ${paths.ingressLog}\\n` +\n ` other people's prompts are kept ` +\n `${String(loaded.config.ingress.communityPromptDays)} days, then hashed\\n`,\n );\n return 0;\n}\n\n// -- log ---------------------------------------------------------------------\n\n/**\n * Where a job's time went, when the device recorded it — B195.\n *\n * Todd asked how much of a slow job is *\"waiting on claude and gpt vs resource\n * utilization\"*. `durationMs` alone cannot answer that: the clock starts on the\n * first line of the backend's `execute()`, so spawn, the vendor wait and\n * reading the answer are one number.\n *\n * Printed as an aside on the same line rather than as its own block, because it\n * is a refinement of the duration standing next to it and not a separate fact.\n *\n * **`waiting` is a BOUND, not an attribution**, and the word is chosen for\n * that: it is the child's own startup plus its first vendor response, and those\n * are not separable from outside the child. A CLI that prints a banner before\n * it calls anybody would show a small number for a reason that has nothing to\n * do with a vendor.\n *\n * Nothing is printed for an entry that carries neither — every line written\n * before this landed, and every call that died before it spawned.\n */\nfunction split(entry: {\n spawnMs?: number | undefined;\n firstOutputMs?: number | undefined;\n}): string {\n const parts: string[] = [];\n if (entry.spawnMs !== undefined) parts.push(`spawn ${String(entry.spawnMs)}`);\n if (entry.firstOutputMs !== undefined) {\n parts.push(`waiting ${String(entry.firstOutputMs)}`);\n }\n return parts.length === 0 ? \"\" : ` (${parts.join(\", \")})`;\n}\n\nasync function commandLog(\n paths: DaemonPaths,\n args: readonly string[],\n io: CliIo,\n): Promise<ExitCode> {\n const full = args.includes(\"--full\");\n const nIndex = args.findIndex((arg) => arg === \"-n\" || arg === \"--lines\");\n const limit =\n nIndex === -1 ? 20 : Math.max(1, Number(args[nIndex + 1] ?? \"20\") || 20);\n\n const { ingress } = await context(paths);\n const entries = await ingress.read();\n const shown = entries.slice(-limit);\n\n if (shown.length === 0) {\n io.out(\"nothing has run on this device yet\\n\");\n return 0;\n }\n\n /**\n * Which backend ran each job — B064 step 3.\n *\n * The outcome entry does not carry it and the prompt entry does, and a\n * prompt is always written before its own outcome\n * ({@link MUSTS.INGRESS_LOGGED_BEFORE_EXECUTION} puts it before the backend\n * is touched at all). So walking in order is enough, and this needs no\n * second field on the wire or in the log.\n *\n * It matters because the remedy is per-backend: \"raise `num_predict`\" is\n * useful for Ollama and meaningless for a subscription CLI, and telling an\n * owner to turn a knob that does not exist is worse than saying nothing.\n */\n const ranOn = new Map<string, BackendId>();\n\n for (const entry of shown) {\n const at = new Date(entry.at).toISOString().replace(\"T\", \" \").slice(0, 19);\n if (entry.type === \"prompt\")\n ranOn.set(entry.jobId, entry.backendId as BackendId);\n if (entry.type === \"outcome\") {\n const backendId = ranOn.get(entry.jobId);\n const stopped =\n entry.stop === undefined || backendId === undefined\n ? undefined\n : stopLine(backendId, entry.stop, entry.stopKind);\n io.out(\n `${at} ${entry.outcome.padEnd(8)} ${entry.jobId}` +\n (entry.durationMs === undefined\n ? \"\"\n : ` ${String(entry.durationMs)}ms${split(entry)}`) +\n `${entry.detail === undefined ? \"\" : ` ${stripControlChars(entry.detail)}`}\\n` +\n /* Its own line, indented under the outcome: this is the sentence\n the owner is meant to act on, and appending it to a line that\n already carries an id and a duration is how it gets skimmed. */\n (stopped === undefined ? \"\" : ` ${stopped}\\n`),\n );\n continue;\n }\n\n /**\n * The memory guard's decisions — B080.\n *\n * Rendered here rather than left to `jq` because this is the log an owner\n * is pointed at, and a decision nobody can read is the tuning data\n * byollm_022 asked for sitting in a file nobody opens. Admits included:\n * the question the 2 GB floor needs answered is how close the admits ran,\n * and a log of refusals alone cannot answer it.\n */\n if (entry.type === \"memory\") {\n const gb = gigabytes;\n io.out(\n `${at} ${(entry.admit ? \"admit\" : \"REFUSE\").padEnd(8)} ` +\n entry.backendId +\n (entry.availableBytes === undefined\n ? \"\"\n : ` ${gb(entry.availableBytes)} free`) +\n ` pressure ${entry.pressure}` +\n ` ${stripControlChars(entry.why)}\\n`,\n );\n continue;\n }\n\n io.out(\n `${at} ${entry.audience.padEnd(7)} ${entry.kind} ` +\n `via ${entry.backendId}:${entry.model} for ${entry.owner} ` +\n `@ ${new URL(entry.origin).host}\\n`,\n );\n if (entry.prompt === undefined) {\n // Retention removed the text. Say so — a blank line here would read as\n // \"empty prompt\", and zero must never look like unknown.\n io.out(\n ` prompt not retained (${String(entry.promptChars)} chars, ` +\n `sha256 ${entry.promptHash.slice(0, 16)}…)\\n`,\n );\n } else if (full) {\n io.out(`${indent(stripControlChars(entry.prompt))}\\n`);\n } else {\n const firstLine = entry.prompt.split(\"\\n\")[0] ?? \"\";\n io.out(\n ` ${stripControlChars(firstLine.slice(0, 90))}` +\n `${entry.prompt.length > 90 ? \"…\" : \"\"}\\n`,\n );\n }\n }\n if (!full) {\n io.out(`\\n(${String(entries.length)} entries; --full for whole prompts)\\n`);\n }\n return 0;\n}\n\nfunction indent(text: string): string {\n return text\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n}\n\n// -- allow / disallow (tombstones) --------------------------------------------\n\n/**\n * Two commands that no longer exist, and why they will not be coming back.\n *\n * `byollm allow <site> <user>` kept a device-local list of people permitted\n * to run work here; `byollm disallow` removed one. Both were deleted on\n * 2026-08-26 (byollm_016 Amendments I and J).\n *\n * The reason is not simplification. **The user-granularity was illusory.** A\n * daemon cannot verify a foreign site's user identities, so an entry admitted\n * whatever that site asserted per job about who its user was — which is\n * precisely the unsigned per-job assertion Amendment G property 1 outlawed\n * for the cloud route, wearing an allowlist costume. Against a dishonest site\n * it gated nothing; against an honest one it second-guessed the only party\n * that owns the namespace.\n *\n * The git analogy that settled it: no git client keeps a local list of\n * permitted GitHub users. Who may push is GitHub's decision, made in\n * GitHub's namespace, enforced where the namespace lives. Blocking exists —\n * and you do it at GitHub.\n *\n * A tombstone rather than \"unknown command\", because somebody's fingers still\n * know these and an unknown-command error would send them to look for a typo.\n * It names where the capability went, which is the whole obligation of a\n * refusal.\n */\nfunction commandRetiredAdmission(name: \"allow\" | \"disallow\", io: CliIo): 2 {\n io.err(\n `${wrap(\n `\\`byollm ${name}\\` is gone. This device no longer keeps its own list ` +\n `of who may use it — it could never check the names on that list, ` +\n `so the list only ever agreed with whoever was asking.`,\n )}\\n\\n` +\n `${wrap(\n `Membership lives with your relay now, and reaches this device one ` +\n `signed grant at a time. Add or remove people from your team page.`,\n )}\\n\\n` +\n `${wrap(\n `A device with no relay serves its owner and nobody else, which is ` +\n `what \\`byollm status\\` will tell you.`,\n )}\\n`,\n );\n return 2;\n}\n\n// -- offer -------------------------------------------------------------------\n\n/**\n * What a service's offer scope actually amounts to on this machine.\n *\n * **A request is not a state** (ruled 2026-08-26). Three things can narrow an\n * owner's request and each of them used to be invisible on the screen built\n * to show it: a subscription's terms, an unacknowledged spend, and — new with\n * Amendment J — having no relay to admit anybody.\n *\n * The third is the one worth spelling out. `team` means \"whoever my relay\n * admits\", and a device paired with nothing has no relay and therefore admits\n * nobody. That is not a bug to be fixed by widening; it is what direct mode\n * *is*, since nothing there could sign a statement about who a stranger is.\n * But a device printing \"team\" while serving one person is lying, so it says\n * both: what took effect, and what was asked for.\n */\nfunction offerSummary(input: {\n readonly effective: \"private\" | \"team\";\n readonly configured: \"private\" | \"team\" | undefined;\n readonly hasRelay: boolean;\n}): {\n /** The config's own word, so the two surfaces agree with the file. */\n readonly scope: \"private\" | \"team\";\n /** What that word means for a reader who does not know the vocabulary. */\n readonly audience: string;\n /** Why it is not what was asked for, when it is not. */\n readonly narrowedBy: string | undefined;\n} {\n const { effective, configured, hasRelay } = input;\n if (effective === \"team\" && !hasRelay) {\n return {\n scope: \"private\",\n audience: \"only you\",\n narrowedBy:\n \"no relay paired, so nothing here can admit anybody — \" +\n \"`byollm connect <relay>` to share it\",\n };\n }\n if (effective === \"private\") {\n return {\n scope: \"private\",\n audience: \"only you\",\n narrowedBy:\n configured !== undefined && configured !== \"private\"\n ? `narrowed from ${configured} — see ! below`\n : undefined,\n };\n }\n return {\n scope: \"team\",\n audience: \"you and the people your relay admits\",\n narrowedBy: undefined,\n };\n}\n\n/**\n * Change who a backend is offered to.\n *\n * This exists because `resolveConfig` tells an owner to run it. A message that\n * names a command nobody wrote is worse than no message: it reads as though\n * the software has an answer when it does not.\n *\n * Widening a metered backend is the one path here that can cost real money, so\n * it is the one path that asks — and the question names the money rather than\n * asking whether the owner is \"sure\"\n * ({@link MUSTS.METERED_DEFAULTS_SELF}, {@link MUSTS.METERED_REQUIRES_CEILING}).\n */\nasync function commandOffer(\n paths: DaemonPaths,\n args: readonly string[],\n io: CliIo,\n): Promise<ExitCode> {\n const [serviceKey, scope, ...rest] = args;\n if (serviceKey === undefined || scope === undefined) {\n io.err(\"usage: byollm offer <service> <private|team> [--cap <cents>]\\n\");\n return 2;\n }\n if (scope === \"public\") {\n /**\n * A tombstone, not a typo — refusals split by remedy.\n *\n * `public` was a real scope until 2026-08-26 and somebody's fingers still\n * know it, so it gets its own answer rather than being lumped in with a\n * misspelling. The reason is worth saying because it is the whole point\n * of removing it: `matchAudience` returned ALLOWED for a public service\n * **without consulting this device at all**, so the value was an off\n * switch for admission. Every scope that remains asks a question.\n */\n io.err(\n `${wrap(\n `\\`public\\` is gone. A service is offered to you alone, or to the ` +\n `people your relay admits — there is no longer a scope that runs a ` +\n `stranger's job without this device checking who they are.`,\n )}\\n\\n` +\n `\\`byollm offer ${serviceKey} team\\` shares it with your team.\\n`,\n );\n return 2;\n }\n if (scope !== \"private\" && scope !== \"team\") {\n // `self, named` were the pre-alpha.44 words, and they survived inside a\n // string where the rename could not see them — the reason error text now\n // joins the one-vocabulary lint.\n io.err(`\"${scope}\" is not an offer scope — use private or team\\n`);\n return 2;\n }\n\n const capIndex = rest.indexOf(\"--cap\");\n let capCents: number | undefined;\n if (capIndex !== -1) {\n const raw = rest[capIndex + 1];\n const parsed = Number(raw);\n if (raw === undefined || !Number.isFinite(parsed) || parsed <= 0) {\n io.err(\"--cap takes a number of cents per day, greater than zero\\n\");\n return 2;\n }\n capCents = Math.round(parsed);\n }\n\n let raw: string;\n try {\n raw = await readFile(paths.config, \"utf8\");\n } catch {\n io.err(\n `no config at ${paths.config} — nothing to offer yet.\\n` +\n \"Write one, or run `byollm connect <url>` first.\\n\",\n );\n return 1;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n io.err(`${paths.config} is not valid JSON — fix it by hand first\\n`);\n return 1;\n }\n const result = DaemonConfig.safeParse(parsed);\n if (!result.success) {\n io.err(`${paths.config} is not a valid byollm config — fix it first\\n`);\n return 1;\n }\n\n const service = result.data.services[serviceKey];\n if (!service) {\n const known = Object.keys(result.data.services);\n io.err(\n `no service named \"${serviceKey}\" in ${paths.config}\\n` +\n (known.length > 0 ? `configured: ${known.join(\", \")}\\n` : \"\"),\n );\n return 2;\n }\n\n const descriptor = backendDescriptor(service.type);\n const baseUrl = service.baseUrl ?? descriptor.defaultBaseUrl;\n // **With the model.** `resolveCost` reads three things and this call passed\n // two, so it and `resolveConfig` answered differently about the same\n // service — the shape this codebase keeps deleting, arriving in a signature.\n //\n // What it cost: `glm-5.2:cloud` on `http://127.0.0.1:11434/v1` is metered,\n // because the `:cloud` tag means the work leaves the machine whatever the\n // address says. Without the model this saw a loopback URL, called it free,\n // required no consent, and wrote `offer: \"team\"` with no spend block. The\n // command then reported success truthfully — and the daemon, reading the\n // same service *with* the model, narrowed it straight back and told the\n // owner to run the command they had just run.\n // One decision, two views: `classifyCost` answers what it costs *and* why,\n // so a consent sentence can never describe a classification the code did\n // not make.\n const reason = classifyCost(service.type, baseUrl, service.model);\n const cost = reason.cost;\n const widening = scope !== \"private\";\n\n /**\n * The fundamental refusal runs first — ruled 2026-08-25.\n *\n * This check used to sit below the `--cap` one, and the ordering told\n * somebody the wrong thing twice. `byollm offer my-claude team --cap 2500`\n * answered \"sharing it costs you nothing. Drop --cap\" — advice whose whole\n * premise is that sharing is possible. Follow it, re-run, and only then\n * learn the service cannot be offered at all.\n *\n * A fixable detail must never precede an unfixable fact. The ceiling is a\n * flag somebody can drop; a subscription's terms are not a thing they can\n * negotiate, and a message that leads with the flag has buried the answer\n * behind an errand.\n *\n * Named by the service, not by the registry label. The label belongs in the\n * sentence — a subscription's cost *is* the registry's word — but the\n * subject is what the owner typed. \"Claude CLI (your subscription) runs on\n * your own subscription\" both stuttered and answered a question about a\n * service the owner never named.\n */\n if (cost === \"subscription\" && widening) {\n io.err(\n `${wrap(\n `${serviceKey} runs on ${backendName(service.type)}, a subscription ` +\n `whose terms cover your work and nobody else's. It cannot be ` +\n `offered to other people.`,\n )}\\n`,\n );\n return 1;\n }\n\n /**\n * **A flag this path will not use is an error, not a shrug.**\n *\n * `--cap` was parsed, validated, and then reached only the metered branch.\n * Ask to share a service this command believes is free and the ceiling\n * vanished silently — which is exactly what happened when the cost\n * calculation disagreed with the daemon's: the owner passed a ceiling, was\n * told the share succeeded, and got neither.\n *\n * Refusing costs one message and removes a class where a command accepts an\n * instruction it has no intention of following.\n *\n * **No class names.** This said \"my-ollama is free-class\" and \"my-claude is\n * subscription-class\", which is this codebase's vocabulary on somebody\n * else's screen — nobody's mental model has classes in it. A refusal says\n * what the class *means*: it runs on this machine, or it is not being\n * shared.\n *\n * And it ends with the command to run, because an error message is\n * documentation that arrives at the moment somebody needs it. \"Drop --cap\"\n * describes an edit; the line under it can be pasted.\n */\n if (capCents !== undefined && !(cost === \"metered\" && widening)) {\n const because =\n cost === \"metered\"\n ? \"is not being shared, so nothing would spend against it\"\n : \"runs on this machine, so sharing it costs you nothing\";\n io.err(\n `${wrap(`--cap sets a daily spend ceiling, and ${serviceKey} ${because}.`)}\\n` +\n `Drop --cap: \\`byollm offer ${serviceKey} ${scope}\\`\\n`,\n );\n return 2;\n }\n\n if (cost === \"metered\" && widening) {\n const cap = capCents ?? service.spend?.dailyCapCents;\n if (cap === undefined) {\n io.err(\n `${descriptor.label} bills you per token, so sharing it needs a daily\\n` +\n \"ceiling. Add one: `byollm offer \" +\n `${serviceKey} ${scope} --cap <cents>\\`\\n`,\n );\n return 2;\n }\n\n const dollars = (cap / 100).toFixed(2);\n // **Consent names the thing consented to, and the reason it is true.**\n //\n // This read \"This lets other people's jobs run on Any OpenAI-compatible\n // server, which bills your account per token\" — wrong twice. The registry\n // label is the *type*, not the service somebody is about to share; and\n // the type does not bill per token, since an owner's local qwen is the\n // same type and costs only electricity. So the sentence named the wrong\n // object and gave a reason its reader could check and find false.\n //\n // A label may classify. It may not be the object of consent.\n const where = service.baseUrl ?? descriptor.defaultBaseUrl;\n const confirmed = await io.confirm(\n `\\nThis lets other people's jobs run on ${serviceKey}:\\n` +\n ` ${service.model}${where === undefined ? \"\" : ` at ${where}`}\\n\\n` +\n // Wrapped, because the reason is assembled from a rule and cannot be\n // hard-wrapped where it is written. An unwrapped consent sentence runs\n // to 150 columns in a terminal, and a wrapped-by-the-terminal sentence\n // is one somebody skims.\n `${wrap(`It bills your account per token because ${reason.because}.`)}\\n\\n` +\n `${wrap(`You would be paying for their work, up to $${dollars} a day, every day, until you change it. Spending stops at that ceiling and resumes the next day.`)}\\n\\n` +\n `Offer ${serviceKey} to your team?`,\n );\n if (!confirmed) {\n io.out(\"nothing changed\\n\");\n return 0;\n }\n\n result.data.services[serviceKey] = {\n ...service,\n offer: scope,\n spend: {\n centsPerMillionTokens: service.spend?.centsPerMillionTokens ?? 1500,\n ...service.spend,\n acknowledged: true,\n dailyCapCents: cap,\n },\n };\n } else {\n result.data.services[serviceKey] = {\n ...service,\n offer: scope,\n // Narrowing back to `self` withdraws the consent too, so a later\n // widening has to be agreed to again rather than inherited.\n ...(cost === \"metered\" && !widening && service.spend !== undefined\n ? { spend: { ...service.spend, acknowledged: false } }\n : {}),\n };\n }\n\n await writeConfig(paths.config, result.data);\n\n const written = result.data.services[serviceKey].spend?.dailyCapCents;\n io.out(\n `${serviceKey} is now offered to ${scope}` +\n (written === undefined || !widening\n ? \"\"\n : `, capped at ${dollars(written)} a day`) +\n \"\\n\",\n );\n return 0;\n}\n\n// -- forget -------------------------------------------------------------------\n\nasync function commandForget(\n paths: DaemonPaths,\n args: readonly string[],\n io: CliIo,\n): Promise<ExitCode> {\n const target = args[0];\n if (target === undefined) {\n io.err(\"usage: byollm forget <app-url>\\n\");\n return 2;\n }\n const pairings = new Pairings(paths.pairings);\n await pairings.load();\n reportSkipped(pairings, io);\n const removed = await pairings.remove(normalizeOrigin(target));\n io.out(\n removed\n ? `forgot ${normalizeOrigin(target)} — the app may still list this runner ` +\n `until you revoke it there too\\n`\n : `not paired with ${normalizeOrigin(target)}\\n`,\n );\n return 0;\n}\n\n// -- sites / approve ---------------------------------------------------------\n\n/**\n * Which sites this machine serves, which are waiting on an answer, and which\n * it has served before — V1-1.\n *\n * The pinned fingerprints were already in `byollm status`; what was missing\n * was the *question*. A site the upstream added arrives on a heartbeat, and\n * before V1-1 it was simply pinned — so the one screen where somebody could\n * have compared a fingerprint never existed. This is that screen.\n */\nasync function commandSites(paths: DaemonPaths, io: CliIo): Promise<ExitCode> {\n const pairings = new Pairings(paths.pairings);\n await pairings.load();\n reportSkipped(pairings, io);\n\n const list = pairings.list();\n if (list.length === 0) {\n io.out(\"not paired with anything — run `byollm connect`\\n\");\n return 0;\n }\n\n /**\n * What this device serves, and what it still holds a key for.\n *\n * The waiting queue is gone with `byollm approve` (Amendment K) — there is\n * nothing here to answer any more, so this reports rather than asks.\n *\n * The pinned-but-not-offered rows stay, and they matter more now than they\n * did. A key kept for a site nobody is mentioning is exactly what somebody\n * should be able to see they are still holding, and with site policy moved\n * to the account it is the only place that list is visible on the machine.\n */\n for (const pairing of list) {\n io.out(`${pairing.origin}\\n`);\n const served = Object.entries(pairing.sites);\n if (served.length === 0) io.out(\" (serving nothing right now)\\n\");\n for (const [, site] of served) {\n io.out(` serving ${fingerprint(site.identity)}\\n`);\n }\n for (const id of Object.keys(pairing.known ?? {})) {\n if (id in pairing.sites) continue;\n io.out(` pinned ${id} (not offered right now)\\n`);\n }\n }\n return 0;\n}\n\n/**\n * `byollm approve`, retired — byollm_016 Amendment K.\n *\n * The device no longer decides which sites it serves. That is the largest\n * single reduction in device-side control in this design and it is\n * deliberate: site policy moves at account speed, where the person changing\n * it is signed in and can see what they are changing.\n *\n * What the machine kept is the pairing ceremony — where a human compares a\n * fingerprint — the pinning that refuses a key moving under an id, and the\n * grant check that refuses work no control plane signed for. What it gave up\n * is the per-site yes.\n *\n * The tombstone says so plainly rather than pointing at a replacement\n * command, because there is no replacement on this machine. It names the\n * levers that remain, since a refusal with nothing to do about it is noise.\n */\nfunction commandRetiredApprove(io: CliIo): 2 {\n io.err(\n `${wrap(\n \"`byollm approve` is gone. Which sites this device serves is decided \" +\n \"in your dashboard now, not here — and the first job from a new one \" +\n \"says so in `byollm run`, with its fingerprint.\",\n )}\\n\\n` +\n `${wrap(\n \"This device still refuses work no grant was signed for, still \" +\n \"refuses a site key that changes under an id it pinned, and still \" +\n \"stops entirely on `byollm stop`.\",\n )}\\n`,\n );\n return 2;\n}\n\n// -- backends ------------------------------------------------------------------\n\n/**\n * `byollm services manage` — the one screen, B100a.\n *\n * A thin caller on purpose. The conversation belongs to `services-manage.ts`\n * because `setup` runs the same one, and the two of them differ in exactly\n * two things: setup asks a device name first and finishes by pairing, and\n * this one starts from the config that is already there.\n *\n * **The starting state is the whole difference, and it is what makes this one\n * screen rather than two.** Todd: re-running *\"shows what is already there\"* —\n * current services pre-marked, with their shares and their caps — so somebody\n * who ran setup a month ago changes one row instead of rebuilding the list.\n */\nasync function commandServicesManage(\n paths: DaemonPaths,\n io: CliIo,\n rest: readonly string[],\n signal?: AbortSignal,\n /* The environment the supervisor is read from — B241. Threaded rather than\n read from the global so a test can say \"there is a supervisor\" without\n announcing it to every other test file in the process. */\n env: NodeJS.ProcessEnv = process.env,\n): Promise<ExitCode> {\n if (rest.length > 0) {\n io.err(\n `byollm services manage takes no arguments, and got ` +\n `${rest.map((arg) => JSON.stringify(arg)).join(\" \")}.\\n`,\n );\n return 2;\n }\n const terminal = terminalIo(\n (text) => {\n io.out(text);\n },\n (text) => {\n io.err(text);\n },\n );\n try {\n return await manageWith(paths, io, terminal, signal, env);\n } catch (error) {\n return endedOrThrow(error, io);\n } finally {\n terminal.close();\n }\n}\n\nasync function manageWith(\n paths: DaemonPaths,\n io: CliIo,\n terminal: TerminalIo,\n signal?: AbortSignal,\n /* The environment the supervisor is read from — B241, threaded rather than\n taken from the global. */\n env: NodeJS.ProcessEnv = process.env,\n): Promise<ExitCode> {\n const existing = await readExistingConfig(paths.config);\n const outcome = await manageServices({\n io: terminal,\n existing: existing?.services ?? {},\n });\n if (!outcome.decided) return 1;\n if (\n !(await writeManaged(\n paths.config,\n terminal,\n existing?.rest ?? {},\n outcome,\n env,\n ))\n )\n return 1;\n\n io.out(`\\nWrote ${paths.config}\\n${summarise(outcome).join(\"\\n\")}\\n`);\n /**\n * The daemon is told, or the screen is a screen about a file.\n *\n * A running daemon read its config at start. Somebody who just turned a\n * service on and watched the file be written has every reason to think the\n * device now offers it, and until a restart it does not — which is the\n * \"wizard that stops one step from done reads as done\" defect, one command\n * along.\n *\n * Said rather than done: restarting a daemon mid-job is not this command's\n * to decide, and `stop && start` is the pair that already exists.\n */\n /* Under a supervisor, TELL it rather than printing a pair of commands a box\n does not have — B207, duty three. `serviceIsInstalled` is false on a box,\n so before this the screen said nothing at all and the box picked the\n config up only on the supervisor's next retry, up to a minute later. */\n /* `writeManaged` has already told a supervisor, if there is one — B207. This\n is the other world: a systemd/launchd service, which nothing signals. */\n const installed =\n supervisorPid(env) === undefined &&\n (await serviceIsInstalled(serviceTarget(paths, defaultServiceIo())));\n if (installed) {\n io.out(\n \"\\nThe background service is running with the config it started on.\\n\" +\n \" byollm stop && byollm start pick this up\\n\",\n );\n }\n return signal?.aborted === true ? 1 : 0;\n}\n\nasync function commandServices(\n paths: DaemonPaths,\n io: CliIo,\n service: ServiceIo,\n): Promise<ExitCode> {\n /**\n * A fresh machine gets a next step, not an error — byollm_020.\n *\n * `services` is the only list now, and on a machine with no config it used\n * to fall through to a config read that fails. The command it replaced said\n * \"run `byollm setup`\", which is the one useful thing to say to somebody\n * who has just installed this and typed the obvious command. Losing that\n * sentence in a rename would be the rename costing a first impression.\n *\n * And the rename did lose it. With no file, `loadConfig` hands back\n * `DEFAULT_CONFIG`, so this listed an Ollama at 11434 nobody had configured,\n * probed it, and sent the reader to `byollm model` — which refused because\n * there was no config. Said before the probe, because a service that is in\n * no file is not one this machine offers.\n */\n const { loaded, configured, ingress, budgets, spend, spentGrants } =\n await context(paths);\n if (!configured) {\n io.out(\n \"services\\n\" +\n ` (none — no config at ${paths.config})\\n` +\n \"\\n\" +\n NOT_SET_UP_HINT,\n );\n return 1;\n }\n const pairings = new Pairings(paths.pairings);\n await pairings.load();\n const hasRelay = pairings\n .list()\n .some((pairing) => pairing.controlPlanePublic !== undefined);\n const runner = new Runner({\n client: new ProtocolClient({ origin: \"https://unused.invalid\" }),\n runnerId: \"local\",\n owner: \"local\",\n daemonVersion: DAEMON_VERSION,\n loaded,\n budgets,\n spend,\n spentGrants,\n ingress,\n });\n\n const advertised = await runner.detectCapabilities();\n const advertisedKinds = new Set(advertised.map((c) => c.kind));\n\n io.out(\"services\\n\");\n for (const route of loaded.routes) {\n const ok = advertisedKinds.has(route.kind);\n const pays =\n route.cost === \"free\"\n ? \"free (your electricity)\"\n : route.cost === \"subscription\"\n ? \"your subscription — locked to your work\"\n : route.spendAcknowledged\n ? `metered — ${route.offerScope}, cap ${dollars(route.spendDailyCapCents ?? 0)}/day`\n : \"metered — your money, not shared\";\n /**\n * Who it is offered to — which this command did not say at all.\n *\n * It printed the kind, the service, the backend, the model, the address\n * and who pays, and left out the one fact somebody runs `byollm services`\n * to check. Found on 2026-08-26 when the question \"is anything on this\n * machine offered publicly?\" had to be answered by reading config.json:\n * the surface built to answer it could not, and the surface that could\n * was the file the daemon does not necessarily agree with.\n *\n * Effective, therefore, not configured — for the reason `status` carries\n * the same note. A `team` request that the spend rules narrowed to\n * `private` is a service shared with nobody, and printing the request\n * would be reporting an intention as a state.\n */\n const summary = offerSummary({\n effective: route.offerScope,\n configured: loaded.config.services[route.service]?.offer,\n hasRelay,\n });\n const offered =\n `offered to ${summary.audience}` +\n (summary.narrowedBy === undefined ? \"\" : ` (${summary.narrowedBy})`);\n io.out(\n ` ${ok ? \"✓\" : \"✗\"} ${route.kind.padEnd(14)} ` +\n `${route.service} — ${route.backendId}:${route.model}` +\n `${route.baseUrl === undefined ? \"\" : ` @ ${route.baseUrl}`}\\n` +\n ` ${pays}\\n` +\n ` ${offered}\\n`,\n );\n // Why, and what to do about it — cloud_002's detection-first ruling.\n // \"0 of 2 routes are healthy\" is a true sentence that leaves the reader\n // exactly as stuck as before it was printed.\n if (!ok) {\n /**\n * Everything the probe learned, handed to the thing that explains it —\n * B098.\n *\n * The declared type because the finding is that it and the address can\n * disagree, and a diagnosis given only one of them cannot see it. The\n * `detail` because the probe that just ran is the only thing holding\n * the backend's own sentence, and this parameter had **no caller\n * passing it at all** — which is how \"Nothing is listening\" came to be\n * printed about servers that were answering.\n */\n const probed = runner.serviceStates.get(route.service)?.state;\n const detail =\n probed?.kind === \"signed-out\" || probed?.kind === \"blocked\"\n ? probed.detail\n : undefined;\n const hint = await diagnoseRoute({\n baseUrl: route.baseUrl,\n backendId: route.backendId,\n ...(detail === undefined ? {} : { detail }),\n });\n if (hint !== undefined) io.out(` ${hint}\\n`);\n }\n }\n // **Withheld is shown, never merely absent** — the same obligation `status`\n // carries, in the command an owner runs when they are asking exactly this\n // question.\n for (const held of loaded.withheld) {\n io.out(\n ` … ${held.kind.padEnd(14)} no default — ${String(held.services.length)} services ` +\n `answer it (${held.services.join(\", \")})\\n` +\n ` a job naming one of them runs; a job naming none has nowhere ` +\n `to go\\n` +\n ` set defaults.${held.kind} in ~/.byollm/config.json\\n`,\n );\n }\n for (const problem of loaded.problems) {\n io.out(` ! ${problem.where}: ${problem.message}\\n`);\n }\n // What the build cannot do yet, said where the owner is looking.\n for (const notice of loaded.notices) {\n io.out(` i ${notice}\\n`);\n }\n // **This command speaks for the shell it runs in, not for the daemon.**\n //\n // It used to say \"healthy and will be advertised\", which is a promise only\n // the daemon can make — and on the machine that produced this change, it was\n // false. The daemon runs under launchd with launchd's own PATH; `claude`\n // lives in `~/.local/bin`; so a probe here found the CLI, reported it\n // healthy, and the daemon could not execute it. The device advertised\n // nothing, and the surface a person turns to for \"why\" was the one lying.\n //\n // PATH was that machine's divergence and the installer now captures it, but\n // it is one source among many: a different user, a different HOME, a\n // credential visible in a login shell and not to a background agent. So the\n // wording no longer claims to know what the daemon sees. Saying less is the\n // fix; claiming to speak for a process you are not is the bug.\n // From the daemon's own paths, never `homedir()`. The first version read\n // the real home while the rest of this command read `paths` — so under a\n // test, or anywhere `BYOLLM_HOME` differs, it answered about a different\n // machine's state than the one it was describing. Caught by the control\n // asserting the warning is *absent* when nothing is installed, which is the\n // half of the pair that is easy not to write.\n // Built from the same target `install` uses, so the two agree about where\n // the unit lives. The first version called `serviceIsInstalled(homedir())`,\n // which read the real home while the rest of this command read `paths` — it\n // answered about a different machine's state than the one it was\n // describing, and would have done so anywhere `BYOLLM_HOME` differs. Caught\n // by the control asserting the warning is *absent* when nothing is\n // installed, which is the half of that pair that is easy not to write.\n /**\n * Models this machine is serving that nothing points at — B100b.\n *\n * Todd pulled `smollm2:135m`, `ollama list` showed five models, and this\n * command showed the two his config names. Nothing on any byollm surface\n * said the other three existed or how to reach one.\n *\n * The reader has existed the whole time: `probeLocalServers` pulls model\n * ids from an OpenAI-shaped `/v1/models`, and `setup` calls it once. After\n * onboarding nothing did — another instrument with no reader.\n *\n * **Read for DISPLAY only.** Cost is classified from the owner's configured\n * value and never from a server's list, which `backends.ts` states in as\n * many words, so nothing here reaches the router.\n */\n const servers = await probeLocalServers();\n /**\n * A service whose type contradicts the server behind it — B116.\n *\n * Printed here rather than only guarded in the writer, because the configs\n * that have this are the ones written before the probe could tell: it is on\n * Todd's own machine right now, and nothing on any screen said so. Above\n * the spare-models block, because it is about a service that exists rather\n * than one that could.\n */\n for (const line of misTypedReport(\n misTypedServices({ services: loaded.config.services, servers }),\n )) {\n io.out(`${line}\\n`);\n }\n\n for (const line of unusedModelsReport({\n servers,\n configured: Object.values(loaded.config.services).map((entry) => ({\n baseUrl: entry.baseUrl,\n model: entry.model,\n })),\n })) {\n io.out(`${line}\\n`);\n }\n\n const installed = await serviceIsInstalled(serviceTarget(paths, service));\n io.out(\n `\\n${String(advertised.length)} of ${String(loaded.routes.length)} services are ` +\n `healthy from this shell.\\n` +\n (loaded.withheld.length > 0\n ? `${String(loaded.withheld.length)} kind(s) are withheld until you pick a default.\\n`\n : \"\") +\n `A route that is not healthy is never offered to an app — the daemon does\\n` +\n `not advertise what it cannot actually run.\\n` +\n (installed\n ? `\\nThis is your shell's view. The daemon runs under a service manager\\n` +\n `with its own environment, so what it can reach may differ. Compare\\n` +\n `with the device's page, and after installing a new CLI run\\n` +\n `\\`byollm stop && byollm start\\` so the service picks up your PATH.\\n`\n : \"\"),\n );\n return advertised.length === 0 ? 1 : 0;\n}\n\n// -- shared -------------------------------------------------------------------\n\nasync function context(paths: DaemonPaths): Promise<{\n loaded: Awaited<ReturnType<typeof loadConfig>>;\n /**\n * Whether the owner has written a config at all.\n *\n * `loadConfig` answers a missing file with `DEFAULT_CONFIG`, which is right\n * for a daemon that has to run on something and wrong for a screen: on a\n * machine nobody had set up, `services` listed the default's Ollama as if\n * somebody had chosen it, `model` then refused to touch a file that did not\n * exist, and `status` read `running`. The two commands that describe this\n * machine need \"nothing written\" and \"this is what is written\" told apart.\n */\n configured: boolean;\n ingress: IngressLog;\n budgets: Budgets;\n spend: SpendLedger;\n spentGrants: SpentGrants;\n}> {\n const loaded = await loadConfig(paths.config);\n const configured = await access(paths.config).then(\n () => true,\n () => false,\n );\n const ingress = new IngressLog({\n path: paths.ingressLog,\n communityPromptDays: loaded.config.ingress.communityPromptDays,\n keepSelfPrompts: loaded.config.ingress.keepSelfPrompts,\n });\n const budgets = new Budgets(paths.budgets, loaded.config.community);\n await budgets.load(Date.now());\n const spend = new SpendLedger(paths.spend);\n // Loaded before the loop starts, so a restart inside a grant's freshness\n // window still knows what it already ran.\n const spentGrants = new SpentGrants(paths.spentGrants);\n spentGrants.load(Date.now());\n await spend.load(Date.now());\n return { loaded, configured, ingress, budgets, spend, spentGrants };\n}\n\n/**\n * The one useful thing to say to somebody who has just installed this and\n * typed the obvious command. `services` and `status` both say it, in the\n * same words, so a fresh machine cannot describe itself differently\n * depending on where you look.\n */\nconst NOT_SET_UP_HINT =\n \"Run `byollm setup` to find what this computer already has.\\n\";\n\n/**\n * Ask, on a real terminal.\n *\n * Refuses outright when stdin is not a TTY: widening who may use someone's\n * machine is not a thing to do on an implied yes from a script.\n */\nasync function confirmInteractively(question: string): Promise<boolean> {\n if (!process.stdin.isTTY) {\n process.stderr.write(\n \"refusing to widen access without an interactive confirmation\\n\",\n );\n return false;\n }\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n const answer = await rl.question(`${question} [y/N] `);\n return /^y(es)?$/i.test(answer.trim());\n } finally {\n rl.close();\n }\n}\n\n/**\n * How this machine appears in the app's runner list.\n *\n * `BYOLLM_LABEL` overrides it, because \"todd@Todds-MacBook-Pro\" is more than\n * some people want to hand an app they are only trying out.\n */\n\n/**\n * Wrap prose to a width a terminal will not re-wrap for us.\n *\n * Sentences assembled from a rule cannot be hard-wrapped where they are\n * written, and an unwrapped consent runs past 150 columns — where the terminal\n * breaks it mid-word and the reader skims. Consent that is not read is not\n * consent.\n */\nfunction wrap(text: string, width = 68): string {\n const out: string[] = [];\n let line = \"\";\n for (const word of text.split(/\\s+/)) {\n if (line === \"\") line = word;\n else if (`${line} ${word}`.length <= width) line = `${line} ${word}`;\n else {\n out.push(line);\n line = word;\n }\n }\n if (line !== \"\") out.push(line);\n return out.join(\"\\n\");\n}\n\nfunction hostLabel(): string {\n const override = process.env[\"BYOLLM_LABEL\"];\n if (override !== undefined && override !== \"\") return override.slice(0, 120);\n try {\n return `${userInfo().username}@${hostname()}`.slice(0, 120);\n } catch {\n return hostname().slice(0, 120);\n }\n}\n\n/** The `byollm` executable. */\nexport async function main(argv: readonly string[]): Promise<ExitCode> {\n try {\n return await runCli(argv);\n } catch (error) {\n /**\n * A mistyped address is a usage error, and it is answered here rather\n * than at each command that takes one.\n *\n * Centrally on purpose. The per-command version of this is a list that\n * has to be extended every time a command learns to take an address, and\n * a list like that does not grow when the code does — which is the exact\n * shape of the check that failed to catch the stop-ship this refusal\n * exists because of. Every caller of `normalizeOrigin` lands here for\n * free, including ones written after this comment.\n */\n if (error instanceof UnusableOrigin) {\n process.stderr.write(\n `${\n error.input.trim() === \"\"\n ? \"That is not a usable address\"\n : `\"${stripControlChars(error.input)}\" is not a usable address`\n }: ${error.reason}.\\n` +\n `Addresses look like https://app.example.com or localhost:8080.\\n`,\n );\n return 2;\n }\n process.stderr.write(\n `${error instanceof ClientError || error instanceof Error ? error.message : String(error)}\\n`,\n );\n return 1;\n }\n}\n\n/** Whether a path is there at all — the fallback leaves no other trace. */\nasync function exists(path: string): Promise<boolean> {\n return access(path).then(\n () => true,\n () => false,\n );\n}\n","/**\n * Make one value on a busy screen impossible to skip — walk finding, ux 09-03.\n *\n * The pairing flow prints three numbered steps, and step 2 holds the only\n * thing a person has to carry to another window. Todd watched his own code\n * expire while the terminal sat there: the code was on the screen, in a line\n * that looked exactly like the two lines around it.\n *\n * ## Reverse video rather than a colour\n *\n * The ruling asks for a background. A *chosen* background is a guess about\n * somebody's theme — dark text on a dark terminal is a value that has been\n * highlighted into invisibility — and reverse video asks the terminal to swap\n * whatever its own two colours are. It is the one emphasis that cannot\n * collide with a palette we were never told.\n *\n * ## And it disappears when nobody is watching\n *\n * Escapes are for a person at a terminal. Piped into a log, a file or a CI\n * transcript they are noise at best, and at worst noise *inside a value\n * somebody is about to paste*. So this is off unless stdout is a TTY, off\n * when `NO_COLOR` is set — the convention, honoured because a person who set\n * it has already said this once — and forced on only by `FORCE_COLOR`, which\n * exists for the terminal we failed to detect.\n */\nconst REVERSE = \"\\u001b[7m\";\nconst BOLD = \"\\u001b[1m\";\nconst RESET = \"\\u001b[0m\";\n\nexport interface EmphasisContext {\n /** Is anybody looking? */\n readonly tty: boolean;\n readonly env: Readonly<Record<string, string | undefined>>;\n}\n\n/** Whether escapes may be written at all, given the terminal and the person. */\nexport function emphasisAllowed(context: EmphasisContext): boolean {\n // Set to anything, including the empty string — the convention is presence,\n // not value, and reading it as a boolean would ignore `NO_COLOR=`.\n if (context.env[\"NO_COLOR\"] !== undefined) return false;\n /*\n * `FORCE_COLOR` is read by value, and that is not an inconsistency.\n *\n * The two variables are different kinds of statement. `NO_COLOR` exists to\n * be *set*, and its convention is explicitly presence-not-value.\n * `FORCE_COLOR` carries a level, and `FORCE_COLOR=0` means **off** to every\n * tool that reads it — so treating it as presence turned the one variable a\n * person uses to disable colour in a pipeline into a switch that forced it\n * on. That is escapes inside a code somebody is about to paste, produced by\n * the setting they used to prevent exactly that.\n *\n * Anything else set is a level, and any level is on.\n */\n const forced = context.env[\"FORCE_COLOR\"];\n if (forced !== undefined) return forced !== \"0\" && forced !== \"\";\n return context.tty;\n}\n\n/**\n * The value, wrapped so the eye lands on it — or exactly the value, unchanged.\n *\n * Never the label with it. What somebody carries to the other window is the\n * code, and a highlight that swallowed \"Enter code:\" would make the block of\n * emphasis bigger than the thing emphasised, which is how emphasis stops\n * meaning anything.\n */\nexport function emphasise(value: string, context: EmphasisContext): string {\n return emphasisAllowed(context)\n ? `${REVERSE}${BOLD} ${value} ${RESET}`\n : value;\n}\n\n/** What the process itself is, for the CLI's own calls. */\nexport function terminalContext(): EmphasisContext {\n return {\n // `@types/node` declares `isTTY` as `boolean`, and Node sets it to\n // `undefined` when the stream is not a terminal. So this comparison is\n // load-bearing even though the type says it cannot be — the linter is\n // reasoning from a declaration that is wrong about its own runtime, and\n // deleting it puts `undefined` into a field typed `boolean`. The same\n // note stands over `setup.ts`, which met this first.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-boolean-literal-compare\n tty: process.stdout.isTTY === true,\n env: process.env,\n };\n}\n","import { readFile } from \"node:fs/promises\";\nimport type { BackendId } from \"@byollm/protocol\";\nimport { writeConfig } from \"./config.js\";\nimport { knownModelsFor } from \"./known-models.js\";\n\n/**\n * `byollm model` — byollm_017 Phase 1.\n *\n * ## The config on the device is the truth (ruling 1)\n *\n * Every path to a model change ends here: the daemon writes\n * `services.<name>.model` and re-announces. There is no hub-side \"desired\n * model\" column that a device may or may not honour, because a cloud row that\n * disagrees with the machine is two truths and the machine is the one that\n * runs the job.\n *\n * ## Found is not works (ruling 2)\n *\n * A candidate is probed with one real call before it is written, and the\n * CLI's own first line comes back on refusal — \"model not found\", \"not\n * available on your plan\", \"needs sign-in\". A model that cannot answer is\n * never stored, which is what makes free text safe: the promise is that a\n * model released this morning works this morning, and the check that keeps\n * that honest is a probe rather than a list.\n *\n * On refusal the config is byte-identical afterwards. Not \"restored\" —\n * untouched, because nothing is written until the probe answers.\n */\n\nexport interface ModelIo {\n readonly out: (text: string) => void;\n readonly err: (text: string) => void;\n}\n\n/** What the caller needs to know without this module owning the process. */\nexport interface ModelResult {\n readonly changed: boolean;\n readonly code: 0 | 1 | 2;\n}\n\ninterface ConfigShape {\n services?: Record<string, { type?: string; model?: string }>;\n}\n\nasync function readConfig(path: string): Promise<ConfigShape | undefined> {\n try {\n return JSON.parse(await readFile(path, \"utf8\")) as ConfigShape;\n } catch {\n return undefined;\n }\n}\n\n/**\n * One service's model, and what its CLI is known to accept.\n *\n * The suggestions are printed as suggestions. Free text is the promise\n * (ruling 3), so this must not read as a menu — the sentence says \"known to\n * this build\", which is both true and a hint that a newer name is fine.\n */\nexport async function showModel(\n configPath: string,\n service: string,\n io: ModelIo,\n): Promise<ModelResult> {\n const config = await readConfig(configPath);\n const entry = config?.services?.[service];\n if (entry === undefined) {\n io.err(\n `No service called ${JSON.stringify(service)} in ${configPath}.\\n` +\n \"Run `byollm services` to see what this device has.\\n\",\n );\n return { changed: false, code: 1 };\n }\n io.out(` ${service}: ${entry.model ?? \"(no model set)\"}\\n`);\n const known = knownModelsFor((entry.type ?? \"\") as BackendId);\n if (known.length > 0) {\n io.out(\n `\\n Known to this build: ${known.join(\", \")}\\n` +\n \" Any name its CLI accepts works — this checks before it saves.\\n\",\n );\n }\n return { changed: false, code: 0 };\n}\n\n/**\n * The probe, as a function rather than as a closure in the router.\n *\n * It lived inline in `commandModel`, where it could only be exercised by\n * running the real backend — which for a local server means a network call\n * whose answer depends on whether Ollama happens to be running on the machine\n * executing the tests. \"Hard to test\" was the signal that it was in the wrong\n * file.\n *\n * One definition of \"answers\", shared with `setup`: health first, then the\n * cheapest true call the backend has. A backend with no canary returns\n * `undefined` — not asked, which is not no.\n */\nexport function backendVerifier(\n make: (id: BackendId) => {\n canary?: (model: string) => Promise<{ healthy: boolean; detail?: string }>;\n },\n) {\n return async (\n id: BackendId,\n candidate: string,\n ): Promise<{ answers: boolean | undefined; detail?: string | undefined }> => {\n const backend = make(id);\n if (backend.canary === undefined) return { answers: undefined };\n const proof = await backend.canary(candidate);\n return {\n answers: proof.healthy,\n ...(proof.detail === undefined ? {} : { detail: proof.detail }),\n };\n };\n}\n\n/**\n * Set it, having proved it answers.\n *\n * `verify` is injected because the real one spawns the vendor CLI, and\n * because this is the one function whose whole contract is \"what happens when\n * the probe says no\".\n */\nexport async function setModel(\n input: {\n readonly configPath: string;\n readonly service: string;\n readonly model: string;\n },\n io: ModelIo,\n verify: (\n id: BackendId,\n model: string,\n ) => Promise<{ answers: boolean | undefined; detail?: string | undefined }>,\n /**\n * The memory guard, as a PROMPT rather than a refusal — B106.\n *\n * `guardApplies` and `memoryGate` lived in `runner.ts` and nowhere else, so\n * the whole of B080/B090 protected the job path only — and this command is\n * a documented, guard-free model load. `verify` calls the backend's canary,\n * which is *\"the cheapest true call the backend has\"*, and for a local\n * server a true call loads the model. That is H2, the shape that wedged\n * Todd's machine on 09-08, reachable from a line in the help text.\n *\n * **A prompt, because an owner typing this IS consent** — which a remote\n * job is not, and refusing here would be second-guessing somebody about\n * their own machine. But the failure mode does not care who asked, so\n * below the floor they are told what is about to be loaded and what is\n * left, and asked.\n *\n * **Above the floor it says nothing.** A guard that narrates on the happy\n * path teaches people to skip reading it, and then it is furniture on the\n * day it matters.\n *\n * Absent means no check — the same shape as `spawnServer` and\n * `readMemory` on the Runner. `byollm status` is where an owner learns the\n * guard is not active on this machine.\n */\n memoryCheck?: (\n backendId: BackendId,\n ) => Promise<\n { readonly ask: false } | { readonly ask: true; readonly question: string }\n >,\n confirm?: (question: string) => Promise<boolean>,\n): Promise<ModelResult> {\n const raw = await readFile(input.configPath, \"utf8\").catch(() => undefined);\n if (raw === undefined) {\n io.err(`No config at ${input.configPath}. Run \\`byollm setup\\` first.\\n`);\n return { changed: false, code: 1 };\n }\n const config = JSON.parse(raw) as ConfigShape;\n const entry = config.services?.[input.service];\n if (entry === undefined) {\n io.err(\n `No service called ${JSON.stringify(input.service)}.\\n` +\n \"Run `byollm services` to see what this device has.\\n\",\n );\n return { changed: false, code: 1 };\n }\n if (entry.model === input.model) {\n // Not an error and not a write. Saying so is cheaper than a probe, and a\n // no-op that reported success would be indistinguishable from one that\n // had actually checked.\n io.out(` ${input.service} is already on ${input.model}.\\n`);\n return { changed: false, code: 0 };\n }\n\n /**\n * Asked BEFORE the canary, which is the whole point — B106.\n *\n * The canary is the thing that loads the model. A check after it would\n * describe a machine that had already been wedged, which is the ordering\n * mistake the job path was careful about and this path never had.\n */\n const backendId = (entry.type ?? \"\") as BackendId;\n if (memoryCheck !== undefined) {\n const verdict = await memoryCheck(backendId);\n if (verdict.ask) {\n const goAhead =\n confirm === undefined ? false : await confirm(verdict.question);\n if (!goAhead) {\n io.err(\n `\\n Nothing was changed — ${input.service} is still on ` +\n `${entry.model ?? \"its previous model\"}.\\n`,\n );\n return { changed: false, code: 1 };\n }\n }\n }\n\n io.out(` Checking ${input.model} answers on ${input.service}…\\n`);\n const proof = await verify(backendId, input.model);\n if (proof.answers === false) {\n io.err(\n `\\n ${input.service} refused ${input.model}:\\n` +\n (proof.detail === undefined ? \"\" : ` ${proof.detail}\\n`) +\n `\\n Nothing was changed — ${input.service} is still on ` +\n `${entry.model ?? \"its previous model\"}.\\n`,\n );\n return { changed: false, code: 1 };\n }\n\n /**\n * `undefined` is not a refusal.\n *\n * A backend with no canary cannot be asked, and reading \"not asked\" as\n * \"does not work\" would make this command impossible for every local model\n * server — the same third state that had to be defended in `setup`. It is\n * written, and the daemon's own health reporting says the rest.\n */\n if (proof.answers === undefined) {\n io.out(\n ` ${input.service} has no way to check a model before use — ` +\n \"saving it.\\n\",\n );\n }\n\n entry.model = input.model;\n await writeConfig(input.configPath, config);\n io.out(\n `\\n ${input.service} is now on ${input.model}.\\n` +\n \" Restart the daemon to pick it up: `byollm start` if it runs in \" +\n \"the\\n background, or Ctrl-C and `byollm run` if it is in a \" +\n \"terminal.\\n\",\n );\n return { changed: true, code: 0 };\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport {\n BackendIdSchema,\n JobKind,\n OfferScope,\n backendDescriptor,\n effectiveOfferScope,\n classifyCost,\n type BackendCost,\n type BackendId,\n} from \"@byollm/protocol\";\nimport { z } from \"zod\";\nimport { dirname } from \"node:path\";\nimport { DEFAULT_FLOOR_BYTES } from \"./memory-gate.js\";\nimport { normalizeOrigin } from \"./origins.js\";\nimport { checkBaseUrl } from \"./ssrf.js\";\n\n/**\n * One configured backend instance.\n *\n * `openai-http` may be configured many times — one per model server the owner\n * runs (Ollama here, `mlx_lm.server` there, a llama.cpp box on the LAN). That\n * is byollm_001 Rev 1 §A's \"one backend, N base URLs\".\n */\nexport const ServiceConfig = z\n .object({\n /**\n * How it is reached — the transport, not the thing.\n *\n * Renamed from `backend` because that was the noun doing the work, and it\n * named the wrong thing: an owner running two Ollama models has two\n * services and one transport. The service is what they think about and\n * what they will name; the transport is a detail of reaching it.\n */\n type: BackendIdSchema,\n /** Required for HTTP-class transports; meaningless for process-class. */\n baseUrl: z.string().optional(),\n /**\n * The model this service serves. Owner-chosen; a payload can never\n * influence it ({@link MUSTS.NO_PAYLOAD_ROUTING}).\n *\n * Moved here from the route, which is the whole reorganization in one\n * field: the model was a property of *what serves a kind*, and that thing\n * had no name. Now it does.\n */\n model: z.string().min(1),\n /**\n * What this service answers.\n *\n * Declared, then detected — declaring a kind does not advertise it\n * ({@link MUSTS.CAPABILITY_IS_DETECTED}). A kind that cannot be served is\n * dropped with a loud problem, never a silent advertisement.\n */\n kinds: z.array(JobKind).min(1),\n /**\n * What the owner is willing to run for others, **per service** — which is\n * what owners actually mean. Subscription-class services ignore this and\n * are locked to `self` ({@link MUSTS.SUBSCRIPTION_SELF_LOCK}).\n */\n offer: OfferScope.default(\"private\"),\n /**\n * Name of an environment variable holding this backend's API key, for a\n * remote OpenAI-compatible server that needs one. The *name*, never the\n * value — a key does not belong in a config file the owner may share.\n */\n apiKeyEnv: z.string().optional(),\n /**\n * Required before a `metered` backend may be offered past `self`\n * ({@link MUSTS.METERED_DEFAULTS_SELF}). There is deliberately no `cost`\n * field: a provider's cost class comes from the protocol registry and is\n * not the owner's to declare ({@link MUSTS.COST_NOT_CONFIGURABLE}).\n */\n spend: z\n .object({\n /** Set by `byollm offer`, which states the consequence in words. */\n acknowledged: z.boolean().default(false),\n /**\n * Ceiling on community spend, in whole cents, per day. A widened\n * metered backend without one is refused\n * ({@link MUSTS.METERED_REQUIRES_CEILING}) — \"unlimited\" is not a\n * thing an owner can mean by accident.\n */\n dailyCapCents: z.number().int().positive().optional(),\n /**\n * What this provider charges, for the ceiling estimate. Providers do\n * not return a price, so the owner supplies one; the default is\n * deliberately high so an unset rate trips the brake early rather\n * than late.\n */\n centsPerMillionTokens: z.number().positive().default(1500),\n })\n .strict()\n .optional(),\n })\n .strict();\nexport type ServiceConfig = z.infer<typeof ServiceConfig>;\n\n/**\n * Budgets applied to jobs whose owner is not this machine's owner\n * ({@link MUSTS.COMMUNITY_BUDGETS}).\n */\nexport const CommunityBudget = z\n .object({\n maxJobsPerHour: z.number().int().positive().default(20),\n maxJobsPerDay: z.number().int().positive().default(100),\n /** Wall-clock ceiling for one community job. */\n maxWallClockMs: z.number().int().positive().default(120_000),\n /** Output-size ceiling for one community job. */\n maxOutputBytes: z\n .number()\n .int()\n .positive()\n .default(256 * 1024),\n /** Payload-size ceiling — stricter than the protocol's absolute limit. */\n maxPayloadChars: z.number().int().positive().default(100_000),\n })\n .strict();\nexport type CommunityBudget = z.infer<typeof CommunityBudget>;\n\n/** Ingress-log retention (byollm_004 Rev 1). */\nexport const IngressRetention = z\n .object({\n /**\n * How long a `named`/`public` prompt is kept in full before being reduced\n * to a hash. A volunteer must not indefinitely retain strangers' content.\n */\n communityPromptDays: z.number().int().positive().default(7),\n /** Whether the owner's own prompts are kept in full. Their call. */\n keepSelfPrompts: z.boolean().default(true),\n })\n .strict();\nexport type IngressRetention = z.infer<typeof IngressRetention>;\n\n/** Ceilings applied to every job, community or not. */\nexport const Limits = z\n .object({\n maxWallClockMs: z.number().int().positive().default(600_000),\n maxOutputBytes: z\n .number()\n .int()\n .positive()\n .default(4 * 1024 * 1024),\n })\n .strict();\nexport type Limits = z.infer<typeof Limits>;\n\n/**\n * The version of `~/.byollm/config.json`'s shape — B236.\n *\n * ## Why a file with one version needs a version\n *\n * This file is precious and it is the owner's. It holds the services they\n * configured, the defaults they chose, the spend caps they acknowledged — and\n * until now it carried nothing that said which shape it was. That is fine\n * exactly once. The first time the shape has to change, every file on every\n * machine is of indeterminate age, and the only way to read one is to guess\n * from its contents, which is how a migration comes to be a heuristic.\n *\n * Trivial now, painful later, and the later is not hypothetical: B229's setup\n * wizard and B230's installer both WRITE this file, and they have to write it\n * versioned from their first line or the field arrives after the files it was\n * meant to date.\n *\n * ## Absent means 1, permanently\n *\n * Every config written before this field exists is a version-1 config — that\n * is not a convention, it is what those files are. So the rule is permanent\n * rather than transitional, and it is the one migration this chain actually\n * performs today. A mechanism whose only entry is unreachable is a mechanism\n * nobody has run; this one runs on every config on every machine that has one.\n *\n * ## A newer file is refused, in words\n *\n * A config written by a future byollm is not a malformed config, and saying\n * \"unrecognized key\" about it sends its owner to delete a field they were\n * told to add. {@link loadConfig} reads the version BEFORE the schema, so a\n * version this build does not know is refused by number rather than by\n * whatever the shape happened to do.\n */\nexport const CONFIG_VERSION = 1;\n\n/**\n * Bring a config of any known vintage up to {@link CONFIG_VERSION}.\n *\n * Reads the raw parsed JSON, before the schema, because a migration's whole\n * job is to turn a shape this build does not accept into one it does.\n *\n * Returns the object to hand to the schema. Throws {@link ConfigTooNew} when\n * the file is from ahead of this build, which is not a migration's business —\n * you cannot migrate downwards without knowing what was added.\n */\nexport function migrateConfig(raw: unknown, path: string): unknown {\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n /* Not an object at all: let the schema say so in its own words, which are\n better than anything this function could invent about a file holding a\n number or a list. */\n return raw;\n }\n const declared = (raw as { version?: unknown }).version;\n\n if (declared === undefined) {\n /* The unversioned era. These files ARE version 1 — the shape they hold is\n the shape this build reads — so the migration is to say so. */\n return { ...raw, version: CONFIG_VERSION };\n }\n\n if (declared === CONFIG_VERSION) return raw;\n\n throw new ConfigTooNew(path, declared);\n}\n\n/**\n * A config from a byollm newer than this one.\n *\n * Its own error type because the caller's options differ from every other\n * config failure: nothing about the file is wrong, and editing it is the one\n * thing not to do.\n */\nexport class ConfigTooNew extends Error {\n constructor(\n readonly path: string,\n readonly declared: unknown,\n ) {\n super(\n `${path} was written by a newer byollm (config version ` +\n `${JSON.stringify(declared)}); this one reads version ` +\n `${String(CONFIG_VERSION)}.\\n` +\n ` Nothing is wrong with the file — this daemon is behind it.\\n` +\n ` Upgrade byollm rather than editing the config: a field you delete ` +\n `to make this\\n message go away is a setting the newer byollm is ` +\n `still going to look for.`,\n );\n this.name = \"ConfigTooNew\";\n }\n}\n\nexport const DaemonConfig = z\n .object({\n /**\n * The services this device runs, by the names their owner gave them.\n *\n * A name is the owner's word — `qwen`, `gwen-voice`, `claude` — and it is\n * what a job may later select. It is never a model id and never a vendor.\n */\n /**\n * Which shape this file is — B236. See {@link CONFIG_VERSION}.\n *\n * Defaulted rather than required, because {@link migrateConfig} has\n * already put it there by the time the schema sees a real config and a\n * config assembled in code should not have to restate it. The literal is\n * the point: a file declaring any other version never reaches the schema,\n * because `loadConfig` reads the number first and refuses by number.\n */\n version: z.literal(CONFIG_VERSION).default(CONFIG_VERSION),\n services: z.record(z.string().min(1), ServiceConfig),\n /**\n * Which service serves a kind when a job does not say.\n *\n * Optional, and it earns its place only under ambiguity: one service\n * offering a kind simply serves it. Two or more and the config will not\n * load without a default — loudly, in the owner's terminal, rather than\n * as a job-time mystery three hops away.\n *\n * `partialRecord`, not `record`: an owner with an ambiguous\n * `llm.generate` and a single `llm.chat` needs a default for one and not\n * the other.\n */\n defaults: z.partialRecord(JobKind, z.string().min(1)).prefault({}),\n /** How many jobs to run at once. */\n concurrency: z.number().int().min(1).max(32).default(2),\n /**\n * Take updates the hub offers, without being asked each time — B053.\n *\n * **Default off, and that is this release only.** The ruling (016\n * §Auto-update) puts it on by default for personal machines behind\n * setup's \"Keep byollm up to date automatically? [Y/n]\" — one release\n * after this one, so the mechanism ships and gets watched on the fleet\n * we own before it touches anybody's laptop. Hosted devices set it true\n * at provision and are told so plainly on the provision page.\n *\n * Off is also the honest default for a config that predates the field:\n * a machine whose owner has never been asked has not said yes, and\n * turning it on for them because they upgraded is exactly the consent\n * shortcut this product does not take.\n *\n * The trade this switch represents is written down rather than\n * discovered: an auto-updating daemon means whoever controls the npm\n * publish controls the fleet. The controls on that are 2FA at publish,\n * exact-version installs (never a tag), forward-only versions, offers\n * from {@link updateAuthority} alone, SLSA provenance checked before the\n * new binary runs, and the canary and rollback in `update.ts`. Staged\n * cohorts are NOT among them yet: nothing in this file or on the wire\n * spreads an offer over time, so every machine the authority offers a\n * version to takes it on its next heartbeat. It is the Chrome trade,\n * taken deliberately.\n */\n autoUpdate: z.boolean().default(false),\n /**\n * The one origin whose update offers this daemon takes — B360.\n *\n * Unset means the reference hub (`DEFAULT_ORIGIN` in `cli.ts`), and it is\n * resolved there rather than defaulted here on purpose: `byollm offer`\n * writes the PARSED config back, so a schema default would be baked into\n * every owner's file — and an older daemon, which is exactly what a\n * rollback installs, refuses a key its strict schema has never seen.\n *\n * Every other paired origin's offer is ignored and said once. The\n * authority's own offers count only over a pairing that pinned a control\n * plane; a direct-mode pairing never updates the daemon.\n */\n updateAuthority: z\n .string()\n .refine(\n (value) => {\n try {\n normalizeOrigin(value);\n return true;\n } catch {\n return false;\n }\n },\n {\n message:\n \"an origin byollm can reach, the way `byollm connect` takes \" +\n \"one (https://hub.byollm.cloud)\",\n },\n )\n .optional(),\n /**\n * The memory floor this device refuses model work below — B090.\n *\n * Named for what it is rather than for what it reads. Both halves of\n * `minAvailableMemoryBytes` are load-bearing and each closes a different\n * misreading:\n *\n * **`min`** stops an owner setting their machine's TOTAL here. A field\n * called `availableMemoryBytes` reads as a *reading* — \"tell byollm how\n * much memory this machine has\" — and 24 GB in that box refuses every\n * job forever. That is the broken-closed failure this whole guard exists\n * to prevent, arriving through the config instead of through\n * `os.freemem()`.\n *\n * **`available`** is the distinction the guard was built on: 0.28 GB\n * free against 7.51 GB available, on the machine that actually wedged.\n * Free is not available, and a floor compared against the wrong one is a\n * floor that fires on a machine that is fine.\n *\n * ## Two gigabytes, and the Windows argument is what sets it\n *\n * Low on purpose. An 8 GB floor would refuse on the laptop this guard\n * was written for while it holds 7.5 GB and serves jobs — `os.freemem()`\n * wearing a different number. But it cannot be token either:\n * {@link readPressure} returns `\"unknown\"` on every platform except\n * darwin and linux, **so on Windows this floor is the only guard there\n * is**, and a model server needs headroom past the weights for KV cache\n * and runtime.\n *\n * Owners with a large model are told to raise it — the release note says\n * so — because byollm does not check whether a model FITS, only whether\n * there is room to be going on with. That asymmetry is deliberate: the\n * size of a model is a fact about a file we do not have, and guessing it\n * would be the kind of derivation this codebase keeps refusing.\n */\n minAvailableMemoryBytes: z\n .number()\n .int()\n .positive()\n /* The gate's own constant, not a second 2 GB written here. One fact in\n two places is how the release runbook came to say four packages when\n there were six — and a floor that disagreed with the gate's default\n would be that bug with a machine wedging at the end of it. */\n .default(DEFAULT_FLOOR_BYTES),\n // `prefault`, not `default`: zod 4's `.default()` takes an *output* value,\n // which would mean restating every nested default here where it could\n // drift. `prefault` feeds `{}` through the schema so the nested defaults\n // stay the single source of truth.\n community: CommunityBudget.prefault({}),\n ingress: IngressRetention.prefault({}),\n limits: Limits.prefault({}),\n })\n .strict();\nexport type DaemonConfig = z.infer<typeof DaemonConfig>;\n\n/** One kind, served by one named service, ready to execute. */\nexport interface ResolvedRoute {\n readonly kind: z.infer<typeof JobKind>;\n /**\n * The owner's name for the service serving this kind.\n *\n * Carried onto the wire in the capability matrix, so a device advertises\n * *which* of its services answers a kind rather than only that something\n * does. Phase B lets a job select by this name.\n */\n readonly service: string;\n /**\n * Whether this is the service an *unselected* job for this kind reaches.\n *\n * Phase B advertises the whole menu, so a kind may have several routes and\n * exactly one of them is where a job that named nothing goes. Stated rather\n * than inferred from position: the previous shape advertised only the\n * winner, so \"the route for this kind\" and \"the default for this kind\" were\n * the same object, and every consumer that took the first match was correct\n * by accident.\n *\n * False for a kind that is withheld — nothing is the default there, which\n * is the whole meaning of withheld.\n */\n readonly isDefault: boolean;\n readonly backendId: BackendId;\n readonly backendClass: \"http\" | \"process\";\n readonly model: string;\n /** Who pays for this route's tokens. From the registry, never from config. */\n readonly cost: BackendCost;\n /** After the cost rules are applied — never the raw configured value. */\n readonly offerScope: z.infer<typeof OfferScope>;\n /** The owner's spend consent, for a `metered` route. */\n readonly spendAcknowledged: boolean;\n readonly spendDailyCapCents: number | undefined;\n readonly spendCentsPerMillionTokens: number;\n readonly baseUrl: string | undefined;\n readonly apiKeyEnv: string | undefined;\n}\n\nexport interface ConfigProblem {\n readonly where: string;\n readonly message: string;\n}\n\n/**\n * A kind this device could serve and deliberately does not.\n *\n * Withholding has to be *loud on the owner's side*, in every surface that\n * could otherwise show absence. An owner who adds a second `llm.generate`\n * service and finds their team's jobs quietly stop matching, with nothing\n * anywhere saying why, has been failed by the correct behaviour — which is\n * the quiet kind of correct this project keeps deleting.\n */\ninterface WithheldKind {\n readonly kind: z.infer<typeof JobKind>;\n /** The services that answer it, any of which the owner could name. */\n readonly services: readonly string[];\n}\n\nexport interface LoadedConfig {\n readonly config: DaemonConfig;\n readonly routes: readonly ResolvedRoute[];\n /** Kinds not advertised because the owner has not said which service wins. */\n readonly withheld: readonly WithheldKind[];\n /**\n * What this build cannot yet do, in the owner's words.\n *\n * Separate from `problems` on purpose: a problem means the config says\n * something wrong and something was dropped. A notice means the config is\n * fine and *the build* is behind it. Folding the two would make every\n * limitation read as an owner's mistake — and would make a genuine mistake\n * easier to miss among them.\n */\n readonly notices: readonly string[];\n /** Non-fatal problems: a route that cannot be served is dropped, not fatal. */\n readonly problems: readonly ConfigProblem[];\n}\n\n/** The config used when the owner has not written one. */\nexport const DEFAULT_CONFIG: DaemonConfig = DaemonConfig.parse({\n services: {\n ollama: {\n type: \"openai-http\",\n baseUrl: \"http://127.0.0.1:11434/v1\",\n model: \"llama3.2\",\n kinds: [\"llm.generate\", \"llm.chat\"],\n },\n },\n});\n\n/**\n * Read and resolve `~/.byollm/config.json`.\n *\n * A missing file yields {@link DEFAULT_CONFIG}; a malformed one throws,\n * because silently running with defaults when the owner *did* write a config\n * would execute work under rules they did not choose.\n */\nexport async function loadConfig(path: string): Promise<LoadedConfig> {\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch (error) {\n if (isNotFound(error)) return resolveConfig(DEFAULT_CONFIG);\n throw error;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (error) {\n throw new Error(\n `${path} is not valid JSON: ${error instanceof Error ? error.message : \"unknown error\"}`,\n { cause: error },\n );\n }\n\n /* The version first, and before the schema — B236. A file from a newer\n byollm is not a malformed file, and the schema can only ever say\n \"unrecognized key\" about it, which sends its owner to delete a field they\n were told to add. */\n const migrated = migrateConfig(parsed, path);\n\n const result = DaemonConfig.safeParse(migrated);\n if (!result.success) {\n const issues = result.error.issues\n .map((issue) => ` ${issue.path.join(\".\") || \"(root)\"}: ${issue.message}`)\n .join(\"\\n\");\n throw new Error(`${path} is not a valid byollm config:\\n${issues}`);\n }\n return resolveConfig(result.data);\n}\n\n/**\n * The one writer of `~/.byollm/config.json` — B236.\n *\n * ## Why there is exactly one\n *\n * There were three, and they disagreed about what they wrote. `byollm model`\n * and `byollm offer` wrote the PARSED config, so every schema default was\n * baked into the owner's file; `byollm services` wrote a merged raw object,\n * so none of them were. Neither is wrong on its own and the pair is: the same\n * file means two different things depending on which command last touched it.\n *\n * That was survivable while the file carried no facts about itself. It stops\n * being survivable the moment it carries its own version, because a version\n * written by two of three doors is a version you cannot trust to be there —\n * and the one thing a version is for is being trusted to be there.\n *\n * So: one writer, it stamps the version, and\n * `the-config-has-one-writer.test.ts` asserts there is no second. B229's\n * wizard and B230's installer inherit the stamp by calling this instead of\n * remembering a field.\n *\n * ## It stamps the version and nothing else\n *\n * Deliberately not `DaemonConfig.parse(config)`. Baking every default into\n * somebody's file turns their two-line config into forty lines of things they\n * never chose, and freezes today's defaults into a file that would otherwise\n * follow them. The version is the one field that must be present because it\n * describes the file rather than configures the daemon.\n */\nexport async function writeConfig(path: string, config: object): Promise<void> {\n /* The caller's version is dropped rather than merged, and the stamp goes\n first so the field a reader needs is the field a reader sees. Spreading\n the caller last would let a config object carrying `version: 2` write a\n file this build cannot read back — a writer that can emit what its own\n loader refuses is not a writer, it is a second format. */\n const { version: _stated, ...rest } = config as Record<string, unknown>;\n await mkdir(dirname(path), { recursive: true });\n await writeFile(\n path,\n `${JSON.stringify({ version: CONFIG_VERSION, ...rest }, null, 2)}\\n`,\n );\n}\n\n/**\n * Turn a parsed config into executable routes, applying the subscription lock\n * and rejecting unusable backends.\n *\n * A problem here is not fatal: a machine with three routes and one broken\n * backend should serve the other two and say so, rather than refusing to\n * start. What it must never do is *advertise* the broken one\n * ({@link MUSTS.CAPABILITY_IS_DETECTED}).\n */\nexport function resolveConfig(config: DaemonConfig): LoadedConfig {\n const routes: ResolvedRoute[] = [];\n const problems: ConfigProblem[] = [];\n const withheld: WithheldKind[] = [];\n const notices: string[] = [];\n\n /**\n * Who claims each kind, decided before anything is resolved.\n *\n * Ambiguity is a property of the whole config, not of one service, so it\n * cannot be judged inside the loop that walks them.\n */\n const claimants = new Map<z.infer<typeof JobKind>, string[]>();\n for (const [name, service] of Object.entries(config.services)) {\n for (const kind of service.kinds) {\n claimants.set(kind, [...(claimants.get(kind) ?? []), name]);\n }\n }\n\n /**\n * Which service actually serves each kind.\n *\n * One claimant serves without ceremony. Two or more need the owner to say,\n * and until they do the kind is **not advertised at all** — a device that\n * announced a kind it could not resolve deterministically would turn a\n * config ambiguity into a job-time mystery three hops away, which is the\n * shape this whole reorganization exists to remove.\n */\n const serves = new Map<z.infer<typeof JobKind>, string>();\n for (const [kind, names] of claimants) {\n const [sole] = names;\n if (names.length === 1 && sole !== undefined) {\n serves.set(kind, sole);\n continue;\n }\n const chosen = config.defaults[kind];\n if (chosen === undefined) {\n problems.push({\n where: `defaults.${kind}`,\n message:\n `${String(names.length)} services answer ${kind} (${names.join(\", \")}) ` +\n `— set defaults.${kind} to the one that should serve it. Until then ` +\n `a job that names one of them still runs; a job that names none has ` +\n `nowhere to go.`,\n });\n withheld.push({ kind, services: names });\n continue;\n }\n if (!names.includes(chosen)) {\n problems.push({\n where: `defaults.${kind}`,\n message: `\"${chosen}\" does not answer ${kind}. It is served by: ${names.join(\", \")}.`,\n });\n // Withheld for the same reason and reported the same way: the owner\n // said something, and it did not resolve.\n withheld.push({ kind, services: names });\n continue;\n }\n serves.set(kind, chosen);\n }\n\n for (const [name, service] of Object.entries(config.services)) {\n const where = `services.${name}`;\n const descriptor = backendDescriptor(service.type);\n\n // A named provider supplies its own address; the generic transport and any\n // override still have to be given one.\n const baseUrl = service.baseUrl ?? descriptor.defaultBaseUrl;\n\n if (descriptor.class === \"http\") {\n if (baseUrl === undefined) {\n problems.push({\n where,\n message: \"an HTTP-class service needs a baseUrl\",\n });\n continue;\n }\n const check = checkBaseUrl(baseUrl);\n if (!check.ok) {\n problems.push({ where: `${where}.baseUrl`, message: check.detail });\n continue;\n }\n }\n\n // Cost comes from the registry, or from where the request goes — never\n // from config ({@link MUSTS.COST_NOT_CONFIGURABLE},\n // {@link MUSTS.REMOTE_IS_NEVER_FREE}).\n const reason = classifyCost(service.type, baseUrl, service.model);\n const cost = reason.cost;\n const acknowledged = service.spend?.acknowledged === true;\n const capCents = service.spend?.dailyCapCents;\n\n // A widened metered service without a ceiling is refused rather than\n // silently given an unlimited one ({@link MUSTS.METERED_REQUIRES_CEILING}).\n const widened = service.offer !== \"private\";\n if (\n cost === \"metered\" &&\n widened &&\n acknowledged &&\n capCents === undefined\n ) {\n problems.push({\n where: `${where}.spend`,\n message:\n \"sharing a metered service needs spend.dailyCapCents — an \" +\n \"unlimited ceiling is not something anyone means on purpose\",\n });\n continue;\n }\n\n const configured = service.offer;\n const offerScope = effectiveOfferScope(configured, cost, {\n acknowledged: acknowledged && capCents !== undefined,\n });\n if (offerScope !== configured) {\n problems.push({\n where: `${where}.offer`,\n message:\n cost === \"subscription\"\n ? `\"${configured}\" was ignored: ${descriptor.label} runs on your ` +\n `own subscription, so it is locked to your work only`\n : // \"self\" is the pre-alpha.44 word and survived the rename here,\n // so a message about a scope named `private` told its reader\n // about one named `self`. The command it names now carries the\n // ceiling too — without `--cap` it lands right back here, which\n // is the loop Todd hit.\n // The same defect the consent ceremony had: `descriptor.label`\n // is the type, and the type is not what bills. Naming the rule\n // that fired lets an owner check the claim rather than take it.\n `\"${configured}\" was narrowed to \"private\": ${name} bills you ` +\n `per token because ${reason.because}, and no spend consent is ` +\n `recorded. \\`byollm offer ${name} ${configured} ` +\n `--cap <cents-per-day>\\` to share it deliberately, with a ceiling`,\n });\n }\n\n for (const kind of service.kinds) {\n // **Every claimant is a route now** — byollm_016 Phase B.\n //\n // This read `if (serves.get(kind) !== name) continue;` under a comment\n // saying a second claimant \"is configured and idle until Phase B lets a\n // job name it — advertising it now would promise a selection nothing can\n // make\". That was exactly right, and Phase B is the release that made\n // its condition false: a job *can* name it now, and the hub matches a\n // selection against what the device advertised. Advertising only the\n // winner meant selecting anything else was refused as unadvertised — so\n // selection worked for precisely the service you would never need to\n // name.\n //\n // The menu travels; `isDefault` says which row an unselected job takes.\n routes.push({\n kind,\n service: name,\n isDefault: serves.get(kind) === name,\n backendId: service.type,\n backendClass: descriptor.class,\n model: service.model,\n cost,\n offerScope,\n spendAcknowledged: acknowledged,\n spendDailyCapCents: capCents,\n spendCentsPerMillionTokens:\n service.spend?.centsPerMillionTokens ?? 1500,\n baseUrl,\n apiKeyEnv: service.apiKeyEnv,\n });\n }\n }\n\n /**\n * Retired with the release that made its opposite true — Amendment G, B2.\n *\n * This said \"team enforcement is local-allowlist in this build; roster sync\n * lands next\", and it was correct for every build that carried it: `team`\n * was named for central membership and enforced through the same per-person\n * list `named` used, so the name ran ahead of its behaviour and had to say\n * so where the name is used.\n *\n * It is gone rather than reworded because the fact it reported is no longer\n * a fact about *the build*. Which authority decides is now a fact about\n * each **pairing** — a roster where one is held, the local list where no\n * control-plane key was ever pinned — and `resolveConfig` cannot see\n * pairings. `byollm status` can, and says which is in force for each one.\n *\n * Retired here and not one release earlier: a build whose `team` was still\n * local had to keep saying so.\n */\n\n return { config, routes, withheld, notices, problems };\n}\n\nfunction isNotFound(error: unknown): boolean {\n return (\n typeof error === \"object\" &&\n error !== null &&\n \"code\" in error &&\n (error as { code?: unknown }).code === \"ENOENT\"\n );\n}\n","import { isIP } from \"node:net\";\n\n/**\n * Why a base URL was refused.\n */\nexport type BaseUrlRefusal =\n | \"not-a-url\"\n | \"bad-scheme\"\n | \"credentials-in-url\"\n | \"cloud-metadata\"\n | \"link-local\"\n | \"wildcard-address\";\n\nexport type BaseUrlCheck =\n | { readonly ok: true; readonly url: URL }\n | {\n readonly ok: false;\n readonly refusal: BaseUrlRefusal;\n readonly detail: string;\n };\n\n/**\n * Hostnames that resolve to a cloud instance's credential endpoint. Reaching\n * one from a machine running in a cloud VM hands out IAM credentials.\n */\nconst METADATA_HOSTS = new Set([\n \"metadata.google.internal\",\n \"metadata.goog\",\n \"instance-data\",\n \"metadata\",\n]);\n\n/** Literal addresses of the same endpoints. */\nconst METADATA_ADDRESSES = new Set([\n \"169.254.169.254\",\n \"169.254.170.2\",\n \"fd00:ec2::254\",\n]);\n\n/**\n * Validate an owner-configured backend base URL\n * ({@link MUSTS.HTTP_BASE_URL_SAFE}).\n *\n * **What this is and is not.** byollm_004 Rev 1 calls the HTTP-class threat\n * surface \"SSRF-shaped\", and the shape matters: the base URL comes from the\n * machine owner's config and from nowhere else — no payload field can set it,\n * redirect it, or append to it. There is therefore no attacker-controlled\n * input channel into this value at all. What remains is an owner who\n * misconfigures their own machine, and the one case where that is genuinely\n * dangerous is a cloud metadata endpoint.\n *\n * So this deliberately **allows loopback and private LAN addresses**. Blocking\n * them, as a generic SSRF filter would, would refuse\n * `http://127.0.0.1:11434` — which is Ollama's default and the entire point of\n * the product. A filter that breaks the primary path in exchange for no real\n * protection is theatre, and byollm_004's honesty rule forbids claiming it as\n * a guarantee.\n *\n * Redirects are a separate matter and are refused outright by the HTTP\n * backend, so a permitted base URL cannot become a forbidden one in flight.\n */\nexport function checkBaseUrl(raw: string): BaseUrlCheck {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n return {\n ok: false,\n refusal: \"not-a-url\",\n detail: \"base URL is not a valid absolute URL\",\n };\n }\n\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n return {\n ok: false,\n refusal: \"bad-scheme\",\n detail: `base URL scheme ${url.protocol} is not http or https`,\n };\n }\n\n if (url.username !== \"\" || url.password !== \"\") {\n // Credentials in the URL would end up in logs and error messages.\n return {\n ok: false,\n refusal: \"credentials-in-url\",\n detail: \"base URL must not embed a username or password\",\n };\n }\n\n const host = url.hostname.toLowerCase().replace(/^\\[|\\]$/g, \"\");\n\n if (METADATA_HOSTS.has(host) || METADATA_ADDRESSES.has(host)) {\n return {\n ok: false,\n refusal: \"cloud-metadata\",\n detail: `${host} is a cloud metadata endpoint`,\n };\n }\n\n // 169.254.0.0/16 and fe80::/10 — link-local. The metadata addresses above\n // live here too; this catches the rest of the range.\n if (isIP(host) === 4 && host.startsWith(\"169.254.\")) {\n return {\n ok: false,\n refusal: \"link-local\",\n detail: `${host} is link-local`,\n };\n }\n if (isIP(host) === 6 && /^fe[89ab]/.test(host)) {\n return {\n ok: false,\n refusal: \"link-local\",\n detail: `${host} is link-local`,\n };\n }\n\n // A wildcard address is a listening address, not a destination.\n if (host === \"0.0.0.0\" || host === \"::\" || host === \"\") {\n return {\n ok: false,\n refusal: \"wildcard-address\",\n detail: `${host || \"(empty)\"} is not a destination address`,\n };\n }\n\n return { ok: true, url };\n}\n\n/** Human-readable explanations for the trust UI and startup errors. */\nexport const BASE_URL_REFUSAL_MESSAGES: Readonly<\n Record<BaseUrlRefusal, string>\n> = Object.freeze({\n \"not-a-url\": \"that base URL could not be parsed\",\n \"bad-scheme\": \"a backend base URL must be http or https\",\n \"credentials-in-url\":\n \"put credentials in the backend's auth config, not in the URL\",\n \"cloud-metadata\":\n \"that address is a cloud metadata endpoint and would expose instance credentials\",\n \"link-local\": \"link-local addresses are refused\",\n \"wildcard-address\":\n \"that is an address to listen on, not one to connect to — use 127.0.0.1\",\n});\n","import type { BackendId } from \"@byollm/protocol\";\n\n/**\n * Models a backend's CLI is known to accept — byollm_017 ruling 3.\n *\n * **Suggestions, and nothing stronger.** The promise this product makes is\n * that a model released this morning works this morning, which a frozen list\n * anywhere a person picks from breaks on the first day it matters. So free\n * text is always allowed, and what makes it safe is ruling 2: a candidate is\n * probed against the real CLI before it is written, so \"found is not works\"\n * is answered by the machine rather than by a list somebody maintains.\n *\n * These ship with the daemon and are announced with the capability, so the\n * dashboard shows what *this device's* CLI knows rather than what the cloud\n * last heard about. A list held cloud-side would be one more thing to update\n * on release day and wrong for everybody who had not upgraded.\n *\n * Aliases first, dated ids after. The aliases are what people type and what\n * survives a model refresh; the dated ids are what somebody pins when they\n * need this month's behaviour not to move under them.\n */\nconst KNOWN: Partial<Record<BackendId, readonly string[]>> = Object.freeze({\n \"claude-cli\": Object.freeze([\n \"opus\",\n \"sonnet\",\n \"haiku\",\n \"claude-opus-4-1\",\n \"claude-sonnet-4-5\",\n \"claude-haiku-4-5\",\n ]),\n \"codex-cli\": Object.freeze([\"gpt-5-codex\", \"gpt-5\", \"o4-mini\"]),\n});\n\n/**\n * What to suggest for a backend, or nothing.\n *\n * An empty list is the honest answer for a local server: it serves whatever\n * has been pulled onto that machine, which this module cannot know and the\n * server itself can be asked. A reader must not render an empty list as \"no\n * models available\" — it is \"nothing to suggest\", which is a different fact\n * and the reason the wire field is optional rather than defaulted to `[]`.\n */\nexport function knownModelsFor(id: BackendId): readonly string[] {\n return KNOWN[id] ?? [];\n}\n","import { spawn } from \"node:child_process\";\nimport type { BackendId } from \"@byollm/protocol\";\n\n/**\n * Signing a vendor CLI in, from inside `byollm setup`.\n *\n * Two machines sat in \"we thought it wasn't working\" because a logged-out CLI\n * only produced a *note*: setup found the binary, said it could not answer\n * yet, wrote the config anyway and moved on. The config was right — nothing\n * routes until it answers — and the person was not stopped at the one moment\n * they were sitting in front of a terminal ready to fix it.\n *\n * ## One terminal, because that is what a hosted console has\n *\n * The obvious shape is \"open another terminal and log in there\", and it is\n * wrong for the place this has to work: a hosted device's console is one\n * window, and so is an SSH session. The shell-job-control version — background\n * setup, run the CLI, `fg` — needs job control a child process does not own.\n *\n * So setup spawns the login itself with the TTY inherited. The vendor CLI\n * takes over the terminal, does whatever it does, exits, and setup re-probes.\n *\n * ## Verified against the shipped CLIs, not assumed\n *\n * The ruling allowed for \"a direct login subcommand if one exists, else\n * interactive plus a `/exit` instruction\". Both have one, which is better than\n * the fallback in every way — it exits by itself when the login finishes,\n * rather than depending on somebody typing the right thing to come back:\n *\n * claude claude auth login (also: claude auth status)\n * codex codex login --device-auth (also: codex login status)\n *\n * Checked by running them, per the FIXED_ARGV precedent. `claude login` is not\n * a command; `claude auth login` is. Guessing the first would have produced a\n * gate that always failed, on the path a new person meets first.\n *\n * **`--device-auth` since B148, and the table said `codex login` for one\n * commit after the code stopped.** That flow opens a localhost OAuth callback,\n * so on any machine without a browser and a loopback listener it does not fail\n * — it **hangs**. A summary whose authority is *\"we ran these\"*, listing a\n * command removed **because it hangs**, is worse than an unverified one; and\n * it is the first thing a reader of this file meets.\n *\n * The rule it breaks is instruction 20's corollary one scope down: a ruling is\n * not landed until the code matches it, and a file's own header is code's\n * nearest neighbour.\n *\n * ## Except on Windows, where the spawn cannot work — ruled 2026-09-04\n *\n * An npm-installed `claude` on Windows is `claude.cmd`, and Node will not\n * spawn a `.cmd` without a shell: since the CVE-2024-27980 fix (18.20/20.12)\n * it throws EINVAL, and before that ENOENT. {@link runLogin} is built to\n * swallow that — \"cannot spawn\" and \"exited nonzero\" are the same outcome to\n * the caller — so Kevin got three rounds of \"Opening Claude's sign-in now\"\n * followed by \"Still cannot answer\", with nothing opening and no error ever\n * printed. Two defensible designs composing into a silent loop.\n *\n * Todd ruled: on Windows do not spawn at all. Print the command, prominently,\n * and let the person run it. {@link loginPlan} is where that decision lives,\n * so it is made once and is testable without a platform to run on.\n */\n\n/** How a backend's CLI is signed in, when it has a way. */\nexport interface LoginCommand {\n readonly argv: readonly [string, ...string[]];\n /** What to tell somebody before the terminal stops being ours. */\n readonly says: string;\n}\n\n/**\n * The login invocation per backend, or `undefined` for one that has none.\n *\n * `undefined` is not \"cannot log in\" — Ollama's cloud models authenticate\n * elsewhere entirely — it is \"this module has nothing to spawn\", and the\n * caller falls back to asking rather than inventing a command.\n */\nexport function loginCommandFor(id: BackendId): LoginCommand | undefined {\n switch (id) {\n case \"claude-cli\":\n return {\n argv: [\"claude\", \"auth\", \"login\"],\n says:\n \"Opening Claude's sign-in now. Finish it there and this picks\\n\" +\n \" straight back up.\",\n };\n case \"codex-cli\":\n return {\n /**\n * `--device-auth`, EVERYWHERE, not only on a machine we think is\n * headless — B148, and the choice is the interesting part.\n *\n * Plain `codex login` opens a **localhost OAuth callback**. Codex says\n * so itself: *\"On a remote or headless machine? Use `codex login\n * --device-auth` instead.\"* Anything without a browser and a loopback\n * listener — a hosted box, a server, a container, an SSH session —\n * does not fail on that flow, it **hangs** waiting for a callback\n * nobody can make.\n *\n * **The alternative was to detect headless and branch**, and that is a\n * guess about somebody's machine: no `DISPLAY`, `SSH_CONNECTION` set,\n * no browser on PATH. Every one of those is right about most machines\n * and wrong about some, and the failure when it is wrong is a hang\n * rather than a message. One flow that works on every machine beats\n * two flows and a heuristic deciding between them.\n *\n * **The cost, stated:** on a laptop this prints a URL and a code\n * instead of opening a browser. Todd, 09-10: *\"we will need to\n * integrate that for codex in setup, at least for hosted, probably\n * everywhere.\"* This is the everywhere reading.\n *\n * Per instruction 11, what would change it: evidence that the extra\n * paste costs laptop users more than the hang costs headless ones —\n * which is a complaint we would hear, rather than something to predict.\n *\n * `claude` needs no equivalent, checked the same way rather than\n * assumed: run headless, `claude auth login` prints a URL whose\n * `redirect_uri` is `platform.claude.com` and takes a pasted code.\n */\n argv: [\"codex\", \"login\", \"--device-auth\"],\n says:\n \"Codex will print a URL and a code. Open the URL anywhere — your\\n\" +\n \" phone is fine — enter the code, and this picks straight back up.\",\n };\n default:\n return undefined;\n }\n}\n\n/**\n * Run it, with this terminal.\n *\n * `stdio: \"inherit\"` is the whole mechanism: the child gets our stdin, stdout\n * and stderr, so a browser prompt, a device code or a password field all work\n * exactly as they do when somebody runs the command themselves. Nothing is\n * captured, because capturing is what would break them.\n *\n * Resolves to whether the child exited cleanly. It never throws: a CLI that\n * cannot be spawned at all is the same outcome to the caller as one that\n * exited nonzero — the person is not signed in, and the next thing to do is\n * ask them rather than crash the wizard they are halfway through.\n *\n * **It reports before it swallows.** The never-throws contract is about\n * control flow, and it had quietly become a contract about information too:\n * the spawn failure carried the word EINVAL and nobody ever saw it, so three\n * identical rounds looked like three identical nothings. `report` gets the\n * error's own words before the `false` goes back. Same law as the supervisor\n * refusal, twice in one day — an interpretation is not the evidence, and\n * silence is not either.\n */\nexport async function runLogin(\n command: LoginCommand,\n report: (text: string) => void = () => undefined,\n spawnImpl: typeof spawn = spawn,\n): Promise<boolean> {\n const [file, ...args] = command.argv;\n const failed = (error: unknown): void => {\n report(\n ` \\`${command.argv.join(\" \")}\\` could not be started: ` +\n `${error instanceof Error ? error.message : String(error)}\\n`,\n );\n };\n return new Promise<boolean>((resolve) => {\n try {\n const child = spawnImpl(file, args, { stdio: \"inherit\" });\n child.on(\"error\", (error) => {\n failed(error);\n resolve(false);\n });\n child.on(\"close\", (code) => {\n resolve(code === 0);\n });\n } catch (error) {\n failed(error);\n resolve(false);\n }\n });\n}\n\n/**\n * Spawn it, or tell them to run it — the one place that decides.\n *\n * Windows gets `print` for the reason in the module note above: the spawn\n * cannot work there, and a wizard that says \"Opening Claude's sign-in now\"\n * and opens nothing is worse than one that hands over the command.\n */\nexport function loginPlan(\n command: LoginCommand,\n platform: NodeJS.Platform,\n):\n | { readonly kind: \"spawn\" }\n | { readonly kind: \"print\"; readonly say: string } {\n if (platform !== \"win32\") return { kind: \"spawn\" };\n return {\n kind: \"print\",\n say:\n ` Run this in another window, then come back:\\n\\n` +\n ` ${command.argv.join(\" \")}\\n\\n` +\n ` (Windows cannot open it for you from here.)`,\n };\n}\n","/**\n * One sentence about a service, for the three surfaces its owner reads.\n *\n * Your Devices, `byollm status`, and the daemon's own output all answer the\n * same question — can this thing do its job, and if not what do I run — so\n * they say it in one string rather than three that drift.\n *\n * The person this is for is the owner, and only the owner. A site gets a class\n * and a fixed sentence and never this; the CLI's own words quote paths,\n * usernames and account emails, and they name which backend answered, which\n * is what the disclosure fence exists to prevent.\n */\n\n/** What asking the backend produced. */\nexport type ServiceState =\n /** Ran a real call and answered. */\n | { readonly kind: \"answers\"; readonly model: string }\n /** The binary is there; the credentials are not. */\n | { readonly kind: \"signed-out\"; readonly detail?: string | undefined }\n /** The config names a binary this machine does not have. */\n | { readonly kind: \"missing\" }\n /**\n * Installed here, not running, and startable — B056 / D4.\n *\n * Advertised, unlike `missing`, because it IS available: one spawn away,\n * and the daemon starts it when a job arrives (B050). Its own kind rather\n * than reusing `missing` or `unknown`, because this is the one state where\n * the machine ADVERTISES the service — and a service advertised while\n * `status` calls it \"not found on this device\" is a machine disagreeing\n * with itself on two screens.\n */\n /**\n * Stopped, and nothing here knows how to start it — B098.\n *\n * Distinct from `missing`, which means the program is not on this machine.\n * Conflating them told an owner to install software that was already\n * installed, which is an afternoon spent on a fix that was never the\n * problem.\n */\n | { readonly kind: \"unstartable\"; readonly model: string }\n | {\n readonly kind: \"stopped\";\n readonly model: string;\n /**\n * Whether anything on this device will actually start it — B087.\n *\n * The comment above says \"the daemon starts it when a job arrives\n * (B050)\", and until this field existed that was not true: the starter\n * is behind a `spawnServer` seam nothing passes, so a stopped server\n * was advertised and a job routed to it reached nothing listening —\n * the claimed-then-failed outcome B056's own ruling forbids.\n *\n * Optional, and absent means NO. `byollm status` is a different\n * process reading what the daemon wrote, so a file written before this\n * field existed is read by a daemon that has it — and the honest\n * default for \"I cannot tell whether anything starts this\" is not to\n * promise that something will.\n */\n readonly starts?: boolean | undefined;\n }\n /**\n * Signed in, healthy, and out of quota until further notice — 019 §3.2.\n *\n * Its own state because its remedy is the opposite of signed-out's: this\n * one needs **time and nothing else**, and there is nothing for the owner\n * to go and do. Rendering it as \"needs sign-in\" would send somebody to a\n * terminal to fix an account that is working perfectly.\n */\n | {\n readonly kind: \"blocked\";\n readonly detail?: string | undefined;\n /** Epoch ms the CLI expects to be back, when it said so. */\n readonly until?: number | undefined;\n }\n /**\n * Nobody asked, because there was nothing to ask with.\n *\n * Not a failure and not a success. A backend with no canary — an HTTP model\n * server, say — has no credentials of its own to check, and rendering this\n * as \"cannot answer\" would tell every local-server owner their model was\n * broken. Not asked is not no, the same way an empty variable is not a\n * configured one.\n */\n | { readonly kind: \"unknown\"; readonly model: string };\n\n/**\n * A service's state plus the remedy its backend supplies.\n *\n * Kept together because they are learned together — at probe time, from the\n * backend instance that knows both — and needed together, by every surface\n * that renders a line. A caller looking the remedy up later would be looking\n * it up from somewhere that does not know which backend this service used.\n */\nexport interface ServiceReport {\n readonly state: ServiceState;\n readonly signIn?: string | undefined;\n}\n\nexport interface ServiceLine {\n /** The single line. */\n readonly line: string;\n /** The backend's own words, shown muted beneath. Owner-only. */\n readonly detail?: string | undefined;\n}\n\n/**\n * The template, once.\n *\n * `device` is in every sentence on purpose: somebody with three machines\n * reading their dashboard needs to know which one to go and fix, and \"run\n * claude in a terminal\" is useless advice if it is the wrong terminal.\n */\nexport function serviceLine(input: {\n readonly service: string;\n readonly device: string;\n readonly state: ServiceState;\n /** How this backend is signed in, from the backend itself. */\n readonly signIn?: string | undefined;\n /** What removes it, for a binary that is gone. */\n readonly removeWith?: string | undefined;\n}): ServiceLine {\n const { service, device, state } = input;\n switch (state.kind) {\n case \"answers\":\n return { line: `${service} — ${state.model}` };\n case \"unknown\":\n // No auth sentence at all: health as it was before any of this existed.\n return { line: `${service} — ${state.model}` };\n case \"signed-out\": {\n const remedy = input.signIn ?? \"sign it in\";\n return {\n line: `${service} — needs sign-in on ${device}: ${remedy}`,\n ...(state.detail === undefined ? {} : { detail: state.detail }),\n };\n }\n case \"stopped\":\n /* Not a problem and not a promise of speed. It says the two things\n somebody needs: nothing is wrong, and the first job pays for the\n start.\n\n And only when a start is actually going to happen — B087. The\n parenthetical was printed unconditionally while nothing could start\n anything, which is a promise the device could not keep, on the one\n screen an owner checks to find out what their device is doing. */\n return {\n line:\n `${service} — ${state.model}, not running` +\n (state.starts === true\n ? \" (starts when a job needs it)\"\n : \" — start it to offer it\"),\n };\n case \"missing\": {\n const remove = input.removeWith ?? `remove it from ~/.byollm/config.json`;\n return {\n line: `${service} — not found on ${device}: install it, or ${remove}`,\n };\n }\n case \"unstartable\":\n /**\n * Not running, and this device has no way to start it — B098.\n *\n * Split out of `missing`, which said *\"not found on this device:\n * install it\"* about software that was installed and running. Todd's\n * services are `openai-http` at loopback addresses; `startCommandFor`\n * knows a command for `ollama` only, so they were unstartable and got\n * told they were absent.\n *\n * The sentence ends in what a person can actually do. There is no knob\n * here and inventing one would be the invented-remedy mistake\n * byollm_021 names, so it says the true thing: start it yourself.\n */\n return {\n line:\n `${service} — ${state.model}, not running, and ${device} cannot ` +\n `start it for you: start it yourself, then it will be offered`,\n };\n case \"blocked\": {\n /* No remedy, because there is not one — and saying so is the point.\n Every other sentence here ends in something to run. This one ends in\n a time, or in nothing, and both are more use than an instruction that\n cannot help. */\n const when =\n state.until === undefined\n ? \"it needs time, not a fix\"\n : `back around ${clockTime(state.until)}`;\n return {\n line: `${service} — out of quota on ${device}: ${when}`,\n ...(state.detail === undefined ? {} : { detail: state.detail }),\n };\n }\n }\n}\n\n/**\n * A time somebody can act on, in their own timezone.\n *\n * Local because this line is read by one person on one machine, and the CLI\n * that produced the time meant the same clock they are looking at. No date\n * unless it is not today: \"back around 8:28 AM\" is what somebody needs when\n * the block lifts this afternoon, and a full timestamp is noise around it.\n */\nfunction clockTime(at: number): string {\n const when = new Date(at);\n const time = when.toLocaleTimeString(undefined, {\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n const today = new Date().toDateString() === when.toDateString();\n return today\n ? time\n : `${when.toLocaleDateString(undefined, { month: \"short\", day: \"numeric\" })}, ${time}`;\n}\n\n/**\n * Every service as the owner's surfaces print it — the block, not the line.\n *\n * Lives here rather than in the CLI because it is rendering, and rendering\n * that sits inside a two-thousand-line command file is rendering nobody\n * tests. `byollm connect` and the daemon both print exactly this.\n */\nexport function renderServices(\n states: ReadonlyMap<string, ServiceReport>,\n device: string,\n): string[] {\n const lines: string[] = [];\n for (const [service, report] of states) {\n const said = serviceLine({\n service,\n device,\n state: report.state,\n ...(report.signIn === undefined ? {} : { signIn: report.signIn }),\n });\n lines.push(` ${said.line}`);\n // The backend's own words, for the owner, beneath the remedy. Indented\n // because it is evidence for the sentence above it, not a second finding.\n if (said.detail !== undefined) lines.push(` ${said.detail}`);\n }\n return lines;\n}\n\n/**\n * The line `byollm status` adds beneath a service, or nothing.\n *\n * Nothing in two cases, and they are different: a service that answered has\n * no remedy to offer, and a service nobody has probed has no finding at all.\n * Both print what `status` always printed, because inventing a sentence from\n * an absence is how \"not asked\" becomes \"cannot answer\".\n */\nexport function authNote(input: {\n readonly service: string;\n readonly device: string;\n readonly report: ServiceReport | undefined;\n}): ServiceLine | undefined {\n const { report } = input;\n if (report === undefined) return undefined;\n if (report.state.kind === \"answers\") return undefined;\n if (report.state.kind === \"unknown\") return undefined;\n return serviceLine({\n service: input.service,\n device: input.device,\n state: report.state,\n ...(report.signIn === undefined ? {} : { signIn: report.signIn }),\n });\n}\n","import type { LoadedConfig, ServiceConfig } from \"./config.js\";\nimport { loginCommandFor, loginPlan, type LoginCommand } from \"./login.js\";\nimport { serviceLine } from \"./service-line.js\";\n\n/**\n * Ask the backends now, while somebody is watching — B047.\n *\n * Kevin, on .81: he signed out of `claude`, ran `start`, and nothing said a\n * word. Then `run`, and nothing again. The daemon was right and every screen\n * was silent.\n *\n * ## Why the honest version of this was silent\n *\n * `start` already named signed-out services. It read them from\n * services.json — what the daemon's own startup probe recorded — which is\n * the right source for a passive line and the wrong one for this moment.\n * `installService` waits for the daemon to be ALIVE, not for its first probe\n * to have finished and been written, and a probe is a real network call. So\n * the file `start` read was written before the sign-out on a used machine,\n * and did not exist at all on a new one. The tri-state rule then did exactly\n * what it should: absent is not signed-out, so it said nothing.\n *\n * Both of Kevin's cases are that, and neither is a bug in the read-back. The\n * read-back answers \"what did this daemon learn last time\" at the one moment\n * a person deserves \"what is true right now\".\n *\n * ## So this asks, and it replaces the read-back rather than joining it\n *\n * Two answers to one question on one screen is how they come to disagree.\n * The cost is one verification per mapped service per human-initiated start,\n * spent at the moment somebody is looking at the answer.\n *\n * ## Only for a person at a terminal\n *\n * A supervisor respawning the daemon at logon is not somebody who can answer\n * a prompt, and canaries cost money. Non-interactive callers return before\n * anything is spent, and the daemon's own probe stays the authority there.\n */\nexport interface PreflightDeps {\n readonly loaded: LoadedConfig;\n readonly device: string;\n readonly io: {\n readonly out: (text: string) => void;\n readonly err: (text: string) => void;\n };\n /** Whether we may ask at all — a TTY, not a supervisor. */\n readonly interactive: boolean;\n readonly ask: (question: string) => Promise<string>;\n readonly platform: NodeJS.Platform;\n /**\n * Ask one service whether it can answer.\n *\n * Takes the whole service config, not an id and a model. An HTTP-class\n * transport needs its `baseUrl` to be constructed at all — building one\n * without it throws, and the first version of this took `(id, model)` and\n * crashed `byollm run` outright for anybody serving Ollama. The narrower\n * signature was not simpler; it was missing a field the callee needs.\n */\n readonly verify: (config: ServiceConfig) => Promise<{\n readonly answers: boolean | undefined;\n readonly detail?: string | undefined;\n }>;\n readonly login: (command: LoginCommand) => Promise<boolean>;\n /** How each backend says it is signed in, for the remedy in the line. */\n readonly signInFor: (config: ServiceConfig) => string | undefined;\n}\n\nexport async function preflight(deps: PreflightDeps): Promise<void> {\n if (!deps.interactive) return;\n\n for (const [service, config] of Object.entries(deps.loaded.config.services)) {\n const proof = await deps.verify(config);\n /**\n * `undefined` is not a failure — it is \"there was no way to ask\", which\n * is the third state the whole tri-state exists for. A local model server\n * with no canary must not be reported as signed out.\n */\n if (proof.answers !== false) continue;\n\n const signIn = deps.signInFor(config);\n const said = serviceLine({\n service,\n device: deps.device,\n state: {\n kind: \"signed-out\",\n ...(proof.detail === undefined ? {} : { detail: proof.detail }),\n },\n ...(signIn === undefined ? {} : { signIn }),\n });\n /* stderr, alongside the success rather than instead of it: the service\n is starting either way, and this must not land in a pipeline that\n wanted the serving line. */\n deps.io.err(`\\n ${said.line}\\n`);\n if (said.detail !== undefined) deps.io.err(` ${said.detail}\\n`);\n\n const command = loginCommandFor(config.type);\n if (command === undefined) continue;\n\n const plan = loginPlan(command, deps.platform);\n if (plan.kind === \"print\") {\n /* Windows, per Todd's ruling and B049's evidence: the spawn cannot\n work there, so it is not offered. */\n deps.io.err(`\\n${plan.say}\\n`);\n continue;\n }\n\n /**\n * Asked, never assumed. A login opens a browser, and opening one on\n * somebody's machine because they typed `start` is not a thing to do\n * without being told yes.\n *\n * Asked once. Declining is a decision, and a second prompt would be\n * arguing with it.\n */\n const answer = await deps.ask(` Sign in to ${service} now? [Y/n] `);\n if (/^n(o)?$/i.test(answer.trim())) continue;\n\n deps.io.err(` ${command.says}\\n\\n`);\n await deps.login(command);\n const again = await deps.verify(config);\n deps.io.err(\n again.answers === true\n ? ` ${service} is signed in.\\n`\n : ` ${service} still cannot answer` +\n (again.detail === undefined ? \"\" : `: ${again.detail}`) +\n `\\n Starting anyway — nothing routes to it until it can.\\n`,\n );\n }\n}\n","/**\n * Updating byollm in place, and being able to take it back — B053.\n *\n * Ruled 2026-09-02 (016 §Auto-update): the supervisor is the home, there is\n * no second runner, and the daemon learns the version on a channel it already\n * polls. What that ruling stresses, and what this module is mostly made of,\n * is one sentence: **an updater must be able to un-update.**\n *\n * ## The order is the safety\n *\n * Drain, install, re-register, canary, and only then keep it. Each step\n * exists because skipping it breaks something specific:\n *\n * - **Drain** — finish the running job, claim nothing new. `shutdown` was\n * the nearest existing behaviour and it is the wrong one: it cancels the\n * active jobs and releases the leases, so updating would mean killing work\n * somebody was waiting on. An update is elective; a job is not.\n * - **Exact version, never a tag.** `npm i -g byollm@latest` inside an\n * updater means the fleet installs whatever the tag points at by the time\n * each machine gets around to it — different machines, different builds,\n * one version number in the logs. {@link exactVersion} refuses anything\n * that is not a literal version, and that refusal is the reason it exists.\n * - **Forward only** — B360. A version at or below the running one is\n * refused, and so is a pair this cannot order. An offer is a claim about\n * what is newer, and a downgrade is how a machine is walked back onto a\n * release with a known hole in it.\n * - **Provenance before it runs** — B360. After npm installs and before\n * anything starts the new binary, the package's SLSA provenance must say\n * our release workflow built these exact bytes (`provenance.ts`). A\n * package that fails is rolled back without ever having been executed.\n * - **Canary by identity, not liveness.** The check is \"does the installed\n * binary say it is the version we asked for\", not \"does it start\". A\n * half-finished install that leaves the old binary in place starts\n * perfectly.\n * - **Rollback**, which is the same install run with the version we came\n * from. Kept as a value rather than discovered later: after a bad install\n * the machine can no longer be asked what it used to be.\n *\n * ## What it does when it cannot fix itself\n *\n * A failed rollback is not retried and not hidden. It reports, loudly, on\n * the owner's surfaces and stops — a loop here is a machine that reinstalls\n * npm packages forever, and the honest end state is a daemon that says what\n * happened and leaves the machine to a person.\n */\n\nimport { compareVersions } from \"@byollm/protocol\";\nimport type { Provenance } from \"./provenance.js\";\n\n/** A version this updater is willing to install. */\nexport type ExactVersion = string & { readonly __exact: unique symbol };\n\n/**\n * Accept only a literal version, never a tag or a range.\n *\n * `latest`, `^0.1.0` and `0.1.x` all name \"whatever is current when this\n * runs\", and an updater that accepts them cannot say what it installed. The\n * whole fleet asking for the same tag at different minutes is a fleet on\n * different builds reporting the same number.\n */\nexport function exactVersion(value: string): ExactVersion | undefined {\n return /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$/.test(value)\n ? (value as ExactVersion)\n : undefined;\n}\n\nexport interface UpdateDeps {\n /** Finish the running job and claim nothing. Resolves when idle. */\n readonly drain: () => Promise<void>;\n /** `npm i -g byollm@<version>`. Resolves to whether it succeeded. */\n readonly install: (version: string) => Promise<boolean>;\n /**\n * Did our release workflow build what was just installed? — B360.\n *\n * Asked after the install and before {@link reregister}, which is the\n * first thing that would run the new binary.\n */\n readonly verify: (version: string) => Promise<Provenance>;\n /** Re-register with the supervisor, since the entry point moved. */\n readonly reregister: () => Promise<boolean>;\n /**\n * What the installed binary says its version is.\n *\n * `undefined` when it could not be asked at all — which is a failure here,\n * not a third state: this runs immediately after an install, so \"cannot\n * say\" means the thing we just installed does not answer.\n */\n readonly installedVersion: () => Promise<string | undefined>;\n /** Said on the owner's surfaces. */\n readonly report: (line: string) => void;\n}\n\nexport type UpdateOutcome =\n | { readonly kind: \"updated\"; readonly to: string }\n | { readonly kind: \"refused\"; readonly why: string }\n | { readonly kind: \"rolled-back\"; readonly to: string; readonly why: string }\n | { readonly kind: \"stranded\"; readonly why: string };\n\n/**\n * Move this machine to `to`, or put it back the way it was.\n *\n * `from` is passed in rather than read here on purpose: it is the value the\n * rollback needs, and after a bad install the machine can no longer be asked\n * what it used to be.\n */\nexport async function update(\n from: string,\n to: string,\n deps: UpdateDeps,\n): Promise<UpdateOutcome> {\n const target = exactVersion(to);\n if (target === undefined) {\n /* Before anything is drained. An update we would not finish must not\n cost somebody the jobs this machine was about to claim. */\n const why = `refusing to install \"${to}\" — the updater installs exact versions only`;\n deps.report(why);\n return { kind: \"refused\", why };\n }\n /* `from` has to be installable too, or the rollback is a promise we cannot\n keep. Better to decline the update than to take a machine somewhere it\n cannot come back from. */\n if (exactVersion(from) === undefined) {\n const why = `refusing to update from \"${from}\" — no version to roll back to`;\n deps.report(why);\n return { kind: \"refused\", why };\n }\n /* Forward only — B360. Both sides are exact by now, so `undefined` should\n not happen; it is refused anyway, because \"cannot tell which is newer\"\n is not permission to install. */\n const order = compareVersions(target, from);\n if (order === 0) {\n return { kind: \"refused\", why: `already on ${from}` };\n }\n if (order === undefined || order < 0) {\n const why =\n order === undefined\n ? `refusing to install ${target} — cannot tell whether it is newer than ${from}`\n : `refusing to install ${target} — it is older than ${from}, and the updater never downgrades`;\n deps.report(why);\n return { kind: \"refused\", why };\n }\n\n await deps.drain();\n\n if (!(await deps.install(target))) {\n /* Nothing was replaced, so there is nothing to undo — npm leaves the\n previous global in place when an install fails. Re-register anyway:\n the drain stopped this daemon claiming, and leaving it drained would\n be a machine that quietly serves nothing after a failed update. */\n await deps.reregister();\n const why = `could not install ${target} — staying on ${from}`;\n deps.report(why);\n return { kind: \"refused\", why };\n }\n\n /* Before `reregister`, because that is what first runs the new binary.\n A package nobody can vouch for goes back out without having executed —\n the install itself runs no scripts (`update-deps.ts`). */\n const proof = await deps.verify(target);\n if (!proof.verified) {\n return rollBack(\n from,\n `${target} failed its provenance check: ${proof.why}`,\n deps,\n );\n }\n\n await deps.reregister();\n\n const reported = await deps.installedVersion();\n if (reported === target) {\n return { kind: \"updated\", to: target };\n }\n\n /* The canary failed: either the binary does not answer, or it answers with\n a version nobody asked for. Both mean the machine is not running what we\n believe it is running, which is the state an updater exists to prevent. */\n return rollBack(\n from,\n reported === undefined\n ? `${target} did not answer after installing`\n : `installed ${target} but the binary reports ${reported}`,\n deps,\n );\n}\n\n/** Put `from` back, once, and say how that went. */\nasync function rollBack(\n from: string,\n why: string,\n deps: UpdateDeps,\n): Promise<UpdateOutcome> {\n if (!(await deps.install(from))) {\n const stranded = `${why}; rolling back to ${from} also failed`;\n deps.report(stranded);\n return { kind: \"stranded\", why: stranded };\n }\n await deps.reregister();\n\n const back = await deps.installedVersion();\n if (back !== from) {\n /* Rolled back and it did not take. Not retried — a loop here reinstalls\n forever — and said plainly, because a person has to look at this one. */\n const stranded = `${why}; rolled back to ${from} and the binary reports ${\n back ?? \"nothing\"\n }`;\n deps.report(stranded);\n return { kind: \"stranded\", why: stranded };\n }\n\n deps.report(`${why}; rolled back to ${from}`);\n return { kind: \"rolled-back\", to: from, why };\n}\n\n/**\n * Which offers reach the updater at all — B360.\n *\n * Every paired origin's heartbeat can carry `updateTo`, and until this the\n * newest one won whichever site named it: any site somebody paired with could\n * tell their machine what to install. Now exactly one origin may — the update\n * authority, `updateAuthority` in the config, the reference hub when unset —\n * and only over a pairing that pinned a control-plane key. A direct-mode\n * pairing has none: it is one site, and one site does not get to move the\n * daemon every other site is also talking to.\n *\n * A refused offer is said once per origin, not once per heartbeat: the offer\n * repeats until something takes it, and a line every ten seconds is a line\n * nobody reads.\n */\nexport function offerInbox(input: {\n /** Normalised, as the pairing origins are. */\n readonly authority: string;\n /** Whether this origin's pairing pinned a control-plane key. */\n readonly hasControlPlane: (origin: string) => boolean;\n readonly report: (line: string) => void;\n}): {\n readonly receive: (origin: string, version: string) => void;\n readonly current: () => string | undefined;\n} {\n let offered: string | undefined;\n const refused = new Set<string>();\n return {\n receive: (origin, version) => {\n const why =\n origin !== input.authority\n ? `updates are taken only from ${input.authority}`\n : input.hasControlPlane(origin)\n ? undefined\n : \"it is a direct-mode pairing, and those never update this daemon\";\n if (why === undefined) {\n /* The newest offer wins, among the authority's own. `??=` here once\n held the FIRST offer forever, so a machine that rolled back never\n updated again until restarted (B053); the watcher refuses to retry\n a version it has already tried, which is what makes this safe. */\n offered = version;\n return;\n }\n if (refused.has(origin)) return;\n refused.add(origin);\n input.report(`ignoring an offer of ${version} from ${origin} — ${why}`);\n },\n current: () => offered,\n };\n}\n","/**\n * Did the version npm just installed come from our release workflow? — B360.\n *\n * The updater's exact-version rule says WHICH version is installed; it says\n * nothing about who built it. Whoever can publish `byollm` to npm can put\n * anything under a new number, and an auto-updating daemon would take it.\n * So before the new binary is ever run, this asks the registry for the SLSA\n * provenance our release job attaches to every publish, and requires three\n * things of it:\n *\n * - it was built by `oftomorrowinc/byollm`'s workflow, from the tag\n * `v<version>` — not a fork, not a branch;\n * - it names this package at this version;\n * - its subject digest is the tarball's `dist.integrity` — the hash npm\n * checked the download against, so the statement is about the bytes that\n * were installed and not about some other tarball.\n *\n * ## What this does not do, said where it is done\n *\n * It does not re-verify the Sigstore signature on the bundle. npm validates\n * a provenance bundle when it is published and refuses a publish whose\n * bundle does not verify; this module trusts that, and reads the statement\n * the registry serves. What that buys is the case that matters most — a\n * stolen publish token cannot mint a provenance statement from our workflow,\n * so a package published with one arrives without it and is refused. What it\n * does not buy is protection from the registry itself lying, which is\n * `npm audit signatures`' job and is not this one. `docs/security.md`\n * §Updates says the same.\n *\n * Everything unreadable is a failure. A 404, a timeout, a body that is not\n * JSON, an attestation list with no SLSA entry — each is \"not verified\", and\n * not verified means the update is rolled back. An updater that installs\n * what it could not check is an updater with no check.\n */\n\n/** Where npm serves both the version document and its attestations. */\nexport const NPM_REGISTRY = \"https://registry.npmjs.org\";\n\n/** The repository whose release workflow is the only acceptable builder. */\nconst RELEASE_REPOSITORY = \"https://github.com/oftomorrowinc/byollm\";\n\nconst SLSA_V1 = \"https://slsa.dev/provenance/v1\";\n\nexport type Provenance =\n | { readonly verified: true }\n | { readonly verified: false; readonly why: string };\n\ntype Fetch = (url: string) => Promise<Response>;\n\n/**\n * Check `byollm@<version>` against its published provenance.\n *\n * `fetch` is injected so every failure path can be run without a network;\n * the default is the global one.\n */\nexport async function verifyProvenance(\n version: string,\n options: { readonly fetch?: Fetch; readonly registry?: string } = {},\n): Promise<Provenance> {\n const get = options.fetch ?? ((url: string) => fetch(url));\n const registry = options.registry ?? NPM_REGISTRY;\n const no = (why: string): Provenance => ({ verified: false, why });\n\n const integrity = await readJson(get, `${registry}/byollm/${version}`);\n if (!integrity.ok)\n return no(`could not read the registry entry: ${integrity.why}`);\n const expected = sha512Hex(\n (integrity.body as { dist?: { integrity?: unknown } } | null)?.dist\n ?.integrity,\n );\n if (expected === undefined) {\n return no(\"the registry entry has no sha512 integrity to check against\");\n }\n\n const listed = await readJson(\n get,\n `${registry}/-/npm/v1/attestations/byollm@${version}`,\n );\n if (!listed.ok) return no(`could not read the attestations: ${listed.why}`);\n const attestations = (listed.body as { attestations?: unknown } | null)\n ?.attestations;\n if (!Array.isArray(attestations))\n return no(\"the registry lists no attestations\");\n\n const slsa = attestations.filter(\n (entry: unknown) =>\n (entry as { predicateType?: unknown } | null)?.predicateType === SLSA_V1,\n );\n if (slsa.length === 0) return no(\"there is no SLSA provenance for it\");\n\n /* Every SLSA statement has to pass, not any one of them. There is one per\n publish; if there were two and one named somebody else's workflow, the\n honest reading is that we do not know who built it. */\n for (const entry of slsa) {\n const statement = decodeStatement(entry);\n if (statement === undefined) return no(\"its provenance could not be read\");\n const why = checkStatement(statement, version, expected);\n if (why !== undefined) return no(why);\n }\n return { verified: true };\n}\n\ninterface Statement {\n readonly subject?: readonly {\n readonly name?: unknown;\n readonly digest?: { readonly sha512?: unknown };\n }[];\n readonly predicate?: {\n readonly buildDefinition?: {\n readonly externalParameters?: {\n readonly workflow?: {\n readonly repository?: unknown;\n readonly ref?: unknown;\n };\n };\n };\n };\n}\n\nfunction checkStatement(\n statement: Statement,\n version: string,\n expected: string,\n): string | undefined {\n const workflow =\n statement.predicate?.buildDefinition?.externalParameters?.workflow;\n if (workflow?.repository !== RELEASE_REPOSITORY) {\n return `it was built by ${named(workflow?.repository)}, not ${RELEASE_REPOSITORY}`;\n }\n if (workflow.ref !== `refs/tags/v${version}`) {\n return `it was built from ${named(workflow.ref)}, not refs/tags/v${version}`;\n }\n /* `isArray` narrows to `any[]`, so the element type is restated. */\n const subjects: NonNullable<Statement[\"subject\"]> = Array.isArray(\n statement.subject,\n )\n ? (statement.subject as NonNullable<Statement[\"subject\"]>)\n : [];\n const ours = subjects.find((s) => s.name === `pkg:npm/byollm@${version}`);\n if (ours === undefined)\n return `its provenance does not name byollm@${version}`;\n if (ours.digest?.sha512 !== expected) {\n return \"its provenance names a different tarball than the one npm installed\";\n }\n return undefined;\n}\n\nasync function readJson(\n get: Fetch,\n url: string,\n): Promise<{ ok: true; body: unknown } | { ok: false; why: string }> {\n try {\n const response = await get(url);\n if (!response.ok)\n return { ok: false, why: `HTTP ${String(response.status)}` };\n return { ok: true, body: await response.json() };\n } catch (error) {\n return {\n ok: false,\n why: error instanceof Error ? error.message : \"unknown error\",\n };\n }\n}\n\nfunction decodeStatement(entry: unknown): Statement | undefined {\n const payload = (\n entry as { bundle?: { dsseEnvelope?: { payload?: unknown } } } | null\n )?.bundle?.dsseEnvelope?.payload;\n if (typeof payload !== \"string\") return undefined;\n try {\n const parsed = JSON.parse(\n Buffer.from(payload, \"base64\").toString(\"utf8\"),\n ) as unknown;\n return typeof parsed === \"object\" && parsed !== null ? parsed : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * `sha512-<base64>` as the lowercase hex an in-toto subject carries.\n *\n * The two encodings of one hash are the whole comparison, so a wrong\n * conversion here is a check that refuses every release — or, written\n * carelessly, one that accepts any. The test runs it against a real\n * registry answer for that reason.\n */\nexport function sha512Hex(integrity: unknown): string | undefined {\n if (typeof integrity !== \"string\") return undefined;\n const match = /^sha512-([A-Za-z0-9+/]+={0,2})$/.exec(integrity.trim());\n if (match?.[1] === undefined) return undefined;\n const bytes = Buffer.from(match[1], \"base64\");\n return bytes.length === 64 ? bytes.toString(\"hex\") : undefined;\n}\n\nfunction named(value: unknown): string {\n return typeof value === \"string\" ? value : \"an unnamed workflow\";\n}\n","import type { CommandRunner } from \"./install.js\";\nimport {\n NPM_REGISTRY,\n verifyProvenance,\n type Provenance,\n} from \"./provenance.js\";\nimport type { UpdateDeps } from \"./update.js\";\n\n/**\n * The real hands behind {@link update} — B053.\n *\n * Kept apart from the decision logic because everything here shells out, and\n * a module that installs npm packages is a module you cannot run the failure\n * paths of. `update.ts` holds the ordering and the rollback and is tested\n * exhaustively; this holds the four commands and is small enough to read.\n *\n * `npm` is spelled as an argv rather than a shell string, through the same\n * runner the supervisor uses: no shell means nothing here can be\n * re-interpreted by whichever `/bin/sh` a platform ships, and a version\n * string is the one argument an attacker would most like to see concatenated\n * into a command line. `exactVersion` has already refused anything that is\n * not a literal version by the time it reaches here, so this is the second\n * of two fences rather than the only one.\n *\n * ## Two flags on the install, both B360\n *\n * `--ignore-scripts`, because the provenance check runs AFTER npm has put\n * the package down, and an install script would run before it. byollm ships\n * none and neither do its dependencies, so the flag costs nothing today and\n * means a package that fails the check has executed nothing when it is\n * rolled back. A future dependency that needs an install script will fail\n * here, loudly, which is the right time to have that conversation.\n *\n * `--registry`, pinned to the one {@link verifyProvenance} reads. A check\n * against npmjs of a tarball that came from a mirror checks nothing; an\n * owner whose npm points elsewhere gets a failed install and stays where\n * they are, rather than a verified-looking update from somewhere unverified.\n */\nexport function realUpdateDeps(input: {\n readonly run: CommandRunner;\n readonly drain: () => Promise<void>;\n readonly reregister: () => Promise<boolean>;\n readonly report: (line: string) => void;\n /** Injected in tests; the registry's provenance for the real thing. */\n readonly verify?: (version: string) => Promise<Provenance>;\n /** How the installed CLI is asked its version. `byollm`, normally. */\n readonly binary?: string;\n}): UpdateDeps {\n const binary = input.binary ?? \"byollm\";\n return {\n drain: input.drain,\n install: async (version) => {\n const result = await input.run([\n \"npm\",\n \"install\",\n \"--global\",\n \"--ignore-scripts\",\n `--registry=${NPM_REGISTRY}`,\n `byollm@${version}`,\n ]);\n if (result.code !== 0) {\n /* The words npm used, not our reading of them. A global install fails\n for reasons that are somebody's to fix — a permissions problem on a\n system prefix reads nothing like a registry outage — and an\n interpretation here would throw away the line that tells them\n apart. The same rider the supervisor refusal took. */\n input.report(\n `npm install byollm@${version} failed (exit ${String(result.code)})` +\n (result.output.trim() === \"\"\n ? \"\"\n : `: ${result.output.trim().split(\"\\n\").slice(-3).join(\" \")}`),\n );\n }\n return result.code === 0;\n },\n verify: input.verify ?? ((version) => verifyProvenance(version)),\n reregister: async () => (await input.run([binary, \"start\"])).code === 0,\n installedVersion: async () => {\n const result = await input.run([binary, \"--version\"]);\n if (result.code !== 0) return undefined;\n /**\n * The version, from a line that carries more than the version.\n *\n * `byollm --version` prints a sentence, so a whole-output comparison\n * would never match and every update would roll back.\n *\n * **Anchored to `byollm`, and the first draft was not.** It took the\n * first semver-shaped token, which is fine until the line is reworded\n * — a mutation that changed the prefix made it match `node 24.18.0`\n * from the second line and report NODE's version as the daemon's. Not\n * a failed canary: a confidently wrong one, rolling every machine back\n * and saying the binary reports 24.18.0.\n *\n * Anchored, a reworded line matches nothing and the answer is\n * `undefined`, which the caller treats as a failed canary. Wrong in\n * the direction that is safe, and loud.\n */\n const found = /^byollm (\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?)/m.exec(\n result.output,\n );\n return found?.[1];\n },\n report: input.report,\n };\n}\n","import { backendDescriptor, type BackendId } from \"@byollm/protocol\";\nimport { ClaudeCliBackend } from \"./claude-cli.js\";\nimport { CodexCliBackend } from \"./codex-cli.js\";\nimport { OpenAiHttpBackend } from \"./openai-http.js\";\nimport type { Backend, BackendInit } from \"./types.js\";\n\n/**\n * Construct a backend instance.\n *\n * Exhaustive over {@link BackendClass}, and now actually so — cloud_008 Tier\n * 3, finding 15. The docstring used to claim \"the switch is exhaustive over\n * `BackendId`, so adding a backend to the protocol registry without\n * implementing it here is a compile error\", and there was no switch: an `if`\n * on `class === \"process\"` with everything else falling through to the HTTP\n * transport. A backend class added to the registry would have got an\n * OpenAI-compatible client and failed at runtime, in the shape the comment\n * promised was impossible.\n *\n * The `never` assignment is what makes the claim true. It costs one line and\n * fails at compile time in the file that has to change.\n *\n * Providers are still registry entries rather than implementations\n * (byollm_007 §3): every HTTP-class id speaks the same\n * `/v1/chat/completions`, so they share one transport and one adversarial\n * corpus. Adding a *provider* is a line in the registry. Adding a *class* is\n * this function.\n */\nexport function createBackend(id: BackendId, init: BackendInit): Backend {\n const kind = backendDescriptor(id).class;\n switch (kind) {\n // Process class dispatches by **id**, not by class, and the difference is\n // not stylistic. `case \"process\": return new ClaudeCliBackend()` was\n // correct for exactly as long as there was one process backend; the\n // moment `codex-cli` was registered it inherited Claude's frozen argv —\n // right class, wrong program. The coverage check now asserts the\n // constructed id, which is what caught it.\n case \"process\":\n return createProcessBackend(id);\n // HTTP class dispatches by class on purpose. Providers are registry\n // entries rather than implementations (byollm_007 §3): every HTTP id\n // speaks the same `/v1/chat/completions`, so they share one transport and\n // one adversarial corpus, and adding a provider stays a line in the\n // registry.\n case \"http\":\n return new OpenAiHttpBackend(init);\n default: {\n const unimplemented: never = kind;\n throw new Error(\n `no backend implementation for class ${String(unimplemented)}`,\n );\n }\n }\n}\n\n/**\n * One binary per process backend, chosen exhaustively.\n *\n * The `never` is what makes registering a process backend without writing its\n * adapter a compile error in this file, rather than a runtime surprise in\n * somebody's job. An argv is the whole of what a process backend is, so there\n * is nothing here to share and no sensible default to fall through to.\n */\nfunction createProcessBackend(id: BackendId): Backend {\n switch (id) {\n case \"claude-cli\":\n return new ClaudeCliBackend();\n case \"codex-cli\":\n return new CodexCliBackend();\n default:\n throw new Error(`no process backend implementation for ${id}`);\n }\n}\n\nexport { ClaudeCliBackend, childEnv, claudeArgv } from \"./claude-cli.js\";\nexport { CodexCliBackend, codexArgv } from \"./codex-cli.js\";\nexport { OpenAiHttpBackend } from \"./openai-http.js\";\nexport { stopReasonOf } from \"./types.js\";\nexport type {\n Backend,\n StopReason,\n StopReasonMapping,\n BackendErrorCode,\n BackendHealth,\n BackendInit,\n BackendRequest,\n BackendResult,\n} from \"./types.js\";\n","import { execFile } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { delimiter, join } from \"node:path\";\nimport type { BackendClass, BackendId } from \"@byollm/protocol\";\nimport { runProcessJob } from \"./process-backend.js\";\nimport type {\n StopReasonMapping,\n Backend,\n BackendHealth,\n BackendRequest,\n BackendResult,\n} from \"./types.js\";\n\n/**\n * The exact argv this backend ever runs, minus the model.\n *\n * A frozen literal, not a builder, so there is no code path that appends to\n * it and no mechanism to pass job-supplied arguments\n * ({@link MUSTS.NO_SHELL_INTERPOLATION}). The adversarial suite asserts that\n * every hostile payload it throws produces exactly this argv.\n *\n * - `--print` — non-interactive, answer and exit.\n * - `--output-format text` — plain text, no envelope to misparse.\n * - `--tools \"\"` — the CLI's own switch for *disabling every built-in tool*.\n * byollm_004 §2 requires the model have no tools; this is the flag that\n * delivers it. Verified against the shipped CLI, not assumed.\n * - `--strict-mcp-config` with an empty `--mcp-config` — no MCP servers, and\n * none inherited from the user's own settings.\n * - `--restricted` — see below. Without it, `--tools \"\"` is not enough.\n * - `--no-session-persistence` — a job leaves no session behind.\n *\n * **`--restricted`, and why `--tools \"\"` alone was not containment (BY-01).**\n *\n * The CLI expands `@`-file mentions in the prompt *before* the model turn, in\n * its own input preprocessor. That is not the tool system, so `--tools \"\"`\n * does not touch it: the file's bytes are already in the context window by the\n * time a tool would have been offered. The empty scratch `cwd` defeats only\n * *relative* mentions, and `HOME` is in {@link ENV_ALLOWLIST} by design — so\n * `@/absolute/path` and `@~/path` both resolved, and any site the owner paired\n * with could make the owner's device read any file the owner can read and\n * return it in the answer. Reported privately as BY-01; reproduced here on\n * macOS against `claude` 2.1.277 with this exact argv, environment and cwd.\n *\n * `--restricted` **confines the file surface — the preprocessor included — to\n * the working directories**, and our working directory is the empty scratch\n * dir `runProcessJob` makes per job. So the containment is structural rather\n * than a refusal: there is nothing in scope to name. Measured, not read off\n * the help page — with it, seven mention shapes (`@/abs`, `@~/`,\n * `@$HOME/abs`, each bare, with a read verb, and with none) returned no canary\n * while the same argv without it returned the canary every time; and\n * `@inside.txt`, a file we placed in the scratch cwd, **still expands**, which\n * is the positive control proving the confinement is to the directory rather\n * than a blanket refusal the model happened to perform.\n *\n * It also ignores the user's, the project's and the local settings files,\n * which is the other half of the report: a hook or an `--append-system-prompt`\n * out of somebody's own `~/.claude` no longer reaches a job. Managed (policy)\n * settings still apply — `docs/security.md` §3.3 says so.\n *\n * Two flags that look like they should do this and do not, tested rather than\n * assumed: `--setting-sources \"\"` (David's first candidate) leaked every time,\n * as did `--safe-mode`, `--disable-slash-commands` and\n * `--permission-prompts none`. `--bare` is documented to skip keychain reads\n * and take Anthropic auth *only* from `ANTHROPIC_API_KEY` — which byollm_002\n * forbids this backend from holding — and it exits 1 with \"Not logged in\", so\n * it is not available to us at any price.\n *\n * `file-mentions-stay-outside.test.ts` is what keeps this from becoming a\n * memory: it runs the real binary against a real canary, and its control is\n * this argv with `--restricted` removed.\n */\nconst FIXED_ARGV = Object.freeze([\n \"--print\",\n \"--output-format\",\n \"text\",\n \"--tools\",\n \"\",\n \"--strict-mcp-config\",\n \"--mcp-config\",\n '{\"mcpServers\":{}}',\n \"--restricted\",\n \"--no-session-persistence\",\n]);\n\n/**\n * Environment variables a `claude` child is allowed to see.\n *\n * Everything else is dropped, so a prompt that says \"read your environment\"\n * finds nothing worth having ({@link MUSTS.STRIPPED_CHILD_ENV}).\n *\n * `HOME` is present and that is a deliberate, documented compromise: the CLI\n * reads its subscription credentials from the user's own config directory, so\n * removing `HOME` would remove the authentication this backend exists to use.\n * The honest statement is in `docs/security.md` — the child can reach the\n * filesystem the user can reach, and what stops it doing anything with that\n * is having no tools, not the environment.\n *\n * `ANTHROPIC_API_KEY` is deliberately **absent**: byollm_002 requires that\n * billing cannot silently move from the subscription to a metered key.\n *\n * `USER` is here for the same reason as `HOME`, found the hard way on\n * 2026-08-25 during the first cross-user job. On macOS the CLI keeps its\n * credentials in the login Keychain rather than under `HOME`, and reaching\n * them needs `USER`; without it every run answers \"Not logged in · Please run\n * /login\" and exits non-zero.\n *\n * This is exactly the failure the Windows note below predicts and it arrived\n * on the other platform first: **the backend reports healthy and every job\n * fails.** The health check runs `--version`, which needs no credentials, so\n * nothing between install and the first real prompt says anything is wrong.\n * `LOGNAME` carries the same name and does not fix it — tested, not assumed —\n * so it stays out.\n */\nconst ENV_ALLOWLIST = Object.freeze([\n \"PATH\",\n \"HOME\",\n \"USER\",\n \"LANG\",\n \"LC_ALL\",\n \"TZ\",\n \"TMPDIR\",\n]);\n\n/**\n * The Windows half of the same compromise `HOME` documents above.\n *\n * The CLI stores its subscription credentials under the user's profile, which\n * on Windows is named by `USERPROFILE` / `APPDATA` rather than `HOME`. Without\n * them the child starts and then cannot authenticate — the backend reports\n * healthy and every job fails, which is a worse failure than not starting.\n *\n * `SystemRoot` and `windir` are here because Windows resolves core DLLs\n * (including the socket stack) through them; a child without them fails in\n * ways that look nothing like a missing variable. `TEMP`/`TMP` are the\n * platform's `TMPDIR`, and `PATHEXT` is how Windows resolves an extensionless\n * command name at all.\n *\n * This widens the allowlist on Windows only. The rule byollm_004 §2 states —\n * the child sees an allowlist, never the daemon's environment — is unchanged.\n */\nconst WINDOWS_ENV_ALLOWLIST = Object.freeze([\n \"USERPROFILE\",\n \"APPDATA\",\n \"LOCALAPPDATA\",\n \"TEMP\",\n \"TMP\",\n \"SystemRoot\",\n \"windir\",\n \"PATHEXT\",\n]);\n\n/** Build the child's environment from the allowlist. */\nexport function childEnv(\n source: NodeJS.ProcessEnv = process.env,\n platform: NodeJS.Platform = process.platform,\n): Record<string, string> {\n const allowed =\n platform === \"win32\"\n ? [...ENV_ALLOWLIST, ...WINDOWS_ENV_ALLOWLIST]\n : ENV_ALLOWLIST;\n\n const env: Record<string, string> = {};\n for (const name of allowed) {\n const value = source[name];\n if (value !== undefined) env[name] = value;\n }\n // Signals to the CLI that it is not attached to a terminal, so it never\n // tries to render interactive UI into a pipe.\n env[\"CI\"] = \"1\";\n return env;\n}\n\n/** The argv this backend would run for a given model. Exported for the suite. */\nexport function claudeArgv(model: string): readonly string[] {\n return Object.freeze([...FIXED_ARGV, \"--model\", model]);\n}\n\n/** What to actually execute, and anything that must precede the CLI's own argv. */\nexport interface ClaudeLaunch {\n readonly command: string;\n readonly prefixArgs: readonly string[];\n}\n\n/**\n * Where the `claude` entry point really is on Windows.\n *\n * npm installs `claude` as `claude.cmd` and `claude.ps1` — there is no `.exe`.\n * Node refuses to spawn a `.cmd` without a shell (hardened after\n * CVE-2024-27980), so `spawn(\"claude\")` fails with ENOENT and this backend can\n * never report healthy on Windows.\n *\n * `shell: true` would fix the symptom and breach\n * {@link MUSTS.NO_SHELL_INTERPOLATION}, so it is not on the table. Instead we\n * find the JavaScript the shim would have run and run it under the Node binary\n * already executing this daemon: no shell, and the CLI's own argv is still the\n * frozen literal above, passed through untouched.\n */\nfunction findWindowsEntry(\n binary: string,\n source: NodeJS.ProcessEnv,\n npmPackage: string,\n): ClaudeLaunch | null {\n const dirs = (source[\"PATH\"] ?? \"\").split(delimiter).filter(Boolean);\n\n for (const dir of dirs) {\n // The npm package that owns this binary, so a second process backend gets\n // the same hard-won shim handling rather than its own copy. Split here\n // rather than stored pre-split: a scope and a name is how npm writes it\n // and how a reader recognises it.\n const pkg = join(dir, \"node_modules\", ...npmPackage.split(\"/\"));\n\n // Claude Code 2.x ships a native executable rather than a script. Node\n // spawns a real `.exe` without a shell, so this case needs none of the\n // indirection below — and checking it first matters, because the 1.x\n // layout it replaced is what the rest of this function looks for.\n //\n // Missing this is what made every route unhealthy the day the CLI updated:\n // no `cli.js`, a shim naming an `.exe` rather than a script, and so a\n // fallback to spawning `claude` bare — which on Windows is the extensionless\n // shim Node refuses to run.\n const exe = join(pkg, \"bin\", `${binary}.exe`);\n if (existsSync(exe)) return { command: exe, prefixArgs: [] };\n\n // The 1.x npm global layout: a JS entry beside the shim that launches it.\n const direct = join(pkg, \"cli.js\");\n if (existsSync(direct)) {\n return { command: process.execPath, prefixArgs: [direct] };\n }\n\n // Otherwise read the shim, which names its target. Cheap, and it survives\n // layouts we have not seen — pnpm and Volta both differ from npm.\n const shim = join(dir, `${binary}.cmd`);\n if (!existsSync(shim)) continue;\n try {\n // Both spellings of the shim's own directory appear in the wild:\n // `%~dp0` from npm's older template, `%dp0%` from the current one.\n const match =\n /\"?([A-Za-z]:\\\\[^\"\\r\\n]*?\\.(?:js|exe)|%~?dp0%?[^\"\\r\\n]*?\\.(?:js|exe))\"?/i.exec(\n readFileSync(shim, \"utf8\"),\n );\n if (!match?.[1]) continue;\n // Rebuilt with `join` rather than string substitution, and split on\n // either separator: a shim names its target relative to itself, and the\n // separator it uses is whichever npm happened to write. Concatenating\n // produced a mixed-separator path that resolves on Windows and nowhere\n // else — which is precisely the shape a cross-platform test needs.\n const target = /^[A-Za-z]:/.test(match[1])\n ? match[1]\n : join(\n dir,\n ...match[1].replace(/^%~?dp0%?[\\\\/]?/i, \"\").split(/[\\\\/]+/),\n );\n if (!existsSync(target)) continue;\n return /\\.exe$/i.test(target)\n ? { command: target, prefixArgs: [] }\n : { command: process.execPath, prefixArgs: [target] };\n } catch {\n // An unreadable shim is not an error worth failing over; try the next\n // directory and let health() report the honest \"not installed\".\n }\n }\n return null;\n}\n\n/**\n * Resolve what to spawn for the CLI on this platform.\n *\n * Everywhere but Windows this is the identity: the binary name, no prefix. The\n * caller's argv is unchanged on every platform, which is what keeps the\n * adversarial suite's argv assertions meaningful.\n */\nconst launchCache = new Map<string, ClaudeLaunch>();\n\n/** Drop the memo, for tests that change PATH between calls. */\nexport function resetClaudeLaunchCache(): void {\n launchCache.clear();\n}\n\nexport function resolveClaudeLaunch(\n binary = \"claude\",\n platform: NodeJS.Platform = process.platform,\n source: NodeJS.ProcessEnv = process.env,\n): ClaudeLaunch {\n return resolveCliLaunch(\n binary,\n \"@anthropic-ai/claude-code\",\n platform,\n source,\n );\n}\n\n/**\n * Resolve what to spawn for a CLI-backed backend on this platform.\n *\n * Generalised from `resolveClaudeLaunch` when `codex-cli` arrived. Everything\n * here except the npm package name was already backend-agnostic — the Windows\n * shim problem is npm's, not Anthropic's — and a second copy of a function\n * that parses `.cmd` shims with a regex is not something this codebase should\n * own twice.\n */\nexport function resolveCliLaunch(\n binary: string,\n npmPackage: string,\n platform: NodeJS.Platform = process.platform,\n source: NodeJS.ProcessEnv = process.env,\n): ClaudeLaunch {\n // Memoized because this runs on every `health()` and every `execute()`, and\n // on Windows walks each PATH entry with two `existsSync` calls. At the\n // default concurrency that is a synchronous filesystem crawl on the job\n // dispatch path, blocking the event loop while other jobs are in flight.\n //\n // Safe to cache: the answer is a function of the binary name, the platform\n // and PATH, none of which change meaningfully inside one daemon process. A\n // CLI installed while the daemon runs is picked up on restart — the same\n // thing already true of config.\n const key = `${platform}\\u0000${binary}\\u0000${npmPackage}\\u0000${source[\"PATH\"] ?? \"\"}`;\n const cached = launchCache.get(key);\n if (cached) return cached;\n const resolved = resolveUncached(binary, platform, source, npmPackage);\n launchCache.set(key, resolved);\n return resolved;\n}\n\nfunction resolveUncached(\n binary: string,\n platform: NodeJS.Platform,\n source: NodeJS.ProcessEnv,\n npmPackage: string,\n): ClaudeLaunch {\n if (platform !== \"win32\") return { command: binary, prefixArgs: [] };\n\n // A JavaScript entry point runs under this Node, wherever it came from. On\n // Unix a `#!/usr/bin/env node` script is executable and spawns directly;\n // Windows has no shebang, so the same file has to be handed to Node\n // explicitly. The adversarial suite's probe is exactly such a script, which\n // is why that suite could not run on Windows at all.\n if (/\\.[cm]?js$/i.test(binary)) {\n return { command: process.execPath, prefixArgs: [binary] };\n }\n\n // A real executable needs no help.\n if (/[\\\\/]/.test(binary) || /\\.(exe|com|bat|cmd)$/i.test(binary)) {\n return { command: binary, prefixArgs: [] };\n }\n\n return (\n findWindowsEntry(binary, source, npmPackage) ?? {\n command: binary,\n prefixArgs: [],\n }\n );\n}\n\n/**\n * The process-class backend: the user's own `claude` CLI, on their own\n * subscription.\n *\n * Subscription-class, so its offer scope is locked to `private`\n * ({@link MUSTS.SUBSCRIPTION_SELF_LOCK}) — one account runs one person's work.\n *\n * Every requirement of byollm_004 §2 applies here and is implemented here:\n * fixed argv, prompt on stdin, stripped environment, empty scratch `cwd`, no\n * inherited descriptors beyond the three std streams, hard timeout, hard\n * output cap.\n */\nexport class ClaudeCliBackend implements Backend {\n /**\n * Nothing to read — checked, not assumed (byollm_021).\n *\n * {@link FIXED_ARGV} runs `--output-format text`, and text is exactly what\n * comes back: an answer with no envelope around it, deliberately, because\n * an envelope is a thing to misparse. So there is no field carrying a stop\n * reason and no amount of parsing invents one.\n *\n * `--output-format json` would very likely carry it. Changing the frozen\n * argv is not a thing to do inside a mapping declaration: that argv is what\n * the adversarial suite asserts every hostile payload produces, and moving\n * it is its own decision with its own review.\n */\n readonly stopReasons: StopReasonMapping = {\n kind: \"unavailable\",\n why: \"`claude --output-format text` returns the answer and nothing else — no field says why generation stopped\",\n };\n\n readonly id: BackendId = \"claude-cli\";\n readonly class: BackendClass = \"process\";\n readonly signIn = \"run `claude` in a terminal\";\n readonly #binary: string;\n\n /**\n * @param binary - which executable to run. Defaults to `claude` and is\n * **not reachable from configuration**: {@link createBackend} constructs\n * this with no arguments, and {@link BackendInit} has no field for it. It\n * exists so the adversarial suite can substitute a probe that reports the\n * argv, environment, cwd and stdin it actually received — which is the only\n * way to *prove* byollm_004 §2 rather than assert it.\n */\n constructor(binary = \"claude\") {\n this.#binary = binary;\n }\n\n /**\n * One real, tiny call — the only way to learn whether credentials work.\n *\n * `--version` answers \"is the binary here\", which was never the question.\n * This asks the question, and pays for it: a handful of tokens against the\n * owner's own subscription, at daemon start rather than on every heartbeat.\n *\n * A failure here is reported rather than thrown, and the runner treats an\n * `unauthorized` exactly as it treats one from a real job — the service is\n * withdrawn and the owner is told once.\n */\n async canary(model: string): Promise<BackendHealth> {\n const result = await this.execute({\n model,\n prompt: \"Reply with the single word: ok\",\n timeoutMs: 30_000,\n // Enough for a word and a newline, and small enough that a chatty\n // model's answer cannot make this expensive.\n maxOutputBytes: 256,\n signal: new AbortController().signal,\n });\n return result.ok\n ? { healthy: true, models: [] }\n : { healthy: false, models: [], detail: result.message };\n }\n\n async health(): Promise<BackendHealth> {\n const version = await new Promise<string | null>((resolve) => {\n // execFile, never exec: no shell is involved even for our own fixed\n // arguments (the eslint rule bans the shell-invoking variants outright).\n const launch = resolveClaudeLaunch(this.#binary);\n execFile(\n launch.command,\n [...launch.prefixArgs, \"--version\"],\n { timeout: 10_000, env: childEnv() },\n (error, stdout) => {\n resolve(error ? null : stdout.trim());\n },\n );\n });\n\n if (version === null) {\n return {\n healthy: false,\n models: [],\n detail:\n \"the `claude` CLI is not installed or not on PATH \" +\n \"(https://claude.com/claude-code)\",\n };\n }\n // The CLI does not enumerate models, and inventing a list would breach\n // \"never advertise what isn't real\". The configured model is the claim,\n // and a wrong one surfaces as `model-not-found` on first use.\n return { healthy: true, models: [] };\n }\n\n async execute(request: BackendRequest): Promise<BackendResult> {\n const started = Date.now();\n // The spawn itself lives in `process-backend.ts` — one implementation of\n // byollm_004 §2's machinery for every process backend, because two copies\n // of a scratch cwd, a kill escalation and an output ceiling are two places\n // for them to drift apart. What stays here is the part that is genuinely\n // this backend's: which binary, and the frozen argv that turns its tools\n // off.\n return runProcessJob({\n launch: resolveClaudeLaunch(this.#binary),\n argv: claudeArgv(request.model),\n env: childEnv(),\n displayName: \"the claude CLI\",\n request,\n started,\n });\n }\n}\n","import { spawn } from \"node:child_process\";\nimport { quotaBlock, type Observation } from \"./quota.js\";\nimport { mkdtemp, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { BackendRequest, BackendResult } from \"./types.js\";\n\n/**\n * How long a dead child's pipes get to close before we settle anyway — B200.\n *\n * `exit` says the process is gone; `close` says nothing still holds its\n * streams. Normally they are microseconds apart and `close` wins, delivering\n * every byte. This timer only ever runs out when a helper the CLI spawned\n * inherited stdout and outlived it — and then waiting longer changes nothing\n * except how long the slot stays held.\n *\n * Two seconds: long enough that no ordinary flush loses a byte, short enough\n * that a job whose answer is already complete is not sitting on a device.\n *\n * **The value is not demonstrably necessary and is kept anyway — measured.**\n * Setting it to zero passes every case in\n * `a-helper-cannot-hold-a-job-open.test.ts`, because on this machine `close`\n * always wins the race and `finish` is idempotent. What the margin buys is the\n * case that race can lose: a large answer still draining when the process\n * exits. **That failure would be silent truncation** — a shorter answer, no\n * error, nothing to notice — and a margin against a silent failure is worth\n * two seconds on a path that only runs when a job has already ended.\n *\n * Said out loud rather than implied, because a constant whose test cannot fail\n * is exactly the kind that gets tuned to zero by somebody reading the green.\n */\nconst PIPE_GRACE_MS = 2_000;\n\n/**\n * The spawn every process-class backend runs — byollm_004 §2, in one place.\n *\n * This was `ClaudeCliBackend.#spawn`, and it was the only one there could be\n * while `claude-cli` was the only process backend. `codex-cli` made that\n * false, and the choice was to copy two hundred lines of security-critical\n * machinery or to share them. Copying is how two implementations of one rule\n * drift, and the rules here are the ones with teeth: an empty scratch cwd, a\n * stripped environment, exactly three std streams, no shell, a hard timeout\n * with SIGTERM→SIGKILL escalation, and an output ceiling enforced while bytes\n * arrive rather than after.\n *\n * What is **not** shared is the argv. An argv is the whole of what a process\n * backend is — which binary, in what mode, with which capabilities switched\n * off — so each backend supplies its own, frozen, and this function passes it\n * through untouched. That is what keeps the adversarial suite's argv\n * assertions meaningful: it can substitute a probe for the binary and prove\n * that a hostile payload produced exactly the argv the backend declared.\n */\nexport interface ProcessJob {\n /** What to execute, plus anything that must precede the CLI's own argv. */\n readonly launch: {\n readonly command: string;\n readonly prefixArgs: readonly string[];\n };\n /** The backend's own frozen argv. Never built from a payload. */\n readonly argv: readonly string[];\n /** Environment the child may see — already an allowlist. */\n readonly env: NodeJS.ProcessEnv;\n /** How to name this CLI in a diagnostic, e.g. \"the codex CLI\". */\n readonly displayName: string;\n readonly request: BackendRequest;\n /** When the caller started timing, so durations cover the whole call. */\n readonly started: number;\n /**\n * The clock the quota corpus reads, injectable — S1's shape, one adapter\n * over.\n *\n * A \"try again at\" already past is dropped, because a stale clock brings\n * somebody back to a machine that is still blocked. Read from `Date.now()`\n * inside, any transcript we hold stops exercising that path the day after\n * it was captured — a test that goes green on a schedule, which is what the\n * Codex adapter was filed for.\n */\n readonly now?: () => number;\n /** The observations to classify against. Tests own their own strings. */\n readonly corpus?: readonly Observation[];\n}\n\nexport async function runProcessJob(job: ProcessJob): Promise<BackendResult> {\n // A call with no time limit is a caller bug, and running it unbounded is a\n // worse answer than refusing it. Guarded here rather than trusted to the\n // type: the message this replaces read \"did not answer within undefinedms\",\n // which names a number nobody set — and it fired instantly, so the run\n // looked like a timeout that had not happened.\n if (!Number.isFinite(job.request.timeoutMs) || job.request.timeoutMs <= 0) {\n return {\n ok: false,\n code: \"backend-error\",\n message: \"no time limit was set for this call\",\n durationMs: 0,\n };\n }\n\n // An empty directory of our own making. The child's `cwd` is never the\n // daemon's, never the user's home, and never anything a payload named.\n const scratch = await mkdtemp(join(tmpdir(), \"byollm-job-\"));\n try {\n return await spawnIn(job, scratch);\n } finally {\n await removeScratch(scratch);\n }\n}\n\n/**\n * Remove the job's scratch directory, and never let that fail the job.\n *\n * This was `await rm(...)` in the `finally` above, which has two edges. A\n * throw in a `finally` **replaces the value being returned** — so a job that\n * ran perfectly would come back as an unrelated filesystem error. And the\n * throw is not hypothetical on Windows: this backend's whole subject is a\n * helper the CLI spawned that outlives it (B200), and a live process holding\n * a directory makes `rmdir` fail with `EBUSY` there. Measured on\n * `windows-latest`: `EBUSY: resource busy or locked, rmdir\n * 'C:\\Users\\RUNNER~1\\AppData\\Local\\Temp\\byollm-job-...'`, failing three\n * cases in the suite written for exactly that scenario.\n *\n * `maxRetries` is Node's own answer to this — it exists because Windows\n * releases handles asynchronously — and the `catch` is what makes the promise\n * above. **A leaked temp directory is the smaller harm than a lost answer**\n * somebody is waiting on, and the OS clears `tmpdir()` regardless.\n */\nexport async function removeScratch(scratch: string): Promise<void> {\n await rm(scratch, {\n recursive: true,\n force: true,\n maxRetries: 5,\n retryDelay: 50,\n }).catch(() => undefined);\n}\n\nfunction spawnIn(job: ProcessJob, scratch: string): Promise<BackendResult> {\n const { request, started, displayName } = job;\n return new Promise<BackendResult>((resolve) => {\n // Check before spawning, not only via the listener below: a job cancelled\n // between the claim and this line arrives with its signal already\n // aborted, and `addEventListener(\"abort\")` never fires for a signal that\n // has already fired. Without this the child would spawn and run to\n // completion after the owner had already said stop.\n if (request.signal.aborted) {\n resolve({\n ok: false,\n code: \"canceled\",\n message: \"the job was canceled before it started\",\n durationMs: Date.now() - started,\n });\n return;\n }\n\n const child = spawn(\n job.launch.command,\n [...job.launch.prefixArgs, ...job.argv],\n {\n cwd: scratch,\n env: job.env,\n // Exactly the three std streams. Nothing else is inherited, so the\n // child cannot reach a descriptor the daemon happens to hold open.\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n // No shell, ever. With `shell: false` the argv array is passed to\n // execvp verbatim and metacharacters in it are just bytes.\n shell: false,\n /**\n * Its own process group, so helpers die with it — B200.\n *\n * `child.kill()` signals the DIRECT child only. An agentic CLI that\n * spawns a helper left that helper running after SIGTERM, and the\n * helper inherited stdout — so `close` never fired, the promise never\n * settled, and the job held its slot for ever. Todd watched two sit at\n * 20+ minutes under a 600s ceiling.\n *\n * POSIX only. On Windows `detached` means a new console rather than a\n * process group, and negative pids are not a thing — that platform\n * keeps exactly the behaviour it had, which is the honest position\n * until somebody can test a fix there.\n */\n detached: process.platform !== \"win32\",\n },\n );\n\n let stdout = \"\";\n let stderr = \"\";\n let outputBytes = 0;\n /**\n * The two boundaries a parent can see — B195.\n *\n * `spawn` fires when the process exists; the first chunk on either stream\n * is the first sign it has anything to say. Everything between them is the\n * child's own startup plus its first vendor response, and those are not\n * separable from out here — see `BackendTiming`, which says so rather than\n * letting the number imply otherwise.\n *\n * `undefined` rather than 0 until each happens, because a call that died\n * before spawning has no spawn time and a zero would be a measurement\n * nobody made.\n */\n let spawnedAt: number | undefined;\n let firstOutputAt: number | undefined;\n const timing = (): { spawnMs?: number; firstOutputMs?: number } => ({\n ...(spawnedAt === undefined ? {} : { spawnMs: spawnedAt - started }),\n ...(firstOutputAt === undefined\n ? {}\n : { firstOutputMs: firstOutputAt - started }),\n });\n const sawOutput = (): void => {\n firstOutputAt ??= Date.now();\n };\n child.on(\"spawn\", () => {\n spawnedAt = Date.now();\n });\n let settled = false;\n let exited = false;\n let reason: \"timeout\" | \"canceled\" | \"output-too-large\" | null = null;\n\n const finish = (result: BackendResult): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n request.signal.removeEventListener(\"abort\", onAbort);\n /* Attached here rather than at each `finish(...)` call — B195. There are\n six of them across timeout, cancel, output-ceiling, spawn error and\n two close paths, and a timing added to five of six is a gap nobody\n would see: the missing one is whichever failure somebody is trying to\n explain. */\n resolve({ ...result, timing: timing() });\n };\n\n /**\n * Signal the whole group where that is possible — B200.\n *\n * `process.kill(-pid)` addresses the group the `detached` spawn created,\n * which is the only way a helper the CLI started ever hears anything.\n * Falls back to the direct child on Windows, on a child with no pid, and\n * on ESRCH — a group that is already gone is not an error worth throwing\n * out of a kill path whose entire job is to stop waiting.\n */\n const signal = (sig: NodeJS.Signals): void => {\n const pid = child.pid;\n if (pid === undefined || process.platform === \"win32\") {\n child.kill(sig);\n return;\n }\n try {\n process.kill(-pid, sig);\n } catch {\n child.kill(sig);\n }\n };\n\n /**\n * Is anything in the child's process group still running — B216.\n *\n * Signal 0 delivers nothing and reports whether a target exists, so this\n * asks the question the escalation actually needs: not \"did the child\n * exit\" but \"is there still something in the group to kill\". A group is\n * alive while ANY member is, which is exactly the helper case.\n *\n * **The honest caveat:** once the child is reaped its pid can be reused,\n * and a reused pid that happens to lead a group would answer yes here. A\n * 2-second window makes that rare, and the trade is deliberate — the\n * alternative is the leak this fixes, where every timed-out job on a\n * 320 MiB box leaves a helper behind until the box dies. Falls back to\n * `!exited` where groups do not exist (Windows) or there is no pid.\n */\n const groupAlive = (): boolean => {\n const pid = child.pid;\n if (pid === undefined || process.platform === \"win32\") return !exited;\n try {\n process.kill(-pid, 0);\n return true;\n } catch {\n return false;\n }\n };\n\n const kill = (why: typeof reason): void => {\n reason = why;\n // SIGTERM first, SIGKILL shortly after: a wedged child must not be able\n // to outlive its budget by ignoring the polite signal.\n //\n // The escalation is NOT gated on `child.killed` — that flag means \"a\n // signal was sent\", not \"the process died\", so gating on it would mean\n // the SIGKILL never fires against exactly the child that ignores\n // SIGTERM.\n //\n // **Nor on `exited` — B216, reported privately by Rob and verified in\n // this file.** `exited` comes from the DIRECT child's `close`, and the\n // kill is aimed at the GROUP. So a well-behaved parent that obeys\n // SIGTERM suppressed the escalation while a same-group helper with a\n // no-op SIGTERM handler lived on, past its job, un-SIGKILLed. **The gate\n // answered \"did the child die\" while the target was \"the group\".**\n // B200 aimed at the group precisely because helpers exist; the gate\n // never followed.\n //\n // Sharpest on a box — 320 MiB, concurrency 1 — where one orphan per\n // timed-out job accumulates until the OOM killer arrives.\n signal(\"SIGTERM\");\n setTimeout(() => {\n if (groupAlive()) signal(\"SIGKILL\");\n }, 2_000).unref();\n };\n\n const timer = setTimeout(() => {\n kill(\"timeout\");\n }, request.timeoutMs);\n\n const onAbort = (): void => {\n kill(\"canceled\");\n };\n request.signal.addEventListener(\"abort\", onAbort, { once: true });\n\n child.stdout.on(\"data\", (chunk: Buffer) => {\n sawOutput();\n outputBytes += chunk.byteLength;\n if (outputBytes > request.maxOutputBytes) {\n kill(\"output-too-large\");\n return;\n }\n stdout += chunk.toString(\"utf8\");\n });\n\n child.stderr.on(\"data\", (chunk: Buffer) => {\n sawOutput();\n // Bounded independently: a chatty stderr must not exhaust memory\n // either, and it is only ever used for a diagnostic message.\n if (stderr.length < 8_192) stderr += chunk.toString(\"utf8\");\n });\n\n child.on(\"error\", (error: Error) => {\n finish({\n ok: false,\n code: \"backend-unreachable\",\n message: `could not start ${displayName}: ${error.message}`,\n durationMs: Date.now() - started,\n });\n });\n\n /**\n * Settle from a finished child — B200.\n *\n * Extracted because it now has two callers. It ran only from `close`, and\n * **Node fires `close` when the process has exited AND every stdio stream\n * has closed** — a pipe a grandchild inherited keeps it from ever firing.\n * The promise then pends for ever: no outcome, no release, and a slot held\n * by a job that is already dead.\n */\n const settleFrom = (code: number | null): void => {\n const durationMs = Date.now() - started;\n\n if (reason === \"canceled\") {\n finish({\n ok: false,\n code: \"canceled\",\n message: \"the job was canceled\",\n durationMs,\n });\n return;\n }\n if (reason === \"timeout\") {\n finish({\n ok: false,\n code: \"timeout\",\n message: `the model did not answer within ${String(request.timeoutMs)}ms`,\n durationMs,\n });\n return;\n }\n if (reason === \"output-too-large\") {\n finish({\n ok: false,\n code: \"output-too-large\",\n message: `the model produced more than ${String(request.maxOutputBytes)} bytes`,\n durationMs,\n });\n return;\n }\n if (code !== 0) {\n // An agentic CLI that cannot authenticate exits non-zero and says so\n // in prose. Recognising that turns a generic `backend-error` into the\n // one typed failure the runner can act on — see {@link isAuthFailure}.\n const said = `${stdout}\\n${stderr}`;\n /*\n * Quota before auth — byollm_019 §3.1.\n *\n * Both are prose from the same stream, and the order decides which\n * remedy the owner is given. A quota block needs time and nothing\n * else; being told to sign in when the account is merely busy until\n * 7pm is the remedy-must-match-the-cause failure in a new place.\n *\n * Read here, on this adapter's own definition of failure — a non-zero\n * exit is what \"failed\" means for a CLI that reports it honestly.\n * Codex does not, which is why its adapter decides for itself from a\n * terminal event and calls the same corpus.\n *\n * The corpus is observed-only, so on every machine that has not met\n * one of these strings this branch is never taken and behaviour is\n * exactly what it was.\n */\n const blocked = quotaBlock(said, (job.now ?? Date.now)(), job.corpus);\n const authFailed = blocked === undefined && isAuthFailure(said);\n finish({\n ok: false,\n code:\n blocked !== undefined\n ? \"quota-exhausted\"\n : authFailed\n ? \"unauthorized\"\n : \"backend-error\",\n ...(blocked?.until === undefined ? {} : { until: blocked.until }),\n message:\n blocked !== undefined\n ? /* The CLI's own words, as the Codex path already carries —\n M2, 2026-09-04. This said only \"out of quota for now\",\n throwing away the sentence that names the time, on the one\n surface entitled to it. Owner-only: the class is all a site\n ever sees. */\n `${displayName} is out of quota: ${firstLine(blocked.detail)}`\n : authFailed\n ? `${displayName} is not signed in`\n : stderr.trim() !== \"\"\n ? `${displayName} failed: ${firstLine(stderr)}`\n : /* Nothing on stderr does not mean nothing was said. An\n agentic CLI writes its errors to stdout — that is where\n \"Failed to authenticate\" arrived — and reporting a bare\n exit code while holding the explanation is the one thing a\n diagnostic must not do. No new exposure: stdout already\n goes to the site on success. */\n stdout.trim() !== \"\"\n ? `${displayName} failed: ${firstLine(stdout)}`\n : `${displayName} exited with status ${String(code)}`,\n durationMs,\n });\n return;\n }\n finish({ ok: true, text: stdout, durationMs });\n };\n\n /**\n * `exit` fires when the process is gone, whatever still holds its pipes.\n *\n * The grace is what keeps a normal job's output intact: for a child that\n * simply ended, `close` arrives within microseconds of `exit` and settles\n * with everything it said. The timer only ever runs out when something is\n * still holding a stream open — the case this row is about — and then a\n * dead child's answer is whatever it managed to say.\n *\n * `exited` moves here too. The SIGKILL escalation gates on it, and gating\n * on `close` meant the escalation was deciding from a signal that the very\n * failure it exists for prevents.\n */\n child.on(\"exit\", (code) => {\n exited = true;\n setTimeout(() => {\n settleFrom(code);\n }, PIPE_GRACE_MS).unref();\n });\n\n /* The happy path, unchanged: for a child that simply ended, `close`\n arrives with everything it said and settles before the grace above\n expires. `finish` is idempotent, so whichever wins is the only one that\n counts. */\n child.on(\"close\", (code) => {\n exited = true;\n settleFrom(code);\n });\n\n // The prompt goes here and nowhere else: on stdin, as bytes, after the\n // argv is already fixed. This is the line byollm_004 §2 is about.\n child.stdin.on(\"error\", () => {\n // A child that died before reading stdin surfaces through `close`.\n });\n child.stdin.end(request.prompt, \"utf8\");\n });\n}\n\n/** First line of stderr, for a one-line diagnostic. */\nfunction firstLine(text: string): string {\n return text.trim().split(\"\\n\")[0] ?? \"\";\n}\n\n/**\n * Does this output say \"you are not signed in\"?\n *\n * A CLI backend that has lost its credentials exits non-zero with prose and\n * no machine-readable code, so this is text matching, which rots. It is worth\n * having anyway because of what it is *for*: the runner withdraws a service on\n * `unauthorized` and does nothing on `backend-error`, so a phrase this misses\n * leaves today's behaviour exactly as it was. The failure mode of the match is\n * silence, not a wrong action — which is the only shape a heuristic like this\n * may take.\n *\n * The corpus is what real CLIs print. Claude says \"Not logged in · Please run\n * /login\" on stdout and exits 1; that line is why this exists, found when the\n * first cross-user job reached Todd's Mac and the backend reported healthy\n * throughout.\n *\n * Deliberately narrow, and only consulted on a **non-zero exit** — a job that\n * succeeded never reaches it. That bounds the risk without removing it: a run\n * can produce output and then fail for another reason, and the output is a\n * model's answer. So the patterns are word-anchored rather than substrings,\n * and the tests carry the sentences a model might plausibly write.\n */\nconst AUTH_FAILURE = [\n // Word-anchored, not substrings. `includes(\"not logged in\")` also matches\n // \"was not logged into the system\", which is a sentence a model can write —\n // and a false positive here withdraws a service that works, which is worse\n // than the silence this replaces.\n /\\bnot logged in\\b/,\n /\\bplease run \\/login\\b/,\n /\\bplease log in\\b/,\n /\\bnot authenticated\\b/,\n /\\bauthentication failed\\b/,\n /\\binvalid api key\\b/,\n /\\b401 unauthorized\\b/,\n /**\n * The expiry family — Todd's Mac, 2026-08-31, and the reason this list grew.\n *\n * The CLI said \"Failed to authenticate. API Error: 401 OAuth access token\n * has expired. Re-authenticate to continue.\" and not one pattern above\n * matched it: the list had \"authentication failed\" and the CLI wrote the\n * same two words in the other order, and it had \"401 unauthorized\" against\n * a 401 that named OAuth instead. So a signed-out backend was reported as\n * `backend-error`, the service was not withdrawn, and the sentence that\n * reached the person was \"the claude CLI exited with status 1\".\n *\n * An expired token is the *common* case in a subscription CLI — it is what\n * happens to everybody eventually, on a machine that was working\n * yesterday — and it was the one the corpus lacked.\n *\n * \"failed to authenticate\" was in this list for about a minute. The test\n * below refused it: \"the guard had failed to authenticate the visitor's\n * papers\" is a sentence a model can write, and matching it would withdraw a\n * service that works. The two that remain are auth machinery talking about\n * itself, which prose has little reason to imitate — and Todd's message\n * contained both, so nothing was lost by dropping the loose one.\n */\n /\\baccess token has expired\\b/,\n /\\bre-?authenticate\\b/,\n];\n\nexport function isAuthFailure(output: string): boolean {\n const text = output.toLowerCase();\n return AUTH_FAILURE.some((pattern) => pattern.test(text));\n}\n","/**\n * Is this CLI blocked by its own quota, and until when — byollm_019 §3.1.\n *\n * A subscription CLI that is signed in, healthy, and simply *busy until 7pm*\n * used to classify as `backend-error`, which is the class for \"something went\n * wrong once\" and which the runner deliberately does nothing about. So every\n * job took the slow path: claimed by a device whose service would fail,\n * failing, and telling the site nothing until the job's TTL ran out. The\n * fallback Todd promised Eric cannot fire, because the site was never told\n * anything to fall back *from*.\n *\n * Distinct from `unauthorized`, and that is the point rather than tidiness:\n * the remedies are opposite. A signed-out CLI needs a person at a terminal. A\n * quota-blocked one needs **time and nothing else**, and telling somebody to\n * sign in when their account is merely busy is the remedy-must-match-the-cause\n * failure in a new place.\n *\n * ## The corpus admits only what a CLI actually said — ruled 2026-09-03\n *\n * Every entry below is verbatim output somebody met, with the date and the\n * version that produced it. **Guessed strings never.** The reason is in the\n * failure modes, which are not symmetric:\n *\n * - a phrase we have not seen changes nothing — the service stays\n * advertised and behaves exactly as it does today;\n * - a phrase we invented that matches something else withdraws a service\n * that works, and tells its owner to wait for a block that does not exist.\n *\n * **The failure mode of the match is silence, not a wrong action** — the rule\n * the auth corpus states, and which that corpus learned the hard way by\n * growing wrong the first time.\n *\n * An empty corpus is a legal state. The machinery ships either way; what it\n * does with nothing observed is nothing, which is today's behaviour.\n *\n * ## What it does not read\n *\n * Never the model's own answer. A prompt asking about rate limits, or a model\n * discussing them, must not withdraw the service that answered — so callers\n * pass only a *failure* diagnostic, and only when the adapter has already\n * decided the call failed.\n */\n\n/** One thing a CLI really printed, kept with the evidence that it did. */\nexport interface Observation {\n /** The pattern, anchored on words rather than substrings. */\n readonly pattern: RegExp;\n /** Which CLI said it, and the version that said it. */\n readonly seenOn: string;\n /** When somebody met it. */\n readonly seenAt: string;\n /** The output, near enough verbatim to recognise. */\n readonly verbatim: string;\n}\n\n/**\n * Observed quota blocks.\n *\n * Adding one is not a code change so much as a filing: paste what the CLI\n * said, name the version, date it. Nothing here may be written from\n * documentation, from a changelog, or from a guess about how the sentence\n * probably goes.\n */\nconst OBSERVED: readonly Observation[] = [\n {\n // Met on Todd's Mac; the same message the outside report quoted when it\n // showed Codex reporting failure while exiting zero.\n pattern: /\\busage limit\\b/iu,\n seenOn: \"codex-cli 0.149.1\",\n seenAt: \"2026-09-03\",\n verbatim:\n \"You've hit your usage limit. Try again at Sep 3rd, 2026 8:28 AM.\",\n },\n];\n\n/**\n * When the CLI says it will be back.\n *\n * A reason without a clock turns \"wait\" into \"give up\" — somebody told their\n * service is blocked and not told for how long has no way to tell an hour from\n * a week, and reaches for the remedy that always looks available: turning it\n * off.\n *\n * Read only from observed shapes, and absent is a perfectly good answer. A\n * wrong time is worse than none: it would have somebody come back to a machine\n * that is still blocked, having been told it would not be.\n */\nfunction until(message: string, now: number): number | undefined {\n // \"Try again at Sep 3rd, 2026 8:28 AM.\" — codex-cli 0.149.1, 2026-09-03.\n const at = /\\btry again at ([^.]+)\\./iu.exec(message);\n if (at?.[1] === undefined) return undefined;\n // The ordinal suffix is not something `Date` parses; nothing else in the\n // string needs touching.\n const parsed = Date.parse(at[1].replace(/(\\d+)(st|nd|rd|th)\\b/iu, \"$1\"));\n if (Number.isNaN(parsed)) return undefined;\n // A time already past is a time we read wrong — a block that ended before\n // we were told about it is not a block. Silence beats a confident mistake.\n return parsed > now ? parsed : undefined;\n}\n\nexport interface QuotaBlock {\n /** The CLI's own words, for the owner and nobody else. */\n readonly detail: string;\n /** Epoch ms the CLI expects to be back, when it said so. */\n readonly until?: number;\n}\n\n/**\n * Classify one failure diagnostic.\n *\n * `undefined` means \"not a quota block as far as we know\", which is the answer\n * for everything the corpus has not met. Callers must already have decided the\n * call failed: this reads a diagnostic, never an answer.\n */\nexport function quotaBlock(\n message: string,\n now: number,\n /**\n * Which observations to match against. Injectable, and not for convenience.\n *\n * §6.1 rules an empty corpus legal, and the suite made that false: six\n * tests reddened when `OBSERVED` was emptied, because they asserted\n * behaviour through whichever strings we happened to have collected. That\n * couples \"does the classifier work\" to \"what have we filed\", and the\n * second is meant to change without ceremony.\n *\n * **A test fixture owns its own strings; the corpus owns only what ships.**\n */\n corpus: readonly Observation[] = OBSERVED,\n): QuotaBlock | undefined {\n if (!corpus.some((seen) => seen.pattern.test(message))) return undefined;\n const at = until(message, now);\n return { detail: message, ...(at === undefined ? {} : { until: at }) };\n}\n\n/** What the corpus holds, for the test that proves it is reachable at all. */\nexport const observedQuotaCorpus = OBSERVED;\n","import { execFile } from \"node:child_process\";\nimport type { BackendClass, BackendId } from \"@byollm/protocol\";\nimport { childEnv, resolveCliLaunch } from \"./claude-cli.js\";\nimport { isAuthFailure, runProcessJob } from \"./process-backend.js\";\nimport { quotaBlock, type Observation } from \"./quota.js\";\nimport { loginCommandFor } from \"../login.js\";\nimport type {\n StopReasonMapping,\n Backend,\n BackendErrorCode,\n BackendHealth,\n BackendRequest,\n BackendResult,\n} from \"./types.js\";\n\n/**\n * The exact argv this backend ever runs, minus the model.\n *\n * A frozen literal, not a builder, so there is no code path that appends to it\n * and no mechanism to pass job-supplied arguments\n * ({@link MUSTS.NO_SHELL_INTERPOLATION}).\n *\n * **Codex is an agent, and byollm_004 §2 says the model gets no tools.** That\n * is not a default here — it is a list of switches, and every one of them was\n * verified against the shipped binary rather than read off a help page. The\n * default feature set of `@openai/codex` 0.149 has `shell_tool`,\n * `unified_exec`, `browser_use`, `browser_use_full_cdp_access`, `computer_use`,\n * `hooks`, `plugins`, `apps`, `multi_agent`, `image_generation` and\n * `skill_search` all *stable and on*. A backend that shipped without disabling\n * them would have handed any site the owner trusts a shell and a browser on the\n * owner's machine.\n *\n * How it was verified, because \"we passed some flags\" is not evidence: a canary\n * string was written to a file in the child's directory and the model was asked\n * to read it. With these flags it answers that it has no file-reading tool and\n * the canary never appears; with them removed it returns the canary verbatim.\n * The control is the half that matters — without it the test would pass against\n * a model that was merely being agreeable. `codex-tools-disabled.test.ts` keeps\n * that check runnable rather than a memory.\n *\n * - `exec` — the non-interactive subcommand; answers and exits.\n * - `--json` — a terminal event decides the outcome. Codex can report\n * `turn.failed` and still exit zero, so process status is not success.\n * - `--skip-git-repo-check` — **required**, not hygiene. Codex refuses to run\n * outside a trusted git directory, and byollm_004 §2 requires the child's cwd\n * be an empty scratch dir, which never is one. Without this every job fails\n * before the model is reached.\n * - `-s read-only` — the sandbox for model-generated commands. Belt to the\n * braces of the disables above: if a future release renames a feature flag,\n * this still bounds what a tool could do.\n * - `--ephemeral` — a job leaves no session behind.\n * - `--ignore-user-config` — the owner's own `config.toml` does not reach this\n * child. A daemon whose behaviour changed because a person edited their\n * personal Codex settings would be a daemon whose guarantees are advisory.\n * - `--color never` — plain bytes, no escape sequences to misparse.\n */\nconst FIXED_ARGV = Object.freeze([\n \"exec\",\n \"--json\",\n \"--skip-git-repo-check\",\n \"-s\",\n \"read-only\",\n \"--ephemeral\",\n \"--ignore-user-config\",\n \"--color\",\n \"never\",\n \"--disable\",\n \"shell_tool\",\n \"--disable\",\n \"unified_exec\",\n \"--disable\",\n \"browser_use\",\n \"--disable\",\n \"browser_use_external\",\n \"--disable\",\n \"browser_use_full_cdp_access\",\n \"--disable\",\n \"computer_use\",\n \"--disable\",\n \"hooks\",\n \"--disable\",\n \"plugins\",\n \"--disable\",\n \"apps\",\n \"--disable\",\n \"multi_agent\",\n \"--disable\",\n \"image_generation\",\n \"--disable\",\n \"skill_search\",\n]);\n\n/** The argv for one call, model included. Frozen, and never payload-derived. */\nexport function codexArgv(model: string): readonly string[] {\n return Object.freeze([...FIXED_ARGV, \"--model\", model]);\n}\n\n/** The meaning of one complete `codex exec --json` stream. */\nexport type CodexOutput =\n | { readonly ok: true; readonly text: string }\n | {\n readonly ok: false;\n readonly code: BackendErrorCode;\n readonly message: string;\n /**\n * When the CLI expects to be back — 019 §3.2, carried not dropped.\n *\n * This shape had no such field, so a Codex quota block arrived without\n * the clock `quota.ts` had just parsed out of it. The runner then\n * released the service on the next detection pass: a five-hour block\n * would withdraw for seconds, re-advertise, and fail again forever, so\n * the fast failover never engaged for the one CLI the corpus has\n * actually observed. The runner tests missed it because they injected\n * `until` on a synthetic backend rather than reading a real transcript.\n */\n readonly until?: number | undefined;\n };\n\nfunction record(value: unknown): Record<string, unknown> | undefined {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\n/** One bounded line from a provider diagnostic, for the device owner only. */\nfunction diagnostic(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const text = Array.from(value, (character) => {\n const codeUnit = character.charCodeAt(0);\n return codeUnit <= 31 || codeUnit === 127 ? \" \" : character;\n })\n .join(\"\")\n .trim()\n .slice(0, 2_000);\n return text === \"\" ? undefined : text;\n}\n\nfunction failureMessage(event: Record<string, unknown>): string {\n const error = record(event[\"error\"]);\n return (\n diagnostic(event[\"message\"]) ??\n diagnostic(error?.[\"message\"]) ??\n \"codex reported an error\"\n );\n}\n\nfunction classifyFailure(\n message: string,\n now: number,\n corpus?: readonly Observation[],\n): {\n readonly code: BackendErrorCode;\n readonly until?: number | undefined;\n} {\n /*\n * Quota first, and from the shared corpus rather than from patterns typed\n * here — byollm_019 §3.1, ruled 2026-09-03.\n *\n * This function shipped with three patterns: one met on a real machine and\n * two that were plausible English nobody had seen. The ruling is that the\n * corpus admits observed strings only and that an empty corpus is legal,\n * because the failure modes are not symmetric — a phrase we have not met\n * changes nothing, and a phrase we invented withdraws a service that works.\n * The two guesses are gone; the observed one moved to `quota.ts`, where\n * both CLIs read it and where an addition carries its date and version.\n *\n * Gated on Codex's *own* definition of failure, which is what this whole\n * file exists to establish: a terminal `error` or `turn.failed` event, not\n * the exit status. A quota block that exits zero — the shape this adapter\n * was written for — would be invisible to the machinery built for it if\n * this read the exit code.\n */\n const blocked = quotaBlock(message, now, corpus);\n if (blocked !== undefined) {\n return {\n code: \"quota-exhausted\",\n ...(blocked.until === undefined ? {} : { until: blocked.until }),\n };\n }\n if (isAuthFailure(message)) {\n return { code: \"unauthorized\" };\n }\n return { code: \"backend-error\" };\n}\n\n/**\n * Interpret Codex's JSONL stream without trusting its process exit status.\n *\n * Codex can emit `error` and `turn.failed` and still exit zero. Treating that\n * status as success hands the provider's failure text to the site as though it\n * were a model answer. A run succeeds only when the stream contains\n * `turn.completed`; a terminal error wins when it appears first.\n *\n * Unknown and malformed rows are inert. Model prose is read only from\n * `item.completed` and is never searched for error words, so an answer that\n * discusses a usage limit cannot withdraw working capacity.\n */\nexport function parseCodexOutput(\n output: string,\n /**\n * The clock, injectable — and not for convenience.\n *\n * The quota corpus drops a \"try again at\" that has already passed, because\n * a stale clock brings somebody back to a machine that is still blocked.\n * Read from `Date.now()` inside, the one observed transcript we hold stops\n * exercising that path the day after it was captured: the test would go\n * green having proved nothing, on a schedule.\n */\n now: number = Date.now(),\n /** The observations to classify against — see `quota.ts` on why. */\n corpus?: readonly Observation[],\n): CodexOutput {\n const messages: string[] = [];\n\n for (const line of output.split(/\\r?\\n/u)) {\n if (line.trim() === \"\") continue;\n\n let event: Record<string, unknown> | undefined;\n try {\n event = record(JSON.parse(line) as unknown);\n } catch {\n continue;\n }\n if (event === undefined) continue;\n\n const type = event[\"type\"];\n if (type === \"item.completed\") {\n const item = record(event[\"item\"]);\n if (item?.[\"type\"] === \"agent_message\") {\n const text = item[\"text\"] ?? item[\"message\"];\n if (typeof text === \"string\" && text !== \"\") messages.push(text);\n }\n continue;\n }\n\n if (type === \"error\" || type === \"turn.failed\") {\n const detail = failureMessage(event);\n const classified = classifyFailure(detail, now, corpus);\n return {\n ok: false,\n ...classified,\n message: `the codex CLI failed: ${detail}`,\n };\n }\n\n if (type === \"turn.completed\") {\n return { ok: true, text: messages.join(\"\\n\") };\n }\n }\n\n return {\n ok: false,\n code: \"backend-error\",\n message: \"the codex CLI ended without a terminal event\",\n };\n}\n\n/**\n * The process-class backend for OpenAI's Codex CLI, on the owner's ChatGPT\n * plan.\n *\n * Subscription-class, so its offer scope is locked to its owner\n * ({@link MUSTS.SUBSCRIPTION_SELF_LOCK}) — one account runs one person's work.\n * That lock is doing more here than it does for `claude-cli`: it is the floor\n * under the tool disables above, so that even a future release which renames a\n * flag out from under us cannot expose the owner's machine to *other people's*\n * prompts. It does not bound the sites the owner has consented to, which is why\n * the disables are verified rather than trusted.\n */\n/**\n * The remedy, from the one place that decides what we would run.\n *\n * A function rather than an inline expression so the `undefined` case is\n * handled once and loudly: `loginCommandFor` returns `undefined` for a backend\n * with nothing to spawn, and codex is not one — a silent `??` fallback here\n * would be a second hard-coded copy of the argv, which is the thing being\n * removed.\n */\nfunction signInRemedy(): string {\n const command = loginCommandFor(\"codex-cli\");\n if (command === undefined) {\n throw new Error(\"codex-cli has no login command, which cannot happen\");\n }\n return `run \\`${command.argv.join(\" \")}\\``;\n}\n\nexport class CodexCliBackend implements Backend {\n /**\n * Nothing to read — checked by running it (byollm_021).\n *\n * A complete `codex exec --json` stream on this machine is four events:\n * `thread.started`, `turn.started`, `item.completed` carrying the text, and\n * `turn.completed` carrying token usage. **`turn.completed` has counts and\n * no stop reason**, which is the near-miss worth writing down: it is the\n * event that would carry one, and it does not.\n */\n readonly stopReasons: StopReasonMapping = {\n kind: \"unavailable\",\n why: \"`codex exec --json` ends with turn.completed, which carries token usage and no stop reason\",\n };\n\n readonly id: BackendId = \"codex-cli\";\n readonly class: BackendClass = \"process\";\n /**\n * The remedy an owner is shown, derived from the command we would run —\n * B148.\n *\n * This said `run \\`codex login\\``, which is the flow that **hangs** on any\n * machine without a browser and a loopback listener: a hosted box, a\n * server, a container, an SSH session. Two places describing one sign-in,\n * and only one of them was corrected when the flow changed — so this asks\n * the one that decides rather than restating it.\n *\n * `claude-cli`'s remedy is deliberately NOT derived the same way: it says\n * *\"run `claude` in a terminal\"* under B047's print-not-spawn ruling, which\n * is a different instruction for a different reason, and it is not broken.\n */\n readonly signIn = signInRemedy();\n readonly #binary: string;\n\n /**\n * @param binary - which executable to run. Defaults to `codex` and is **not\n * reachable from configuration**, exactly as for `claude-cli`: it exists so\n * the adversarial suite can substitute a probe that reports the argv,\n * environment, cwd and stdin it actually received.\n */\n constructor(binary = \"codex\") {\n this.#binary = binary;\n }\n\n async health(): Promise<BackendHealth> {\n const version = await new Promise<string | null>((resolve) => {\n // execFile, never exec: no shell is involved even for our own fixed\n // arguments.\n const launch = resolveCliLaunch(this.#binary, \"@openai/codex\");\n execFile(\n launch.command,\n [...launch.prefixArgs, \"--version\"],\n { timeout: 10_000, env: childEnv() },\n (error, stdout) => {\n resolve(error ? null : stdout.trim());\n },\n );\n });\n\n if (version === null) {\n return {\n healthy: false,\n models: [],\n detail:\n \"the `codex` CLI is not installed or not on PATH \" +\n \"(npm i -g @openai/codex)\",\n };\n }\n // `--version` succeeds whether or not anybody has signed in, so this\n // reports installed rather than ready, and the distinction is deliberate.\n // An unauthenticated CLI exits non-zero with an empty stdout on the first\n // real call, which surfaces as `backend-error` on that job — visible, and\n // attributable. Probing auth here would mean a network round trip on every\n // heartbeat to answer a question the first job answers for free.\n return { healthy: true, models: [] };\n }\n\n /**\n * One real, tiny call — the half of the auth gate codex never had.\n *\n * `health()` runs `--version`, which answers \"is the binary here\" and was\n * never the question. Without a `canary` the detector returns\n * `answers: undefined` — *not asked* — and that is deliberately not `false`\n * everywhere it is read, so a signed-out codex sailed through `byollm\n * setup`, through `connect`, and through `byollm model`, and every job it\n * was given failed.\n *\n * The gate that stops a logged-out Claude has named codex in its copy since\n * the day it shipped. This is the method that makes the sentence true.\n *\n * Identical in shape to the claude backend's, and deliberately so: one\n * definition of \"can it answer\", which is a real call and not a version\n * string. It costs a handful of tokens against the owner's own\n * subscription, at setup and at daemon start rather than per heartbeat.\n */\n async canary(model: string): Promise<BackendHealth> {\n const result = await this.execute({\n model,\n prompt: \"Reply with the single word: ok\",\n timeoutMs: 30_000,\n /*\n * Enough for the answer *and the envelope it arrives in*, and small\n * enough that a chatty model cannot make this expensive.\n *\n * This number is coupled to `FIXED_ARGV`, which is why it is not 256\n * any more. Under `--json` the CLI spends about 340 bytes saying \"ok\":\n * a thread id, two turn rows and a usage record wrap a two-character\n * answer. A budget overrun is not a truncated answer here — the child\n * is killed and the call returns `output-too-large`, which this canary\n * would read as \"cannot answer\" and report against a machine that\n * answers fine. Anything that changes the output format has to revisit\n * this line.\n */\n maxOutputBytes: 8 * 1024,\n signal: new AbortController().signal,\n });\n return result.ok\n ? { healthy: true, models: [] }\n : { healthy: false, models: [], detail: result.message };\n }\n\n async execute(request: BackendRequest): Promise<BackendResult> {\n const started = Date.now();\n const result = await runProcessJob({\n launch: resolveCliLaunch(this.#binary, \"@openai/codex\"),\n argv: codexArgv(request.model),\n env: childEnv(),\n displayName: \"the codex CLI\",\n request,\n started,\n });\n if (!result.ok) return result;\n\n const outcome = parseCodexOutput(result.text);\n return { ...outcome, durationMs: result.durationMs };\n }\n}\n","import type { BackendClass, BackendId } from \"@byollm/protocol\";\nimport { checkBaseUrl } from \"../ssrf.js\";\nimport type {\n StopReason,\n StopReasonMapping,\n Backend,\n BackendHealth,\n BackendInit,\n BackendRequest,\n BackendResult,\n} from \"./types.js\";\n\n/**\n * The HTTP-class backend: any server speaking OpenAI-compatible\n * `/v1/chat/completions`.\n *\n * One implementation covers Ollama, `mlx_lm.server`, llama.cpp server and\n * vLLM (byollm_001 Rev 1 §A) — the collapse that puts MLX inference in v1.\n *\n * **Why this is the safest class.** It spawns nothing, so byollm_004 §2's\n * argv, stdin, environment and sandbox requirements do not apply *by\n * construction* rather than by discipline. The prompt travels as a JSON\n * string in a request body; there is no command line for it to escape into\n * because there is no command line.\n *\n * What remains is the destination, and that is nailed down: the base URL\n * comes from owner config, is validated once at load and again here, and\n * redirects are refused so a permitted URL cannot become a forbidden one in\n * flight ({@link MUSTS.HTTP_BASE_URL_SAFE}).\n */\n/**\n * OpenAI's `finish_reason`, mapped — byollm_021.\n *\n * **Verified by running it**, per the `login.ts` precedent, against\n * ollama 0.x on this machine, both directions from the same model:\n * · `max_tokens: 5` -> `finish_reason: \"length\"`, content empty\n * · room to finish -> `finish_reason: \"stop\"`, content complete\n *\n * `\"stop\"` maps to `\"end\"` and NOT to `\"stop-sequence\"`, which is the one\n * judgement here. OpenAI reports `stop` for a model finishing on its own AND\n * for a configured stop token being hit; the field cannot tell them apart, so\n * claiming `\"stop-sequence\"` would be inventing a distinction the wire does\n * not carry. `\"end\"` is what we can honestly say.\n *\n * `tool_calls` and `content_filter` are OpenAI's and unmapped here on\n * purpose: this daemon sends no tools, and a filtered response is a refusal\n * rather than a stop reason. Anything unlisted resolves to `\"unknown\"`, which\n * is the honest answer rather than the flattering one.\n */\nconst FINISH_REASONS = Object.freeze({\n stop: \"end\",\n length: \"length\",\n}) as Readonly<Record<string, StopReason>>;\n\nexport class OpenAiHttpBackend implements Backend {\n readonly stopReasons: StopReasonMapping = {\n kind: \"declared\",\n from: \"choices[0].finish_reason\",\n map: FINISH_REASONS,\n };\n\n readonly id: BackendId = \"openai-http\";\n readonly class: BackendClass = \"http\";\n readonly #baseUrl: URL;\n readonly #apiKeyEnv: string | undefined;\n\n constructor(init: BackendInit) {\n if (init.baseUrl === undefined) {\n throw new Error(\"openai-http backend requires a baseUrl\");\n }\n const check = checkBaseUrl(init.baseUrl);\n if (!check.ok) {\n throw new Error(`refusing base URL: ${check.detail}`);\n }\n this.#baseUrl = check.url;\n this.#apiKeyEnv = init.apiKeyEnv;\n }\n\n /**\n * Build a URL under the configured base.\n *\n * The path is a hardcoded literal from this file — never anything derived\n * from a job — and the result is re-checked against the base's origin so a\n * surprising `baseUrl` (say, one with a `..` path) cannot walk elsewhere.\n */\n #endpoint(path: \"chat/completions\" | \"models\"): URL {\n const base = this.#baseUrl.href.endsWith(\"/\")\n ? this.#baseUrl.href\n : `${this.#baseUrl.href}/`;\n const url = new URL(path, base);\n if (url.origin !== this.#baseUrl.origin) {\n throw new Error(\"computed endpoint left the configured origin\");\n }\n return url;\n }\n\n #headers(): Record<string, string> {\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n };\n if (this.#apiKeyEnv !== undefined) {\n const key = process.env[this.#apiKeyEnv];\n if (key !== undefined && key !== \"\") {\n headers[\"authorization\"] = `Bearer ${key}`;\n }\n }\n return headers;\n }\n\n async health(): Promise<BackendHealth> {\n try {\n const response = await fetch(this.#endpoint(\"models\"), {\n method: \"GET\",\n headers: this.#headers(),\n redirect: \"error\",\n signal: AbortSignal.timeout(5_000),\n });\n if (!response.ok) {\n return {\n healthy: false,\n models: [],\n detail: `model list returned HTTP ${String(response.status)}`,\n };\n }\n const body: unknown = await response.json();\n return { healthy: true, models: extractModelIds(body) };\n } catch (error) {\n return {\n healthy: false,\n models: [],\n detail: describeFetchError(error, this.#baseUrl.origin),\n };\n }\n }\n\n async execute(request: BackendRequest): Promise<BackendResult> {\n // A call with no time limit is a caller bug, and running it unbounded is a\n // worse answer than refusing it. Guarded here rather than trusted to the\n // type: the message this replaces read \"did not answer within undefinedms\",\n // which names a number nobody set — and it fired instantly, so the run\n // looked like a timeout that had not happened.\n if (!Number.isFinite(request.timeoutMs) || request.timeoutMs <= 0) {\n return {\n ok: false,\n code: \"backend-error\",\n message: \"no time limit was set for this call\",\n durationMs: 0,\n };\n }\n\n const started = Date.now();\n // One timeout governs the call whether it stalls before or during the\n // response body — a server that accepts and then dribbles forever must\n // not be able to wedge the machine.\n const timeout = AbortSignal.timeout(request.timeoutMs);\n const signal = AbortSignal.any([request.signal, timeout]);\n\n try {\n const response = await fetch(this.#endpoint(\"chat/completions\"), {\n method: \"POST\",\n headers: this.#headers(),\n // The payload is a JSON string field. Nothing about it is parsed as\n // configuration, and `model` comes from owner config.\n body: JSON.stringify({\n model: request.model,\n messages: [{ role: \"user\", content: request.prompt }],\n stream: false,\n }),\n redirect: \"error\",\n signal,\n });\n\n if (response.status === 401 || response.status === 403) {\n return this.#fail(\n \"unauthorized\",\n \"the model server rejected our credentials\",\n started,\n );\n }\n if (response.status === 404) {\n return this.#fail(\n \"model-not-found\",\n `the model server does not know \"${request.model}\"`,\n started,\n );\n }\n if (!response.ok) {\n return this.#fail(\n \"backend-error\",\n `the model server returned HTTP ${String(response.status)}`,\n started,\n );\n }\n\n const text = await readCapped(response, request.maxOutputBytes, signal);\n if (text === null) {\n // A hostile or broken local model producing unbounded output must not\n // be able to exhaust memory — byollm_004 §5's zip-bomb row.\n return this.#fail(\n \"output-too-large\",\n `the model produced more than ${String(request.maxOutputBytes)} bytes`,\n started,\n );\n }\n\n const answer = extractContent(text);\n if (answer === null) {\n return this.#fail(\n \"backend-error\",\n \"the model server's response was not in OpenAI chat-completion shape\",\n started,\n );\n }\n return {\n ok: true,\n text: answer.content,\n durationMs: Date.now() - started,\n stop: answer.stop,\n };\n } catch (error) {\n if (request.signal.aborted) {\n return this.#fail(\"canceled\", \"the job was canceled\", started);\n }\n if (isAbort(error)) {\n return this.#fail(\n \"timeout\",\n `the model did not answer within ${String(request.timeoutMs)}ms`,\n started,\n );\n }\n return this.#fail(\n \"backend-unreachable\",\n describeFetchError(error, this.#baseUrl.origin),\n started,\n );\n }\n }\n\n /*\n * No `retryable` argument any more — ruled 2026-09-04.\n *\n * This adapter used to decide it per failure, and it was the only one whose\n * rule was defensible: `true` for unreachable and for a 5xx. That is\n * exactly why removing it matters. The decision now lives once, in the\n * site-facing class table, and it was the *disagreement* between three\n * adapters that let a value only quota produced become readable as a fact\n * about somebody's account.\n */\n #fail(\n code: Exclude<BackendResult & { ok: false }, never>[\"code\"],\n message: string,\n started: number,\n ): BackendResult {\n return {\n ok: false,\n code,\n message,\n durationMs: Date.now() - started,\n };\n }\n}\n\n/**\n * Read a response body, refusing to buffer past `maxBytes`.\n *\n * `response.text()` would happily allocate whatever the server sends. Reading\n * the stream and stopping at the cap is what makes the ceiling real.\n *\n * @returns the text, or null if the cap was exceeded.\n */\nasync function readCapped(\n response: Response,\n maxBytes: number,\n signal: AbortSignal,\n): Promise<string | null> {\n const body = response.body;\n if (body === null) return \"\";\n\n // `getReader()` is typed as `any` chunks under this lib target; the stream\n // is bytes, and saying so is what lets the cap arithmetic below be checked.\n const reader = (body as ReadableStream<Uint8Array>).getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n try {\n for (;;) {\n signal.throwIfAborted();\n const { done, value } = await reader.read();\n if (done) break;\n total += value.byteLength;\n if (total > maxBytes) {\n await reader.cancel();\n return null;\n }\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n return new TextDecoder().decode(concat(chunks, total));\n}\n\nfunction concat(chunks: readonly Uint8Array[], total: number): Uint8Array {\n const out = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return out;\n}\n\n/** Pull the assistant text out of an OpenAI chat-completion response. */\nfunction extractContent(\n raw: string,\n): { content: string; stop: StopReason } | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n if (typeof parsed !== \"object\" || parsed === null) return null;\n const choices = (parsed as { choices?: unknown }).choices;\n if (!Array.isArray(choices) || choices.length === 0) return null;\n const message = (choices[0] as { message?: unknown }).message;\n if (typeof message !== \"object\" || message === null) return null;\n const content = (message as { content?: unknown }).content;\n if (typeof content !== \"string\") return null;\n const finish = (choices[0] as { finish_reason?: unknown }).finish_reason;\n return {\n content,\n /* Absent, or a value we have not mapped, is `unknown` — never `end`. An\n adapter that cannot tell must not be able to claim completion. */\n stop:\n typeof finish === \"string\"\n ? (FINISH_REASONS[finish] ?? \"unknown\")\n : \"unknown\",\n };\n}\n\n/** Model ids from an OpenAI `/v1/models` response. */\nfunction extractModelIds(body: unknown): string[] {\n if (typeof body !== \"object\" || body === null) return [];\n const data = (body as { data?: unknown }).data;\n if (!Array.isArray(data)) return [];\n return data\n .map((entry) =>\n typeof entry === \"object\" && entry !== null\n ? (entry as { id?: unknown }).id\n : undefined,\n )\n .filter((id): id is string => typeof id === \"string\");\n}\n\nfunction isAbort(error: unknown): boolean {\n return (\n error instanceof Error &&\n (error.name === \"AbortError\" || error.name === \"TimeoutError\")\n );\n}\n\n/**\n * A reachability failure the owner can act on.\n *\n * Deliberately names the origin and not the underlying error text, which for\n * `fetch` is often a bare \"fetch failed\" that tells nobody anything.\n */\nfunction describeFetchError(error: unknown, origin: string): string {\n const cause =\n error instanceof Error && \"cause\" in error && error.cause instanceof Error\n ? error.cause.message\n : error instanceof Error\n ? error.message\n : \"unknown error\";\n return `could not reach the model server at ${origin} (${cause})`;\n}\n","import {\n StopReasonSchema,\n type StopReason,\n type BackendClass,\n type BackendId,\n} from \"@byollm/protocol\";\n\n/**\n * The text of one model call, already composed by the daemon.\n *\n * Note what a backend receives: a string and a model name. It gets no access\n * to the job, the payload object, or anything that could carry routing. By\n * the time execution reaches here, the payload has been reduced to the only\n * thing byollm_004 §1 permits a job to cause — text sent to a model.\n */\nexport interface BackendRequest {\n /** The composed prompt text. */\n readonly prompt: string;\n /** The model, from owner config only ({@link MUSTS.NO_PAYLOAD_ROUTING}). */\n readonly model: string;\n /** Hard wall-clock ceiling. */\n readonly timeoutMs: number;\n /** Hard output ceiling; output past this truncates and fails the job. */\n readonly maxOutputBytes: number;\n /** Aborts the in-flight call — how cancel and revocation take effect. */\n readonly signal: AbortSignal;\n}\n\n/**\n * Why a model stopped generating — byollm_021, and it is a closed set for the\n * same reason {@link BackendErrorCode} is.\n *\n * That union's own note says different truths must never share a message: an\n * owner whose model server is down needs a different sentence from one whose\n * job hit its timeout. **Truncation is a different truth, and until now it\n * shared the success message.** A model that hits its own `max_tokens`\n * returned an unfinished answer reported as a finished one — Kevin found the\n * wall by trial and error, because trial and error was the only instrument\n * we gave anybody.\n *\n * Sharper: this codebase already has `output-too-large`, which is OUR\n * ceiling. We gave our own truncation a distinct code and gave the model's\n * none, and the guard we built is what taught us we were covered.\n */\n/**\n * An adapter's answer to \"how do you know why the model stopped\".\n *\n * Two shapes because there are two honest answers, and \"I have not checked\"\n * must not be able to masquerade as \"there is nothing to check\".\n */\nexport type StopReasonMapping =\n | {\n readonly kind: \"declared\";\n /** The vendor field this reads, named so a reviewer can go and look. */\n readonly from: string;\n /** That field's values, mapped onto ours. */\n readonly map: Readonly<Record<string, StopReason>>;\n }\n | {\n /**\n * Checked, and this adapter's output carries no such signal.\n *\n * Distinct from `unverified`, and the distinction is the same one this\n * codebase keeps arriving at: \"nobody looked\" and \"we looked and there\n * is nothing\" are different facts, and an owner surface that prints one\n * for the other is telling somebody to go and find something that does\n * not exist.\n */\n readonly kind: \"unavailable\";\n /** What was checked and what it did not carry. */\n readonly why: string;\n }\n | {\n readonly kind: \"unverified\";\n /** Why not, in words — this is said out loud on the owner surface. */\n readonly why: string;\n };\n\n/* Re-exported so adapter code keeps importing one module, while the\n definition itself lives where the wire vocabulary lives — B064 step 4. */\nexport { StopReasonSchema, type StopReason };\n\n/**\n * The stop reason a result actually carries, with absence resolved.\n *\n * Read through this rather than off the field, so an adapter that has not\n * been taught to report one cannot be mistaken for a model that ran to\n * completion.\n */\nexport function stopReasonOf(result: BackendResult): StopReason {\n return result.ok ? (result.stop ?? \"unknown\") : \"unknown\";\n}\n\n/**\n * Where a call's time went, as far as it can be seen from outside — B195.\n *\n * Todd, 09-14: *\"I want to know how much of that is waiting on claude and gpt\n * vs resource utilization.\"* `durationMs` is one number covering spawn, the\n * vendor wait and reading the answer, because `started` is taken on the first\n * line of `execute()` and everything happens inside `runProcessJob`.\n *\n * These are the two boundaries a parent process can actually observe:\n *\n * - **`spawnMs`** — `started` to the child's `spawn` event. **This is the\n * segment a starved box shows up in**: 50m of CPU and 269 MiB of 320 used\n * (B122) is where a fork gets slow, and nothing about it is the vendor's.\n * - **`firstOutputMs`** — `started` to the first byte on stdout or stderr.\n *\n * **What the middle segment is NOT, said plainly:** `firstOutputMs - spawnMs`\n * is *the child's own startup plus its first vendor response*, and those two\n * are not separable from out here — a CLI that prints a banner before it calls\n * anybody makes the number small for a reason that has nothing to do with\n * latency. **It is a bound, not an attribution**, and it must be read as one.\n * Separating them further needs the CLI to say so, which is a vendor's choice\n * rather than ours.\n *\n * Optional on both variants because a call that never spawned has neither, and\n * reporting a zero there would be a measurement nobody made.\n */\n/* Not exported: nothing outside this file names the type, and knip is right\n that an export nobody imports is a wider surface than the code needs. The\n shape travels on `BackendResult`, which is exported. */\ninterface BackendTiming {\n readonly spawnMs?: number;\n readonly firstOutputMs?: number;\n}\n\nexport type BackendResult =\n | {\n readonly ok: true;\n readonly text: string;\n readonly durationMs: number;\n readonly timing?: BackendTiming;\n /**\n * Why generation ended — byollm_021.\n *\n * Optional on the type and NOT optional in practice: every registered\n * adapter must declare a mapping, and the adversarial coverage check\n * enforces that, the same way it enforces a hostile-payload corpus.\n * Absent resolves to `\"unknown\"` through {@link stopReasonOf}, which is\n * the honest reading and not `\"end\"`.\n */\n readonly stop?: StopReason;\n }\n | {\n readonly ok: false;\n readonly code: BackendErrorCode;\n readonly message: string;\n readonly durationMs: number;\n readonly timing?: BackendTiming;\n /**\n * When the backend expects to be usable again — byollm_019 §3.2.\n *\n * Epoch ms, and only when the CLI actually said so. Carried on the\n * result rather than looked up later because the sentence that names\n * the time is the failure diagnostic itself, and it is gone by the time\n * anything else could ask.\n *\n * **A reason without a clock turns \"wait\" into \"give up.\"** Somebody\n * told their service is blocked, and not told for how long, cannot tell\n * an hour from a week and reaches for the remedy that always looks\n * available: turning it off.\n *\n * Owner-only, like every other diagnostic here. It never reaches a\n * site: a duration leaks which block was hit, and which block was hit\n * is a fact about how much somebody has been working today.\n */\n readonly until?: number | undefined;\n };\n\n/*\n * `retryable` used to live on this shape and no longer does — ruled\n * 2026-09-04.\n *\n * Every adapter computed one, and after the retry decision moved to the\n * site-facing class table nothing read any of them. They had also drifted:\n * the same quota block reported `true` from Codex and `false` from Claude,\n * and the HTTP backend used a third rule again. A field that is computed\n * three inconsistent ways and read nowhere is not harmless — it is a leak\n * waiting for its first consumer, and the leak it waits for is the one the\n * class table was flattened to close.\n *\n * The wire's `retryable` is a different field on a different shape, is read,\n * and stays.\n */\n\n/**\n * Why a backend call failed.\n *\n * Distinct codes because byollm_002 requires that different truths never\n * share a message: an owner whose model server is down needs a different\n * sentence from one whose job hit its timeout.\n */\nexport type BackendErrorCode =\n /**\n * This device does not have the memory to load a model right now — B080.\n *\n * Its own code because this union's own rule is that different truths must\n * never share a message. \"The server is down\" and \"the server is fine and\n * this machine has no room\" are different things to be told, and only one\n * of them is fixed by starting something.\n *\n * It reaches a site as `service_unavailable`, like every other fact about\n * somebody's machine — telling a site that this device is low on memory is\n * telling it about the device, which is what that mapping exists to stop.\n */\n | \"insufficient-memory\"\n | \"backend-unreachable\"\n | \"backend-error\"\n | \"model-not-found\"\n | \"quota-exhausted\"\n | \"timeout\"\n | \"output-too-large\"\n | \"canceled\"\n | \"unauthorized\";\n\n/** Whether a backend is usable right now, and with which models. */\nexport interface BackendHealth {\n readonly healthy: boolean;\n /** Models the backend reports; empty when it could not be reached. */\n readonly models: readonly string[];\n /** Why it is unhealthy — shown verbatim in `byollm status`. */\n readonly detail?: string;\n}\n\n/**\n * A way of reaching a model.\n *\n * Implementations are registered in {@link BACKENDS} and must ship\n * adversarial-suite rows before they can be added — the coverage check in the\n * adversarial suite enforces that, so a new backend cannot arrive without its\n * hostile-payload corpus.\n */\nexport interface Backend {\n readonly id: BackendId;\n readonly class: BackendClass;\n\n /**\n * Can this backend serve work right now, and with what?\n *\n * The capability matrix is config ∩ *this*\n * ({@link MUSTS.CAPABILITY_IS_DETECTED}) — a configured but unreachable\n * backend must never be advertised.\n */\n health(): Promise<BackendHealth>;\n\n /**\n * Can it actually *do* the work — credentials and all?\n *\n * Optional, and implemented only where {@link Backend.health} cannot answer\n * the question. A subscription CLI passes `--version` without credentials,\n * so its health check reports healthy while every job fails \"not signed in\".\n * That gap cost a live cross-user test on 2026-08-25.\n *\n * A canary spends a real call, so **it never runs on the polling loop.**\n * Daemon start and enablement only: bounded, human-adjacent, cents on a\n * subscription. The runner enforces where it is called from; a backend just\n * answers honestly when asked.\n */\n canary?(model: string): Promise<BackendHealth>;\n\n /**\n * How somebody signs this backend in, in their own terminal.\n *\n * The remedy belongs to the backend because only the backend knows it:\n * `claude` wants the bare command and a browser, `codex` wants\n * `codex login`. A template that guessed would be wrong for one of them and\n * would go on being wrong as they change.\n *\n * Absent when the idea does not apply — an HTTP model server has a URL and a\n * key in the owner's config, not a sign-in, so it gets no sentence rather\n * than a sentence naming a command that does not exist.\n */\n readonly signIn?: string;\n\n /**\n * How this adapter reads its own vendor's stop signal — byollm_021.\n *\n * **Required, and that is Todd's ruling made structural**: \"each service\n * adapter should state its truncated message output along with other\n * errors.\" A required field means an adapter cannot be added without\n * answering the question, the same way `BACKENDS` cannot be extended\n * without an adversarial corpus. A rule you have to remember holds until\n * somebody adds the next one.\n *\n * `unverified` is a legitimate answer and an honest one. The mappings here\n * are checked by running the thing, per the `login.ts` and\n * `startCommandFor` precedent — a guessed field name produces a gate that\n * silently never fires, which is this bug with extra steps.\n */\n readonly stopReasons: StopReasonMapping;\n\n /** Run one model call. The only thing a job is permitted to cause. */\n execute(request: BackendRequest): Promise<BackendResult>;\n}\n\n/** Everything a backend instance needs from the owner's config. */\nexport interface BackendInit {\n /** HTTP-class only. Already validated by {@link checkBaseUrl}. */\n readonly baseUrl?: string | undefined;\n /** Name of the env var holding an API key, if the server needs one. */\n readonly apiKeyEnv?: string | undefined;\n}\n","import { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\n/**\n * How the daemon's conversation with its upstream is going — written by the\n * daemon, read by `byollm status`.\n *\n * The failure that produced this file: a device's every heartbeat was refused\n * for hours, once every ten seconds, and `byollm status` said `state:\n * running` throughout. That was true. The daemon was running. It was also\n * reporting nothing, invisible to the hub, and showing a card full of frozen\n * data — and the only surface that knew was a log line nobody tails.\n *\n * **A persistent rejection is a state, not a louder log.** One refusal is\n * noise: a rolling deploy, a dropped connection, a moment of unluck. Forty in\n * a row is a device that has stopped participating and does not know it, and\n * the difference between those is a count, which means somebody has to keep\n * one.\n */\nexport interface DaemonHealth {\n /** When this was written, epoch ms. */\n readonly at: number;\n /** Consecutive failed exchanges with the upstream. Zero after any success. */\n readonly consecutiveFailures: number;\n /** What the upstream last said, verbatim. */\n readonly lastError?: string;\n /** The origin it was talking to. */\n readonly origin?: string;\n /**\n * This device was revoked, and when — ruled 2026-09-03.\n *\n * `marks-never-destroys` exists so the surfaces can read the mark, and it\n * had nowhere to live: the daemon stopped serving in memory and every\n * process a person actually runs — `status`, `install`, the next `run` — is\n * a different process that could not see it. Todd revoked a machine, and\n * three surfaces told him three things, none of them \"revoked\".\n *\n * **A mark nobody renders is a destroy with extra steps.**\n *\n * Here rather than beside the pairing, because it is a fact about this\n * device's conversation with an upstream, which is exactly what this file\n * is. It survives a pairings file that is empty for any reason, which\n * matters: the exit path has to tell empty-because-revoked from\n * empty-because-never-paired, and those are different sentences with\n * different remedies.\n *\n * Cleared by a successful pairing, because re-pairing is the remedy. A mark\n * that outlived its own fix would be a device told it is revoked while it\n * serves work.\n */\n readonly revoked?: {\n readonly at: number;\n readonly origin: string;\n };\n}\n\n/**\n * Enough failures in a row to mean something.\n *\n * At a ten-second beat this is about a minute — long enough that a rolling\n * deploy or a flaky minute has passed, short enough that somebody typing\n * `byollm status` because \"it isn't working\" gets told why on the first try.\n */\nexport const FAILURES_BEFORE_ALARM = 6;\n\nexport async function writeHealth(\n path: string,\n health: DaemonHealth,\n): Promise<void> {\n try {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(health)}\\n`, \"utf8\");\n } catch {\n // A daemon that cannot write its health file still has work to do. This\n // is a diagnostic, and a diagnostic that can stop the thing it describes\n // is worse than no diagnostic.\n }\n}\n\n/**\n * What the daemon last recorded, or `undefined` if it never has.\n *\n * Undefined is not \"healthy\" — it is \"this daemon has not said\", which is the\n * state of a machine whose daemon predates this file or has never started.\n * Callers must not collapse the two.\n */\nexport async function readHealth(\n path: string,\n): Promise<DaemonHealth | undefined> {\n try {\n const parsed: unknown = JSON.parse(await readFile(path, \"utf8\"));\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n typeof (parsed as { consecutiveFailures?: unknown })\n .consecutiveFailures !== \"number\"\n ) {\n return undefined;\n }\n return parsed as DaemonHealth;\n } catch {\n return undefined;\n }\n}\n","import { randomBytes } from \"node:crypto\";\nimport { readFile, rename, writeFile, mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\n/**\n * Is this daemon alive right now — B202.\n *\n * **Separate from `health.json`, and the split is the point.** Health is\n * persist-on-CHANGE: failures, sign-outs, quota blocks, revocation — the\n * things an owner must still be able to read hours later, and which a healthy\n * daemon must not rewrite every ten seconds to say nothing changed. Its own\n * comment says so at `runner.ts:2368`.\n *\n * That made it useless for liveness, and B201's first attempt read it as a\n * per-beat stamp anyway. Two ways wrong at once: a device whose file was\n * absent stayed \"running\", and a daemon that failed once and recovered froze\n * `at` at the recovery and read \"NOT RUNNING\" a minute later while serving\n * perfectly.\n *\n * So liveness gets its own file with its own cadence. **Written every beat,\n * overwriting a single small record** — never appended, so it cannot grow and\n * needs no rotation, which is what makes a per-beat write affordable at all.\n *\n * Not a box thing: `byollm run` on a laptop has exactly the same gap, and a\n * fix that lived in the box image would be a fork of the daemon.\n */\nexport interface DaemonHeartbeat {\n /** When this beat was written, epoch ms. */\n readonly at: number;\n /** The process that wrote it, so a reader can check it is still there. */\n readonly pid: number;\n}\n\n/**\n * Write the beat, atomically.\n *\n * Temp-and-rename because a reader can arrive mid-write: `rename` within a\n * directory is atomic, so `status` sees either the previous beat or this one\n * and never a half-written line it would report as corrupt.\n *\n * **The temp name is unique per write, and a test found out why.** With a\n * fixed `.writing` suffix two overlapping writes share a file: the second\n * truncates what the first is renaming, one `rename` fails, the error is\n * swallowed by design, and the beat silently stays at the older value. Beats\n * are ten seconds apart so production would not have met it for a long time —\n * which is exactly the kind of latency a race takes before it appears.\n *\n * Silent on failure, like `writeHealth`: a daemon that cannot write its\n * heartbeat still has work to do, and a diagnostic that can stop the thing it\n * describes is worse than no diagnostic.\n */\nexport async function writeHeartbeat(\n path: string,\n beat: DaemonHeartbeat,\n): Promise<void> {\n try {\n await mkdir(dirname(path), { recursive: true });\n const temp = `${path}.${randomBytes(6).toString(\"hex\")}`;\n await writeFile(temp, `${JSON.stringify(beat)}\\n`, \"utf8\");\n await rename(temp, path);\n } catch {\n // See above: this is a diagnostic, not the work.\n }\n}\n\n/** The last beat, or nothing — absent, unreadable and malformed are one answer. */\nexport async function readHeartbeat(\n path: string,\n): Promise<DaemonHeartbeat | undefined> {\n try {\n const parsed: unknown = JSON.parse(await readFile(path, \"utf8\"));\n if (\n typeof parsed === \"object\" &&\n parsed !== null &&\n typeof (parsed as { at?: unknown }).at === \"number\" &&\n typeof (parsed as { pid?: unknown }).pid === \"number\"\n ) {\n return parsed as DaemonHeartbeat;\n }\n return undefined;\n } catch {\n /* Absent is the common case and not an error: a daemon that has never run\n on this machine has never written one. The caller decides what that\n means, because \"never started\" and \"stopped\" are different facts and\n only the caller knows whether this device is meant to be serving. */\n return undefined;\n }\n}\n\n/**\n * Is the process that wrote this beat definitely gone — B228.\n *\n * `pid` has been on this record since B202 with a comment saying what it is\n * for — *\"so a reader can check it is still there\"* — and for three releases\n * nothing checked it. `status` read the beat's AGE and nothing else, which\n * leaves a window: a daemon killed five seconds ago has a fresh file, so the\n * age arm says nothing, `consecutiveFailures` is zero because it was working\n * when it died, `supervision.state` is `absent` for a daemon run in a\n * terminal, and the headline falls through to **`running` for a process that\n * does not exist.** That is a full minute of lying, and it is precisely the\n * minute in which somebody who just typed `byollm stop` types `byollm status`.\n *\n * ## It may demote, and it may never promote\n *\n * The asymmetry is the whole design, and getting it backwards is the classic\n * pidfile bug.\n *\n * **`ESRCH` is proof of death.** No process holds that id, so the writer is\n * gone, and nothing else has to be true for that to hold.\n *\n * **Success is not proof of life.** A pid is reused: the daemon's id can be\n * handed to something unrelated between its death and this read, and\n * `kill(pid, 0)` would then succeed about a process that has nothing to do\n * with byollm. So a successful signal returns `false` — *\"not proven gone\"* —\n * and the age rule still decides. This function can only ever move the answer\n * toward NOT RUNNING, never toward running, which means pid reuse costs a\n * missed demotion rather than a false reassurance.\n *\n * **`EPERM` is not death either.** Something exists under that id and belongs\n * to another user — evidence the id has been reused, but not evidence about\n * our daemon. Treated as \"not proven gone\" for the same reason.\n *\n * ## Where it is wrong, said out loud\n *\n * A pid only means something inside the namespace that issued it. If `status`\n * ever ran outside the daemon's namespace — a different container to the one\n * the daemon runs in — every pid would read `ESRCH` and this would demote a\n * healthy device. That is not today's shape (on a box the supervisor and the\n * daemon share a container, and on a laptop there is one namespace), and the\n * caller prints its evidence so a reader meeting that case can see the\n * reasoning rather than just the verdict.\n */\nexport function beatWriterIsGone(\n beat: DaemonHeartbeat,\n /**\n * The liveness probe, injected — B228, and the seam `tellSupervisor` already\n * uses two files over.\n *\n * What this function decides is a mapping from **errno to verdict**:\n * `ESRCH` is death, `EPERM` is a process that exists and is not ours. The\n * process table is merely the cheapest way to produce those errnos, and it\n * is a fixture that differs by platform — the test borrowed **pid 1**, which\n * exists and is unsignalable on POSIX and does not exist on Windows, so\n * `windows-latest` read the verdict as \"gone\" and the case failed for a\n * reason that had nothing to do with the mapping.\n *\n * Handing the probe in takes the OS out of the assertion. A branch that can\n * only be exercised by the platform it describes is a branch nobody checks.\n *\n * Wrapped rather than passed as `process.kill` directly: detaching a method\n * from its object is the shape lint objects to, and a caller wants its own\n * anyway.\n */\n kill: (pid: number, signal: number) => void = (pid, signal) => {\n process.kill(pid, signal);\n },\n): boolean {\n /* `process.kill(0, ...)` signals the whole process group and a negative pid\n signals a group too. Neither is a question about this daemon, and one of\n them is dangerous even with signal 0, so a pid that is not a plain process\n id is simply not evidence. */\n if (!Number.isInteger(beat.pid) || beat.pid <= 0) return false;\n try {\n kill(beat.pid, 0);\n return false;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === \"ESRCH\";\n }\n}\n","import { createInterface } from \"node:readline\";\nimport type { LoginCommand } from \"./login.js\";\nimport {\n detectInstalled,\n manageServices,\n readExistingConfig,\n summarise,\n writeManaged,\n type Detected,\n type Detector,\n type ManageIo,\n type Probe,\n type Verifier,\n} from \"./services-manage.js\";\nimport type { DaemonPaths } from \"./paths.js\";\n\n/**\n * `byollm setup` — byollm_015 Phase 1.\n *\n * The product's answer to \"edit this JSON file\". Everything else built this\n * month assumes somebody can hand-author `~/.byollm/config.json`, and normal\n * users cannot; this is the conversation that writes it for them.\n *\n * **Config is the only output.** No wizard-only state, no second config\n * surface, nothing a hand could not have written. Somebody who never runs this\n * loses convenience and nothing else, which is what keeps the file the single\n * source of truth rather than a cache of the wizard's opinions.\n */\n\n/**\n * How the wizard talks — one definition, and it lives with the screen.\n *\n * `ManageIo` is the same interface under the name of the module that now owns\n * the conversation. Aliased rather than re-declared so `terminalIo` below, the\n * CLI, and every test keep the word they already use while there is exactly\n * one shape.\n */\nexport type SetupIo = ManageIo;\n\nexport type { Detector, Probe, Detected };\nexport { detectInstalled };\n\nconst yes = (answer: string, fallback: boolean): boolean => {\n const text = answer.trim().toLowerCase();\n if (text === \"\") return fallback;\n return /^y(es)?$/.test(text);\n};\n\nexport interface SetupResult {\n readonly wrote: boolean;\n readonly services: readonly string[];\n /** Whether pairing ran and succeeded. Absent when setup stopped earlier. */\n readonly connected?: boolean;\n /** Whether the background service was installed. */\n readonly running?: boolean;\n}\n\n/**\n * The defaults live where the work does — one place, not two.\n *\n * These were `= detectInstalled`, `= detect`, `= runLogin(...)` here AND in\n * the screen this now calls. Two defaults for one question is the divergence\n * instruction 9 names, so setup forwards what it was given and\n * `manageServices` decides what `undefined` means. A caller that passes\n * nothing gets the real machine either way; a test that passes a stub is the\n * only thing that changes anything.\n */\nexport async function runSetup(\n paths: DaemonPaths,\n io: SetupIo,\n detector?: Detector,\n probe?: Probe,\n /**\n * Asks whether a found CLI can actually answer — \"found\" is not \"works\".\n *\n * Fifth, so that adding it did not renumber the parameters every existing\n * caller passes positionally. Which it did, briefly, and the compiler said\n * so in four places before anything ran.\n */\n verifier?: Verifier,\n /**\n * Running a vendor CLI's sign-in, with this terminal — 2026-09-02.\n *\n * Injected for the same reason `verifier` is, and more urgently: the real\n * one hands the TTY to another program. A test that reached the default\n * would sit waiting for somebody to complete an OAuth flow.\n */\n login?: (command: LoginCommand) => Promise<boolean>,\n /**\n * The wizard's own hands: `connect` and `install`, run as this process.\n *\n * Injected rather than imported so a test can watch what setup decided to\n * do without pairing against a real hub or writing a launch agent. The\n * default is supplied by the CLI, which owns those verbs — passing them in\n * keeps this module from importing the command table that imports it.\n */\n run: (argv: readonly string[]) => Promise<number> = () => Promise.resolve(0),\n /**\n * The environment the supervisor is read from — B241.\n *\n * Injected for the same reason every other dependency here is: a test that\n * had to set `process.env` to say \"there is a supervisor\" was mutating a\n * global two other test files read, and paired runs failed 8% of the time\n * because of it. The default is the real environment, so nothing about a\n * person running `byollm setup` changes.\n */\n env: NodeJS.ProcessEnv = process.env,\n /**\n * Which machine this is — last, for the same reason `verifier` was last.\n *\n * Injected rather than read from `process` at the point of use so the\n * Windows path is testable on the machines we actually have. The bug it\n * exists for was found on a box none of us own.\n */\n platform?: NodeJS.Platform,\n): Promise<SetupResult> {\n if (!io.interactive) {\n io.err(\n \"byollm setup needs a terminal it can ask questions in.\\n\" +\n \"Write ~/.byollm/config.json by hand instead: \" +\n \"https://docs.byollm.cloud/guides/models\\n\",\n );\n return { wrote: false, services: [] };\n }\n\n // An existing config is the owner's work and is never edited from under\n // them. Offering to start over is a different thing from doing it.\n const existing = await readExistingConfig(paths.config);\n if (existing !== undefined) {\n const count = Object.keys(existing.services).length;\n /**\n * A config with nothing in it is not work to protect — it is a dead end.\n *\n * The rule above is right: an existing config is the owner's and is never\n * edited from under them. But a file with zero services was written by a\n * version that wrote one before it knew how to find anything, and it made\n * this command unusable — \"It has 0 service(s). Setup will not change it\"\n * and then nothing, on a machine where setup is exactly what was needed.\n * Kevin's Windows box, and anybody who installed before alpha.44.\n *\n * So the rule keeps its teeth and gains a door: nothing is overwritten\n * without a yes, and the yes is one line rather than a wizard somebody has\n * to abandon and rerun with a flag they have to find out about.\n */\n if (count > 0) {\n io.out(\n `You already have a config at ${paths.config}.\\n` +\n `It has ${String(count)} service(s). Setup will not change it.\\n` +\n \"Run `byollm services` to see what it does, or\\n\" +\n \"`byollm services manage` to change it.\\n\",\n );\n return { wrote: false, services: [] };\n }\n io.out(\n `\\nYour config at ${paths.config} has no services in it, so nothing\\n` +\n \"can run yet. That is how versions before alpha.44 left it.\\n\",\n );\n const go = await io.ask(\" Set it up now? [Y/n] \");\n if (!yes(go, true)) {\n io.out(\" Left alone. Nothing was changed.\\n\");\n return { wrote: false, services: [] };\n }\n }\n\n io.out(`\\nSetting up byollm. Change any of it later in ${paths.config}.\\n\\n`);\n\n // ── 1. what this device is called ────────────────────────────────────\n const suggested = defaultDeviceName();\n const nameAnswer = await io.ask(\n `What should this device be called? [${suggested}] `,\n );\n const deviceName = nameAnswer.trim() === \"\" ? suggested : nameAnswer.trim();\n\n // ── 2. what this machine may run ─────────────────────────────────────\n //\n // **One implementation, and this is the caller** — Todd, 09-10: *\"We should\n // remove the old setup and replace with this one. I shouldn't have even\n // suggested having two.\"* What stood here was a subscription loop, a local-\n // server picker and a defaults question, all of which `services manage`\n // now does — and doing them twice would have been instruction 9 written\n // into the plan on purpose rather than discovered by accident later.\n //\n // `existing` is empty by construction: a config with any services returned\n // a few lines up, so the only file that reaches here has none. Passing\n // `{}` is therefore a statement of that fact rather than a shortcut, and\n // `services manage` is where somebody re-runs against a config that has\n // something in it.\n const outcome = await manageServices({\n io,\n existing: {},\n ...(detector === undefined ? {} : { detector }),\n ...(verifier === undefined ? {} : { verifier }),\n ...(probe === undefined ? {} : { probe }),\n ...(login === undefined ? {} : { login }),\n ...(platform === undefined ? {} : { platform }),\n });\n if (!outcome.decided) return { wrote: false, services: [] };\n const enabled = [...outcome.enabled];\n\n if (\n !(await writeManaged(paths.config, io, existing?.rest ?? {}, outcome, env))\n ) {\n return { wrote: false, services: [] };\n }\n\n io.out(`\\nWrote ${paths.config}\\n${summarise(outcome).join(\"\\n\")}\\n`);\n\n /**\n * The wizard finishes the job — ruled 2026-09-01, after two onboardings.\n *\n * It ended with \"Next: byollm connect --name …\", which is a correct\n * sentence and four verbs short of a working device. Both walks stopped\n * there: one ran `connect` in a window they later closed, one never ran it\n * at all. The gap is not knowledge — the line was on screen — it is that a\n * wizard which stops one step from done reads as done.\n *\n * Two questions, both defaulting yes, both composing verbs that already\n * exist. Nothing new is invented here; what changes is that the person is\n * asked rather than instructed.\n *\n * The ending is still the ruled one: the true sentence, or the single\n * command that finishes whatever was skipped. A `no` is a decision and gets\n * the command, not a warning.\n */\n const doConnect = yes(\n await io.ask(\"\\n Connect to byollm.cloud? [Y/n] \"),\n true,\n );\n if (!doConnect) {\n io.out(\n `\\n Not connected. This device is set up and unreachable — finish with:\\n` +\n ` byollm connect --name ${JSON.stringify(deviceName)}\\n`,\n );\n return { wrote: true, services: enabled, connected: false, running: false };\n }\n\n const connected = await run([\"connect\", \"--name\", deviceName]);\n if (connected !== 0) {\n // Said plainly and not retried. Pairing can fail for reasons this wizard\n // cannot fix — no network, a hub that is draining, a code that expired\n // while somebody found their phone — and `connect` has already printed\n // which. Re-running it is one line and is safe.\n io.out(\n `\\n Pairing did not finish. Nothing else was changed — try again with:\\n` +\n ` byollm connect --name ${JSON.stringify(deviceName)}\\n`,\n );\n return { wrote: true, services: enabled, connected: false, running: false };\n }\n\n /* Names what it does rather than where it goes — byollm_020. \"Run in\n background\" left \"and after I reboot?\" unanswered, which is the whole\n question the command exists to settle. */\n const doInstall = yes(\n await io.ask(\n \"\\n Start byollm now and keep it running across restarts? [Y/n] \",\n ),\n true,\n );\n if (!doInstall) {\n io.out(\n `\\n Paired, and not running. Start it when you want it:\\n` +\n ` byollm start keep it running, across restarts\\n` +\n ` byollm run run in this terminal and watch it\\n`,\n );\n return { wrote: true, services: enabled, connected: true, running: false };\n }\n\n const running = await run([\"start\"]);\n if (running !== 0) {\n io.out(\n `\\n Paired, and could not start the background service. Either:\\n` +\n ` byollm start try again — it says why when it cannot\\n` +\n ` byollm run run in this terminal instead\\n`,\n );\n return { wrote: true, services: enabled, connected: true, running: false };\n }\n\n /**\n * One printer — ruled 2026-09-03.\n *\n * `TEST YOUR DEVICE` appeared twice in a single setup: `install` printed it\n * on its own success, and this line printed it again. Two tellings of one\n * fact, three lines apart.\n *\n * `install` keeps it, and that is not a coin toss. Since the same ruling,\n * install is the step that *probes* — it waits for the daemon to be running\n * before it claims anything — so it is the only place that knows the\n * sentence is true. This line knows only that install returned zero, which\n * is precisely the weaker fact that caused the original bug.\n */\n io.out(\n `\\n Done. This device is set up, paired, and running in the background.\\n`,\n );\n return { wrote: true, services: enabled, connected: true, running: true };\n}\n\n/** A name a person would recognise, without leaking their username. */\nfunction defaultDeviceName(): string {\n const override = process.env[\"BYOLLM_LABEL\"];\n if (override !== undefined && override !== \"\") return override.slice(0, 120);\n return \"my-computer\";\n}\n\n/**\n * Input that has ended, said as a fact rather than as a hang — B114.\n *\n * Its own error class because two commands catch it and neither should be\n * matching on a message. It is not a failure of the thing being asked: it\n * means the answers ran out, which for a person is Ctrl-D and for a script is\n * the end of the file.\n */\nexport class InputEnded extends Error {\n constructor() {\n super(\"no more input\");\n this.name = \"InputEnded\";\n }\n}\n\n/**\n * The real terminal, wired to readline — one interface, with a line queue.\n *\n * ## What was wrong, and how far it went\n *\n * `ask` opened a NEW `readline` interface per question and closed it after.\n * A person typing one line at a time never noticed. **A script did:** every\n * answer arrives before the first prompt opens, the first interface consumes\n * the lot, and closing it throws them away — so question two waits forever for\n * input that was delivered and dropped. Found building B100a, where the screen\n * asks eight questions in a row instead of three.\n *\n * **A single long-lived interface is not the fix on its own, and running it is\n * what showed that.** `readline` emits `line` as input arrives whether or not\n * anybody is asking, so lines that land between questions are still lost —\n * and `rl.question()` never settles at end of input, so one interface turns a\n * visible abort into a silent hang. Both verified by running them rather than\n * reasoned about.\n *\n * ## So: one interface, a queue, and an end that is an answer\n *\n * Every line is caught and either handed to a waiting question or parked. A\n * question takes a parked line if there is one and waits otherwise. Order\n * stops mattering, which is the only property that makes this safe for a\n * caller that is a program.\n *\n * End of input rejects with {@link InputEnded} — the waiting question and\n * every later one. **It is a third state, not an empty answer**: returning\n * `\"\"` would look like somebody pressing Enter, and the screens read Enter as\n * \"keep the default\", so a finished stdin would silently accept every\n * default including the one that pairs this device.\n *\n * ## `close()` is the caller's, and it is not optional\n *\n * An open interface holds `process.stdin`, so a command that does not close it\n * does not exit. Both callers close in a `finally`.\n */\nexport interface TerminalIo extends SetupIo {\n /** Release stdin. A command that forgets this does not exit. */\n close(): void;\n}\n\nexport function terminalIo(\n out: (text: string) => void,\n err: (text: string) => void,\n input: NodeJS.ReadableStream = process.stdin,\n output: NodeJS.WritableStream = process.stdout,\n): TerminalIo {\n /** Lines that arrived before anybody asked for them. */\n const parked: string[] = [];\n /** Questions that arrived before their line did. */\n const waiting: {\n resolve: (line: string) => void;\n reject: (why: Error) => void;\n }[] = [];\n let ended = false;\n let rl: ReturnType<typeof createInterface> | undefined;\n\n const open = (): void => {\n if (rl !== undefined) return;\n rl = createInterface({ input, output });\n rl.on(\"line\", (line: string) => {\n const next = waiting.shift();\n if (next === undefined) parked.push(line);\n else next.resolve(line);\n });\n rl.on(\"close\", () => {\n ended = true;\n for (const next of waiting.splice(0)) next.reject(new InputEnded());\n });\n };\n\n return {\n out,\n err,\n // `@types/node` declares `isTTY` as `boolean`, and Node sets it to\n // `undefined` when the stream is not a terminal. So this comparison is\n // load-bearing even though the type says it cannot be — the linter is\n // reasoning from a declaration that is wrong about its own runtime, and\n // deleting it puts `undefined` into a field typed `boolean`.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-boolean-literal-compare\n interactive: process.stdin.isTTY === true,\n ask(question: string): Promise<string> {\n open();\n /* The prompt is written whatever happens next, because a transcript\n that shows an answer with no question is a transcript nobody can\n read back. */\n output.write(question);\n const already = parked.shift();\n if (already !== undefined) return Promise.resolve(already);\n if (ended) return Promise.reject(new InputEnded());\n return new Promise<string>((resolve, reject) => {\n waiting.push({ resolve, reject });\n });\n },\n close(): void {\n rl?.close();\n rl = undefined;\n },\n };\n}\n","import type { BackendId, BackendCost, JobKind } from \"@byollm/protocol\";\nimport { backendName, classifyCost } from \"@byollm/protocol\";\nimport { createBackend } from \"./backends/index.js\";\nimport { modelSuggestions, type ModelSuggestion } from \"./cli-models.js\";\nimport { probeLocalServers, type LocalServer } from \"./probe-local.js\";\nimport { readFile } from \"node:fs/promises\";\nimport { tellSupervisor } from \"./supervised.js\";\nimport { DaemonConfig, ServiceConfig, writeConfig } from \"./config.js\";\nimport { isLoopback } from \"./local-server.js\";\nimport { dollars } from \"./spend.js\";\nimport {\n loginCommandFor,\n loginPlan,\n runLogin,\n type LoginCommand,\n} from \"./login.js\";\n\n/**\n * The one screen that turns this machine into services — B100a, byollm_023\n * §the flow.\n *\n * Todd, 09-10: *\"We already have a `byollm setup` that asks you to set up\n * claude, codex and ollama cloud if they are detected. What if we did similar\n * for adding services... Or even `byollm services manage` that lets you enable\n * and disable.\"* And an hour later, closing the obvious follow-up before\n * anybody built it: *\"We should remove the old setup and replace with this\n * one. I shouldn't have even suggested having two.\"*\n *\n * So this is the module and `setup` calls it. Two implementations of one\n * question would have been instruction 9 written into the plan on purpose,\n * which is worse than the drift we normally find by accident.\n *\n * ## What it replaced, and why that was three commands\n *\n * The design before this was `services add`, `services remove` and a\n * `services try` verb — each with its own help text, its own confirmation,\n * its own tests, and each teaching the owner one more word. The picker\n * replaces all three with a gesture people already know from every Linux\n * installer, and it answers *\"what could I use here\"* in the same motion as\n * *\"use it\"*, which is the question B100 existed for.\n *\n * **Verification stopped being a step.** `try` made checking a thing an owner\n * has to know to run; here the list only contains what a live server named or\n * what a real binary answered, so the check happens by construction. The\n * residue is the memory hazard — B106 — which was never about verification\n * and is not solved by a picker.\n *\n * ## Lines, not keypresses\n *\n * Todd asked whether it is *\"press 2 and it toggles a highlight of that row\"*.\n * Live keypresses need raw mode. This does not and looks nearly the same:\n * print the numbered list, read a LINE, redraw with the marks moved, repeat\n * until blank. It works with the `ask` that already exists, it is testable by\n * feeding lines, and **it degrades honestly with no TTY** — which is the\n * constraint that made the raw version design rather than wiring.\n *\n * ## Kinds are not on this screen at all\n *\n * Todd: *\"98% of users will not know what generate and chat mean, why they\n * exist and are separate, and what to choose.\"* Every service answers both,\n * so the only thing left to settle is which one wins when a job names none —\n * one question, at the end, and only when more than one service claims them.\n * Without it `resolveConfig` withholds a kind whenever two services answer\n * it, and the device advertises nothing.\n */\n\n/**\n * Ask the owner what a detected CLI serves — B211.\n *\n * `undefined` means \"not offered\", and every path to it says why first — an\n * owner who skips, and one whose three tries never answer, both land exactly\n * where everybody landed before.\n *\n * **No `interactive` guard here, deliberately.** Both screens that reach this\n * refuse a non-terminal at their own front door (`manageServices` and\n * `runSetup` both say \"needs a terminal it can ask questions in\"), so a guard\n * would be a branch nothing can enter — and this codebase treats a guard\n * nobody can reach as dead code wearing an API. The first version had one,\n * and its test proved only that the OUTER refusal fires.\n *\n * **Verified before it is accepted**, because a typed model is the one place\n * a typo can enter a config that validates any non-empty string — and the\n * failure would surface on somebody else's job. The backend's own words come\n * back in `detail`, so a misspelled model is reported as what the CLI said\n * rather than as \"signed out\": one sentence for two states is the defect this\n * file's own `Detected` type was split to prevent.\n *\n * Bounded at three rounds, for the reason `signIn` gives one screen up: an\n * unbounded prompt in a wizard is a wizard somebody Ctrl-Cs.\n */\nasync function modelByHand(input: {\n cli: { readonly id: BackendId; readonly binary: string };\n io: ManageIo;\n verifier: Verifier;\n /** Injected in tests; production reads the vendor CLIs' own files. */\n suggest?: (cli: BackendId) => Promise<readonly ModelSuggestion[]>;\n}): Promise<string | undefined> {\n const { cli, io, verifier } = input;\n\n /**\n * What the CLI itself says it has — B218.\n *\n * Todd, at this exact prompt on the real box: *\"I have no idea how to\n * answer since I don't know what codex has and it doesn't show me.\"* The\n * question was honest and still the wrong one to ask a person, because\n * `gpt-5.6-terra` was in a file on that disk the whole time.\n *\n * Empty is fine and common — a fresh install has no cache — and then this\n * is exactly the prompt it was before.\n */\n const suggestions = await (input.suggest ?? ((id) => modelSuggestions(id)))(\n cli.id,\n );\n const menu =\n suggestions.length === 0\n ? \"\"\n : `\\n${suggestions\n .map(\n (s, at) =>\n ` ${String(at + 1)} ${s.id}${s.note === undefined ? \"\" : ` — ${s.note}`}`,\n )\n .join(\"\\n\")}\\n`;\n const byHand =\n `\\n \\`${cli.binary}\\` is installed, and byollm does not know which ` +\n `model it serves.\\n Add it by hand and it will appear here next ` +\n `time: https://docs.byollm.cloud/guides/models\\n`;\n\n for (let round = 0; round < 3; round += 1) {\n const answer = (\n await io.ask(\n `\\n \\`${cli.binary}\\` is installed. Which model does it serve?\\n` +\n menu +\n (suggestions.length === 0\n ? ` (press enter to skip — it will not be offered) `\n : ` (a number, or type a model name; enter skips — it will not be offered) `),\n )\n ).trim();\n\n /* Skipping is a choice, not a failure, and it lands where it always did. */\n if (answer === \"\") {\n io.out(byHand);\n return undefined;\n }\n\n /**\n * A number picks from the menu; anything else is the model name.\n *\n * Only when it lands IN the menu — free text stays the rule, and a model\n * genuinely called \"2\" (nobody's, but the rule should not depend on that)\n * still reaches the verifier as itself once the menu is empty or short.\n */\n const picked = /^[0-9]+$/.test(answer)\n ? suggestions[Number(answer) - 1]\n : undefined;\n const chosen = picked?.id ?? answer;\n\n const proof = await verifier(cli.id, chosen);\n /* `undefined` is \"this backend has no canary\" and is NOT a refusal — the\n `Detected` docstring is explicit that rendering it as one is wrong. */\n if (proof.answers !== false) return chosen;\n\n io.out(\n `\\n \\`${cli.binary}\\` did not answer for \\`${chosen}\\`` +\n `${proof.detail === undefined ? \"\" : `: ${proof.detail}`}\\n`,\n );\n }\n\n /* Three rounds and no working model. Said, then left alone. */\n io.out(byHand);\n return undefined;\n}\n\n/** How this screen talks. Injected so tests are not a TTY. */\nexport interface ManageIo {\n out(text: string): void;\n err(text: string): void;\n /** Asks, and returns the raw answer. */\n ask(question: string): Promise<string>;\n /** Whether we may ask at all. */\n interactive: boolean;\n}\n\n/**\n * Whether a backend is startable on this machine.\n *\n * Injected rather than imported so a test can describe the machine it is\n * testing. The first version of the empty-machine case asserted \"writes\n * nothing when no CLI is installed\" and failed on a laptop that has `claude`\n * — a test whose answer depends on what the developer happens to have\n * installed says one thing here and another in CI, which is worse than no\n * test because it teaches people to re-run until green.\n */\nexport type Detector = (id: BackendId) => Promise<boolean>;\n\n/**\n * What answered on the local ports — injected for the same reason detection\n * is.\n *\n * A screen test that reaches the network tests the network. Worse, it tests\n * whatever the developer happens to be running: the empty-machine case failed\n * once already because this laptop has `claude` installed.\n */\nexport type Probe = () => Promise<LocalServer[]>;\n\n/**\n * What a detected CLI can actually do — found, or found *and* able to answer.\n *\n * `health()` runs `--version`, which needs no credentials. That is the right\n * question for \"is it installed\" and the wrong one for \"will it work\", and\n * setup was asking only the first and reporting \"Found the `claude` CLI\".\n *\n * The gap has a cost measured in evenings. A subscription token that expired\n * last week leaves a CLI that answers `--version` perfectly and every job with\n * status 1, so the machine advertises a service it cannot provide, the\n * dashboard shows it, a site sends work to it, and the first person to learn\n * is whoever was waiting for an answer. A job is not where somebody should\n * discover their token lapsed.\n *\n * So detection asks both, and they are different words: `installed` is the\n * binary, `answers` is the credentials.\n */\nexport interface Detected {\n /** The binary is there and runs. */\n readonly installed: boolean;\n /**\n * It answered a real prompt. `undefined` when the backend offers no canary,\n * which is not the same as `false` and must not be rendered as one.\n */\n readonly answers: boolean | undefined;\n /** Why it could not answer, in the backend's own words, for the owner. */\n readonly detail?: string | undefined;\n}\n\nexport type Verifier = (id: BackendId, model: string) => Promise<Detected>;\n\n/**\n * Subscription CLIs the screen offers, in the order it offers them — B116.\n *\n * ## The model is not ours to declare\n *\n * This carried `model: \"sonnet\"` for claude, and Todd's machine runs\n * `claude-opus-5`. So the picker offered his configured `claude` **and** a\n * second row called `claude-2` pinned to a model he never chose — a surface\n * deciding for itself, which is B085's shape in different clothes.\n *\n * **Checked by running the CLIs, because the obvious fix is to ask them and\n * you cannot.** `claude --help` has no models command; `claude config get\n * model` is not a command at all and runs as a PROMPT (it answered in prose\n * and spent a token); and the argv this daemon sends is frozen at\n * `--output-format text` on purpose, so the response carries no model field.\n * Three ways, no answer.\n *\n * ## So the sources are, in order\n *\n * 1. **The owner's configured service for this binary.** A machine has one\n * `claude`, so if they have configured it, that is what it serves — and\n * the canary then runs against the model the machine actually uses rather\n * than against our guess.\n * 2. **A model in this build's own {@link knownModelsFor} list for that\n * backend**, which byollm_017 already maintains as what the CLI is known\n * to accept, and which is already announced with the capability. `sonnet`\n * is there and is documented in `claude --help` as an alias for the latest\n * sonnet — the CLI's word rather than ours.\n * 3. **Nothing.** Then the row is not offered, because enabling it would\n * write a service pinned to a model nobody chose.\n *\n * `codex` is at (3) today: `gpt-5.6-terra` appears in no help output and in no\n * list this build maintains. It is Todd's configured model, which is where it\n * came from — one machine's setting, frozen into a constant for everybody.\n * The invariant test below is what stops that happening again.\n */\nexport const SUBSCRIPTION_CLIS: readonly {\n readonly id: BackendId;\n readonly binary: string;\n readonly plan: string;\n /**\n * What to offer on a machine that has not configured this CLI yet.\n *\n * **Optional, and absent is the honest value** — not a placeholder waiting\n * to be filled in. Every value here must appear in `knownModelsFor` for its\n * backend, which is asserted rather than promised.\n */\n readonly model?: string;\n readonly install: string;\n}[] = Object.freeze([\n {\n id: \"claude-cli\",\n binary: \"claude\",\n plan: \"Claude subscription\",\n /**\n * Opus — RULED by Todd, 09-14: *\"Let's update to opus for #3.\"*\n *\n * This was `sonnet`, chosen under the reasoning above: the alias the CLI's\n * own help documents, so it was the CLI's word rather than ours. That\n * reasoning still holds for `opus`, which is in `knownModelsFor` beside it\n * and is an alias of the same kind — the invariant test below is what\n * checks that rather than this comment.\n *\n * **What changed is not the sourcing rule, it is the default.** A person\n * setting up for the first time gets the best model their subscription\n * covers, and the counterweight — that it spends quota faster — is said on\n * the screen rather than discovered in a quota block a week later.\n */\n model: \"opus\",\n install: \"https://claude.com/claude-code\",\n },\n {\n id: \"codex-cli\",\n binary: \"codex\",\n plan: \"ChatGPT plan\",\n install: \"npm i -g @openai/codex\",\n },\n]);\n\n/**\n * Both kinds, on every service, always — Todd 09-10.\n *\n * *\"I've never understood why we wouldn't offer one or the other since the end\n * user can just choose which to use.\"* Right, and it is what makes one\n * default question enough for two kinds: the same answer is legal for both\n * precisely because every service answers both. Multi-modal is a separate\n * problem and does not arrive through this constant.\n */\nexport const BOTH_KINDS: readonly JobKind[] = Object.freeze([\n \"llm.generate\",\n \"llm.chat\",\n]);\n\n/** The share budget offered to somebody who has never set one. */\nexport const DEFAULT_SHARE_CAP_CENTS = 1_000;\n\n/**\n * A service block as it is written to `~/.byollm/config.json`.\n *\n * **Deliberately untyped, and it is the same decision `runSetup` made.** The\n * schema is `config.ts`'s and it is the thing that validates this — a second\n * TypeScript shape here would be a second definition of the config format,\n * kept in sync by hand, which is the divergence this row spent its time\n * removing elsewhere. What guards the write is `DaemonConfig.safeParse`, on\n * the real schema, before the file is touched.\n *\n * It is also the honest type for a block this screen PRESERVES. An owner's\n * `apiKeyEnv`, their `spend.centsPerMillionTokens`, a field added to the\n * schema next month: the picker does not know what is in there and must put\n * back exactly what it found.\n */\nexport type ServiceBlock = Record<string, unknown>;\n\n/** A block this screen is creating, where it does know every field. */\nexport interface NewService {\n readonly type: BackendId;\n readonly baseUrl?: string;\n readonly model: string;\n readonly kinds: readonly JobKind[];\n readonly offer: \"private\" | \"team\";\n}\n\n/**\n * One row on the screen.\n *\n * `selected`, `shared` and `capCents` are the mutable state of the screen and\n * nothing else is: everything a row *is* comes from a probe, a canary, or the\n * owner's existing config, so a row can never describe a service that does\n * not exist.\n */\ninterface Candidate {\n name: string;\n /**\n * What the server is, and it can be upgraded — B116.\n *\n * Not `readonly`, unlike the address and the model, and the asymmetry is\n * the point: those two are what this row IS, and this is what we currently\n * know about it. A probe's answer replaces a config's memory.\n */\n type: BackendId;\n readonly baseUrl: string | undefined;\n readonly model: string;\n /** Recomputed when {@link Candidate.type} is, from the one classifier. */\n cost: BackendCost;\n /** True when a server answered, rather than a config remembering. */\n identified: boolean;\n /** Where it lives, in the words a person would use. */\n where: string;\n /**\n * A subscription CLI's binary, when this row is one.\n *\n * Mutable, and B117 is why: a row read from the config does not know it is\n * a CLI until detection says so, and it is the field the sign-in path keys\n * on. Set only where a binary was actually found on this machine.\n */\n binary?: string;\n /**\n * The block this row was read from, when it came from the config.\n *\n * Kept whole rather than picked apart, because the fields this screen does\n * not ask about are exactly the ones it must not lose.\n */\n readonly original?: ServiceBlock;\n /** Set when the row is a CLI that is installed and cannot answer. */\n signedOut: boolean;\n /**\n * Set when the config names a CLI whose binary is NOT on this machine —\n * B119.\n *\n * A different state from {@link Candidate.signedOut}, and the difference is\n * the whole row: *signed out* means the command is here and has no\n * credentials, which an owner fixes by signing in. **This means the command\n * is not here at all**, which signing in cannot fix and which the sign-in\n * path must not offer.\n *\n * It does not deselect. Unselected rows are not written, so removing the\n * mark would DELETE a service the owner configured — on the one machine\n * where they are most likely to be about to reinstall the CLI. The row\n * stays; the claim over it changes.\n */\n missing: boolean;\n /**\n * Why, in the backend's own words.\n *\n * *\"Invalid API key · Please run /login\"* is the sentence that names the\n * fix, and it comes from the CLI rather than from us. Carried on the row\n * because the row is where the refusal is now said — dropping it would\n * leave \"cannot answer yet\" as the whole explanation, which is the\n * true-but-useless shape this project keeps having to fix.\n */\n detail?: string | undefined;\n selected: boolean;\n shared: boolean;\n capCents: number | undefined;\n}\n\nexport interface ManageResult {\n /** The complete `services` map to write. Empty when nothing was decided. */\n readonly services: Record<string, ServiceBlock>;\n /** `defaults`, empty when one service makes it unnecessary. */\n readonly defaults: Partial<Record<JobKind, string>>;\n /** The names in `services`, in screen order. */\n readonly enabled: readonly string[];\n /** False when the screen could not run or the person changed nothing. */\n readonly decided: boolean;\n}\n\n/**\n * A service name from a model id — settled by Todd on 09-10.\n *\n * `smollm2:135m` becomes **`smollm2-135m`**: keep what follows the colon and\n * turn the colon into a hyphen. The earlier version dropped the tag, which\n * read better and collided — `smollm2:135m` and `smollm2:360m` were one name,\n * and a picker that wrote both would have silently kept one.\n *\n * **The namespace before a `/` still goes.** CW's ruling, Todd's to veto:\n * `mlx-community/Qwen2.5-14B-Instruct-4bit` keeps only the last segment,\n * because what precedes it is a publisher rather than a name, and\n * `mlx-community-Qwen2.5-14B-Instruct-4bit` is not a service name anybody\n * wants to type into `enqueue({ service })`.\n *\n * It is a suggestion rather than a rule: the owner's word for a service is\n * theirs, this only has to produce something legal and recognisable in a file\n * they can edit.\n */\nexport function serviceNameFor(model: string): string {\n /**\n * `:latest` is the tag that means \"no tag\", and it goes.\n *\n * The rule above keeps the tag because tags distinguish — `smollm2:135m`\n * and `smollm2:360m` are two models and must be two names. `:latest` is\n * what Ollama writes when nobody chose a tag at all, so keeping it\n * distinguishes nothing and names every default-pulled model\n * `something-latest`. Found by running this on Todd's laptop, where\n * `gemma4-agent:latest` came out as `gemma4-agent-latest`.\n *\n * It cannot cost a collision: a server holding both `x` and `x:latest`\n * still gets two names, because {@link uniqueName} is what settles that and\n * it is asked either way.\n */\n const untagged = model.replace(/:latest$/, \"\");\n const base = untagged.split(\"/\").pop() ?? untagged;\n const cleaned = base\n .replace(/[^A-Za-z0-9._-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n return cleaned.length > 0 ? cleaned : \"my-model\";\n}\n\n/**\n * That name, made unique in this config.\n *\n * The tag collisions are gone with the rule above, and one class remains that\n * no naming rule can remove: **the same model id behind two servers**.\n * `qwen3:8b` on Ollama and on LM Studio are two services and one name, and a\n * plain assignment would keep whichever was written second.\n */\nfunction uniqueName(base: string, taken: Iterable<string>): string {\n const used = new Set(taken);\n if (!used.has(base)) return base;\n for (let n = 2; ; n += 1) {\n const candidate = `${base}-${String(n)}`;\n if (!used.has(candidate)) return candidate;\n }\n}\n\n/**\n * The config block for one model on one server — one definition, two readers.\n *\n * `byollm services` prints this as a pasteable block when the picker has not\n * been run (B100b's sanctioned fallback), and the picker writes it. Two\n * spellings of the same block would be two answers to *\"what does a service\n * for this model look like\"*, which is exactly the divergence instruction 9\n * is about.\n *\n * `offer` is written out rather than left to the schema default, because a\n * service created without a visible scope is a consent decision made by a\n * tool.\n */\nexport function serviceBlockFor(input: {\n readonly model: string;\n readonly baseUrl: string;\n /** What the server said it is, when it said — B112. */\n readonly type?: BackendId | undefined;\n}): NewService {\n return {\n /**\n * The provider the server named, or the generic transport — B112.\n *\n * This was `\"openai-http\"` unconditionally while the probe already knew\n * it was talking to Ollama, so the block we handed people was the one\n * config shape that **cannot be started on demand** — which B098 then has\n * to explain and offer to fix. One field, thrown away one line from where\n * it was learned.\n *\n * `openai-http` remains right for a server nobody identified. It is the\n * transport every HTTP backend speaks, so the service runs either way;\n * what the specific id buys is `startCommandFor` and a declared cost\n * class. B097 keeps the cost honest under both spellings — a `:cloud`\n * model at a loopback address is metered whichever type names it.\n */\n type: input.type ?? \"openai-http\",\n baseUrl: input.baseUrl,\n model: input.model,\n kinds: [...BOTH_KINDS],\n offer: \"private\",\n };\n}\n\n/**\n * What a line typed at the picker means.\n *\n * Forgiving about separators and unforgiving about nonsense, and the second\n * half is the load-bearing one: an unrecognised word must NOT fall through to\n * \"finish\". Blank means done, and a picker that treats `y` as blank ends the\n * screen on somebody answering a question it did not ask.\n */\nexport type Toggle =\n | { readonly kind: \"done\" }\n | { readonly kind: \"all\" }\n | {\n readonly kind: \"pick\";\n readonly at: readonly number[];\n /** Pieces of the line that were not rows, named rather than dropped. */\n readonly ignored: readonly string[];\n }\n | { readonly kind: \"unknown\"; readonly words: readonly string[] };\n\nexport function parseToggle(line: string, count: number): Toggle {\n const text = line.trim();\n if (text === \"\") return { kind: \"done\" };\n if (/^a(ll)?$/i.test(text)) return { kind: \"all\" };\n\n const at: number[] = [];\n const bad: string[] = [];\n for (const piece of text.split(/[\\s,]+/)) {\n if (piece === \"\") continue;\n const n = Number.parseInt(piece, 10);\n if (!/^\\d+$/.test(piece) || !Number.isFinite(n) || n < 1 || n > count) {\n bad.push(piece);\n continue;\n }\n if (!at.includes(n - 1)) at.push(n - 1);\n }\n /**\n * A line that was partly understood still counts as understood — and says\n * what it dropped.\n *\n * Refusing the whole line over one stray number would cost somebody their\n * other picks, which is why the forgiving half is right. **The silent half\n * was not.** Found by running it: `3,6` on a machine with five rows toggled\n * row three and dropped the six without a word, and the only clue was a\n * redraw somebody would have to diff against the last one.\n */\n if (at.length === 0) return { kind: \"unknown\", words: bad };\n return { kind: \"pick\", at, ignored: bad };\n}\n\n/**\n * Dollars typed by a person, as whole cents — Todd's wording, the daemon's\n * field.\n *\n * `spend.dailyCapCents` is the only spelling the daemon reads and the screen\n * asks in dollars, so this is the one conversion and it lives next to the\n * question that needs it.\n *\n * **Undefined for anything that is not a number**, and the caller asks again\n * rather than guessing. A cap is consent to spend somebody's money; an\n * unparseable answer to it is not a small number, it is not an answer.\n */\nexport function dollarsToCents(text: string): number | undefined {\n const cleaned = text.trim().replace(/^\\$/, \"\");\n if (!/^\\d+(\\.\\d{1,2})?$/.test(cleaned)) return undefined;\n const dollars = Number.parseFloat(cleaned);\n if (!Number.isFinite(dollars) || dollars < 0) return undefined;\n return Math.round(dollars * 100);\n}\n\nconst yesNo = (answer: string, fallback: boolean): boolean => {\n const text = answer.trim().toLowerCase();\n if (text === \"\") return fallback;\n return /^y(es)?$/.test(text);\n};\n\n/**\n * Detection means running the thing — byollm_013, and this is the screen's\n * half of it.\n *\n * There is a real tension in the spec and it is worth naming rather than\n * resolving silently. Detection \"must mean the probe exercised the argv we\n * will actually send — never `which` alone\", and the probe \"must not cost a\n * token\". Those pull opposite ways: the argv we actually send ends in a model\n * call, and a model call spends somebody's quota to answer a question about\n * installation.\n *\n * `health()` is where they meet — it spawns the real binary through the real\n * launch resolution, the same `resolveCliLaunch` an actual job uses, Windows\n * shim and all. The canary is the second question and the cheapest true call\n * the backend has: one token, to learn whether the credentials are live.\n */\nasync function detect(id: BackendId, model: string): Promise<Detected> {\n try {\n const backend = createBackend(id, {});\n const health = await backend.health();\n if (!health.healthy) {\n return {\n installed: false,\n answers: undefined,\n ...(health.detail === undefined ? {} : { detail: health.detail }),\n };\n }\n if (backend.canary === undefined) {\n return { installed: true, answers: undefined };\n }\n const proof = await backend.canary(model);\n return {\n installed: true,\n answers: proof.healthy,\n ...(proof.detail === undefined ? {} : { detail: proof.detail }),\n };\n } catch {\n return { installed: false, answers: undefined };\n }\n}\n\nexport async function detectInstalled(id: BackendId): Promise<boolean> {\n try {\n const backend = createBackend(id, {});\n const health = await backend.health();\n return health.healthy;\n } catch {\n // A backend that throws while being asked whether it exists is a backend\n // that does not exist, as far as somebody setting up a laptop is\n // concerned. The detail is in `byollm services`.\n return false;\n }\n}\n\n/**\n * The sign-in loop: offer to open it, run it, ask again.\n *\n * Two mechanisms, and the ruling names both. Spawning the vendor's own login\n * is the good one — it works in a single terminal, which is the only kind a\n * hosted console or an SSH session has, and it ends by itself when the login\n * finishes. Enter-to-recheck is the fallback for where spawning interactive\n * is unreliable, and it is also what somebody gets who would rather do it\n * their own way in another window.\n *\n * The loop re-probes rather than trusting the exit code. `claude auth login`\n * exiting 0 means the command finished, not that this machine can now answer\n * a prompt — the same distinction as \"installed\" versus \"answers\", one level\n * up, and believing the exit code would put the screen's whole point back\n * where it started.\n *\n * Bounded, because a person can be stuck: three rounds, then the caller's\n * refusal. An unbounded prompt in a wizard is a wizard somebody Ctrl-Cs.\n */\nasync function signIn(input: {\n cli: {\n readonly id: BackendId;\n readonly binary: string;\n readonly model: string;\n };\n io: ManageIo;\n verifier: Verifier;\n login: (command: LoginCommand) => Promise<boolean>;\n platform: NodeJS.Platform;\n}): Promise<Detected> {\n const { cli, io, verifier, login, platform } = input;\n const command = loginCommandFor(cli.id);\n /* Windows cannot spawn an npm `.cmd` — see login.ts. The offer to open it\n is withdrawn there rather than made and silently failed. */\n const plan = command === undefined ? undefined : loginPlan(command, platform);\n let proof: Detected = { installed: true, answers: false };\n\n for (let round = 0; round < 3; round += 1) {\n if (command !== undefined && plan?.kind === \"print\") {\n /* Told once, not offered three times: the command is the answer here,\n and asking [Y/n] again would be asking whether to do the thing this\n platform has already said it cannot do. */\n if (round === 0) io.out(`\\n${plan.say}\\n`);\n } else if (command !== undefined) {\n const go = await io.ask(` Sign in to ${cli.binary} now? [Y/n] `);\n if (yesNo(go, true)) {\n io.out(` ${command.says}\\n\\n`);\n // The terminal belongs to the child until it exits. Nothing is\n // captured — capturing is exactly what breaks a browser handoff or a\n // device code.\n await login(command);\n proof = await verifier(cli.id, cli.model);\n if (proof.answers !== false) return proof;\n io.out(\n `\\n Still cannot answer.` +\n (proof.detail === undefined ? \"\" : ` ${proof.detail}`) +\n \"\\n\",\n );\n continue;\n }\n }\n\n // The fallback, and the door for somebody who wants to do it their way.\n const again = await io.ask(\n ` Sign in with \\`${command?.argv.join(\" \") ?? cli.binary}\\` elsewhere, ` +\n `then press Enter to re-check (n to skip): `,\n );\n if (/^n(o)?$/i.test(again.trim())) return proof;\n proof = await verifier(cli.id, cli.model);\n if (proof.answers !== false) return proof;\n io.out(\n ` Still cannot answer.` +\n (proof.detail === undefined ? \"\" : ` ${proof.detail}`) +\n \"\\n\",\n );\n }\n return proof;\n}\n\n/**\n * What makes two rows the same service — B116, and it is a machine, an\n * address, and a model.\n *\n * **`type` used to be in this key, and that is the whole bug.** Todd's config\n * says `openai-http` at `127.0.0.1:11434` for `glm-5.2:cloud`; B112 taught the\n * probe to say `ollama` for the same address and the same model. Two keys, two\n * rows, one model — and enabling both writes two services where only one\n * carries his spend cap. The words a config happens to use are not what a\n * service IS.\n *\n * **Different models stay different services, and that is wanted rather than\n * tolerated.** `claude-sonnet-5` and `claude-opus-5` cost differently and\n * answer differently, and choosing between them is what a picker is for.\n *\n * **Loopback spellings collapse to one token.** `localhost:11434` and\n * `127.0.0.1:11434` are one machine and one port, and a config using one\n * spelling beside a probe using the other is a third way to get a duplicate.\n * The set of spellings is `isLoopback`'s, asked rather than restated.\n *\n * A process-class backend has no address, so its binary is its address: a\n * machine has one `claude`.\n */\nfunction identityOf(input: {\n readonly type: BackendId;\n readonly baseUrl: string | undefined;\n readonly model: string;\n}): string {\n return `${addressOf(input.type, input.baseUrl)} ${input.model}`;\n}\n\nfunction addressOf(type: BackendId, baseUrl: string | undefined): string {\n if (baseUrl === undefined) return `cli:${type}`;\n try {\n const url = new URL(baseUrl);\n return isLoopback(baseUrl)\n ? `local:${url.port}`\n : `${url.protocol}//${url.host}`;\n } catch {\n return baseUrl.trim();\n }\n}\n\n/** The three blocks, in the order they are shown. */\nconst GROUPS: readonly {\n readonly cost: BackendCost;\n readonly heading: string;\n}[] = Object.freeze([\n {\n cost: \"subscription\",\n heading: \"subscriptions this machine is signed in to\",\n },\n { cost: \"free\", heading: \"models on this machine\" },\n {\n cost: \"metered\",\n heading: \"models this machine can reach, billed to your account\",\n },\n]);\n\n/**\n * The screen, as lines — grouped, numbered, and marked.\n *\n * Local and `:cloud` in separate blocks is Todd's ruling and it is not\n * cosmetic: they are separate decisions with separate consequences, and a\n * single list headed \"on this machine\" already nearly shipped a metered paste\n * once (B100b, caught by running it on Todd's laptop).\n *\n * **Every row shows its cost class**, because consenting to spend money on\n * something that is not labelled as costing money is not consent.\n */\nfunction renderChoices(rows: readonly Candidate[]): string[] {\n const lines: string[] = [];\n let heading: string | undefined;\n rows.forEach((row, at) => {\n /**\n * Numbered from the array, and the heading follows the rows.\n *\n * The first version walked the groups and numbered inside each one, which\n * made the number on screen a function of the grouping and the number the\n * toggle reads a function of the array — two numbering schemes that agree\n * only while the array happens to be sorted. **A mutation that deleted\n * the sort changed nothing that any test could see, and would have moved\n * every mark to the wrong row on a machine whose probe answered in a\n * different order.** So the number is the index, always, and the group is\n * a heading printed when it changes.\n */\n const group = GROUPS.find((entry) => entry.cost === row.cost);\n if (group?.heading !== heading) {\n if (heading !== undefined) lines.push(\"\");\n heading = group?.heading;\n lines.push(` ${heading ?? \"other\"}`);\n }\n const mark = row.selected ? \"x\" : \" \";\n /**\n * The note carries the exception, and `missing` comes first — B119.\n *\n * Before `signedOut`, because a machine without the command is not signed\n * out of it; and before the cost line, because *\"your subscription — your\n * own jobs only\"* under a heading reading \"subscriptions this machine is\n * signed in to\" is two claims about a binary that is not here.\n *\n * The row keeps its mark. Unselected rows are not written, so clearing it\n * would delete a service the owner configured — on the machine where they\n * are most likely about to reinstall the CLI.\n */\n const note = row.missing\n ? `\\`${row.type.replace(/-cli$/, \"\")}\\` is not on this machine — the ` +\n \"service is kept, and unused until it is\"\n : row.signedOut\n ? \"signed out\"\n : row.cost === \"subscription\"\n ? \"your subscription — your own jobs only\"\n : row.cost === \"metered\"\n ? \"metered — runs on your provider's account\"\n : \"free — your electricity\";\n lines.push(\n ` ${String(at + 1).padStart(2)}. [${mark}] ${row.name.padEnd(24)} ${row.where}`,\n );\n lines.push(`${\" \".repeat(31)}${note}`);\n });\n lines.push(\"\");\n return lines;\n}\n\n/**\n * Everything this machine could run, from three sources that must agree.\n *\n * The third source is the one it is easy to leave out and the one that makes\n * this safe to re-run: **services already in the config that no probe found**.\n * A hand-written `anthropic` entry, or a server on a port nobody guesses,\n * appears here pre-marked — because a screen that rewrites `services` while\n * only knowing about two thirds of it is a screen that deletes somebody's\n * work. Deselecting is how a service is removed, and it has to be a choice.\n */\nasync function candidates(input: {\n readonly existing: Record<string, ServiceBlock>;\n readonly detector: Detector;\n readonly verifier: Verifier;\n readonly probe: Probe;\n readonly io: ManageIo;\n /** B218. Injected in tests so no case reads a real home directory. */\n readonly suggest?: (cli: BackendId) => Promise<readonly ModelSuggestion[]>;\n}): Promise<{\n readonly rows: Candidate[];\n /** Entries the schema refused, put back untouched. */\n readonly unreadable: Record<string, ServiceBlock>;\n}> {\n const rows: Candidate[] = [];\n const unreadable: Record<string, ServiceBlock> = {};\n const seen = new Map<string, Candidate>();\n const names = new Set<string>();\n\n /**\n * Add a row, or merge it into the one that is already this service — B116.\n *\n * **The merged row takes each field from whoever actually knows it.** The\n * owner's decisions win, always: the name they gave it, the offer, the cap,\n * the `apiKeyEnv`, anything they typed. **The probe wins `type`**, and only\n * `type`, because the probe asked the server what it is and the config only\n * remembers what somebody typed once.\n *\n * That direction is not a preference. `startCommandFor` switches on `type`,\n * so `openai-http` is the one shape that **cannot be started on demand** —\n * letting a stored guess beat an observation would silently un-start a\n * server we proved we can start, in the field, on `.88`. So a stored\n * `openai-http` at an address the probe now names `ollama` is UPGRADED here\n * and written that way, not preserved.\n */\n const add = (row: Candidate): void => {\n const key = identityOf(row);\n const already = seen.get(key);\n if (already !== undefined) {\n if (row.identified && !already.identified) {\n already.type = row.type;\n already.identified = true;\n /* The label too: it came from the same answer, and a row saying\n \"Ollama\" beside a type of `openai-http` is the disagreement this\n row exists to end. */\n already.where = row.where;\n /* Cost is re-asked rather than carried, because the type it was\n computed from just changed. One classifier, asked again — not a\n second opinion. */\n already.cost = classifyCost(\n already.type,\n already.baseUrl,\n already.model,\n ).cost;\n }\n return;\n }\n row.name = uniqueName(row.name, names);\n names.add(row.name);\n seen.set(key, row);\n rows.push(row);\n };\n\n /* The config first, so its names, shares and caps win over a probe's\n suggestions for the same service. Re-running shows what is already there,\n which is what makes this one screen rather than two. */\n for (const [name, block] of Object.entries(input.existing)) {\n /**\n * Read through the real schema, and a block it refuses is left alone.\n *\n * This screen rewrites `services` wholesale, so an entry it cannot\n * understand is an entry it would delete. `ServiceConfig` is the same\n * parser the daemon loads with, which means \"understood here\" and\n * \"understood there\" cannot drift — and the alternative, reaching into\n * `block[\"type\"]` by hand, is a second reader of the config format.\n */\n const parsed = ServiceConfig.safeParse(block);\n if (!parsed.success) {\n input.io.out(\n `\\n ${name} is in your config in a shape this screen does not\\n` +\n ` understand, so it is left exactly as it is.\\n`,\n );\n unreadable[name] = block;\n continue;\n }\n const service = parsed.data;\n add({\n name,\n type: service.type,\n baseUrl: service.baseUrl,\n model: service.model,\n cost: classifyCost(service.type, service.baseUrl, service.model).cost,\n where:\n service.baseUrl === undefined\n ? backendName(service.type)\n : `${backendName(service.type)} at ${service.baseUrl}`,\n original: block,\n identified: false,\n signedOut: false,\n missing: false,\n selected: true,\n shared: service.offer === \"team\",\n capCents: service.spend?.dailyCapCents,\n });\n }\n\n for (const cli of SUBSCRIPTION_CLIS) {\n if (!(await input.detector(cli.id))) {\n /**\n * The binary is absent, and the config may still name it — B119.\n *\n * This was a bare `continue`, so a configured `claude-cli` service on a\n * machine without `claude` kept the defaults the config loop gave it:\n * **not signed out, pre-selected, and printed under \"subscriptions this\n * machine is signed in to\"** — a heading asserting the one thing that\n * is not true of it.\n *\n * Bounded, which is why it is a marking rather than a removal:\n * `detectCapabilities` drops the service at runtime, so nothing is\n * falsely ADVERTISED. What was wrong is three surfaces disagreeing, and\n * the picker was the one saying the flattering thing.\n */\n for (const row of rows.filter((row) => row.type === cli.id)) {\n row.missing = true;\n }\n continue;\n }\n\n /**\n * A machine has one `claude` — B116 — and it can serve several models.\n *\n * If the owner already configured services for this binary, those rows\n * ARE this CLI and detection annotates them rather than adding another.\n * Keying on the model would not do it: their `claude-opus-5` and our\n * `sonnet` are genuinely different models, and the fix is to stop having\n * an opinion about which one their machine runs.\n *\n * **`filter`, not `find` — B117, a regression B116 created.** \"A machine\n * has one `claude`\" is true of the BINARY; answerability is per (binary,\n * model), which is why `verifier` takes both. B116 made\n * one-binary-many-models reachable and left this loop assuming one row\n * per binary, so with `claude-opus-5` and `claude-sonnet-5` both\n * configured the canary ran once, for the first, and the second row was\n * pre-selected showing nothing wrong. Our own ruling landing in half the\n * code.\n *\n * One real call per configured model is the honest cost of that: the\n * question \"can this machine answer\" has a different answer for each of\n * them, and a machine cannot be asked once about two.\n */\n const configured = rows.filter((row) => row.type === cli.id);\n if (configured.length > 0) {\n for (const row of configured) {\n const proof = await input.verifier(cli.id, row.model);\n row.signedOut = proof.answers === false;\n row.detail = proof.detail;\n /**\n * The binary, carried onto the row it belongs to — B117's second half,\n * and it bit the ONE-service case too.\n *\n * `binary` was set only on rows this branch ADDS, and the sign-in\n * path skips a row without one. So a configured CLI that could not\n * answer was marked `signed out` on screen, never offered the sign-in,\n * never deselected, and written into the config anyway — the exact\n * \"a note in a wizard that finished by saying it was done\" failure\n * that path exists to prevent, reintroduced by the row that stopped\n * duplicating it.\n */\n row.binary = cli.binary;\n }\n continue;\n }\n\n let model = cli.model;\n /**\n * Did the OWNER name this model just now — B219.\n *\n * Todd, on the real box, straight after typing `gpt-5.6-terra` at the\n * prompt: the toggle screen showed `codex` unchecked and he had to press\n * `2` to turn on the thing he had just configured.\n *\n * Typing a model name is an affirmative act. The old semantics made a\n * person say yes twice for one intention, and the two answers did not\n * even mean different things — there is no reason to name a model for a\n * service you do not want offered.\n *\n * Only for a model answered THIS RUN. A CLI whose model this build\n * already knew was never asked anything, so nothing was affirmed and its\n * default is unchanged; and a SKIPPED prompt still leaves the row out\n * entirely, which is the `continue` below.\n */\n let namedByOwner = false;\n if (model === undefined) {\n /**\n * Detected, and its model is not one this build can confirm — so ASK.\n * B211, ruled 09-15.\n *\n * This used to say *\"byollm does not know which model it serves — add\n * it by hand\"* and skip the row, which is where the bug lived. **A\n * hosted box cannot add anything by hand** (fixed command console, no\n * editor, and `byollm services` has one sub-verb), so `codex` could not\n * be turned on there **at all** — not \"irreversibly toggled off\", never\n * on. Todd's box had it only from an earlier configuration; deleting\n * that record dropped it into the state every new customer starts in.\n *\n * Asking removes the class rather than the instance. Adding\n * `gpt-5.6-terra` to `knownModelsFor` would be one machine's setting\n * frozen into a constant for everybody — what the invariant test in\n * this file exists to prevent — and the next model any vendor ships\n * lands here again. **The owner is the only source**: the doc above\n * records three ways of asking the CLI, all of which fail.\n */\n const named = await modelByHand({\n cli,\n io: input.io,\n verifier: input.verifier,\n ...(input.suggest === undefined ? {} : { suggest: input.suggest }),\n });\n if (named === undefined) continue;\n model = named;\n namedByOwner = true;\n }\n\n /*\n * No cost note here — B206, ruled 09-15, and the sentence that used to sit\n * here had two defects of its own.\n *\n * It said Opus was \"the strongest model your plan covers\", which we cannot\n * know and which Fable already makes false; and its only way out was to\n * edit `~/.byollm/config.json`, which a hosted box CANNOT do — the console\n * runs a fixed command list with no editor, and the edit would not reload\n * a running daemon anyway. A remedy that cannot be completed is worse than\n * no remedy; this file's own `#unauthenticated` neighbour (T2-S1) is where\n * that lesson was written down.\n *\n * The ruling's reason is separate and broader: \"Opus spends faster than\n * Sonnet\" is the same kind of statement as \"Sonnet spends faster than\n * Haiku\", which nobody would print, so a relative-tier cost note is\n * arbitrary. Opus is the default and it is chosen silently.\n */\n const proof = await input.verifier(cli.id, model);\n add({\n name: uniqueName(cli.binary, names),\n type: cli.id,\n baseUrl: undefined,\n model,\n cost: \"subscription\",\n /* Detection reached here, so the binary is on this machine — B119. */\n missing: false,\n where: `your ${cli.plan}`,\n binary: cli.binary,\n /* A process backend was detected by RUNNING it, so its id is an\n observation rather than a stored word — and nothing else can name a\n binary, so there is no probe to disagree with. */\n identified: true,\n signedOut: proof.answers === false,\n ...(proof.detail === undefined ? {} : { detail: proof.detail }),\n /* B219: on, if its model was just answered. See `namedByOwner`. */\n selected: namedByOwner,\n shared: false,\n capCents: undefined,\n });\n }\n\n input.io.out(\"\\nLooking for local model servers...\\n\");\n for (const server of await input.probe()) {\n for (const model of server.models) {\n const block = serviceBlockFor({\n model,\n baseUrl: server.baseUrl,\n type: server.backendId,\n });\n add({\n name: serviceNameFor(model),\n type: block.type,\n baseUrl: block.baseUrl,\n model,\n /* A server answered at this address; nothing here is a missing CLI. */\n missing: false,\n // Asked of the one classifier rather than decided here. A `:cloud`\n // model on a loopback port is metered, and a surface that classified\n // for itself is the defect B085 arrived as.\n cost: classifyCost(block.type, block.baseUrl, model).cost,\n where: `${server.label} at ${server.baseUrl}`,\n /* Only when a server actually named itself. `undefined` from the\n probe is \"we did not verify a provider\" — it must not beat a\n config's `ollama` with a generic `openai-http`. */\n identified: server.backendId !== undefined,\n signedOut: false,\n selected: false,\n shared: false,\n capCents: undefined,\n });\n }\n }\n\n /* Screen order, not discovery order: the groups are the screen, and a row\n that renders in block three must be numbered in block three. */\n const rank = new Map(GROUPS.map((group, at) => [group.cost, at]));\n rows.sort((a, b) => (rank.get(a.cost) ?? 9) - (rank.get(b.cost) ?? 9));\n return { rows, unreadable };\n}\n\n/**\n * The whole screen. Returns the `services` map and `defaults` to write.\n *\n * Nothing is written here: `setup` and `byollm services manage` own the file,\n * and this owns the conversation. Same reason `runSetup` parses before it\n * writes — a screen that can emit a config the daemon refuses has invented a\n * second format.\n */\nexport async function manageServices(input: {\n readonly io: ManageIo;\n readonly existing: Record<string, ServiceBlock>;\n readonly detector?: Detector;\n readonly verifier?: Verifier;\n readonly probe?: Probe;\n readonly login?: (command: LoginCommand) => Promise<boolean>;\n readonly platform?: NodeJS.Platform;\n /**\n * What each CLI has to offer — B218. Injected so no test reads a real home\n * directory: without this the suite would pick up whatever `~/.codex` held\n * on the machine running it, which is a test that passes or fails by\n * whose laptop it is on.\n */\n readonly suggest?: (cli: BackendId) => Promise<readonly ModelSuggestion[]>;\n}): Promise<ManageResult> {\n const io = input.io;\n const empty: ManageResult = {\n services: {},\n defaults: {},\n enabled: [],\n decided: false,\n };\n if (!io.interactive) {\n io.err(\n \"byollm services manage needs a terminal it can ask questions in.\\n\" +\n \"Edit ~/.byollm/config.json instead: \" +\n \"https://docs.byollm.cloud/guides/models\\n\",\n );\n return empty;\n }\n\n const detector = input.detector ?? detectInstalled;\n const verifier = input.verifier ?? detect;\n const probe = input.probe ?? (() => probeLocalServers());\n const platform = input.platform ?? process.platform;\n const login =\n input.login ??\n ((command: LoginCommand) =>\n runLogin(command, (text) => {\n io.err(text);\n }));\n\n const { rows, unreadable } = await candidates({\n existing: input.existing,\n detector,\n verifier,\n probe,\n io,\n ...(input.suggest === undefined ? {} : { suggest: input.suggest }),\n });\n\n if (rows.length === 0) {\n io.out(\n \"\\nNothing to configure yet — no supported CLI, and no model server\\n\" +\n \"answering on this machine. Install one, or add a model by hand:\\n\" +\n SUBSCRIPTION_CLIS.map(\n (cli) => ` ${cli.binary}: ${cli.install}\\n`,\n ).join(\"\") +\n \" local: https://docs.byollm.cloud/guides/models\\n\",\n );\n return empty;\n }\n\n // ── 1. what this machine may run ──────────────────────────────────────\n io.out(\n \"\\nWhat should byollm be able to run on this machine?\\n\" +\n \" Type a number to turn one on or off, `a` for all, Enter when done.\\n\\n\",\n );\n await pick(io, rows, () => renderChoices(rows));\n\n const chosen = rows.filter((row) => row.selected);\n if (chosen.length === 0) {\n io.out(\"\\nNothing selected.\\n\");\n return empty;\n }\n\n // ── 2. a CLI that cannot answer is not written down ────────────────────\n //\n // Setup used to stop the whole wizard here, and the reasoning was right:\n // a logged-out CLI reached the person as a *note* in a wizard that kept\n // going and finished by saying it was done, and two machines sat in \"we\n // thought it wasn't working\" for days.\n //\n // **What changed is only where the loudness lives.** Stopping a screen that\n // is also the re-run path would throw away every other choice somebody just\n // made, over one expired token. So the refusal moved onto the row: the sign-\n // in is offered here, and a CLI that still cannot answer is DESELECTED and\n // said out loud rather than written into a config that would claim it works.\n for (const row of chosen) {\n if (!row.signedOut || row.binary === undefined) continue;\n io.out(\n `\\n\\`${row.binary}\\` is installed and cannot answer yet — it needs signing in.\\n` +\n (row.detail === undefined ? \"\" : ` ${row.detail}\\n`),\n );\n const proof = await signIn({\n cli: { id: row.type, binary: row.binary, model: row.model },\n io,\n verifier,\n login,\n platform,\n });\n if (proof.answers === false) {\n row.selected = false;\n io.out(\n `\\n Leaving \\`${row.binary}\\` out: it is installed and not signed in, so\\n` +\n ` nothing would route to it. Sign in with ` +\n `\\`${loginCommandFor(row.type)?.argv.join(\" \") ?? row.binary}\\`, then\\n` +\n ` run \\`byollm services manage\\` again.\\n`,\n );\n continue;\n }\n row.signedOut = false;\n }\n\n const enabled = rows.filter((row) => row.selected);\n if (enabled.length === 0) {\n io.out(\"\\nNothing left to write.\\n\");\n return empty;\n }\n\n /* Said before the sharing question, not after it: the self-lock is consent\n wording, and the moment of enablement is the moment of disclosure. */\n const locked = enabled.filter((row) => row.cost === \"subscription\");\n if (locked.length > 0) {\n io.out(\n `\\n${locked.map((row) => row.name).join(\", \")} ` +\n `${locked.length === 1 ? \"uses\" : \"use\"} your own subscription, for YOUR\\n` +\n \"OWN jobs only — never shared with a team, whatever the config says.\\n\" +\n \"Someone else's terms are not yours to lend.\\n\",\n );\n }\n\n // ── 3. the team, asked before anything is priced ───────────────────────\n //\n // Todd: ask about the team FIRST and skip the whole branch on no. Most\n // people are one person on one machine, and a wizard that asks them to\n // price something for nobody is a wizard that teaches them their answers do\n // not matter.\n const shareable = enabled.filter((row) => row.cost !== \"subscription\");\n let anyShared = false;\n if (shareable.length > 0) {\n const hasTeam = yesNo(\n await io.ask(\n \"\\nDo you have, or plan to create, a team to share these services\\n\" +\n \"with on this computer? [y/N] \",\n ),\n false,\n );\n if (hasTeam) {\n io.out(\n \"\\nWhich of these may your team use?\\n\" +\n \" Same list: a number turns one on or off, `a` for all, Enter when done.\\n\\n\",\n );\n if (locked.length > 0) {\n io.out(\n ` not shareable: ${locked.map((row) => row.name).join(\", \")} — ` +\n \"runs on your own subscription\\n\\n\",\n );\n }\n await pick(\n io,\n shareable,\n () => renderShares(shareable),\n (row) => {\n row.shared = !row.shared;\n },\n (row) => row.shared,\n );\n\n // ── 4. the money, only where somebody else spends it ───────────────\n //\n // Free rows never reach this: there is nothing to cap. No owner-side\n // metering either — ruled and closed — and the code agrees, `spend.ts`\n // is explicit that only community metered work consults the cap.\n for (const row of shareable.filter(\n (candidate) => candidate.shared && candidate.cost === \"metered\",\n )) {\n const cents = await askCap(io, row);\n if (cents === 0) {\n row.shared = false;\n row.capCents = undefined;\n io.out(` ${row.name} stays private.\\n`);\n continue;\n }\n row.capCents = cents;\n }\n anyShared = shareable.some((row) => row.shared);\n }\n }\n\n // ── 5. one question, two kinds ─────────────────────────────────────────\n const defaults: Partial<Record<JobKind, string>> = {};\n if (enabled.length > 1) {\n io.out(\"\\nWhat would you like your default service to be?\\n\");\n /* Claude if it was selected, which is deliberate. A subscription default\n means a teammate's job that names no service is refused as\n `default-unusable` — and Todd ruled that is correct rather than a\n defect: *\"they are probably using my share for a specific model I make\n available, which is likely not my personal default\"*. A teammate\n reaching for a shared machine names the service they were given. */\n const preferred = enabled.findIndex((row) => row.type === \"claude-cli\");\n const fallback = preferred === -1 ? 0 : preferred;\n enabled.forEach((row, at) => {\n /* Suggested by Todd as a one-line addition rather than a different\n ruling: when anything was shared, say which choices cannot serve the\n people it was shared with. */\n const note =\n anyShared && row.cost === \"subscription\"\n ? \" (cannot serve teammates)\"\n : \"\";\n io.out(` ${String(at + 1)}. ${row.name}${note}\\n`);\n });\n const answer = await io.ask(` [${String(fallback + 1)}] `);\n const typed = Number.parseInt(answer.trim(), 10);\n /* Anything unparseable falls to the offered row, which the prompt already\n promised. Erroring here would make a typo cost the whole conversation.\n Nothing is spent by this answer, which is why it may be forgiving where\n the cap question is not. */\n const winner =\n enabled[Number.isFinite(typed) ? typed - 1 : fallback] ??\n enabled[fallback];\n if (winner !== undefined) {\n for (const kind of BOTH_KINDS) defaults[kind] = winner.name;\n }\n }\n\n /* Blocks the schema refused first, so a name collision cannot let the\n screen overwrite one it has already promised to leave alone. */\n const services: Record<string, ServiceBlock> = { ...unreadable };\n for (const row of enabled) services[row.name] = draftOf(row);\n\n return {\n services,\n defaults,\n enabled: enabled.map((row) => row.name),\n decided: true,\n };\n}\n\n/**\n * One row, as the config block it becomes.\n *\n * **A row that came from the config is edited, not rebuilt.** This screen asks\n * about two things — whether a service exists and who may use it — so those\n * are the only two fields it may change. An owner's `apiKeyEnv`, their\n * `spend.centsPerMillionTokens`, a `kinds` list naming one kind on purpose:\n * all of it is put back exactly as it was found.\n *\n * **Which means an existing service keeps its declared kinds.** \"Both kinds on\n * every service\" is the rule for services this screen CREATES; applying it to\n * a hand-written single-kind entry would be the picker widening what a machine\n * answers without ever asking about it, in a screen whose whole premise is\n * that kinds are hidden from the person. Recorded rather than assumed, per\n * instruction 11: what would change it is a reason to believe a single-kind\n * service is always a mistake.\n */\nfunction draftOf(row: Candidate): ServiceBlock {\n const shared = row.shared && row.cost !== \"subscription\";\n const base: ServiceBlock =\n row.original ??\n ({\n type: row.type,\n ...(row.baseUrl === undefined ? {} : { baseUrl: row.baseUrl }),\n model: row.model,\n kinds: [...BOTH_KINDS],\n } satisfies ServiceBlock);\n\n /* The acknowledgement and the ceiling arrive together or not at all.\n `resolveConfig` refuses a widened metered service that has one without the\n other, so a screen that collected them separately could write a config the\n daemon then rejects — the person finding out later from an error rather\n than sooner from a question. */\n const previous = base[\"spend\"];\n const spend =\n shared && row.cost === \"metered\" && row.capCents !== undefined\n ? {\n ...(typeof previous === \"object\" && previous !== null\n ? previous\n : {}),\n acknowledged: true,\n dailyCapCents: row.capCents,\n }\n : /* Kept, not cleared, when sharing is turned off. Somebody answering\n \"0 disables sharing\" said what to do with the offer, not what to\n forget about their ceiling — and the number is theirs. */\n previous;\n\n return {\n ...base,\n /**\n * The type this row actually is, over whatever the file remembered —\n * B116, and it is the one owner-typed field this screen overwrites.\n *\n * Instruction 10 makes this a migration rather than a compatibility\n * problem: nothing is backwards compatible until we are live, and the\n * consumers of this file are four machines we can name. A stored\n * `openai-http` at an address the probe calls `ollama` is a service that\n * cannot be started on demand, and the whole point of B116 is that we\n * know better at the moment we write.\n *\n * It is not a licence to rewrite anything else. `identified` is false for\n * every row that only a config knows about, so a hand-written service at\n * a port nobody probes keeps its type untouched.\n */\n type: row.type,\n offer: shared ? \"team\" : \"private\",\n ...(spend === undefined ? {} : { spend }),\n };\n}\n\n/** The sharing pass, rendered from the same rows with a different mark. */\nfunction renderShares(rows: readonly Candidate[]): string[] {\n return rows.map((row, at) => {\n const mark = row.shared ? \"x\" : \" \";\n const cost =\n row.cost === \"metered\"\n ? \"metered — your account pays for their jobs\"\n : \"free — your electricity\";\n return ` ${String(at + 1).padStart(2)}. [${mark}] ${row.name.padEnd(24)} ${cost}`;\n });\n}\n\n/**\n * The toggle loop itself: draw, read a line, move the marks, draw again.\n *\n * Bounded at a hundred rounds. Not a real limit for a person — it is the\n * guard against a caller whose `ask` returns the same non-empty string\n * forever, which is what a piped stdin does at end of input, and an\n * unbounded loop there is a CLI that hangs instead of finishing.\n */\nasync function pick(\n io: ManageIo,\n rows: Candidate[],\n render: () => string[],\n toggle: (row: Candidate) => void = (row) => {\n row.selected = !row.selected;\n },\n isOn: (row: Candidate) => boolean = (row) => row.selected,\n): Promise<void> {\n for (let round = 0; round < 100; round += 1) {\n for (const line of render()) io.out(`${line}\\n`);\n const answer = await io.ask(\" > \");\n const verdict = parseToggle(answer, rows.length);\n if (verdict.kind === \"done\") return;\n if (verdict.kind === \"unknown\") {\n io.out(\n `\\n \"${verdict.words.join(\" \")}\" is not a number on this list. ` +\n `Type 1-${String(rows.length)}, \\`a\\`, or Enter to finish.\\n\\n`,\n );\n continue;\n }\n if (verdict.kind === \"all\") {\n /* Toggle, not set: `a` turns everything on, and turns everything off\n when it is already on. Both gestures out of one key, and the second\n is the only way to clear a list somebody filled by accident. */\n const turningOn = !rows.every((row) => isOn(row));\n for (const row of rows) if (isOn(row) !== turningOn) toggle(row);\n } else {\n for (const at of verdict.at) {\n const row = rows[at];\n if (row !== undefined) toggle(row);\n }\n if (verdict.ignored.length > 0) {\n io.out(\n `\\n ignored ${verdict.ignored.join(\", \")} — ` +\n `this list stops at ${String(rows.length)}.\\n`,\n );\n }\n }\n io.out(\"\\n\");\n }\n}\n\n/**\n * The cap, in Todd's words and the daemon's units.\n *\n * *\"You authorized the team to spend up to this amount on model <name>\n * each day in dollars (0 disables sharing)\"* — the sentence is the\n * acknowledgement, which is why `spend.acknowledged` is set from answering it\n * rather than from a second question.\n *\n * **Fails closed.** Three unparseable answers leave the service private\n * rather than falling back to the offered default: the default is an offer,\n * and an offer nobody accepted is not consent to spend their money.\n */\nasync function askCap(io: ManageIo, row: Candidate): Promise<number> {\n const offered = row.capCents ?? DEFAULT_SHARE_CAP_CENTS;\n for (let round = 0; round < 3; round += 1) {\n const answer = await io.ask(\n `\\n You authorized the team to spend up to this amount on model ` +\n `${row.name}\\n each day, in dollars (0 disables sharing): ` +\n `[${dollars(offered).replace(\"$\", \"\")}] `,\n );\n if (answer.trim() === \"\") return offered;\n const cents = dollarsToCents(answer);\n if (cents !== undefined) return cents;\n io.out(\n ` \"${answer.trim()}\" is not an amount. Type a number of dollars, ` +\n `like 10 or 2.50.\\n`,\n );\n }\n io.out(` No amount given, so ${row.name} stays private.\\n`);\n return 0;\n}\n\n/**\n * The existing config, whole — not just the part this screen writes.\n *\n * It used to return `{ services }` and nothing else, and the wizard then wrote\n * `{ services, defaults }` over the top. Every other key the owner had was\n * silently dropped: `concurrency`, the community and ingress blocks, a\n * per-service `offer`. Settings somebody chose deliberately, deleted by a\n * command that never said it would touch them.\n *\n * It only bites on a config with **zero** services, because a config with any\n * is refused a few lines up — which is exactly why it survived. The path that\n * loses the owner's work is the path taken by people whose config a previous\n * version left empty, i.e. the people already having a bad time.\n *\n * The whole object comes back so the write can put it back. What this screen\n * knows about, it replaces; what it does not, it leaves alone. A tool that\n * cannot enumerate every setting it is not editing must not assume there are\n * none.\n */\nexport async function readExistingConfig(\n path: string,\n): Promise<\n | { services: Record<string, ServiceBlock>; rest: Record<string, unknown> }\n | undefined\n> {\n try {\n const raw = await readFile(path, \"utf8\");\n const parsed: unknown = JSON.parse(raw);\n if (typeof parsed !== \"object\" || parsed === null)\n return { services: {}, rest: {} };\n const row = parsed as Record<string, unknown>;\n\n /**\n * Carried across only if the daemon would still accept it.\n *\n * The first version of this kept every key it did not recognise, and the\n * existing suite refused it within the minute: a pre-alpha.44 config\n * carries `device`, `DaemonConfig` is `.strict()`, and the wizard's own\n * \"would the daemon load this?\" check then failed. Preserving a key the\n * schema has since dropped does not save somebody's work — it writes a\n * file that will not load, which is worse than the deletion it was\n * fixing.\n *\n * So the set is the schema's own top-level keys, read from the schema\n * rather than typed out here. A setting added to `DaemonConfig` next month\n * survives a re-run without anybody remembering this function.\n */\n const known = new Set(Object.keys(DaemonConfig.shape));\n const rest: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(row)) {\n // `services` and `defaults` are this screen's to rewrite.\n if (key === \"services\" || key === \"defaults\") continue;\n if (known.has(key)) rest[key] = value;\n }\n\n const services = row[\"services\"];\n /**\n * Typed as blocks, and every reader re-parses.\n *\n * Nothing here has been validated — this is `JSON.parse` output from a\n * file a hand may have written — so the type is a claim about shape and\n * not about contents. It is safe because {@link candidates} puts every\n * entry through `ServiceConfig` before it becomes a row, and an entry that\n * fails is carried back out untouched rather than interpreted.\n */\n return {\n services:\n typeof services === \"object\" && services !== null\n ? (services as Record<string, ServiceBlock>)\n : {},\n rest,\n };\n } catch {\n return undefined;\n }\n}\n\n/**\n * Write the screen's answers, through the schema the daemon actually loads.\n *\n * **One writer, because there are two callers now.** `setup` wrote this block\n * and `services manage` would have written the same one — including the\n * subtle half, which is that `config` is written and never `parsed.data`.\n *\n * Parsing is validation here and nothing else. `parsed.data` carries\n * `concurrency`, the community and ingress blocks, per-service\n * `offer: \"private\"` — today's values for settings nobody was asked about,\n * written into a file that outlives them. Tune a budget next year and every\n * screen-written config sits on the old number, chosen by no one, and the\n * owner has no way to tell which of those lines they meant. A default belongs\n * in one place; writing it down a second time is the same defect as a fixture\n * that restates a constant.\n */\nexport async function writeManaged(\n path: string,\n io: ManageIo,\n rest: Record<string, unknown>,\n outcome: ManageResult,\n /**\n * The environment the supervisor is read from — B241.\n *\n * Last and optional, defaulting to the real one, so every existing caller\n * is unchanged and production behaviour is identical. It exists because\n * `tellSupervisor` has taken an `env` since it was written, and this was the\n * one link in the chain that did not pass one — so the only way a test could\n * say \"there is a supervisor\" was to set `process.env` on the whole process.\n *\n * **Two test files did exactly that, and it cost 8% of paired runs.**\n * `start-says-what-is-signed-out.test.ts` sets `BYOLLM_SUPERVISOR_PID` while\n * `setup.test.ts`'s supervisor cases read it; in parallel workers sharing one\n * `process.env`, each hung waiting for a supervisor that was not theirs. A\n * global is not a fixture, and the seam to avoid it already existed one layer\n * down.\n */\n env: NodeJS.ProcessEnv = process.env,\n): Promise<boolean> {\n const config = {\n // Whatever the owner had that this screen does not ask about, first, so\n // the keys it *does* write win.\n ...rest,\n services: outcome.services,\n ...(Object.keys(outcome.defaults).length > 0\n ? { defaults: outcome.defaults }\n : {}),\n };\n\n // A screen that can emit a config the daemon refuses is a screen that has\n // invented a second format.\n const parsed = DaemonConfig.safeParse(config);\n if (!parsed.success) {\n io.err(\n \"byollm built a config this daemon would refuse, which is a bug:\\n\" +\n parsed.error.issues\n .map((issue) => ` ${issue.path.join(\".\")}: ${issue.message}`)\n .join(\"\\n\") +\n \"\\n\",\n );\n return false;\n }\n\n await writeConfig(path, config);\n\n /**\n * And whoever supervises this daemon is told — B207, duty three.\n *\n * **Here rather than at the call sites**, because this is the one function\n * that writes the config: `setup` and `services manage` both land on it, and\n * a third writer added later inherits the behaviour instead of forgetting\n * it. Putting the signal beside each caller is how one rule becomes two\n * implementations that drift — the defect class this board records most.\n *\n * A running daemon reads config once at start, so before this a box picked\n * up a change only when the supervisor next respawned a dead daemon, up to a\n * minute later, and a supervisor that was NOT respawning (because the daemon\n * was up and serving) never picked it up at all.\n */\n const told = tellSupervisor(undefined, env);\n if (told === \"told\") {\n io.out(\n \"\\nTold the supervisor — this device is picking the change up now.\\n\",\n );\n } else if (told === \"gone\") {\n io.out(\n \"\\nThis device's supervisor is not answering, so nothing is serving the\\n\" +\n \"new configuration yet. Restarting the device picks it up.\\n\",\n );\n }\n return true;\n}\n\n/**\n * What was written, per service, in the words that describe THIS config.\n *\n * The line it replaces said *\"claude, qwen — your own jobs only\"* for every\n * config the wizard could write, which was true right up until this screen\n * could share one. A summary that cannot be wrong about the thing it\n * summarises is a summary nobody has to check.\n */\nexport function summarise(outcome: ManageResult): string[] {\n return outcome.enabled.map((name) => {\n const block = outcome.services[name] as\n { offer?: unknown; spend?: { dailyCapCents?: number } } | undefined;\n if (block?.offer !== \"team\") return ` ${name} — your own jobs only`;\n const cap = block.spend?.dailyCapCents;\n return (\n ` ${name} — your team may use it` +\n (cap === undefined ? \"\" : `, up to ${dollars(cap)} a day`)\n );\n });\n}\n\n/**\n * A configured service whose type contradicts what the server says it is —\n * B116, and it is the check that keeps this closed.\n *\n * **The rule: nothing a probe identified may carry a different type.** Todd's\n * file today has `glm-5.2` as `openai-http` at `127.0.0.1:11434`, and that\n * address answers Ollama's own API. `openai-http` is the one shape\n * `startCommandFor` has no command for, so that service is the one thing on\n * his machine that cannot be started on demand — which is exactly the\n * capability we proved works in the field an hour ago.\n *\n * **`:cloud` is the sharp case and it lands the same way.** An `ollama:cloud`\n * model is served by an Ollama daemon on loopback: it is `type: \"ollama\"` and\n * it is startable. Metered is a COST fact, not a transport fact — B097 reads\n * the tag before the declared cost, so typing it `ollama` cannot make it look\n * free. **Nothing Ollama serves may be typed `openai-http`.**\n *\n * `openai-http` stays right for a server nobody identified, and that is all it\n * is for: the generic transport, not somewhere to fall back to when the\n * specific id is inconvenient to carry. A service at a port no probe visits —\n * Todd's MLX on 6999 — is untouched by this, which is the control.\n */\nexport interface MisTyped {\n readonly service: string;\n readonly stored: BackendId;\n readonly probed: BackendId;\n readonly baseUrl: string;\n}\n\nexport function misTypedServices(input: {\n readonly services: Record<string, ServiceBlock>;\n readonly servers: readonly LocalServer[];\n}): MisTyped[] {\n /* Only servers that NAMED themselves. A probe that identified nothing has\n no opinion to enforce, and treating its silence as `openai-http` would\n turn \"we did not ask\" into a finding. */\n const identified = new Map<string, BackendId>();\n for (const server of input.servers) {\n if (server.backendId === undefined) continue;\n identified.set(\n addressOf(server.backendId, server.baseUrl),\n server.backendId,\n );\n }\n\n const found: MisTyped[] = [];\n for (const [name, block] of Object.entries(input.services)) {\n const parsed = ServiceConfig.safeParse(block);\n if (!parsed.success) continue;\n const service = parsed.data;\n if (service.baseUrl === undefined) continue;\n const probed = identified.get(addressOf(service.type, service.baseUrl));\n if (probed === undefined || probed === service.type) continue;\n found.push({\n service: name,\n stored: service.type,\n probed,\n baseUrl: service.baseUrl,\n });\n }\n return found;\n}\n\n/** The lines `byollm services` prints about them, or nothing. */\nexport function misTypedReport(found: readonly MisTyped[]): string[] {\n if (found.length === 0) return [];\n return found.flatMap((row) => [\n ` ! ${row.service}: your config says \"${row.stored}\", and ${row.baseUrl} ` +\n `answers as ${backendName(row.probed)}.`,\n ` byollm only starts a server whose service names it, so this one ` +\n `will not be started`,\n ` when a job needs it. \\`byollm services manage\\` fixes it, or set ` +\n `\"type\": \"${row.probed}\".`,\n ]);\n}\n","import { readFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { BackendId } from \"@byollm/protocol\";\n\n/**\n * What a vendor CLI can tell us about the models it has — B218.\n *\n * Todd, on the real box, at B211's new prompt: *\"I have no idea how to answer\n * since I don't know what codex has and it doesn't show me.\"* The prompt was\n * honest and still handed a person a question the machine was better placed\n * to answer — `gpt-5.6-terra` was sitting in a file on that disk the whole\n * time.\n *\n * ## Suggestions, never a validator\n *\n * Everything here is a hint for a picker. Free text stays, and an answer that\n * appears in no list is still accepted, because B211's whole lesson is that\n * the model namespace moves faster than our releases. A list that could\n * REFUSE a name would be the stale hardcoded offer again, wearing a nicer\n * coat.\n *\n * ## Reads files. Never runs a CLI.\n *\n * No inference (that spends), and no sub-invocation of a vendor binary (a\n * prompt inside a non-interactive context is a hang — B213). **Measured\n * hazard, 09-16:** `codex models`, `codex list` and `codex model` are not\n * subcommands, and codex answers an unknown one with *\"options will be\n * forwarded to the interactive CLI\"* — so a probe that guesses a verb starts\n * an interactive session. Read the file; never guess.\n *\n * ## The two CLIs are not symmetrical, and the difference is the point\n *\n * **codex** keeps `~/.codex/models_cache.json` — a real list, with\n * `visibility` (\"list\" or \"hide\") and `priority` already in it, so the picker\n * is the vendor's own idea of what to show and in what order.\n *\n * **claude** keeps no list at all, and that is the RIGHT answer rather than a\n * gap. Its `--model` help says: *\"Provide an alias for the latest model (e.g.\n * 'fable', 'opus', or 'sonnet')\"*. **An alias cannot go stale**, which is\n * exactly the failure B211 exists to stop — so we offer aliases and\n * deliberately do not build a versioned list we would have to chase.\n */\n\nexport interface ModelSuggestion {\n /** What gets written to the config if chosen. */\n readonly id: string;\n /** Shown beside it. The vendor's words where we have them. */\n readonly note?: string;\n}\n\n/** Claude's own documented aliases, which track the latest model. */\nexport const CLAUDE_ALIASES: readonly ModelSuggestion[] = [\n { id: \"opus\", note: \"most capable\" },\n { id: \"sonnet\", note: \"balanced\" },\n { id: \"fable\", note: \"fastest\" },\n];\n\n/** Read and parse, or undefined. Absence is normal and is not an error. */\nasync function readJson(path: string): Promise<unknown> {\n try {\n return JSON.parse(await readFile(path, \"utf8\")) as unknown;\n } catch {\n /* Missing, unreadable, or not JSON. A fresh install has no cache, a\n different account has no settings, and neither is worth a word on\n screen — the prompt below still works without us. */\n return undefined;\n }\n}\n\n/** The shape this reads out of codex's cache, and nothing more. */\ninterface CachedModel {\n readonly slug?: unknown;\n readonly display_name?: unknown;\n readonly description?: unknown;\n readonly visibility?: unknown;\n readonly priority?: unknown;\n}\n\nfunction codexSuggestions(cache: unknown): ModelSuggestion[] {\n if (typeof cache !== \"object\" || cache === null) return [];\n const models = (cache as { models?: unknown }).models;\n if (!Array.isArray(models)) return [];\n\n const listed = models\n .filter((m): m is CachedModel => typeof m === \"object\" && m !== null)\n /* `visibility` is the vendor's own answer to \"should a person see this\".\n `gpt-reserve` and `codex-auto-review` are marked `hide`, and offering\n either would be us showing internals as choices. Anything not\n explicitly `list` is left out: an unknown value is not a yes. */\n .filter((m) => m.visibility === \"list\")\n .filter(\n (m): m is CachedModel & { slug: string } => typeof m.slug === \"string\",\n );\n\n /* The vendor's ordering, kept. Lower priority first — on the machine this\n was read from, that put `gpt-5.6-terra` at the top, which is the model\n Todd typed by hand. */\n listed.sort((a, b) => {\n const left =\n typeof a.priority === \"number\" ? a.priority : Number.MAX_SAFE_INTEGER;\n const right =\n typeof b.priority === \"number\" ? b.priority : Number.MAX_SAFE_INTEGER;\n return left - right;\n });\n\n return listed.map((m) => ({\n id: m.slug,\n ...(typeof m.description === \"string\" && m.description !== \"\"\n ? { note: m.description }\n : typeof m.display_name === \"string\"\n ? { note: m.display_name }\n : {}),\n }));\n}\n\nasync function claudeSuggestions(home: string): Promise<ModelSuggestion[]> {\n const settings = await readJson(join(home, \".claude\", \"settings.json\"));\n const configured =\n typeof settings === \"object\" && settings !== null\n ? (settings as { model?: unknown }).model\n : undefined;\n\n const out: ModelSuggestion[] = [];\n if (typeof configured === \"string\" && configured !== \"\") {\n out.push({ id: configured, note: \"what claude is set to use\" });\n }\n for (const alias of CLAUDE_ALIASES) {\n // The configured value may BE an alias, or an alias with a suffix\n // (`opus[1m]`). Either way it is already offered above, and offering it\n // twice would read as two different answers.\n if (out.some((s) => s.id === alias.id || s.id.startsWith(`${alias.id}[`))) {\n continue;\n }\n out.push(alias);\n }\n return out;\n}\n\n/**\n * What to offer for this CLI. Empty is a perfectly good answer — the caller\n * falls through to the free-text prompt that existed before any of this.\n */\nexport async function modelSuggestions(\n cli: BackendId,\n home: string = homedir(),\n): Promise<readonly ModelSuggestion[]> {\n if (cli === \"codex-cli\") {\n return codexSuggestions(\n await readJson(join(home, \".codex\", \"models_cache.json\")),\n );\n }\n if (cli === \"claude-cli\") return claudeSuggestions(home);\n return [];\n}\n","/**\n * Find local model servers by asking them — byollm_013, applied to onboarding.\n *\n * The obvious way is `ps`, and it is the wrong one. A process list tells you a\n * program is running, not that it will answer, not which port it took when its\n * default was busy, and not what it can serve. It is also three\n * implementations — `ps`, `tasklist`, and whatever BSD does — for a question\n * none of them actually answer.\n *\n * So this asks. One GET per well-known port; whatever replies to\n * `/v1/models` with a model list is a service the owner can use, and the reply\n * carries the model names too. That is the same rule detection already lives\n * under — *running the thing beats naming the thing* — and it is cross-platform\n * for free, because HTTP is.\n *\n * It cannot find a server on a port nobody guessed. That is a real limit and\n * the wizard says so rather than presenting the list as exhaustive: the\n * fallback is the same config file it was always going to be.\n */\nimport type { BackendId } from \"@byollm/protocol\";\n\n/** Ports these servers take by default, with the name a person would know. */\nconst WELL_KNOWN: readonly { readonly port: number; readonly label: string }[] =\n Object.freeze([\n { port: 11434, label: \"Ollama\" },\n { port: 1234, label: \"LM Studio\" },\n { port: 8080, label: \"llama.cpp or MLX\" },\n { port: 8000, label: \"vLLM\" },\n { port: 5000, label: \"LocalAI\" },\n { port: 1337, label: \"Jan\" },\n ]);\n\nexport interface LocalServer {\n readonly label: string;\n readonly baseUrl: string;\n /** What it said it can serve. Empty is legal — some servers list nothing. */\n readonly models: readonly string[];\n /**\n * Which provider this actually is, when the server said so — B112.\n *\n * `label` is a guess from the port and always has been: it is what to print\n * beside an address, and printing is all it was ever asked to do. **Then it\n * became the only thing we knew**, so `pasteableService` and the picker\n * wrote `type: \"openai-http\"` for a server we had just identified — the one\n * config shape that cannot be started on demand, which is what B098 then\n * has to explain and offer to fix.\n *\n * `undefined` is not \"unknown provider\" in the vague sense. It is **we did\n * not verify one**, and the caller falls back to the generic transport,\n * which is exactly what it did before. A port map is a guess and a guess\n * must not decide what a service's type is; only an answer may.\n */\n readonly backendId?: BackendId;\n}\n\n/**\n * Every well-known port that answered, with what it offers.\n *\n * Probed in parallel with a short timeout: this runs while somebody is\n * watching a prompt, and six sequential connection refusals on a quiet machine\n * is a pause long enough to look broken.\n */\nexport async function probeLocalServers(\n timeoutMs = 1_500,\n fetchImpl: typeof fetch = fetch,\n): Promise<LocalServer[]> {\n const found = await Promise.all(\n WELL_KNOWN.map(({ port, label }) =>\n probeOne(\n `http://127.0.0.1:${String(port)}/v1`,\n label,\n timeoutMs,\n fetchImpl,\n ),\n ),\n );\n return found.filter((server): server is LocalServer => server !== undefined);\n}\n\nasync function probeOne(\n baseUrl: string,\n label: string,\n timeoutMs: number,\n fetchImpl: typeof fetch,\n): Promise<LocalServer | undefined> {\n const abort = new AbortController();\n const timer = setTimeout(() => {\n abort.abort();\n }, timeoutMs);\n try {\n const response = await fetchImpl(`${baseUrl}/models`, {\n signal: abort.signal,\n });\n if (!response.ok) return undefined;\n const body: unknown = await response.json();\n const identified = await identify(baseUrl, timeoutMs, fetchImpl);\n return {\n label: identified?.label ?? label,\n baseUrl,\n models: modelsFrom(body),\n ...(identified === undefined ? {} : { backendId: identified.id }),\n };\n } catch {\n // Refused, timed out, or answered something that is not JSON. All of them\n // mean the same thing to somebody setting up a laptop: nothing to offer\n // here. The detail belongs in `byollm services`, which is about a service\n // the owner has actually chosen.\n return undefined;\n } finally {\n clearTimeout(timer);\n }\n}\n\n/**\n * Who this server actually is, asked rather than inferred — B112.\n *\n * **Ollama only, and that is a scope rather than an oversight.** The point of\n * knowing the provider is that `startCommandFor` can start it, and `ollama` is\n * the only id that has a start command today. Identifying LM Studio would\n * produce a more specific `type` and change nothing a person can act on, so\n * the other five stay port guesses until somebody has the machine to write\n * their start command on — the way the login commands were done, one at a\n * time, by running them.\n *\n * `/api/version` is Ollama's own API rather than the OpenAI compatibility\n * layer, so nothing else on a well-known port answers it. Verified by running\n * it against a live Ollama: `{\"version\":\"0.30.8\"}`, 200.\n *\n * **Asked at whatever address answered, not at the port we expected.** Ollama\n * on 8080 would otherwise be labelled \"llama.cpp or MLX\" by the port map and\n * typed `ollama` by this — a screen contradicting itself. The answer wins for\n * both, which is what makes the label verified where it can be.\n */\nasync function identify(\n baseUrl: string,\n timeoutMs: number,\n fetchImpl: typeof fetch,\n): Promise<{ readonly id: BackendId; readonly label: string } | undefined> {\n const abort = new AbortController();\n const timer = setTimeout(() => {\n abort.abort();\n }, timeoutMs);\n try {\n const response = await fetchImpl(new URL(\"/api/version\", baseUrl), {\n signal: abort.signal,\n });\n if (!response.ok) return undefined;\n const body: unknown = await response.json();\n if (\n typeof body !== \"object\" ||\n body === null ||\n typeof (body as { version?: unknown }).version !== \"string\"\n ) {\n return undefined;\n }\n return { id: \"ollama\", label: \"Ollama\" };\n } catch {\n /* Not there, not Ollama, or not JSON. All of them mean the same thing: we\n did not learn a provider, so nobody may claim one. */\n return undefined;\n } finally {\n clearTimeout(timer);\n }\n}\n\n/**\n * Model ids out of an OpenAI-shaped `/v1/models` reply.\n *\n * Parsed defensively rather than cast: this is a response from a program\n * nobody here wrote, on a port anything could be listening to. A server that\n * answers 200 with a shape we did not expect is not an error worth failing\n * setup over — it is a server with no models to list, which is a legal answer.\n */\nfunction modelsFrom(body: unknown): string[] {\n if (typeof body !== \"object\" || body === null) return [];\n const data = (body as { data?: unknown }).data;\n if (!Array.isArray(data)) return [];\n return data\n .map((row) =>\n typeof row === \"object\" && row !== null\n ? (row as { id?: unknown }).id\n : undefined,\n )\n .filter((id): id is string => typeof id === \"string\" && id.length > 0);\n}\n","/**\n * Telling a supervisor that this device's configuration changed — B207.\n *\n * A running daemon reads its config once, at start, so a command that writes\n * config has changed a file and nothing else. On a laptop the answer is the\n * service pair (`byollm stop && byollm start`). **On a hosted box there is no\n * service**, and until now there was no other answer either: the box's\n * supervisor HANDLED `SIGHUP` and nothing in either repo ever sent it, so\n * B185's third duty was a listener with no speaker. The box worked only\n * because the supervisor retries a dead daemon every minute and the next retry\n * happened to read the new config.\n *\n * **Push, not poll, and the supervisor names itself rather than being\n * guessed at.** `BYOLLM_SUPERVISOR_PID` is set by the box supervisor in the\n * environment its console and daemon inherit. Off a box it is unset and every\n * function here says so and does nothing.\n *\n * Inferring instead would be the dangerous version: `process.ppid === 1` is\n * true for a console child on a box AND for anything run directly under init\n * on an ordinary Linux host, where `SIGHUP` to pid 1 is a signal to the\n * machine's init system. A caller states what it is; nobody deduces it.\n */\n\n/** The supervisor to tell, if this process is running under one. */\nexport function supervisorPid(\n env: NodeJS.ProcessEnv = process.env,\n): number | undefined {\n const raw = env[\"BYOLLM_SUPERVISOR_PID\"];\n if (raw === undefined) return undefined;\n const pid = Number(raw);\n /* A pid is a positive integer. Anything else is a variable somebody set by\n hand, and signalling a number we did not parse is how a typo becomes a\n signal to an unrelated process. */\n if (!Number.isInteger(pid) || pid <= 0) return undefined;\n return pid;\n}\n\n/**\n * What happened when we tried to tell it — three outcomes, not two.\n *\n * `absent` (there was nobody to tell) and `gone` (there was, and it is not\n * there any more) are different facts about the machine, and a caller that\n * folded them together would print the same sentence for \"you are on a laptop\"\n * and \"your box's supervisor has died\".\n */\nexport type ToldSupervisor = \"told\" | \"absent\" | \"gone\";\n\n/**\n * Signal the supervisor to reload, if there is one.\n *\n * `SIGHUP` because that is what the box supervisor already listens for, and\n * because the reload it performs drains in-flight jobs rather than killing\n * them — a person who just changed a service should not cost somebody else\n * the answer they were waiting on.\n */\nexport function tellSupervisor(\n /* Wrapped rather than passed as `process.kill` directly: detaching a method\n from its object is the shape lint objects to, and a test wants to hand in\n its own anyway. */\n kill: (pid: number, signal: string) => void = (pid, signal) => {\n process.kill(pid, signal);\n },\n env: NodeJS.ProcessEnv = process.env,\n /* Injected so the win32 branch is provable on the machines this is actually\n developed on. A branch that can only be exercised by the platform it\n guards against is a branch nobody checks. */\n platform: string = process.platform,\n): ToldSupervisor {\n const pid = supervisorPid(env);\n if (pid === undefined) return \"absent\";\n\n /**\n * Windows has no `SIGHUP`, and asking for one there is not a no-op.\n *\n * `process.kill(pid, \"SIGHUP\")` on win32 does not deliver a signal — it\n * **terminates the target process**. A supervised box is Linux, so this\n * cannot arise from our own code; it would take somebody setting the\n * variable by hand on a Windows machine, and the cost of being wrong is\n * killing whatever process that number happens to name.\n *\n * So the platform that cannot be told is treated as one with nobody to\n * tell, which is exactly what it is.\n */\n if (platform === \"win32\") return \"absent\";\n try {\n kill(pid, \"SIGHUP\");\n return \"told\";\n } catch {\n /* ESRCH — it is gone. Not this command's job to fix, and not something to\n throw over either: the config WAS written, which is what the caller\n asked for. */\n return \"gone\";\n }\n}\n\n/**\n * Whether this daemon is supervised, and whether it may ask a human — B213.\n *\n * **`isTTY` lies inside a box.** The Pod sets `tty: true` so a person can type\n * at the console, which makes `process.stdout.isTTY` true for every process in\n * that container — including the daemon the supervisor starts, which has no\n * human anywhere near it. So `interactive = process.stdout.isTTY` read TRUE on\n * a box, `run` took the preflight path, and `ask(\"Sign in to X now?\")` waited\n * for an answer that could never come. The event loop drained, Node exited 13,\n * the supervisor restarted it, and the box crash-looped — on the ordinary\n * onboarding order, setup then connect then sign in.\n *\n * The same line got `supervised` backwards for the same reason:\n * `!process.stdout.isTTY` is FALSE on a box, so the one daemon that certainly\n * IS supervised reported that it was not.\n *\n * **The supervisor already says so.** B207 has it state `BYOLLM_SUPERVISOR_PID`\n * into the environment of what it starts, so the answer is a fact rather than\n * an inference from a terminal that belongs to somebody else. `isTTY` remains\n * the answer where there is no supervisor — a person running `byollm run` in\n * their own shell.\n *\n * A function rather than two default parameters because the defaults were the\n * bug: every test passed `interactive` and `supervised` explicitly, so the\n * expressions that shipped were the one part nothing drove.\n */\nexport function howItRuns(\n env: NodeJS.ProcessEnv = process.env,\n isTty = process.stdout.isTTY,\n): { readonly supervised: boolean; readonly interactive: boolean } {\n if (supervisorPid(env) !== undefined) {\n /* Told, not guessed. A supervised daemon never asks, whatever the tty\n says, and it is always supervised. */\n return { supervised: true, interactive: false };\n }\n return { supervised: !isTty, interactive: isTty };\n}\n","import { z } from \"zod\";\nimport { LedgerWriter, readLedger } from \"./ledger.js\";\n\nconst SpendFile = z\n .object({\n version: z.literal(1),\n /** Per backend key: epoch-ms timestamps and the cents charged. */\n entries: z.record(\n z.string(),\n z.array(\n z.object({ at: z.number().int().positive(), cents: z.number().min(0) }),\n ),\n ),\n })\n .strict();\n\n/**\n * What the owner has spent running other people's work on a metered backend.\n *\n * byollm_007 §4: a widened metered backend must carry a ceiling and stop at\n * it. A ceiling nobody counts against is a comment, so this is the counting.\n *\n * **On accuracy, honestly.** Providers do not return a price, so this\n * estimates from token counts and an owner-supplied rate. It will not match an\n * invoice to the cent. It does not need to: its job is to stop a runaway, and\n * for that a defensible over-estimate is worth more than a precise number\n * arriving too late. The ceiling is a brake, not an accountant.\n */\nexport class SpendLedger {\n readonly #path: string;\n #entries: Record<string, { at: number; cents: number }[]> = {};\n #loaded = false;\n #untrusted: string | undefined;\n readonly #writer: LedgerWriter;\n\n constructor(path: string) {\n this.#path = path;\n this.#writer = new LedgerWriter(path);\n }\n\n /**\n * Read the ledger, and remember whether it could be read at all.\n *\n * This used to funnel every failure — unparseable JSON, wrong schema, an\n * I/O error — into the same empty object as a file that had never been\n * written, and the comment here promised a brake that `hasReachedCeiling`\n * did not apply. Zero spent is the *unsafe* reading: with a cap configured,\n * `0 >= cap` is false and the metered gate opens on exactly the state a\n * torn write produces.\n */\n async load(now: number): Promise<void> {\n const read = await readLedger(this.#path, SpendFile);\n this.#entries = read.state === \"loaded\" ? read.data.entries : {};\n this.#untrusted = read.state === \"untrusted\" ? read.why : undefined;\n this.#prune(now);\n this.#loaded = true;\n }\n\n /**\n * Why this ledger cannot be counted on, if it cannot — for `byollm status`.\n *\n * The owner has to be able to find out why their machine stopped taking\n * community work. A brake nobody can explain looks like a broken daemon.\n */\n untrustedReason(): string | undefined {\n return this.#untrusted;\n }\n\n /** Cents spent on community work for this backend in the last 24 hours. */\n spentTodayCents(backendKey: string, now: number): number {\n this.#assertLoaded();\n const since = now - 86_400_000;\n return (this.#entries[backendKey] ?? [])\n .filter((e) => e.at >= since)\n .reduce((sum, e) => sum + e.cents, 0);\n }\n\n /**\n * Has this backend spent its daily ceiling?\n *\n * A backend with no ceiling reads as reached — not as unlimited. That is\n * the safe direction and it matches the config rule: sharing without a\n * ceiling is refused at load, so reaching this state means something is\n * inconsistent and the brake should be on.\n */\n hasReachedCeiling(\n backendKey: string,\n capCents: number | undefined,\n now: number,\n ): boolean {\n /* An unreadable ledger reads as reached, whatever the cap says. The\n counts behind this number are gone, so the honest answer to \"how much\n has been spent\" is \"unknown\", and unknown spends nothing further of\n somebody else's money. Only community metered work consults this — the\n owner's own jobs never reach it, so a bookkeeping failure cannot brake\n the machine's own work. */\n if (this.#untrusted !== undefined) return true;\n if (capCents === undefined) return true;\n return this.spentTodayCents(backendKey, now) >= capCents;\n }\n\n /** Record an estimated charge for community work. */\n async record(backendKey: string, cents: number, now: number): Promise<void> {\n this.#assertLoaded();\n /* The latch. Writing here would replace an unreadable ledger with a file\n holding one entry and call it the day's total — destroying the evidence\n and releasing the brake in the same line. It clears on a clean load,\n which is what happens once the owner moves the bad file aside. */\n if (this.#untrusted !== undefined) return;\n (this.#entries[backendKey] ??= []).push({ at: now, cents });\n this.#prune(now);\n await this.#writer.write(() =>\n JSON.stringify({ version: 1, entries: this.#entries }),\n );\n }\n\n /** Everything spent per backend today, for `byollm status`. */\n summary(now: number): Record<string, number> {\n this.#assertLoaded();\n const out: Record<string, number> = {};\n for (const key of Object.keys(this.#entries)) {\n out[key] = this.spentTodayCents(key, now);\n }\n return out;\n }\n\n #assertLoaded(): void {\n if (!this.#loaded) throw new Error(\"spend ledger used before load()\");\n }\n\n /** Anything older than a day can never affect the window again. */\n #prune(now: number): void {\n const cutoff = now - 86_400_000;\n const pruned: Record<string, { at: number; cents: number }[]> = {};\n for (const [key, entries] of Object.entries(this.#entries)) {\n const kept = entries.filter((e) => e.at >= cutoff);\n if (kept.length > 0) pruned[key] = kept;\n }\n this.#entries = pruned;\n }\n}\n\n/**\n * Estimate the cost of a call in cents.\n *\n * Deliberately crude and deliberately generous: characters over four is a\n * rough token count, and the rate is whatever the owner said. Over-estimating\n * trips the brake early, which is the failure everyone prefers.\n */\nexport function estimateCents(\n promptChars: number,\n outputChars: number,\n centsPerMillionTokens: number,\n): number {\n const tokens = (promptChars + outputChars) / 4;\n return (tokens / 1_000_000) * centsPerMillionTokens;\n}\n\n/**\n * Cents, as money — one place, because four surfaces print this number.\n *\n * The consent ceremony said \"$25.00 a day\" and the `services` row said\n * \"2500c/day\" for the same ceiling, which made a person check whether they\n * were looking at the same figure. Surfaces sharing a value share its unit,\n * and the unit is the one the money is in.\n *\n * **Moved here from `cli.ts` when B100a made a fourth caller.** It lived\n * beside the command that printed it and its own comment said \"one place\";\n * the picker asks its cap question in dollars, so keeping it there would have\n * meant a copy inside the week that named the rule. It belongs with the\n * ledger that counts the cents.\n */\nexport function dollars(cents: number): string {\n return `$${(cents / 100).toFixed(2)}`;\n}\n","import { randomUUID } from \"node:crypto\";\nimport {\n closeSync,\n fsyncSync,\n mkdirSync,\n openSync,\n readFileSync,\n renameSync,\n rmSync,\n writeSync,\n} from \"node:fs\";\nimport { mkdir, open, readFile, rename, rm } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport type { z } from \"zod\";\n\n/**\n * What one attempt to read a ledger meant — byollm_016, 2026-09-03.\n *\n * Three states, because the two that used to be one are the whole bug. A\n * ledger that has never been written and a ledger that will not parse both\n * produced \"no entries\", and every counter above them read that as \"nothing\n * has happened yet\" — which is true for the first and the opposite of true\n * for the second. A torn write is how a machine manufactures the second from\n * the first, so this was reachable without an attacker: write, lose power,\n * restart, and the brake that had been counting is now counting from zero.\n *\n * **Missing is not none.** `fresh` is an answer. `untrusted` is a refusal to\n * answer, and callers have to treat it as one.\n */\nexport type LedgerRead<T> =\n | { readonly state: \"fresh\" }\n | { readonly state: \"loaded\"; readonly data: T }\n | { readonly state: \"untrusted\"; readonly why: string };\n\n/**\n * Why a ledger could not be trusted, in words for its owner.\n *\n * Never the file's content. Somebody debugging this is holding a ledger of\n * what their machine did for other people, and a diagnostic that quotes it\n * puts that in a log, a screenshot and a support thread.\n */\nfunction untrusted(why: string): { state: \"untrusted\"; why: string } {\n return { state: \"untrusted\", why };\n}\n\nfunction interpret<T>(\n raw: string,\n schema: z.ZodType<T>,\n path: string,\n): LedgerRead<T> {\n let json: unknown;\n try {\n json = JSON.parse(raw);\n } catch {\n return untrusted(`${path} is not valid JSON`);\n }\n const parsed = schema.safeParse(json);\n return parsed.success\n ? { state: \"loaded\", data: parsed.data }\n : untrusted(`${path} is not a ledger this version can read`);\n}\n\n/**\n * ENOENT is the only good reason for a ledger to be absent.\n *\n * Everything else — a permission change, an I/O error, a directory where the\n * file should be — is the disk declining to tell us what it holds, which is\n * not the same as it holding nothing.\n */\nfunction readFailure(\n error: unknown,\n path: string,\n): LedgerRead<never> | \"fresh\" {\n const code = (error as NodeJS.ErrnoException | null)?.code;\n if (code === \"ENOENT\") return \"fresh\";\n return untrusted(`${path} could not be read (${code ?? \"unknown error\"})`);\n}\n\nexport async function readLedger<T>(\n path: string,\n schema: z.ZodType<T>,\n): Promise<LedgerRead<T>> {\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch (error) {\n const failure = readFailure(error, path);\n return failure === \"fresh\" ? { state: \"fresh\" } : failure;\n }\n return interpret(raw, schema, path);\n}\n\n/** The same reading, for the one caller that cannot await — see `SpentGrants`. */\nexport function readLedgerSync<T>(\n path: string,\n schema: z.ZodType<T>,\n): LedgerRead<T> {\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch (error) {\n const failure = readFailure(error, path);\n return failure === \"fresh\" ? { state: \"fresh\" } : failure;\n }\n return interpret(raw, schema, path);\n}\n\n/**\n * Replace a ledger without ever being halfway through replacing it.\n *\n * The pairing file's pattern, with a sync added. Writing to the live path is\n * what let an interrupted write produce the corrupt file the reader above now\n * refuses — the failure and its trigger were the same line, in all three\n * ledgers.\n *\n * A unique temp name per attempt: two writers sharing one is how the identity\n * file's first fix broke its own race test. `fsync` before the rename because\n * what is wanted here is crash durability and not merely atomic visibility —\n * a rename can land while the bytes it names are still in a cache. The\n * directory sync that follows is best-effort: it is what makes the rename\n * itself survive, and Windows has no equivalent to attempt.\n */\nexport async function writeLedger(path: string, body: string): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n const temp = `${path}.${randomUUID()}.tmp`;\n try {\n const handle = await open(temp, \"wx\", 0o600);\n try {\n await handle.writeFile(body, \"utf8\");\n await handle.sync();\n } finally {\n await handle.close();\n }\n await rename(temp, path);\n } catch (error) {\n await rm(temp, { force: true });\n throw error;\n }\n await syncDirectory(dirname(path));\n}\n\n/** The same write, synchronously, for the burn that must precede a job. */\nexport function writeLedgerSync(path: string, body: string): void {\n mkdirSync(dirname(path), { recursive: true });\n const temp = `${path}.${randomUUID()}.tmp`;\n try {\n const fd = openSync(temp, \"wx\", 0o600);\n try {\n writeSync(fd, body);\n fsyncSync(fd);\n } finally {\n closeSync(fd);\n }\n renameSync(temp, path);\n } catch (error) {\n rmSync(temp, { force: true });\n throw error;\n }\n syncDirectorySync(dirname(path));\n}\n\n/* Best-effort by design: a platform that will not let us open a directory has\n nothing to offer here, and failing the write over it would turn a durability\n nicety into an outage. The bytes are already synced and the rename has\n already happened. */\nasync function syncDirectory(path: string): Promise<void> {\n try {\n const handle = await open(path, \"r\");\n try {\n await handle.sync();\n } finally {\n await handle.close();\n }\n } catch {\n // Windows, and any filesystem that refuses a directory handle.\n }\n}\n\nfunction syncDirectorySync(path: string): void {\n try {\n const fd = openSync(path, \"r\");\n try {\n fsyncSync(fd);\n } finally {\n closeSync(fd);\n }\n } catch {\n // As above.\n }\n}\n\n/**\n * One writer per ledger, so a slow rename cannot land on a fast one.\n *\n * Found by CW's rolling review, and it reproduces: twenty-five concurrent\n * `record()` calls left twenty-one on disk. Nothing here is thread-unsafe —\n * the loss is in the awaits. Each call serialises its own snapshot and then\n * suspends across `open`, `write`, `sync` and `rename`, so two calls can\n * finish out of order and the *earlier* snapshot wins. The entries between\n * them are gone.\n *\n * That is a brake under-counting what it was built to count, which is the\n * unsafe direction.\n *\n * The body is a thunk rather than a string, and that is the other half: taken\n * after the previous write has landed, so a queued write serialises current\n * state rather than the state its caller saw. A queue of stale snapshots\n * would order the writes and still lose the entries.\n */\nexport class LedgerWriter {\n readonly #path: string;\n readonly #sink: (path: string, body: string) => Promise<void>;\n #tail: Promise<unknown> = Promise.resolve();\n\n /**\n * The write itself is injectable, and only the tests pass one.\n *\n * Not decoration: the first test written for this asserted the *symptom* —\n * twenty-five concurrent records, twenty-five entries on disk — and passed\n * with the serialisation removed. It reproduces on a real filesystem and it\n * did not reproduce under the runner, which makes it a test that would have\n * gone green in CI on the broken code. A seam that cannot be driven can\n * only be tested by luck.\n */\n constructor(\n path: string,\n sink: (path: string, body: string) => Promise<void> = writeLedger,\n ) {\n this.#path = path;\n this.#sink = sink;\n }\n\n write(body: () => string): Promise<void> {\n const next = this.#tail.then(() => this.#sink(this.#path, body()));\n // The chain must survive a failed write, or one rejection stops every\n // later one. Callers still see their own failure through `next`.\n this.#tail = next.catch(() => undefined);\n return next;\n }\n}\n","import { z } from \"zod\";\nimport type { CommunityBudget } from \"./config.js\";\nimport { LedgerWriter, readLedger } from \"./ledger.js\";\n\nconst BudgetFile = z\n .object({\n version: z.literal(1),\n /** Epoch-ms timestamps of community jobs accepted, newest last. */\n accepted: z.array(z.number().int().positive()),\n })\n .strict();\n\nexport type BudgetRefusal =\n \"hourly-cap\" | \"daily-cap\" | \"payload-too-large\" | \"ledger-untrusted\";\n\nexport type BudgetDecision =\n | { readonly ok: true }\n | {\n readonly ok: false;\n readonly refusal: BudgetRefusal;\n readonly detail: string;\n };\n\n/**\n * The owner's ceiling on work done for other people\n * ({@link MUSTS.COMMUNITY_BUDGETS}).\n *\n * byollm_004 §4 distinguishes two directions of abuse, and this is the one\n * aimed *at* the volunteer: a stranger who can enqueue unlimited `public`\n * jobs owns your GPU. Jobs for the machine's own owner are never counted —\n * their machine, their call.\n */\nexport class Budgets {\n readonly #path: string;\n readonly #limits: CommunityBudget;\n #accepted: number[] = [];\n #loaded = false;\n #untrusted: string | undefined;\n readonly #writer: LedgerWriter;\n\n constructor(path: string, limits: CommunityBudget) {\n this.#path = path;\n this.#limits = limits;\n this.#writer = new LedgerWriter(path);\n }\n\n /**\n * Read the record of what has already been accepted.\n *\n * Every failure used to become an empty array, which reconstructs the\n * counts as zero and passes both caps — the file that says \"you have\n * already run your hundred jobs today\" and the file that will not parse\n * were the same answer.\n */\n async load(now: number): Promise<void> {\n const read = await readLedger(this.#path, BudgetFile);\n this.#accepted = read.state === \"loaded\" ? [...read.data.accepted] : [];\n this.#untrusted = read.state === \"untrusted\" ? read.why : undefined;\n this.#prune(now);\n this.#loaded = true;\n }\n\n /** Why community work is braked, if it is — for `byollm status`. */\n untrustedReason(): string | undefined {\n return this.#untrusted;\n }\n\n /**\n * May this community job run?\n *\n * @param payloadChars - total payload text length, checked against the\n * stricter community limit rather than the protocol's absolute ceiling.\n */\n check(now: number, payloadChars: number): BudgetDecision {\n if (!this.#loaded) throw new Error(\"budgets used before load()\");\n /* Before the counts, because the counts are the thing in doubt. Refusing\n is a nuisance for strangers and costs the owner nothing: only work for\n other people is checked here, and their own jobs never reach it. */\n if (this.#untrusted !== undefined) {\n return {\n ok: false,\n refusal: \"ledger-untrusted\",\n detail:\n \"this device cannot read its record of community work, so it is \" +\n \"not accepting any until that is fixed\",\n };\n }\n this.#prune(now);\n\n if (payloadChars > this.#limits.maxPayloadChars) {\n return {\n ok: false,\n refusal: \"payload-too-large\",\n detail:\n `community jobs are limited to ${String(this.#limits.maxPayloadChars)} ` +\n `characters; this one is ${String(payloadChars)}`,\n };\n }\n\n const hour = this.#countSince(now - 3_600_000);\n if (hour >= this.#limits.maxJobsPerHour) {\n return {\n ok: false,\n refusal: \"hourly-cap\",\n detail: `already ran ${String(hour)} community jobs in the last hour`,\n };\n }\n\n const day = this.#countSince(now - 86_400_000);\n if (day >= this.#limits.maxJobsPerDay) {\n return {\n ok: false,\n refusal: \"daily-cap\",\n detail: `already ran ${String(day)} community jobs today`,\n };\n }\n return { ok: true };\n }\n\n /** Count a community job as accepted. Call only after {@link check} passes. */\n async record(now: number): Promise<void> {\n // The latch: an untrusted ledger is not overwritten with a count of one.\n if (this.#untrusted !== undefined) return;\n this.#accepted.push(now);\n this.#prune(now);\n await this.#writer.write(() =>\n JSON.stringify({ version: 1, accepted: this.#accepted }),\n );\n }\n\n /** Current usage, for `byollm status`. */\n usage(now: number): { hour: number; day: number; limits: CommunityBudget } {\n this.#prune(now);\n return {\n hour: this.#countSince(now - 3_600_000),\n day: this.#countSince(now - 86_400_000),\n limits: this.#limits,\n };\n }\n\n #countSince(since: number): number {\n return this.#accepted.filter((at) => at >= since).length;\n }\n\n /** Anything older than a day can never affect either window again. */\n #prune(now: number): void {\n const cutoff = now - 86_400_000;\n this.#accepted = this.#accepted.filter((at) => at >= cutoff);\n }\n}\n","import {\n type ResultDisposition,\n type SealedEnvelope,\n ClaimResponse,\n FetchResponse,\n type PublicIdentity,\n HeartbeatResponse,\n PROTOCOL_VERSION,\n PairPollResponse,\n PairStartResponse,\n ReleaseResponse,\n ResultResponse,\n WireError,\n type Capability,\n type WithheldKind,\n type Endpoint,\n} from \"@byollm/protocol\";\nimport { type z } from \"zod\";\n\n/**\n * Why a protocol call failed, from the daemon's seat.\n *\n * byollm_002 requires that \"server unreachable\", \"revoked\", \"no matching\n * work\" and \"backend down\" never share a message. Three of those are\n * distinguishable here; the fourth is a local condition the loop reports\n * itself. \"No matching work\" is deliberately *not* an error — it is a `200`\n * with an empty list.\n */\nexport type ClientErrorKind =\n | \"unreachable\"\n | \"revoked\"\n | \"unauthorized\"\n | \"rejected\"\n /**\n * The server does not speak our protocol version. Its own kind because it\n * is the one refusal a retry can never fix and an upgrade always can — and\n * because a daemon that reports it as a generic rejection sends its owner\n * looking at their network.\n */\n | \"version-unsupported\"\n /**\n * The job is over, and asking again cannot change that.\n *\n * Its own kind because it arrives on the SAME 409 as `not-ready`, whose\n * instruction is the opposite — that one says keep asking, this one says\n * stop. The protocol's own note on `too-late` says a daemon \"must stop\n * rather than retry\"; mapped by status, this daemon did the retrying,\n * polling a finished job until the payload deadline and then releasing a\n * lease on work that had already ended.\n *\n * Distinct from `rejected` too, which is \"no such job for this device\".\n * The remedy is the same — stop — and the sentence in the log is not, and\n * a device that says \"no such job\" about a job it just finished is a\n * device somebody goes looking for a bug in.\n */\n | \"too-late\"\n /**\n * This machine's clock is too far from the upstream's.\n *\n * Its own kind for the same reason `version-unsupported` has one: a retry\n * can never fix it and one command always can. Reported as a generic\n * `unauthorized` it sends its owner looking at their keys or their pairing,\n * neither of which is wrong — the signature was probably fine, and the\n * timestamp inside it was not.\n */\n | \"clock-skew\"\n /**\n * The upstream holds this job for us but has no payload to give yet.\n *\n * Only reachable off the direct plane. A direct site seals when asked,\n * because it holds the keys; a relay must wait for the site to seal to the\n * device that claimed — so \"not yet\" is a normal answer there, and treating\n * it as a refusal drops work the daemon legitimately still holds.\n */\n | \"not-ready\"\n /**\n * The upstream knows exactly who this daemon is, and is refusing anyway.\n *\n * Distinct from `unauthorized` (it does not know us) and from `revoked`\n * (this pairing is over). A device asking about a job it does not hold, or\n * an upstream that does not route for the site named, is neither of those —\n * and this client used to report all three as `revoked`, because it\n * dispatched on the 403 rather than on the code beside it. A daemon told it\n * was revoked stops for good; that is the wrong response to a refusal that\n * is about one request.\n */\n | \"forbidden\"\n | \"rate-limited\"\n | \"server-error\"\n | \"malformed-response\";\n\nexport class ClientError extends Error {\n override readonly name = \"ClientError\";\n constructor(\n readonly kind: ClientErrorKind,\n message: string,\n /** Seconds the server asked us to wait, when it said. */\n readonly retryAfter?: number,\n ) {\n super(message);\n }\n\n /** Is retrying this same call plausibly useful? */\n get retryable(): boolean {\n return (\n this.kind === \"unreachable\" ||\n this.kind === \"not-ready\" ||\n this.kind === \"rate-limited\" ||\n this.kind === \"server-error\"\n );\n }\n}\n\nexport interface ClientOptions {\n /** The app's origin, e.g. `https://app.example.com`. */\n readonly origin: string;\n /** Bearer token from pairing. Absent while pairing. */\n /**\n * How this daemon proves who it is (byollm_009 §4.2).\n *\n * A signer rather than a key, so the client never holds private material\n * and the daemon decides where keys live. Absent while pairing, which is\n * the one exchange that establishes an identity rather than using one.\n */\n readonly identity?: {\n readonly runnerId: string;\n sign(input: {\n endpoint: string;\n runnerId: string;\n issuedAt: number;\n body: string;\n }): Promise<string> | string;\n };\n /** Per-request timeout. */\n readonly timeoutMs?: number;\n /** Injectable fetch, for tests. */\n readonly fetch?: typeof fetch;\n}\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n/**\n * The daemon's outbound protocol client.\n *\n * Every call is outbound; nothing here ever listens. That is the whole\n * network posture of the product, and it lives in this one class.\n */\nexport class ProtocolClient {\n readonly #origin: string;\n readonly #identity: ClientOptions[\"identity\"];\n readonly #timeoutMs: number;\n readonly #fetch: typeof fetch;\n\n constructor(options: ClientOptions) {\n this.#origin = options.origin.replace(/\\/+$/, \"\");\n this.#identity = options.identity;\n this.#timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.#fetch = options.fetch ?? globalThis.fetch;\n }\n\n /** A client for the same origin that signs as a given runner. */\n withIdentity(\n identity: NonNullable<ClientOptions[\"identity\"]>,\n ): ProtocolClient {\n return new ProtocolClient({\n origin: this.#origin,\n identity,\n timeoutMs: this.#timeoutMs,\n fetch: this.#fetch,\n });\n }\n\n get origin(): string {\n return this.#origin;\n }\n\n async pairStart(input: {\n version: string;\n label: string;\n platform: \"darwin\" | \"linux\" | \"win32\";\n /** This machine's public keys (byollm_009 §5). */\n device: PublicIdentity;\n capabilities: readonly Capability[];\n }): Promise<PairStartResponse> {\n return this.#post(\"pair\", PairStartResponse, {\n protocolVersion: PROTOCOL_VERSION,\n action: \"start\",\n device: input.device,\n daemon: {\n version: input.version,\n label: input.label,\n platform: input.platform,\n },\n capabilities: input.capabilities,\n });\n }\n\n async pairPoll(deviceCode: string): Promise<PairPollResponse> {\n return this.#post(\"pair\", PairPollResponse, {\n protocolVersion: PROTOCOL_VERSION,\n action: \"poll\",\n deviceCode,\n });\n }\n\n /**\n * Collect the payload for a lease this daemon holds (byollm_009 §6).\n *\n * The second half of claim-then-fetch: a claim answers with a stub, and the\n * work is collected by the device that took it.\n */\n async fetch(input: {\n runnerId: string;\n jobId: string;\n leaseId: string;\n }): Promise<FetchResponse> {\n return this.#post(\"fetch\", FetchResponse, {\n protocolVersion: PROTOCOL_VERSION,\n runnerId: input.runnerId,\n jobId: input.jobId,\n leaseId: input.leaseId,\n });\n }\n\n async claim(input: {\n runnerId: string;\n capabilities: readonly Capability[];\n max: number;\n }): Promise<ClaimResponse> {\n return this.#post(\"claim\", ClaimResponse, {\n protocolVersion: PROTOCOL_VERSION,\n runnerId: input.runnerId,\n capabilities: input.capabilities,\n max: input.max,\n });\n }\n\n async heartbeat(input: {\n runnerId: string;\n daemonVersion: string;\n capabilities: readonly Capability[];\n /** Kinds this device could serve and is withholding — byollm_016. */\n withheld?: readonly WithheldKind[];\n activeLeases: readonly { jobId: string; leaseId: string }[];\n }): Promise<HeartbeatResponse> {\n return this.#post(\"heartbeat\", HeartbeatResponse, {\n protocolVersion: PROTOCOL_VERSION,\n runnerId: input.runnerId,\n daemonVersion: input.daemonVersion,\n capabilities: input.capabilities,\n withheld: input.withheld ?? [],\n activeLeases: input.activeLeases,\n });\n }\n\n async result(input: {\n runnerId: string;\n jobId: string;\n /** The grant the work was done under — cloud_008 §1.4a. */\n leaseId: string;\n envelope: SealedEnvelope;\n disposition: ResultDisposition;\n }): Promise<ResultResponse> {\n return this.#post(\"result\", ResultResponse, {\n protocolVersion: PROTOCOL_VERSION,\n ...input,\n });\n }\n\n async release(input: {\n runnerId: string;\n leases: readonly { jobId: string; leaseId: string }[];\n reason: \"shutdown\" | \"pause\" | \"revoked\" | \"backend-down\" | \"refused\";\n }): Promise<ReleaseResponse> {\n return this.#post(\"release\", ReleaseResponse, {\n protocolVersion: PROTOCOL_VERSION,\n runnerId: input.runnerId,\n leases: input.leases,\n reason: input.reason,\n });\n }\n\n async #post<T>(\n endpoint: Endpoint,\n schema: z.ZodType<T>,\n body: unknown,\n ): Promise<T> {\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n };\n\n // Serialise once and sign exactly those bytes. Signing a re-serialised\n // copy would sign something the server never receives.\n const rawBody = JSON.stringify(body);\n if (this.#identity !== undefined) {\n const issuedAt = Date.now();\n headers[\"x-byollm-runner\"] = this.#identity.runnerId;\n headers[\"x-byollm-issued-at\"] = String(issuedAt);\n headers[\"x-byollm-signature\"] = await this.#identity.sign({\n endpoint,\n runnerId: this.#identity.runnerId,\n issuedAt,\n body: rawBody,\n });\n }\n\n let response: Response;\n try {\n response = await this.#fetch(`${this.#origin}/byollm/${endpoint}`, {\n method: \"POST\",\n headers,\n body: rawBody,\n redirect: \"error\",\n signal: AbortSignal.timeout(this.#timeoutMs),\n });\n } catch (error) {\n // No response at all: the server is unreachable. Distinct from every\n // answer it could have given us, including a refusal.\n throw new ClientError(\n \"unreachable\",\n `could not reach ${this.#origin} (${error instanceof Error ? error.message : \"unknown error\"})`,\n );\n }\n\n const text = await response.text();\n let parsed: unknown;\n try {\n parsed = text === \"\" ? {} : JSON.parse(text);\n } catch {\n throw new ClientError(\n \"malformed-response\",\n `${this.#origin} returned HTTP ${String(response.status)} with a body that is not JSON`,\n );\n }\n\n if (!response.ok) {\n throw this.#toError(response, parsed);\n }\n\n const result = schema.safeParse(parsed);\n if (!result.success) {\n // A server whose response does not match the protocol is not one we can\n // safely guess about.\n throw new ClientError(\n \"malformed-response\",\n `${this.#origin} returned a ${endpoint} response that does not match protocol v${PROTOCOL_VERSION}`,\n );\n }\n return result.data;\n }\n\n #toError(response: Response, body: unknown): ClientError {\n const wire = WireError.safeParse(body);\n const retryAfterHeader = response.headers.get(\"retry-after\");\n const retryAfter =\n wire.success && wire.data.retryAfter !== undefined\n ? wire.data.retryAfter\n : retryAfterHeader !== null && /^\\d+$/.test(retryAfterHeader)\n ? Number(retryAfterHeader)\n : undefined;\n\n const message = wire.success\n ? wire.data.message\n : `${this.#origin} returned HTTP ${String(response.status)}`;\n\n if (wire.success && wire.data.error === \"revoked\") {\n return new ClientError(\"revoked\", message, retryAfter);\n }\n if (\n typeof body === \"object\" &&\n body !== null &&\n (body as { error?: unknown }).error === \"unsupported-protocol-version\"\n ) {\n // The server already composed a message naming the fix; pass it through\n // rather than paraphrasing it into something vaguer.\n return new ClientError(\"version-unsupported\", message, retryAfter);\n }\n if (\n typeof body === \"object\" &&\n body !== null &&\n (body as { error?: unknown }).error === \"too-late\"\n ) {\n /* Before the status switch, because `too-late` and `not-ready` share a\n 409 and carry opposite instructions. The code is what a caller acts\n on; the status is the class of the problem. */\n return new ClientError(\"too-late\", message, retryAfter);\n }\n if (\n typeof body === \"object\" &&\n body !== null &&\n (body as { error?: unknown }).error === \"daemon-below-floor\"\n ) {\n /**\n * B052. Named rather than left to the status code, for the reason the\n * revocation branch above gives: without this, a floor refusal arrives\n * as whatever HTTP status carried it — a 403 reads as `forbidden`,\n * which this daemon reports as a permission problem and retries\n * against forever. It is neither. It is a version problem with a\n * one-line fix, and the fix is already in the message.\n */\n return new ClientError(\"version-unsupported\", message, retryAfter);\n }\n if (\n typeof body === \"object\" &&\n body !== null &&\n (body as { error?: unknown }).error === \"clock-skew\"\n ) {\n // The upstream sends its own time, so the message can name the drift\n // rather than the symptom. A number a person can act on beats a\n // sentence they have to interpret.\n const serverTime = (body as { serverTime?: unknown }).serverTime;\n const drift =\n typeof serverTime === \"number\"\n ? ` This device is ${describeDrift(Date.now() - serverTime)}.`\n : \"\";\n return new ClientError(\n \"clock-skew\",\n `${message}.${drift} ${syncTimeCommand()}`,\n retryAfter,\n );\n }\n switch (response.status) {\n case 401:\n return new ClientError(\"unauthorized\", message, retryAfter);\n case 403:\n // `revoked` is decided by the code above, never by the status —\n // cloud_008 §1.4d. Every 403 used to land here and be reported as a\n // revocation, so a site-plane refusal about a single request looked\n // to a daemon exactly like its pairing being torn up.\n return new ClientError(\"forbidden\", message, retryAfter);\n case 409:\n return new ClientError(\"not-ready\", message, retryAfter);\n case 429:\n return new ClientError(\"rate-limited\", message, retryAfter);\n case 400:\n case 404:\n // Never retried: the request is wrong, and repeating it stays wrong.\n return new ClientError(\"rejected\", message, retryAfter);\n default:\n return response.status >= 500\n ? new ClientError(\"server-error\", message, retryAfter)\n : new ClientError(\"rejected\", message, retryAfter);\n }\n }\n}\n\n/**\n * How far off, in words somebody can act on.\n *\n * \"7 minutes ahead\" tells a person to look at their clock. \"clock skew\n * detected\" tells them to search for the phrase.\n */\n/**\n * How to fix it, for the machine this is running on.\n *\n * The upstream can say *how far off* — it knows its own time. It cannot say\n * *what to run*, because it has no idea what this machine is. So the sentence\n * is composed here, which is byollm_013's rule about which side names the fix.\n *\n * Turning on time sync rather than setting the clock once: a clock that\n * drifted far enough to be refused will drift again, and `date -s` fixes today\n * only. These are the commands that make it stop happening.\n */\nfunction syncTimeCommand(): string {\n switch (process.platform) {\n case \"darwin\":\n return \"Turn on network time: `sudo sntp -sS time.apple.com`, or System Settings → General → Date & Time → Set automatically.\";\n case \"win32\":\n return \"Turn on network time: `w32tm /resync`, or Settings → Time & language → Set time automatically.\";\n default:\n return \"Turn on network time: `sudo timedatectl set-ntp true` (or install chrony/ntpd).\";\n }\n}\n\nfunction describeDrift(ms: number): string {\n const seconds = Math.round(Math.abs(ms) / 1000);\n const amount =\n seconds < 120\n ? `${String(seconds)} seconds`\n : `${String(Math.round(seconds / 60))} minutes`;\n return `${amount} ${ms > 0 ? \"ahead of\" : \"behind\"} the server`;\n}\n","import { readHealth, writeHealth } from \"./health.js\";\n\n/**\n * What a revoked device says, and where the person is sent — ruled\n * 2026-09-03.\n *\n * One definition, because three surfaces print it and they disagreed once\n * already. Todd revoked a machine and then asked three of them what was\n * wrong: `status` said \"state: running\" above \"service: NOT running\",\n * `install` said \"retry: byollm install\", and the service log said \"No apps\n * are paired yet\". Three answers, none of them revoked, and one of them\n * actively wrong — retrying an install cannot fix a revocation.\n *\n * **A remedy must match the cause.** Re-pairing is the fix, so re-pairing is\n * what every one of these says.\n */\nexport const REVOKED_SENTENCE = \"this device was revoked — re-pair to return\";\n\n/**\n * What happens after the remedy, which nobody was told — ruled 2026-09-03.\n *\n * `connect` said \"paired\" and the machine stayed dark, so the remedy looked\n * like it had failed. Two different things carry it back, and both are\n * automatic: a parked daemon polls the mark and promotes itself, and a daemon\n * that is not up is restarted by a supervisor holding `KeepAlive` — so the\n * one instruction that used to follow this line, `byollm install`, was work\n * nobody needed to do.\n *\n * Said out loud because a person who sees nothing happen reaches for the\n * reinstall on their own. The wait is bounded and short; the sentence is\n * here so the waiting is expected rather than alarming.\n */\nexport const REVOKED_RETURN =\n \"the installed service returns by itself once you do — no reinstall\";\n\n/**\n * The remedy line, in the shape the CLI prints elsewhere.\n *\n * `returnsByItself` is asked rather than assumed, because the promise is only\n * true where something is holding the daemon up. A person running `byollm\n * run` in their own terminal has no service to come back on its own, and\n * telling them one exists would send them looking for a machine that is never\n * going to light up. **A promise belongs to the party that can keep it.**\n */\nexport function revokedRemedy(origin?: string, returnsByItself = true): string {\n return (\n ` re-pair: byollm connect${origin === undefined ? \"\" : ` ${origin}`}\\n` +\n (returnsByItself\n ? ` ${REVOKED_RETURN}.\\n`\n : /* The restart step, on the one branch that needs one — rider closed\n 2026-09-04.\n \n The original rider asked for `byollm install` back in the remedy.\n It is not the answer: a supervised daemon polls its mark and\n promotes itself, and a reinstall was work nobody needed to do. But\n the branch where nothing self-returns was left with a re-pair step\n and no next step at all — somebody running the daemon in their own\n terminal, whose process has already exited by the time they read\n this. Their restart is the command they typed, not an install. */\n ` then: byollm run\\n`)\n );\n}\n\n/**\n * Was this device revoked, according to the last daemon that ran?\n *\n * Read from the health file rather than from the pairing, and that is\n * deliberate: the pairing may be absent for reasons that have nothing to do\n * with revocation, and the exit path has to tell those apart —\n * empty-because-revoked and empty-because-never-paired are different facts\n * with different remedies.\n *\n * `undefined` means \"no daemon has said\", which is not the same as \"not\n * revoked\" and is exactly what a machine looks like before its first run. The\n * callers here can treat it as not-revoked, because every one of them is\n * answering \"should I say the revoked sentence\" and the honest answer with no\n * evidence is no.\n */\nexport async function revokedMark(\n healthPath: string,\n): Promise<{ at: number; origin: string } | undefined> {\n return (await readHealth(healthPath))?.revoked;\n}\n\n/**\n * Forget the mark, because the thing it names has been fixed.\n *\n * Called when a pairing succeeds. Re-pairing is the remedy this whole file\n * points at, so a mark that survived it would leave every surface saying\n * \"revoked — re-pair to return\" to somebody who just did.\n *\n * Reads and rewrites rather than deleting the file: the rest of the record is\n * the daemon's account of its upstream and is nobody's to throw away here.\n * Absent or unreadable is nothing to clear, which is the ordinary case on a\n * machine that has never been revoked.\n */\nexport async function clearRevokedMark(healthPath: string): Promise<void> {\n const health = await readHealth(healthPath);\n if (health?.revoked === undefined) return;\n const { revoked: _gone, ...rest } = health;\n await writeHealth(healthPath, rest);\n}\n","/**\n * The one sentence that says \"go and prove it works\" — ruled 2026-09-02.\n *\n * ## Why it lives in the terminal\n *\n * The dashboard's approved banner used to say the device would start taking\n * work within a few seconds, and an earlier ruling would have put a test link\n * beside it. Both were wrong at the moment they rendered: `byollm setup` asks\n * \"Run in background?\" *after* the pairing ceremony — the correct order, since\n * install must never precede pairing — so at approval time the daemon may not\n * be installed or running at all.\n *\n * **A promise belongs to the party that can keep it.** The web knows one fact,\n * that the device was approved. Only this process watches the install succeed,\n * so only this process may say the device is ready to test.\n *\n * ## Why it is a constant\n *\n * Two callers print it: the end of `byollm setup`, and `byollm start` on its\n * own — the same moment reached two ways, and somebody who installs standalone\n * has exactly as much reason to be told. Written out twice it would be two\n * sentences within a month, and the walk that found this found it by reading\n * the words.\n *\n * \"Connect BYOLLM\" rather than \"the BYOLLM button\", because that is the label\n * on the control. The earlier wording sent people looking for something with a\n * different name on it.\n */\nexport const TEST_YOUR_DEVICE =\n \"TEST YOUR DEVICE: Visit https://test.byollm.cloud and press the \" +\n \"Connect BYOLLM button.\";\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport {\n BACKEND_IDS,\n backendDescriptor,\n backendName,\n type BackendId,\n} from \"@byollm/protocol\";\nimport { startCommandFor } from \"./local-server.js\";\n\nconst run = promisify(execFile);\n\n/**\n * Why a route is not healthy, in terms somebody can act on — cloud_002.\n *\n * `byollm backends` said \"0 of 2 routes are healthy\" and left the reader to\n * work out why. Todd hit it on a fresh install: the default config points at\n * Ollama's port, he does not run Ollama, and nothing on screen connected those\n * two facts. The ruling is **detection-first over auto-start** — the daemon\n * says which of three things is true rather than starting a server nobody\n * asked for:\n *\n * - **not installed** — the tool that usually listens there is not on PATH.\n * - **not running** — it is installed and nothing is listening.\n * - **wrong port** — something answered, but not as a model server.\n *\n * Each answer carries the command that fixes it. A diagnosis without a next\n * step is a more precise way of being stuck.\n *\n * ## Why this is a heuristic, and why that is honest\n *\n * A base URL does not say which server is behind it — `openai-http` at 11434\n * is *probably* Ollama because that is its default port and the daemon's own\n * default config, but somebody may have put anything there. So the wording is\n * \"usually\" rather than \"is\", and the port table is small and explicit rather\n * than clever. Being approximately right and saying so beats being silent.\n */\n\n/** The tool that conventionally listens on a port, and how to get it going. */\ninterface Suspect {\n readonly name: string;\n /** Binary to look for on PATH. */\n readonly binary: string;\n /** What to run when it is installed but not listening. */\n readonly start: string;\n /** What to run when it is not installed at all. */\n readonly install: string;\n}\n\nconst BY_PORT: Record<string, Suspect> = {\n \"11434\": {\n name: \"Ollama\",\n binary: \"ollama\",\n start: \"ollama serve # then: ollama pull llama3.2\",\n install: \"brew install ollama && ollama pull llama3.2\",\n },\n \"8080\": {\n name: \"MLX or llama.cpp\",\n binary: \"mlx_lm.server\",\n start: \"mlx_lm.server --model <model> --port 8080\",\n install: \"pip install mlx-lm\",\n },\n \"8000\": {\n name: \"vLLM\",\n binary: \"vllm\",\n start: \"vllm serve <model> --port 8000\",\n install: \"pip install vllm\",\n },\n \"1234\": {\n name: \"LM Studio\",\n binary: \"lms\",\n start: \"start LM Studio and turn on its local server\",\n install: \"install LM Studio from lmstudio.ai\",\n },\n};\n\n/**\n * Is a binary on PATH? Never throws — an unknown answer is \"cannot tell\".\n *\n * Injectable, and that is not only for coverage: a test that shells out to\n * `which` asserts something about the machine it runs on, so the same suite\n * would print different advice on a laptop with Ollama installed than in CI\n * without it. The probe is the environment; the sentence is the unit.\n */\ntype PathProbe = (binary: string) => Promise<boolean | undefined>;\n\n/**\n * What is actually on the port — asked, because nobody was passing the answer.\n *\n * This used to be inferred from a `detail` string: *\"if the health message\n * matches /ECONNREFUSED|fetch failed/ then nothing is listening\"*. **The only\n * caller in the repository never passed `detail`**, so every unhealthy route\n * took the `undefined` branch and every diagnosis began \"Nothing is listening\n * on …\" — including for a server that was up and simply did not have the\n * configured model, which is what `byollm services` shows when\n * `health.models` omits it.\n *\n * Found by running B098's new sentence on a live Ollama: it offered to change\n * a service's `type` so byollm could start a server that was already running.\n * **An instrument with no reader, and a claim resting on it.**\n *\n * So the port is asked. One GET, the same one `probeLocalServers` makes, on\n * the address the route already names — running the thing beats naming the\n * thing, and a diagnosis is exactly where a guess is least affordable.\n */\nexport type Reach = (\n baseUrl: string,\n) => Promise<\"answered\" | \"wrong\" | \"silent\">;\n\nconst fetchProbe: Reach = async (baseUrl) => {\n const abort = new AbortController();\n const timer = setTimeout(() => {\n abort.abort();\n }, 1_500);\n try {\n const response = await fetch(`${baseUrl}/models`, { signal: abort.signal });\n if (!response.ok) return \"wrong\";\n const body: unknown = await response.json();\n /* A 200 that is not a model list is something else on the port, which is\n a different problem from an empty catalogue — a server with no models\n is still a server, and `probeLocalServers` treats it as one. */\n return typeof body === \"object\" && body !== null ? \"answered\" : \"wrong\";\n } catch {\n /* Refused, timed out, or answered something that is not JSON at all.\n \"Silent\" is the honest word: this cannot tell a dead port from a\n hostile one, and the advice is the same either way. */\n return \"silent\";\n } finally {\n clearTimeout(timer);\n }\n};\n\nconst whichProbe: PathProbe = async (binary) => {\n try {\n await run(process.platform === \"win32\" ? \"where\" : \"which\", [binary], {\n timeout: 2_000,\n });\n return true;\n } catch (error) {\n // `which` exits non-zero when not found, which is an answer. Anything\n // else — no shell, a timeout — is genuinely unknown, and saying \"not\n // installed\" then would be a confident lie.\n const code = (error as { code?: unknown }).code;\n return code === 1 || code === \"ENOENT\" ? false : undefined;\n }\n};\n\n/**\n * Which provider byollm could start at this address — B098, and it asks\n * rather than adding a third table.\n *\n * Two authorities, both already here: the protocol registry knows each\n * provider's default address, and `startCommandFor` knows which ones this\n * daemon can spawn. Writing `11434 -> ollama` into {@link BY_PORT} would have\n * been a third place naming that port and a promise to update it when the\n * start command list grows, which is the divergence instruction 9 is about.\n * **Delete `ollama serve` from `startCommandFor` and this offer disappears by\n * itself**, which is the property a hand-written table cannot have.\n */\nfunction startableAt(origin: string): BackendId | undefined {\n for (const id of BACKEND_IDS) {\n if (startCommandFor(id) === undefined) continue;\n const address = backendDescriptor(id).defaultBaseUrl;\n if (address === undefined) continue;\n try {\n if (new URL(address).origin === origin) return id;\n } catch {\n /* A registry entry with an unparseable address is not this function's\n to complain about; it simply matches nothing. */\n }\n }\n return undefined;\n}\n\n/**\n * A sentence about one unhealthy route, or nothing when there is no better\n * guess than the health detail already printed.\n */\nexport async function diagnoseRoute(input: {\n baseUrl?: string | undefined;\n detail?: string | undefined;\n /**\n * What the owner's config says this service is — B098.\n *\n * The reason the sentence can change from *\"start it yourself\"* to *\"byollm\n * would start this if your config named it\"*. Without it the diagnosis\n * knows the address and not the service, and the whole finding is that\n * those two disagree.\n */\n backendId?: BackendId | undefined;\n onPath?: PathProbe;\n reach?: Reach;\n}): Promise<string | undefined> {\n const onPath = input.onPath ?? whichProbe;\n const reach = input.reach ?? fetchProbe;\n if (input.baseUrl === undefined) return undefined;\n\n let url: URL;\n try {\n url = new URL(input.baseUrl);\n } catch {\n return undefined;\n }\n\n const loopback = [\"localhost\", \"127.0.0.1\", \"[::1]\", \"::1\"].includes(\n url.hostname,\n );\n if (!loopback) {\n // A remote server that is not answering is somebody else's outage, and\n // the daemon has nothing useful to add about their infrastructure.\n return undefined;\n }\n\n const suspect = BY_PORT[url.port];\n const on = await reach(input.baseUrl);\n\n if (on === \"answered\") {\n /**\n * The server is up, and this route is unhealthy for a reason the port\n * cannot show — B098, found by running it.\n *\n * Almost always the model: `detectCapabilities` refuses to advertise a\n * model the server's own catalogue does not list, so a config naming\n * `glm-5.2` against a server serving `glm-5.2:cloud` is unhealthy while\n * the server answers perfectly. This branch used to be unreachable, and\n * the sentence it fell through to was \"Nothing is listening\" — the wrong\n * cause, and then an offer to fix it by changing a field.\n */\n return (\n `${url.origin} is answering, so the address is right and this ` +\n `service still is not usable.\\n` +\n ` The usual reason is the model: this device will not advertise ` +\n `a model the server\\n` +\n ` does not list. \\`byollm services\\` names what it is serving; ` +\n `\\`byollm model <service> <name>\\`\\n checks one and writes it.` +\n (input.detail === undefined ? \"\" : `\\n ${input.detail}`)\n );\n }\n\n if (on === \"wrong\") {\n // Something is listening and answered wrongly — the one case where the\n // port is right and the server behind it is not what we assumed.\n return (\n `Something is listening on ${url.origin} but did not answer as a model ` +\n `server. Check that it speaks the OpenAI-compatible API, or point ` +\n `this route at the server that does.`\n );\n }\n\n if (suspect === undefined) {\n return (\n `Nothing is listening on ${url.origin}. Start the model server you ` +\n `meant, or change this route's baseUrl in ~/.byollm/config.json.`\n );\n }\n\n const installed = await onPath(suspect.binary);\n if (installed === false) {\n return (\n `Nothing is listening on ${url.origin}, and ${suspect.binary} is not ` +\n `on your PATH — ${suspect.name} usually serves that port.\\n` +\n ` ${suspect.install}`\n );\n }\n\n const here = `Nothing is listening on ${url.origin}. ${suspect.name} usually serves that port${installed === true ? \" and is installed\" : \"\"}.`;\n const startable = startableAt(url.origin);\n\n /**\n * The offer, and the reason it is an offer — B098, ruled (c) by Todd 09-10.\n *\n * `startCommandFor` switches on the backend id, so only `type: \"ollama\"`\n * is ever started — and the config people actually write for Ollama is\n * `openai-http` pointed at `127.0.0.1:11434`, which is what Todd's own\n * machine had and what the old wizard produced. **The feature did not\n * reach its own common case, and said nothing about it.**\n *\n * Three options were on the table and the middle one is the interesting\n * rejection: keying startability on the ADDRESS would reach every such\n * config, and would mean byollm running `ollama serve` for a service whose\n * owner never named Ollama — if something else is listening on 11434, that\n * is software the owner did not ask for. **Per instruction 11, what would\n * change it:** a way to know what a STOPPED server would have been, which\n * is precisely what nobody can ask a port that is not answering.\n *\n * So the daemon neither decides on the owner's behalf nor stays silent. It\n * names the one field, which is the same shape as B056's advertising\n * ruling and B106's memory prompt: where the machine can infer a useful\n * action but not the intent, the surface asks.\n */\n if (startable !== undefined && input.backendId !== startable) {\n return (\n `${here}\\n` +\n ` byollm could start it when a job needs one, and will not for ` +\n `this service:\\n` +\n ` its type is \"${input.backendId ?? \"unset\"}\", so it never named a ` +\n `server to start.\\n` +\n ` Set \"type\": \"${startable}\" on this service and byollm will start ` +\n `it for you.\\n` +\n ` Or start it yourself: ${suspect.start}`\n );\n }\n\n /**\n * And when the config DOES name it, say what this build actually does.\n *\n * This sentence handed somebody `ollama serve` and nothing else, which was\n * the whole truth until 0.1.0-alpha.88 shipped on-demand start. Telling an\n * owner to start by hand a server the daemon is about to start for them is\n * advice that was true when it was written and is not now — the shape\n * instruction 7 exists for, found on the row that had to read this function\n * anyway.\n */\n if (startable !== undefined) {\n return (\n `${here}\\n` +\n ` byollm starts ${backendName(startable)} itself when a job needs ` +\n `it, unless this device is\\n` +\n ` below its memory floor — \\`byollm status\\` shows the guard.\\n` +\n ` To use it from this shell now: ${suspect.start}`\n );\n }\n\n return `${here}\\n ${suspect.start}`;\n}\n","import { platform } from \"node:os\";\nimport {\n verifyPublicIdentity,\n type Capability,\n type PublicIdentity,\n} from \"@byollm/protocol\";\nimport { ClientError, type ProtocolClient } from \"./client.js\";\nimport type { Pairing } from \"./pairings.js\";\n\nexport interface ConnectOptions {\n readonly client: ProtocolClient;\n readonly daemonVersion: string;\n readonly label: string;\n readonly capabilities: readonly Capability[];\n /** This machine's public keys, presented at pair start (byollm_009 §5). */\n readonly device: PublicIdentity;\n /** Called once, with what to show the user. */\n readonly onCode: (info: {\n userCode: string;\n verificationUrl: string;\n expiresAt: number;\n }) => void;\n /** Called each poll, so a CLI can show it is still waiting. */\n readonly onPoll?: () => void;\n readonly now?: () => number;\n readonly sleep?: (ms: number) => Promise<void>;\n readonly signal?: AbortSignal;\n}\n\nexport type ConnectResult =\n | { readonly ok: true; readonly pairing: Pairing }\n | {\n readonly ok: false;\n readonly reason: \"denied\" | \"expired\" | \"aborted\";\n readonly message: string;\n };\n\n/**\n * The device-code pairing flow, from the daemon's side.\n *\n * The daemon asks for a code, shows it, and polls. The user approves inside\n * the app's own authenticated session, which is how the server learns who\n * they are — the daemon never asserts an identity and never accepts a pasted\n * long-lived secret ({@link MUSTS.PAIR_INTERACTIVE}).\n *\n * Nothing listens on the user's machine for this. A loopback redirect would\n * be fewer keystrokes and would contradict the product's whole posture, as\n * well as breaking on the headless boxes most likely to be running a model.\n */\nexport async function connect(options: ConnectOptions): Promise<ConnectResult> {\n const now = options.now ?? Date.now;\n const sleep = options.sleep ?? defaultSleep;\n\n const started = await options.client.pairStart({\n version: options.daemonVersion,\n label: options.label,\n platform: currentPlatform(),\n device: options.device,\n capabilities: options.capabilities,\n });\n\n options.onCode({\n userCode: started.userCode,\n verificationUrl: started.verificationUrl,\n expiresAt: started.expiresAt,\n });\n\n for (;;) {\n if (options.signal?.aborted === true) {\n return { ok: false, reason: \"aborted\", message: \"pairing was canceled\" };\n }\n if (now() >= started.expiresAt) {\n return {\n ok: false,\n reason: \"expired\",\n message: \"the pairing code expired before it was approved\",\n };\n }\n\n await sleep(started.pollIntervalMs);\n options.onPoll?.();\n\n let polled;\n try {\n polled = await options.client.pairPoll(started.deviceCode);\n } catch (error) {\n // A blip while waiting for a human is not a failure — keep polling\n // until the code itself expires.\n if (error instanceof ClientError && error.retryable) continue;\n throw error;\n }\n\n switch (polled.status) {\n case \"pending\":\n continue;\n case \"denied\":\n return {\n ok: false,\n reason: \"denied\",\n message: \"the pairing request was declined\",\n };\n case \"expired\":\n return {\n ok: false,\n reason: \"expired\",\n message: \"the pairing code expired before it was approved\",\n };\n case \"approved\":\n // Verify before pinning. A site whose encryption key is not signed by\n // the identity presenting it is either misconfigured or being\n // impersonated, and pinning it would make the impersonation\n // permanent — which is the failure mode pinning exists to prevent,\n // arrived at by pinning.\n // **Every** site, not the first one — cloud_009 §5. A pairing now\n // covers a set, and one unverifiable member is the whole pairing\n // refused: pinning the others and quietly dropping that one would\n // leave a machine serving an upstream whose account of itself did\n // not add up, which is the thing this check exists to refuse.\n for (const site of Object.values(polled.sites)) {\n if (verifyPublicIdentity(site)) continue;\n return {\n ok: false,\n reason: \"denied\",\n message:\n \"this app presented keys that do not verify: an encryption \" +\n \"key is not signed by the identity it claims. Nothing was \" +\n \"paired.\",\n };\n }\n // Approved and covering nothing is **normal**, and refusing it here\n // made a first install impossible.\n //\n // This used to return `denied` with \"offered no sites to serve.\n // Nothing was paired.\" The reasoning was that a pairing with no site\n // reports \"paired\" and serves nobody. But that is the ordinary state\n // of a brand-new account: somebody installs, pairs, and only then\n // connects their first site — in that order, because the dashboard\n // tells them to, and because there is nothing to connect a site *to*\n // beforehand. Every genuinely new user hit this.\n //\n // What made it hard to see is that it looked like success from the\n // outside. The control plane had already approved, so the dashboard\n // showed the device with \"It will start taking work within a few\n // seconds\", while this end had discarded the pairing — leaving a\n // machine that never reports, services that never appear, and jobs\n // that time out into \"nothing was listening\", none of which name a\n // pairing.\n //\n // Serving nothing yet is a state the rest of this design already\n // understands: sites arrive later and are announced with their\n // fingerprints on their first job, `byollm status` prints \"(serving\n // nothing right now)\", and `known` exists precisely to hold ids that\n // are pinned but not currently offered. The guard was refusing a\n // condition its own neighbours model.\n return {\n ok: true,\n pairing: {\n origin: options.client.origin,\n runnerId: polled.runnerId,\n owner: polled.owner,\n ...(polled.ownerLabel === undefined\n ? {}\n : { ownerLabel: polled.ownerLabel }),\n sites: polled.sites,\n // Approved by the act of pairing: these are the sites the app\n // named while somebody was typing its code into a browser it\n // opened themselves. Recorded as approved so that a site whose\n // consent later ends and then resumes is not a new question, and\n // so that a key which moves under one of these ids is refused\n // rather than read as somebody new (V1-1).\n known: polled.sites,\n // The key every later roster is checked against — Amendment G.\n // Written down here because pairing is the only moment it is\n // offered: a daemon that does not record it now can never verify\n // a roster, and would refuse every one it is later sent.\n ...(polled.controlPlanePublic === undefined\n ? {}\n : { controlPlanePublic: polled.controlPlanePublic }),\n pairedAt: now(),\n },\n };\n }\n }\n}\n\n/** The platform, narrowed to what the protocol accepts. */\nexport function currentPlatform(): \"darwin\" | \"linux\" | \"win32\" {\n const current = platform();\n if (current === \"darwin\" || current === \"linux\" || current === \"win32\") {\n return current;\n }\n // byollm_002 scopes v1 to macOS and Linux. Anything else reports as linux\n // rather than refusing outright — the protocol field is descriptive, and a\n // BSD user with Ollama running should not be blocked by a label.\n return \"linux\";\n}\n\nconst defaultSleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n","import { createHash } from \"node:crypto\";\nimport { appendFile, mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport type { Audience } from \"@byollm/protocol\";\nimport {\n StopReasonSchema,\n type StopReason,\n type StopReasonMapping,\n} from \"./backends/types.js\";\nimport type { MemoryPressure, MemoryReading } from \"./memory.js\";\nimport { z } from \"zod\";\n\n/**\n * A prompt about to be executed.\n *\n * Written *before* the backend is called\n * ({@link MUSTS.INGRESS_LOGGED_BEFORE_EXECUTION}), so a job that wedges or\n * crashes the machine still leaves a record of what it was.\n */\nexport const PromptEntry = z\n .object({\n type: z.literal(\"prompt\"),\n at: z.number().int().positive(),\n /** Origin of the server that sent the job. */\n origin: z.string().min(1),\n jobId: z.string().min(1),\n /**\n * Which site sent it — V1-3.\n *\n * A job id belongs to a site, so the log's own primary fact was ambiguous\n * the moment a machine served two of them: two lines saying `job_1` were\n * two different prompts, and nothing on the line said so. Optional\n * because lines written before this existed are still readable, and a\n * reader that refused them would be a meter that stops working when it\n * changes.\n */\n site: z.string().min(1).optional(),\n kind: z.string().min(1),\n audience: z.string().min(1),\n /** Who this prompt is *for* — which, for community work, is not you. */\n owner: z.string().min(1),\n backendId: z.string().min(1),\n backendClass: z.string().min(1),\n model: z.string().min(1),\n /** Always present, including after retention drops the text. */\n promptHash: z.string().length(64),\n promptChars: z.number().int().nonnegative(),\n /**\n * The prompt itself. Absent means \"not retained\" — distinct from an empty\n * prompt, which the protocol forbids anyway. Zero and unknown never look\n * alike.\n */\n prompt: z.string().optional(),\n })\n .strict();\nexport type PromptEntry = z.infer<typeof PromptEntry>;\n\n/** How a job ended. A separate line, so the prompt line is never rewritten. */\nexport const OutcomeEntry = z\n .object({\n type: z.literal(\"outcome\"),\n at: z.number().int().positive(),\n jobId: z.string().min(1),\n /** Which site's job — V1-3, same reason as the prompt line. */\n site: z.string().min(1).optional(),\n outcome: z.enum([\"ok\", \"error\", \"canceled\", \"refused\"]),\n /** Present for a job that ran; absent for one refused before execution. */\n durationMs: z.number().int().nonnegative().optional(),\n /**\n * Where that duration went, as far as it can be seen — B195.\n *\n * `durationMs` is spawn + vendor + read in one number, because the clock\n * starts on the first line of the backend's `execute()`. These two are the\n * boundaries a parent process can observe, and `BackendTiming` records\n * what the gap between them is and is not.\n *\n * Optional, and old entries have neither: this log outlives the version\n * that wrote it, and a reader that required them would refuse every line\n * written before today.\n */\n spawnMs: z.number().int().nonnegative().optional(),\n firstOutputMs: z.number().int().nonnegative().optional(),\n outputChars: z.number().int().nonnegative().optional(),\n /** Why, for `error` and `refused`. */\n detail: z.string().optional(),\n /**\n * Why generation stopped — B064 step 3.\n *\n * Local only. This log is the owner's own file, read by `byollm log` on\n * the same machine that wrote it, so recording the reason here is not a\n * wire change and reaches every site already in the field.\n *\n * Optional because it is meaningless on the arms where nothing ran: a\n * refused job has no model to have stopped, and an entry written before\n * this field existed is read by a daemon that has it.\n */\n stop: StopReasonSchema.optional(),\n /**\n * Whether the adapter could read a stop signal at all — B105.\n *\n * `unknown` is two different facts and the resolved value cannot tell\n * them apart: an adapter that CANNOT report, and one that reported a word\n * we do not map. Saying \"does not report\" about the second is false, and\n * it is the common case — `FINISH_REASONS` maps two values.\n *\n * Recorded at write time rather than looked up at read time, for the\n * reason a log is a log: if an adapter gains a signal in a later release,\n * entries written before that should still read as they were true.\n */\n stopKind: z.enum([\"declared\", \"unavailable\", \"unverified\"]).optional(),\n })\n .strict();\nexport type OutcomeEntry = z.infer<typeof OutcomeEntry>;\n\n/**\n * What the memory guard decided, and the numbers it decided on — B080.\n *\n * byollm_022 asks for every decision to be logged, and gives the reason:\n * **the 2 GB floor should be tuned from a distribution rather than from one\n * laptop.** A refusal alone cannot do that — a log holding only refusals says\n * nothing about how close the admits were, which is the whole question when\n * choosing a threshold. So admits are written too, with their readings.\n *\n * Only where the guard is actually active. A proxy route holds no model on\n * this machine, and a line saying so on every job would be volume without a\n * fact in it.\n *\n * `jobId` is deliberately optional: the decision can precede a job, and a\n * shape that demands one would push the caller into inventing an id.\n */\nconst MemoryEntry = z\n .object({\n type: z.literal(\"memory\"),\n at: z.number().int().positive(),\n jobId: z.string().min(1).optional(),\n backendId: z.string().min(1),\n admit: z.boolean(),\n why: z.string().min(1),\n /** Absent when memory could not be read — that is the guard being off. */\n availableBytes: z.number().int().nonnegative().optional(),\n totalBytes: z.number().int().nonnegative().optional(),\n swapFreeBytes: z.number().int().nonnegative().optional(),\n swapTotalBytes: z.number().int().nonnegative().optional(),\n pressure: z.enum([\"normal\", \"warn\", \"critical\", \"unknown\"]),\n })\n .strict();\ntype MemoryEntry = z.infer<typeof MemoryEntry>;\n\nexport const IngressEntry = z.discriminatedUnion(\"type\", [\n PromptEntry,\n OutcomeEntry,\n MemoryEntry,\n]);\nexport type IngressEntry = z.infer<typeof IngressEntry>;\n\nexport interface IngressOptions {\n readonly path: string;\n /** Days to keep a community prompt in full before reducing it to its hash. */\n readonly communityPromptDays: number;\n /** Whether to record the owner's own prompts in full. */\n readonly keepSelfPrompts: boolean;\n}\n\n/**\n * The append-only record of every prompt that has run on this machine.\n *\n * byollm_002 calls the meter the product's soul. This is it: one JSONL file\n * the owner can read, grep and delete, written before execution rather than\n * after.\n */\nexport class IngressLog {\n readonly #options: IngressOptions;\n\n constructor(options: IngressOptions) {\n this.#options = options;\n }\n\n /** Record a prompt. Await this before starting the backend call. */\n async recordPrompt(input: {\n at: number;\n origin: string;\n jobId: string;\n site?: string;\n kind: string;\n audience: Audience;\n owner: string;\n backendId: string;\n backendClass: string;\n model: string;\n prompt: string;\n }): Promise<void> {\n // Community prompts are always kept initially — retention reduces them\n // later. Only the owner's own prompts can be opted out of up front.\n const keepText =\n input.audience === \"private\" ? this.#options.keepSelfPrompts : true;\n\n await this.#append({\n type: \"prompt\",\n at: input.at,\n origin: input.origin,\n jobId: input.jobId,\n ...(input.site === undefined ? {} : { site: input.site }),\n kind: input.kind,\n audience: input.audience,\n owner: input.owner,\n backendId: input.backendId,\n backendClass: input.backendClass,\n model: input.model,\n promptHash: hashText(input.prompt),\n promptChars: input.prompt.length,\n ...(keepText ? { prompt: input.prompt } : {}),\n });\n }\n\n /** Record how a job ended. */\n async recordOutcome(input: {\n at: number;\n jobId: string;\n site?: string;\n outcome: OutcomeEntry[\"outcome\"];\n durationMs?: number;\n spawnMs?: number;\n firstOutputMs?: number;\n outputChars?: number;\n detail?: string;\n stop?: StopReason;\n stopKind?: StopReasonMapping[\"kind\"];\n }): Promise<void> {\n await this.#append({\n type: \"outcome\",\n at: input.at,\n jobId: input.jobId,\n ...(input.site === undefined ? {} : { site: input.site }),\n outcome: input.outcome,\n ...(input.durationMs === undefined\n ? {}\n : { durationMs: input.durationMs }),\n ...(input.spawnMs === undefined ? {} : { spawnMs: input.spawnMs }),\n ...(input.firstOutputMs === undefined\n ? {}\n : { firstOutputMs: input.firstOutputMs }),\n ...(input.outputChars === undefined\n ? {}\n : { outputChars: input.outputChars }),\n ...(input.detail === undefined ? {} : { detail: input.detail }),\n ...(input.stop === undefined ? {} : { stop: input.stop }),\n ...(input.stopKind === undefined ? {} : { stopKind: input.stopKind }),\n });\n }\n\n /** Record what the memory guard decided — B080. */\n async recordMemory(input: {\n at: number;\n jobId?: string;\n backendId: string;\n decision: { admit: boolean; why: string };\n memory: MemoryReading;\n pressure: MemoryPressure;\n }): Promise<void> {\n const read = input.memory.kind === \"read\" ? input.memory : undefined;\n await this.#append({\n type: \"memory\",\n at: input.at,\n ...(input.jobId === undefined ? {} : { jobId: input.jobId }),\n backendId: input.backendId,\n admit: input.decision.admit,\n why: input.decision.why,\n ...(read === undefined\n ? {}\n : {\n availableBytes: read.availableBytes,\n totalBytes: read.totalBytes,\n ...(read.swapFreeBytes === undefined\n ? {}\n : { swapFreeBytes: read.swapFreeBytes }),\n ...(read.swapTotalBytes === undefined\n ? {}\n : { swapTotalBytes: read.swapTotalBytes }),\n }),\n pressure: input.pressure,\n });\n }\n\n async #append(entry: IngressEntry): Promise<void> {\n await mkdir(dirname(this.#options.path), { recursive: true });\n // JSON.stringify escapes control characters, so a prompt containing ANSI\n // escapes or newlines cannot forge a log line or repaint the terminal of\n // whoever later reads the file ({@link MUSTS.OUTPUT_INERT}).\n await appendFile(this.#options.path, `${JSON.stringify(entry)}\\n`, {\n mode: 0o600,\n });\n }\n\n /** Read the log, oldest first. Malformed lines are skipped, not fatal. */\n async read(): Promise<IngressEntry[]> {\n let raw: string;\n try {\n raw = await readFile(this.#options.path, \"utf8\");\n } catch {\n return [];\n }\n const entries: IngressEntry[] = [];\n for (const line of raw.split(\"\\n\")) {\n if (line.trim() === \"\") continue;\n try {\n const parsed = IngressEntry.safeParse(JSON.parse(line));\n if (parsed.success) entries.push(parsed.data);\n } catch {\n // A truncated final line after a hard kill is expected, not an error.\n }\n }\n return entries;\n }\n\n /**\n * Apply retention: drop the text of community prompts older than the\n * window, keeping the hash, the metadata and the character count.\n *\n * byollm_004 Rev 1: a volunteer must not indefinitely retain strangers'\n * content. The hash stays, so the owner can still prove what ran.\n *\n * @returns how many entries were reduced.\n */\n async applyRetention(now: number): Promise<number> {\n const entries = await this.read();\n const cutoff = now - this.#options.communityPromptDays * 86_400_000;\n let reduced = 0;\n\n const kept = entries.map((entry) => {\n if (entry.type !== \"prompt\") return entry;\n /**\n * Not-private, rather than a list of the sharing scopes.\n *\n * The stored `audience` is `z.string()` on purpose, so this log keeps\n * reading entries written by older daemons — including `public`, a\n * scope removed on 2026-08-26. Enumerating the sharing scopes would\n * have made those legacy rows non-community overnight and kept\n * somebody else's prompts on this disk for ever.\n *\n * Stated this way an audience this version does not recognise is\n * retained *less*, never more, which is the safe direction for text\n * that belongs to somebody who is not the owner.\n */\n const isCommunity = entry.audience !== \"private\";\n if (!isCommunity || entry.prompt === undefined || entry.at >= cutoff) {\n return entry;\n }\n reduced += 1;\n const { prompt: _dropped, ...rest } = entry;\n return rest;\n });\n\n if (reduced > 0) {\n // The log is append-only in operation; retention is the one deliberate\n // exception, and it only ever removes text.\n await writeFile(\n this.#options.path,\n `${kept.map((entry) => JSON.stringify(entry)).join(\"\\n\")}\\n`,\n { mode: 0o600 },\n );\n }\n return reduced;\n }\n}\n\n/** SHA-256 of a prompt, hex. */\nexport function hashText(text: string): string {\n return createHash(\"sha256\").update(text, \"utf8\").digest(\"hex\");\n}\n\n/**\n * Replace terminal control sequences before printing untrusted text.\n *\n * Model output and job payloads both reach the owner's terminal through\n * `byollm log` and `byollm status`. Text that can move the cursor or set\n * colours can forge output — the ANSI/log-injection row of byollm_004 §5's\n * corpus. Stored bytes stay verbatim; only the *display* is sanitised, so the\n * log remains an honest record of what actually arrived.\n *\n * Tab and newline are kept: they are legitimate content, not control.\n */\nexport function stripControlChars(text: string): string {\n // eslint-disable-next-line no-control-regex -- matching control characters is the entire purpose\n return text.replace(/[\\u0000-\\u0008\\u000B-\\u001F\\u007F-\\u009F]/g, \"\\uFFFD\");\n}\n","import type { BackendId } from \"@byollm/protocol\";\nimport { memoryGate, gigabytes } from \"./memory-gate.js\";\nimport type { MemoryPressure, MemoryReading } from \"./memory.js\";\n\n/**\n * Whether loading a model from the owner's own command needs asking — B106.\n *\n * The guard was wired into the job path and nowhere else, so `byollm model\n * <svc> <name>` was a documented model load with nothing in front of it. The\n * canary that command runs is \"the cheapest true call the backend has\", and\n * for a local server a true call loads the model — H2, from the help text.\n *\n * ## It asks, it does not refuse\n *\n * An owner typing the command is consent; a remote job is not. Refusing here\n * would be second-guessing somebody about their own machine, which is the\n * ruling B080 already took. But the failure mode does not care who asked, so\n * below the floor they are told what is about to happen and asked.\n *\n * ## The same gate, not a second opinion\n *\n * {@link memoryGate} decides, with the owner's configured floor, exactly as\n * it does for a job. Re-deriving \"is there room\" beside it is the shape B085\n * arrived as — one question answered in two places, agreeing right up until\n * the day they did not. The only thing this adds is what to do with the\n * answer: a job is refused, a person is asked.\n */\nexport function modelLoadQuestion(input: {\n readonly backendId: BackendId;\n readonly baseUrl: string | undefined;\n readonly model: string;\n readonly memory: MemoryReading;\n readonly pressure: MemoryPressure;\n readonly floorBytes: number;\n}):\n { readonly ask: false } | { readonly ask: true; readonly question: string } {\n /**\n * One question, asked once — and the proxy case comes free.\n *\n * A first draft checked {@link guardApplies} here before consulting the\n * gate, which reads as belt and braces and is neither: the gate's own\n * first act is to ask that same function and admit anything that holds no\n * model on this machine. Deleting the early return changed no answer,\n * which a mutation proved — so it was a second decision point that could\n * only ever agree, and the kind that stops agreeing the day somebody edits\n * one of them.\n */\n const decision = memoryGate({\n backendId: input.backendId,\n baseUrl: input.baseUrl,\n model: input.model,\n memory: input.memory,\n pressure: input.pressure,\n floorBytes: input.floorBytes,\n });\n /**\n * Silent above the floor, and that is a decision rather than an omission.\n *\n * A guard that narrates on the happy path teaches people to skip reading\n * it, and then it is furniture on the day it matters. `byollm status` is\n * where somebody goes to see the reading when nothing is wrong.\n */\n if (decision.admit) return { ask: false };\n\n const gb = gigabytes;\n const room =\n input.memory.kind === \"read\"\n ? `${gb(input.memory.availableBytes)} available of ${gb(input.memory.totalBytes)}`\n : \"memory could not be read\";\n return {\n ask: true,\n /* What is about to be loaded and what is left, then the question. The\n reason comes from the gate rather than being restated here, so the\n sentence cannot describe a different rule from the one that fired. */\n question:\n `Loading ${input.model} on ${input.backendId} will use this machine's ` +\n `memory, and ${decision.why} (${room}). Load it anyway?`,\n };\n}\n","import { resolveCost, type BackendId } from \"@byollm/protocol\";\nimport type { LocalServer } from \"./probe-local.js\";\nimport { serviceBlockFor, serviceNameFor } from \"./services-manage.js\";\n\n/**\n * Models a server is offering that nothing on this device points at — B100b.\n *\n * Todd pulled `smollm2:135m`, `ollama list` showed five models, and `byollm\n * services` showed the two his config names. Nothing on any byollm surface\n * said the other three existed or how to reach one. For a product whose pitch\n * is \"run the models you already have\", the gap between having one and using\n * one was a JSON file nobody mentions.\n *\n * ## Display only, and the rule is not a formality\n *\n * `backends.ts` is explicit that cost is classified from the owner's\n * CONFIGURED value and that a server's catalogue \"is not theirs to be\n * classified by\". A discovery surface that became a routing input would let a\n * server's own list decide what this machine offers and what it costs, which\n * is the hole B097 had money on. So this returns names to print and nothing\n * else: no ids, no types, no cost, nothing a router could reach for.\n *\n * ## The catalogue is what is PULLED, not what will run\n *\n * `gemma4:26b` is 17 GB and appears in this list on a 36 GB laptop that\n * cannot comfortably serve it. Naming it is still right — B056's rule is that\n * the SERVER answers, not that a model is loaded — but the list is an\n * inventory, not a promise, and B106's prompt is what stands between somebody\n * picking one and a wedged machine.\n */\nexport interface UnusedModels {\n readonly label: string;\n readonly baseUrl: string;\n readonly models: readonly string[];\n /**\n * The provider the server named, carried through — B112.\n *\n * The rule above still holds and this does not weaken it: the CATALOGUE is\n * display-only and never reaches a router. Who the server *is* is a\n * different fact, and it is the one that decides whether the block printed\n * below can be started on demand. Optional because it is only ever set by\n * an answer.\n */\n readonly backendId?: BackendId;\n}\n\nexport function unusedModels(input: {\n readonly servers: readonly LocalServer[];\n /** Every (baseUrl, model) pair a configured service already points at. */\n readonly configured: readonly {\n baseUrl?: string | undefined;\n model?: string | undefined;\n }[];\n}): UnusedModels[] {\n /**\n * Matched on the pair, not on the model name alone.\n *\n * The same model id can sit behind two servers — `qwen-2.5-14b` on MLX and\n * on Ollama — and a service pointing at one of them says nothing about the\n * other. Keying on the name would hide a genuinely unused model because its\n * namesake elsewhere was in use.\n */\n const taken = new Set(\n input.configured\n .filter(\n (entry) => entry.baseUrl !== undefined && entry.model !== undefined,\n )\n .map((entry) => `${normalise(entry.baseUrl ?? \"\")} ${entry.model ?? \"\"}`),\n );\n\n return input.servers\n .map((server) => ({\n label: server.label,\n baseUrl: server.baseUrl,\n ...(server.backendId === undefined\n ? {}\n : { backendId: server.backendId }),\n models: server.models.filter(\n (model) => !taken.has(`${normalise(server.baseUrl)} ${model}`),\n ),\n }))\n .filter((server) => server.models.length > 0);\n}\n\n/**\n * The config block that would use one — one definition, two readers.\n *\n * byollm_023 split B100 and then ruled the split away: one interactive screen\n * rather than three verbs. That screen exists now — `byollm services manage`,\n * B100a — and this stays, because the screen needs a terminal and this\n * command does not. A machine reached over a pipe, a CI log, somebody who\n * would rather edit the file: all of them get the exact JSON with their model\n * and their address already in it.\n *\n * **It is the same block the picker writes**, because it asks the picker's\n * own {@link serviceBlockFor} rather than restating the shape. Two spellings\n * of \"what a service for this model looks like\" is exactly the divergence\n * instruction 9 exists for, and this file carried one of them until B100a.\n *\n * `offer` is written out rather than left to a default, because a service\n * created without a visible scope is a consent decision made by a tool —\n * B100's second constraint, which applies to a printed block exactly as it\n * would to a wizard.\n */\nexport function pasteableService(input: {\n readonly model: string;\n readonly baseUrl: string;\n readonly type?: BackendId | undefined;\n}): string {\n return JSON.stringify(\n { [serviceNameFor(input.model)]: serviceBlockFor(input) },\n null,\n 2,\n );\n}\n\nfunction normalise(url: string): string {\n try {\n const parsed = new URL(url);\n return `${parsed.protocol}//${parsed.host}`;\n } catch {\n return url.trim();\n }\n}\n\n/**\n * The whole section `byollm services` prints, or nothing at all — B100b.\n *\n * Lines rather than writes, the same shape `service-line.ts` uses: the\n * decisions here are which models to name, how to mark the ones that are not\n * local compute, and which one to hold up as the example — and every one of\n * those is worth a test. Presentation built inside a command is presentation\n * nothing can reach, which the coverage gate said out loud when this lived in\n * `cli.ts`.\n *\n * Empty when there is nothing to say. A machine whose every model is already\n * configured gets no paragraph, because a section that is always there is\n * furniture — the same reason the memory guard says nothing above the floor.\n */\nexport function unusedModelsReport(input: {\n readonly servers: readonly LocalServer[];\n readonly configured: readonly {\n baseUrl?: string | undefined;\n model?: string | undefined;\n }[];\n}): string[] {\n const spare = unusedModels(input);\n if (spare.length === 0) return [];\n\n const lines = [\"\", \"also on this machine, not used by any service\"];\n for (const server of spare) {\n lines.push(` ${server.label} (${server.baseUrl})`);\n for (const model of server.models) {\n /**\n * A `:cloud` tag is marked, because this list looks like local compute\n * and one of these is not.\n *\n * Found by running it: Todd's Ollama serves `kimi-k3:cloud` beside\n * three genuinely local models. Ollama proxies hosted models through\n * the same loopback port, so the address says local and the bill does\n * not — B097's whole subject, arriving on a new surface three rows\n * later. Printing it unmarked under \"on this machine\" would be the\n * page saying the friendlier half.\n *\n * Asked of {@link resolveCost} rather than decided here: cost has one\n * home, and a surface that classified for itself is the defect B085\n * arrived as.\n */\n const cost = costOf(server.baseUrl, model, server.backendId);\n lines.push(\n ` ${model}${cost === \"free\" ? \"\" : ` (${cost} — runs on your provider's account)`}`,\n );\n }\n }\n\n /**\n * One example, and a FREE one where there is one.\n *\n * The block is the same shape every time, so printing six would bury the\n * sentence explaining it — but taking the first model blindly would offer\n * somebody a paste that quietly creates a metered service. Local first, and\n * the mark above still tells the truth when every model here is hosted.\n */\n const example =\n spare\n .flatMap((server) => server.models.map((name) => ({ server, name })))\n .find(\n ({ server, name }) =>\n costOf(server.baseUrl, name, server.backendId) === \"free\",\n ) ??\n (spare[0]?.models[0] === undefined\n ? undefined\n : { server: spare[0], name: spare[0].models[0] });\n if (example === undefined) return lines;\n\n lines.push(\n \"\",\n \" `byollm services manage` turns one on, and asks nothing you have to\",\n \" look up. Or add it to the `services` block of ~/.byollm/config.json:\",\n \"\",\n ...pasteableService({\n model: example.name,\n baseUrl: example.server.baseUrl,\n type: example.server.backendId,\n })\n .split(\"\\n\")\n .map((line) => ` ${line}`),\n \"\",\n \" `offer` is written out on purpose — a service created without a\",\n \" visible scope is a decision made for you. `private` means only your\",\n \" own work runs on it.\",\n );\n return lines;\n}\n\n/**\n * What a model behind this address would cost, asked of the one classifier.\n *\n * **The same type the printed block declares**, so the answer describes the\n * service somebody would actually create rather than a hypothetical one —\n * which is why this takes the argument rather than hard-coding a transport.\n * B112 made the block say `ollama` where the server said so, and a classifier\n * still asked about `openai-http` would have been answering about a different\n * config from the one on screen.\n *\n * It happens not to change any answer today: `classifyCost` reads the\n * `:cloud` tag before the declared cost, which is B097's fix, so both\n * spellings agree. **Passing it anyway is the point** — the one-definition\n * rule was breached last time through exactly this gap, an asker who supplied\n * two of three arguments.\n */\nfunction costOf(\n baseUrl: string,\n model: string,\n type: BackendId | undefined,\n): string {\n return resolveCost(type ?? \"openai-http\", baseUrl, model);\n}\n","import type { BackendId } from \"@byollm/protocol\";\nimport type { StopReason, StopReasonMapping } from \"./backends/index.js\";\n\n/**\n * What an owner does about a truncated answer — B064 step 3, corrected by\n * B105.\n *\n * byollm_021: *\"a truncation without a remedy turns 'raise the output\n * ceiling' into 'this model is bad,'\"* and somebody then reaches for the fix\n * that always looks available — switching models, or turning the service off.\n * So the local surface says the fact AND the knob.\n *\n * ## Whose ceiling it is, which decides the whole sentence\n *\n * **Ours fails loudly and is not this.** `maxOutputBytes` is applied while\n * reading the response and produces `output-too-large`, a refusal with its\n * own code. A `length` result is therefore always the MODEL's own ceiling —\n * verified by reading the adapter, which never sends `max_tokens` at all, so\n * nothing byollm configures is capping generation. Pointing an owner at our\n * config for their model's limit would be the \"this model is bad\" outcome by\n * a different route.\n *\n * ## Verified by running it, per the `startCommandFor` precedent\n *\n * Against the local Ollama on 2026-09-09, `smollm2:135m`, both directions\n * from the same server: `max_tokens: 8` gave `finish_reason: length`, a short\n * answer gave `stop`, and `/api/generate` with `options.num_predict: 8` gave\n * `done_reason: length`. So `num_predict` is the knob.\n */\n\n/**\n * The knob, for every backend — B105.\n *\n * A `Record<BackendId, …>` rather than a `switch` with a `default`, and that\n * is the fix rather than a tidy-up. The switch enumerated eight of the\n * seventeen HTTP backends, so **nine vendor APIs fell to silence by omission\n * rather than by decision** — and a backend added next year would have\n * inherited that silence with nothing failing.\n *\n * Total, so the compiler is the check. The registry already makes\n * `adversarialCorpus` and `stopReasons` required for exactly this reason: a\n * new backend cannot arrive without answering, and this is the same\n * treatment one layer out.\n *\n * `null` means \"no owner-side knob\", which is a decision and reads as one.\n * byollm_021 is explicit that where there is none we say so plainly rather\n * than invent one — an instruction that does nothing spends the owner's\n * afternoon before it spends their patience.\n */\nconst REMEDIES: Readonly<Record<BackendId, string | null>> = Object.freeze({\n /* Local servers: the owner owns the model and its parameters. */\n ollama: \"raise `num_predict` on the model's parameters\",\n llamacpp: \"raise the output ceiling on the server that served it\",\n vllm: \"raise the output ceiling on the server that served it\",\n lmstudio: \"raise the output ceiling on the server that served it\",\n jan: \"raise the output ceiling on the server that served it\",\n localai: \"raise the output ceiling on the server that served it\",\n mlx: \"raise the output ceiling on the server that served it\",\n \"openai-http\": \"raise the output ceiling on the server that served it\",\n\n /**\n * Vendor APIs: no knob, and the reason is the same for all nine.\n *\n * The ceiling is the vendor's own default for that model, and byollm sends\n * no `max_tokens`, so there is nothing on this device to turn. Telling an\n * owner to raise a ceiling they do not control is the invented instruction\n * byollm_021 warns about — and it would be nine of the seventeen.\n */\n anthropic: null,\n openai: null,\n gemini: null,\n grok: null,\n groq: null,\n openrouter: null,\n together: null,\n deepseek: null,\n mistral: null,\n\n /* Subscription CLIs: no owner-side ceiling at all. */\n \"claude-cli\": null,\n \"codex-cli\": null,\n});\n\nexport function stopRemedy(\n backendId: BackendId,\n stop: StopReason,\n): string | undefined {\n if (stop !== \"length\") return undefined;\n return REMEDIES[backendId] ?? undefined;\n}\n\n/**\n * The one sentence an owner reads about a stopped answer — B105 rewrote it.\n *\n * ## `unknown` is two different facts, and saying one of them is false\n *\n * The first version printed *\"does not report why generation stopped\"* for\n * every `unknown`. Seen by running it, that is false for the common case:\n * `FINISH_REASONS` maps `stop` and `length` and nothing else, so a\n * **declared** adapter returning `content_filter`, `tool_calls`, or any\n * vendor extra resolves to `unknown` — and it did report, we did not\n * recognise the word.\n *\n * The daemon holds the distinguishing fact. `stopReasons.kind` is `declared`\n * when the adapter reads a real signal and `unavailable` when it cannot, so\n * the sentence branches on it rather than on the resolved value. This is the\n * same conflation B064's third mapping kind was introduced to fix, arriving\n * on the surface instead of in the declaration.\n *\n * ## No registry label as a sentence subject\n *\n * Second instance of the defect fixed one row earlier. `backendName` on the\n * generic backend is *\"Any OpenAI-compatible server\"*, a noun phrase written\n * for a list, and as a subject it reads *\"Any OpenAI-compatible server does\n * not report why generation stopped\"* — which sounds like a claim about the\n * category rather than about the service that just ran.\n *\n * So no name at all: the line is printed indented under an entry that already\n * says `ollama:smollm2:135m`, and repeating it there was what forced a\n * subject into the sentence in the first place.\n */\nexport function stopLine(\n backendId: BackendId,\n stop: StopReason,\n kind: StopReasonMapping[\"kind\"] | undefined,\n): string | undefined {\n if (stop === \"end\" || stop === \"stop-sequence\") return undefined;\n if (stop === \"unknown\") {\n /* Absent `kind` is an entry written before this was recorded. It cannot\n be resolved either way now, so it says the thing that is true of both:\n we do not know why. */\n if (kind === undefined) return \"why this answer stopped was not recorded\";\n return kind === \"unavailable\"\n ? \"this answer may be incomplete — this service does not report why generation stopped\"\n : \"this answer may be incomplete — the service did not say why it stopped this time\";\n }\n const remedy = stopRemedy(backendId, stop);\n return (\n \"the model stopped at its own output ceiling, so this answer is cut off\" +\n (remedy === undefined ? \"\" : ` — ${remedy}`)\n );\n}\n","import { randomUUID } from \"node:crypto\";\nimport {\n chmod,\n link,\n mkdir,\n readFile,\n rm,\n stat,\n writeFile,\n} from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport {\n StoredKeys,\n fingerprint,\n generateKeys,\n publicIdentityOf,\n signRequest,\n signWith,\n type PublicIdentity,\n} from \"@byollm/protocol\";\n\n/**\n * This machine's identity — byollm_009 §3.\n *\n * Generated once, at first run, and then never again: the device key *is* the\n * daemon, in a way a runner token never was. A token is a bearer secret a\n * server minted and can reissue; this is the machine saying who it is, and\n * nothing upstream can mint one.\n *\n * That difference is why this file is treated more carefully than the others\n * the daemon writes. Losing it means re-pairing every site. Leaking it means\n * someone else can be this machine.\n */\nexport class DeviceIdentity {\n readonly #path: string;\n #keys: StoredKeys | undefined;\n\n constructor(path: string) {\n this.#path = path;\n }\n\n /**\n * Load the keys, generating them on first run.\n *\n * A corrupt file is **not** silently replaced. Generating fresh keys over\n * an unreadable file would silently orphan every pairing the owner has —\n * every site would see an unknown device and refuse — and it would look\n * like a network problem. Refusing loudly names the file and lets the owner\n * decide, which is the only honest option when the alternative is\n * destroying something unrecoverable.\n */\n async load(now: number): Promise<StoredKeys> {\n if (this.#keys) return this.#keys;\n\n let raw: string;\n try {\n raw = await readFile(this.#path, \"utf8\");\n } catch (error) {\n if (!isNotFound(error)) throw error;\n return this.#create(now);\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (error) {\n throw new Error(\n `${this.#path} is not valid JSON. This file is this device's ` +\n `identity; delete it only if you accept re-pairing every app.`,\n { cause: error },\n );\n }\n\n const result = StoredKeys.safeParse(parsed);\n if (!result.success) {\n throw new Error(\n `${this.#path} is not a valid key file. This file is this device's ` +\n `identity; delete it only if you accept re-pairing every app.`,\n );\n }\n\n await this.#warnIfReadable();\n this.#keys = result.data;\n return result.data;\n }\n\n /** The public half, for pairing and the handshake. */\n async publicIdentity(now: number): Promise<PublicIdentity> {\n return publicIdentityOf(await this.load(now));\n }\n\n /** What the owner compares out of band. */\n async fingerprint(now: number): Promise<string> {\n return fingerprint((await this.load(now)).identityPublic);\n }\n\n /** Sign a challenge nonce. */\n async sign(data: Uint8Array, now: number): Promise<string> {\n return signWith(await this.load(now), data);\n }\n\n /** Sign one outgoing protocol request (byollm_009 §4.2). */\n async signRequest(input: {\n endpoint: string;\n runnerId: string;\n issuedAt: number;\n body: string;\n }): Promise<string> {\n return signRequest(await this.load(input.issuedAt), input).signature;\n }\n\n async #create(now: number): Promise<StoredKeys> {\n const keys = generateKeys(now);\n await mkdir(dirname(this.#path), { recursive: true });\n\n // Written somewhere else, then **linked** into place — and the reason is\n // a Windows CI failure on this file's own race test.\n //\n // It was `writeFile(…, { flag: \"wx\" })`: exclusive create, so two daemons\n // racing at first start could not each believe they had made the\n // identity. That half was right and remains. What it does not give is an\n // *atomic* file: `wx` creates the name and then writes the bytes, so\n // between those two moments the other daemon reads a file that exists and\n // is empty. On Linux the window is small enough that this passed for\n // months; on Windows it opened wide enough to fail, with \"keys.json is\n // not valid JSON\" — a message about corruption, for a file that was\n // merely half-written.\n //\n // `link` makes the name appear only when the content is already complete,\n // and fails with EEXIST if somebody won the race first. Both properties in\n // one syscall, on every platform that has hard links.\n // A unique name per attempt, not per process. Two daemons in one process\n // is a test rather than a deployment, and this file's own race test is\n // exactly that: with the pid as the suffix, both wrote the same temp,\n // the second overwrote the first, and the daemon that won the `link`\n // returned keys that were never the ones on disk. A worse failure than\n // the one being fixed, and invisible anywhere but here.\n const temp = `${this.#path}.${randomUUID()}.tmp`;\n await writeFile(temp, JSON.stringify(keys, null, 2), { mode: 0o600 });\n try {\n await link(temp, this.#path);\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code === \"EEXIST\") {\n await rm(temp, { force: true });\n // The winner's file, complete by construction now.\n return this.load(now);\n }\n // A filesystem without hard links (some network and FAT mounts). Fall\n // back to the old behaviour rather than refusing to start: a smaller\n // race is better than no daemon, and this path is rare enough that\n // saying so in a comment is the honest treatment.\n if (code === \"EPERM\" || code === \"ENOSYS\" || code === \"EXDEV\") {\n await rm(temp, { force: true });\n try {\n await writeFile(this.#path, JSON.stringify(keys, null, 2), {\n mode: 0o600,\n flag: \"wx\",\n });\n } catch (fallbackError) {\n if ((fallbackError as NodeJS.ErrnoException).code === \"EEXIST\") {\n return this.load(now);\n }\n throw fallbackError;\n }\n this.#keys = keys;\n return keys;\n }\n await rm(temp, { force: true });\n throw error;\n }\n await rm(temp, { force: true });\n this.#keys = keys;\n return keys;\n }\n\n /**\n * Say something if the key file is group- or world-readable.\n *\n * We write `0600`, but a restore from backup, a careless `chmod -R`, or a\n * synced folder can widen it afterwards. Tightening it silently would hide\n * that something else on this machine is treating the file as ordinary\n * data, which is worth knowing.\n *\n * **Windows is exempt, because the check would lie.** Node's `mode` there\n * is synthesized — a writable file reports `0o666` regardless of what was\n * passed to `writeFile`, and `chmod` only toggles the read-only flag. So on\n * Windows this would warn on every start and claim to fix something it had\n * not fixed. The honest position is in `docs/security.md` §3.4: on Windows\n * the key file is protected by the ACLs it inherits from the user profile,\n * not by a mode we set.\n */\n async #warnIfReadable(): Promise<void> {\n if (process.platform === \"win32\") return;\n try {\n const mode = (await stat(this.#path)).mode & 0o777;\n if ((mode & 0o077) !== 0) {\n process.stderr.write(\n `warning: ${this.#path} is mode ${mode.toString(8)} — this device's\\n` +\n `private key is readable by other users. Fixing to 0600.\\n`,\n );\n await chmod(this.#path, 0o600);\n }\n } catch {\n // A stat failure must not stop a daemon from starting.\n }\n }\n}\n\nfunction isNotFound(error: unknown): boolean {\n return (error as NodeJS.ErrnoException | undefined)?.code === \"ENOENT\";\n}\n","import { Buffer } from \"node:buffer\";\nimport {\n ConsoleFrame,\n CONSOLE_FRAME_VERSION,\n CONSOLE_MAX_DATA_BYTES,\n consoleDataBytes,\n consoleEnvelope,\n consoleOrder,\n encodeConsoleData,\n keyId,\n open,\n seal,\n verifyPublicIdentity,\n type PublicIdentity,\n type SealedEnvelope,\n type StoredKeys,\n} from \"@byollm/protocol\";\n\n/**\n * The box side of a console session — byollm_018 §\"Console redesign: brokered\n * E2E replaces SSH-first (RULED, Todd 2026-09-03)\".\n *\n * ## What this can prove, and what it cannot — read this before hardening it\n *\n * The stream is sealed with `crypto_box_seal`, which is **anonymous**\n * encryption: the box's encryption key is public, so ANYONE who can reach the\n * box can produce a frame it will decrypt. The box therefore cannot tell the\n * owner's browser from the broker on cryptography alone, and no amount of\n * work here changes that — what authorises a session is the hub's grant, and\n * the hub is the party issuing it.\n *\n * That is not a gap in this implementation. It is the ruling's own finding,\n * reached before any of this was designed: *\"every hosted console is\n * operator-ACCOUNTABLE, not operator-incapable — the control plane that mints\n * access can always mint access, and true 'cannot' on hardware we operate is\n * attestation (B2), not software. We build the accountable version, then say\n * exactly that.\"* Item 4's session feed is where the accountability is paid,\n * which is why {@link ConsoleSessionDeps.record} is not optional.\n *\n * **So do not add a check here that pretends otherwise.** A guarantee the\n * architecture cannot keep is worse than the honest copy we ship, because the\n * copy is what a customer reads.\n *\n * ## What it CAN prove, and does\n *\n * Within one session, continuity. The hub announces the browser's ephemeral\n * identity at session start; this agent pins it for the session's life and\n * verifies every frame against it, including the `hello`. After that first\n * frame nobody can splice into the stream — not another tab, not the hub —\n * without the signature failing. And `hello` carries the browser's identity\n * a second time, SEALED, so a hub that announces one key while a browser\n * speaks with another is caught rather than silently tolerated.\n *\n * Plus everything `consoleOrder` gives: no drops, no reordering, no replay,\n * checked inside the ciphertext where the router cannot reach.\n */\n\n/** What the session writes to the owner's feed. Ruling item 4. */\nexport interface ConsoleSessionRecord {\n readonly sessionId: string;\n readonly at: number;\n readonly event: \"started\" | \"ended\";\n /** Present on `ended`. A sentence, because an owner reads it. */\n readonly reason?: string;\n}\n\n/** The pty side, kept abstract so the pump is testable without one. */\nexport interface ConsoleShell {\n write(data: Buffer): void;\n resize(cols: number, rows: number): void;\n /**\n * Subscribe to the shell's output.\n *\n * **Whatever the shell produced before this was called must be delivered\n * here.** A pty starts producing at spawn and the console does not build\n * its session until a socket is up, so a shell's greeting and its first\n * prompt — the only signal that it is safe to type — land in that window.\n * An implementation that subscribes lazily drops them and shows an\n * operator an empty pane. See `pty-shell.test.ts`, which holds this to it.\n */\n onData(handler: (chunk: Buffer) => void): void;\n onExit(handler: (reason: string) => void): void;\n kill(): void;\n}\n\nexport interface ConsoleSessionDeps {\n readonly sessionId: string;\n /** When this session's envelopes stop being valid. */\n readonly deadlineAt: number;\n /** The box's own keys — the pairing identity, per the 09-16 delta call. */\n readonly keys: StoredKeys;\n /**\n * The browser's ephemeral identity, as announced by the hub at session\n * start. Pinned for this session and cross-checked against `hello`.\n */\n readonly browser: PublicIdentity;\n readonly shell: ConsoleShell;\n /** Send one sealed envelope to the hub. */\n send(envelope: SealedEnvelope): Promise<void>;\n /** Ruling item 4: EVERY session, including any we ever open ourselves. */\n record(entry: ConsoleSessionRecord): Promise<void>;\n /**\n * What the agent did with each frame — the box's half of `?debug=1`.\n *\n * Separate from {@link record}, which is the OWNER's feed and says only that\n * a session began and ended. This says what happened inside one, and the\n * distinction matters: today a session sat open for five minutes producing\n * nothing while the browser sent twenty-eight frames into it, and neither\n * end could say whether they arrived. The browser had counters by then; this\n * side had none.\n *\n * Kinds and counts, never contents — the frames carry somebody's shell.\n */\n log?:\n ((message: string, fields?: Record<string, unknown>) => void) | undefined;\n now(): number;\n}\n\nexport interface ConsoleSession {\n /** Feed one envelope that arrived from the hub. */\n deliver(envelope: SealedEnvelope): Promise<void>;\n /** End the session and tell the other end why. */\n stop(reason: string): Promise<void>;\n /** Why the session ended, or undefined while it runs. */\n readonly ended: string | undefined;\n}\n\n/**\n * Start a console session.\n *\n * Nothing here starts on its own: this is reached from an explicit\n * subcommand the box's supervisor launches, never from `byollm run`. A\n * capability that lets a remote broker drive a pty should be absent from a\n * laptop daemon's behaviour, not merely disabled in it.\n */\nexport function consoleSession(deps: ConsoleSessionDeps): ConsoleSession {\n const inbound = consoleOrder(\"browser\");\n /* Counted so a session that goes quiet can say whether it stopped RECEIVING\n or stopped ANSWERING — two different faults that look identical from a\n browser, and the ambiguity that cost this afternoon. */\n /* Bound once rather than called through `?.` at each site: two optional\n calls are two arms apiece, and the arm where nobody is listening is the\n one a test forgets. One default, exercised both ways. */\n const log = deps.log ?? (() => undefined);\n let fromBrowser = 0;\n let toBrowser = 0;\n const boxKeyId = keyId(deps.keys.identityPublic);\n const browserKeyId = keyId(deps.browser.identity);\n\n let outSeq = 0;\n let ended: string | undefined;\n let started = false;\n\n const sealTo = async (frame: ConsoleFrame): Promise<void> => {\n const envelope = await seal({\n /**\n * Parsed before it is sealed — B304's law, applied to the other end.\n *\n * Both call sites below pass fresh object literals, so TypeScript's\n * excess-property check covers them and this is safe **today**. That is\n * exactly the property `runner.ts` had until somebody spread a variable\n * into the object one refactor later: `ran` arrived from a method with\n * an inferred return type, the `satisfies` annotation stopped meaning\n * anything, and two extra keys travelled for months.\n *\n * A sealed payload is the one kind nothing in between can check. The\n * browser opens this, fails `.strict()`, and has nowhere to put the\n * complaint — the console would simply stop, the way every claude-cli\n * result simply stopped.\n */\n plaintext: JSON.stringify(ConsoleFrame.parse(frame)),\n senderKeys: deps.keys,\n recipientEncryptionPublic: deps.browser.encryption,\n context: consoleEnvelope({\n sessionId: deps.sessionId,\n from: \"box\",\n senderKeyId: boxKeyId,\n recipientKeyId: browserKeyId,\n deadlineAt: deps.deadlineAt,\n }),\n });\n await deps.send(envelope);\n };\n\n const finish = async (reason: string): Promise<void> => {\n if (ended !== undefined) return;\n ended = reason;\n /* One line per session saying what actually moved. A console that ends\n having received frames and sent none is a different fault from one that\n received none at all, and from a browser they look the same. */\n log(\"console session ending\", {\n reason,\n framesFromBrowser: fromBrowser,\n framesToBrowser: toBrowser,\n });\n deps.shell.kill();\n // The record is written even if telling the browser fails — the owner's\n // feed is the thing that must not have a hole in it.\n try {\n outSeq += 1;\n await sealTo({\n v: CONSOLE_FRAME_VERSION,\n kind: \"bye\",\n seq: outSeq,\n reason,\n });\n } catch {\n // The channel is already gone; that is what most `bye`s mean.\n }\n await deps.record({\n sessionId: deps.sessionId,\n at: deps.now(),\n event: \"ended\",\n reason,\n });\n };\n\n deps.shell.onData((chunk) => {\n if (ended !== undefined) return;\n // Split at the frame cap rather than sending one oversized frame the far\n // end is obliged to refuse — a burst of output is normal, not an attack.\n for (let at = 0; at < chunk.length; at += CONSOLE_MAX_DATA_BYTES) {\n const slice = chunk.subarray(at, at + CONSOLE_MAX_DATA_BYTES);\n outSeq += 1;\n toBrowser += 1;\n void sealTo({\n v: CONSOLE_FRAME_VERSION,\n kind: \"stdout\",\n seq: outSeq,\n data: encodeConsoleData(slice),\n }).catch((cause: unknown) => {\n /* Was \"the console channel closed\", flatly, whatever happened — the\n same sentence the socket's own onClose uses, so a failed SEAL and a\n closed SOCKET were one message with two causes and no way to tell\n them apart in a log. */\n log(\"could not send output to the browser\", {\n reason: cause instanceof Error ? cause.message : String(cause),\n fromBrowser,\n toBrowser,\n });\n /* Returned, not voided: the caller already voids the chain, and the\n catch handler owns the shutdown it starts. */\n return finish(\"the console channel closed\");\n });\n }\n });\n\n deps.shell.onExit((reason) => void finish(reason));\n\n return {\n get ended() {\n return ended;\n },\n\n async stop(reason: string) {\n await finish(reason);\n },\n\n async deliver(envelope: SealedEnvelope) {\n if (ended !== undefined) return;\n\n const opened = await open({\n envelope,\n recipientKeys: deps.keys,\n senderIdentityPublic: deps.browser.identity,\n expected: {\n jobId: deps.sessionId,\n senderKeyId: browserKeyId,\n recipientKeyId: boxKeyId,\n direction: \"payload\",\n },\n });\n if (!opened.ok) {\n await finish(`a console frame did not verify (${opened.reason})`);\n return;\n }\n\n const parsed = ConsoleFrame.safeParse(\n JSON.parse(opened.plaintext) as unknown,\n );\n if (!parsed.success) {\n await finish(\"a console frame was not a console frame\");\n return;\n }\n const frame = parsed.data;\n\n const order = inbound.accept(frame);\n if (!order.ok) {\n // Named in the reason, because these are the four things a router can\n // do to a stream it cannot read, and an owner reading the feed after\n // a dropped session deserves to know which one happened.\n await finish(`the console stream was ${order.fault}`);\n return;\n }\n\n switch (frame.kind) {\n case \"hello\": {\n // The hub told us this key; the browser now tells us the same key\n // from inside the ciphertext. Disagreement means the two are not\n // talking about the same browser.\n const same =\n frame.browser.identity === deps.browser.identity &&\n frame.browser.encryption === deps.browser.encryption &&\n frame.browser.encryptionSig === deps.browser.encryptionSig;\n if (!same || !verifyPublicIdentity(frame.browser)) {\n await finish(\n \"the console key announced was not the key that spoke\",\n );\n return;\n }\n deps.shell.resize(frame.cols, frame.rows);\n started = true;\n await deps.record({\n sessionId: deps.sessionId,\n at: deps.now(),\n event: \"started\",\n });\n return;\n }\n case \"stdin\":\n /* Unreachable, and left explicit for the same reason `stdout` below\n is: every path that leaves `started` false also ends the session,\n and `deliver` returns at the door once it has. Keeping the guard\n costs nothing; making it SPEAK would have been a log line for a\n case that cannot happen, which is how a silent drop gets looked\n for in the wrong place. The keystrokes really were vanishing —\n see `decodeConsoleData`, which is where. */\n if (!started) return;\n fromBrowser += 1;\n deps.shell.write(Buffer.from(consoleDataBytes(frame.data)));\n return;\n case \"resize\":\n if (!started) return; // Unreachable; see `stdin` above.\n fromBrowser += 1;\n deps.shell.resize(frame.cols, frame.rows);\n return;\n case \"stdout\":\n // `consoleOrder` already refuses this as `wrong-way`; unreachable,\n // and left explicit so a future kind cannot fall through silently.\n await finish(\"the console stream was wrong-way\");\n return;\n case \"bye\":\n await finish(frame.reason);\n return;\n }\n },\n };\n}\n","import { Buffer } from \"node:buffer\";\nimport type { ConsoleShell } from \"./console-agent.js\";\n\n/**\n * A real terminal for a console session, over `node-pty` — RULED by Todd,\n * 2026-09-16: *\"Why don't we add node-pty to the container build instead of a\n * 7th package? If we only need it for hosted that doesn't make sense to\n * publish.\"*\n *\n * ## Why there is no dependency entry anywhere for this\n *\n * `node-pty` is a NATIVE module, and `byollm` is the package every user\n * installs. Three shapes were weighed: `optionalDependencies` plus a lazy\n * import (mine), a seventh published package (CW's), and this. Todd's is\n * smaller than both, and it is the only one where a broken `npm install\n * byollm` is impossible **by construction** rather than by a mechanism\n * behaving as designed — there is no entry in any manifest to go wrong.\n *\n * The box's Dockerfile installs `node-pty` globally beside `byollm`, and\n * Node's resolution walks ancestors until it reaches the global\n * `lib/node_modules`, so a global `byollm` finds a global sibling. **Measured\n * before this was written**, from an unrelated working directory, because the\n * whole shape rests on it.\n *\n * Everywhere else it is simply absent, and this says so in one line rather\n * than failing with a module-resolution stack trace at the moment somebody\n * opens a console.\n */\n\n/** The subset of `node-pty` used here. Typed locally: there is no dependency\n * to take types from, and inventing one would be the entry we just avoided. */\ninterface PtyProcess {\n onData(cb: (data: string) => void): void;\n onExit(cb: (e: { exitCode: number; signal?: number }) => void): void;\n write(data: string): void;\n resize(cols: number, rows: number): void;\n kill(signal?: string): void;\n}\ninterface NodePty {\n spawn(\n file: string,\n args: readonly string[],\n options: {\n name: string;\n cols: number;\n rows: number;\n cwd: string;\n env: Record<string, string>;\n },\n ): PtyProcess;\n}\n\nexport const NO_PTY_MESSAGE =\n \"byollm console-agent needs node-pty, which is a hosted-box feature and is \" +\n \"not installed here.\\nIf you are self-hosting a box, install it beside \" +\n \"byollm: npm i -g node-pty\";\n\n/** Why {@link openPtyShell} could not start. */\nexport class NoPtyError extends Error {\n constructor() {\n super(NO_PTY_MESSAGE);\n this.name = \"NoPtyError\";\n }\n}\n\nexport interface PtyShellOptions {\n readonly command: string;\n readonly args: readonly string[];\n readonly cwd: string;\n readonly env: Record<string, string>;\n readonly cols?: number;\n readonly rows?: number;\n /** Injected in tests; production takes the real module. */\n readonly load?: () => Promise<NodePty>;\n}\n\nconst loadNodePty = async (): Promise<NodePty> => {\n // A dynamic import, and a variable specifier so no bundler or static\n // analyser turns this into a hard dependency on the way past.\n const id = \"node-pty\";\n const loaded: unknown = await import(id);\n return loaded as NodePty;\n};\n\n/** Is this \"the module is not here\", as opposed to \"it is here and broken\"? */\nconst isMissingModule = (error: unknown): boolean => {\n if (typeof error !== \"object\" || error === null) return false;\n const code = (error as { code?: unknown }).code;\n return code === \"ERR_MODULE_NOT_FOUND\" || code === \"MODULE_NOT_FOUND\";\n};\n\n/**\n * How much a shell may say before anyone is listening.\n *\n * Sized for a greeting and a prompt with room to spare, not for output: a\n * console that nobody attaches to is a leak, and the bound is what makes the\n * hold safe to do at all.\n */\nconst HELD_BEFORE_ATTACH_BYTES = 64 * 1024;\n\nexport async function openPtyShell(\n options: PtyShellOptions,\n): Promise<ConsoleShell> {\n /**\n * The wrapping lives HERE rather than inside the production loader, so the\n * injected and real paths behave identically — a test that exercised a\n * different error path from production was how this was found.\n *\n * And only a MISSING module becomes {@link NoPtyError}. A node-pty that is\n * present but unloadable — a native build against the wrong ABI, the\n * classic — must not be reported as \"not installed\": that sentence sends\n * somebody to install what they already have.\n */\n let pty: NodePty;\n try {\n pty = await (options.load ?? loadNodePty)();\n } catch (error) {\n if (isMissingModule(error)) throw new NoPtyError();\n throw error;\n }\n\n const child = pty.spawn(options.command, options.args, {\n name: \"xterm-256color\",\n cols: options.cols ?? 80,\n rows: options.rows ?? 24,\n cwd: options.cwd,\n env: options.env,\n });\n\n /**\n * A pty produces from the instant it spawns. The session that consumes it\n * is not built until a socket has been dialled and a door opened, and\n * `onData` only subscribes when it is called — so everything the shell said\n * in that window went to a listener that did not exist yet.\n *\n * What the shell says in that window is its greeting and its first prompt,\n * and the first prompt is the entire signal that it is safe to type. An\n * operator opened a console, saw `[connected]` and an empty pane, and had\n * no way to tell a ready shell from a dead one.\n *\n * This is the third time this gap has been paid for: the browser's hello\n * went into it, then the console's stylesheet, and now the box's own\n * greeting. So the subscription happens at spawn and what arrives is held\n * until somebody comes for it.\n */\n let deliver: ((chunk: Buffer) => void) | undefined;\n const held: Buffer[] = [];\n let heldBytes = 0;\n let exited: string | undefined;\n let announce: ((reason: string) => void) | undefined;\n\n child.onData((data) => {\n const chunk = Buffer.from(data, \"utf8\");\n if (deliver !== undefined) {\n deliver(chunk);\n return;\n }\n /* Bounded, because \"nobody ever attaches\" is a reachable state and a\n shell left talking to itself must not grow without limit. A prompt is\n bytes; this is orders of magnitude above one and still finite. */\n if (heldBytes + chunk.length > HELD_BEFORE_ATTACH_BYTES) return;\n held.push(chunk);\n heldBytes += chunk.length;\n });\n\n child.onExit(({ exitCode, signal }) => {\n const reason =\n signal !== undefined && signal !== 0\n ? `the shell was stopped (signal ${String(signal)})`\n : `the shell exited (${String(exitCode)})`;\n /* Same gap, same fix. A shell that dies before the session is built —\n a bad command, a missing binary — would otherwise leave the session\n waiting on an exit that had already happened. */\n if (announce === undefined) {\n exited = reason;\n return;\n }\n announce(reason);\n });\n\n return {\n write(data: Buffer) {\n // node-pty speaks strings. utf8 round-trips what a terminal sends, and\n // the console's traffic is typing and escape sequences.\n child.write(data.toString(\"utf8\"));\n },\n resize(cols: number, rows: number) {\n try {\n child.resize(cols, rows);\n } catch {\n // A pty that has already gone refuses to be resized, and a console\n // session ending is not a reason to throw out of a resize frame.\n }\n },\n onData(handler: (chunk: Buffer) => void) {\n deliver = handler;\n /* Drained before returning, so a consumer that attaches and then asks\n what it has seen gets an answer that includes the greeting. */\n const waiting = held.splice(0, held.length);\n heldBytes = 0;\n for (const chunk of waiting) handler(chunk);\n },\n onExit(handler: (reason: string) => void) {\n announce = handler;\n if (exited !== undefined) handler(exited);\n },\n kill() {\n try {\n child.kill();\n } catch {\n // Already gone. Killing twice is how a session ends cleanly when the\n // shell exited first — `finish()` kills regardless of the reason.\n }\n },\n };\n}\n","import { consoleSession, type ConsoleSessionRecord } from \"./console-agent.js\";\nimport { openPtyShell } from \"./pty-shell.js\";\nimport { NoPtyError } from \"./pty-shell.js\";\nimport type {\n PublicIdentity,\n SealedEnvelope,\n StoredKeys,\n} from \"@byollm/protocol\";\nimport {\n SealedEnvelope as SealedEnvelopeSchema,\n signRequest,\n} from \"@byollm/protocol\";\n\n/**\n * `byollm console-agent` — the box side of a browser console, and the caller\n * that `consoleSession` was missing.\n *\n * ## This is not something `byollm run` ever starts\n *\n * A capability that lets a remote broker drive a pty should be ABSENT from a\n * laptop daemon's behaviour, not merely disabled in it. So it is a subcommand\n * the box's supervisor launches and nothing else invokes — default-off is a\n * mitigation; not-running-unless-invoked is a property. The pty itself is not\n * even installed outside the box image (see {@link openPtyShell}), so on an\n * ordinary machine this command has nothing to run and says so in one line.\n *\n * ## What it does NOT decide\n *\n * Authorisation. The hub decides which owner may open a session against which\n * box, mints the per-session grant, and announces the browser's ephemeral\n * identity. This agent verifies what it can — that every frame is signed by\n * the announced key and that the sealed `hello` names the same one — and\n * carries the ruling's own honesty about the rest: a control plane that mints\n * access can always mint access, which is why every session is written to the\n * owner's feed.\n */\n\n/** The hub's data door. Mirrors the hub's own `CONSOLE_BOX_ENDPOINT`. */\nexport const CONSOLE_BOX_ENDPOINT = \"/console/box\";\n\nexport interface ConsoleAgentOptions {\n /** `wss://…` — the hub's console endpoint for this session. */\n readonly url: string;\n readonly sessionId: string;\n readonly deadlineAt: number;\n readonly keys: StoredKeys;\n /**\n * The runner this box is known by — required to SIGN the data-socket dial.\n *\n * `/console/box` is an authenticated door: it wants runner, issued-at and a\n * signature over the session id, and refuses with 401 before the handshake\n * without them. This dialer sent none, so it could never have connected —\n * the sibling of .97's never-dialed bug, and its exact inverse: there the\n * production default was missing, here it exists and cannot succeed.\n */\n readonly runnerId: string;\n /** The browser's ephemeral identity, as the hub announced it. */\n readonly browser: PublicIdentity;\n /** The restricted shell, as the box image defines it. */\n readonly command: string;\n readonly args: readonly string[];\n readonly cwd: string;\n readonly env: Record<string, string>;\n readonly record: (entry: ConsoleSessionRecord) => Promise<void>;\n /** What the agent did with each frame — see `ConsoleAgentDeps.log`. */\n readonly log?: (message: string, fields?: Record<string, unknown>) => void;\n readonly now?: () => number;\n /** Injected in tests. Production opens a real WebSocket, signed. */\n readonly connect?: (\n url: string,\n headers: Record<string, string>,\n ) => Promise<ConsoleSocket>;\n readonly openShell?: typeof openPtyShell;\n}\n\n/** The transport, as narrow as the agent actually needs it. */\nexport interface ConsoleSocket {\n send(text: string): void;\n onMessage(handler: (text: string) => void): void;\n onClose(handler: (reason: string) => void): void;\n close(): void;\n}\n\n/**\n * Node 22 ships a global `WebSocket`, so the box needs no client library —\n * measured in the image the box actually runs, not assumed from the local\n * Node. That is the second dependency this feature does not add.\n */\nconst connectWebSocket = (\n url: string,\n headers: Record<string, string>,\n): Promise<ConsoleSocket> =>\n new Promise((resolve, reject) => {\n const socket = new WebSocket(url, { headers });\n socket.addEventListener(\"open\", () => {\n resolve({\n send: (text) => {\n socket.send(text);\n },\n onMessage: (handler) => {\n socket.addEventListener(\"message\", (event: MessageEvent) => {\n handler(String(event.data));\n });\n },\n onClose: (handler) => {\n socket.addEventListener(\"close\", () => {\n handler(\"the console channel closed\");\n });\n },\n close: () => {\n socket.close();\n },\n });\n });\n socket.addEventListener(\"error\", () => {\n reject(new Error(`could not reach the console broker at ${url}`));\n });\n });\n\n/**\n * Run one console session to completion. Resolves with why it ended.\n */\nexport async function runConsoleAgent(\n options: ConsoleAgentOptions,\n): Promise<string> {\n const shell = await (options.openShell ?? openPtyShell)({\n command: options.command,\n args: options.args,\n cwd: options.cwd,\n env: options.env,\n });\n\n /**\n * Signed over the SESSION ID, which is what the hub verifies.\n *\n * The control socket signs over its own endpoint because there is no session\n * yet; this one cannot, and must not — a signature captured from one session\n * would otherwise be replayable to join another, which is the whole reason\n * the box door names the session in the body.\n */\n const signed = signRequest(options.keys, {\n endpoint: CONSOLE_BOX_ENDPOINT,\n runnerId: options.runnerId,\n issuedAt: (options.now ?? Date.now)(),\n body: options.sessionId,\n });\n const socket = await (options.connect ?? connectWebSocket)(options.url, {\n \"x-byollm-runner\": signed.runnerId,\n \"x-byollm-issued-at\": String(signed.issuedAt),\n \"x-byollm-signature\": signed.signature,\n });\n\n const session = consoleSession({\n sessionId: options.sessionId,\n deadlineAt: options.deadlineAt,\n keys: options.keys,\n browser: options.browser,\n shell,\n send: (envelope: SealedEnvelope) => {\n socket.send(JSON.stringify(envelope));\n return Promise.resolve();\n },\n record: options.record,\n log: options.log,\n now: options.now ?? (() => Date.now()),\n });\n\n return new Promise<string>((resolve) => {\n const done = (why: string): void => {\n socket.close();\n resolve(session.ended ?? why);\n };\n\n /**\n * Stopping is not finishing, and forgetting that was a real bug here: on a\n * malformed message this ended the SESSION and then waited forever for a\n * `done` that only the delivery path and the socket's close could reach.\n * The process would have sat holding a socket with nothing left to do.\n * Found by a test that TIMED OUT rather than failed, which is the tell.\n */\n const abandon = (why: string): void => {\n session.stop(why).then(\n () => {\n done(why);\n },\n () => {\n done(why);\n },\n );\n };\n\n socket.onMessage((text) => {\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n abandon(\"the broker sent something that was not a frame\");\n return;\n }\n // Parsed, not cast. The broker is not trusted to send well-formed\n // envelopes — it is not trusted at all, which is the point of sealing.\n const envelope = SealedEnvelopeSchema.safeParse(parsed);\n if (!envelope.success) {\n abandon(\"the broker sent something that was not a frame\");\n return;\n }\n session\n .deliver(envelope.data)\n .then(() => {\n if (session.ended !== undefined) done(session.ended);\n })\n .catch(() => {\n done(\"the console session failed\");\n });\n });\n\n socket.onClose((reason) => {\n session\n .stop(reason)\n .then(() => {\n done(reason);\n })\n .catch(() => {\n done(reason);\n });\n });\n });\n}\n\n/** What the CLI prints when the pty is simply not here. */\nexport function consoleAgentUnavailable(error: unknown): string | undefined {\n return error instanceof NoPtyError ? error.message : undefined;\n}\n","import { z } from \"zod\";\nimport { PublicIdentity } from \"@byollm/protocol\";\n\n/**\n * What the hub tells a box when it wants a console — the one definition of\n * that message, parsed rather than trusted.\n *\n * It arrives as one argument to `byollm console-agent`, which means it comes\n * from the box's supervisor, which got it from the hub. None of those is a\n * reason to skip validating it: the hub is exactly the party the sealing\n * exists to keep out of the session, so a message from it is input.\n */\nexport const ConsoleAgentSpec = z\n .object({\n /** `wss://…`, the broker endpoint for this session. */\n url: z.string().min(1),\n sessionId: z.string().min(1),\n deadlineAt: z.number().int().positive(),\n /** The browser's ephemeral identity, pinned for this session. */\n browser: PublicIdentity,\n shell: z\n .object({\n command: z.string().min(1),\n args: z.array(z.string()).default([]),\n cwd: z.string().min(1),\n env: z.record(z.string(), z.string()).default({}),\n })\n .strict(),\n })\n .strict();\nexport type ConsoleAgentSpec = z.infer<typeof ConsoleAgentSpec>;\n","import { signRequest, type StoredKeys } from \"@byollm/protocol\";\nimport { ConsoleAgentSpec } from \"./console-agent-spec.js\";\nimport type { ConsoleSocket } from \"./console-agent-main.js\";\n\n/**\n * Waiting to be told a console is wanted — the box side of B018c's hole 2.\n *\n * Until this existed a console session could be minted, opened by a browser,\n * and never joined: `byollm console-agent` took a session description and\n * **nothing ever gave it one**. The browser end is what exposed that, because\n * nothing else had ever tried to be the other party.\n *\n * ## Why a held socket rather than the heartbeat\n *\n * Ruled by CW, 2026-09-16, and the losing option was reasonable. The daemon\n * already heartbeats, so the response could have carried a pending console and\n * nothing would stay connected — but the console is INTERACTIVE, and a\n * heartbeat interval before anything happens reads as broken to the person who\n * just clicked. The showcase is \"setup via the website\"; the first impression\n * is the connect. It would also be a wire change, which the schema lock prices\n * at a version both ends move together.\n *\n * ## It carries announcements, never frames\n *\n * This socket learns that a session exists and nothing else. The session's\n * bytes go over a second, separate connection — the data door the broker\n * already relays blindly. Keeping them apart means the broker never has to\n * parse what it carries, which is the property the whole design rests on.\n */\n\n/**\n * What the hub sends down the control socket. Parsed, never trusted.\n *\n * Not exported: it is structural, and every caller reaches it through\n * `ConsoleListenDeps[\"run\"]` rather than by name. An export nothing imports is\n * surface without a reader, which `knip` is right to object to.\n */\ninterface ConsoleAnnouncement {\n readonly sessionId: string;\n readonly deadlineAt: number;\n readonly browser: ConsoleAgentSpec[\"browser\"];\n}\n\n/** The endpoint the signature covers. One definition; the hub has the other. */\nexport const CONSOLE_DEVICE_ENDPOINT = \"/console/device\";\n\nexport interface ConsoleListenDeps {\n /** `wss://hub…/console/device`, already absolute. */\n readonly url: string;\n readonly runnerId: string;\n readonly keys: StoredKeys;\n /** Run one session. Returns when it ends. */\n readonly run: (announcement: ConsoleAnnouncement) => Promise<void>;\n readonly log: (message: string, fields?: Record<string, unknown>) => void;\n readonly now?: () => number;\n /**\n * Injected in tests. **Production uses {@link dialControlSocket}**, and the\n * absence of that default is the whole reason a console never worked.\n *\n * `dial()` began `if (deps.connect === undefined) return;` and the CLI never\n * passed one — so on every box, in every release, this function opened no\n * socket, registered with no broker, and returned without a word. The box\n * printed \"listening for consoles\", which was the last true thing it said.\n *\n * It survived because it is invisible from both ends. The box looks healthy\n * (nothing failed). The hub looks healthy (a device that never attached is\n * indistinguishable from one that is merely offline). And .96's keepalive\n * made it worse by making it calm: before, the process at least crash-looped\n * loudly; after, it held a socketless silence forever.\n */\n readonly connect?: (\n url: string,\n headers: Record<string, string>,\n ) => Promise<ConsoleSocket>;\n /** Injected in tests, so a reconnect ladder can be walked without waiting. */\n readonly wait?: (ms: number) => Promise<void>;\n /** Injected in tests. Holds the event loop open; see `keepalive` below. */\n readonly keepalive?: () => { stop(): void };\n}\n\n/** Headers the box presents. Mirrors the hub's `BOX_HEADERS`. */\nexport const deviceHeaders = (\n keys: StoredKeys,\n runnerId: string,\n now: number,\n): Record<string, string> => {\n const signed = signRequest(keys, {\n endpoint: CONSOLE_DEVICE_ENDPOINT,\n runnerId,\n issuedAt: now,\n /* The endpoint signs for itself: there is no session yet, which is the\n entire point of this door. A replay inside the skew window opens a\n socket that receives the announcements the real box would receive —\n it buys an attacker nothing it did not already have. */\n body: CONSOLE_DEVICE_ENDPOINT,\n });\n return {\n \"x-byollm-runner\": signed.runnerId,\n \"x-byollm-issued-at\": String(signed.issuedAt),\n \"x-byollm-signature\": signed.signature,\n };\n};\n\nexport interface ConsoleListener {\n /** Stop listening. Any session already running is left to finish. */\n stop(): void;\n readonly listening: boolean;\n}\n\n/**\n * Hold the control socket and run a session per announcement.\n *\n * One session at a time, deliberately: a box is one person's machine with one\n * console, and two consoles typing into one shell is not a feature anybody\n * asked for. A second announcement while one is running is refused loudly\n * rather than queued — a console that opens minutes later, when the person has\n * given up and clicked again, is worse than one that says no.\n */\n/**\n * The control socket a box holds open, with the headers that say which box.\n *\n * Node's global `WebSocket` takes a `headers` option, which is not in the\n * WHATWG standard but is what makes this possible without a client library —\n * the browser's door has to carry its credentials in the query string\n * precisely because a browser cannot do this.\n *\n * **Measured in the image the box actually runs**, per the standard\n * `console-agent-main.ts` set for the same question: `node:22-bookworm-slim`\n * at the pinned digest, Node v22.23.2, header received. Not assumed from the\n * local Node, which is a 24.\n */\nconst dialControlSocket = (\n url: string,\n headers: Record<string, string>,\n): Promise<ConsoleSocket> =>\n new Promise((resolve, reject) => {\n /* The options bag is accepted by the runtime and by the type — no cast\n needed, which is worth noticing: the headers path is supported rather\n than smuggled. */\n const socket = new WebSocket(url, { headers });\n socket.addEventListener(\"open\", () => {\n resolve({\n send: (text) => {\n socket.send(text);\n },\n onMessage: (handler) => {\n socket.addEventListener(\"message\", (event: MessageEvent) => {\n handler(String(event.data));\n });\n },\n onClose: (handler) => {\n socket.addEventListener(\"close\", (event: CloseEvent) => {\n /* The code, because \"it closed\" was never the question. A 1006\n and a 1008 send an operator to different places, and the door\n refusing a signature looks identical to a network drop without\n it. */\n handler(\n `${String(event.code)}${event.reason === \"\" ? \"\" : ` ${event.reason}`}`,\n );\n });\n },\n close: () => {\n socket.close();\n },\n });\n });\n socket.addEventListener(\"error\", () => {\n reject(new Error(`could not reach the console broker at ${url}`));\n });\n });\n\nexport function consoleListener(deps: ConsoleListenDeps): ConsoleListener {\n const now = deps.now ?? Date.now;\n let stopped = false;\n let running = false;\n let socket: ConsoleSocket | undefined;\n\n const handle = (text: string): void => {\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n deps.log(\"the console broker sent something that was not a message\");\n return;\n }\n const message = parsed as { type?: unknown } | null;\n if (message?.type !== \"console-session\") return;\n\n /* `type` is stripped before parsing, and the reason is the one this\n project documented in the schema lock this morning: the spec is\n `.strict()`, so an UNKNOWN KEY IS REFUSED — passing the whole message\n through made every announcement unreadable. The envelope's routing\n field is not part of the thing it routes. */\n const { type: _routing, ...body } = message as Record<string, unknown>;\n const spec = ConsoleAnnouncement(body);\n if (spec === undefined) {\n deps.log(\"the console broker announced a session we cannot read\");\n return;\n }\n\n if (running) {\n /* Said, not swallowed. The person clicked and nothing will happen; the\n log is the only place that can explain why. */\n deps.log(\"a console is already running on this box\", {\n refused: spec.sessionId,\n });\n return;\n }\n\n running = true;\n deps.log(\"a console session was announced\", { session: spec.sessionId });\n void deps\n .run(spec)\n .catch((cause: unknown) => {\n deps.log(\"the console session ended badly\", {\n session: spec.sessionId,\n reason: cause instanceof Error ? cause.message : String(cause),\n });\n })\n .finally(() => {\n running = false;\n });\n };\n\n /**\n * Something that holds the event loop open ON PURPOSE.\n *\n * The box logs on 09-17 read: \"listening for consoles\" → Node's *\"Detected\n * unsettled top-level await\"* → exit → supervisor restart, every few\n * seconds. The listener was alive only for as long as its socket was: the\n * caller parks on `await new Promise(() => undefined)`, which settles never\n * and REFERENCES nothing, so the moment the socket closed the loop had no\n * work left, drained, and Node exited with that await still pending.\n *\n * A process whose lifetime is a side effect of an open socket cannot\n * reconnect, because reconnecting is something you do after the socket is\n * gone. So the listener owns a handle of its own and keeps it until `stop()`.\n */\n const alive = (deps.keepalive ?? defaultKeepalive)();\n\n /* 1s doubling to 30s, reset on every successful dial. A box whose hub is\n briefly away should be back in a second; a box whose hub is down for an\n hour should not spend that hour dialling. */\n let backoffMs = 1_000;\n const wait = deps.wait ?? ((ms) => new Promise((r) => setTimeout(r, ms)));\n\n const redial = (why: string, detail?: string): void => {\n if (stopped) return;\n const delay = backoffMs;\n backoffMs = Math.min(backoffMs * 2, 30_000);\n /* SAYS WHY, every time. The old listener printed one optimistic line and\n then died silently on a loop — B228's status-lie in a sidecar: the\n process reported what it intended, never what happened to it. */\n deps.log(\"the console control socket is gone — reconnecting\", {\n why,\n ...(detail === undefined ? {} : { detail }),\n inMs: delay,\n });\n /* Handled rather than voided, and the rule that insisted is right: a\n rejection here is the reconnect quietly not happening, which is the\n exact silence this whole change exists to end. */\n wait(delay)\n .then(() => {\n if (stopped) return undefined;\n return dial();\n })\n .catch((cause: unknown) => {\n deps.log(\"the reconnect itself failed\", {\n reason: cause instanceof Error ? cause.message : String(cause),\n });\n });\n };\n\n const dial = async (): Promise<void> => {\n /* Defaulted, never skipped. An absent dependency that turns the whole\n function into a silent no-op is not a safe default — it is the bug. */\n const connect = deps.connect ?? dialControlSocket;\n try {\n socket = await connect(\n deps.url,\n deviceHeaders(deps.keys, deps.runnerId, now()),\n );\n } catch (cause) {\n redial(\n \"the broker refused or could not be reached\",\n cause instanceof Error ? cause.message : String(cause),\n );\n return;\n }\n if (stopped) {\n socket.close();\n return;\n }\n backoffMs = 1_000;\n deps.log(\"holding the console control socket\");\n socket.onMessage(handle);\n socket.onClose((reason) => {\n redial(\"the broker closed it\", reason);\n });\n };\n dial().catch(() => {\n /* `dial` already logs what it could not do; this is the last net so a\n rejection here cannot take the daemon down with it. */\n });\n\n return {\n get listening() {\n return !stopped;\n },\n stop() {\n stopped = true;\n alive.stop();\n socket?.close();\n },\n };\n}\n\n/** A bare timer, unref'd nowhere: holding the loop open is its whole job. */\nfunction defaultKeepalive(): { stop(): void } {\n const handle = setInterval(() => undefined, 60_000);\n return {\n stop() {\n clearInterval(handle);\n },\n };\n}\n\n/** Read an announcement, or undefined. The hub is input, like anything else. */\nfunction ConsoleAnnouncement(value: unknown): ConsoleAnnouncement | undefined {\n const parsed = ConsoleAgentSpec.pick({\n sessionId: true,\n deadlineAt: true,\n browser: true,\n }).safeParse(value);\n return parsed.success ? parsed.data : undefined;\n}\n","import { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, rename, rm, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { PublicIdentity } from \"@byollm/protocol\";\nimport { z } from \"zod\";\nimport { normalizeOrigin, UnusableOrigin } from \"./origins.js\";\n\n/**\n * One paired server.\n *\n * A daemon may pair with several apps; each pairing is a separate identity\n * with its own token and its own owner. Nothing is shared between them —\n * `owner` from one server means nothing on another\n * ({@link MUSTS.PAIR_ONE_USER}).\n */\nexport const Pairing = z\n .object({\n origin: z.string().min(1),\n runnerId: z.string().min(1),\n /** The app's id for this daemon's owner. */\n owner: z.string().min(1),\n ownerLabel: z.string().optional(),\n /**\n * The sites this pairing covers, keyed by each site's identity key id.\n *\n * Pinned rather than fetched: a key re-fetched on each connection is a\n * key an upstream can change, which is the whole thing pinning prevents.\n * The owner compares a fingerprint against what the site displays.\n *\n * A **map**, because one pairing covers an upstream rather than a site\n * (cloud_009 §5): a user who connects a site on a web dashboard has no\n * reason to run a command on a laptop afterwards, so the set follows\n * consent and arrives on the heartbeat. A direct site is one entry, which\n * is the same shape rather than a special case.\n *\n * `site` and `token` are gone. `token` was a bearer credential nothing\n * had minted, sent or read since alpha.18 — a secret kept at rest for\n * nothing. `site` was the single-site shape, and carrying both would be\n * two answers to \"which key opens this\", which is this project's most\n * repeated bug. Pre-1.0, an existing pairing file is re-paired rather\n * than migrated, and the README says so.\n */\n sites: z.record(z.string().min(1), PublicIdentity),\n /**\n * Every site ever approved here, with the key it was approved under —\n * V1-1.\n *\n * `sites` follows consent and shrinks; this only grows. Without it, an\n * upstream could drop a site id from one heartbeat and re-offer it on the\n * next under a key of its own choosing, and the daemon would read the\n * second offer as somebody new rather than as the substitution pinning\n * exists to refuse.\n *\n * Optional on disk: a file written before this existed is read as \"the\n * sites in it were approved\", which is true — they came through\n * `connect`'s fingerprint compare.\n */\n known: z.record(z.string().min(1), PublicIdentity).optional(),\n /**\n * The control plane's grant-signing key, pinned at pairing —\n * Amendment J.\n *\n * Optional because a direct-mode server has no control plane and signs\n * nothing, and because every pairing written before this existed has\n * none. A pairing without one serves its owner alone, which is the\n * correct amount of function for a relationship whose admission authority\n * was never established.\n */\n controlPlanePublic: z.string().min(1).optional(),\n pairedAt: z.number().int().positive(),\n })\n .strict();\nexport type Pairing = z.infer<typeof Pairing>;\n\n/**\n * Drop the site entries that cannot be read, and name them — V1-9.\n *\n * Mutates the row in place before it is parsed, which is the only point where\n * \"skip what you cannot read\" can be applied per entry to a `z.record`.\n * Returns the keys removed, so a caller can say which rather than leaving\n * somebody to wonder why a site went quiet.\n */\nfunction siftEntries(row: unknown): string[] {\n const dropped: string[] = [];\n if (typeof row !== \"object\" || row === null) return dropped;\n for (const field of [\"sites\", \"known\"] as const) {\n const map = (row as Record<string, unknown>)[field];\n if (typeof map !== \"object\" || map === null) continue;\n const kept: Record<string, unknown> = {};\n for (const [id, entry] of Object.entries(map as Record<string, unknown>)) {\n if (PublicIdentity.safeParse(entry).success) {\n kept[id] = entry;\n continue;\n }\n dropped.push(`${field}.${id}`);\n }\n // Rebuilt rather than deleted from: a map with entries removed one by one\n // is the same map, and this way the row that gets parsed is only ever\n // made of entries that parsed.\n (row as Record<string, unknown>)[field] = kept;\n }\n return dropped;\n}\n\n/**\n * The file's shape, with its rows left unparsed — cloud_008 §2.3a.\n *\n * `z.array(z.unknown())` on purpose. This used to be `z.array(Pairing)`, and\n * `safeParse` is all-or-nothing: **one malformed row silently disconnected\n * the daemon from every site it had paired with.** The CLI then said \"not\n * paired with <origin>\" — a true sentence about a state nobody intended — and\n * `byollm list` showed nothing rather than showing a problem.\n *\n * Third instance of the shape in this brief. §0.1 was the control-plane\n * projection, where one bad device row froze revocation for everyone; §2.1a\n * was the routing store, where two stubs written by an older version denied\n * every claim on the hub. This is the same lesson on the daemon's own disk:\n * **parse per row, skip what you cannot read, and say which one.**\n *\n * A bad row here is one pairing's problem. It was never everyone's.\n */\nconst PairingFile = z\n .object({ version: z.literal(1), pairings: z.array(z.unknown()) })\n .strict();\n\n/**\n * Fields written by a version that had machinery this one does not.\n *\n * `Pairing` is `.strict()`, so leaving these in place would quarantine every\n * pairing written by alpha.53–.57 — a device that appears to have forgotten\n * every site it serves, over a field nobody needs. Stripping them silently\n * would be the other failure: state from deleted machinery is cleaned up or\n * refused loudly, never half-read.\n *\n * So they are stripped *and* announced. What the announcement is for: a\n * roster held on disk was this device's answer to \"who may use me\", and its\n * removal is a real change in how the machine behaves, not housekeeping.\n */\nconst RETIRED_PAIRING_FIELDS = Object.freeze([\n \"roster\",\n \"rosterRefusal\",\n \"pending\",\n] as const);\n\n/** A row that would not parse, for the caller to report. */\nexport interface SkippedPairing {\n /** Whatever the row called its origin, when it had a usable one. */\n readonly origin: string;\n /** Which fields failed, never their values. */\n readonly problem: string;\n}\n\n/** The daemon's paired servers, on disk. */\nexport class Pairings {\n readonly #path: string;\n #pairings: Pairing[] = [];\n #skipped: SkippedPairing[] = [];\n #retired: string[] = [];\n #loaded = false;\n\n constructor(path: string) {\n this.#path = path;\n }\n\n async load(): Promise<void> {\n this.#pairings = [];\n this.#skipped = [];\n this.#retired = [];\n let file: unknown;\n try {\n file = JSON.parse(await readFile(this.#path, \"utf8\"));\n } catch (error) {\n // A daemon that has never paired is the ordinary case rather than an\n // error, so a missing file stays silent. **Anything else does not** —\n // V1-9. Unreadable and never-paired were one branch, and they say\n // opposite things: one means \"run `byollm connect`\", the other means\n // this machine has pairings it cannot see, and the CLI would have\n // offered the first advice in both cases.\n const code = (error as NodeJS.ErrnoException).code;\n if (code !== \"ENOENT\") {\n this.#skipped.push({\n origin: this.#path,\n problem:\n code === undefined\n ? \"the pairings file is not valid JSON\"\n : `the pairings file could not be read (${code})`,\n });\n }\n this.#loaded = true;\n return;\n }\n\n const parsed = PairingFile.safeParse(file);\n if (!parsed.success) {\n this.#skipped.push({\n origin: this.#path,\n problem: \"the pairings file is not in a shape this version can read\",\n });\n this.#loaded = true;\n return;\n }\n\n for (const row of parsed.data.pairings) {\n // Per *entry*, one level further down than §2.3a went — V1-9.\n //\n // `sites` is a single `z.record`, so one unreadable site entry failed\n // the whole pairing: the amplification this file's own comment is about,\n // repeating itself inside the row it fixed. A machine paired with four\n // sites lost all four because one of them was written by a version that\n // spells a key differently.\n const dropped = siftEntries(row);\n // Retired machinery, off the row before it is parsed — see\n // {@link RETIRED_PAIRING_FIELDS}.\n const record = row as Record<string, unknown>;\n const carried = RETIRED_PAIRING_FIELDS.filter(\n (field) => record[field] !== undefined,\n );\n if (carried.length > 0) {\n const origin =\n typeof record[\"origin\"] === \"string\" ? record[\"origin\"] : \"a pairing\";\n this.#retired.push(\n `${origin} carried a held roster; this version admits per job ` +\n `from a signed grant instead, so the roster was dropped`,\n );\n }\n /**\n * Rebuilt without the retired keys, rather than deleted from.\n *\n * Assigning `undefined` was the first attempt and it does not work:\n * `.strict()` rejects a **key it does not declare**, and a key holding\n * `undefined` is still a key. The test above caught it, which is the\n * only reason this comment is not still claiming otherwise.\n */\n const cleaned =\n carried.length === 0\n ? row\n : Object.fromEntries(\n Object.entries(record).filter(\n ([key]) =>\n !(RETIRED_PAIRING_FIELDS as readonly string[]).includes(key),\n ),\n );\n const pairing = Pairing.safeParse(cleaned);\n if (pairing.success) {\n // Normalized once, here, so every comparison below is `===` on a key\n // this class produced rather than a function call on whatever a\n // previous version happened to write down. A row whose origin will\n // not normalize is quarantined like any other unreadable row: it is\n // one pairing's problem, it is reported, and it is never silently\n // given a key that could collide with a real one.\n let origin: string;\n try {\n origin = normalizeOrigin(pairing.data.origin);\n } catch (error) {\n this.#skipped.push({\n origin: pairing.data.origin,\n problem:\n error instanceof UnusableOrigin\n ? `the origin is unusable — ${error.reason}`\n : \"the origin could not be read\",\n });\n continue;\n }\n this.#pairings.push({ ...pairing.data, origin });\n for (const name of dropped) {\n this.#skipped.push({\n origin: pairing.data.origin,\n problem: `a site entry could not be read (${name})`,\n });\n }\n continue;\n }\n const origin = (row as { origin?: unknown }).origin;\n this.#skipped.push({\n origin: typeof origin === \"string\" ? origin : \"an unnamed entry\",\n // Paths, not values: a pairing row holds pinned keys, and a\n // diagnostic that quotes the row puts key material in a log. (It held\n // a bearer token too, until alpha.25 removed a credential nothing had\n // ever sent — the rule outlived the secret, and so should the habit.)\n problem: pairing.error.issues.map((i) => i.path.join(\".\")).join(\", \"),\n });\n }\n this.#loaded = true;\n }\n\n /**\n * Rows the last {@link load} could not read.\n *\n * Exposed rather than logged from here: this class has no opinion about\n * where a message goes, and the CLI is the thing with a user in front of\n * it. Empty on a healthy file, which is what makes it worth checking.\n */\n get skipped(): readonly SkippedPairing[] {\n this.#assertLoaded();\n return [...this.#skipped];\n }\n\n /**\n * Machinery this version removed, found in the file and taken out of it.\n *\n * Separate from {@link skipped} because they are different events with\n * different remedies: a skipped row is something wrong that somebody may\n * need to fix, and a retirement is something we changed and owe them a\n * sentence about. One list for both would have a device reporting our\n * decisions as its own problems.\n */\n get retired(): readonly string[] {\n this.#assertLoaded();\n return [...this.#retired];\n }\n\n list(): readonly Pairing[] {\n this.#assertLoaded();\n return [...this.#pairings];\n }\n\n get(origin: string): Pairing | undefined {\n this.#assertLoaded();\n const normalized = normalizeOrigin(origin);\n return this.#pairings.find((pairing) => pairing.origin === normalized);\n }\n\n /** Add or replace the pairing for an origin. Re-pairing supersedes. */\n async put(pairing: Pairing): Promise<void> {\n this.#assertLoaded();\n const normalized = normalizeOrigin(pairing.origin);\n this.#pairings = this.#pairings.filter(\n (existing) => existing.origin !== normalized,\n );\n this.#pairings.push({ ...pairing, origin: normalized });\n await this.#save();\n }\n\n /** Forget a pairing. Returns whether one was removed. */\n async remove(origin: string): Promise<boolean> {\n this.#assertLoaded();\n const normalized = normalizeOrigin(origin);\n const before = this.#pairings.length;\n this.#pairings = this.#pairings.filter(\n (pairing) => pairing.origin !== normalized,\n );\n if (this.#pairings.length === before) return false;\n await this.#save();\n return true;\n }\n\n #assertLoaded(): void {\n if (!this.#loaded) throw new Error(\"pairings used before load()\");\n }\n\n /**\n * Write the file, or leave the old one — V1-9.\n *\n * Written elsewhere and renamed into place, for the reason `identity.ts`\n * gives at length about `keys.json`: an in-place write makes the name exist\n * while the bytes are still arriving, and a daemon reading it in that window\n * sees a truncated file. `load()` treats unparseable as never-paired, so a\n * torn write here disconnected a machine from every site it served — and\n * `byollm list` then said, accurately and uselessly, that nothing was\n * paired.\n *\n * `rename` rather than `link`, because this replaces: `keys.json` is created\n * once and must never be overwritten, so it wants `link`'s EEXIST. Here the\n * whole point is that the new file takes the old one's place in one step.\n */\n async #save(): Promise<void> {\n await mkdir(dirname(this.#path), { recursive: true });\n const body = `${JSON.stringify(\n { version: 1, pairings: this.#pairings },\n null,\n 2,\n )}\\n`;\n // A unique name per attempt: two writers sharing one temp name is how the\n // identity file's first fix broke its own race test.\n const temp = `${this.#path}.${randomUUID()}.tmp`;\n try {\n // Pinned keys live here. Nobody else on a shared machine reads them.\n await writeFile(temp, body, { mode: 0o600 });\n await rename(temp, this.#path);\n } catch (error) {\n await rm(temp, { force: true });\n throw error;\n }\n }\n}\n\n/**\n * Record the site set an upstream just described, if it moved.\n *\n * The heartbeat is the authority on which sites a pairing covers (cloud_009\n * §5), and the file has to follow it: a daemon that learns the set every few\n * seconds and forgets it at every restart behaves differently depending on\n * how recently it was rebooted.\n *\n * Its own function rather than a branch inside the run loop, because a seam\n * that cannot be called cannot be tested — and this one has four outcomes\n * worth naming: nothing paired here, nothing changed, written, and the write\n * failed. Returns what happened so a caller can say so without inspecting the\n * file.\n */\nexport async function recordSites(\n pairings: Pairings,\n origin: string,\n sites: ReadonlyMap<string, PublicIdentity>,\n /**\n * Every site id this device has ever pinned, with the key it pinned.\n *\n * Outlives `sites` deliberately: `sites` follows what the upstream is\n * currently offering, so a site that leaves it is gone — and if that were\n * the only record, an upstream could drop an id and re-offer it under a key\n * of its own choosing. This map only grows, so the second offer is compared\n * against the first.\n */\n extra: { readonly known?: ReadonlyMap<string, PublicIdentity> } = {},\n): Promise<\"unpaired\" | \"unchanged\" | \"written\"> {\n const pairing = pairings.get(origin);\n if (!pairing) return \"unpaired\";\n const next = {\n ...pairing,\n sites: Object.fromEntries(sites),\n ...(extra.known ? { known: Object.fromEntries(extra.known) } : {}),\n };\n // Compared as text, deliberately: the values are small, flat and\n // JSON-shaped, and a deep-equality helper here would be a second\n // implementation of a comparison the file format already defines.\n if (JSON.stringify(next) === JSON.stringify(pairing)) return \"unchanged\";\n await pairings.put(next);\n return \"written\";\n}\n","import { z } from \"zod\";\nimport { CLOCK_SKEW_WARN_MS, GRANT_MAX_AGE_MS } from \"@byollm/protocol\";\nimport { readLedgerSync, writeLedgerSync } from \"./ledger.js\";\n\nconst SpentFile = z\n .object({\n version: z.literal(1),\n /** Grant id to the moment past which it could not be replayed anyway. */\n spent: z.record(z.string(), z.number().int().positive()),\n })\n .strict();\n\n/**\n * Grants this device has already admitted — byollm-review 2026-08-27.\n *\n * A grant admits one job, once. That was enforced by a `Map` that lived only\n * in the process, defended in a comment: \"an entry can only matter for as\n * long as the grant naming it is still fresh, and a device that has restarted\n * since is past that window anyway.\"\n *\n * The second half is false, and it is the half the argument rests on.\n * {@link GRANT_MAX_AGE_MS} is two minutes; a supervised restart — systemd, a\n * crash loop, a deploy — completes in about a second. So the window is not\n * \"past\" at all: the set comes back empty while the grant is still perfectly\n * fresh, and a relay that re-delivers the same stub gets a second admission\n * and a second execution. Double-spend of somebody's metered backend, or\n * duplicated side effects, on work that was already done.\n *\n * ## What durable means here, exactly\n *\n * Written with a **synchronous** write, and that is the whole mechanism. The\n * threat is a process that dies and is restarted by a supervisor, which is\n * survived the moment the bytes reach the operating system — the page cache\n * outlives the process. It is not `fsync`, so a machine losing power between\n * the write and the flush can still forget. That is a worse failure with a\n * far smaller window, and paying an `fsync` per admitted job to close it\n * would put disk latency in front of every claim.\n *\n * Synchronous rather than awaited because {@link Runner.admit} answers\n * synchronously, and the record has to be durable *before* the job runs —\n * writing afterwards would leave exactly the gap this closes. The file holds\n * only ids that are still fresh, so it is small by construction rather than\n * by pruning policy.\n */\nexport class SpentGrants {\n readonly #path: string | undefined;\n #spent = new Map<string, number>();\n #untrusted: string | undefined;\n #untrustedUntil = 0;\n\n /**\n * Without a path this is memory-only, which is what tests want and what\n * direct mode is: no control plane, no grants, nothing to replay.\n */\n constructor(path?: string) {\n this.#path = path;\n }\n\n /**\n * Read what survived the last run, dropping whatever has expired.\n *\n * A file that will not parse is no longer read as \"nothing spent\". That\n * reading was chosen deliberately here — the comment argued that refusing\n * every grant turns a corrupt cache into a total outage — and the argument\n * is sound against refusing *forever*. It is not an argument for this,\n * because the exposure and the remedy have the same clock: an id only\n * matters while the grant naming it is still fresh, so refusing for that\n * long is all fail-closed ever needed. A torn write plus a supervised\n * restart inside a grant's window lets the same valid grant execute twice,\n * with nothing forged.\n */\n load(now: number): void {\n if (this.#path === undefined) return;\n const read = readLedgerSync(this.#path, SpentFile);\n if (read.state === \"loaded\") {\n this.#spent = new Map(Object.entries(read.data.spent));\n } else {\n this.#spent = new Map();\n }\n if (read.state === \"untrusted\") {\n this.#untrusted = read.why;\n /* Everything the lost file could have been protecting is expired by\n here, so this is where refusal stops being protection and starts\n being an outage. Skew is added because the grants being refused were\n stamped by somebody else's clock, and this device tolerates that much\n disagreement everywhere else it reads one. */\n this.#untrustedUntil = now + GRANT_MAX_AGE_MS + CLOCK_SKEW_WARN_MS;\n }\n this.#forget(now);\n }\n\n /**\n * Why every relayed grant is being refused, if it is.\n *\n * This is the one ledger whose brake covers the owner's own jobs too. The\n * other two count what the machine did for other people, and their brakes\n * stop exactly that. This one guards the wire: it is what stands between a\n * re-delivered stub and a second execution, and a duplicate of your own\n * metered job is still your money.\n */\n blockedReason(now: number): string | undefined {\n if (this.#untrusted === undefined) return undefined;\n if (now >= this.#untrustedUntil) {\n // The explicit reset. Nothing that was in the unreadable file can be\n // replayed now, so the empty set in memory is the truth again.\n this.#untrusted = undefined;\n return undefined;\n }\n return this.#untrusted;\n }\n\n /** Has this grant already admitted a job? */\n has(grantId: string, now: number): boolean {\n this.#forget(now);\n return this.#spent.has(grantId);\n }\n\n /**\n * Burn it, durably, and say whether that worked.\n *\n * Returns `false` when the burn is not on disk, and the caller must refuse\n * the job. This used to swallow the write failure and let the job run on\n * in-memory protection alone — which is precisely the protection that a\n * restart erases, so the case where the note fails and the case where the\n * note is needed are the same case.\n */\n spend(grantId: string, issuedAt: number, now: number): boolean {\n if (this.#path === undefined) {\n // Memory-only: direct mode, and the tests. There is no relay here to\n // re-deliver anything, so in-memory is the whole of the guarantee.\n this.#spent.set(grantId, issuedAt + GRANT_MAX_AGE_MS);\n this.#forget(now);\n return true;\n }\n if (this.blockedReason(now) !== undefined) return false;\n\n this.#spent.set(grantId, issuedAt + GRANT_MAX_AGE_MS);\n this.#forget(now);\n try {\n writeLedgerSync(\n this.#path,\n JSON.stringify({\n version: 1,\n spent: Object.fromEntries(this.#spent),\n }),\n );\n return true;\n } catch {\n /* Burned in memory and refused anyway. Keeping it burned is the safe\n direction: this process will not admit it either, and the upstream\n re-offers with a fresh grant rather than retrying this one. */\n return false;\n }\n }\n\n /**\n * Drop entries that can no longer matter.\n *\n * An id only has to outlive the grant naming it: past the freshness window\n * the verifier refuses it anyway, so keeping it would guard a shut door.\n * Swept on use rather than on a timer — a daemon claiming nothing has\n * nothing to forget and should not wake up to say so.\n */\n #forget(now: number): void {\n for (const [id, expiresAt] of this.#spent) {\n if (expiresAt <= now) this.#spent.delete(id);\n }\n }\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/**\n * Where the daemon keeps its state.\n *\n * One directory the owner can `ls`, `cat` and delete. The trust surface is\n * the product (byollm_002), and a trust surface you cannot find is not one.\n */\nexport interface DaemonPaths {\n /** `~/.byollm` — everything below lives here. */\n readonly root: string;\n /** Job routing and backend configuration, owner-authored. */\n readonly config: string;\n /** Paired servers: origin, runner id, token, owner. */\n readonly pairings: string;\n /** The local `team` allowlist — one file, every app. */\n readonly allowlist: string;\n /** Append-only JSONL: every prompt that has run on this machine. */\n readonly ingressLog: string;\n /** Community-job counters, for rate limits and the daily cap. */\n readonly budgets: string;\n /** Estimated money spent running other people's work on metered backends. */\n readonly spend: string;\n /**\n * Grant ids this device has already admitted — byollm-review 2026-08-27.\n *\n * On disk because a grant stays valid for two minutes and a supervised\n * restart takes about one, so an in-process set was empty exactly when it\n * was still needed. Holds only unexpired ids, so it stays small on its own.\n */\n readonly spentGrants: string;\n /**\n * This machine's device keypairs (byollm_009 §3). The most sensitive file\n * the daemon writes: it is the machine's identity, not a token a server can\n * revoke and reissue.\n */\n readonly keys: string;\n /**\n * What the last probe learned about each service — the owner's copy.\n *\n * Written by whoever ran the probe, read by `byollm status`, which is a\n * different process and must not spend a model call of its own to answer\n * \"how are my services\". A canary on a metered backend costs real money,\n * and `status` is a command people run often.\n *\n * Latest state only, never a history: this answers \"where does this stand\n * now\", and a log of every probe would be a log of when somebody's token\n * lapsed, which is nobody's business including ours.\n */\n readonly serviceStates: string;\n /**\n * How the daemon's last conversation with an upstream went.\n *\n * Written by the running daemon, read by `byollm status`, which otherwise\n * cannot ask it anything: the two are separate processes and `state:\n * running` was derived from a local flag alone. That sentence was true\n * for hours while every heartbeat this device sent was refused — the\n * daemon was running and reporting nothing, and the only surface that knew\n * was a log line nobody tails.\n *\n * A file rather than a socket because the question is small and the answer\n * survives a restart of either side.\n */\n readonly health: string;\n /** Liveness, rewritten every beat — B202. */\n readonly heartbeat: string;\n /**\n * What this machine calls itself when it pairs.\n *\n * Its own file rather than a field in `config.json`, because that file is\n * routing and backends — the things somebody edits when work is going\n * wrong — and a name is neither. A one-line file is also a thing a person\n * can `cat` when they are wondering why an approval screen said\n * `todd@Todds-Mac-Studio`.\n */\n readonly label: string;\n /**\n * Per-job scratch directories. A process-class backend runs with its `cwd`\n * set to an empty one of these and nothing else (byollm_004 §2).\n */\n readonly scratch: string;\n}\n\n/** Resolve the daemon's paths, rooted at `~/.byollm` unless overridden. */\nexport function daemonPaths(root = defaultRoot()): DaemonPaths {\n return {\n root,\n config: join(root, \"config.json\"),\n pairings: join(root, \"pairings.json\"),\n allowlist: join(root, \"allow.json\"),\n ingressLog: join(root, \"ingress.log\"),\n budgets: join(root, \"budgets.json\"),\n spend: join(root, \"spend.json\"),\n serviceStates: join(root, \"services.json\"),\n spentGrants: join(root, \"spent-grants.json\"),\n keys: join(root, \"keys.json\"),\n health: join(root, \"health.json\"),\n heartbeat: join(root, \"heartbeat.json\"),\n label: join(root, \"label\"),\n scratch: join(root, \"scratch\"),\n };\n}\n\n/** Where a real device keeps its state, with nothing overriding it. */\nexport function homeRoot(): string {\n return join(homedir(), \".byollm\");\n}\n\n/**\n * `BYOLLM_HOME` is a TEST SEAM. Ruled 09-15 (B205): always or never, never\n * \"sometimes\" — and it stays, explicitly scoped and loud rather than silent.\n *\n * It exists so the conformance kit and the adversarial suite can run real\n * daemons without touching the developer's own `~/.byollm`; `edges.test.ts:48`\n * asserts exactly that. **On a person's own machine, a set `BYOLLM_HOME` is a\n * bug**, and {@link overriddenRootNotice} is what stops that being silent.\n *\n * One deliberate exception, and it is not a person's machine: the box\n * supervisor STATES this variable to the daemon it spawns (`boxDaemon`, B204's\n * rider) so that PID 1 and the daemon cannot disagree about where the heartbeat\n * file lives. It sets it to this same default, which is why the notice below\n * asks whether the root has actually MOVED rather than whether the variable\n * exists — see there.\n */\nexport function defaultRoot(): string {\n return process.env[\"BYOLLM_HOME\"] ?? homeRoot();\n}\n\n/**\n * The one line a daemon says at startup when its state directory is not where\n * it should be — B205 item 2, *\"make production LOUD, not silent\"*.\n *\n * **Keyed on the root having MOVED, not on the variable being set**, and the\n * row's own reasoning is why. B205 justifies this notice as costing nothing\n * because `BYOLLM_HOME` is \"never set in prod\" — and that stopped being true\n * an hour before the row was written. `0c16de6` (which B205 cites, two\n * paragraphs up, as its consistency guarantee) has the box supervisor spawn\n * the daemon with `env: {...process.env, BYOLLM_HOME: home}`, unconditionally.\n * So a presence check would print this warning on **every hosted box, every\n * start, for ever** — a operator's first line in the box log, always false.\n *\n * A warning that is wrong every time is worse than no warning: it is the one\n * people learn to scroll past, and it would be doing that in the exact log\n * where a real fault has to be noticed. Asking whether the root has moved\n * keeps the notice silent on a box (the supervisor sets it to this very\n * default), silent on a normal machine, and loud in the case the row cares\n * about — somebody's real device pointed somewhere else.\n */\nexport function overriddenRootNotice(root = defaultRoot()): string | undefined {\n if (root === homeRoot()) return undefined;\n return `state directory is ${root}, not ${homeRoot()} — BYOLLM_HOME is a test seam and should not be set on a real device`;\n}\n","import type { BackendErrorCode } from \"./backends/types.js\";\n\n/**\n * What a site is told when a backend fails, and why it is never the text.\n *\n * The owner gets the CLI's own words — on Your Devices, in `byollm status`,\n * in the daemon's output and in `ingress.log`. The site gets a class and one\n * fixed sentence per class. Two reasons, and the second is the larger.\n *\n * **The text carries the owner's machine in it.** CLI errors quote paths,\n * usernames, config locations and account emails. A stranger's page is not\n * where those go, and `firstLine(stderr)` was sending them there.\n *\n * **And the message names the service.** \"the claude CLI is not signed in\"\n * tells the site which model answered — the one thing the disclosure fence\n * exists to prevent, arriving through the error path because nobody was\n * looking at the error path. Every message here named its backend, so every\n * failure leaked what every success is careful to hide.\n *\n * So no backend message reaches a site. The class says what a site can act\n * on — is it worth retrying, is it the job or the device — and the sentence\n * says the rest in words that are the same for everybody.\n */\n\n/** The one sentence a site sees when somebody's device could not answer. */\nexport const SERVICE_UNAVAILABLE =\n \"a device service is unavailable — its owner has been told\";\n\n/**\n * The class and sentence for each way a backend can fail.\n *\n * `timeout` and `output-too-large` keep their own class because they are\n * facts about the *job* — a site can shorten a prompt or try again, and\n * neither says anything about the person's setup. Everything else is a fact\n * about somebody's machine and arrives as one class, because telling them\n * apart would be telling the site which machine.\n */\nconst FOR_SITE: Readonly<\n Record<\n BackendErrorCode,\n { code: string; message: string; retryable: boolean }\n >\n> = Object.freeze({\n /* Retryable, and honestly so: memory frees up. It is the one refusal in\n this table that is expected to stop being true within minutes. */\n \"insufficient-memory\": {\n code: \"service_unavailable\",\n message: SERVICE_UNAVAILABLE,\n retryable: true,\n },\n \"backend-unreachable\": {\n code: \"service_unavailable\",\n message: SERVICE_UNAVAILABLE,\n retryable: true,\n },\n \"backend-error\": {\n code: \"service_unavailable\",\n message: SERVICE_UNAVAILABLE,\n retryable: true,\n },\n \"model-not-found\": {\n code: \"service_unavailable\",\n message: SERVICE_UNAVAILABLE,\n retryable: true,\n },\n \"quota-exhausted\": {\n code: \"service_unavailable\",\n message: SERVICE_UNAVAILABLE,\n retryable: true,\n },\n unauthorized: {\n code: \"service_unavailable\",\n message: SERVICE_UNAVAILABLE,\n retryable: true,\n },\n timeout: {\n code: \"timeout\",\n message: \"the device did not answer in time\",\n retryable: true,\n },\n \"output-too-large\": {\n code: \"output-too-large\",\n message: \"the answer was too large to return\",\n retryable: false,\n },\n canceled: {\n code: \"canceled\",\n message: \"the job was canceled\",\n retryable: false,\n },\n});\n\n/**\n * What a site is told, retry decision included — ruled 2026-09-04 (CW).\n *\n * `retryable` used to travel from the backend result, on the reasoning that\n * whether to try again is the site's decision and says nothing about whose\n * machine it was. That held while the flag did not distinguish anything: both\n * failures that reached `service_unavailable` reported `false`.\n *\n * `quota-exhausted` broke it by arriving `true`. Within one site-facing class\n * exactly one path produced that value, so the pair\n * `(service_unavailable, retryable: true)` read as **\"his account is\n * rate-limited\"** — the inference the fence exists to prevent, arriving on the\n * job-failure surface rather than the enqueue one nobody was watching. The two\n * adapters also disagreed: the same block reported `true` from Codex and\n * `false` from Claude.\n *\n * So the flag is a property of the **class**, decided here, and no longer of\n * the individual failure. Two failures a site cannot tell apart by code and\n * message cannot be told apart by this either, which is a shape rather than a\n * promise. The person-or-time question lives at enqueue, in the slot-level\n * wait-bit, and nowhere else.\n *\n * ## And the class value is `true` — corrected 2026-09-04\n *\n * The first version of this flattened to `false` and its comment claimed\n * nothing changed for a site except the leak. **That was wrong, and the\n * review caught it:** the HTTP backend already reported `true` for\n * `backend-unreachable` and for a 5xx `backend-error`, so the class was\n * split three ways and the flatten broke transient retries — a site with\n * retry-on-retryable would stop re-enqueueing after a 503 from somebody's\n * local model server.\n *\n * `true` is also the honest value. `service_unavailable` says nobody can\n * answer *right now*, and right now implies possibly-later. A site that\n * retries a quota block simply meets the fast enqueue refusal, which is the\n * cheap path 019 exists to provide.\n */\n/**\n * Every code that reaches a site as one class, derived rather than listed.\n *\n * The fence test used to name its five siblings by hand, so the claim \"these\n * are indistinguishable\" held by convention: a sixth code added to the table\n * would be a sixth thing a site could tell apart, and no test would notice.\n * Membership comes off the table now, so a new sibling joins the assertion\n * the moment it exists.\n */\nexport function siblingsOf(siteCode: string): BackendErrorCode[] {\n return Object.entries(FOR_SITE)\n .filter(([, forSite]) => forSite.code === siteCode)\n .map(([code]) => code as BackendErrorCode);\n}\n\nexport function outcomeForSite(code: BackendErrorCode): {\n readonly code: string;\n readonly message: string;\n readonly retryable: boolean;\n} {\n return FOR_SITE[code];\n}\n","import { ensureLocalServer, startability } from \"./local-server.js\";\nimport { guardApplies, memoryGate } from \"./memory-gate.js\";\nimport type { MemoryPressure, MemoryReading } from \"./memory.js\";\nimport type { ServiceReport } from \"./service-line.js\";\nimport { knownModelsFor } from \"./known-models.js\";\nimport { outcomeForSite } from \"./site-outcome.js\";\nimport {\n type Succession,\n RETIREMENT_WINDOW_MS,\n walkSuccession,\n ENVELOPE_MAX_AGE_MS,\n keyId,\n open,\n publicIdentityOf,\n seal,\n payloadTextLength,\n sizeClassCeiling,\n fingerprint,\n verifyPublicIdentity,\n type ClaimedStub,\n type JobPayload,\n type PublicIdentity,\n type SealedEnvelope,\n type StoredKeys,\n REFUSAL_MESSAGES,\n matchAudience,\n type Capability,\n type ClaimedJob,\n type RunMetadata,\n /* A VALUE, not only a type — B304. `satisfies SealedOutcome` at the seal\n read like a guarantee and checked nothing: TypeScript excess-property-\n checks a fresh literal, and `ran` arrives as a variable from another\n method whose return type is inferred. */\n SealedOutcome,\n type JobOutcome,\n CLOCK_ATTRIBUTION_MS,\n CLOCK_SKEW_WARN_MS,\n GRANT_MAX_AGE_MS,\n RESERVED_PURPOSE,\n type GrantRefusal,\n type SignedGrant,\n verifyGrant,\n} from \"@byollm/protocol\";\nimport {\n createBackend,\n stopReasonOf,\n type Backend,\n type BackendResult,\n} from \"./backends/index.js\";\nimport { SpentGrants } from \"./spent-grants.js\";\nimport type { Budgets } from \"./budgets.js\";\nimport { ClientError, type ProtocolClient } from \"./client.js\";\nimport { composePrompt } from \"./compose.js\";\nimport type { LoadedConfig, ResolvedRoute } from \"./config.js\";\nimport { writeHealth } from \"./health.js\";\nimport { writeHeartbeat } from \"./heartbeat.js\";\nimport type { IngressLog } from \"./ingress.js\";\nimport { estimateCents, type SpendLedger } from \"./spend.js\";\n\n/** What the daemon is currently doing, for `byollm status`. */\nexport interface RunnerStatus {\n readonly origin: string;\n readonly owner: string;\n readonly runnerId: string;\n readonly revoked: boolean;\n readonly activeJobs: number;\n readonly capabilities: readonly Capability[];\n /** Set when the last server contact failed — one of the four truths. */\n readonly lastError?: string;\n readonly completed: number;\n readonly refused: number;\n}\n\nexport interface RunnerOptions {\n readonly client: ProtocolClient;\n readonly runnerId: string;\n readonly owner: string;\n readonly daemonVersion: string;\n readonly loaded: LoadedConfig;\n /**\n * The control plane's grant-signing key, pinned at pairing — Amendment J.\n *\n * **This field decides which of two regimes the device is in**, and it is\n * the only thing that does.\n *\n * Present: a relayed route with a control plane. Every claimed job must\n * carry a grant that verifies against this key, including the owner's own —\n * a job with no grant is refused rather than admitted by default.\n *\n * Absent: direct mode. There is no control plane to author anything, so\n * there is no way for a device to learn that a stranger may use it, and\n * the owner's own work is the only work that runs (ruled 2026-08-26). A\n * `team` offer on such a device narrows to `private`, loudly, because a\n * scope that admits nobody should not print as though it admits somebody.\n */\n readonly controlPlanePublic?: string | undefined;\n /**\n * Where already-admitted grant ids live across a restart.\n *\n * Optional, and memory-only without it: that is direct mode, where there is\n * no control plane and nothing to replay, and it is what the tests that do\n * not care about persistence get.\n */\n readonly spentGrants?: SpentGrants;\n /**\n * Called when what a service can do changes mid-run — S2, 2026-09-04.\n *\n * The daemon probes; `byollm status` reports; they are different processes\n * and a file is the only thing between them. It was written once at start,\n * so a service that went out of quota an hour later was invisible to every\n * surface except the daemon's own stderr.\n *\n * A callback rather than a path because this class writes no files of its\n * own except the health note, and the CLI already owns where these live.\n */\n readonly onServiceStates?: (\n states: ReadonlyMap<string, ServiceReport>,\n ) => Promise<void>;\n readonly budgets: Budgets;\n /** Tracks money spent on other people's work, for metered backends. */\n readonly spend: SpendLedger;\n readonly ingress: IngressLog;\n /**\n * How this daemon opens work sealed to it, and what it checks it against.\n *\n * `sitePinned` is the identity taken at pairing (byollm_009 §5). Verifying\n * against it — rather than against anything the envelope claims — is what\n * makes a relay unable to substitute work: it can produce an envelope this\n * machine can open, but not one signed by the site.\n */\n readonly identity?: {\n keys(): Promise<StoredKeys>;\n /**\n * Every site this machine is paired with, keyed by the site's identity\n * key id — cloud_009 §5.\n *\n * The same id `stub.site` carries (Amendment A §A.3), so resolving a\n * job's site is a map read rather than a join against a second\n * namespace.\n *\n * The set the *upstream* says this pairing covers, refreshed on every\n * heartbeat (cloud_009 §5). `sitePinned` is gone: one pairing covers an\n * upstream rather than a site, and two answers to \"which key opens this\"\n * is this project's most repeated bug.\n */\n readonly sites: ReadonlyMap<string, PublicIdentity>;\n /**\n * Every site this machine has ever *approved*, with the key it was\n * approved under — including sites whose consent has since ended.\n *\n * The pinning half that the site set alone cannot carry. `sites` follows\n * consent, so a site that leaves it is gone; if that were the only\n * record, an upstream could drop an id and re-offer it under a key of\n * its own choosing and the daemon would read it as somebody new. This map\n * only grows, so the second offer is compared against the first.\n */\n readonly known?: ReadonlyMap<string, PublicIdentity>;\n };\n /** Heartbeat cadence before jitter. */\n readonly heartbeatMs?: number;\n /**\n * Where to record how the upstream conversation is going.\n *\n * Optional because a test or an embedder driving the loop by hand has no\n * `~/.byollm` to write into, and a diagnostic that requires one would make\n * the daemon harder to run than it needs to be.\n */\n readonly healthPath?: string;\n /**\n * Where to write the per-beat liveness record — B202.\n *\n * Separate from {@link RunnerOptions.healthPath} because the two have\n * different cadences and different jobs: health persists on CHANGE and must\n * survive for an owner to read hours later; this is rewritten every beat and\n * means only \"a daemon was alive at this instant\".\n */\n readonly heartbeatPath?: string;\n readonly now?: () => number;\n /** Notified on every state change, so the CLI can render progress. */\n readonly onEvent?: (event: RunnerEvent) => void;\n /** Injectable backend factory, so tests need no real model server. */\n readonly backendFactory?: (route: ResolvedRoute) => Backend;\n /**\n * Start a local model server — B050. Absent means never start one.\n *\n * Injected rather than imported, and ABSENT BY DEFAULT, because this\n * spawns a process on somebody's machine. Every short-lived command that\n * builds a Runner — `connect`, `services`, `status` — would otherwise be\n * able to start a model server as a side effect of asking a question. Only\n * the daemon that runs jobs passes one.\n */\n readonly spawnServer?: (command: readonly string[]) => void;\n\n /**\n * How this machine's memory is read — B080, injected for the reason the\n * clock is.\n *\n * A guard that reads the host inside its own tests is a guard whose tests\n * pass or fail with whatever else is running, and the readings that matter\n * here (2 GB free, pressure critical) are ones you cannot arrange on a\n * developer's laptop on purpose.\n *\n * **Absent means the guard is not active**, the same shape as\n * `spawnServer`: `connect`, `services` and `status` build runners to ask\n * questions, and none of them should be spawning `vm_stat`. The daemon that\n * runs jobs passes one, and `byollm status` says out loud when nothing does\n * — an absent guard that stays quiet is indistinguishable from one that is\n * passing everything.\n */\n /**\n * Whether a binary is on PATH — injected for the same reason the clock is.\n *\n * `isStartable` already took this seam and the Runner never passed one, so\n * the advertising decision consulted the real machine. That made a test of\n * the memory guard's ORDERING pass on a laptop with `ollama` installed and\n * fail on CI, which had none: the route was not startable there, the job\n * never dispatched, and the failure named a timeout rather than the\n * missing binary.\n *\n * A test that reads the host is a test whose verdict depends on the host.\n * Absent still means the real lookup, so production is unchanged.\n */\n readonly onPath?: (binary: string) => Promise<boolean>;\n\n readonly readMemory?: () => Promise<{\n readonly memory: MemoryReading;\n readonly pressure: MemoryPressure;\n }>;\n}\n\n/**\n * Every reason a device turns a grant away.\n *\n * `verifyGrant`'s own refusals, plus the ones only a device can make: a\n * document that is genuine and about something else. Named once because it\n * was spelled twice — the event union and the method that fires it — and two\n * copies of a union drift the first time one gains a member, silently, since\n * the wider one still assigns to nothing.\n */\nexport type GrantRefusalCause =\n | GrantRefusal\n | \"replayed\"\n | \"absent\"\n | \"wrong-user\"\n | \"wrong-site\"\n | \"wrong-kind\"\n | \"wrong-purpose\";\n\nexport type RunnerEvent =\n | { readonly type: \"heartbeat\"; readonly capabilities: number }\n /** A disclosure went stale; the user has something to read — finding 48. */\n | { readonly type: \"awaiting-consent\"; readonly sites: readonly string[] }\n | { readonly type: \"consent-resumed\" }\n /**\n * The hub named a version this machine should move to — B053.\n *\n * An event rather than an action taken here: whether this daemon updates\n * itself is the owner's setting and the supervisor's business, and the\n * runner's job is to say what it heard. A runner that reinstalled its own\n * package would be a runner that could not be tested without one.\n */\n | { readonly type: \"update-offered\"; readonly version: string }\n /** A local model server was found down and started — B050. */\n | { readonly type: \"local-server\"; readonly detail: string }\n /** A pinned site's encryption key moved under its identity — refused. */\n | { readonly type: \"site-key-changed\"; readonly site: string }\n /**\n * The first job from a site this machine has never served — Amendment K.\n *\n * Loud because site policy moved to the control plane, and a change made\n * in an account should still be visible at the hardware that acts on it.\n * Fired at admission rather than after the run: a notice that followed the\n * first job would be a receipt, and the first job is the one worth warning\n * about.\n */\n | {\n readonly type: \"now-serving\";\n readonly site: string;\n readonly fingerprint: string;\n }\n /**\n * A site this machine has never approved arrived on the heartbeat. Nothing\n * is served for it until somebody at this keyboard says so — V1-1.\n */\n /**\n * A service said it is not signed in, so it is no longer advertised.\n *\n * Loud because the failure it replaces was silent: the health check runs\n * `--version`, which needs no credentials, so a signed-out CLI reported\n * healthy and every job failed. One notice, on the first job that proves it.\n */\n | {\n readonly type: \"service-not-signed-in\";\n readonly service: string;\n readonly detail: string;\n }\n /**\n * A service is out of quota, so it is no longer advertised — 019 §3.2.\n *\n * Its own event rather than a variant of the one above, because the two\n * sentences a surface writes from them are opposite: one names something to\n * go and do, and this one names a time to come back. A notice telling\n * somebody to sign in when their account is simply busy is the\n * remedy-must-match-the-cause failure in a new place.\n *\n * No detail field. The CLI's own words are on the service line, where the\n * owner reads them; an event carries only what a surface needs to say that\n * something changed and when it changes back.\n */\n /**\n * A withdrawn service answered again, so it is being advertised — T2-S1.\n *\n * The counterpart to `service-not-signed-in`, and the reason that one is\n * not the end of the story: somebody was told to go and sign in, and this\n * is the daemon noticing that they did.\n */\n | {\n readonly type: \"service-signed-in\";\n readonly service: string;\n }\n | {\n readonly type: \"service-out-of-quota\";\n readonly service: string;\n /** Epoch ms, when the CLI said so. */\n readonly until?: number | undefined;\n }\n /**\n * A grant arrived and was not honoured — Amendment J.\n *\n * Loud, because the consequence is a job that did not run and the cause is\n * invisible from the outside: somebody whose teammate's work stopped\n * landing has no other way to learn that the document saying it could was\n * refused, or which of the checks refused it.\n */\n /**\n * This device's clock disagrees with its control plane enough to matter.\n *\n * Ahead or behind, signed as the device sees it. A warning rather than a\n * refusal: everything still works at this point, which is exactly why it is\n * worth saying — past {@link GRANT_MAX_AGE_MS} of drift the same fact\n * arrives as every relayed job failing.\n */\n | { readonly type: \"clock-skew\"; readonly skewMs: number }\n /** And when it comes back, so a fixed clock stops looking broken. */\n | { readonly type: \"clock-recovered\"; readonly skewMs: number }\n | {\n readonly type: \"grant-refused\";\n readonly refusal: GrantRefusalCause;\n readonly jobId: string;\n }\n /**\n * A site proved continuity from a key this machine already approved, and the\n * approval moved with it — byollm_009 Amendment C.\n *\n * Loud by design (C.5, ruling 3). Rotation is automatic because requiring a\n * second ceremony would train people to approve keys they cannot check, and\n * the price of automatic is that it is never silent: a rotation that\n * produced no line anywhere would make \"your machine only serves keys you\n * approved\" quietly untrue.\n */\n | {\n readonly type: \"site-rotated\";\n readonly site: string;\n readonly from: string;\n readonly fromFingerprint: string;\n readonly fingerprint: string;\n /** Every key id passed through, oldest first. */\n readonly path: readonly string[];\n }\n /** A site the upstream offered whose own account of itself did not add up. */\n | {\n readonly type: \"site-refused\";\n readonly site: string;\n readonly reason: string;\n }\n | { readonly type: \"claimed\"; readonly jobId: string; readonly kind: string }\n | {\n readonly type: \"refused\";\n readonly jobId: string;\n readonly reason: string;\n }\n | {\n readonly type: \"finished\";\n readonly jobId: string;\n readonly outcome: string;\n readonly durationMs: number;\n }\n /**\n * Every slot is held, so nothing new can be claimed — B196.\n *\n * Edge-triggered, like `serving-nothing` above: a poll runs every few\n * seconds and a device that is legitimately busy would otherwise print a\n * line each time.\n *\n * **The silence was the defect.** `#poll` returned here without a claim\n * call, a log line, an event or an error, so a device that had stopped\n * taking work looked identical to one with nothing to do — paired,\n * heartbeating, online on Your Devices. Todd watched `byollm status` hold at\n * ten prompts while two more jobs sat queued, and nothing anywhere said why.\n *\n * It carries both numbers because \"2 of 2\" and \"2 of 8\" are different\n * situations, and the second is not one a default install can reach.\n */\n | {\n readonly type: \"no-free-slot\";\n readonly active: number;\n readonly concurrency: number;\n }\n /** A slot came back, so claiming resumes — the other edge of the above. */\n | { readonly type: \"free-slot-again\"; readonly free: number }\n | { readonly type: \"revoked\" }\n /**\n * Nothing is consented for this machine right now — V1-2. Not revocation:\n * the pairing stands, the pins stand, and the loop keeps beating.\n */\n | { readonly type: \"serving-nothing\" }\n | { readonly type: \"error\"; readonly message: string };\n\n/**\n * How often a withdrawn service is asked whether it can answer again — T2-S1.\n *\n * A failed canary costs nothing: a signed-out CLI fails before it reaches a\n * model. What it does cost is a process spawn, so this is a minute rather\n * than every heartbeat — short enough that somebody who has just signed in\n * does not conclude the remedy failed, long enough not to fork a child every\n * ten seconds forever on a machine whose owner is asleep.\n */\n/**\n * How many times a job may fail to hand over its payload before this device\n * treats it as gone — B038, corrected.\n *\n * One was too few: a site slower than the relay's sealing window produces a\n * 404 for a job that is alive, and refusing on the first made a transient\n * failure permanent. Unbounded was the original bug. Three gives a slow site\n * several fresh sealing windows — each attempt costs a lease lapse, so this\n * is minutes of patience, not milliseconds — and still ends a poison job.\n */\nconst FETCH_ATTEMPTS_BEFORE_GONE = 3;\n\n/** How long a give-up is remembered. Past this the job cannot be offered. */\nconst GAVE_UP_TTL_MS = 10 * 60_000;\n\n/**\n * The backstop that does not have to be clever — B041.\n *\n * {@link FETCH_ATTEMPTS_BEFORE_GONE} ends one poison loop: a job whose\n * payload never arrives. It is the right fix for that failure and it is\n * scoped to it, and the loop it closed was found the way these are always\n * found — by watching one happen.\n *\n * Every other way a job can fail on the way to a result has the same shape.\n * A payload that opens but does not verify, a route that throws, a backend\n * that dies on one particular prompt, an ingress write that cannot land: the\n * daemon does not report a result, the lease lapses, the hub offers the job\n * again, and this device takes it again forever. **Each of those wants its\n * own remedy and none of them should need one for the loop to end.**\n *\n * So this counts attempts on a job id ACROSS CLAIMS and refuses after\n * {@link MAX_JOB_ATTEMPTS}, regardless of why. It is deliberately the\n * dumbest possible rule: a classifier that has to be right about every\n * failure mode is a classifier that will be wrong about the next one, and\n * the next one is the one that loops.\n */\nconst MAX_JOB_ATTEMPTS = 3;\n\n/**\n * How long the same job id waits between attempts on this device.\n *\n * Not a timer, and deliberately not: a job arriving inside its own backoff\n * is simply not worked, and the lease lapses exactly as it does for a fetch\n * give-up. The hub re-offers when the lease expires, so the LEASE is the\n * spacing mechanism and this is only the floor under it. That is the same\n * \"do nothing and let it lapse\" the fetch path settled on, and it asks for\n * no reason code that would be untrue on the wire — `release` has\n * `shutdown`, `pause`, `revoked`, `backend-down` and `refused`, and none of\n * them means \"not yet, offer it later\".\n */\nconst ATTEMPT_BACKOFF_MS = 30_000;\n\n/** How long an attempt record is kept — same reasoning as the give-up TTL. */\nconst ATTEMPT_TTL_MS = 10 * 60_000;\n\n/**\n * A job id is only unique WITHIN a site — B041, found by an existing test.\n *\n * Two sites can each have a `job_1`, and `two-sites.test.ts` claims exactly\n * that pair in one response, on purpose. Per-job bookkeeping keyed on the id\n * alone therefore merges two unrelated jobs: one site's failures are counted\n * against the other's work, and the second site's job is refused for a loop\n * it had no part in.\n *\n * `#gaveUpFetching` had this too, and it is shipped. Narrow — it needs two\n * sites using the same id inside ten minutes — but the failure is a job\n * refused for somebody else's reason, which is the kind that is very hard to\n * read from the outside.\n */\nfunction jobKey(job: { id: string; site?: string }): string {\n return `${job.site ?? \"direct\"}:${job.id}`;\n}\n\nconst AUTH_RECHECK_MS = 60_000;\n\n/**\n * How often a drain checks whether the running work has finished.\n *\n * Short enough that an idle machine updates promptly, long enough that a\n * drain is not a spin loop. Nothing here is waiting on a person.\n */\nconst DRAIN_POLL_MS = 250;\n\n/** The wait a drain uses, injectable so a test does not spend real seconds. */\nconst pause = (ms: number): Promise<void> =>\n new Promise((wake) => setTimeout(wake, ms));\n\n/**\n * How long to wait before looking again for a service that ISN'T there — B198.\n *\n * **Not a cadence.** Todd amended this row away from the every-15-minutes\n * shape he first proposed: *\"do we even need that if we just try the jobs and\n * assume they are online until we hit an error and know they are not?\"* So on\n * a machine where every configured service is advertised, the probe runs at\n * start and then never again on a clock — the steady state costs nothing,\n * which is where the ~17,000 spawns a day were.\n *\n * This interval governs the one case that assumption cannot cover. A service\n * that is NOT advertised is sent no jobs, so no job of its can fail, so the\n * failure-invalidation this row leans on can never fire for it: the owner\n * installs the missing CLI and, with no clock at all, nothing ever looks\n * again. A degraded machine pays a slow clock; a healthy one pays nothing.\n *\n * Deliberately NOT related to {@link DEFAULT_HEARTBEAT_MS}. They answer\n * different questions: the heartbeat is how often this device asks for work,\n * and that IS job latency, so it stays in seconds.\n */\nconst DETECT_INTERVAL_MS = 15 * 60_000;\n\nconst DEFAULT_HEARTBEAT_MS = 10_000;\n\n/**\n * How long to wait after an unproductive tick — B212, Todd's ladder, ruled\n * 09-15.\n *\n * **The flat cadence WAS the product's perceived slowness.** Every chat\n * message is a job, and every job waited up to a full interval for pickup, so\n * a conversation paid ~10s per turn on an idle fleet before a model saw a\n * word of it.\n *\n * Stepped rather than exponential, and expressed as FRACTIONS of the\n * configured heartbeat rather than as seconds: at the default 10s these are\n * Todd's 0/1/2/4/6/8/10, and a deployment that configures a different cadence\n * gets the same shape instead of a ladder that climbs past its own ceiling.\n * A productive tick drops straight back to the first rung.\n */\n/**\n * How long to wait before the next poll — B212.\n *\n * **The ceiling keeps the multiplicative jitter it always had**, and that is\n * not a detail: ±15% of 10s is ±1.5s of fleet desynchronisation, and the whole\n * reason it exists is that idle daemons must not synchronise into a herd\n * against one server. Replacing it with the ladder's small additive jitter\n * would shrink that to ±0.35s and quietly undo a protection nobody asked to\n * remove — the ruling says so in its own third note.\n *\n * Below the ceiling the additive jitter is the right shape instead: at a\n * 1-second rung, ±15% is ±150ms, a tie-break tight enough that the device with\n * the faster clock wins every follow-up. Additive keeps B194's \"ties are luck,\n * not speed\" property where the polling is fastest.\n *\n * A module function rather than a method, and `random` injected, because a\n * private method cannot be driven — and the first version's ceiling case\n * asserted a relationship between two CONSTANTS, which stayed green when the\n * ceiling branch was deleted entirely.\n */\n/**\n * Which rung the next sleep uses — B212's transition rule.\n *\n * Took work: back to the first rung, because the device that just claimed is\n * the one a follow-up is most likely for. That is the conversation\n * stickiness the ruling is after — one device and one model for a chat, which\n * also softens B197's mixed-model surprise. Took none: climb one, to the\n * ceiling and no further.\n *\n * A function rather than a line inside the loop because it is the rule, and a\n * rule inside a `while` that only real time can drive is a rule nothing\n * checks — replacing it with `step = 0` left every case green.\n */\nexport function nextRung(step: number, claimed: number): number {\n return claimed > 0 ? 0 : Math.min(step + 1, POLL_LADDER.length - 1);\n}\n\nexport function pollDelay(\n step: number,\n heartbeatMs: number,\n random: () => number = Math.random,\n): number {\n const rung = POLL_LADDER[step] ?? 1;\n const base = heartbeatMs * rung;\n if (rung === 1) return base * (0.85 + random() * 0.3);\n return base + POLL_JITTER_MS.min + random() * POLL_JITTER_MS.span;\n}\n\nexport const POLL_LADDER = [0, 0.1, 0.2, 0.4, 0.6, 0.8, 1] as const;\n\n/**\n * Jitter below the ceiling: additive, small, and on EVERY rung.\n *\n * B194's property is that two devices racing for the same job tie by luck\n * rather than by clock speed. A ladder without this turns the fast end into a\n * speed contest — the device that polled 20ms sooner wins every follow-up —\n * which is the thing B194 declined to build a picker for.\n */\nexport const POLL_JITTER_MS = { min: 200, span: 300 } as const;\n\n/**\n * Whether two accounts of a site are the same key material.\n *\n * All three fields, not the encryption key alone: the identity is what a\n * fingerprint is taken of, the encryption key is what work is sealed to, and\n * the signature is what ties them together. A comparison that skipped any of\n * them would call two different keys the same.\n */\nfunction sameKey(a: PublicIdentity, b: PublicIdentity): boolean {\n return (\n a.identity === b.identity &&\n a.encryption === b.encryption &&\n a.encryptionSig === b.encryptionSig\n );\n}\n\n/**\n * The daemon's loop: heartbeat, claim, execute, report.\n *\n * Everything that makes the daemon a *trust anchor* rather than a worker\n * happens in {@link Runner.admit} and {@link Runner.runJob} — the local\n * audience check, the budget check, and the ingress write that precedes\n * execution. The loop itself is deliberately dull.\n */\n/**\n * What a job produced, and how — B064 step 4.\n *\n * `runJob` returned only the outcome, so everything {@link RunMetadata}\n * needs had to be reconstructed at the seal from whatever the caller still\n * had in scope. What it did not have was the duration, which was therefore\n * written as `0` on every result any site has ever received.\n *\n * `ran` is absent when no backend ran at all — a job with no route on this\n * device. That is not the same as a job that ran and failed, which HAS\n * metadata: a model, a class, and a real duration.\n */\nexport interface RanJob {\n readonly outcome: JobOutcome;\n readonly ran?: RunMetadata;\n}\n\nexport class Runner {\n readonly #options: RunnerOptions;\n readonly #backends = new Map<string, Backend>();\n /**\n * Jobs in flight, by job id → the grant and how to stop it.\n *\n * **Keyed by lease id, not by job id** — V1-3. A job id is chosen by its\n * site, so a daemon serving two sites can hold two different jobs called\n * `job_1`; keyed by id, the second overwrote the first, and the first lost\n * the `AbortController` that could stop it and the lease id that could\n * release it. Its lease then lapsed mid-run, the upstream offered the work\n * to somebody else, and the same prompt ran twice on somebody's machine.\n *\n * The lease id is the unique grant (byollm_009 §4.2, `Lease.id`), which is\n * also why every lease-scoped call names it: a release that names only the\n * job releases whatever lease exists when it arrives, which for a replayed\n * request is not the one this daemon meant.\n */\n readonly #active = new Map<\n string,\n { controller: AbortController; jobId: string; site: string | undefined }\n >();\n\n /**\n * Grants abandoned because their site's consent ended mid-run — V1-7.\n *\n * Held rather than acted on immediately: the backend call is already being\n * aborted, and what happens next belongs where the job finishes, once.\n */\n readonly #abandoned = new Set<string>();\n readonly #now: () => number;\n #capabilities: Capability[] = [];\n /**\n * The last probe, reused by the polling loop — B198.\n *\n * `undefined` means \"ask again\", which is both the initial state and what\n * invalidation produces.\n */\n #probed: { at: number; capabilities: Capability[] } | undefined;\n #revoked = false;\n #awaitingConsent = \"\";\n #servingNothing = false;\n /** Whether the last poll found every slot held — B196, edge-triggered. */\n #noFreeSlot = false;\n #stopped = false;\n #consecutiveFailures = 0;\n /**\n * Services that answered \"not signed in\" — withdrawn until proven otherwise.\n *\n * In memory rather than on disk: a restart re-runs the start-up canary,\n * which is a better answer than a remembered verdict about credentials that\n * may since have been fixed. Nothing here should outlive the process that\n * observed it.\n */\n /**\n * The sleep a completing job can end early — B212.\n *\n * Held rather than passed because the waker is a job's `finally`, which is\n * nowhere near the loop. `undefined` while nothing is sleeping, so a wake\n * that arrives mid-tick is a no-op rather than an abort saved up for the\n * next sleep — the next poll is already imminent.\n */\n #wake: AbortController | undefined;\n\n readonly #unauthenticated = new Set<string>();\n /**\n * Services out of quota, and when each expects to be back — 019 §3.2.\n *\n * Kept apart from `#unauthenticated` because the two states recover\n * differently, and folding them would make one of them wrong. A signed-out\n * service waits for a person and stays withdrawn until a probe says\n * otherwise. A blocked one waits for a clock, and must come back **without\n * anybody doing anything** — which is the whole promise of classifying it\n * separately.\n */\n readonly #blocked = new Map<string, number | undefined>();\n /**\n * The blocked services' own reports, kept so a rebuild can restore them.\n *\n * `serviceStates` is rebuilt from scratch on every detection pass and a\n * blocked service never reaches the probe that would fill its entry, so\n * without this the state survives one pass and disappears — and `byollm\n * status`, which is a different process reading the file, could never say a\n * service was blocked.\n */\n readonly #blockedReport = new Map<string, ServiceReport>();\n /** Signed-out services' reports, kept for the same reason blocked ones are. */\n readonly #withdrawnReport = new Map<string, ServiceReport>();\n /** Earliest moment each withdrawn service may be asked again — T2-S1. */\n readonly #authRecheckAt = new Map<string, number>();\n /**\n * Jobs whose payload this device could not fetch, and how often.\n *\n * Bounded by pruning on use: an entry only matters while the job it names\n * could still be offered again, and a daemon that met one bad job a year\n * ago should not still be carrying it.\n */\n readonly #gaveUpFetching = new Map<\n string,\n { attempts: number; at: number }\n >();\n\n /**\n * How many times this device has begun each job id — B041.\n *\n * Separate from {@link #gaveUpFetching} rather than folded into it,\n * because they answer different questions and merging them would blunt\n * both: that one counts a specific, diagnosable failure and reports it by\n * name, this one counts attempts and refuses to care why. Pruned on use,\n * like its neighbour, so it cannot become the leak this project has\n * already fixed once.\n */\n readonly #attempts = new Map<string, { attempts: number; at: number }>();\n /** What was last handed to the writer, so an unchanged pass writes nothing. */\n #lastWritten: string | undefined;\n\n /**\n * The key grants are checked against — from the pairing, or adopted later.\n *\n * Mutable because a re-pair happens in another process and must reach this\n * loop without a restart; see {@link Runner.adoptControlPlaneKey}.\n */\n #controlPlanePublic: string | undefined;\n /**\n * Grant ids this device has already acted on, with when they stop mattering.\n *\n * **Replay.** A grant is a bearer document for one unit\n * of work, and one that could be presented twice would let a relay run a\n * job again after the owner's membership ended — inside the signature's own\n * window, with every check passing.\n *\n * Keyed by grant id rather than job id, which is the distinction Amendment\n * J's pushback established: a claim that times out is re-claimed and gets a\n * *fresh* grant, so binding single-use to the job would refuse the retry\n * this device asked for.\n *\n * In memory, and bounded by {@link GRANT_MAX_AGE_MS} rather than by size. A\n * restart forgets it, which is honest and not a hole: an entry can only\n * matter for as long as the grant naming it is still fresh, and a device\n * that has restarted since is past that window anyway.\n */\n /**\n * Grants already admitted — persistent when the CLI gives it a home.\n *\n * Was a bare `Map`, which emptied on every restart while the grants it was\n * guarding stayed fresh for two minutes. See {@link SpentGrants}.\n */\n readonly #spentGrants: SpentGrants;\n /** Whether the clock is currently past the warning threshold. */\n #clockWarned = false;\n #lastUpstreamError: string | undefined;\n #lastError: string | undefined;\n #completed = 0;\n #refused = 0;\n\n constructor(options: RunnerOptions) {\n this.#options = options;\n this.#now = options.now ?? Date.now;\n // Memory-only unless a caller gives it a file. A device with a control\n // plane gets one from the CLI; direct mode has no grants to replay.\n this.#spentGrants = options.spentGrants ?? new SpentGrants();\n // Seeded from the pairing this loop started with; a re-pair in another\n // process reaches it later through `adoptControlPlaneKey`.\n this.#controlPlanePublic = options.controlPlanePublic;\n // Copied, not aliased: the pairing's map is what was on disk, and this\n // one is what the upstream last said. Sharing them would let a heartbeat\n // rewrite a file nobody wrote.\n this.#sites = new Map(options.identity?.sites ?? []);\n /**\n * The pins this device already holds, checked on the way in.\n *\n * Seeded from both maps because `sites` follows what the upstream is\n * currently offering and `known` holds the ids whose consent has ended —\n * together they are every id this machine has ever pinned, which is what\n * the substitution check compares against.\n *\n * **Verified, not trusted**, and that is a check this constructor did not\n * used to make. `applyApprovals` made it, because approvals arrived\n * through the pairings file and \"a file is not a smaller thing to verify\n * than a heartbeat\". Amendment K deleted approvals; the file is still a\n * file, and the entries still arrive from it. Deleting the caller without\n * moving its check would have quietly removed the check with it.\n *\n * A row that fails is dropped rather than repaired: an id whose key does\n * not belong to it is a pin that would make every later comparison\n * compare against the wrong thing.\n */\n this.#known = new Map(\n [\n ...(options.identity?.known ?? []),\n ...(options.identity?.sites ?? []),\n ].filter(\n ([id, site]) =>\n verifyPublicIdentity(site) && keyId(site.identity) === id,\n ),\n );\n }\n\n /** The sites this daemon holds pins for, so the CLI can persist changes. */\n get sites(): ReadonlyMap<string, PublicIdentity> {\n return this.#sites;\n }\n\n /** Every site ever approved here, so the CLI can persist the tombstones. */\n get known(): ReadonlyMap<string, PublicIdentity> {\n return this.#known;\n }\n\n /**\n * Superseded ids still being served, and until when — Amendment C.\n *\n * Exposed for the same reason `known` is: what this machine is willing to\n * verify against is a thing a person should be able to read, and a window\n * nobody can see is a window nobody can check has closed.\n */\n get retiring(): ReadonlyMap<string, number> {\n return this.#retiring;\n }\n\n status(): RunnerStatus {\n return {\n origin: this.#options.client.origin,\n owner: this.#options.owner,\n runnerId: this.#options.runnerId,\n revoked: this.#revoked,\n activeJobs: this.#active.size,\n capabilities: [...this.#capabilities],\n ...(this.#lastError === undefined ? {} : { lastError: this.#lastError }),\n completed: this.#completed,\n refused: this.#refused,\n };\n }\n\n /**\n * Build the capability matrix: owner config intersected with what is\n * actually reachable and healthy right now\n * ({@link MUSTS.CAPABILITY_IS_DETECTED}).\n *\n * A configured route whose backend is down simply does not appear. The\n * daemon then receives no work for it, which is the correct outcome and one\n * the owner can see in `byollm status`.\n */\n /**\n * What the last probe learned about each service, for the owner's surfaces.\n *\n * Read by `byollm status`, by `byollm connect`'s report and by the daemon's\n * own output — one answer, three renderings, through `serviceLine`.\n */\n serviceStates = new Map<string, ServiceReport>();\n\n /**\n * Capabilities for a claim, from the cache when it is fresh — B198.\n *\n * Todd: *\"Shouldn't we do that poll every 15 minutes or something and use\n * the stored value? If the device service times out we recover or send to a\n * different box while recovering.\"*\n *\n * **Two cadences that were one.** `health()` spawns `<cli> --version` per\n * configured CLI, and `#tick()` ran it before every claim — about 17,000\n * spawns a day. Measured in the box image at three CPU shares:\n *\n * | CPU | one tick's probes |\n * | --- | --- |\n * | unthrottled | ~20–29 ms |\n * | 500m, the box's limit | ~21–74 ms |\n * | **50m, the box's request** | **~1.9–2.2 s** |\n *\n * The box requests 50m and limits 500m, **so this is invisible on a quiet\n * node and eighty times worse on a contended one** — and because the probe\n * runs before the claim on the same tick, under contention it stretches the\n * claim cadence, which is queue latency.\n *\n * **Only the polling path caches.** `detectCapabilities` is untouched and\n * every human-facing caller still probes: `byollm start`'s canary, and the\n * `advertised` read a person triggers by asking. Somebody who asks is asking\n * *now*.\n *\n * **What keeps it honest is invalidation, not the interval.** A stale\n * advertisement discovers itself at job time — the job fails, the release\n * requeues it, another device claims — and {@link Runner.#forgetProbe} makes\n * that failure force a fresh probe. The failure is a better probe than the\n * probe.\n */\n async #capabilitiesForTick(): Promise<Capability[]> {\n const probed = this.#probed;\n if (probed !== undefined && !this.#recheckIsDue(probed)) {\n return probed.capabilities;\n }\n const capabilities = await this.detectCapabilities();\n this.#probed = { at: this.#now(), capabilities };\n return capabilities;\n }\n\n /**\n * Whether some service is waiting to be let back IN — B198's rider.\n *\n * `#forgetProbe()` has one caller: a failed job. That makes the cache safe\n * in exactly one direction.\n *\n * - **true -> false**, a service that was advertised and has stopped\n * working: a job goes to it, the job fails, the probe is forgotten. Self\n * correcting, and the row is right that the failure is the better probe.\n * - **false -> true**, a service that was NOT advertised and has started\n * working: **nothing can trigger a re-probe, because an unadvertised\n * service is sent no jobs, so no job of its can fail.**\n *\n * Two recoveries live inside {@link Runner.detectCapabilities} and so happen\n * only as often as the tick calls it: the signed-out recheck\n * ({@link AUTH_RECHECK_MS} — T2-S1's *\"a remedy that cannot be completed is\n * worse than no remedy\"*) and the quota-block release (019 §3.2,\n * *\"advertised again with nobody lifting a finger\"*). Caching the withdrawn\n * answer for fifteen minutes silently made both of those fifteen minutes\n * late; dropping the clock entirely, as B198's amendment proposes, makes\n * them never — the owner signs in and the daemon never looks again.\n *\n * So the cache covers the healthy path only, and a pending recovery keeps\n * its own clock. That is not a second cadence bolted on: it is the same rule\n * as the failure path, which is that the cache is reused only while nothing\n * is owed an answer. Cost is one probe per {@link AUTH_RECHECK_MS} rather\n * than per tick — still six times cheaper than before this row, and the\n * saving this row was for is untouched on a machine where nothing is\n * withdrawn, which is the machine it was measured on.\n */\n #recheckIsDue(probed: { at: number; capabilities: Capability[] }): boolean {\n if (this.#recoveryIsDue()) return true;\n\n /* Everything configured is advertised: assume it stays up, and let a real\n job's failure be what says otherwise. No clock at all — the amendment. */\n const advertised = new Set(probed.capabilities.map((c) => c.service));\n const missing = this.#options.loaded.routes.some(\n (route) => !advertised.has(route.service),\n );\n if (!missing) return false;\n\n /* Something configured is not being advertised, and no job can ever fail\n on a service nobody is sent to. This is the arm that lets an installed\n CLI be noticed without a restart. */\n return this.#now() - probed.at >= DETECT_INTERVAL_MS;\n }\n\n #recoveryIsDue(): boolean {\n const now = this.#now();\n for (const service of this.#unauthenticated) {\n if (now >= (this.#authRecheckAt.get(service) ?? 0)) return true;\n }\n for (const [service, until] of this.#blocked) {\n /* An open-ended block is released on the next detection pass by design\n (019 §3.2), so it is owed one now. */\n void service;\n if (until === undefined || now >= until) return true;\n }\n return false;\n }\n\n /**\n * Forget the probe, so the next tick asks again — B198's invalidation.\n *\n * The whole set rather than one service's entry. Per-service would be\n * tighter and the tightness buys nothing: a failure is rare, a re-probe is\n * one tick's worth of work, and **a cache that under-invalidates advertises\n * something that is not real** — which is the property this whole mechanism\n * is spending.\n */\n #forgetProbe(): void {\n this.#probed = undefined;\n }\n\n async detectCapabilities(\n options: {\n /**\n * Also spend one real call per credentialed backend — ruled 2026-08-25.\n *\n * **Start and enablement only.** `#tick()` never passes this: a canary\n * on the polling loop would spend a subscription call every heartbeat,\n * which is a standing cost to answer a question whose answer changes\n * about once a month.\n *\n * The pairing is deliberate. This catches a signed-out backend before\n * any of somebody's work is refused; the `unauthorized` path catches\n * credentials that expire while the daemon runs, and costs nothing. Two\n * legs, and neither is a poll.\n */\n canary?: boolean;\n } = {},\n ): Promise<Capability[]> {\n /**\n * Why each service is or is not usable, kept rather than discarded.\n *\n * The canary already knew: it ran, it failed, the route was dropped. What\n * reached the person was \"0 backends are healthy\", which is true of a\n * machine with no CLI installed and of a machine whose subscription token\n * expired last week, and those want opposite actions. The check was never\n * the problem; throwing away its answer was.\n */\n this.serviceStates = new Map();\n const capabilities: Capability[] = [];\n\n /**\n * One probe per service — byollm_016.\n *\n * Detection used to run per route, so a service answering two kinds was\n * asked twice. That doubles the network cost of every heartbeat, and it\n * lets one service return two different answers about itself in the same\n * tick — a machine that is half-advertised for reasons nobody can\n * reconstruct. A service is one thing; it is asked once.\n *\n * The model check moves with it: in this shape the model belongs to the\n * service, so \"does the server actually have it\" is the same question for\n * every kind that service answers.\n */\n const probed = new Map<string, boolean>();\n\n for (const route of this.#options.loaded.routes) {\n // Withdrawn on an auth failure, before the backend is asked anything.\n // The probe would say healthy — `--version` needs no credentials — which\n // is the whole reason this set exists.\n if (this.#unauthenticated.has(route.service)) {\n /*\n * Carried across the rebuild, like the block — tick-1 rider.\n *\n * `serviceStates` is emptied at the top of every pass and a withdrawn\n * service skips the probe that would fill its entry, so the\n * signed-out state was visible for exactly one heartbeat and then\n * erased. `status` would show the fault once and go quiet, about a\n * service still refusing every job — which is worse than never having\n * shown it, because the silence reads as recovery.\n */\n /**\n * And asked again, because the remedy has to work — T2-S1.\n *\n * The set had `add` and `has` and no `delete` anywhere. The skip was\n * unconditional, so the promise that a withdrawn service \"stays\n * withdrawn until a probe says otherwise\" described a probe that\n * could never run: the owner followed the sign-in remedy, signed in,\n * and the daemon kept the service withdrawn — and, after the last\n * change, kept saying so in `services.json` — until the process was\n * restarted. **A remedy that cannot be completed is worse than no\n * remedy**, because it sends somebody to do a thing and then ignores\n * that they did it.\n *\n * Asking is nearly free here, and that is what makes it affordable on\n * the polling loop where the canary is otherwise forbidden: a\n * signed-out CLI fails its canary *before* reaching a model, so the\n * call that discovers \"still signed out\" spends no tokens. Only the\n * one that discovers \"back\" costs anything, and it costs it once.\n *\n * Spaced anyway, because each attempt is a process spawn.\n */\n const recheckAt = this.#authRecheckAt.get(route.service) ?? 0;\n const backend = this.#backendFor(route);\n const lifted =\n this.#now() >= recheckAt &&\n backend.canary !== undefined &&\n (await backend.canary(route.model)).healthy;\n\n if (!lifted) {\n if (this.#now() >= recheckAt) {\n this.#authRecheckAt.set(\n route.service,\n this.#now() + AUTH_RECHECK_MS,\n );\n }\n const kept = this.#withdrawnReport.get(route.service);\n if (kept !== undefined) this.serviceStates.set(route.service, kept);\n continue;\n }\n\n // Signed back in. Everything that made it withdrawn is forgotten, and\n // the pass below fills its entry from a live probe rather than from\n // the memory of a fault it no longer has.\n this.#unauthenticated.delete(route.service);\n this.#withdrawnReport.delete(route.service);\n this.#authRecheckAt.delete(route.service);\n this.#options.onEvent?.({\n type: \"service-signed-in\",\n service: route.service,\n });\n }\n /*\n * Blocked until the clock says otherwise, and then advertised again\n * with nobody lifting a finger — 019 §3.2.\n *\n * A block whose end time is unknown is released here too, on the next\n * detection pass. That is deliberate: without a time the only\n * alternatives are to guess one or to withdraw the service until a\n * restart, and a service that never comes back is a worse failure than\n * one job that discovers the block is still on. One job re-establishes\n * it, which is the same cost the auth path pays.\n */\n const blockedUntil = this.#blocked.get(route.service);\n if (this.#blocked.has(route.service)) {\n if (blockedUntil !== undefined && this.#now() < blockedUntil) {\n /*\n * Carried across the reset above — S2, found by CW reading the code.\n *\n * This map is rebuilt from scratch each pass, and a blocked service\n * skips the probe that would fill its entry. So the state existed\n * for exactly one pass and then vanished: `byollm status` could\n * never say a service was blocked, let alone until when, though\n * acceptance §4 names that surface by name. The block is a fact\n * about right now, and a surface asking \"what is true\" has to be\n * able to read it.\n */\n const kept = this.#blockedReport.get(route.service);\n if (kept !== undefined) this.serviceStates.set(route.service, kept);\n continue;\n }\n /*\n * The lift is a state change too — S-2, 2026-09-04.\n *\n * Only the block was written. An until-less block withdraws for one\n * pass, recovers on the next, and `services.json` goes on saying \"out\n * of quota: it needs time, not a fix\" forever, about a service that\n * is answering. A file that is written when things get worse and\n * never when they get better is not a record, it is an accusation.\n *\n * **A withdrawal or restoration that `status` would report\n * differently is a state change, and every state change writes.**\n */\n this.#blocked.delete(route.service);\n this.#blockedReport.delete(route.service);\n this.serviceStates.delete(route.service);\n /* No write here: the probe below fills this entry back in and the\n pass writes once at the end. Writing twice would put an `absent`\n on the way to a state that is about to be known, which is a\n flicker in a file other processes poll. */\n }\n let usable = probed.get(route.service);\n if (usable === undefined) {\n const backend = this.#backendFor(route);\n const health = await backend.health();\n // If the backend enumerates its models, honour that: advertising a\n // model the server does not have would be advertising a lie.\n usable =\n health.healthy &&\n (health.models.length === 0 ||\n modelPresent(health.models, route.model));\n\n // The credentialed check, when asked for and when the backend has one.\n // Only after `health` passed — there is no sense spending a call on a\n // binary that is not there.\n /**\n * What the probe learned, kept beside the remedy that fixes it.\n *\n * Learned together, from the backend instance that knows both, so a\n * surface rendering the line later does not have to work out which\n * backend a service used in order to say how to sign it in.\n */\n const remedy =\n backend.signIn === undefined ? {} : { signIn: backend.signIn };\n if (!health.healthy) {\n /**\n * Not running is not the same as not installed — B056 / D4.\n *\n * The ruling: a configured-but-stopped LOCAL server advertises,\n * because it is available one spawn away and B050 starts it when a\n * job arrives. What that must not become is advertising a config\n * that names a server nobody installed — the fleet would claim work\n * it cannot serve, turning a site's clean no-runner silence into a\n * claimed-then-failed job, which is worse for them than saying\n * nothing.\n *\n * So the question is asked of the MACHINE: do we know a start\n * command, is the url on this machine, and is the binary actually\n * on PATH. Any no leaves the behaviour exactly as it was.\n */\n const startable = await startability({\n id: route.backendId,\n baseUrl:\n this.#options.loaded.config.services[route.service]?.baseUrl,\n ...(this.#options.onPath === undefined\n ? {}\n : { onPath: this.#options.onPath }),\n });\n /**\n * Startable is not the same as started — B087, a defect shipped in\n * .86.\n *\n * B056 advertises a stopped-but-installed server \"because it is\n * available one spawn away and B050 starts it when a job arrives.\"\n * The first half is true and the second was not: the starter sits\n * behind a `spawnServer` seam that nothing in the repository\n * passes, so `ensureLocalServer` returns before it can do anything\n * and the job reaches a port with nothing listening.\n *\n * That is the exact outcome B056's own comment forbids — *\"the\n * fleet would claim work it cannot serve, turning a site's clean\n * no-runner silence into a claimed-then-failed job, which is worse\n * for them than saying nothing.\"* It guarded the uninstalled case\n * and the installed-but-stopped case produced the same result by a\n * different route.\n *\n * So advertising is tied to the starter EXISTING rather than\n * switched off with a constant. Wire `spawnServer` and this\n * resumes on its own; leave it unwired and nothing is promised.\n * The two facts cannot drift apart, which a `false` here would\n * have allowed the moment somebody wired the seam and did not\n * think to look at this line.\n */\n const starts = this.#options.spawnServer !== undefined;\n usable = startable.startable && starts;\n this.serviceStates.set(route.service, {\n /**\n * Three answers, not two — B098.\n *\n * `missing` used to swallow every reason a service could not be\n * started, so a service this module has no command for was\n * reported as software the owner had not installed. On Todd's\n * machine that meant `byollm status` telling him to install\n * Ollama and MLX while both were installed and serving.\n *\n * Only `not-installed` is `missing`. The advertising decision is\n * unchanged: nothing here is offered either way.\n */\n state: startable.startable\n ? { kind: \"stopped\", model: route.model, starts }\n : startable.why === \"not-installed\"\n ? { kind: \"missing\" }\n : { kind: \"unstartable\", model: route.model },\n ...remedy,\n });\n } else if (options.canary !== true || backend.canary === undefined) {\n // Nothing was asked. Not a failure — see `ServiceState.unknown`.\n this.serviceStates.set(route.service, {\n state: { kind: \"unknown\", model: route.model },\n ...remedy,\n });\n } else {\n this.serviceStates.set(route.service, {\n state: { kind: \"answers\", model: route.model },\n ...remedy,\n });\n }\n\n if (usable && options.canary === true && backend.canary !== undefined) {\n const proof = await backend.canary(route.model);\n if (!proof.healthy) {\n this.serviceStates.set(route.service, {\n state: {\n kind: \"signed-out\",\n ...(proof.detail === undefined ? {} : { detail: proof.detail }),\n },\n ...remedy,\n });\n usable = false;\n this.#unauthenticated.add(route.service);\n this.#options.onEvent?.({\n type: \"service-not-signed-in\",\n service: route.service,\n detail: proof.detail ?? \"the check call did not succeed\",\n });\n }\n }\n probed.set(route.service, usable);\n }\n if (!usable) continue;\n\n capabilities.push({\n kind: route.kind,\n service: route.service,\n backendId: route.backendId,\n backendClass: route.backendClass,\n model: route.model,\n // What this device's CLI knows, so the dashboard suggests from the\n // machine rather than from a list the cloud last heard about —\n // byollm_017 ruling 3. Omitted when there is nothing to suggest:\n // absent is \"nothing to offer\", and `[]` on the wire would invite a\n // reader to render \"no models available\" for a local server that\n // serves whatever has been pulled onto it.\n ...(knownModelsFor(route.backendId).length === 0\n ? {}\n : { knownModels: [...knownModelsFor(route.backendId)] }),\n offerScope: route.offerScope,\n });\n }\n\n this.#capabilities = capabilities;\n /* Every pass ends by saying what it found — S-2, both entry points.\n `byollm run` wired the writer and never called it, so a daemon that\n started, probed and settled left `status` reading whatever the last\n `connect` had written. The dirty check inside keeps a settled machine\n from writing a file every heartbeat. */\n this.#writeServiceStates();\n return capabilities;\n }\n\n #backendFor(route: ResolvedRoute): Backend {\n const key = `${route.service}:${route.backendId}`;\n let backend = this.#backends.get(key);\n if (!backend) {\n backend =\n this.#options.backendFactory?.(route) ??\n createBackend(route.backendId, {\n baseUrl: route.baseUrl,\n apiKeyEnv: route.apiKeyEnv,\n });\n this.#backends.set(key, backend);\n }\n return backend;\n }\n\n /**\n * Has this route spent the owner's daily ceiling on other people's work?\n *\n * Only meaningful for `metered` routes; `free` and `subscription` never\n * reach here in a way that matters, because the matcher refuses them for\n * other reasons first ({@link MUSTS.METERED_REQUIRES_CEILING}).\n */\n #spendCeilingReached(route: ResolvedRoute): boolean {\n if (route.cost !== \"metered\") return false;\n return this.#options.spend.hasReachedCeiling(\n route.service,\n route.spendDailyCapCents,\n this.#now(),\n );\n }\n\n /**\n * The route a job takes — byollm_016 Phase B, both sides of it.\n *\n * Was `routes.find((r) => r.kind === kind)`, which was correct while a kind\n * had exactly one route and became a coin-flip the moment the menu started\n * travelling: it would have handed an unselected job to whichever service\n * sorted first, which is the guess `withheld` exists to refuse.\n *\n * A named service is matched exactly and never approximately. `undefined`\n * here means refuse — not fall back to the default — because serving a\n * selection from something else is the substitution `NO_PAYLOAD_ROUTING`\n * forbids, and the daemon is the party that owns that rule. The hub already\n * matched on the same pair; this is the second check, for the same reason\n * the allowlist is checked twice.\n */\n #routeFor(kind: string, service?: string): ResolvedRoute | undefined {\n const routes = this.#options.loaded.routes;\n if (service !== undefined) {\n return routes.find(\n (route) => route.kind === kind && route.service === service,\n );\n }\n return routes.find((route) => route.kind === kind && route.isDefault);\n }\n\n /**\n * Who this device will serve, and on whose authority — Amendment G, B2.\n *\n * Two regimes, and never both at once. The one that applies is decided by\n * whether this pairing pinned a control-plane key, which is the honest\n * question: a key means this upstream has a control plane that authors\n * grants, and a device that pinned one knows whose word to check them\n * against.\n *\n * **With a key**, the grant decides. Nothing local adds and nothing local\n * subtracts — the document is the whole answer, and this device's job is to\n * establish that it is genuine, fresh, unspent, and about work this device\n * actually offers to this person.\n *\n * **Without one**, the owner's own work is the only work that runs. Direct\n * mode has no control plane, so there is no signature that could tell this\n * device a stranger may use it, and a local list of names would be back to\n * believing the site's per-job claim about who its users are — the thing\n * Amendment G property 1 outlawed, wearing an allowlist costume. Ruled\n * 2026-08-26: direct mode is owner-only.\n *\n * The two never combine, and there is no third.\n */\n #grantAdmits(job: ClaimedStub): { ok: true } | { ok: false; reason: string } {\n const key = this.#controlPlanePublic;\n if (key === undefined) {\n // Direct mode. `matchAudience` still runs and still refuses a stranger\n // at `private` scope; this only has to make sure a `team` offer does\n // not admit one, since there is nothing here that could have checked\n // who they are.\n return job.owner === this.#options.owner\n ? { ok: true }\n : {\n ok: false,\n reason:\n \"this device serves its owner only — it is not paired with a \" +\n \"relay, so nothing here can tell it who anybody else is \" +\n \"(`byollm connect <relay>` to share it)\",\n };\n }\n\n const grant = job.grant;\n if (grant === undefined) {\n // **Refused, not admitted by default.** An upstream with a control\n // plane authors a grant for every job it routes, including the owner's\n // own, so a claimed job without one is either a relay that dropped it\n // or a version skew — and both are answered the same way, because a\n // device cannot tell them apart and must not guess in the open\n // direction.\n this.#noteGrantRefusal(\"absent\", job.id);\n return {\n ok: false,\n reason:\n `no grant arrived with job ${job.id}, and this device only runs ` +\n \"relayed work its control plane has signed for\",\n };\n }\n\n // The signature first, plus everything the signature is\n // *about* — that this grant names this device's owner, this job, and a\n // moment close enough to now to still mean something.\n const refusal = verifyGrant({\n grant,\n owner: this.#options.owner,\n jobId: job.id,\n controlPlanePublic: key,\n now: this.#now(),\n });\n if (refusal !== null) {\n this.#noteGrantRefusal(refusal, job.id);\n return { ok: false, reason: this.#grantRefusalText(refusal, grant) };\n }\n\n /**\n * Still check one: the grant must be about this job's *person*.\n *\n * `verifyGrant` binds the document to this device's owner and to this job\n * id. Neither says anything about who the work belongs to, and the stub's\n * `owner` is a claim by the party that routed it. A grant written for bob\n * attached to a job stubbed as carol's would otherwise admit carol —\n * every signature valid, the wrong person served, and the budget charged\n * against a name nobody authorised.\n *\n * The grant wins because it is the signed one. They are never reconciled,\n * only refused: two answers to \"whose work is this\" is exactly the shape\n * that must not survive the consolidation.\n */\n if (grant.user !== job.owner) {\n this.#noteGrantRefusal(\"wrong-user\", job.id);\n return {\n ok: false,\n reason: \"the grant for this job names a different user than the job\",\n };\n }\n\n /**\n * And about this job's *slot* — the same law, applied to the rest of it.\n *\n * `user` was the first field where the signed document and the unsigned\n * stub could disagree, and the rule ratified there is general: **the\n * signature's word is the only word, and disagreement is refusal.** It was\n * applied to one field and left off two, which is how a law becomes a\n * special case.\n *\n * `kind` and `purpose` are what the control plane *resolved against*. The\n * grant says \"for this purpose, at this kind, use this service\", and the\n * route is then selected with `job.kind` — a value the routing party\n * chose. A relay that keeps the job id and rewrites `kind` runs the\n * resolved service under a slot the person never mapped: a different\n * per-kind limit, a different sizeClass bucket, and a consent that was\n * never given for it.\n *\n * An absent `purpose` on the stub resolves to {@link RESERVED_PURPOSE},\n * which is exactly what the engine did before signing — so the comparison\n * is against the same value the engine used, not against a raw `undefined`\n * that would refuse every single-purpose site.\n */\n /**\n * And about this job's *site*.\n *\n * Job ids are chosen per site — a daemon serving two sites can hold two\n * different jobs called `job_1` — so a grant authored for one site's\n * `job_1` satisfied every other check against another site's. The signed\n * field that should have caught it was in the control plane's namespace,\n * which this device has no way to resolve; it now carries the key id\n * pinned at approval, so the comparison is direct.\n */\n if (grant.site !== job.site) {\n this.#noteGrantRefusal(\"wrong-site\", job.id);\n return {\n ok: false,\n reason: \"the grant for this job names a different site than the job\",\n };\n }\n\n if (grant.kind !== job.kind) {\n this.#noteGrantRefusal(\"wrong-kind\", job.id);\n return {\n ok: false,\n reason: \"the grant for this job names a different kind than the job\",\n };\n }\n\n if (grant.purpose !== (job.purpose ?? RESERVED_PURPOSE)) {\n this.#noteGrantRefusal(\"wrong-purpose\", job.id);\n return {\n ok: false,\n reason: \"the grant for this job names a different purpose than the job\",\n };\n }\n\n /* The record of what has already been admitted is unreadable, so every\n grant is refused until nothing in it could have mattered. Checked here,\n with the other grant refusals, because that is what this is: the device\n cannot tell a first admission from a second, and admitting on a guess\n is the replay this whole path exists to stop. */\n const blocked = this.#spentGrants.blockedReason(this.#now());\n if (blocked !== undefined) {\n this.#noteGrantRefusal(\"replayed\", job.id);\n return {\n ok: false,\n reason:\n \"this device cannot read its record of used grants, so it is \" +\n \"refusing relayed work until that record can be trusted again\",\n };\n }\n\n // Replay: a grant admits one job, once.\n if (this.#spentGrants.has(grant.grantId, this.#now())) {\n this.#noteGrantRefusal(\"replayed\", job.id);\n return {\n ok: false,\n reason:\n \"this grant has already been used — a grant admits one job, once\",\n };\n }\n\n return { ok: true };\n }\n\n /**\n * Say something about the clock *before* it costs anybody work.\n *\n * `serverTime` has been on every heartbeat response since the field was\n * added, with a docstring saying what it is for, and the daemon read\n * `.sites`, `.successions`, `.awaitingConsent`, `.lost` and `.cancel` and\n * never this one — the ruled proactive half of the skew warning, dead since\n * it was ruled.\n *\n * The reactive half already existed and is the wrong half on its own: a\n * refusal names the clock only once drift has passed\n * {@link GRANT_MAX_AGE_MS}, by which point every relayed job has already\n * been refused. The window between {@link CLOCK_SKEW_WARN_MS} and that is\n * precisely where a warning is worth something — the device still works,\n * and its owner can fix an ntp problem before it becomes an outage.\n *\n * Warned once per crossing rather than every beat. A daemon heartbeats\n * every few seconds and a clock stays wrong for as long as it takes\n * somebody to notice; a line per beat is how a real warning becomes noise\n * that gets filtered. The state resets when the clock comes back, so a\n * second drift warns again.\n */\n #noteClockSkew(serverTime: number): void {\n // Signed: **behind** and **ahead** are different remedies and the sign is\n // the only thing that says which. Reported as the device sees it — a\n // positive number means this machine is ahead of its control plane.\n const skew = this.#now() - serverTime;\n const past = Math.abs(skew) > CLOCK_SKEW_WARN_MS;\n\n if (past && !this.#clockWarned) {\n this.#clockWarned = true;\n this.#options.onEvent?.({ type: \"clock-skew\", skewMs: skew });\n } else if (!past && this.#clockWarned) {\n this.#clockWarned = false;\n this.#options.onEvent?.({ type: \"clock-recovered\", skewMs: skew });\n }\n }\n\n /**\n * Say why, in words that send somebody to the right place.\n *\n * The clock gets named rather than implied. A device whose clock disagrees\n * with its control plane by more than {@link GRANT_MAX_AGE_MS} refuses\n * every grant it is sent, and \"this grant expired\" is a true sentence that\n * would have somebody debugging the relay for an afternoon. Past\n * {@link CLOCK_ATTRIBUTION_MS} of apparent disagreement the refusal says\n * what is actually wrong; below it the clock is not the story and saying so\n * would send them to check ntp about something else.\n */\n #grantRefusalText(refusal: GrantRefusal, grant: SignedGrant): string {\n const skew = this.#now() - grant.issuedAt;\n const clockIsTheStory =\n (refusal === \"expired\" || refusal === \"from-the-future\") &&\n Math.abs(skew) > GRANT_MAX_AGE_MS + CLOCK_ATTRIBUTION_MS;\n if (clockIsTheStory) {\n return (\n `this device's clock disagrees with its control plane by about ` +\n `${String(Math.round(Math.abs(skew) / 1000))}s, so every grant it is ` +\n `sent looks ${refusal === \"expired\" ? \"expired\" : \"post-dated\"} — ` +\n \"fix the clock, not the relay\"\n );\n }\n switch (refusal) {\n case \"expired\":\n return \"the grant for this job was signed too long ago to honour\";\n case \"from-the-future\":\n return \"the grant for this job is dated in the future\";\n case \"wrong-owner\":\n return \"the grant for this job was written for a different device owner\";\n case \"wrong-job\":\n return \"the grant that arrived was written for a different job\";\n case \"bad-signature\":\n return (\n \"the grant for this job is not signed by the control plane this \" +\n \"device paired with\"\n );\n }\n }\n\n #noteGrantRefusal(refusal: GrantRefusalCause, jobId: string): void {\n this.#options.onEvent?.({ type: \"grant-refused\", refusal, jobId });\n }\n\n /**\n * Decide whether this machine will run a claimed job.\n *\n * The upstream already applied its own version of these rules, and that is\n * not what this checks. This is the device enforcing against the upstream.\n *\n * **Four checks, and they are now the whole of it** — Amendment J folded\n * consent, membership, admission and selection into one signed document, so\n * what remains on this side is the entire defence:\n *\n * 1. **the signature**, against the key pinned at pairing, over a document\n * that must name this owner, this job and this job's user;\n * 2. **replay** — a grant admits one job, once;\n * 3. **offer-consistency** — the service the grant names is one this device\n * actually offers, for this kind;\n * 4. **private is absolute** — a `private` service runs the owner's work\n * and nobody else's, whatever any grant says.\n *\n * Four is deliberately load-bearing. A bug in any one of them is a bug in\n * consent, membership, admission and selection at once, which is the price\n * of the consolidation and the reason each has its own test and its own\n * mutation.\n *\n * Check 4 is structural rather than written here, and that is the strongest\n * form it can take: {@link matchAudience} only consults `admits` in its\n * `team` branch, so a `private` service refuses a stranger before any grant\n * is looked at. **No compromise of a control plane can grant somebody\n * else's job onto a private service**, because the code path that would\n * carry the grant is not reached.\n *\n * A job that fails here is released with reason `refused`, which the\n * upstream remembers so it is never offered back.\n */\n admit(job: ClaimedStub): { ok: true } | { ok: false; reason: string } {\n // Before anything else, and before a payload is fetched: is this a site\n // this machine serves? `#pinFor` asks the same question at seal time,\n // which is *after* a backend has been paid to answer a prompt from a site\n // nobody here approved. Asked here, the answer costs a release.\n if (this.#options.identity && !this.#sites.has(job.site)) {\n return {\n ok: false,\n reason:\n `this device does not serve site ${job.site} ` +\n `(serving ${[...this.#sites.keys()].sort().join(\", \") || \"nothing\"})`,\n };\n }\n\n // Checks one and two.\n const granted = this.#grantAdmits(job);\n if (!granted.ok) return granted;\n\n /**\n * Check three, first half: whose choice of service is this?\n *\n * On a relayed route, the grant's — it carries the resolution the control\n * plane made from this person's own mapping, and there is nothing else to\n * consult. A guard used to sit here refusing a stub that named a\n * *different* service, because for one release both could speak. Amendment\n * L took the field off the stub, so the disagreement it caught is now\n * unrepresentable rather than refused, and the guard went with the field.\n *\n * `undefined` is direct mode, where there is no control plane and the\n * owner's own defaults answer under the ambiguity law as shipped.\n */\n const requested = job.grant?.service;\n\n // Check three, second half: do we actually offer it, for this kind? An\n // unknown or unrouted kind is refused, never guessed\n // ({@link MUSTS.KIND_TYPED_ONLY}), and a grant naming a service this\n // device does not serve is refused the same way — the control plane\n // chooses from what this device advertised, and anything else is either\n // stale or forged.\n const route = this.#routeFor(job.kind, requested);\n if (!route) {\n return { ok: false, reason: REFUSAL_MESSAGES[\"no-capability\"] };\n }\n\n const match = matchAudience(\n {\n owner: job.owner,\n audience: job.audience,\n // No `audienceAllow`: it is not on the wire any more (cloud_008\n // §0.2), and this is the branch that made it look load-bearing. It\n // narrowed a decision `admits` below already owns — the site\n // could only ever agree with the daemon's own list or contradict it,\n // and nothing wrote down which won.\n },\n {\n owner: this.#options.owner,\n offerScope: route.offerScope,\n cost: route.cost,\n spend: {\n acknowledged: route.spendAcknowledged,\n ceilingReached: this.#spendCeilingReached(route),\n },\n // Checks three and four, applied by the one function that owns the\n // audience law. `true` here is not a shortcut: `#grantAdmits` above\n // is what earned it, and this predicate is only *reached* for a\n // `team` service — which is what makes check 4 structural.\n admits: () => true,\n },\n );\n if (!match.ok) {\n return { ok: false, reason: REFUSAL_MESSAGES[match.refusal] };\n }\n\n if (job.owner !== this.#options.owner) {\n /**\n * A CHEAP EARLY REFUSAL ON A NUMBER THE SENDER CHOSE — B188.\n *\n * From the stub's bucket, because admission happens before the payload\n * is fetched. The ceiling is charged rather than a midpoint: refusing\n * slightly too eagerly is the safe direction for someone else's work on\n * the owner's machine.\n *\n * **That argument is about ROUNDING and says nothing about a LIE, and\n * this used to be the only payload check there was.** `sizeClass` is\n * declared by whoever enqueued the job; an untrusted end user declares\n * `small` and sends whatever they like. The 08-27 review measured the\n * overrun at ~40x the owner's configured community limit, on their\n * metered backend, and `budgets.check`'s own parameter is documented as\n * *\"total payload text length\"* while being handed a bucket.\n *\n * It stays, because refusing a job that cannot fit before fetching it\n * is worth doing. **It is no longer load-bearing**: the payload is\n * measured against the same limit after it is opened, in\n * {@link ByollmRunner.#withinCommunityBudget}.\n */\n const decision = this.#options.budgets.check(\n this.#now(),\n sizeClassCeiling(job.sizeClass),\n );\n if (!decision.ok) return { ok: false, reason: decision.detail };\n }\n\n /**\n * The first job from a site this machine has never served — Amendment K.\n *\n * Here, on the admitted path and before {@link runJob} touches a backend,\n * because the mitigation for \"site policy moved to the account\" is that\n * the machine says so *first*. Fired after the grant verified, so it is\n * evidence rather than a guess: a site the relay merely mentioned has not\n * asked this device for anything yet.\n */\n if (!this.#served.has(job.site)) {\n this.#served.add(job.site);\n const site = this.#sites.get(job.site);\n this.#options.onEvent?.({\n type: \"now-serving\",\n site: job.site,\n fingerprint: site ? fingerprint(site.identity) : job.site,\n });\n }\n\n // Spent only now, on the way out. A grant burned by a refusal would make\n // the retry that follows fail for a second, unrelated reason — and the\n // upstream re-offers with a *fresh* grant anyway, so there is nothing to\n // protect against by burning it early.\n if (job.grant !== undefined) {\n const burned = this.#spentGrants.spend(\n job.grant.grantId,\n job.grant.issuedAt,\n this.#now(),\n );\n /* The burn has to outlive this process or it is not protection at all.\n A job admitted on a note that was never written is a job whose second\n delivery, after a restart, looks exactly like its first. */\n if (!burned) {\n this.#noteGrantRefusal(\"replayed\", job.id);\n return {\n ok: false,\n reason:\n \"this device could not durably record that this grant was used, \" +\n \"so it is refusing the job rather than risk running it twice\",\n };\n }\n }\n\n return { ok: true };\n }\n\n /**\n * Execute one admitted job.\n *\n * Order is load-bearing: the ingress write is awaited *before* the backend\n * is touched ({@link MUSTS.INGRESS_LOGGED_BEFORE_EXECUTION}), so a job that\n * hangs the machine still leaves a record of what it was.\n */\n /**\n * Bring up the local server behind this route, if there is one to bring up.\n *\n * Everything it needs is asked of the route and the config rather than\n * assumed: whether the service is HTTP-class at all, whether its url is on\n * this machine, and whether the id is one whose start command we can say.\n * Any \"no\" leaves the behaviour exactly as it was, and costs no request.\n *\n * **`spawnServer` absent means nothing is ever started.** The daemon that\n * runs jobs passes one; `connect`, `services` and `status` build runners\n * too, and none of them should be able to start a model server as a side\n * effect of asking a question.\n */\n /**\n * Should this machine take a job that loads a model right now — B080.\n *\n * Returns the decision, or `undefined` when there was nothing to decide:\n * no reader injected, or a route that holds no model here. Both are\n * silence rather than an admit, because an admit gets logged and a line\n * per job saying \"this is a proxy\" is volume with no fact in it.\n *\n * Asked HERE, at dispatch, and not at start-up. The hazard that actually\n * happened on 09-08 was not a stopped server being started — it was a\n * running one loading 17 GB because a job arrived, and the only moment\n * that can be refused is the moment the job arrives.\n *\n * Memory is read only when the guard applies, so the `vm_stat` spawn is\n * paid on the routes it can protect and on no others. `guardApplies` is\n * the gate's own function, not a second opinion — B085 was exactly a\n * second place answering a question this one already answers.\n */\n async #memoryDecision(\n route: ResolvedRoute,\n jobId: string,\n ): Promise<{ admit: boolean; why: string } | undefined> {\n const read = this.#options.readMemory;\n if (read === undefined) return undefined;\n if (\n !guardApplies({\n backendId: route.backendId,\n baseUrl: route.baseUrl,\n model: route.model,\n })\n ) {\n return undefined;\n }\n const { memory, pressure } = await read();\n const decision = memoryGate({\n backendId: route.backendId,\n baseUrl: route.baseUrl,\n model: route.model,\n memory,\n pressure,\n /**\n * The owner's floor, and passing it is the whole of B090 — B090.\n *\n * The field without this line is the worse half of the two: it would\n * parse, validate, appear in `byollm status`, and change nothing,\n * because the gate would go on using {@link DEFAULT_FLOOR_BYTES}. An\n * owner who followed the release note would set a number that does\n * nothing — which is worse than the note being wrong, since it fails\n * silently and looks like it worked.\n */\n floorBytes: this.#options.loaded.config.minAvailableMemoryBytes,\n });\n /* Every decision, admits included — byollm_022 asks for the distribution,\n and a log of refusals alone cannot say how close the admits ran. */\n await this.#options.ingress.recordMemory({\n at: this.#now(),\n jobId,\n backendId: route.backendId,\n decision,\n memory,\n pressure,\n });\n return decision;\n }\n\n /**\n * Start the server if needed, then run the job on it.\n *\n * Extracted so the gate above reads as one decision with two outcomes\n * rather than an early return threaded past a long call — and so that\n * \"start the server\" and \"ask the server\" stay on the same side of the\n * guard. They have to: starting one is the expensive half.\n */\n async #runOnBackend(\n route: ResolvedRoute,\n backend: Backend,\n prompt: string,\n context: {\n community: boolean;\n limits: LoadedConfig[\"config\"];\n signal: AbortSignal;\n /**\n * When the job stops being worth doing — the stub's own TTL.\n *\n * `Infinity` when a caller assembled a job without one, which yields the\n * unclamped ceiling: the behaviour that existed before B199, rather than\n * a zero that would refuse every job.\n */\n deadlineAt: number;\n },\n ): Promise<BackendResult> {\n await this.#ensureLocalServer(route, backend);\n const { community, limits } = context;\n const ceiling = community\n ? Math.min(limits.community.maxWallClockMs, limits.limits.maxWallClockMs)\n : limits.limits.maxWallClockMs;\n /**\n * Never grind past the moment the answer stopped being wanted — B199.\n *\n * Todd's four-tab test: the box claimed two chat jobs and produced no\n * outcome for ten minutes. They were not hung — they were grinding toward\n * `maxWallClockMs`, **600,000 ms by default, while the job's own TTL was\n * 120,000** and the page had stopped watching at 45,000.\n *\n * **The loss is not the wasted work, it is the slot.** A held slot is a\n * device that claims nothing (B196), so at the default concurrency two\n * dead jobs silence a box for ten minutes. `.89` fixes the LEAK; a slot\n * held by a live child grinding past its job's death is a different loss\n * and nothing addressed it.\n *\n * The stub carries the deadline and the daemon knows the time, so the\n * ceiling is simply the smaller of the two. An owner who sets a long wall\n * clock still gets it — for jobs whose sites are still waiting.\n */\n const remaining = context.deadlineAt - this.#now();\n if (remaining <= 0) {\n /* Already past its TTL when we reached it. Spawning here would burn a\n slot on an answer nobody can still receive, and `process-backend`'s\n own guard would report it as \"no time limit was set\", which names the\n wrong problem. */\n return {\n ok: false,\n code: \"backend-error\",\n message:\n \"this job's deadline passed before it could start, so it was not run\",\n durationMs: 0,\n };\n }\n return backend.execute({\n prompt,\n model: route.model,\n timeoutMs: Math.min(ceiling, remaining),\n maxOutputBytes: community\n ? Math.min(\n limits.community.maxOutputBytes,\n limits.limits.maxOutputBytes,\n )\n : limits.limits.maxOutputBytes,\n signal: context.signal,\n });\n }\n\n async #ensureLocalServer(\n route: ResolvedRoute,\n backend: Backend,\n ): Promise<void> {\n const spawnServer = this.#options.spawnServer;\n if (spawnServer === undefined) return;\n if (route.backendClass !== \"http\") return;\n const baseUrl =\n this.#options.loaded.config.services[route.service]?.baseUrl;\n if (baseUrl === undefined) return;\n await ensureLocalServer({\n id: route.backendId,\n baseUrl,\n answers: async () => (await backend.health()).healthy,\n spawn: (command) => {\n spawnServer(command);\n },\n wait: (ms) => new Promise((wake) => setTimeout(wake, ms)),\n report: (line) => {\n this.#options.onEvent?.({ type: \"local-server\", detail: line });\n },\n });\n }\n\n /**\n * Run one job, and report HOW it ran as well as what it produced — B064\n * step 4.\n *\n * This returned a bare `JobOutcome`, so everything the caller needed for\n * {@link RunMetadata} had to be reconstructed or invented at the seal —\n * and `durationMs` was invented, as the literal `0`, on every result the\n * site has ever received. The signed account of how a job was produced\n * said each one took no time at all.\n *\n * Returning the metadata with the outcome is what makes the stop reason\n * reachable at the seal, and fixing the duration falls out of the same\n * plumbing rather than being a second change.\n */\n async runJob(job: ClaimedJob): Promise<RanJob> {\n const route = this.#routeFor(job.kind, job.service);\n if (!route) {\n return {\n outcome: {\n outcome: \"error\",\n code: \"no-capability\",\n message: \"this device has no route for that job kind\",\n /* Not retryable, and not from the class table: this is the runner's\n own refusal rather than a backend failure. A device with no route\n for a kind will not grow one by being asked again. */\n retryable: false,\n },\n /* No route, so no backend, so nothing to say about how it ran. The\n caller falls back to what it knows about the job itself. */\n };\n }\n\n const controller = new AbortController();\n this.#active.set(job.lease.id, {\n controller,\n jobId: job.id,\n site: job.site,\n });\n\n /**\n * The `try` starts HERE, not after the ingress write — B021.\n *\n * Its only job is the `finally` that releases the slot, and it used to\n * begin **after** `recordPrompt` and `budgets.record`. Both are awaited\n * writes and both can throw — a full disk, a permission change, a corrupt\n * ledger — and a throw between the `set` above and the old `try` left the\n * entry in `#active` **for ever**.\n *\n * That is not a leaked object, it is a leaked concurrency slot:\n * `free = concurrency - #active.size`, so at the default of 2 two such\n * failures make the daemon claim nothing again, permanently, while\n * `activeJobs: 2` and the status screen both report work in progress.\n * **The device goes quiet and every surface says it is busy.**\n *\n * There is no `catch`, so moving the boundary changes no error handling\n * at all — the throw still propagates. It only guarantees the slot is\n * given back on the way out.\n */\n try {\n const prompt = composePrompt(job);\n const community = job.owner !== this.#options.owner;\n const limits = this.#options.loaded.config;\n\n await this.#options.ingress.recordPrompt({\n at: this.#now(),\n origin: this.#options.client.origin,\n jobId: job.id,\n ...(job.site === undefined ? {} : { site: job.site }),\n kind: job.kind,\n audience: job.audience,\n owner: job.owner,\n backendId: route.backendId,\n backendClass: route.backendClass,\n model: route.model,\n prompt,\n });\n\n if (community) await this.#options.budgets.record(this.#now());\n\n const backend = this.#backendFor(route);\n /**\n * Start the local server if this job needs one — B050.\n *\n * Here rather than at start-up, which is the whole ruling: nothing is\n * pre-warmed, and a server nobody is asking anything of stays off.\n * First-job latency pays the load, and the job's own deadline bounds\n * it. Costs nothing on the paths it cannot help — see the guard.\n */\n /**\n * The guard, and it sits BEFORE the server is started or asked — B080.\n *\n * After this line the model is being loaded, and nothing downstream can\n * take that back. The refusal is shaped like a backend failure because\n * that is what it is from every reader's point of view: the job did not\n * run, and the reason belongs to this machine.\n */\n const gate = await this.#memoryDecision(route, job.id);\n const result: BackendResult =\n gate !== undefined && !gate.admit\n ? {\n ok: false,\n code: \"insufficient-memory\",\n message: gate.why,\n durationMs: 0,\n }\n : await this.#runOnBackend(route, backend, prompt, {\n community,\n limits,\n signal: controller.signal,\n /* Absent means unclamped, which is what happened before B199.\n The stub always carries it, so on the real path it is set. */\n deadlineAt: job.deadlineAt ?? Number.POSITIVE_INFINITY,\n });\n\n /**\n * A service that cannot authenticate stops being advertised, after one\n * job — ruled 2026-08-25.\n *\n * The free half of closing healthy-but-every-job-fails. The paid half is\n * a canary at start; this costs nothing and catches the case the canary\n * cannot: credentials that expire while the daemon is running.\n *\n * One failure, not a streak. A backend that says \"not signed in\" is not\n * flaky — it is telling us a fact that will hold until somebody logs in,\n * and every further job spent confirming it is somebody's work refused\n * for a reason we already knew.\n */\n if (!result.ok && result.code === \"unauthorized\") {\n this.#unauthenticated.add(route.service);\n /* Written, like every other change — S-2's second half, 2026-09-04.\n This path never got the treatment the quota path did, so a mid-run\n sign-out was invisible to `byollm status`: the surface said the\n service was answering while every job it took was refused. */\n /* Spread over what is already there, so the backend's `signIn`\n remedy survives — tick-1 rider. Replacing the whole report drops\n the one field that tells the owner what to run, on the state whose\n entire purpose is to tell them what to run. */\n const withdrawn = this.#noteServiceState(route.service, {\n ...this.serviceStates.get(route.service),\n state: {\n kind: \"signed-out\",\n detail: result.message,\n },\n });\n this.#withdrawnReport.set(route.service, withdrawn);\n this.#options.onEvent?.({\n type: \"service-not-signed-in\",\n service: route.service,\n detail: result.message,\n });\n }\n\n /**\n * A service out of quota stops being advertised, after one job — 019\n * §3.2.\n *\n * The same one-failure rule the auth path states, for the same reason:\n * a CLI reporting a quota block is not flaky, it is telling us a fact\n * that will hold until a clock says otherwise, and every further job\n * spent rediscovering it is somebody's work refused for a reason we\n * already knew.\n *\n * Withdrawing is what lets the site fail fast. Left advertised, the\n * next job is claimed by this device, fails the same way, and the site\n * learns nothing until the job's TTL expires — which is the slow path\n * this whole change exists to close.\n */\n if (!result.ok && result.code === \"quota-exhausted\") {\n this.#blocked.set(route.service, result.until);\n const report = this.#noteServiceState(route.service, {\n ...this.serviceStates.get(route.service),\n state: {\n kind: \"blocked\",\n detail: result.message,\n ...(result.until === undefined ? {} : { until: result.until }),\n },\n });\n this.#blockedReport.set(route.service, report);\n this.#options.onEvent?.({\n type: \"service-out-of-quota\",\n service: route.service,\n ...(result.until === undefined ? {} : { until: result.until }),\n });\n }\n\n const outcome: JobOutcome = result.ok\n ? { outcome: \"ok\", text: result.text }\n : result.code === \"canceled\"\n ? { outcome: \"canceled\" }\n : /**\n * The class, never the text — a8137b5.\n *\n * `result.message` is the CLI's own words and stays with the\n * owner: the event above, the ingress log below, `byollm status`\n * and Your Devices. It does not travel, for two reasons.\n *\n * It quotes the owner's machine — paths, usernames, config\n * locations, account emails all live in CLI errors, and a\n * stranger's page is not where those go.\n *\n * And it names the service. \"the claude CLI is not signed in\"\n * tells a site which model answered, which is the one thing the\n * disclosure fence exists to prevent — arriving through the error\n * path because nobody was watching the error path. Every message\n * named its backend, so every failure leaked what every success\n * is careful to hide.\n *\n * `retryable` comes from the class, not from this result —\n * ruled 2026-09-04. Taking it from the backend let one path\n * inside `service_unavailable` report `true` and made the pair\n * readable as \"rate-limited\". A flag a site can read is a channel\n * whether or not anybody meant it as one.\n */\n {\n outcome: \"error\",\n ...outcomeForSite(result.code),\n };\n\n if (!result.ok) {\n /**\n * A job-time failure is a better probe than the probe — B198.\n *\n * The polling loop reuses a capability set for\n * {@link DETECT_INTERVAL_MS}, and this is what bounds how wrong that\n * set may be. A service that just failed a real call has answered the\n * question `--version` was being asked 17,000 times a day to guess at,\n * and answered it with the only evidence that counts.\n *\n * Unconditional on the failure KIND, deliberately. Classifying which\n * codes mean \"this service is unusable\" is a judgement that would be\n * wrong about the next code somebody adds, and the cost of being wrong\n * in this direction is one extra probe.\n */\n this.#forgetProbe();\n }\n\n await this.#options.ingress.recordOutcome({\n at: this.#now(),\n jobId: job.id,\n ...(job.site === undefined ? {} : { site: job.site }),\n outcome: outcome.outcome,\n durationMs: result.durationMs,\n /* The split, carried with the total — B195. Spread so a result with\n neither writes neither, rather than two zeroes nobody measured. */\n ...(result.timing ?? {}),\n outputChars: result.ok ? result.text.length : 0,\n ...(result.ok ? {} : { detail: result.message }),\n /**\n * Why generation stopped — B064 step 3, and the first production\n * reader `stopReasonOf` has ever had.\n *\n * Detection shipped in .86 and nothing called it, so the daemon knew\n * an answer had been cut off and no surface said so. This is the\n * owner's own log on the owner's own machine: no wire change, and it\n * works for every site already in the field.\n *\n * Read through `stopReasonOf` rather than off `result.stop`, which\n * is the whole point of that function — an adapter that was never\n * taught to report one must not be mistaken for a model that ran to\n * completion.\n */\n ...(result.ok\n ? {\n stop: stopReasonOf(result),\n /* B105: `unknown` from an adapter that cannot read a signal and\n `unknown` from one whose answer we did not recognise are\n different facts, and only the adapter's declaration tells\n them apart. */\n stopKind: backend.stopReasons.kind,\n }\n : {}),\n });\n\n // Community work on a metered backend spends the owner's money, so it\n // goes on the ledger the ceiling is checked against. Own work is not\n // counted: their machine, their key, their call.\n if (community && route.cost === \"metered\") {\n await this.#options.spend.record(\n route.service,\n estimateCents(\n prompt.length,\n result.ok ? result.text.length : 0,\n route.spendCentsPerMillionTokens,\n ),\n this.#now(),\n );\n }\n\n this.#completed += 1;\n this.#options.onEvent?.({\n type: \"finished\",\n jobId: job.id,\n outcome: outcome.outcome,\n durationMs: result.durationMs,\n /* The split, carried with the total — B195. Spread so a result with\n neither writes neither, rather than two zeroes nobody measured. */\n ...(result.timing ?? {}),\n });\n return {\n outcome,\n ran: {\n model: route.model,\n backendClass: route.backendClass,\n /* The measured one. This was the literal `0` at the seal — see\n `runJob`'s note. */\n durationMs: result.durationMs,\n /**\n * **`result.timing` is NOT sealed — B304, and it was for months.**\n *\n * B195 added `...(result.timing ?? {})` here beside the same spread\n * on the two local events, and `RunMetadata` is `.strict()` with\n * five keys, none of them `spawnMs` or `firstOutputMs`. So every\n * result from a PROCESS backend sealed an object the protocol\n * refuses: `openSealedOutcome` at the site returns null, the caller\n * drops it, and the page says \"still going\" until the person gives\n * up. Todd's claude-cli jobs completed in 4–9 seconds on two devices\n * and never displayed; qwen over `openai-http` did, because only\n * `process-backend.ts` attaches a timing split.\n *\n * The two events keep it, and that is the point: the split is an\n * OWNER's fact. `spawnMs` and `firstOutputMs` say where the time\n * went on somebody's own machine — what `byollm status` exists to\n * answer, and what a site is neither owed nor able to use. The same\n * argument `stopReported` makes below about this same seal: same\n * project, opposite requirements.\n */\n /**\n * Only on `ok`, because a failed call has no generation to have\n * ended, and both facts because `unknown` is two of them — an\n * adapter that cannot report, and one whose word we do not map.\n */\n ...(result.ok\n ? {\n stop: stopReasonOf(result),\n /**\n * Three kinds narrowed to one boolean — B107, and the reason\n * belongs here because this is where the narrowing happens.\n *\n * `stopReasons.kind` is `declared`, `unavailable` or\n * `unverified`, and the wire carries a boolean. That is not a\n * loss, because **the two falsy kinds are the same fact to a\n * SITE**: either way no stop signal is coming, and there is\n * nothing a requester could do differently for one over the\n * other.\n *\n * The distinction they hold — *\"we looked and there is\n * nothing\"* versus *\"nobody looked\"* — is an OWNER's fact. It\n * decides what `byollm status` tells somebody to go and check\n * on their own machine, and telling a site to go and check an\n * adapter it does not run would be advice about somebody\n * else's inventory.\n *\n * **Same project, opposite requirements** — the rule\n * `REFUSAL_TEXT` is written under, one field over.\n */\n stopReported: backend.stopReasons.kind === \"declared\",\n }\n : {}),\n },\n };\n } finally {\n this.#active.delete(job.lease.id);\n /* A slot is free, so the loop should not wait out its rung — B212.\n After the delete, so the tick this wakes sees the capacity. */\n this.#wakeNow();\n }\n }\n\n /**\n * Abort one grant's in-flight backend call ({@link MUSTS.CANCEL_HONORED}).\n *\n * By lease, because that is what a cancel names — V1-3. Cancelling by job\n * id aborted whichever job this daemon happened to have filed under that\n * name, which for two sites that chose the same id is a coin flip: one\n * site's cancel stopped another site's work and left its own running.\n */\n cancelLease(leaseId: string): void {\n this.#active.get(leaseId)?.controller.abort();\n }\n\n /** Abort everything — revocation, or shutdown. */\n cancelAll(): void {\n for (const { controller } of this.#active.values()) controller.abort();\n }\n\n /**\n * Run until stopped.\n *\n * Resumable and idempotent by job id: a daemon that dies mid-job loses\n * nothing, because the server reclaims the lease and offers the job again.\n */\n async run(signal: AbortSignal): Promise<void> {\n const heartbeatMs = this.#options.heartbeatMs ?? DEFAULT_HEARTBEAT_MS;\n /** Which rung of {@link POLL_LADDER} the next sleep uses — B212. */\n let step = 0;\n\n while (!signal.aborted && !this.#stopped) {\n try {\n const claimed = await this.tick();\n step = nextRung(step, claimed);\n this.#lastError = undefined;\n } catch (error) {\n this.#lastError =\n error instanceof Error ? error.message : \"unknown error\";\n this.#options.onEvent?.({\n type: \"error\",\n message: this.#lastError,\n });\n if (this.#revoked) return;\n const backoff =\n error instanceof ClientError && error.retryAfter !== undefined\n ? error.retryAfter * 1000\n : heartbeatMs;\n /* The error path keeps its own backoff and does NOT touch the rung —\n B212 note 2. A tick that threw is not a tick that took work, and\n resetting to the fast end here is how a claim→fail→claim spin would\n get built: the server's retry-after is the guard, and this must not\n out-poll it. */\n await sleep(backoff, signal);\n continue;\n }\n /* The ladder's own clock — B212. A wake (a job finished and freed a\n slot) ends this sleep early, so a chat's next message is picked up\n now rather than on the next rung. */\n const wake = new AbortController();\n this.#wake = wake;\n try {\n await sleep(\n this.#pollDelay(step, heartbeatMs),\n AbortSignal.any([signal, wake.signal]),\n );\n } finally {\n this.#wake = undefined;\n }\n }\n }\n\n #pollDelay(step: number, heartbeatMs: number): number {\n return pollDelay(step, heartbeatMs);\n }\n\n /**\n * A slot came free, so stop waiting — B212's chat win.\n *\n * Called when a job leaves `#active`. Without it a back-to-back message\n * waits out whatever rung the ladder is on, which on an idle device is the\n * full interval — the thing this row exists to remove.\n *\n * Safe to call when nothing is sleeping: the controller is cleared by the\n * sleeper itself, and aborting a spent one does nothing.\n */\n #wakeNow(): void {\n this.#wake?.abort();\n }\n\n /**\n * Record a service's state, and tell whoever writes the file — S-2.\n *\n * One door, because the bug was two doors and only one of them wrote. The\n * quota path wrote on block and not on lift; the sign-out path never wrote\n * at all. Both are changes `byollm status` would report differently, and\n * status is a different process whose only source is that file.\n */\n #noteServiceState(service: string, report: ServiceReport): ServiceReport {\n this.serviceStates.set(service, report);\n this.#writeServiceStates();\n return report;\n }\n\n /**\n * Hand the current map to whoever persists it.\n *\n * Not awaited, and the rejection is swallowed on purpose: a job must not\n * fail — nor the runner die on an unhandled rejection — because a status\n * note could not be written. The note is evidence about the job, not part\n * of it.\n */\n #writeServiceStates(): void {\n /*\n * Only when it actually moved.\n *\n * This is called at the end of every detection pass as well as on each\n * change, because `byollm run` wired the callback and never wrote a\n * first time — so a daemon that started, probed, and sat there left\n * `status` reading whatever the last `connect` had said, which on a\n * machine that has since signed out is a stale reassurance.\n *\n * Passes are far more frequent than changes, so the comparison is what\n * keeps this from being a small write every heartbeat forever.\n */\n const now = JSON.stringify([...this.serviceStates]);\n if (now === this.#lastWritten) return;\n const noted = this.#options.onServiceStates?.(this.serviceStates);\n if (noted === undefined) {\n this.#lastWritten = now;\n return;\n }\n /*\n * Marked written only once it was — tick-1 rider.\n *\n * Recording the attempt rather than the success made a failed write\n * permanent: the comparison would match on every later pass and never try\n * again, so one unwritable moment left `status` reading a state the\n * daemon had long since left. A retry costs one file write on the next\n * heartbeat; not retrying costs the truth until a restart.\n */\n void noted.then(\n () => {\n this.#lastWritten = now;\n },\n () => {\n this.#lastWritten = undefined;\n },\n );\n }\n\n /**\n * Note a refusal that means the relationship is over.\n *\n * The **only** way this daemon learns it was revoked — V1-2. It used to\n * infer revocation from an empty site set, which made a control-plane\n * glitch indistinguishable from somebody's decision and cost the machine\n * its pinned keys. An upstream that means \"stop\" says so with a code.\n *\n * Handled here rather than only in {@link Runner.run} because `tick` is\n * what tests and embedders call: a rule that only holds when the loop is\n * driving it is a rule with a shape somebody will step around.\n */\n #revokedBy(error: unknown): boolean {\n if (!(error instanceof ClientError) || error.kind !== \"revoked\") {\n return false;\n }\n this.#revoked = true;\n this.#stopped = true;\n this.cancelAll();\n /**\n * Written down, because every surface that will be asked about this is a\n * different process.\n *\n * The runner stops here and the person finds out somewhere else entirely\n * — `byollm status` tomorrow morning, or a failed `install`. Neither can\n * see this object. Not awaited for the reason the caller's handler gives:\n * a file that cannot be written is worth a message rather than taking\n * down a runner that has already stopped.\n */\n void this.#recordHealth().catch(() => {\n // A health file that cannot be written is a diagnostic that failed, not\n // a reason to take down a runner that has already stopped. The surfaces\n // will fall back to saying less rather than saying something wrong.\n });\n this.#options.onEvent?.({ type: \"revoked\" });\n return true;\n }\n\n /** One heartbeat-and-claim cycle. Exposed so tests can step deterministically. */\n async tick(): Promise<number> {\n /**\n * The beat, written first and unconditionally — B202.\n *\n * Before `#tick()` rather than after, so a daemon wedged inside a tick\n * still shows the beat it started: the question this file answers is *\"is\n * a daemon alive\"*, and one that is stuck partway through a cycle is.\n * A record written only on success would go stale for a daemon that is\n * running and struggling, which is `NOT REPORTING`'s job and not this\n * one's.\n *\n * Unawaited and swallowed: liveness must not be able to fail the work it\n * describes.\n */\n void this.#recordBeat().catch(() => {\n /* Already swallowed inside; this satisfies the no-floating-promises rule\n without pretending there is a failure path to handle. */\n });\n try {\n const claimed = await this.#tick();\n // A cycle that completed is the only thing that clears the count. It is\n // written on the transition rather than every beat, so a healthy daemon\n // is not rewriting a file every ten seconds to say nothing changed.\n if (this.#consecutiveFailures > 0) {\n this.#consecutiveFailures = 0;\n await this.#recordHealth();\n }\n return claimed;\n } catch (error) {\n // Revocation is an answer, not a failure. Swallowed once recorded, so\n // that a caller stepping this loop by hand — a test, an embedder, the\n // conformance kit — sees a daemon that has stopped rather than an\n // exception it has to know to interpret. Everything else still throws.\n if (this.#revokedBy(error)) return 0;\n // Counted before it is rethrown. One refusal is noise — a rolling\n // deploy, a dropped connection — and forty in a row is a device that\n // has stopped participating and does not know it. Only a count tells\n // those apart, so something has to keep one.\n this.#consecutiveFailures += 1;\n this.#lastUpstreamError =\n error instanceof Error ? error.message : \"unknown error\";\n await this.#recordHealth();\n throw error;\n }\n }\n\n /**\n * Beats are written in order, one at a time — B202.\n *\n * Fire-and-forget keeps liveness off the critical path, and two writes in\n * flight at once can land in either order: the older `rename` completing\n * last leaves the file saying a daemon beat LONGER ago than it did, which is\n * the one direction that matters — a stale-looking beat is what `status`\n * calls death.\n *\n * Found by a test that failed one run in three. **Flakiness is evidence**:\n * beats are ten seconds apart in production, so this would have taken a slow\n * disk and a long time to appear, and then appeared as a device reported\n * dead while serving.\n */\n #beats: Promise<void> = Promise.resolve();\n\n #recordBeat(): Promise<void> {\n const path = this.#options.heartbeatPath;\n if (path === undefined) return Promise.resolve();\n const at = this.#now();\n this.#beats = this.#beats.then(() =>\n writeHeartbeat(path, { at, pid: process.pid }),\n );\n return this.#beats;\n }\n\n async #recordHealth(): Promise<void> {\n const path = this.#options.healthPath;\n if (path === undefined) return;\n await writeHealth(path, {\n at: this.#now(),\n consecutiveFailures: this.#consecutiveFailures,\n ...(this.#lastUpstreamError === undefined\n ? {}\n : { lastError: this.#lastUpstreamError }),\n origin: this.#options.client.origin,\n ...(this.#revoked\n ? {\n revoked: {\n at: this.#now(),\n origin: this.#options.client.origin,\n },\n }\n : {}),\n });\n }\n\n async #tick(): Promise<number> {\n const capabilities = await this.#capabilitiesForTick();\n\n const heartbeat = await this.#options.client.heartbeat({\n runnerId: this.#options.runnerId,\n daemonVersion: this.#options.daemonVersion,\n capabilities,\n // What this device could serve and is not, so the surfaces that must\n // explain a missing kind are not all on this machine. The hub decides\n // who is told what; the device only reports.\n withheld: this.#options.loaded.withheld.map((held) => ({\n kind: held.kind,\n claimants: held.services.map((service) => ({\n service,\n offer:\n this.#options.loaded.config.services[service]?.offer ?? \"private\",\n })),\n })),\n activeLeases: [...this.#active.entries()].map(([leaseId, held]) => ({\n jobId: held.jobId,\n leaseId,\n })),\n });\n\n this.#noteClockSkew(heartbeat.serverTime);\n\n // The site set, applied — cloud_008 finding 59.\n //\n // A site that left the set is revoked *for that site*: its pin goes and\n // the others stay. Revocation used to be a boolean that stopped the\n // daemon and dropped its whole pairing, so one site's revocation ended a\n // machine's relationship with every other site it served.\n //\n // A key that *changed* for a site still in the set is refused rather than\n // replaced. The map is keyed by identity key id, so a changed identity\n // arrives as a different entry — this is the encryption key moving under\n // an identity somebody already compared a fingerprint of, which pinning\n // exists to refuse. Amendment A.3.1's rotation is an explicit path, not\n // a silent swap.\n this.#applySites(heartbeat.sites, heartbeat.successions);\n\n // Announced *after* the set is applied — V1-17. The CLI persists\n // `runner.sites` when it sees this event, so firing it first wrote the\n // previous heartbeat's set to disk: every change reached the file one\n // beat late, and the last change before a shutdown never reached it at\n // all.\n this.#options.onEvent?.({\n type: \"heartbeat\",\n capabilities: capabilities.length,\n });\n\n // Nothing will be offered for these, and the reason is not a fault —\n // finding 48. Said once per transition rather than every few seconds: a\n // daemon that repeats itself on a five-second heartbeat is a daemon\n // nobody reads.\n /* Said once per version, not once per beat. The offer arrives on every\n heartbeat until the machine takes it, and a daemon that repeats itself\n on a five-second timer is a daemon nobody reads — the same reasoning\n as the consent line below, which learned it first. */\n if (\n heartbeat.updateTo !== undefined &&\n heartbeat.updateTo !== this.#updateOffered\n ) {\n this.#updateOffered = heartbeat.updateTo;\n this.#options.onEvent?.({\n type: \"update-offered\",\n version: heartbeat.updateTo,\n });\n }\n\n const waiting = heartbeat.awaitingConsent.join(\",\");\n if (waiting !== this.#awaitingConsent) {\n this.#awaitingConsent = waiting;\n if (waiting === \"\") {\n this.#options.onEvent?.({ type: \"consent-resumed\" });\n } else {\n this.#options.onEvent?.({\n type: \"awaiting-consent\",\n sites: [...heartbeat.awaitingConsent],\n });\n }\n }\n\n // A job the server says we lost must be abandoned, not finished\n // ({@link MUSTS.LEASE_HONORED}).\n for (const grant of heartbeat.lost) this.cancelLease(grant.leaseId);\n for (const grant of heartbeat.cancel) this.cancelLease(grant.leaseId);\n\n // An empty set is **not** revocation — V1-2.\n //\n // It used to be read as one: the daemon stopped for good, cancelled\n // everything and dropped its pairing, which meant a projection that\n // arrived empty for a moment cost this machine every pin it held. Absence\n // of consent and withdrawal of consent are different facts, and only the\n // second is somebody's decision. Revocation now arrives the one way it\n // can be certain — the upstream refusing the call with `revoked`, handled\n // in the loop above.\n //\n // The leases named above are still abandoned: whether or not consent has\n // ended, work with no route to return to is work that should stop.\n if (Object.keys(heartbeat.sites).length === 0) {\n if (!this.#servingNothing) {\n this.#servingNothing = true;\n this.#options.onEvent?.({ type: \"serving-nothing\" });\n }\n // And the claim below still goes out. That is the point: asking for\n // work is what tells these two states apart. An upstream that has\n // ended the relationship answers `revoked` and this daemon stops; one\n // that simply has nothing consented answers with no jobs and this\n // daemon keeps its pairing. Inferring either from silence is what V1-2\n // was.\n } else {\n this.#servingNothing = false;\n }\n\n /* Draining: the running job finishes and nothing new is taken. */\n /* Nothing taken, so the ladder treats it as unproductive — B212. */\n if (this.#draining || capabilities.length === 0) return 0;\n\n const { concurrency } = this.#options.loaded.config;\n const free = concurrency - this.#active.size;\n if (free <= 0) {\n /**\n * Said once, on the way in — B196.\n *\n * This was a bare `return`. **A device with no free slot claims nothing\n * for as long as the condition lasts, and said nothing at all**, so it\n * was indistinguishable from a device with no work: still paired, still\n * heartbeating, still `online`.\n *\n * It matters more than an idle log line because the slot leak B021 fixed\n * ships in `.89` and boxes install `latest`, which is `.88`. At the\n * default `concurrency: 2`, two failed ingress writes silence a device\n * permanently — and this is the only line that would say so.\n */\n if (!this.#noFreeSlot) {\n this.#noFreeSlot = true;\n this.#options.onEvent?.({\n type: \"no-free-slot\",\n active: this.#active.size,\n concurrency,\n });\n }\n /* No slot, so nothing was claimed — the ladder must not read a full\n device as a productive tick and poll it harder. B212. */\n return 0;\n }\n if (this.#noFreeSlot) {\n /* The other edge. Without it somebody reads \"taking no work\" and never\n learns it cleared, which is the same silence pointed the other way. */\n this.#noFreeSlot = false;\n this.#options.onEvent?.({ type: \"free-slot-again\", free });\n }\n\n const { jobs } = await this.#options.client.claim({\n runnerId: this.#options.runnerId,\n capabilities,\n max: free,\n });\n\n // Not awaited: claimed jobs run concurrently up to the owner's limit, and\n // the loop keeps heartbeating so their leases stay alive.\n //\n // The `catch` is load-bearing. Without it, a failure in the background\n // handler — an ingress write that cannot land because the disk is full or\n // the state directory has gone — becomes an unhandled rejection and takes\n // the whole daemon down. The lease lapses instead and the server offers\n // the job again, which is the recovery the protocol is built around.\n for (const job of jobs) {\n void this.#handle(job).catch((error: unknown) => {\n this.#lastError =\n error instanceof Error ? error.message : \"unknown error\";\n this.#options.onEvent?.({ type: \"error\", message: this.#lastError });\n });\n }\n /**\n * What the ladder reads — B212.\n *\n * The number CLAIMED, not the number finished: the jobs above are\n * deliberately not awaited, so \"productive\" has to mean \"this device just\n * took work\" rather than \"this device just delivered\". A device that\n * claimed three jobs is exactly the device a fourth is likely to be for.\n */\n return jobs.length;\n }\n\n /**\n * Has this device already had its turns at this job id — B041.\n *\n * `\"go\"` records the attempt; `\"wait\"` and `\"poison\"` do not, so a job\n * refused inside its backoff does not burn one of its own chances.\n */\n #attemptVerdict(key: string): \"go\" | \"wait\" | \"poison\" {\n /* Pruned here because this is the only place that learns time has\n passed for these entries — the same reasoning as the give-up map, and\n the same reason a timer would be the wrong instrument. */\n for (const [id, entry] of this.#attempts) {\n if (this.#now() - entry.at > ATTEMPT_TTL_MS) this.#attempts.delete(id);\n }\n\n const seen = this.#attempts.get(key);\n if (seen === undefined) {\n this.#attempts.set(key, { attempts: 1, at: this.#now() });\n return \"go\";\n }\n if (seen.attempts >= MAX_JOB_ATTEMPTS) return \"poison\";\n if (this.#now() - seen.at < ATTEMPT_BACKOFF_MS) return \"wait\";\n this.#attempts.set(key, {\n attempts: seen.attempts + 1,\n at: this.#now(),\n });\n return \"go\";\n }\n\n /**\n * This job reached an end, so its attempt history stops mattering — B041.\n *\n * Called wherever the job leaves this device for good. Forgetting on\n * success is what keeps the breaker a breaker rather than a lifetime quota\n * on a job id: a site that legitimately re-runs the same id after a\n * completed job should not find this device counting from two.\n */\n #jobSettled(job: { id: string; site?: string }): void {\n this.#attempts.delete(jobKey(job));\n }\n\n async #handle(job: ClaimedStub): Promise<void> {\n this.#options.onEvent?.({\n type: \"claimed\",\n jobId: job.id,\n kind: job.kind,\n });\n\n /**\n * The circuit breaker, before anything else looks at this job — B041.\n *\n * First, so that it covers every way the work below can fail to produce\n * a result. A breaker placed after the interesting code protects against\n * the failures somebody already thought of, which are exactly the ones\n * that already have remedies.\n */\n const verdict = this.#attemptVerdict(jobKey(job));\n if (verdict === \"wait\") {\n /* Not worked and NOT released: the lease lapses and the hub offers it\n again later, which is the spacing. Releasing would need a reason\n code, and none of the five means \"not yet\". */\n return;\n }\n if (verdict === \"poison\") {\n this.#refused += 1;\n await this.#options.ingress.recordOutcome({\n at: this.#now(),\n jobId: job.id,\n site: job.site,\n outcome: \"refused\",\n detail:\n `this device began this job ${String(MAX_JOB_ATTEMPTS)} times ` +\n `without finishing it`,\n });\n this.#options.onEvent?.({\n type: \"refused\",\n jobId: job.id,\n reason:\n `began ${String(MAX_JOB_ATTEMPTS)} times without finishing — ` +\n `refusing it rather than taking it again`,\n });\n /* `refused` is what stops the hub re-offering to this device, which is\n what ends the loop. The job is not declared broken for everybody: a\n different device may well complete it, and this one has no standing\n to say otherwise. */\n await this.#safely(() =>\n this.#options.client.release({\n runnerId: this.#options.runnerId,\n leases: [{ jobId: job.id, leaseId: job.lease.id }],\n reason: \"refused\",\n }),\n );\n this.#jobSettled(job);\n return;\n }\n\n const admission = this.admit(job);\n if (!admission.ok) {\n /* Refused on this device's own rules, permanently — so the attempt\n history is spent bookkeeping about a job that will not come back\n (B041). */\n await this.#refuseOnOurOwnRules(job, admission.reason);\n return;\n }\n\n // Only now, after this daemon has decided it will run the work, does the\n // payload arrive (byollm_009 §6). A daemon that declines on its own\n // allowlist never receives the prompt at all — which was not true when\n // the payload rode along with the claim.\n const fetched = await this.#fetchWhenSealed(job);\n if (!fetched) return;\n if (\"over\" in fetched) {\n /**\n * The upstream says this job has already finished — refused at once.\n *\n * Not routed through the attempts counter below, which exists for\n * \"could not fetch it\" and its transient reading. There is no\n * transient reading of `too-late`: the upstream answered, and the\n * answer will be the same in five seconds. Lease-lapsing three times\n * here would be two more claims of work that no longer exists.\n */\n this.#gaveUpFetching.delete(jobKey(job));\n this.#options.onEvent?.({\n type: \"error\",\n message: `${job.id} is over (${fetched.over})`,\n });\n await this.#safely(() =>\n this.#options.client.release({\n runnerId: this.#options.runnerId,\n leases: [{ jobId: job.id, leaseId: job.lease.id }],\n reason: \"refused\",\n }),\n );\n return;\n }\n if (\"gone\" in fetched) {\n /**\n * Terminal *after a few tries*, not on the first — corrected\n * 2026-09-04 after the first fix over-corrected.\n *\n * A bare 404 does not mean what it looks like. When a site takes longer\n * than `AWAITING_PAYLOAD_MS` to seal, the relay requeues the unsealed\n * claim and clears the holder — so this device's next fetch is a 404\n * for a job that is perfectly alive and about to be sealed. Refusing on\n * the first one made a transient slow-seal permanent: on a sole-runner\n * deployment the owner's own request then never ran at all, silently,\n * until its deadline. The loop I removed was loud; this was quiet,\n * which is worse.\n *\n * The relay offers three outcomes and a daemon release can only ask for\n * two of them: `refused` is forever, and any other reason requeues\n * immediately and stays claimable by this device — a spin. The middle\n * ground it actually needs, \"ask again later\", is `retryAfter`, which\n * only the relay sets.\n *\n * So the middle ground here is to do nothing and let the lease lapse,\n * which is exactly what self-healed for this system's whole life before\n * yesterday. It costs the lease's remaining time and asks for no reason\n * that would be untrue on the wire. Only once a job has done this\n * repeatedly is it treated as genuinely gone.\n */\n /* Forget anything old enough that its job cannot be offered again.\n Done here rather than on a timer: this is the only place that learns\n a give-up happened, and a map nobody prunes is the shape of the\n presence-set leak this project has already fixed once. */\n for (const [id, entry] of this.#gaveUpFetching) {\n if (this.#now() - entry.at > GAVE_UP_TTL_MS) {\n this.#gaveUpFetching.delete(id);\n }\n }\n const seen = this.#gaveUpFetching.get(jobKey(job));\n const attempts = (seen?.attempts ?? 0) + 1;\n this.#gaveUpFetching.set(jobKey(job), { attempts, at: this.#now() });\n this.#options.onEvent?.({\n type: \"error\",\n message:\n `could not fetch ${job.id} (${fetched.gone}) — attempt ` +\n `${String(attempts)} of ${String(FETCH_ATTEMPTS_BEFORE_GONE)}`,\n });\n\n if (attempts < FETCH_ATTEMPTS_BEFORE_GONE) {\n // The lease lapses and the relay offers it again. If the site was\n // merely slow, the next claim gets a fresh sealing window.\n return;\n }\n\n /* Now it is gone. `refused` is what stops the hub re-offering to this\n device, which is what ends the loop — a silent abandon is what made\n it one. */\n this.#gaveUpFetching.delete(jobKey(job));\n this.#jobSettled(job);\n await this.#safely(() =>\n this.#options.client.release({\n runnerId: this.#options.runnerId,\n leases: [{ jobId: job.id, leaseId: job.lease.id }],\n reason: \"refused\",\n }),\n );\n return;\n }\n // It arrived. Whatever this job did before, it is not a poison job.\n this.#gaveUpFetching.delete(jobKey(job));\n const payload = await this.#openPayload(job, fetched.envelope);\n\n /**\n * The payload measured, now that there is one to measure — B188.\n *\n * Declared a BLOCKER on 2026-08-27 and then on no board for fifteen days.\n * `open-door-readiness.md` gates it: *\"must be fixed before any site opens\n * to untrusted end users.\"*\n *\n * Measured with `payloadTextLength`, which is the SAME function the site\n * used to derive `sizeClass` in the first place — so the declaration and\n * the audit cannot disagree about what \"length\" means. Its own docstring\n * already claimed it was *\"used by the daemon's community budget check\"*,\n * and until now that was a sentence about a call that did not exist.\n *\n * Only for other people's work. The owner's own jobs never reach the\n * community budget, here or at admission.\n */\n if (job.owner !== this.#options.owner) {\n const refusal = this.#withinCommunityBudget(job, payload);\n if (refusal !== undefined) {\n await this.#refuseOnOurOwnRules(job, refusal);\n return;\n }\n }\n\n // The grant's resolution, carried into the opened job. A relayed job runs\n // on the service the person mapped; a direct one on the owner's default.\n const resolved = job.grant?.service;\n const route = this.#routeFor(job.kind, resolved);\n const { outcome, ran } = await this.runJob({\n ...job,\n payload,\n service: resolved,\n });\n\n /**\n * It ran — so whatever it did before, it is not poison (B041).\n *\n * Forgetting on a completed run is what keeps this a breaker rather than\n * a lifetime quota on a job id. It mirrors the line above that clears\n * the fetch counter for the same reason and in the same words: whatever\n * this job did before, it is not that any more.\n */\n this.#jobSettled(job);\n\n // The site's consent ended while this ran — V1-7. There is nothing to\n // seal to: the pin went with the consent, and an answer sealed to a\n // withdrawn site is an answer nobody may read. The lease goes back so the\n // upstream is not left waiting out a grant nobody will finish.\n if (this.#abandoned.delete(job.lease.id)) {\n await this.#safely(() =>\n this.#options.client.release({\n runnerId: this.#options.runnerId,\n leases: [{ jobId: job.id, leaseId: job.lease.id }],\n reason: \"revoked\",\n }),\n );\n return;\n }\n\n /**\n * What the run reported, not what this scope could guess — B064 step 4.\n *\n * The fallback is for the one case `runJob` returns no metadata: no route\n * on this device, so no backend, so nothing ran. Everything else — model,\n * class, the measured duration, and now the stop reason — comes from the\n * call that actually happened.\n *\n * `durationMs: 0` used to be unconditional here, so the site's signed\n * account said every job took no time at all.\n */\n const envelope = await this.#sealOutcome(\n job,\n outcome,\n ran ?? {\n model: route?.model ?? \"unknown\",\n backendClass: route?.backendClass ?? \"http\",\n durationMs: 0,\n },\n );\n\n await this.#safely(() =>\n this.#options.client.result({\n runnerId: this.#options.runnerId,\n jobId: job.id,\n // The grant this work was done under, not merely who did it. A runner\n // id survives a claim-release-reclaim cycle; a lease id is the thing\n // that ended.\n leaseId: job.lease.id,\n envelope,\n disposition: outcome.outcome,\n }),\n );\n }\n\n /**\n * Seal a finished outcome back to the site that sent the work.\n *\n * The return leg of {@link ByollmRunner.#openPayload}, and it exists for the\n * same reason: an answer is as sensitive as the prompt that produced it. A\n * relay that is denied one and handed the other has been denied nothing.\n *\n * The signature also does work the payload leg does not need. `RESULT_\n * PROVENANCE` says a result is attributable to a device; until now that\n * rested on the request signature, which covers the request and expires with\n * it. This binds the *outcome itself* to the device's key, so what the app\n * eventually reads carries its own proof of who produced it.\n */\n async #sealOutcome(\n job: ClaimedStub,\n outcome: JobOutcome,\n ran: RunMetadata,\n ): Promise<SealedEnvelope> {\n const identity = this.#options.identity;\n if (!identity) {\n // Unreachable in practice, and the reason is load-bearing: `#openPayload`\n // makes the same check before any work runs, so a keyless daemon fails\n // there — having spent nothing — rather than here, having spent a whole\n // job and lost the answer. Anything that moves the fetch after execution\n // turns this branch into wasted compute.\n throw new Error(\"this daemon has no keys, so it cannot seal a result\");\n }\n const keys = await identity.keys();\n return seal({\n // The outcome **and how it was produced**, as one signed statement —\n // cloud_008 §2.5. Sealing only the outcome would leave the site\n // trusting the envelope for the answer and an unsigned request body for\n // everything about it.\n /**\n * Parsed, not merely `satisfies` — B304.\n *\n * Two extra keys travelled for months under an annotation that reads\n * like a guarantee, and the only thing that noticed was every site\n * dropping the result in silence.\n *\n * `.parse` throws, deliberately. It can only fire when we have already\n * broken the wire contract, and a job that fails loudly on this machine\n * is better than one that succeeds here and hangs forever on somebody\n * else's page — which is exactly the trade B304 turned out to be.\n */\n plaintext: JSON.stringify(SealedOutcome.parse({ outcome, ran })),\n senderKeys: keys,\n // Back to the site that sent it, which is not necessarily the only\n // site this machine serves. Sealing to `sitePinned` would send site B's\n // answer to site A — unopenable there, and readable by nobody, which\n // presents as a job that ran and never came back.\n recipientEncryptionPublic: this.#pinFor(job).encryption,\n context: {\n jobId: job.id,\n senderKeyId: keyId(publicIdentityOf(keys).identity),\n recipientKeyId: keyId(this.#pinFor(job).identity),\n // This runner's clock, not the process's — cloud_008 §31. This was a\n // `Date.now()`, in the one place that stamps a deadline somebody else\n // enforces: a test that moves time moved every other clock here and\n // not this one, so a sealed result carried an expiry from a different\n // timeline than the lease it answered. The retirement window had the\n // same two clocks until it was found the same way; `drain` is the one\n // read left on the process clock, because it waits out real time\n // beside a real sleep.\n deadlineAt: this.#now() + ENVELOPE_MAX_AGE_MS,\n direction: \"result\",\n },\n });\n }\n\n /**\n * Collect the payload, waiting if the upstream does not have it yet.\n *\n * On the direct plane this always succeeds first time: the site *is* the\n * upstream, so it seals when asked. Through a relay the two are different\n * parties — the site must be told which device claimed before it can seal to\n * it — and `not-ready` is a normal answer for as long as that takes.\n *\n * Discovered by the skeleton relay, which is the reason it exists: the\n * original claim-then-fetch had no wait here, so the first relayed job was\n * claimed, refused with a 409 the daemon treated as a rejection, and\n * abandoned while still holding a perfectly good lease.\n *\n * Bounded, and by the lease rather than by a retry count: waiting past our\n * own lease means racing whoever gets the job next. Returning `null` gives\n * the job up quietly — the upstream's `awaiting-payload` timer will requeue\n * it, and the stub was never lost.\n */\n /**\n * Wait for the payload, or say the job is over for this device — B038.\n *\n * The stuck-daemon loop, reported live on a real user's Windows machine: a\n * claimed job came back \"unknown job\", the daemon abandoned it **without\n * releasing the lease**, the hub re-offered it, and the same daemon\n * re-claimed the same id every twenty seconds forever. The log spammed\n * claim, unknown job, reclaim; the device looked wedged while fresh jobs on\n * a live site still ran fine.\n *\n * Both exits leaked. A terminal error threw straight out of `#handle` into\n * the background `catch`, and the give-up-at-deadline path returned `null`\n * into `if (!fetched) return`. Neither released anything, and the comment on\n * that `catch` explains why nobody noticed: letting the lease lapse *is*\n * the recovery the protocol is built around — for a transient failure. For\n * a permanent one it is an invitation to be offered the same job again.\n *\n * So this reports *why* it stopped instead of throwing, and the caller\n * releases. `refused` is the shape the relay already has for exactly this:\n * a permanent decline, which means the job is never offered to this device\n * again. Nothing new on the wire.\n */\n async #fetchWhenSealed(\n job: ClaimedStub,\n ): Promise<\n { envelope: SealedEnvelope } | { gone: string } | { over: string } | null\n > {\n const deadline = Math.min(job.lease.expiresAt, this.#now() + 30_000);\n let delay = 50;\n for (;;) {\n try {\n return await this.#options.client.fetch({\n runnerId: this.#options.runnerId,\n jobId: job.id,\n leaseId: job.lease.id,\n });\n } catch (error) {\n const notReady =\n error instanceof ClientError && error.kind === \"not-ready\";\n if (!notReady) {\n /*\n * `rejected` is a 400 or a 404: the upstream has no such job for\n * this device. Asking again cannot change that, and this is the\n * exact answer the wedged daemon was throwing away.\n *\n * Anything else — unreachable, server-error, rate-limited — is the\n * network or the hub having a moment, and still throws, because\n * the lease lapsing and the job being offered again is the right\n * recovery for those.\n */\n /**\n * `too-late` is its own outcome, not a `gone` — B038's lesson runs\n * out here.\n *\n * `gone` means \"we could not fetch it\", which has a transient\n * reading: refusing on the first 404 turned a slow seal into a\n * permanent refusal, so it lease-lapses three times before it\n * refuses. That is right for a 404 and wrong for this. The\n * upstream is not failing to answer — it is answering, and the\n * answer is that the job has ended. Three lease lapses on a\n * finished job is two more claims of work that no longer exists.\n */\n if (error instanceof ClientError && error.kind === \"too-late\") {\n return { over: error.message };\n }\n if (error instanceof ClientError && error.kind === \"rejected\") {\n return { gone: error.message };\n }\n throw error;\n }\n if (this.#now() + delay >= deadline) {\n this.#options.onEvent?.({\n type: \"error\",\n message: `gave up waiting for the payload of ${job.id}`,\n });\n /* Also a release rather than a silent abandon. A plain one would\n leave this job claimable by this device, which the relay's own\n note says re-claims at once and spins — the same wedge with a\n longer period. This device waited its thirty seconds. */\n return { gone: `the payload never arrived for ${job.id}` };\n }\n await new Promise((resolve) => setTimeout(resolve, delay));\n // Backs off, but stays responsive: a site that is merely slow should\n // not cost the whole lease, and one that is gone is not worth polling.\n delay = Math.min(delay * 2, 1_000);\n }\n }\n }\n\n /**\n * Open work sealed to this machine, or refuse to run it.\n *\n * A failure here is not a job failure to be reported — it is a claim that\n * the work came from the pinned site, which did not hold. Running it anyway\n * would be running whatever an intermediary supplied, on the owner's\n * hardware and their subscription.\n */\n /**\n * Refused on this device's own rules, permanently — extracted for B188.\n *\n * Five steps that have to happen together, and the reason to name them is\n * that there are now two callers: admission, and the payload measurement\n * after the fetch. A second hand-written copy is how two refusals come to\n * differ in which of the five they do — and the one most easily dropped is\n * `release`, whose absence leaves the hub re-offering the job to a device\n * that has already decided against it.\n *\n * `refused` rather than a lapse, because a lapse means \"ask me later\" and\n * this device's answer will not change.\n */\n async #refuseOnOurOwnRules(job: ClaimedStub, reason: string): Promise<void> {\n this.#jobSettled(job);\n this.#refused += 1;\n await this.#options.ingress.recordOutcome({\n at: this.#now(),\n jobId: job.id,\n site: job.site,\n outcome: \"refused\",\n detail: reason,\n });\n this.#options.onEvent?.({ type: \"refused\", jobId: job.id, reason });\n await this.#safely(() =>\n this.#options.client.release({\n runnerId: this.#options.runnerId,\n leases: [{ jobId: job.id, leaseId: job.lease.id }],\n reason: \"refused\",\n }),\n );\n }\n\n /**\n * The community budget, asked about the payload that actually arrived — B188.\n *\n * Returns the refusal to give, or `undefined` to proceed.\n *\n * The admission-time call passes `sizeClassCeiling(job.sizeClass)`, a number\n * the sender chose. This passes `payloadTextLength` of the opened payload,\n * which is the same function the site used to derive that class — so a job\n * whose declaration was honest is measured to the same place it declared,\n * and one that lied is caught here.\n *\n * The whole `check` is re-run rather than the payload arm alone, because the\n * limits it enforces are one decision: a device that has since hit its\n * hourly cap has the same answer for this job, and splitting the predicate\n * would be a second copy of the rule for the sake of a narrower question.\n */\n #withinCommunityBudget(\n job: ClaimedStub,\n payload: JobPayload,\n ): string | undefined {\n const decision = this.#options.budgets.check(\n this.#now(),\n payloadTextLength({\n kind: job.kind,\n payload,\n } as Parameters<typeof payloadTextLength>[0]),\n );\n return decision.ok ? undefined : decision.detail;\n }\n\n async #openPayload(\n job: ClaimedStub,\n envelope: SealedEnvelope,\n ): Promise<JobPayload> {\n const identity = this.#options.identity;\n if (!identity) {\n throw new Error(\n \"this daemon has no keys, so it cannot open work sealed to it\",\n );\n }\n const keys = await identity.keys();\n\n // Which site's key opens this, decided by the stub — Amendment A §A.3\n // makes `stub.site` a key id precisely so this is a lookup the daemon can\n // do without asking anybody.\n const pinned = this.#pinFor(job);\n const pinnedKeyId = keyId(pinned.identity);\n\n // What the envelope *claims* about its sender, checked against what the\n // stub *says* about its site, before any crypto — cloud_009 §4.2.\n //\n // The relay hands over both, and it is the party that would benefit from\n // them disagreeing: a stub naming a site this machine trusts, wrapped\n // around a payload from somewhere else. `open` below refuses that too,\n // because the signature will not verify against the pinned key — which is\n // exactly why this check has to exist separately and say something\n // different. A refusal that reads \"the payload did not verify\" sends\n // whoever is debugging it to the crypto, and the fault is in the routing.\n //\n // Neither value is trusted: `senderKeyId` is inside the signature, so a\n // relay that edits it to match only moves the failure back to `open`.\n if (envelope.senderKeyId !== job.site) {\n throw new Error(\n `refusing job ${job.id}: the stub names site ${job.site}, but the ` +\n `payload declares it was sealed by ${envelope.senderKeyId}`,\n );\n }\n\n const opened = await open({\n envelope,\n recipientKeys: keys,\n senderIdentityPublic: pinned.identity,\n expected: {\n jobId: job.id,\n senderKeyId: pinnedKeyId,\n recipientKeyId: keyId(publicIdentityOf(keys).identity),\n direction: \"payload\",\n },\n });\n if (!opened.ok) {\n throw new Error(\n `refusing job ${job.id}: its payload did not verify as coming from ` +\n `the app this device paired with (${opened.reason})`,\n );\n }\n return JSON.parse(opened.plaintext) as JobPayload;\n }\n\n /**\n * The pinned key for the site a stub names — cloud_009 §5.\n *\n * One place, because both legs need it and they must agree: opening the\n * payload with site A's key while sealing the answer to site B is a job\n * that runs, produces something nobody can read, and reports success.\n *\n * A site the map does not hold is refused by name rather than falling back\n * to any key at hand. The fallback is the failure this exists to prevent —\n * the relay chooses `stub.site`, and a daemon that treats an unknown site\n * as \"probably the one I know\" has handed the choice of which key verifies\n * its work to the party the pinning is defending against.\n */\n /**\n * The sites this daemon is pinned to, as the upstream last described them.\n *\n * Seeded from the pairing and replaced by every heartbeat — cloud_009 §5.\n * Held here rather than read from options each time because it *changes*:\n * a set frozen at construction would serve a site whose consent ended\n * until the process restarted.\n */\n #sites: Map<string, PublicIdentity>;\n\n /** Every site ever approved here, with the key it was approved under. */\n readonly #known: Map<string, PublicIdentity>;\n\n /**\n * Ids whose key has been superseded, and until when this machine will still\n * serve them — byollm_009 Amendment C, `retiringUntil`.\n *\n * A rotation is not instant on the wire. Work enqueued a minute before it\n * was signed by the old key and names the old id, and a daemon that dropped\n * that pin the moment the projection moved would refuse jobs that are\n * perfectly good — a flag day, for everyone whose heartbeat happened to\n * arrive on the wrong side of the change.\n *\n * **This machine's clock decides**, not the projection's deadline alone.\n * The upstream proposes a moment; a value in the past is already over and a\n * value beyond the protocol's window is clamped to it. A projection that\n * could extend this at will would be a two-key site forever, decided by the\n * party the design does not trust.\n */\n readonly #retiring = new Map<string, number>();\n\n /** The last version offered, so the event fires on change rather than on every beat. */\n #updateOffered: string | undefined;\n\n /** Set while this machine is finishing its work and taking none. */\n #draining = false;\n\n /**\n * Sites this device has actually run work for, so the first one is loud.\n *\n * Amendment K moved site policy to the control plane: there is no longer a\n * ceremony on this machine where somebody says yes to a site, which is a\n * real reduction in what a device owner controls and is recorded as an\n * accepted trade. **This set is the mitigation.** The first job from a site\n * this machine has never served announces itself, so a change made in an\n * account is still loud at the hardware.\n *\n * Fired at admission, before the backend is touched. A notice that arrived\n * after the first job had run would be a receipt rather than a warning, and\n * the thing worth warning about is the first one.\n *\n * Distinct from {@link #known}, which is the *pinning* record and must be\n * written the moment a key is first seen. This is about work, and the two\n * answer different questions: \"which key is this site's\" and \"has this\n * machine ever done anything for it\".\n */\n readonly #served = new Set<string>();\n\n /**\n * Take a control-plane key that arrived after this loop started.\n *\n * `byollm connect` is a different process: it writes a new pairing and the\n * running daemon goes on holding the one it was constructed with. Without\n * this, a re-pair looks like it worked, the file gains the key, and the\n * loop keeps refusing every roster — while overwriting the file's good\n * state with its own stale refusal. Which is exactly what happened to\n * Todd's device the first time anybody re-paired for real.\n *\n * Approvals already reach this loop through the file for the same reason.\n * This is that mechanism, for the other thing pairing produces.\n *\n * **Adopted only when there is none.** A key that could be *replaced* from\n * disk would be a downgrade path: anything that could write the file could\n * swap the authority this device checks grants against. Rotation is\n * Amendment C's ceremony, not a file edit.\n */\n adoptControlPlaneKey(key: string): void {\n if (this.#controlPlanePublic !== undefined) return;\n this.#controlPlanePublic = key;\n }\n\n /**\n * Take the upstream's site set, refusing a key that moved under an id.\n *\n * Adds and removals are ordinary: consent is a thing users change, and a\n * site leaving the set is revoked for that site alone. A **changed key for\n * an id already pinned** is not ordinary — the map is keyed by identity key\n * id, so a new identity is a new entry, and this is the encryption key\n * moving under an identity whose fingerprint somebody already compared.\n * Pinning exists to refuse exactly that, so it is kept and reported rather\n * than replaced. Amendment A.3.1's rotation is an explicit path.\n */\n #applySites(\n sites: Record<string, PublicIdentity>,\n successions:\n | Record<\n string,\n { succeeds: Succession[]; retiringUntil?: number | undefined }\n >\n | undefined,\n ): void {\n for (const [id, site] of Object.entries(sites)) {\n // The upstream's account of a site, checked before anything is pinned.\n // A key that is not signed by the identity presenting it is what\n // `connect` refuses at pairing (`connect.ts`), and the heartbeat is the\n // same claim arriving later — an upstream that could add a site here\n // without this check would have a door around the ceremony.\n if (!verifyPublicIdentity(site)) {\n this.#refuseSite(\n id,\n \"its encryption key is not signed by the identity presenting it\",\n );\n continue;\n }\n // The map key is the site's identity key id (Amendment A §A.3) and\n // `stub.site` names the same id. If the key and the identity disagree,\n // a stub could name an id whose pin belongs to a different identity —\n // the pin lookup would succeed while pointing at the wrong site.\n if (keyId(site.identity) !== id) {\n this.#refuseSite(id, \"the id it is filed under is not its key id\");\n continue;\n }\n\n const approved = this.#known.get(id);\n if (approved === undefined && this.#rotateInto(id, site, successions)) {\n // A verified succession. Handled above rather than here because it is\n // not a stranger and not a substitution: it is a key this machine\n // already vouched for, signing for the one in front of it.\n continue;\n }\n if (approved === undefined) {\n /**\n * First sighting: pinned here, and served — Amendment K.\n *\n * There used to be a queue and a ceremony at this line. A site the\n * upstream offered sat unpinned and unserved until somebody ran\n * `byollm approve`, because \"consent to serve a site lives on the\n * site's side of the relay, where the relay itself could write it;\n * the machine that will do the work says yes here.\"\n *\n * That fence moved rather than fell. Site policy is the control\n * plane's now, and what the device kept is the part a relay still\n * cannot forge: the **pairing** ceremony, where a human compared a\n * fingerprint, plus the pinning below. This machine still refuses a\n * key that moves under an id it has already pinned, and still runs\n * nothing without a grant signed by the key it pinned at pairing.\n *\n * What it no longer does is ask. The trade is recorded plainly: a\n * compromised control plane can point this device at a site its owner\n * never chose. Spend caps bound the damage; {@link\n * #served} makes it loud. It is the largest single reduction in\n * device-side control in this design, and it is deliberate.\n */\n this.#known.set(id, site);\n this.#sites.set(id, site);\n continue;\n }\n\n if (!sameKey(approved, site)) {\n // A key that moved under an id somebody already compared a\n // fingerprint of. Refused whether the id is currently served or was\n // dropped and re-offered: `#known` outlives consent precisely so\n // remove-then-re-add is not a way around this branch.\n this.#options.onEvent?.({ type: \"site-key-changed\", site: id });\n continue;\n }\n\n // Pinned from `#known` rather than from the heartbeat: the bytes this\n // machine seals to come off its own disk, having been approved once,\n // rather than off the wire every few seconds.\n this.#sites.set(id, approved);\n }\n\n for (const id of [...this.#sites.keys()]) {\n if (id in sites) continue;\n // Superseded rather than withdrawn: keep serving it until this machine's\n // own clock says the window is over. Checked here rather than at pin\n // time so that a site which withdraws consent *and* rotates still has\n // its work stopped — `sites` not containing the id is the only signal\n // for consent, and the window only holds the pin open, never the route.\n const until = this.#retiring.get(id);\n if (until !== undefined && this.#now() < until) continue;\n this.#retiring.delete(id);\n this.#sites.delete(id);\n // And stop the work already running for it — V1-7.\n //\n // Consent ending used to shrink the set and nothing else: a job\n // in flight ran to completion on somebody's machine, spending their\n // electricity or their API credit, and then failed at the seal because\n // the pin it needed had just been dropped. Full cost, no result, and\n // the one person who paid for it was the one who withdrew.\n for (const [leaseId, held] of this.#active) {\n if (held.site !== id) continue;\n this.#abandoned.add(leaseId);\n held.controller.abort();\n this.#options.onEvent?.({\n type: \"refused\",\n jobId: held.jobId,\n reason: `site ${id} withdrew consent while this job was running`,\n });\n }\n }\n }\n\n /**\n * Refuse an offer without touching what was approved.\n *\n * Deliberately *not* a removal. A site that is being served was approved\n * here, key and all; an upstream that starts sending a malformed record for\n * it has said nothing about consent, and treating garbage as revocation\n * would hand any upstream a way to unpin a site by sending nonsense. The\n * only thing that stops a site being served is its absence from the set,\n * which is the signal consent actually speaks through.\n */\n /**\n * Accept a site whose current key proves descent from one already approved.\n *\n * byollm_009 Amendment C, and the reason `SITES_LOCALLY_APPROVED` was\n * amended rather than exempted: **a verified succession is not a changed\n * key.** The refusal that rule exists for is a new key arriving with nothing\n * but the upstream's word for it. A key that arrives with a signature by the\n * key already pinned is the same site proving continuity, and accepting it\n * is the pin doing its job rather than being bypassed.\n *\n * Mechanically the two are far apart, which is what makes this safe to\n * write: a substitution presents different bytes *for the same key id* and\n * is refused a few lines below; a succession presents a *new key id* plus a\n * signature by the old one over a statement naming both.\n *\n * ## What the chain is walked against\n *\n * `#known`, which outlives consent — every site ever approved here,\n * including ids that were dropped. That is deliberate: a site that left the\n * allowlist and came back is still one this machine vouched for, and\n * rotation must not become a way to launder that distinction in either\n * direction.\n *\n * ## Where the succession is allowed to come from\n *\n * The projection, and nowhere else. Ruling 3 makes the control plane a\n * second authority precisely so that a stolen identity key is not by itself\n * enough to move a site: the proof must be one the control plane is also\n * publishing, which means somebody got at the dashboard as well. A\n * succession arriving on a stub, a request, or any other path a site\n * controls alone would give that second authority away, so there is exactly\n * one caller of this and it is the heartbeat.\n *\n * Returns whether the id was adopted. `false` means \"not a succession\" —\n * the caller carries on to the stranger path, which is the right answer for\n * a chain that verifies but reaches nobody this machine knows.\n */\n #rotateInto(\n id: string,\n site: PublicIdentity,\n successions:\n | Record<\n string,\n { succeeds: Succession[]; retiringUntil?: number | undefined }\n >\n | undefined,\n ): boolean {\n const offered = successions?.[id];\n if (!offered || offered.succeeds.length === 0) return false;\n\n const walk = walkSuccession({\n current: id,\n chain: offered.succeeds,\n approved: (candidate) => this.#known.has(candidate),\n });\n\n if (walk.failure === \"broken-link\" || walk.failure === \"too-long\") {\n // Either a broken site or the attack, and this daemon cannot tell which\n // — so it does what it does for every unverifiable claim: keeps the pin\n // it has and says so. Loudly, because a chain that fails to verify is\n // the one event here that nobody should have to go looking for.\n this.#refuseSite(\n id,\n walk.failure === \"too-long\"\n ? \"its succession chain is longer than this daemon will walk\"\n : \"a link in its succession chain is not signed by the key before it\",\n );\n return true;\n }\n\n if (walk.from === undefined) return false;\n\n const previous = this.#known.get(walk.from);\n /* c8 ignore next */\n if (!previous) return false;\n\n // The pin moves. Both ids stay in `#known`: the old one because\n // tombstones are how remove-then-re-add is refused, and the new one\n // because it is now a key this machine has pinned — by the only ceremony\n // available for it, which is the previous key's signature.\n this.#known.set(id, site);\n this.#sites.set(id, site);\n\n // The predecessor keeps its pin for the length of the window, so work\n // already signed under it still verifies. Clamped to the protocol's\n // constant: the upstream may retire a key sooner than the window, never\n // later — ruling 2 makes the overlap a protocol fact rather than the\n // site's to choose, and \"forever\" is the value a site would pick.\n const ceiling = this.#now() + RETIREMENT_WINDOW_MS;\n const proposed = offered.retiringUntil ?? ceiling;\n this.#retiring.set(walk.from, Math.min(proposed, ceiling));\n\n // Announced once per rotation rather than every heartbeat: the id is in\n // `#known` from here on, so this branch is not reached again for it.\n this.#options.onEvent?.({\n type: \"site-rotated\",\n site: id,\n from: walk.from,\n fromFingerprint: fingerprint(previous.identity),\n fingerprint: fingerprint(site.identity),\n path: walk.path,\n });\n return true;\n }\n\n #refuseSite(id: string, reason: string): void {\n this.#options.onEvent?.({ type: \"site-refused\", site: id, reason });\n }\n\n #pinFor(job: ClaimedStub): PublicIdentity {\n const identity = this.#options.identity;\n if (!identity) {\n throw new Error(\"this daemon has no keys, so it has no site to pin\");\n }\n const fromMap = this.#sites.get(job.site);\n if (fromMap) return fromMap;\n // Names what this machine *is* paired with, not only what it refused.\n // An earlier draft dropped that half — \"paired with X\" stops being a\n // sentence when there are several — and `loop.test.ts` caught it: an\n // operator reading this is comparing two fingerprints, and one of them\n // was missing.\n const paired = [...this.#sites.keys()].sort();\n throw new Error(\n `refusing job ${job.id}: it names site ${job.site}, which this device ` +\n `is not paired with (paired with ${paired.join(\", \")})`,\n );\n }\n\n /**\n * Report-and-forget.\n *\n * A failed *report* must not crash the loop or re-run the job: the lease\n * will lapse and the server will offer the work again, which is exactly the\n * recovery the protocol is built around.\n */\n async #safely(action: () => Promise<unknown>): Promise<void> {\n try {\n await action();\n } catch (error) {\n this.#lastError =\n error instanceof Error ? error.message : \"unknown error\";\n }\n }\n\n /** Release everything on shutdown, so nothing waits for a lease to lapse. */\n /**\n * Stop claiming, and let what is running finish — B053.\n *\n * Deliberately not {@link shutdown}, which cancels the active jobs and\n * releases their leases. That is right for stopping and wrong for\n * updating: an update is elective and a job is not, so replacing the\n * binary under work somebody is waiting on trades their result for our\n * tidiness.\n *\n * Resolves when nothing is held, or when `waitMs` has passed — because a\n * job that outlives its own lease must not hold a machine out of service\n * indefinitely, and the lease is the thing that bounds it either way.\n */\n async drain(waitMs: number, sleep = pause): Promise<void> {\n this.#draining = true;\n const until = Date.now() + waitMs;\n while (this.#active.size > 0 && Date.now() < until) {\n await sleep(DRAIN_POLL_MS);\n }\n }\n\n /** Undo a drain — the machine claims again. */\n resumeClaiming(): void {\n this.#draining = false;\n }\n\n async shutdown(reason: \"shutdown\" | \"pause\"): Promise<void> {\n this.#stopped = true;\n const leases = [...this.#active.entries()].map(([leaseId, held]) => ({\n jobId: held.jobId,\n leaseId,\n }));\n this.cancelAll();\n if (leases.length === 0) return;\n await this.#safely(() =>\n this.#options.client.release({\n runnerId: this.#options.runnerId,\n leases,\n reason,\n }),\n );\n }\n}\n\n/** Case-insensitive model match that tolerates Ollama's `:latest` suffix. */\nfunction modelPresent(models: readonly string[], wanted: string): boolean {\n const target = wanted.toLowerCase();\n return models.some((model) => {\n const id = model.toLowerCase();\n return (\n id === target || id === `${target}:latest` || `${id}:latest` === target\n );\n });\n}\n\nfunction sleep(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n /*\n * An abort that already happened fires no listener.\n *\n * Subscribing was the whole of the wiring, and the common case walks\n * straight past it: a stop lands while `tick()` is in flight, the tick\n * finishes, and the loop calls this with a signal that is already\n * aborted — so it waits out a full heartbeat before the `while` test\n * gets a chance to see it. Ten seconds to answer Ctrl-C, and long enough\n * under launchd for a polite stop to be escalated to a kill.\n *\n * **An AbortSignal is a latch, not an event.** It has to be read as well\n * as subscribed to.\n */\n if (signal.aborted) {\n resolve();\n return;\n }\n const timer = setTimeout(resolve, ms);\n signal.addEventListener(\n \"abort\",\n () => {\n clearTimeout(timer);\n resolve();\n },\n { once: true },\n );\n });\n}\n","import type { ClaimedJob } from \"@byollm/protocol\";\n\n/**\n * Reduce a job's payload to the single string a backend receives.\n *\n * This is the narrowest point in the daemon: everything upstream deals in a\n * job, everything downstream deals in text and an owner-chosen model. By\n * construction there is nothing left for a payload to influence.\n *\n * **On `system`.** byollm_004 §2 forbids payload text on a command line, and\n * the `claude` CLI's only system-prompt input is the argv flag\n * `--system-prompt` — so a payload's `system` can never be passed that way.\n * It is folded into the stdin text instead, under a plain delimiter. That\n * costs a little role fidelity on process-class backends and is documented\n * rather than papered over; HTTP-class backends could carry the role natively\n * but use the same composition so a job produces identical text on either\n * class, which is what makes results comparable across runners.\n */\nexport function composePrompt(job: ClaimedJob): string {\n if (job.kind === \"llm.generate\") {\n const payload = job.payload as { prompt: string; system?: string };\n return joinSections([systemSection(payload.system), payload.prompt]);\n }\n\n const payload = job.payload as {\n messages: { role: string; content: string }[];\n system?: string;\n };\n const turns = payload.messages\n .map((message) => `${roleLabel(message.role)}: ${message.content}`)\n .join(\"\\n\\n\");\n return joinSections([systemSection(payload.system), turns]);\n}\n\nfunction systemSection(system: string | undefined): string | undefined {\n if (system === undefined || system.trim() === \"\") return undefined;\n return `System instructions:\\n${system}`;\n}\n\nfunction roleLabel(role: string): string {\n switch (role) {\n case \"assistant\":\n return \"Assistant\";\n case \"system\":\n return \"System\";\n default:\n return \"User\";\n }\n}\n\nfunction joinSections(sections: readonly (string | undefined)[]): string {\n return sections.filter((section) => section !== undefined).join(\"\\n\\n\");\n}\n","import { mkdir, readFile, rm, stat, writeFile } from \"node:fs/promises\";\nimport { REVOKED_RETURN, REVOKED_SENTENCE } from \"./revoked.js\";\nimport { dirname } from \"node:path\";\nimport {\n refuseToSupervise,\n servicePlan,\n type ServicePlan,\n type ServiceTarget,\n} from \"./service.js\";\n\n/**\n * Installing and removing the service, and asking whether it is running.\n *\n * The half that touches the machine. Kept apart from `service.ts` (which\n * decides *what* to write) so the decisions stay testable everywhere and the\n * effects stay in one small file with two injected seams: writing files and\n * running commands.\n *\n * Nothing here is clever. The interesting property is that every failure\n * arrives as a sentence naming the command that failed and what to do — an\n * install that half-worked is worse than one that refused, because the\n * machine will look installed and not serve.\n */\n\nexport type CommandRunner = (command: readonly string[]) => Promise<{\n readonly code: number;\n readonly output: string;\n}>;\n\n/** What the supervisor says about us. */\nexport type ServiceState =\n | { readonly state: \"running\" }\n /** The unit is installed but the supervisor is not running it. */\n | { readonly state: \"installed\"; readonly detail: string }\n | { readonly state: \"absent\" };\n\nexport async function serviceState(\n plan: ServicePlan,\n run: CommandRunner,\n): Promise<ServiceState> {\n const result = await run(plan.query).catch(() => ({\n code: 127,\n output: \"\",\n }));\n if (result.code !== 0) return { state: \"absent\" };\n\n // Each supervisor answers differently, and each answer has to be read for\n // *running*, not merely *known*. launchd prints a `state = running` line for\n // a live job and a `pid` only when there is one; a loaded-but-stopped job\n // still prints a whole record, so an exit code of zero here means \"the\n // service exists\", never \"it is serving\".\n if (plan.platform === \"darwin\") {\n if (\n /\\bstate = running\\b/.test(result.output) ||\n /\\n\\s*pid = \\d+/.test(result.output)\n ) {\n return { state: \"running\" };\n }\n const last = /last exit (?:code|status) = (-?\\d+)/.exec(result.output);\n return {\n state: \"installed\",\n detail:\n last === null\n ? \"loaded but not running\"\n : `not running (last exit ${last[1] ?? \"?\"})`,\n };\n }\n\n if (plan.platform === \"linux\") {\n // `is-active` exits non-zero for anything but active, so reaching here\n // usually means active — but read the word rather than infer it, because\n // `activating` also exits zero on some versions and is not yet serving.\n const word = result.output.trim();\n return word === \"active\"\n ? { state: \"running\" }\n : { state: \"installed\", detail: word === \"\" ? \"not running\" : word };\n }\n\n // `schtasks /query` prints a status column; \"Running\" is the only one that\n // means a process exists. \"Ready\" means it will start at the next logon,\n // which is a real state and not the same as serving.\n if (/\\bRunning\\b/.test(result.output)) return { state: \"running\" };\n return { state: \"installed\", detail: \"registered, not running\" };\n}\n\n/**\n * What the *installed* unit actually points at, and whether it still exists.\n *\n * Everything else in this file describes what an install would write, built\n * from the running process. Nothing read back what is on disk — and that gap\n * is where the walk's failure lived.\n *\n * `install` records absolute paths to the node runtime and the `byollm` script\n * that ran it. Under a version manager those paths belong to one node version:\n * install from a shell on node 22, upgrade node, and the unit still names a\n * binary that is no longer there. launchd cannot spawn it, the service is\n * \"installed and not running\", and every sentence anybody can reach — the\n * install's own success line, `byollm status`, the dashboard — is about a\n * device that is on rosters and answering nothing.\n *\n * Best-effort by design: it parses a file this program wrote, and a unit\n * somebody hand-edited is theirs. `null` means \"could not tell\", which is\n * printed as nothing rather than as a guess.\n */\nexport async function installedProgram(\n plan: ServicePlan,\n): Promise<{ path: string; exists: boolean } | null> {\n const raw = await readFile(plan.unitPath).catch(() => null);\n if (raw === null) return null;\n // Decoded the way it was written. The Windows task file is UTF-16LE, and\n // reading it as utf8 yields a string with a NUL between every character —\n // which matches nothing, so this returned `null` and said nothing rather\n // than saying something wrong. Quieter than a bug, and still a bug.\n const unit = raw.toString(plan.unitEncoding);\n\n /**\n * Per format, because \"the first absolute path\" is not a format.\n *\n * The first version looked for the first `/`-prefixed token, which reads a\n * plist and a systemd unit correctly and reads a Windows task file's\n * doctype URL instead of its `<Command>`. It passed on two runners and\n * failed on the third, claiming a program was missing on the one platform\n * where it had not looked at the program at all.\n */\n const found =\n plan.platform === \"darwin\"\n ? /<key>ProgramArguments<\\/key>\\s*<array>\\s*<string>([^<]+)<\\/string>/.exec(\n unit,\n )\n : plan.platform === \"linux\"\n ? /^ExecStart=(\\S+)/m.exec(unit)\n : /<Command>([^<]+)<\\/Command>/.exec(unit);\n\n const path = found?.[1]?.trim();\n if (path === undefined || path === \"\") return null;\n return {\n path,\n exists: await stat(path).then(\n () => true,\n () => false,\n ),\n };\n}\n\n/**\n * Wait for the daemon to be running, and to still be running a moment later.\n *\n * Two probes rather than one, and the second is the point. A job that crashes\n * on boot is alive for an instant, so a single probe fired immediately after\n * `bootstrap` can catch it mid-life and report success about a process that is\n * already on its way out. launchd then backs off — `ThrottleInterval` is ten\n * seconds — so the visible state a moment later is the honest one.\n *\n * Bounded, because \"not running yet\" and \"never going to run\" look identical\n * and only a clock tells them apart. On expiry it returns what it last saw,\n * which is a state with a detail in it rather than a timeout with none.\n */\nasync function confirmRunning(\n plan: ServicePlan,\n run: CommandRunner,\n wait: (ms: number) => Promise<void>,\n): Promise<ServiceState> {\n const STEP_MS = 500;\n const ATTEMPTS = 12;\n let seen: ServiceState = { state: \"installed\", detail: \"not started yet\" };\n\n for (let attempt = 0; attempt < ATTEMPTS; attempt++) {\n await wait(STEP_MS);\n seen = await serviceState(plan, run);\n if (seen.state !== \"running\") continue;\n // Alive once. Ask again after a beat: a boot crash is alive once too.\n await wait(STEP_MS);\n const again = await serviceState(plan, run);\n if (again.state === \"running\") return again;\n seen = again;\n }\n return seen;\n}\n\n/**\n * The refusal, named when we can name it.\n *\n * \"exit 1\" tells somebody nothing they can act on. Windows says \"Access is\n * denied\" for the two cases that actually happen — no elevation, or policy —\n * and saying which turns a dead end into a sentence with a next step in it.\n */\nfunction refusalOf(\n command: readonly string[],\n result: { code: number; output: string },\n): readonly string[] {\n const said = result.output.trim();\n const denied = /access is denied|requires elevation|0x80070005/i.test(said);\n /*\n * The interpretation **and** the words it was made from — B049 rider.\n *\n * This used to return the interpretation instead of the raw output when\n * the denied pattern matched. It cost a day: Kevin's field report came\n * back with our sentence and not schtasks's, and our sentence is the one\n * thing in the exchange that cannot distinguish \"this account may not\n * create tasks\" from \"this trigger type needs elevation\" from \"policy\".\n * We had written over the only evidence that separates the hypotheses.\n *\n * So the reading goes first, because it is what most people need, and the\n * raw line goes under it, because it is what anybody debugging needs and\n * it costs one line to keep.\n */\n if (!denied) {\n return [\n `${command.join(\" \")} — exit ${String(result.code)}` +\n (said === \"\" ? \"\" : `: ${said}`),\n ];\n }\n return [\n `${command.join(\" \")} — Windows would not register the scheduled task.`,\n ...(said === \"\" ? [] : [` it said: ${said}`]),\n ];\n}\n\nexport interface InstallResult {\n readonly ok: boolean;\n /** Sentences to print, in order. Empty on plain success is not allowed. */\n readonly lines: readonly string[];\n readonly plan: ServicePlan;\n}\n\nexport async function installService(\n target: ServiceTarget,\n run: CommandRunner,\n /**\n * How to wait between probes. Injected so the tests drive the real polling\n * without spending real seconds — a check that takes ten seconds is a check\n * somebody eventually stops running.\n */\n wait: (ms: number) => Promise<void> = (ms) =>\n new Promise((resolve) => setTimeout(resolve, ms)),\n /**\n * Has this device been revoked?\n *\n * Passed in rather than read here, because the health file is the CLI's to\n * know about and this module's job is the service. What it changes is the\n * *remedy*: a revoked daemon will not start no matter how many times the\n * install is retried, and telling somebody to retry is sending them to run\n * the same failure again.\n */\n revoked = false,\n): Promise<InstallResult> {\n const plan = servicePlan(target);\n\n const refusal = refuseToSupervise(target.scriptPath);\n if (refusal !== null) {\n return { ok: false, lines: [refusal], plan };\n }\n\n await mkdir(dirname(plan.unitPath), { recursive: true });\n await writeUnit(plan.unitPath, plan.unitContents, plan.unitEncoding);\n\n for (const [index, command] of plan.activate.entries()) {\n const result = await run(command);\n // The first step on macOS is a `bootout` that clears any previous copy,\n // and it fails whenever there was none. Only that one is allowed to.\n const mayFail = plan.platform === \"darwin\" && index === 0;\n if (result.code !== 0 && !mayFail) {\n /**\n * A machine that will not register a task still gets to start byollm.\n *\n * Registering a scheduled task is not always permitted: a managed\n * laptop can have it blocked by policy, a standard account can be\n * refused elevation. What that produced was an exit code and \"the\n * daemon is not supervised\" — on the machine most likely to be\n * somebody's work computer, which is the machine most likely to need\n * it, and the one where \"run it in a terminal forever\" is least\n * plausible.\n *\n * So there is a second way, and it is weaker in a way the person is\n * told about rather than left to discover after a crash nobody noticed.\n */\n const fallback = plan.fallback;\n if (fallback !== undefined) {\n // Bound to a local first: `plan.fallback!` inside the closures would\n // be an assertion that the narrowing above still holds several\n // statements later, which is the kind of claim this project makes the\n // compiler check rather than the author.\n const fell = await mkdir(dirname(fallback.unitPath), {\n recursive: true,\n })\n .then(() =>\n writeUnit(\n fallback.unitPath,\n fallback.unitContents,\n fallback.unitEncoding,\n ),\n )\n .then(\n () => true,\n () => false,\n );\n if (fell) {\n /**\n * Outcome, then the way out, then the mechanics — Kevin's finding,\n * quoted on the row: the \"Run as administrator\" line \"is there, but\n * its buried in the middle of the text and easy to miss, it should\n * be the primary thing it tells you since its going to be the\n * solution 99% of the time.\"\n *\n * He is the first person to meet this message cold, and he read it\n * the way it was ordered: the sentence that resolves his problem sat\n * below two other paragraphs, so it read as background.\n *\n * The upgrade line is written to be true either way we land on why\n * Windows refused. It does not say elevation is unusual and it does\n * not promise the elevated attempt succeeds — the one open question\n * (whether a logon trigger needs elevation regardless of how the\n * task is scoped) changes the reason and not the instruction.\n */\n return {\n ok: true,\n plan,\n lines: [\n `byollm will start when you log in, from ${fallback.supervisor}.`,\n \"\",\n ` To get the stronger setup, run \\`byollm start\\` once from a`,\n ` terminal opened with \"Run as administrator\". Or stay on this`,\n ` one — it works, with the limit below.`,\n \"\",\n ` ${fallback.caveat}`,\n \"\",\n ...refusalOf(command, result).map((line) => ` ${line}`),\n \"\",\n ` startup: ${fallback.unitPath}`,\n ` log: ${plan.logPath}`,\n ` check: byollm status`,\n ` remove: byollm stop`,\n ],\n };\n }\n }\n /**\n * \"I asked and was told no\" and \"there was nobody to ask\" — B184, and\n * it is B173's collapse in the other repository.\n *\n * Todd met this on the first real box: *\"systemd (user) refused the\n * task\"*, from a container with no `systemctl` at all. **127 is not a\n * refusal.** `spawnCommand`'s own docstring names these as the two\n * failure modes that matter and `service.test.ts` asserts 127 for the\n * container case — so the code has always distinguished them and only\n * the sentence collapsed them.\n *\n * The cost is not pedantry. \"Refused\" sends somebody looking for a\n * permission they do not have and a systemd that is not there.\n */\n const absent = result.code === 127;\n if (absent) {\n /* The unit is removed rather than left behind. We wrote it before\n asking, and on a machine with no service manager it is a file that\n will sit on the disk for the life of the box meaning nothing —\n Todd found exactly that at\n `~/.config/systemd/user/cloud.byollm.daemon.service`. A leftover\n that describes a service nobody can start is a thing the next\n reader has to disprove. */\n await rm(plan.unitPath, { force: true });\n return {\n ok: false,\n plan,\n lines: [\n `There is no service manager on this machine, so nothing can`,\n `start byollm at login.`,\n \"\",\n ...refusalOf(command, result).map((line) => ` ${line}`),\n \"\",\n `\\`byollm run\\` serves for as long as it is running, which is not`,\n `the same as a device that stays online — so this machine needs`,\n `something else to keep it up.`,\n \"\",\n ` removed ${plan.unitPath}`,\n ],\n };\n }\n\n return {\n ok: false,\n plan,\n lines: [\n /**\n * Nothing is starting byollm on this machine — the branch where\n * even the fallback could not be written.\n *\n * Ordered the same way as the fallback message above (B049,\n * Kevin's finding): what happened, then the way out on its own\n * line, then the evidence, then what works right now. The remedy\n * used to live inside refusalOf's denied sentence, which is where\n * it was buried; moving it up there must not drop it here, where\n * the person has less, not more.\n */\n `${plan.supervisor} refused the task, and the fallback could not be`,\n `written either. Nothing is starting byollm at login.`,\n \"\",\n /* Windows only — B184. This is Kevin's line, and it was printed\n unconditionally, so a Linux box was told to open a terminal \"as\n administrator\": no other terminal, no administrator, and no\n elevation on a console that runs four commands. A remedy that\n does not exist on the machine is worse than none, because it\n ends the search. */\n ...(plan.platform === \"win32\"\n ? [\n ` To get past this, run \\`byollm start\\` once from a terminal`,\n ` opened with \"Run as administrator\".`,\n \"\",\n ]\n : []),\n ...refusalOf(command, result).map((line) => ` ${line}`),\n \"\",\n `\\`byollm run\\` still works in a terminal, and is the way to keep`,\n `serving until this is sorted.`,\n \"\",\n ` wrote ${plan.unitPath}`,\n ],\n };\n }\n }\n\n /**\n * Did it actually start? — ruled 2026-09-03, from the walk.\n *\n * This returned success the moment the supervisor's activate commands\n * exited zero, and printed \"Installed. launchd will keep byollm running\"\n * plus the TEST pointer. Todd's transcript has that text sitting directly\n * above `byollm status` reporting \"installed but NOT running (last exit 2)\n * — this device is on rosters and serving nothing\".\n *\n * Both were true, which is what locates the bug: the success predicate was\n * asking the wrong question. `launchctl bootstrap` exiting zero means the\n * job was *loaded*, never that the daemon is *alive* — found is not works,\n * one layer down, on the same command that already knows the difference.\n * `serviceState` is right here in this file, it reads for running rather\n * than known, and `status` used it to catch what install had just claimed.\n *\n * So install asks it too, and waits: a job that will crash on boot has to\n * be given long enough to do it, or the probe just races the failure and\n * reports the wrong answer more slowly.\n */\n const started = await confirmRunning(plan, run, wait);\n if (started.state !== \"running\") {\n return {\n ok: false,\n plan,\n lines: revoked\n ? [\n /**\n * The cause, not the symptom — ruled 2026-09-03 (2).\n *\n * Todd revoked a machine and `install` told him \"retry: byollm\n * install\". Honest about the failure and wrong about the fix: the\n * daemon starts, is refused, and stops, and it will do that every\n * time. A remedy must match the cause.\n */\n `${plan.supervisor} accepted the service, and the daemon is not running.`,\n \"\",\n ` ${REVOKED_SENTENCE}.`,\n \"\",\n ` re-pair: byollm connect`,\n ` ${REVOKED_RETURN}.`,\n \"\",\n `Starting it again will not change this — the refusal is the hub's,`,\n `and it stands until this device is approved again.`,\n ]\n : [\n `${plan.supervisor} accepted the service, and the daemon is not running.`,\n \"\",\n ` ${started.state === \"absent\" ? \"the supervisor no longer knows it\" : started.detail}`,\n \"\",\n // The log first, because it is the only place the run's own words\n // are, and every other line here is a guess without it.\n ...(await (async () => {\n /* Named when we can name it. \"last exit 2\" is a number somebody has\n to interpret; \"the node it was installed with is gone\" is the\n whole answer, and it is the failure a version manager produces on\n an ordinary upgrade. */\n const program = await installedProgram(plan);\n return program === null || program.exists\n ? []\n : [\n ` the program this service runs no longer exists:`,\n ` ${program.path}`,\n ` that happens when the node it was installed with was`,\n ` removed or upgraded. Running \\`byollm start\\` again records`,\n ` the current one.`,\n \"\",\n ];\n })()),\n ` log: ${plan.logPath}`,\n ` service: ${plan.unitPath}`,\n ` retry: byollm start`,\n ` instead: byollm run runs in this terminal, and prints why`,\n \"\",\n `Nothing is serving until this is sorted — this device will appear on`,\n `rosters and answer nothing.`,\n ],\n };\n }\n\n const lines = [\n /* \"Installed.\" was the rename half-done — CW's rider on B055. `byollm\n start` reporting a word that is no longer any command's name leaves\n the reader holding the old vocabulary at the one moment they are being\n taught the new one. What happened is that it started, and that it will\n keep starting. */\n `Started. ${plan.supervisor} will keep byollm running and restart it if it stops.`,\n \"\",\n ` service: ${plan.unitPath}`,\n ` log: ${plan.logPath}`,\n ` check: byollm status`,\n ` remove: byollm stop`,\n ];\n\n if (plan.platform === \"linux\") {\n // The one thing install does not do for somebody: lingering changes\n // behaviour outside their session, and choosing that for them is not this\n // command's business.\n lines.push(\n \"\",\n \"To keep serving after you log out:\",\n \"\",\n \" sudo loginctl enable-linger $USER\",\n );\n }\n\n return { ok: true, plan, lines };\n}\n\nexport async function uninstallService(\n target: ServiceTarget,\n run: CommandRunner,\n): Promise<InstallResult> {\n const plan = servicePlan(target);\n const failures: string[] = [];\n\n for (const command of plan.deactivate) {\n const result = await run(command).catch(() => ({ code: 127, output: \"\" }));\n // Every deactivation step is allowed to fail: \"it was not installed\" and\n // \"it is now not installed\" are the same end state, and an uninstall that\n // errors on a machine with nothing to remove teaches people to ignore it.\n if (result.code !== 0 && result.output.trim() !== \"\") {\n failures.push(` ${command.join(\" \")} — ${result.output.trim()}`);\n }\n }\n\n await rm(plan.unitPath, { force: true });\n\n /**\n * And the fallback, which on Windows is the one that was actually there.\n *\n * This removed `plan.unitPath` only. Every Windows install had fallen to\n * the Startup folder — see `writeUnit` for why — so uninstall deleted a\n * task XML that had never registered, printed \"Removed\", and left the\n * thing that starts the daemon exactly where it was. Next logon it came\n * back.\n *\n * A daemon that restarts after its owner removed it is not a bug about\n * supervisors. It is somebody's machine doing work they told it to stop\n * doing, and being told it had stopped.\n *\n * Removed unconditionally rather than only when the fallback was used:\n * uninstall's whole contract is that \"it was not installed\" and \"it is now\n * not installed\" end the same way, and a machine that has been through\n * several versions may carry both.\n */\n const fallbackPath = plan.fallback?.unitPath;\n if (fallbackPath !== undefined) {\n await rm(fallbackPath, { force: true });\n }\n\n return {\n ok: true,\n plan,\n lines: [\n `Removed. ${plan.supervisor} is no longer running byollm.`,\n `Your pairings, allowlist and logs are untouched in ~/.byollm.`,\n \"\",\n /**\n * Because the first person to meet the old verb read it the way it was\n * written — B049 item 3.\n *\n * Kevin ran `byollm uninstall` to get byollm off his machine. It\n * unscheduled the daemon and said \"Removed\", which was true about the\n * supervisor and not about the thing he asked for. Todd, on the rename:\n * \"that is literally why I wanted the terms removed since it wasn't\n * uninstalling before — just unscheduling the background job.\"\n *\n * The rename fixes the word. It does not answer the question he\n * actually had, and the answer is one line, so it goes here rather\n * than in a doc he would have to know to look for. It lives in `stop`\n * and outlives the shim, which dies at 0.1.0 with the other aliases.\n */\n `To remove byollm from this machine entirely: \\`npm uninstall -g byollm\\``,\n ...(failures.length === 0\n ? []\n : [\n \"\",\n \"The supervisor had something to say (usually harmless):\",\n ...failures,\n ]),\n ],\n };\n}\n\n/**\n * Run a command and collect everything it said.\n *\n * Exported because the alternative — a private closure inside the CLI — is a\n * seam nothing can exercise, and this one has the two failure modes that\n * matter: a binary that is not there (a container with no `systemctl`) and a\n * command that exits non-zero with its reason on stderr. Both are the\n * difference between \"not installed\" and a crash.\n */\nexport const spawnCommand: CommandRunner = async (command) => {\n const { spawn } = await import(\"node:child_process\");\n const [file, ...args] = command;\n return new Promise((resolve) => {\n // No shell, ever. Every argument is passed through as itself, so nothing\n // here can be re-interpreted by whichever `/bin/sh` a platform ships —\n // and there is nothing left to quote.\n const child = spawn(file ?? \"\", args);\n let output = \"\";\n child.stdout.on(\"data\", (chunk: Buffer) => {\n output += chunk.toString();\n });\n child.stderr.on(\"data\", (chunk: Buffer) => {\n output += chunk.toString();\n });\n child.on(\"error\", () => {\n resolve({ code: 127, output });\n });\n child.on(\"close\", (code) => {\n resolve({ code: code ?? 0, output });\n });\n });\n};\n\n/**\n * Write a unit file as the thing it says it is.\n *\n * This was `writeFile(path, contents, \"utf8\")` for every platform, and the\n * Windows task XML declares `encoding=\"UTF-16\"` on its own first line. MSXML\n * refused the mismatch, so every `schtasks /create /xml` failed — on every\n * Windows machine, with or without administrator rights — and fell to the\n * Startup folder, which cannot restart a crashed daemon. Restart-on-failure\n * has never shipped to a Windows user.\n *\n * The BOM is not decoration. `schtasks` identifies the encoding from it;\n * UTF-16LE bytes without one are read as something else and refused just as\n * firmly as the mismatch was. Node writes the code units and no mark, so it\n * is prepended here.\n *\n * **Not verified on Windows from this machine.** The mismatch is provable\n * from the source and the fix is the shape Task Scheduler's own export uses,\n * but the thing that would settle it is a real `schtasks /create` — which is\n * Kevin, and which is why the test below asserts the bytes rather than the\n * outcome.\n */\nasync function writeUnit(\n path: string,\n contents: string,\n encoding: \"utf8\" | \"utf16le\",\n): Promise<void> {\n await writeFile(\n path,\n encoding === \"utf16le\" ? `\\uFEFF${contents}` : contents,\n encoding,\n );\n}\n","import { stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/**\n * Running the daemon under the machine's own supervisor — cloud_002.\n *\n * Found the first time somebody paired for real: `byollm connect` ends in\n * `Now running jobs… Ctrl-C to stop`, and that was the only way to run one.\n * Todd's words were *\"people shouldn't have to keep terminals open\"*, and the\n * cost is not inconvenience. A machine that stops serving when a window\n * closes stops earning its place on a roster **silently** — the owner finds\n * out when a teammate's job does not run, on a different machine, later.\n *\n * ## A plan, not a side effect\n *\n * Everything platform-specific is decided here, in a function that writes\n * nothing: given a platform and a resolved executable, it returns the unit\n * file's path and contents and the commands that activate it. That is what\n * makes three operating systems testable on one — the macOS plist and the\n * systemd unit are asserted on Linux CI, exactly, including the arguments\n * they run. A wrong plist is otherwise a thing you discover by rebooting.\n *\n * ## User-level, always\n *\n * A LaunchAgent rather than a LaunchDaemon; `systemd --user` rather than a\n * system unit; a logon task rather than a Windows service. All three mean:\n * no root to install, runs as the person whose models these are, and dies\n * with their account. The daemon's whole premise is that it runs on somebody's\n * own machine under their own control — installing it as root would make the\n * uninstall a sudo operation and the process a thing the owner cannot inspect.\n * `~/.byollm` is theirs; so is this.\n *\n * ## What it runs\n *\n * `byollm run`, with no URL: every pairing this machine has. Not `connect`,\n * which is interactive and prints a code for somebody to type. The pairing\n * file already survives a restart, so supervision is the only missing piece.\n */\n\nexport type ServicePlatform = \"darwin\" | \"linux\" | \"win32\";\n\n/** The reverse-DNS name all three platforms key the service by. */\nexport const SERVICE_LABEL = \"cloud.byollm.daemon\";\n\n/**\n * Which of the three supervisors this platform has.\n *\n * Everything that is not macOS or Windows is treated as systemd. That is a\n * simplification with a name: a BSD or a musl box with OpenRC gets a unit file\n * its init system will not read, and `install` will report the failure that\n * follows rather than pretend. Guessing wrong loudly beats refusing to run on\n * a platform somebody actually has.\n */\nexport function servicePlatform(platform: string): ServicePlatform {\n if (platform === \"win32\") return \"win32\";\n if (platform === \"darwin\") return \"darwin\";\n return \"linux\";\n}\n\nexport interface ServicePlan {\n readonly platform: ServicePlatform;\n /** The file that defines the service — plist, unit, or task XML. */\n readonly unitPath: string;\n readonly unitContents: string;\n /**\n * How the unit file must be written — 2026-09-02.\n *\n * Every platform got `utf8`, hardcoded at the write. The Windows task XML\n * declares `encoding=\"UTF-16\"` in its own first line, so MSXML refused the\n * mismatch and **every** Windows registration failed — admin or not, since\n * alpha.44. Each one fell to the Startup folder, which cannot restart a\n * crashed daemon, so restart-on-failure has never once shipped to a Windows\n * user.\n *\n * A file that says what it is and is written as something else is a bug the\n * file itself describes; the encoding belongs beside the contents rather\n * than at the call that happens to write them.\n */\n readonly unitEncoding: \"utf8\" | \"utf16le\";\n /** Run these, in order, to make it live. */\n readonly activate: readonly (readonly string[])[];\n /** Run these, in order, to take it away. Tolerant of \"already gone\". */\n readonly deactivate: readonly (readonly string[])[];\n /** Asks the platform whether it is running right now. */\n readonly query: readonly string[];\n /** Where the supervisor sends the daemon's output. */\n readonly logPath: string;\n /** In words, for the person at the terminal. */\n readonly supervisor: string;\n /**\n * A weaker way to start at logon, for a machine that refuses the first one.\n *\n * Windows only, and it exists because registering a scheduled task is not\n * always allowed: a managed laptop can have task creation blocked by policy\n * and a standard account can be refused elevation. The result was a person\n * being handed an exit code and told the daemon was unsupervised, which on\n * the machine most likely to be somebody's work computer is the machine\n * most likely to need it.\n *\n * The Startup folder always works, needs nobody's permission, and is\n * genuinely worse: it starts byollm at logon and does not restart it if it\n * stops. That difference is stated where it is used rather than hidden\n * behind the word \"installed\" — a supervisor that does not supervise must\n * not be reported as one.\n */\n readonly fallback?: {\n readonly unitPath: string;\n readonly unitContents: string;\n readonly unitEncoding: \"utf8\" | \"utf16le\";\n readonly supervisor: string;\n /** What it does not do, in words, said at install time. */\n readonly caveat: string;\n };\n}\n\nexport interface ServiceTarget {\n readonly platform: ServicePlatform;\n /** The node binary, absolute. */\n readonly execPath: string;\n /** The CLI's entry script, absolute. */\n readonly scriptPath: string;\n readonly home?: string;\n /** `~/.byollm`, so the service and the CLI agree about state. */\n readonly root?: string;\n /**\n * This user's numeric id, for launchd's domain target.\n *\n * Resolved in Node rather than left as `$UID` for a shell to expand. The\n * first version did the latter and it worked on macOS purely because\n * `/bin/sh` there is bash in disguise and sets `UID`; CI's `dash` does not,\n * so the target became the literal `gui/` — a command that would have\n * failed on a real machine in a way that reads like a permissions problem.\n * A plan whose meaning depends on which shell happens to run it is not a\n * plan, and no command here needs a shell now.\n */\n readonly uid?: number;\n /**\n * Who Windows should register the task for — B036.\n *\n * `DOMAIN\\\\user`, or a bare username. Only Windows reads it, and reading\n * it is what makes supervision work without administrator rights: see the\n * `Principal` in the task XML.\n */\n readonly user?: string;\n /**\n * Windows' per-user application data root, for the Startup fallback.\n *\n * Injected rather than read from `process.env` where it is used — found by\n * CI on 2026-09-04, and it was not a test problem. Every `installService`\n * test that exercised the Windows fallback wrote a real `byollm.cmd` into\n * the **ambient** Startup folder: on the Windows runner, into\n * `C:\\Users\\runneradmin\\...\\Startup`, and on any Windows machine that\n * ran the suite, into that person's. A unit test that installs a startup\n * entry on whoever runs it is the same defect `ServiceIo` exists to\n * prevent — the suite already refuses to shell out to a real `launchctl`,\n * and this was the one path that reached the host anyway.\n *\n * `home`-relative would be wrong for the product: APPDATA is where Windows\n * actually keeps this and a roaming profile moves it. So it stays ambient\n * by default and becomes part of the target, which is the thing tests\n * already build.\n */\n readonly appData?: string;\n}\n\n/** XML-escape a path — a home directory can contain `&` and an apostrophe. */\n/**\n * The `PATH` the installed service runs with — the launchd gap, closed.\n *\n * launchd hands an agent `/usr/bin:/bin:/usr/sbin:/sbin` and nothing else, and\n * that is not where anybody's CLI lives. `claude` installs to `~/.local/bin`;\n * npm globals sit under a Node version directory; Homebrew is `/opt/homebrew`.\n * None of them are on that list.\n *\n * What that cost, before this: the daemon under launchd could not find\n * `claude`, so the health probe failed, so the service was never advertised —\n * and the device's page showed only the model server it *could* reach. Nothing\n * logged an error, because \"not installed\" is a legal answer to a health\n * probe and the daemon has no way to tell it apart from \"installed somewhere I\n * cannot see\".\n *\n * Worse, `byollm services` said the opposite. It runs in the user's shell,\n * with the user's `PATH`, so it found the CLI and reported \"healthy and will\n * be advertised\" — a promise about a program the daemon could not execute. A\n * diagnostic that reads a different environment than the thing it diagnoses is\n * worse than no diagnostic, because it is believed.\n *\n * So the installer captures the `PATH` of the shell that ran `byollm start`.\n * That is the environment the person set up on purpose, and it is the only one\n * available at the moment the service is defined. It is a snapshot: a CLI\n * installed to a new directory afterwards needs `byollm start` again, which\n * `byollm services` now says out loud when it notices the difference.\n */\nfunction servicePath(): string {\n const current = process.env[\"PATH\"] ?? \"\";\n // The launchd default stays on the end rather than being replaced, so a\n // service still finds system binaries if the captured PATH is odd.\n const fallback = \"/usr/bin:/bin:/usr/sbin:/sbin\";\n const seen = new Set<string>();\n const out: string[] = [];\n for (const dir of [...current.split(\":\"), ...fallback.split(\":\")]) {\n if (dir === \"\" || seen.has(dir)) continue;\n seen.add(dir);\n out.push(dir);\n }\n return out.join(\":\");\n}\n\nfunction xml(value: string): string {\n return value\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\")\n .replaceAll(\"'\", \"'\");\n}\n\nexport function servicePlan(target: ServiceTarget): ServicePlan {\n const home = target.home ?? homedir();\n const root = target.root ?? join(home, \".byollm\");\n const logPath = join(root, \"service.log\");\n const { execPath, scriptPath } = target;\n\n if (target.platform === \"darwin\") {\n // `KeepAlive` unconditionally true, not `SuccessfulExit: false`: a daemon\n // that exits cleanly because a hub was unreachable at boot has still\n // stopped serving, and \"it exited zero\" is not a reason to leave a\n // machine off its roster.\n const unitPath = join(\n home,\n \"Library\",\n \"LaunchAgents\",\n `${SERVICE_LABEL}.plist`,\n );\n const unitContents = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key><string>${SERVICE_LABEL}</string>\n <key>ProgramArguments</key>\n <array>\n <string>${xml(execPath)}</string>\n <string>${xml(scriptPath)}</string>\n <string>run</string>\n </array>\n <key>EnvironmentVariables</key>\n <dict>\n <key>PATH</key><string>${xml(servicePath())}</string>\n </dict>\n <key>RunAtLoad</key><true/>\n <key>KeepAlive</key><true/>\n <key>ThrottleInterval</key><integer>10</integer>\n <key>StandardOutPath</key><string>${xml(logPath)}</string>\n <key>StandardErrorPath</key><string>${xml(logPath)}</string>\n <key>ProcessType</key><string>Background</string>\n</dict>\n</plist>\n`;\n // `bootstrap gui/<uid>` rather than the deprecated `load`: it reports a\n // real error when the plist is malformed instead of failing quietly,\n // which is the difference between finding a typo now and finding it at\n // the next reboot.\n const domain = `gui/${String(target.uid ?? 0)}`;\n return {\n platform: \"darwin\",\n unitPath,\n unitContents,\n unitEncoding: \"utf8\",\n activate: [\n [\"launchctl\", \"bootout\", domain, unitPath],\n [\"launchctl\", \"bootstrap\", domain, unitPath],\n [\"launchctl\", \"enable\", `${domain}/${SERVICE_LABEL}`],\n ],\n deactivate: [[\"launchctl\", \"bootout\", domain, unitPath]],\n query: [\"launchctl\", \"print\", `${domain}/${SERVICE_LABEL}`],\n logPath,\n supervisor: \"launchd\",\n };\n }\n\n if (target.platform === \"linux\") {\n const unitPath = join(\n home,\n \".config\",\n \"systemd\",\n \"user\",\n `${SERVICE_LABEL}.service`,\n );\n // No `WantedBy=default.target` alone: `systemctl --user enable` writes\n // that link, and `linger` is what makes it survive logout. Whether to\n // enable lingering is the owner's call — it is the one line here that\n // changes something outside their session — so `install` prints it\n // rather than running it.\n const unitContents = `[Unit]\nDescription=byollm — run an app's LLM jobs on your own models\nDocumentation=https://byo-llm.com/docs\nAfter=network-online.target\n\n[Service]\nType=simple\n# The same gap launchd has, for the same reason: a user unit does not inherit\n# the login shell's PATH, so a CLI in ~/.local/bin is invisible to the daemon\n# while being perfectly visible to the person debugging it. See servicePath.\nEnvironment=PATH=${servicePath()}\nExecStart=${execPath} ${scriptPath} run\nRestart=always\nRestartSec=10\n# Output goes to the journal *and* to the same file the other platforms use,\n# so \"where are the logs\" has one answer in the docs.\nStandardOutput=append:${logPath}\nStandardError=append:${logPath}\n\n[Install]\nWantedBy=default.target\n`;\n return {\n platform: \"linux\",\n unitPath,\n unitContents,\n unitEncoding: \"utf8\",\n activate: [\n [\"systemctl\", \"--user\", \"daemon-reload\"],\n [\"systemctl\", \"--user\", \"enable\", \"--now\", `${SERVICE_LABEL}.service`],\n ],\n deactivate: [\n [\"systemctl\", \"--user\", \"disable\", \"--now\", `${SERVICE_LABEL}.service`],\n [\"systemctl\", \"--user\", \"daemon-reload\"],\n ],\n query: [\"systemctl\", \"--user\", \"is-active\", `${SERVICE_LABEL}.service`],\n logPath,\n supervisor: \"systemd (user)\",\n };\n }\n\n /**\n * Whose task this is — B036, and the whole of why `install` needed admin.\n *\n * The XML had no `Principal` and a `LogonTrigger` with no `UserId`. That is\n * not a per-user task: a task that fires when *anybody* logs on is a\n * machine-wide one, and registering it needs administrator rights. So\n * `schtasks /create` answered \"Access is denied\" on a standard account,\n * which is most accounts — and the ruling on Kevin's report was that\n * **supervision must not require admin.**\n *\n * Naming the user, with `InteractiveToken` and `LeastPrivilege`, is what\n * makes it the per-user task it was always meant to be. Nothing here wants\n * elevation: it runs one program, as the person, when that person logs in.\n *\n * Absent on a machine that cannot tell us, which leaves the old shape\n * rather than inventing an identity — and the fallback still catches it.\n */\n const who = target.user;\n\n // Windows has no user-level service, so this is a scheduled task at logon.\n // Registered from XML rather than `schtasks /create /sc onlogon`, because\n // the flag form cannot express restart-on-failure — and a task that starts\n // once at logon and never again after a crash is precisely the silent\n // failure this command exists to prevent.\n const unitPath = join(root, \"byollm-task.xml\");\n const unitContents = `<?xml version=\"1.0\" encoding=\"UTF-16\"?>\n<Task version=\"1.4\" xmlns=\"http://schemas.microsoft.com/windows/2004/02/mit/task\">\n <RegistrationInfo>\n <Description>byollm — run an app's LLM jobs on your own models</Description>\n </RegistrationInfo>\n <Triggers>\n <LogonTrigger>\n <Enabled>true</Enabled>${\n who === undefined ? \"\" : `\\n <UserId>${xml(who)}</UserId>`\n }\n </LogonTrigger>\n </Triggers>\n <Settings>\n <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>\n <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>\n <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>\n <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>\n <RestartOnFailure>\n <Interval>PT1M</Interval>\n <Count>999</Count>\n </RestartOnFailure>\n </Settings>\n <Principals>\n <Principal id=\"byollm\">${\n who === undefined ? \"\" : `\\n <UserId>${xml(who)}</UserId>`\n }\n <LogonType>InteractiveToken</LogonType>\n <RunLevel>LeastPrivilege</RunLevel>\n </Principal>\n </Principals>\n <Actions Context=\"byollm\">\n <Exec>\n <Command>${xml(execPath)}</Command>\n <Arguments>\"${xml(scriptPath)}\" run</Arguments>\n </Exec>\n </Actions>\n</Task>\n`;\n return {\n platform: \"win32\",\n unitPath,\n unitContents,\n unitEncoding: \"utf16le\",\n activate: [\n [\"schtasks\", \"/create\", \"/tn\", SERVICE_LABEL, \"/xml\", unitPath, \"/f\"],\n [\"schtasks\", \"/run\", \"/tn\", SERVICE_LABEL],\n ],\n deactivate: [[\"schtasks\", \"/delete\", \"/tn\", SERVICE_LABEL, \"/f\"]],\n query: [\"schtasks\", \"/query\", \"/tn\", SERVICE_LABEL],\n logPath,\n supervisor: \"Task Scheduler\",\n /**\n * The Startup folder, for a machine that will not register a task.\n *\n * A `.cmd` here runs at logon for this user, needs no elevation and no\n * policy exemption, and cannot be refused. What it does not do is restart\n * byollm if it stops — which is the whole reason the task XML exists — so\n * that is said out loud rather than left for somebody to discover when a\n * crash goes unnoticed.\n *\n * `start \"\"` so the shim exits immediately and the logon does not wait on\n * a long-running process; the empty title is required because `start`\n * reads a first quoted argument as the window title.\n */\n fallback: {\n // A batch file, not XML. It declares no encoding and `cmd` reads it as\n // bytes, so utf8 is right here — the mismatch above never applied to it,\n // which is part of why every Windows machine quietly ended up on it.\n unitEncoding: \"utf8\",\n unitPath: join(\n target.appData ?? process.env[\"APPDATA\"] ?? root,\n \"Microsoft\",\n \"Windows\",\n \"Start Menu\",\n \"Programs\",\n \"Startup\",\n \"byollm.cmd\",\n ),\n unitContents:\n `@echo off\\r\\n` +\n `rem byollm — run an app's LLM jobs on your own models\\r\\n` +\n `start \"\" /b \"${execPath}\" \"${scriptPath}\" run >> \"${logPath}\" 2>&1\\r\\n`,\n supervisor: \"the Startup folder\",\n caveat:\n \"It starts byollm when you log in. It does not restart it if it \" +\n \"stops — Task Scheduler would have, and this machine would not \" +\n \"register the task.\",\n },\n };\n}\n\n/**\n * Why this executable cannot be supervised, or null if it can.\n *\n * `npx` runs from a cache directory that npm deletes without warning. A\n * service pointing into one works today and fails at some later boot with\n * \"no such file\" in a log nobody is reading — the same invisible-stop this\n * command exists to prevent, reintroduced by the install itself. Refusing is\n * the only honest answer, and the fix is one line.\n */\n/**\n * Is a supervised service defined on this machine?\n *\n * Used by `byollm services` to decide whether to warn that its answer is the\n * shell's view rather than the daemon's. Deliberately a file check and not a\n * `launchctl print`: this runs on a command somebody is reading output from,\n * and shelling out to ask a question whose answer only changes the *wording*\n * of a warning is a cost with no matching benefit.\n */\nexport async function serviceIsInstalled(\n target: ServiceTarget,\n): Promise<boolean> {\n const plan = servicePlan(target);\n try {\n await stat(plan.unitPath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function refuseToSupervise(scriptPath: string): string | null {\n const ephemeral =\n /[/\\\\]_npx[/\\\\]|[/\\\\]\\.npm[/\\\\]_cacache[/\\\\]|[/\\\\]npm-cache[/\\\\]_npx[/\\\\]/;\n if (ephemeral.test(scriptPath)) {\n return (\n `this copy of byollm lives in npx's cache (${scriptPath}), which npm ` +\n `deletes without warning — a service pointing at it would stop working ` +\n `at some later boot, silently.\\n\\n` +\n ` Install it properly first: npm install -g byollm@latest\\n` +\n ` Then: byollm start`\n );\n }\n return null;\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { z } from \"zod\";\nimport type { ServiceReport } from \"./service-line.js\";\n\n/**\n * The last thing the probe learned, on disk, for the process that did not run it.\n *\n * `byollm status` is a separate process from the daemon. It cannot run the\n * canary itself — that is a real model call, and on a metered backend it is\n * real money, on a command people run several times a minute while something\n * is wrong. So the daemon writes what it found and `status` reads it.\n *\n * **Latest only, never a history.** This answers \"where does this stand now\".\n * A log of every probe would be a record of the day somebody's subscription\n * lapsed and the days it stayed lapsed, which is nobody's business, ours\n * included. Same reason the hub will hold only the latest when this reaches\n * Your Devices.\n *\n * A missing or unreadable file is not an error and not a state: it is\n * \"nothing has probed yet\", and `status` renders health as it always did.\n * Absent is not signed-out — the same distinction the tri-state is built on.\n */\n\nconst Stored = z.record(\n z.string(),\n z.object({\n state: z.discriminatedUnion(\"kind\", [\n z.object({ kind: z.literal(\"answers\"), model: z.string() }),\n z.object({\n kind: z.literal(\"signed-out\"),\n detail: z.string().optional(),\n }),\n z.object({ kind: z.literal(\"missing\") }),\n /* B098. Stopped and unstartable is not the same as absent, and the\n file `byollm status` reads has to be able to tell them apart. */\n z.object({ kind: z.literal(\"unstartable\"), model: z.string() }),\n /**\n * Installed here, not running, and startable — B056.\n *\n * Its own kind rather than `missing` or `unknown`, because the daemon\n * ADVERTISES this one. A service that is advertised while `status`\n * calls it \"not found on this device\" is a machine disagreeing with\n * itself on two screens, which is the defect this codebase keeps\n * finding under other names.\n */\n z.object({\n kind: z.literal(\"stopped\"),\n model: z.string(),\n /* B087. Absent means no — see the field's note in service-line.ts. */\n starts: z.boolean().optional(),\n }),\n z.object({\n kind: z.literal(\"blocked\"),\n detail: z.string().optional(),\n until: z.number().int().positive().optional(),\n }),\n z.object({ kind: z.literal(\"unknown\"), model: z.string() }),\n ]),\n signIn: z.string().optional(),\n }),\n);\n\nexport async function writeServiceStates(\n path: string,\n states: ReadonlyMap<string, ServiceReport>,\n): Promise<void> {\n const out: Record<string, ServiceReport> = {};\n for (const [service, report] of states) out[service] = report;\n try {\n await writeFile(path, `${JSON.stringify(out, null, 2)}\\n`, \"utf8\");\n } catch {\n // A probe that cannot write its notes has still probed. Refusing to run\n // because a status file is unwritable would trade the thing that matters\n // for the thing that reports on it.\n }\n}\n\nexport async function readServiceStates(\n path: string,\n): Promise<Map<string, ServiceReport>> {\n try {\n const parsed = Stored.safeParse(\n JSON.parse(await readFile(path, \"utf8\")) as unknown,\n );\n if (!parsed.success) return new Map();\n return new Map(Object.entries(parsed.data));\n } catch {\n return new Map();\n }\n}\n"],"mappings":"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","names":["PROTOCOL_VERSION","run","readFile","platform","until","platform","access","join","delimiter","context","readFile","readFile","readFile","readFile","spawn","report","platform","report","signIn","bytes","backendDescriptor","execFile","join","spawn","spawn","platform","join","platform","execFile","execFile","FIXED_ARGV","execFile","backendDescriptor","mkdir","readFile","rm","writeFile","dirname","createInterface","readFile","writeFile","mkdir","dirname","readFile","writeFile","mkdir","dirname","classifyCost","readFile","join","readJson","readFile","platform","z","readFileSync","mkdir","readFile","rename","rm","dirname","z","dollars","platform","classifyCost","row","proof","named","readFile","run","platform","open","backendDescriptor","backendName","classifyCost","fingerprint","z","z","execFile","promisify","backendDescriptor","backendName","promisify","execFile","backendDescriptor","backendName","sleep","mkdir","readFile","writeFile","dirname","z","mkdir","dirname","readFile","writeFile","resolveCost","resolveCost","randomUUID","mkdir","readFile","rm","writeFile","dirname","isNotFound","Buffer","open","verifyPublicIdentity","Buffer","signRequest","z","signRequest","signRequest","connect","randomUUID","mkdir","readFile","rename","rm","writeFile","dirname","PublicIdentity","z","z","PublicIdentity","readFile","record","origin","mkdir","dirname","randomUUID","writeFile","rename","rm","z","z","homedir","join","keyId","open","publicIdentityOf","seal","fingerprint","verifyPublicIdentity","CLOCK_SKEW_WARN_MS","GRANT_MAX_AGE_MS","payload","verifyPublicIdentity","keyId","until","CLOCK_SKEW_WARN_MS","GRANT_MAX_AGE_MS","fingerprint","context","report","seal","publicIdentityOf","open","sleep","mkdir","readFile","rm","stat","writeFile","dirname","stat","homedir","join","platform","unitPath","unitContents","run","readFile","stat","mkdir","dirname","rm","spawn","writeFile","readFile","writeFile","z","report","createInterface","mkdir","dirname","writeFile","named","readFile","pairings","fingerprint","rm","service","untrusted","firstLine","raw","parsed","backendDescriptor","classifyCost","backendName","dollars","PROTOCOL_VERSION"]}
|