@braedonsaunders/appkit-desk 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +126 -1
  2. package/backend.d.ts +32 -7
  3. package/backend.d.ts.map +1 -1
  4. package/backend.js +107 -524
  5. package/backend.js.map +1 -1
  6. package/host.d.ts +27 -1
  7. package/host.d.ts.map +1 -1
  8. package/host.js +47 -19
  9. package/host.js.map +1 -1
  10. package/index.d.ts +2 -0
  11. package/index.d.ts.map +1 -1
  12. package/index.js +2 -0
  13. package/index.js.map +1 -1
  14. package/machine.d.ts +154 -0
  15. package/machine.d.ts.map +1 -0
  16. package/machine.js +573 -0
  17. package/machine.js.map +1 -0
  18. package/package.json +2 -2
  19. package/plan.d.ts +192 -31
  20. package/plan.d.ts.map +1 -1
  21. package/plan.js +298 -12
  22. package/plan.js.map +1 -1
  23. package/port-relay.d.ts +13 -0
  24. package/port-relay.d.ts.map +1 -0
  25. package/port-relay.js +56 -0
  26. package/port-relay.js.map +1 -0
  27. package/virtualization.d.ts +16 -0
  28. package/virtualization.d.ts.map +1 -0
  29. package/virtualization.js +156 -0
  30. package/virtualization.js.map +1 -0
  31. package/vz/Package.swift +23 -0
  32. package/vz/Sources/DeskNet/Addresses.swift +106 -0
  33. package/vz/Sources/DeskNet/DHCP.swift +111 -0
  34. package/vz/Sources/DeskNet/Fence.swift +57 -0
  35. package/vz/Sources/DeskNet/NetworkStack.swift +547 -0
  36. package/vz/Sources/DeskNet/Packets.swift +355 -0
  37. package/vz/Sources/DeskNet/Plumbing.swift +312 -0
  38. package/vz/Sources/DeskNet/Sockets.swift +221 -0
  39. package/vz/Sources/DeskNet/TCPFlow.swift +448 -0
  40. package/vz/Sources/appkit-desk-vz/Control.swift +152 -0
  41. package/vz/Sources/appkit-desk-vz/Helper.swift +550 -0
  42. package/vz/Sources/appkit-desk-vz/main.swift +38 -0
  43. package/vz/Tests/DeskNetTests/DeskNetTests.swift +426 -0
  44. package/vz/appkit-desk-vz.entitlements +8 -0
  45. package/vz/build.sh +23 -0
  46. package/vz/check/extract-boot.py +221 -0
package/backend.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The backend port and its default Cloud Hypervisor implementation.\n *\n * `DeskBackend` is the seam that keeps the package testable without a\n * hypervisor: the host asks a backend to boot a `DeskLaunchPlan` and gets a\n * `DeskMachine` — a request/response channel to the in-guest agent plus an\n * event subscription. CI and non-KVM developer machines inject an in-memory\n * fake; production uses `cloudHypervisorBackend`, whose process-spawning glue\n * is deliberately thin because everything that can be pure lives in the plan\n * builder and the protocol module.\n */\nimport { spawn, type ChildProcess } from 'node:child_process'\nimport { randomUUID } from 'node:crypto'\nimport { request as httpRequest } from 'node:http'\nimport { createConnection } from 'node:net'\nimport { Buffer } from 'node:buffer'\nimport type { Duplex } from 'node:stream'\nimport { DeskError, type DeskLaunchPlan, type DeskLauncherIdentity } from './plan'\nimport {\n encodeFrame,\n FrameDecoder,\n GUEST_AGENT_VSOCK_PORT,\n parseHostBoundMessage,\n type GuestCommand,\n type GuestEventMessage,\n type HostBoundMessage,\n} from './protocol'\n\n/**\n * A request that was in flight when the guest connection dropped.\n *\n * Distinct from a plain `DeskError` because the caller's next move is\n * different: this request may ALREADY HAVE RUN. The frame reached the guest,\n * and whether the guest executed it before the channel went away is not\n * knowable from the host. An `exec` may have sent mail; a `click` may have\n * landed. So the transport refuses to guess — it names the ambiguity and lets\n * the caller decide whether the operation is safe to repeat. Anything that\n * rejects with a plain `DeskError` instead never reached the guest at all.\n */\nexport class DeskRequestFateUnknownError extends DeskError {\n override readonly name: string = 'DeskRequestFateUnknownError'\n}\n\n/**\n * What happened to the transport underneath a machine.\n *\n * `reconnected` means the host lost the guest channel mid-lease and got it\n * back; the desk is usable again. It is NOT a promise that the guest is\n * unchanged — see the note on `onConnectionChange`. `lost` means it will not\n * come back on this VM and the desk needs a fresh boot.\n */\nexport type DeskConnectionChange =\n | {\n state: 'reconnected'\n deskId: string\n /** How the previous connection went away. */\n reason: string\n /** Wall-clock time the desk spent with no channel to its guest. */\n downtimeMs: number\n /** Reconnects this machine has survived, including this one. */\n reconnects: number\n }\n | {\n state: 'lost'\n deskId: string\n /** Why it is gone for good: the VMM exited, or the window ran out. */\n reason: string\n }\n\n/** A booted microVM as the host sees it: one desk, one guest-agent channel. */\nexport interface DeskMachine {\n readonly deskId: string\n /**\n * Send one request to the in-guest agent and await its correlated result.\n *\n * Rejects with `DeskError` when the guest reports an error, the request\n * times out, or the connection is gone for good — in that last case the\n * request never left the host, so repeating it is safe.\n *\n * A connection lost mid-lease is re-established rather than fatal: a call\n * that arrives while that is happening WAITS for it instead of failing.\n * A call that was already in flight when the channel dropped rejects with\n * `DeskRequestFateUnknownError`, because the host cannot tell whether the\n * guest ran it.\n */\n request(command: GuestCommand, options?: { timeoutMs?: number }): Promise<unknown>\n /**\n * Subscribe to unsolicited guest events (job exits, frames, focus changes).\n *\n * Subscriptions belong to the machine, not to the socket under it, so they\n * survive a reconnect. What does NOT survive is the guest's own capture\n * state: a guest agent that restarted has no screen, no frame encoder and\n * no video encoder, and a subscriber will simply stop receiving. Watch\n * `onConnectionChange` to notice.\n */\n subscribe(listener: (event: GuestEventMessage) => void): () => void\n /**\n * Watch the transport under this machine.\n *\n * Optional: a backend whose channel cannot drop has nothing to report. The\n * default backend reports every reconnect, because a reconnect that leaves\n * no trace turns a guest agent restarting every few minutes into a desk that\n * misbehaves for reasons nobody can find.\n */\n onConnectionChange?(listener: (change: DeskConnectionChange) => void): () => void\n /** Stop the VM. Idempotent; resolves once the VMM process has exited. */\n shutdown(): Promise<void>\n /**\n * Park the VM with its memory, so resuming continues the process tree.\n *\n * Optional: a backend with no snapshot facility simply does not offer it, and\n * the host falls back to `shutdown()`. Pauses the guest, writes a snapshot to\n * `snapshotDir`, then stops the VMM — and resolves only once all three have\n * happened, because a caller that believed a snapshot exists when it does not\n * would later refuse to boot the desk at all.\n *\n * The caller is responsible for quiescing the guest filesystem first. A paused\n * guest still holds dirty pages that exist only in the snapshot, so a disk\n * parked without a prior `sync` is consistent only *with that snapshot* —\n * which matters the moment the snapshot is discarded.\n */\n park?(snapshotDir: string): Promise<void>\n}\n\nexport interface DeskBootHooks {\n /**\n * Called on a restore, after the snapshot has been read back and before the\n * guest is allowed to run.\n *\n * This is the window in which the snapshot must be destroyed. A memory image\n * is valid only against the disk it was taken with, so once the guest resumes\n * and writes, any surviving snapshot describes a past that no longer matches —\n * and replaying it later would corrupt the desk. Restoring paused and\n * invalidating here is what makes that impossible rather than unlikely.\n *\n * Throwing aborts the restore: the VM is stopped and the caller is free to\n * cold boot, which is safe because a paused guest has written nothing.\n */\n onRestored?: () => Promise<void>\n}\n\nexport interface DeskBackend {\n boot(plan: DeskLaunchPlan, hooks?: DeskBootHooks): Promise<DeskMachine>\n}\n\nexport type DeskProcessLauncher = (\n command: string,\n args: readonly string[],\n options: { identity?: DeskLauncherIdentity },\n) => ChildProcess\n\nexport interface CloudHypervisorBackendOptions {\n /** Spawns the VMM and image tooling; injectable for tests. */\n launcher?: DeskProcessLauncher\n /** Opens the host side of the vsock Unix socket; injectable for tests. */\n connect?: (socketPath: string) => Duplex\n platform?: NodeJS.Platform\n /**\n * Clock behind every connect and reconnect deadline. Injectable for the same\n * reason `DeskHostOptions.now` is: a test drives a window to its end by\n * moving the clock, rather than sleeping for it and hoping the machine it\n * runs on is fast enough.\n */\n now?: () => number\n idFactory?: () => string\n requestTimeoutMs?: number\n connectTimeoutMs?: number\n connectRetryDelayMs?: number\n /** Bound on a single handshake attempt; see attemptHandshake. */\n handshakeTimeoutMs?: number\n /**\n * How long a machine keeps trying to get its guest channel back after an\n * unexpected drop before declaring the desk lost. Bounded on purpose: the\n * VMM is still alive so a reconnect is cheap and usually fast, but retrying\n * forever against a guest that is never coming back is worse than failing.\n */\n reconnectWindowMs?: number\n /**\n * First delay between reconnect attempts. It doubles toward a ceiling, so\n * this is the fast first poll rather than the steady rate. Separate from\n * `connectRetryDelayMs`, which is sized for a guest that is still cold\n * booting; here the guest was up a moment ago.\n */\n reconnectRetryDelayMs?: number\n killGraceMs?: number\n guestAgentPort?: number\n}\n\nconst DEFAULT_REQUEST_TIMEOUT_MS = 30_000\nconst DEFAULT_CONNECT_TIMEOUT_MS = 20_000\nconst DEFAULT_CONNECT_RETRY_DELAY_MS = 100\nconst DEFAULT_HANDSHAKE_TIMEOUT_MS = 5_000\nconst DEFAULT_RECONNECT_WINDOW_MS = 30_000\nconst DEFAULT_RECONNECT_RETRY_DELAY_MS = 250\n/**\n * How far the reconnect delay is allowed to double. A guest agent that systemd\n * is restarting is not helped by being hammered four times a second for the\n * whole window, and an unbounded doubling would spend most of the window\n * asleep after two failures.\n */\nconst RECONNECT_BACKOFF_CEILING = 8\nconst DEFAULT_KILL_GRACE_MS = 2_000\nconst HANDSHAKE_LINE_LIMIT = 128\n\nexport function createCloudHypervisorBackend(\n options: CloudHypervisorBackendOptions = {},\n): DeskBackend {\n const launcher = options.launcher ?? defaultLauncher\n const connect = options.connect ?? defaultConnect\n const platform = options.platform ?? process.platform\n const now = options.now ?? Date.now\n const idFactory = options.idFactory ?? randomUUID\n const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS\n const connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS\n const connectRetryDelayMs = options.connectRetryDelayMs ?? DEFAULT_CONNECT_RETRY_DELAY_MS\n const handshakeTimeoutMs = options.handshakeTimeoutMs ?? DEFAULT_HANDSHAKE_TIMEOUT_MS\n const reconnectWindowMs = options.reconnectWindowMs ?? DEFAULT_RECONNECT_WINDOW_MS\n const reconnectRetryDelayMs = options.reconnectRetryDelayMs ?? DEFAULT_RECONNECT_RETRY_DELAY_MS\n const killGraceMs = options.killGraceMs ?? DEFAULT_KILL_GRACE_MS\n const guestAgentPort = options.guestAgentPort ?? GUEST_AGENT_VSOCK_PORT\n\n async function boot(plan: DeskLaunchPlan, hooks?: DeskBootHooks): Promise<DeskMachine> {\n if (platform !== 'linux') {\n throw new DeskError(`The AppKit desk requires Linux; received ${platform}.`)\n }\n if (plan.overlay.create) {\n await runToCompletion(\n launcher(plan.overlay.create.command, plan.overlay.create.args, {\n identity: plan.launcherIdentity,\n }),\n `overlay creation for desk ${plan.deskId}`,\n )\n }\n\n const child = launcher(plan.vmm.command, plan.vmm.args, { identity: plan.launcherIdentity })\n const log = drainOutput(child)\n const exit = watchExit(child)\n\n // A restored guest comes back PAUSED (see the `resume=false` reasoning in\n // the plan). Three things have to happen in this order and nowhere else:\n // prove the restore finished, let the caller destroy the snapshot while the\n // guest still cannot write, then start it.\n if (plan.snapshot.restoring) {\n try {\n await waitForVmm(vmmApi, plan.api.socketPath, { now, exited: exit, timeoutMs: connectTimeoutMs })\n await hooks?.onRestored?.()\n await vmmApi(plan.api.socketPath, '/api/v1/vm.resume')\n } catch (error) {\n await terminate(child, exit, killGraceMs)\n const said = log.tail()\n const detail = said === '' ? '' : ` The VMM said: ${said}`\n throw new DeskError(\n `Desk ${plan.deskId} could not be restored from ${plan.snapshot.dir}: ${errorMessage(error)}${detail}`,\n )\n }\n }\n\n let stream: Duplex\n try {\n stream = await connectWithRetry({\n connect,\n now,\n socketPath: plan.vsock.socketPath,\n port: guestAgentPort,\n timeoutMs: connectTimeoutMs,\n retryDelayMs: connectRetryDelayMs,\n attemptTimeoutMs: handshakeTimeoutMs,\n exited: exit,\n })\n } catch (error) {\n await terminate(child, exit, killGraceMs)\n const said = log.tail()\n const detail = said === '' ? '' : ` The VMM said: ${said}`\n throw error instanceof DeskError\n ? new DeskError(`${error.message}${detail}`)\n : new DeskError(\n `Could not reach the guest agent for desk ${plan.deskId}: ${errorMessage(error)}${detail}`,\n )\n }\n return createMachine({\n deskId: plan.deskId,\n child,\n exit,\n stream,\n // The same path the first connection took, deliberately: a reconnect\n // that skipped confirmGuest would adopt the very hollow handshake the\n // boot path learned not to trust.\n reconnect: (abandoned) =>\n connectWithRetry({\n connect,\n now,\n socketPath: plan.vsock.socketPath,\n port: guestAgentPort,\n timeoutMs: reconnectWindowMs,\n retryDelayMs: reconnectRetryDelayMs,\n maxRetryDelayMs: reconnectRetryDelayMs * RECONNECT_BACKOFF_CEILING,\n attemptTimeoutMs: handshakeTimeoutMs,\n exited: exit,\n exitedMessage: `the VMM for desk ${plan.deskId} exited.`,\n abandoned,\n }),\n reconnectWindowMs,\n now,\n idFactory,\n requestTimeoutMs,\n killGraceMs,\n apiSocketPath: plan.api.socketPath,\n vmmApi,\n })\n }\n\n return { boot }\n}\n\n/**\n * Cloud Hypervisor's control plane: HTTP/1.1 over the `--api-socket` Unix\n * socket. The plan has always created that socket; until now nothing spoke to\n * it, because stopping a VM needed nothing more than killing the process.\n *\n * Pausing and snapshotting do need it, and `ch-remote` — the tool that usually\n * does this — is not in the runner image. Writing the two requests by hand is a\n * smaller change than adding a binary to the image, and keeps the whole\n * lifecycle inside one process that already owns the socket path.\n */\nexport type VmmApiCall = (\n socketPath: string,\n path: string,\n body?: Record<string, unknown>,\n method?: 'PUT' | 'GET',\n) => Promise<void>\n\nconst vmmApi: VmmApiCall = async (socketPath, path, body, method = 'PUT') => {\n const payload = body === undefined ? undefined : Buffer.from(JSON.stringify(body), 'utf8')\n await new Promise<void>((resolve, reject) => {\n const request = httpRequest(\n {\n socketPath,\n path,\n method,\n headers: {\n ...(payload ? { 'content-type': 'application/json', 'content-length': String(payload.length) } : {}),\n },\n },\n (response) => {\n // CH answers 204 with no body on success. Drain regardless: an unread\n // response keeps the socket open and the next call queues behind it.\n const chunks: Buffer[] = []\n response.on('data', (chunk: Buffer) => chunks.push(chunk))\n response.on('end', () => {\n const status = response.statusCode ?? 0\n if (status >= 200 && status < 300) {\n resolve()\n return\n }\n const said = Buffer.concat(chunks).toString('utf8').trim().slice(0, 400)\n reject(new DeskError(`${path} returned ${status}${said === '' ? '' : `: ${said}`}`))\n })\n },\n )\n request.on('error', (error) => reject(new DeskError(`${path} failed: ${errorMessage(error)}`)))\n if (payload) request.write(payload)\n request.end()\n })\n}\n\n/**\n * Wait until the VMM answers its control socket.\n *\n * On a restore this is also the only honest signal that the snapshot was read\n * back: Cloud Hypervisor creates the API socket early but does not serve\n * `vm.info` until it holds a VM, so a successful call means the restore\n * completed rather than merely that the process started. A VMM that exits\n * instead — a corrupt snapshot, a config the host can no longer satisfy — stops\n * the wait immediately rather than burning the whole timeout.\n */\nasync function waitForVmm(\n call: VmmApiCall,\n socketPath: string,\n options: { now: () => number; exited: ExitWatcher; timeoutMs: number; retryDelayMs?: number },\n): Promise<void> {\n const deadline = options.now() + options.timeoutMs\n const retryDelayMs = options.retryDelayMs ?? 50\n let lastFailure = 'it never answered'\n for (;;) {\n if (options.exited.done) throw new DeskError(`the VMM exited before it served its control socket`)\n try {\n await call(socketPath, '/api/v1/vm.info', undefined, 'GET')\n return\n } catch (error) {\n lastFailure = errorMessage(error)\n }\n if (options.now() >= deadline) {\n throw new DeskError(`the VMM did not answer ${socketPath} in ${options.timeoutMs}ms: ${lastFailure}`)\n }\n await new Promise((resolve) => setTimeout(resolve, retryDelayMs))\n }\n}\n\n/** The default backend: real Cloud Hypervisor over a real vsock socket. */\nexport const cloudHypervisorBackend: DeskBackend = createCloudHypervisorBackend()\n\n/**\n * The machine, and the small state machine that keeps it connected.\n *\n * A vsock channel is not a fact the host learns once at boot — the transport\n * can lie about being connected, and it can go away mid-lease for reasons that\n * have nothing to do with the desk being unusable: the guest agent restarts,\n * the socat bridge drops, a transient vsock error, the guest wedges for a\n * moment. Treating any of those as terminal stranded the desk for the rest of\n * its lease with a perfectly healthy guest behind it, answering every call\n * with \"no longer connected\". So a live connection is something this machine\n * RE-ESTABLISHES:\n *\n * live ──unexpected drop──▶ reconnecting ──confirmed guest──▶ live\n * │ │\n * │ └──window exhausted / VMM exited──▶ closed\n * └──shutdown(), VMM already exited, protocol violation───────────▶ closed\n *\n * `closed` is terminal. Nothing reconnects out of it, which is what makes\n * `shutdown()` and a dead VMM final rather than the start of a retry loop.\n */\nfunction createMachine(options: {\n deskId: string\n child: ChildProcess\n exit: ExitWatcher\n stream: Duplex\n /** Re-establishes the channel; resolves only once a guest has answered. */\n reconnect: (abandoned: () => boolean) => Promise<Duplex>\n reconnectWindowMs: number\n now: () => number\n idFactory: () => string\n requestTimeoutMs: number\n killGraceMs: number\n /** Cloud Hypervisor's control socket, for pause and snapshot. */\n apiSocketPath: string\n vmmApi: VmmApiCall\n}): DeskMachine {\n const { deskId, child, exit, now, idFactory, requestTimeoutMs, killGraceMs, reconnectWindowMs } =\n options\n const pending = new Map<\n string,\n { resolve: (result: unknown) => void; reject: (error: Error) => void; timer: NodeJS.Timeout }\n >()\n // Listeners live on the machine rather than on the socket, so a reconnect\n // re-points the data path at the new stream and every subscriber keeps\n // working without re-registering.\n const listeners = new Set<(event: GuestEventMessage) => void>()\n const watchers = new Set<(change: DeskConnectionChange) => void>()\n let stream = options.stream\n let state: 'live' | 'reconnecting' | 'closed' = 'live'\n let reconnects = 0\n let reconnecting: Promise<void> | null = null\n\n function failAll(error: Error): void {\n for (const entry of pending.values()) {\n clearTimeout(entry.timer)\n entry.reject(error)\n }\n pending.clear()\n }\n\n function announce(change: DeskConnectionChange): void {\n for (const watcher of [...watchers]) watcher(change)\n }\n\n function close(error: DeskError, intentional: boolean): void {\n if (state === 'closed') return\n state = 'closed'\n failAll(error)\n stream.destroy()\n // A desk the consumer asked to stop is not news; one that stopped on its\n // own is the thing an operator needs to be told about.\n if (!intentional) announce({ state: 'lost', deskId, reason: error.message })\n }\n\n function attach(next: Duplex): void {\n stream = next\n // Each connection decodes with its OWN decoder: a frame the drop cut in\n // half must never be completed by the first bytes of its replacement.\n const decoder = new FrameDecoder()\n const current = () => stream === next && state !== 'closed'\n const onData = (chunk: Buffer): void => {\n if (!current()) return\n let values: unknown[]\n try {\n values = decoder.push(chunk)\n } catch (error) {\n close(\n new DeskError(`Guest agent protocol violation on desk ${deskId}: ${errorMessage(error)}`),\n false,\n )\n return\n }\n for (const value of values) {\n let message: HostBoundMessage\n try {\n message = parseHostBoundMessage(value)\n } catch (error) {\n close(\n new DeskError(`Guest agent sent a malformed message on desk ${deskId}: ${errorMessage(error)}`),\n false,\n )\n return\n }\n if ('id' in message) {\n const entry = pending.get(message.id)\n if (!entry) continue\n pending.delete(message.id)\n clearTimeout(entry.timer)\n if (message.ok) entry.resolve(message.result)\n else entry.reject(new DeskError(`Guest agent error on desk ${deskId}: ${message.error}`))\n } else {\n for (const listener of [...listeners]) listener(message)\n }\n }\n }\n next.on('data', onData)\n next.once('close', () => {\n drop(next, `the guest agent connection closed on desk ${deskId}`)\n })\n next.once('error', (error: Error) => {\n drop(next, `the guest agent connection failed on desk ${deskId}: ${error.message}`)\n })\n }\n\n function drop(from: Duplex, reason: string): void {\n if (state !== 'live' || from !== stream) return\n from.destroy()\n if (exit.done) {\n // A dead VMM is not a connection problem. There is no guest to get back\n // to, and retrying against it forever only delays the fresh boot the\n // desk actually needs.\n close(new DeskError(`Desk ${deskId} is no longer connected: the VMM exited (${reason}).`), false)\n return\n }\n state = 'reconnecting'\n const lostAt = now()\n // In-flight requests CANNOT be silently retried. Each of their frames\n // already reached the guest, and whether the guest ran it before the\n // channel went away is not knowable from here: an `exec` may have sent\n // mail, a `click` may have landed. Replaying a side-effecting operation to\n // paper over a blip is the wrong kind of resilience, so every one of them\n // is told its fate is unknown and the caller decides.\n failAll(\n new DeskRequestFateUnknownError(\n `Desk ${deskId} lost its guest agent connection while this request was in flight `\n + `(${reason}); the guest may or may not have run it.`,\n ),\n )\n reconnecting = restore(reason, lostAt)\n }\n\n /** Always resolves: callers await it to learn the outcome, not to be thrown at. */\n async function restore(reason: string, lostAt: number): Promise<void> {\n let next: Duplex\n try {\n next = await options.reconnect(() => state !== 'reconnecting')\n } catch (error) {\n close(\n new DeskError(\n `Desk ${deskId} lost its guest agent connection and could not get it back: ${errorMessage(error)}`,\n ),\n false,\n )\n return\n }\n if (state !== 'reconnecting') {\n // shutdown() won the race while the replacement socket was coming up.\n next.destroy()\n return\n }\n attach(next)\n state = 'live'\n reconnects += 1\n announce({ state: 'reconnected', deskId, reason, downtimeMs: now() - lostAt, reconnects })\n }\n\n attach(options.stream)\n\n return {\n deskId,\n async request(command, requestOptions = {}) {\n // A call that lands mid-reconnect waits for it rather than failing on\n // the spot: the guest is usually back within seconds, and telling a\n // caller a desk is gone when it is about to be fine is the bug this\n // machine exists to end. The wait is bounded by the same window the\n // reconnect itself is.\n const waitUntil = now() + reconnectWindowMs\n while (state === 'reconnecting') {\n const attempt = reconnecting\n if (!attempt || now() >= waitUntil) break\n await attempt\n }\n if (state !== 'live') {\n // This one never left the host, so — unlike a request that was in\n // flight when the channel dropped — repeating it is safe.\n throw new DeskError(`Desk ${deskId} is no longer connected.`)\n }\n const id = idFactory()\n const timeoutMs = requestOptions.timeoutMs ?? requestTimeoutMs\n const target = stream\n return await new Promise<unknown>((resolvePromise, rejectPromise) => {\n const timer = setTimeout(() => {\n pending.delete(id)\n rejectPromise(new DeskError(`Guest request ${command.op} timed out on desk ${deskId}.`))\n }, timeoutMs)\n timer.unref()\n pending.set(id, { resolve: resolvePromise, reject: rejectPromise, timer })\n target.write(encodeFrame({ ...command, id }))\n })\n },\n subscribe(listener) {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n onConnectionChange(listener) {\n watchers.add(listener)\n return () => {\n watchers.delete(listener)\n }\n },\n async shutdown() {\n // Terminal by construction: `closed` is the state the reconnect loop\n // checks, so an explicit shutdown can never be followed by a reconnect.\n close(new DeskError(`Desk ${deskId} is shutting down.`), true)\n await terminate(child, exit, killGraceMs)\n },\n async park(snapshotDir: string) {\n // Close the guest channel FIRST. Pausing a VM strands whatever was in\n // flight on the vsock, and a reconnect attempt against a paused guest\n // would burn the whole reconnect window before giving up — so the channel\n // is made terminal before the VM stops answering, exactly as shutdown\n // does it.\n close(new DeskError(`Desk ${deskId} is being parked.`), true)\n try {\n await options.vmmApi(options.apiSocketPath, '/api/v1/vm.pause')\n await options.vmmApi(options.apiSocketPath, '/api/v1/vm.snapshot', {\n // Three slashes: `file://` plus an absolute path. CH rejects a\n // destination_url it cannot parse, and the directory must already\n // exist — it writes the config and memory files into it.\n destination_url: `file://${snapshotDir}`,\n })\n } catch (error) {\n // The VM is paused and there is no usable snapshot, which is the one\n // state nothing downstream can interpret. Stop the VMM so the desk is\n // plainly off, and say why — the caller falls back to a cold boot, and\n // the disk is still consistent because the guest was synced before this.\n await terminate(child, exit, killGraceMs)\n throw error instanceof DeskError\n ? new DeskError(`Desk ${deskId} could not be parked with its memory: ${error.message}`)\n : new DeskError(`Desk ${deskId} could not be parked with its memory: ${errorMessage(error)}`)\n }\n await terminate(child, exit, killGraceMs)\n },\n }\n}\n\ninterface ExitWatcher {\n done: boolean\n promise: Promise<void>\n}\n\n/**\n * Read the VMM's stdout and stderr, and keep only the last few kilobytes.\n *\n * This is not for logging — it is load-bearing. The child is spawned with\n * both streams as PIPES, and a pipe nobody reads fills after about 64KB, at\n * which point the VMM BLOCKS on its next write. Cloud Hypervisor logs while\n * it runs, so an unread pipe freezes the guest partway through boot: the\n * process is alive, the disk is untouched, and the guest agent never reaches\n * vsock. Every symptom points at the image and none of them are its fault.\n *\n * Draining costs nothing and buys the tail of what the VMM said, which is the\n * difference between \"the VMM exited\" and knowing why.\n */\nfunction drainOutput(child: ChildProcess, limit = 8 * 1024): { tail: () => string } {\n let buffered = ''\n const absorb = (chunk: unknown) => {\n buffered += String(chunk)\n if (buffered.length > limit) buffered = buffered.slice(buffered.length - limit)\n }\n child.stdout?.on('data', absorb)\n child.stderr?.on('data', absorb)\n // A pipe that errors must not take the process down with it.\n child.stdout?.on('error', () => undefined)\n child.stderr?.on('error', () => undefined)\n return { tail: () => buffered.trim() }\n}\n\nfunction watchExit(child: ChildProcess): ExitWatcher {\n const watcher: ExitWatcher = { done: false, promise: Promise.resolve() }\n watcher.promise = new Promise<void>((resolvePromise) => {\n const settle = () => {\n watcher.done = true\n resolvePromise()\n }\n child.once('exit', settle)\n child.once('error', settle)\n })\n return watcher\n}\n\nasync function terminate(child: ChildProcess, exit: ExitWatcher, killGraceMs: number): Promise<void> {\n if (exit.done) return\n child.kill('SIGTERM')\n const forceKill = setTimeout(() => {\n if (!exit.done) child.kill('SIGKILL')\n }, killGraceMs)\n forceKill.unref()\n await exit.promise\n clearTimeout(forceKill)\n}\n\nfunction runToCompletion(child: ChildProcess, label: string): Promise<void> {\n return new Promise<void>((resolvePromise, rejectPromise) => {\n let stderr = ''\n child.stderr?.on('data', (chunk) => {\n stderr += String(chunk)\n })\n child.once('error', (error) => {\n rejectPromise(new DeskError(`${label} failed to start: ${error.message}`))\n })\n child.once('exit', (code, signal) => {\n if (code === 0) {\n resolvePromise()\n return\n }\n const detail = stderr.trim()\n rejectPromise(\n new DeskError(\n `${label} exited with code ${code ?? 'unknown'}`\n + `${signal ? ` (${signal})` : ''}${detail ? `: ${detail}` : '.'}`,\n ),\n )\n })\n })\n}\n\n/**\n * Cloud Hypervisor exposes guest vsock ports through a host Unix socket with\n * a line-oriented preamble: the client writes `CONNECT <port>\\n` and the VMM\n * answers `OK <assigned>\\n` before the byte stream becomes the guest\n * connection. The VMM creates the socket during boot, so connection attempts\n * retry until the deadline.\n *\n * The same loop serves the first connection and every later reconnect, so a\n * reconnect gets `confirmGuest` too — a mid-lease reconnect that trusted the\n * banner would re-adopt exactly the hollow stream the boot path learned not to.\n */\nasync function connectWithRetry(options: {\n connect: (socketPath: string) => Duplex\n now: () => number\n socketPath: string\n port: number\n timeoutMs: number\n retryDelayMs: number\n /** Ceiling the delay doubles toward; omitted keeps the delay constant. */\n maxRetryDelayMs?: number\n attemptTimeoutMs: number\n exited: ExitWatcher\n /** A dead VMM means something different at boot than it does mid-lease. */\n exitedMessage?: string\n /** Stops the loop early when the caller no longer wants the connection. */\n abandoned?: () => boolean\n}): Promise<Duplex> {\n const deadline = options.now() + options.timeoutMs\n let lastFailure = 'the guest agent never answered'\n let retryDelayMs = options.retryDelayMs\n while (options.now() <= deadline) {\n if (options.exited.done) {\n throw new DeskError(options.exitedMessage ?? 'The VMM exited before the guest agent came up.')\n }\n if (options.abandoned?.() === true) {\n throw new DeskError('the connection attempt was abandoned.')\n }\n try {\n const stream = await attemptHandshake(\n options.connect,\n options.socketPath,\n options.port,\n options.attemptTimeoutMs,\n )\n try {\n await confirmGuest(stream, options.attemptTimeoutMs)\n } catch (error) {\n stream.destroy()\n throw error\n }\n return stream\n } catch (error) {\n lastFailure = errorMessage(error)\n }\n await delay(retryDelayMs)\n if (options.maxRetryDelayMs !== undefined) {\n retryDelayMs = Math.min(retryDelayMs * 2, options.maxRetryDelayMs)\n }\n }\n throw new DeskError(\n `Timed out connecting to the guest agent at ${options.socketPath}: ${lastFailure}`,\n )\n}\n\n/**\n * One handshake attempt, which MUST settle.\n *\n * Cloud Hypervisor accepts a connection on the vsock socket whether or not\n * anything in the guest is listening on the port, and when nothing is it may\n * answer nothing at all: no reply, no error, no close. Without a deadline of\n * its own this promise then never settles — and because the caller awaits it\n * inside the retry loop, the loop stops iterating, the overall deadline is\n * never re-checked, and the boot hangs forever rather than failing. That is\n * indistinguishable from a wedged host and it is why every attempt is bounded\n * here as well as in aggregate.\n */\n/**\n * Prove a guest is behind the handshake by asking it something.\n *\n * Cloud Hypervisor answers `CONNECT <port>` with `OK` whether or not anything\n * in the guest is listening on that port, and only closes the socket a moment\n * later when it finds nobody there. A caller that trusts the banner therefore\n * \"connects\" to a guest that is still booting, gets a closed stream seconds\n * later, and — because a closed machine is not retried — leaves the desk dead\n * for as long as it is leased, with a healthy guest sitting behind it. So the\n * handshake is not the test; a reply is.\n */\nasync function confirmGuest(stream: Duplex, timeoutMs: number): Promise<void> {\n const decoder = new FrameDecoder()\n await new Promise<void>((resolvePromise, rejectPromise) => {\n let settled = false\n const finish = (error?: Error) => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n stream.off('data', onData)\n stream.off('close', onClose)\n stream.off('error', finish)\n if (error) rejectPromise(error)\n else resolvePromise()\n }\n const timer = setTimeout(\n () => finish(new DeskError('the guest agent did not answer the first request.')),\n timeoutMs,\n )\n timer.unref?.()\n const onClose = () =>\n finish(new DeskError('the connection closed before the guest agent answered.'))\n const onData = (chunk: Buffer) => {\n let values: unknown[]\n try {\n values = decoder.push(chunk)\n } catch (error) {\n finish(error instanceof Error ? error : new Error(String(error)))\n return\n }\n for (const value of values) {\n // Anything at all proves a guest is there; the reply is parsed for\n // real by the machine once it owns the stream.\n if (value && typeof value === 'object') {\n finish()\n return\n }\n }\n }\n stream.on('data', onData)\n stream.once('close', onClose)\n stream.once('error', finish)\n stream.write(encodeFrame({ id: 'hello', op: 'ping' }))\n })\n}\n\nfunction attemptHandshake(\n connect: (socketPath: string) => Duplex,\n socketPath: string,\n port: number,\n attemptTimeoutMs: number,\n): Promise<Duplex> {\n return new Promise<Duplex>((resolvePromise, rejectPromise) => {\n let stream: Duplex\n try {\n stream = connect(socketPath)\n } catch (error) {\n rejectPromise(error instanceof Error ? error : new Error(String(error)))\n return\n }\n let banner = ''\n let settled = false\n const timer = setTimeout(() => {\n fail(new DeskError('vsock handshake went unanswered.'))\n }, attemptTimeoutMs)\n // An unref'd timer must not hold the process open on its own.\n timer.unref?.()\n const fail = (error: Error) => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n stream.destroy()\n rejectPromise(error)\n }\n const onData = (chunk: Buffer) => {\n banner += chunk.toString('latin1')\n const newline = banner.indexOf('\\n')\n if (newline === -1) {\n if (banner.length > HANDSHAKE_LINE_LIMIT) {\n fail(new DeskError('vsock handshake reply exceeded the line limit.'))\n }\n return\n }\n const line = banner.slice(0, newline)\n const rest = banner.slice(newline + 1)\n stream.off('data', onData)\n stream.off('error', fail)\n if (!line.startsWith('OK ')) {\n fail(new DeskError(`vsock handshake was refused: ${line}`))\n return\n }\n settled = true\n clearTimeout(timer)\n if (rest.length > 0) stream.unshift(Buffer.from(rest, 'latin1'))\n resolvePromise(stream)\n }\n stream.on('data', onData)\n stream.once('error', fail)\n // A close before the banner is a refusal, not a hang.\n stream.once('close', () => fail(new DeskError('vsock handshake closed before a reply.')))\n stream.write(`CONNECT ${port}\\n`)\n })\n}\n\nfunction defaultLauncher(\n command: string,\n args: readonly string[],\n options: { identity?: DeskLauncherIdentity },\n): ChildProcess {\n return spawn(command, [...args], {\n stdio: ['ignore', 'pipe', 'pipe'],\n uid: options.identity?.uid,\n gid: options.identity?.gid,\n })\n}\n\nfunction defaultConnect(socketPath: string): Duplex {\n return createConnection(socketPath)\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise<void>((resolvePromise) => {\n const timer = setTimeout(resolvePromise, ms)\n timer.unref()\n })\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n"]}
1
+ {"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from 'node:crypto'\nimport { existsSync } from 'node:fs'\nimport { rm } from 'node:fs/promises'\nimport { request as httpRequest } from 'node:http'\nimport { GUEST_AGENT_VSOCK_PORT } from './protocol'\nimport {\n DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_CONNECT_TIMEOUT_MS, DEFAULT_CONNECT_RETRY_DELAY_MS,\n DEFAULT_HANDSHAKE_TIMEOUT_MS, DEFAULT_RECONNECT_WINDOW_MS, DEFAULT_RECONNECT_RETRY_DELAY_MS,\n DEFAULT_KILL_GRACE_MS, RECONNECT_BACKOFF_CEILING, connectWithRetry, createGuestMachine,\n defaultLauncher, defaultConnect, drainOutput, watchExit, terminate, runToCompletion, errorMessage,\n assertShareManifest, writeShareManifest, type ExitWatcher,\n} from './machine'\n/**\n * The backend port.\n *\n * `DeskBackend` is the seam that keeps the package testable without a\n * hypervisor: the host asks a backend to boot a `DeskLaunchPlan` and gets a\n * `DeskMachine` — a request/response channel to the in-guest agent plus an\n * event subscription. CI and non-KVM developer machines inject an in-memory\n * fake; production uses `cloudHypervisorBackend` on Linux or\n * `createVirtualizationBackend` on an Apple silicon Mac, whose process glue is\n * deliberately thin because everything that can be pure lives in the plan\n * builder, the protocol module, and the shared guest channel.\n */\nimport type { ChildProcess } from 'node:child_process'\nimport type { Duplex } from 'node:stream'\nimport {\n DeskError, type CloudHypervisorLaunchPlan, type DeskHypervisor, type DeskLaunchPlan, type DeskLauncherIdentity,\n} from './plan'\nimport type { GuestCommand, GuestEventMessage } from './protocol'\n\n/**\n * A request that was in flight when the guest connection dropped.\n *\n * Distinct from a plain `DeskError` because the caller's next move is\n * different: this request may ALREADY HAVE RUN. The frame reached the guest,\n * and whether the guest executed it before the channel went away is not\n * knowable from the host. An `exec` may have sent mail; a `click` may have\n * landed. So the transport refuses to guess — it names the ambiguity and lets\n * the caller decide whether the operation is safe to repeat. Anything that\n * rejects with a plain `DeskError` instead never reached the guest at all.\n */\nexport class DeskRequestFateUnknownError extends DeskError {\n override readonly name: string = 'DeskRequestFateUnknownError'\n}\n\n/**\n * What happened to the transport underneath a machine.\n *\n * `reconnected` means the host lost the guest channel mid-lease and got it\n * back; the desk is usable again. It is NOT a promise that the guest is\n * unchanged — see the note on `onConnectionChange`. `lost` means it will not\n * come back on this VM and the desk needs a fresh boot.\n */\nexport type DeskConnectionChange =\n | {\n state: 'reconnected'\n deskId: string\n /** How the previous connection went away. */\n reason: string\n /** Wall-clock time the desk spent with no channel to its guest. */\n downtimeMs: number\n /** Reconnects this machine has survived, including this one. */\n reconnects: number\n }\n | {\n state: 'lost'\n deskId: string\n /** Why it is gone for good: the VMM exited, or the window ran out. */\n reason: string\n }\n\n/** A booted microVM as the host sees it: one desk, one guest-agent channel. */\nexport interface DeskMachine {\n readonly deskId: string\n /**\n * Send one request to the in-guest agent and await its correlated result.\n *\n * Rejects with `DeskError` when the guest reports an error, the request\n * times out, or the connection is gone for good — in that last case the\n * request never left the host, so repeating it is safe.\n *\n * A connection lost mid-lease is re-established rather than fatal: a call\n * that arrives while that is happening WAITS for it instead of failing.\n * A call that was already in flight when the channel dropped rejects with\n * `DeskRequestFateUnknownError`, because the host cannot tell whether the\n * guest ran it.\n */\n request(command: GuestCommand, options?: { timeoutMs?: number }): Promise<unknown>\n /**\n * Subscribe to unsolicited guest events (job exits, frames, focus changes).\n *\n * Subscriptions belong to the machine, not to the socket under it, so they\n * survive a reconnect. What does NOT survive is the guest's own capture\n * state: a guest agent that restarted has no screen, no frame encoder and\n * no video encoder, and a subscriber will simply stop receiving. Watch\n * `onConnectionChange` to notice.\n */\n subscribe(listener: (event: GuestEventMessage) => void): () => void\n /**\n * Watch the transport under this machine.\n *\n * Optional: a backend whose channel cannot drop has nothing to report. The\n * default backend reports every reconnect, because a reconnect that leaves\n * no trace turns a guest agent restarting every few minutes into a desk that\n * misbehaves for reasons nobody can find.\n */\n onConnectionChange?(listener: (change: DeskConnectionChange) => void): () => void\n /**\n * Open a TCP stream from the host to `port` on the guest.\n *\n * Optional: a backend whose guest has a routable address (Cloud Hypervisor's\n * TAP) leaves this out and the host dials the guest address directly. A\n * backend whose guest sits behind a userspace NAT (Virtualization.framework)\n * provides it, because there is no address on the Mac to dial. Rejects with\n * `DeskError` when the guest refuses or does not answer.\n */\n connect?(port: number): Promise<Duplex>\n /** Stop the VM. Idempotent; resolves once the VMM process has exited. */\n shutdown(): Promise<void>\n /**\n * Park the VM with its memory, so resuming continues the process tree.\n *\n * Optional: a backend with no snapshot facility simply does not offer it, and\n * the host falls back to `shutdown()`. Pauses the guest, writes a snapshot to\n * `snapshotDir`, then stops the VMM — and resolves only once all three have\n * happened, because a caller that believed a snapshot exists when it does not\n * would later refuse to boot the desk at all.\n *\n * The caller is responsible for quiescing the guest filesystem first. A paused\n * guest still holds dirty pages that exist only in the snapshot, so a disk\n * parked without a prior `sync` is consistent only *with that snapshot* —\n * which matters the moment the snapshot is discarded.\n */\n park?(snapshotDir: string): Promise<void>\n}\n\nexport interface DeskBootHooks {\n /**\n * Called on a restore, after the snapshot has been read back and before the\n * guest is allowed to run.\n *\n * This is the window in which the snapshot must be destroyed. A memory image\n * is valid only against the disk it was taken with, so once the guest resumes\n * and writes, any surviving snapshot describes a past that no longer matches —\n * and replaying it later would corrupt the desk. Restoring paused and\n * invalidating here is what makes that impossible rather than unlikely.\n *\n * Throwing aborts the restore: the VM is stopped and the caller is free to\n * cold boot, which is safe because a paused guest has written nothing.\n */\n onRestored?: () => Promise<void>\n}\n\nexport interface DeskBackend {\n /**\n * Which hypervisor this backend boots, and so which plan the host builds\n * for it. Absent means Cloud Hypervisor.\n */\n readonly hypervisor?: DeskHypervisor\n /**\n * The binary the backend launches — the VMM or the helper — for readiness\n * reporting. Optional; `verifyDeskHost` falls back to its own option.\n */\n readonly vmmPath?: string\n boot(plan: DeskLaunchPlan, hooks?: DeskBootHooks): Promise<DeskMachine>\n}\n\nexport type DeskProcessLauncher = (\n command: string,\n args: readonly string[],\n options: { identity?: DeskLauncherIdentity },\n) => ChildProcess\n\nexport interface CloudHypervisorBackendOptions {\n /** Spawns the VMM and image tooling; injectable for tests. */\n launcher?: DeskProcessLauncher\n /** Opens the host side of the vsock Unix socket; injectable for tests. */\n connect?: (socketPath: string) => Duplex\n platform?: NodeJS.Platform\n /**\n * Clock behind every connect and reconnect deadline. Injectable for the same\n * reason `DeskHostOptions.now` is: a test drives a window to its end by\n * moving the clock, rather than sleeping for it and hoping the machine it\n * runs on is fast enough.\n */\n now?: () => number\n idFactory?: () => string\n requestTimeoutMs?: number\n connectTimeoutMs?: number\n connectRetryDelayMs?: number\n /** Bound on a single handshake attempt; see attemptHandshake. */\n handshakeTimeoutMs?: number\n /**\n * How long a machine keeps trying to get its guest channel back after an\n * unexpected drop before declaring the desk lost. Bounded on purpose: the\n * VMM is still alive so a reconnect is cheap and usually fast, but retrying\n * forever against a guest that is never coming back is worse than failing.\n */\n reconnectWindowMs?: number\n /**\n * First delay between reconnect attempts. It doubles toward a ceiling, so\n * this is the fast first poll rather than the steady rate. Separate from\n * `connectRetryDelayMs`, which is sized for a guest that is still cold\n * booting; here the guest was up a moment ago.\n */\n reconnectRetryDelayMs?: number\n killGraceMs?: number\n guestAgentPort?: number\n /** Whether a share daemon's socket exists yet; injectable for tests. */\n pathExists?: (path: string) => boolean\n /** Removes a stale share socket before its daemon starts; injectable for tests. */\n removeFile?: (path: string) => Promise<void>\n}\n\nexport function createCloudHypervisorBackend(\n options: CloudHypervisorBackendOptions = {},\n): DeskBackend {\n const launcher = options.launcher ?? defaultLauncher\n const connect = options.connect ?? defaultConnect\n const platform = options.platform ?? process.platform\n const now = options.now ?? Date.now\n const idFactory = options.idFactory ?? randomUUID\n const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS\n const connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS\n const connectRetryDelayMs = options.connectRetryDelayMs ?? DEFAULT_CONNECT_RETRY_DELAY_MS\n const handshakeTimeoutMs = options.handshakeTimeoutMs ?? DEFAULT_HANDSHAKE_TIMEOUT_MS\n const reconnectWindowMs = options.reconnectWindowMs ?? DEFAULT_RECONNECT_WINDOW_MS\n const reconnectRetryDelayMs = options.reconnectRetryDelayMs ?? DEFAULT_RECONNECT_RETRY_DELAY_MS\n const killGraceMs = options.killGraceMs ?? DEFAULT_KILL_GRACE_MS\n const guestAgentPort = options.guestAgentPort ?? GUEST_AGENT_VSOCK_PORT\n const pathExists = options.pathExists ?? existsSync\n const removeFile = options.removeFile ?? ((path: string) => rm(path, { force: true }))\n\n /**\n * Start one `virtiofsd` per share and wait until every socket exists.\n *\n * The VMM connects to these at boot and at restore alike, so they come up\n * first. A stale socket is removed beforehand, or its presence would read as\n * a daemon that is listening. Existence is the readiness signal rather than\n * a probe connection, because `virtiofsd` serves exactly one connection and\n * exits when it closes; the moment between bind and listen is covered by\n * Cloud Hypervisor retrying a refused vhost-user connect.\n */\n async function startShareDaemons(plan: CloudHypervisorLaunchPlan): Promise<ShareDaemons> {\n const daemons: { tag: string; child: ChildProcess; exit: ExitWatcher; log: { tail: () => string } }[] = []\n const stop = async () => {\n await Promise.all(daemons.map((daemon) => terminate(daemon.child, daemon.exit, killGraceMs)))\n }\n try {\n for (const share of plan.shares) {\n await removeFile(share.socketPath)\n const child = launcher(share.daemon.command, share.daemon.args, { identity: plan.launcherIdentity })\n daemons.push({ tag: share.tag, child, exit: watchExit(child), log: drainOutput(child) })\n }\n const deadline = now() + connectTimeoutMs\n for (;;) {\n const exited = daemons.find((daemon) => daemon.exit.done)\n if (exited) {\n const said = exited.log.tail()\n throw new DeskError(`virtiofsd for share ${exited.tag} exited before it listened${said === '' ? '.' : `: ${said}`}`)\n }\n const waiting = plan.shares.filter((share) => !pathExists(share.socketPath))\n if (waiting.length === 0) break\n if (now() >= deadline) {\n throw new DeskError(\n `virtiofsd did not create ${waiting.map((share) => share.socketPath).join(', ')} in ${connectTimeoutMs}ms.`,\n )\n }\n // A held timer: the wait is bounded by the deadline, and nothing else may be keeping\n // the process alive while a daemon starts.\n await new Promise<void>((resolvePromise) => setTimeout(resolvePromise, connectRetryDelayMs))\n }\n } catch (error) {\n await stop()\n throw error\n }\n return {\n stop,\n // The VMM cannot serve a share whose daemon is gone; neither can it be\n // handed a new one mid-run. So a daemon that dies stops the VMM, and the\n // desk is lost loudly instead of hanging every guest access to the share.\n supervise: (vmm, vmmExit) => {\n for (const daemon of daemons) {\n void daemon.exit.promise.then(() => terminate(vmm, vmmExit, killGraceMs))\n }\n },\n }\n }\n\n async function boot(plan: DeskLaunchPlan, hooks?: DeskBootHooks): Promise<DeskMachine> {\n if (plan.hypervisor !== 'cloud-hypervisor') throw new DeskError('Cloud Hypervisor requires a cloud-hypervisor plan.')\n if (platform !== 'linux') {\n throw new DeskError(`The AppKit desk requires Linux; received ${platform}.`)\n }\n if (plan.overlay.create) {\n await runToCompletion(\n launcher(plan.overlay.create.command, plan.overlay.create.args, {\n identity: plan.launcherIdentity,\n }),\n `overlay creation for desk ${plan.deskId}`,\n )\n }\n if (plan.snapshot.restoring) {\n try {\n await assertShareManifest(plan.snapshot.dir, plan.shares)\n } catch (error) {\n throw new DeskError(`Desk ${plan.deskId} could not be restored from ${plan.snapshot.dir}: ${errorMessage(error)}`)\n }\n }\n\n let shares: ShareDaemons\n try {\n shares = await startShareDaemons(plan)\n } catch (error) {\n throw new DeskError(`Desk ${plan.deskId} could not start its shares: ${errorMessage(error)}`)\n }\n const child = launcher(plan.vmm.command, plan.vmm.args, { identity: plan.launcherIdentity })\n const log = drainOutput(child)\n const exit = watchExit(child)\n shares.supervise(child, exit)\n // The VMM first: a daemon outliving it is harmless, while a daemon stopped\n // under a running VMM is a share failing in the guest.\n const stop = async () => {\n try {\n await terminate(child, exit, killGraceMs)\n } finally {\n await shares.stop()\n }\n }\n\n // A restored guest comes back PAUSED (see the `resume=false` reasoning in\n // the plan). Three things have to happen in this order and nowhere else:\n // prove the restore finished, let the caller destroy the snapshot while the\n // guest still cannot write, then start it.\n if (plan.snapshot.restoring) {\n try {\n await waitForVmm(vmmApi, plan.api.socketPath, { now, exited: exit, timeoutMs: connectTimeoutMs })\n await hooks?.onRestored?.()\n await vmmApi(plan.api.socketPath, '/api/v1/vm.resume')\n } catch (error) {\n await stop()\n const said = log.tail()\n const detail = said === '' ? '' : ` The VMM said: ${said}`\n throw new DeskError(\n `Desk ${plan.deskId} could not be restored from ${plan.snapshot.dir}: ${errorMessage(error)}${detail}`,\n )\n }\n }\n\n let stream: Duplex\n try {\n stream = await connectWithRetry({\n connect,\n now,\n socketPath: plan.vsock.socketPath,\n port: guestAgentPort,\n timeoutMs: connectTimeoutMs,\n retryDelayMs: connectRetryDelayMs,\n attemptTimeoutMs: handshakeTimeoutMs,\n exited: exit,\n })\n } catch (error) {\n await stop()\n const said = log.tail()\n const detail = said === '' ? '' : ` The VMM said: ${said}`\n throw error instanceof DeskError\n ? new DeskError(`${error.message}${detail}`)\n : new DeskError(\n `Could not reach the guest agent for desk ${plan.deskId}: ${errorMessage(error)}${detail}`,\n )\n }\n return createGuestMachine({\n deskId: plan.deskId,\n exit,\n stream,\n // The same path the first connection took, deliberately: a reconnect\n // that skipped confirmGuest would adopt the very hollow handshake the\n // boot path learned not to trust.\n reconnect: (abandoned) =>\n connectWithRetry({\n connect,\n now,\n socketPath: plan.vsock.socketPath,\n port: guestAgentPort,\n timeoutMs: reconnectWindowMs,\n retryDelayMs: reconnectRetryDelayMs,\n maxRetryDelayMs: reconnectRetryDelayMs * RECONNECT_BACKOFF_CEILING,\n attemptTimeoutMs: handshakeTimeoutMs,\n exited: exit,\n exitedMessage: `the VMM for desk ${plan.deskId} exited.`,\n abandoned,\n }),\n reconnectWindowMs,\n now,\n idFactory,\n requestTimeoutMs,\n stop,\n park: async (snapshotDir) => {\n try {\n await vmmApi(plan.api.socketPath, '/api/v1/vm.pause')\n await vmmApi(plan.api.socketPath, '/api/v1/vm.snapshot', { destination_url: `file://${snapshotDir}` })\n await writeShareManifest(snapshotDir, plan.shares)\n } finally {\n await stop()\n }\n },\n })\n }\n\n return { boot }\n}\n\ninterface ShareDaemons {\n /** Stop every daemon; resolves once all have exited. */\n stop: () => Promise<void>\n /** Stop the VMM if any daemon exits while it runs. */\n supervise: (vmm: ChildProcess, vmmExit: ExitWatcher) => void\n}\n\n/**\n * Cloud Hypervisor's control plane: HTTP/1.1 over the `--api-socket` Unix\n * socket. The plan has always created that socket; until now nothing spoke to\n * it, because stopping a VM needed nothing more than killing the process.\n *\n * Pausing and snapshotting do need it, and `ch-remote` — the tool that usually\n * does this — is not in the runner image. Writing the two requests by hand is a\n * smaller change than adding a binary to the image, and keeps the whole\n * lifecycle inside one process that already owns the socket path.\n */\nexport type VmmApiCall = (\n socketPath: string,\n path: string,\n body?: Record<string, unknown>,\n method?: 'PUT' | 'GET',\n) => Promise<void>\n\nconst vmmApi: VmmApiCall = async (socketPath, path, body, method = 'PUT') => {\n const payload = body === undefined ? undefined : Buffer.from(JSON.stringify(body), 'utf8')\n await new Promise<void>((resolve, reject) => {\n const request = httpRequest(\n {\n socketPath,\n path,\n method,\n headers: {\n ...(payload ? { 'content-type': 'application/json', 'content-length': String(payload.length) } : {}),\n },\n },\n (response) => {\n // CH answers 204 with no body on success. Drain regardless: an unread\n // response keeps the socket open and the next call queues behind it.\n const chunks: Buffer[] = []\n response.on('data', (chunk: Buffer) => chunks.push(chunk))\n response.on('end', () => {\n const status = response.statusCode ?? 0\n if (status >= 200 && status < 300) {\n resolve()\n return\n }\n const said = Buffer.concat(chunks).toString('utf8').trim().slice(0, 400)\n reject(new DeskError(`${path} returned ${status}${said === '' ? '' : `: ${said}`}`))\n })\n },\n )\n request.on('error', (error) => reject(new DeskError(`${path} failed: ${errorMessage(error)}`)))\n if (payload) request.write(payload)\n request.end()\n })\n}\n\n/**\n * Wait until the VMM answers its control socket.\n *\n * On a restore this is also the only honest signal that the snapshot was read\n * back: Cloud Hypervisor creates the API socket early but does not serve\n * `vm.info` until it holds a VM, so a successful call means the restore\n * completed rather than merely that the process started. A VMM that exits\n * instead — a corrupt snapshot, a config the host can no longer satisfy — stops\n * the wait immediately rather than burning the whole timeout.\n */\nasync function waitForVmm(\n call: VmmApiCall,\n socketPath: string,\n options: { now: () => number; exited: ExitWatcher; timeoutMs: number; retryDelayMs?: number },\n): Promise<void> {\n const deadline = options.now() + options.timeoutMs\n const retryDelayMs = options.retryDelayMs ?? 50\n let lastFailure = 'it never answered'\n for (;;) {\n if (options.exited.done) throw new DeskError(`the VMM exited before it served its control socket`)\n try {\n await call(socketPath, '/api/v1/vm.info', undefined, 'GET')\n return\n } catch (error) {\n lastFailure = errorMessage(error)\n }\n if (options.now() >= deadline) {\n throw new DeskError(`the VMM did not answer ${socketPath} in ${options.timeoutMs}ms: ${lastFailure}`)\n }\n await new Promise((resolve) => setTimeout(resolve, retryDelayMs))\n }\n}\n\n/** The default backend: real Cloud Hypervisor over a real vsock socket. */\nexport const cloudHypervisorBackend: DeskBackend = {\n boot: (plan, hooks) => createCloudHypervisorBackend().boot(plan, hooks),\n}\n"]}
package/host.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Buffer } from 'node:buffer';
2
2
  import type { Duplex } from 'node:stream';
3
- import { type DeskLauncherIdentity } from './plan.js';
3
+ import { type DeskLauncherIdentity, type DeskGuestNetworkOptions, type DeskShare, type DeskVirtiofsdSandbox } from './plan.js';
4
4
  import { type DeskBackend } from './backend.js';
5
5
  import type { A11yNode, DeskFrameFormat, DeskHandoverScope, DeskPoint, DeskPointerButton, DeskPorts, DeskVideoChunkKind, WindowInfo } from './events.js';
6
6
  export declare const DEFAULT_CAPACITY = 4;
@@ -34,6 +34,14 @@ export interface DeskHostOptions {
34
34
  runtimeDir?: string;
35
35
  launcherIdentity?: DeskLauncherIdentity;
36
36
  backend?: DeskBackend;
37
+ guestNetwork?: DeskGuestNetworkOptions;
38
+ /**
39
+ * Cloud Hypervisor only: the `virtiofsd` (1.13 or later) behind each share.
40
+ * Defaults to the first on `PATH`, then `/usr/libexec/virtiofsd`.
41
+ */
42
+ virtiofsdPath?: string;
43
+ /** Cloud Hypervisor only: how `virtiofsd` confines itself; defaults to `namespace`. */
44
+ virtiofsdSandbox?: DeskVirtiofsdSandbox;
37
45
  ports: DeskPorts;
38
46
  /** Override TCP dialing for tests or a host-specific transport. */
39
47
  connectGuest?: (address: string, port: number) => Promise<Duplex>;
@@ -46,6 +54,8 @@ export interface DeskHostOptions {
46
54
  now?: () => number;
47
55
  pathExists?: (path: string) => boolean;
48
56
  deviceExists?: (path: string) => boolean;
57
+ /** Whether a share's path is an existing directory; injectable for tests. */
58
+ isDirectory?: (path: string) => boolean;
49
59
  /**
50
60
  * Delete a destroyed desk's overlay. Injectable so the destroy path is
51
61
  * testable without a filesystem; defaults to `rm(path, { force: true })`.
@@ -71,6 +81,12 @@ export interface DeskHostOptions {
71
81
  cpuSignature?: () => Promise<string>;
72
82
  readSnapshotSignature?: (directory: string) => Promise<string | null>;
73
83
  writeSnapshotSignature?: (directory: string, signature: string) => Promise<void>;
84
+ /**
85
+ * The shares a parked desk was snapshotted with, which its restore uses in
86
+ * place of the start options' shares. Defaults to the record the backend
87
+ * writes beside the snapshot; a snapshot without one had no shares.
88
+ */
89
+ readSnapshotShares?: (directory: string) => Promise<readonly DeskShare[]>;
74
90
  /** Create a snapshot directory; defaults to `mkdir(path, { recursive: true })`. */
75
91
  makeSnapshotDir?: (path: string) => Promise<void>;
76
92
  /**
@@ -121,6 +137,14 @@ export interface DeskStartOptions {
121
137
  macAddress?: string;
122
138
  guestAddress?: string;
123
139
  };
140
+ /**
141
+ * Host directories shared into the guest over virtio-fs. A desk parked with
142
+ * memory is restored with the shares it was parked with, because a snapshot
143
+ * holds its devices; these take effect on its next cold boot (see
144
+ * `shutdown`). A parked share whose directory is gone makes the restore fail,
145
+ * and the desk cold boots with these instead.
146
+ */
147
+ shares?: readonly DeskShare[];
124
148
  }
125
149
  export interface DeskExecOptions {
126
150
  command: string;
@@ -398,6 +422,7 @@ export interface OverlayReflinkCheck {
398
422
  }
399
423
  /**
400
424
  * Whether the overlay directory's filesystem can make reflink clones.
425
+ * On macOS this probes APFS cloning with `cp -c`. On Linux:
401
426
  *
402
427
  * This exists because `cp --reflink=auto` — what `buildDeskLaunchPlan` uses,
403
428
  * deliberately, so a desk still boots on a filesystem without reflink — is
@@ -429,6 +454,7 @@ export interface DeskHostVerification {
429
454
  /** Whether this host can serve desks: KVM present and the guest reachable. */
430
455
  supported: boolean;
431
456
  vmmPath: string;
457
+ /** KVM availability; false on macOS, which uses Virtualization.framework. */
432
458
  kvm: boolean;
433
459
  vsock: boolean;
434
460
  virtioGpu: boolean;
package/host.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAML,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,SAAS,EACd,KAAK,oBAAoB,EAC1B,MAAM,QAAQ,CAAA;AACf,OAAO,EAEL,KAAK,WAAW,EAGjB,MAAM,WAAW,CAAA;AAYlB,OAAO,KAAK,EACV,QAAQ,EAGR,eAAe,EAEf,iBAAiB,EACjB,SAAS,EACT,iBAAiB,EACjB,SAAS,EACT,kBAAkB,EAClB,UAAU,EACX,MAAM,UAAU,CAAA;AAEjB,eAAO,MAAM,gBAAgB,IAAI,CAAA;AACjC,eAAO,MAAM,uBAAuB,QAAa,CAAA;AACjD,eAAO,MAAM,gBAAgB,QAAc,CAAA;AAC3C,sEAAsE;AACtE,eAAO,MAAM,oBAAoB,qBAAqB,CAAA;AACtD,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAAU,CAAA;AACzD,eAAO,MAAM,uBAAuB,QAAS,CAAA;AAC7C,eAAO,MAAM,gBAAgB,MAAM,CAAA;AA0BnC,MAAM,WAAW,eAAe;IAC9B,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,gBAAgB,CAAC,EAAE,oBAAoB,CAAA;IACvC,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,YAAY,CAAC,EAAE,uBAAuB,CAAA;IACtC;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,uFAAuF;IACvF,gBAAgB,CAAC,EAAE,oBAAoB,CAAA;IACvC,KAAK,EAAE,SAAS,CAAA;IAChB,mEAAmE;IACnE,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IACjE,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;IACtC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;IACxC,6EAA6E;IAC7E,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;IACvC;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/C;;;;;;;;;;;;;;OAcG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;IACpC,qBAAqB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IACrE,sBAAsB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAChF;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,SAAS,EAAE,CAAC,CAAA;IACzE,mFAAmF;IACnF,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACjD;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAChD;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC7C;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,gCAAgC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC5B,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,mEAAmE;IACnE,SAAS,EAAE,MAAM,CAAA;IACjB,0DAA0D;IAC1D,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC5E;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,SAAS,SAAS,EAAE,CAAA;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACtC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,yEAAyE;IACzE,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,OAAO,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAA;IAC5B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAA;IACrB,OAAO,EAAE,SAAS,UAAU,EAAE,CAAA;IAC9B,OAAO,EAAE,UAAU,GAAG,IAAI,CAAA;CAC3B;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,MAAM,EAAE,eAAe,CAAA;IACvB,EAAE,EAAE,MAAM,CAAA;CACX;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,kBAAkB,CAAA;IACxB,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,iFAAiF;IACjF,QAAQ,EAAE,OAAO,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;CACX;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzC,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,iBAAiB,EAAE,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtF,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACpD;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,IAAI,OAAO,CAAC,eAAe,CAAC,CAAA;IACnC;;;;;;;;;OASG;IACH,KAAK,EAAE,eAAe,CAAA;IACtB,IAAI,EAAE;QAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAA;IAC/D,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9D,SAAS,EAAE;QAAE,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;QAAC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAA;IAC1E;;;;;;;;;;OAUG;IACH,MAAM,CAAC,OAAO,CAAC,EAAE;QACf,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,MAAM,CAAC,EAAE,eAAe,CAAA;KACzB,GAAG,aAAa,CAAC,SAAS,CAAC,CAAA;IAC5B;;;;;;;;;;;;;;;;;OAiBG;IACH,KAAK,CAAC,OAAO,CAAC,EAAE;QACd,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,GAAG,aAAa,CAAC,cAAc,CAAC,CAAA;IACjC,QAAQ,EAAE;QACR,KAAK,CAAC,OAAO,EAAE;YACb,KAAK,EAAE,MAAM,CAAA;YACb,KAAK,CAAC,EAAE,iBAAiB,CAAA;YACzB,KAAK,CAAC,EAAE,MAAM,CAAA;SACf,GAAG,OAAO,CAAC;YAAE,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAC5B,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;QACpB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;KACzB,CAAA;CACF;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;IAC5E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAC1B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,sEAAsE;IACtE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IACtC,IAAI,CAAC,OAAO,EAAE,eAAe,GAAG;QAAE,SAAS,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACtE,IAAI,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;IACzD,0DAA0D;IAC1D,IAAI,IAAI,OAAO,EAAE,CAAA;IACjB,MAAM,EAAE,iBAAiB,CAAA;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAA;IAChB,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,2EAA2E;IAC3E,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,oBAAoB,EAAE,MAAM,CAAA;IAC5B,uBAAuB,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,oBAAoB,GAAG,oBAAoB,CAAA;KAAE,GAAG,IAAI,CAAA;CACpH;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;IACrD,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;IAC3C,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACvC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC,KAAK,IAAI,aAAa,CAAA;IACtB;;;;;OAKG;IACH,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;CACzB;AA2CD,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,QAAQ,CAwtCjE;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAA;IAClB,2EAA2E;IAC3E,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,gBAAgB,CAAC,EAAE,oBAAoB,CAAA;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAA;IAC1B,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;IACtC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;IACxC,SAAS,CAAC,EAAE,MAAM,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,+EAA+E;IAC/E,SAAS,EAAE,OAAO,CAAA;IAClB,gBAAgB,EAAE,MAAM,CAAA;IACxB,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE;IAClD,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,MAAM,CAAA;IACxB,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9D,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA4B/B;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE;IAClD,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,MAAM,CAAA;IACxB,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9D,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAa/B;AAcD,MAAM,WAAW,oBAAoB;IACnC,8EAA8E;IAC9E,SAAS,EAAE,OAAO,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,6EAA6E;IAC7E,GAAG,EAAE,OAAO,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;IACd,SAAS,EAAE,OAAO,CAAA;CACnB;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA8ElG;AAoDD,yFAAyF;AACzF,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEtE;AACD,4FAA4F;AAC5F,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,CAWxD"}
package/host.js CHANGED
@@ -11,13 +11,14 @@
11
11
  import { existsSync } from 'node:fs';
12
12
  import { execFile } from 'node:child_process';
13
13
  import { mkdir, readFile, rm, statfs, writeFile } from 'node:fs/promises';
14
- import { tmpdir } from 'node:os';
14
+ import { arch, cpus, release, tmpdir } from 'node:os';
15
15
  import { dirname, join } from 'node:path';
16
16
  import { createHash, randomUUID } from 'node:crypto';
17
17
  import { Buffer } from 'node:buffer';
18
18
  import { connect as tcpConnect, isIP } from 'node:net';
19
19
  import { buildDeskLaunchPlan, cleanDeskId, DEFAULT_KVM_PATH, DEFAULT_VMM_PATH, DeskError, } from './plan.js';
20
20
  import { cloudHypervisorBackend, } from './backend.js';
21
+ import { readShareManifest } from './machine.js';
21
22
  import { parseCapabilities, parseClipboardText, parseExecResult, parseHandoverBegun, parseJobStarted, parseObservation, } from './protocol.js';
22
23
  export const DEFAULT_CAPACITY = 4;
23
24
  export const DEFAULT_IDLE_SUSPEND_MS = 5 * 60_000;
@@ -77,7 +78,7 @@ export function createDeskHost(options) {
77
78
  // cloud kernel boots without every caller having to name it; an explicit
78
79
  // option always wins.
79
80
  const defaultInitramfs = join(options.imageRoot, 'initrd');
80
- const initramfsPath = options.initramfsPath ?? (pathExists(defaultInitramfs) ? defaultInitramfs : undefined);
81
+ const resolveInitramfsPath = () => options.initramfsPath ?? (pathExists(defaultInitramfs) ? defaultInitramfs : undefined);
81
82
  const kernelCmdline = options.kernelCmdline;
82
83
  const records = new Map();
83
84
  /**
@@ -419,8 +420,13 @@ export function createDeskHost(options) {
419
420
  // names its vsock and tap, and a cold-boot retry that renumbered them would
420
421
  // come up on a different tap than the one the egress rules were put on.
421
422
  const cid = nextCid++;
422
- const planFor = (restore) => buildDeskLaunchPlan({
423
+ const initramfsPath = resolveInitramfsPath();
424
+ const planFor = (restore, shares = startOptions.shares) => buildDeskLaunchPlan({
423
425
  deskId,
426
+ hypervisor: backend.hypervisor ?? 'cloud-hypervisor',
427
+ ...(backend.hypervisor === 'virtualization'
428
+ ? { guestNetwork: options.guestNetwork }
429
+ : { virtiofsdPath: options.virtiofsdPath, virtiofsdSandbox: options.virtiofsdSandbox }),
424
430
  vmmPath: options.vmmPath,
425
431
  kvmPath: options.kvmPath,
426
432
  kernelPath,
@@ -433,13 +439,23 @@ export function createDeskHost(options) {
433
439
  runtimeDir: options.runtimeDir,
434
440
  tapDevice: startOptions.network?.tapDevice,
435
441
  macAddress: startOptions.network?.macAddress,
442
+ shares,
436
443
  // Forward the host's cmdline only when set; otherwise the plan falls
437
444
  // back to DEFAULT_KERNEL_CMDLINE.
438
445
  ...(kernelCmdline === undefined ? {} : { kernelCmdline }),
439
446
  launcherIdentity: options.launcherIdentity,
440
447
  snapshotDir,
441
448
  ...(restore ? { restore: true } : {}),
442
- }, { pathExists, deviceExists });
449
+ }, { pathExists, deviceExists, ...(options.isDirectory ? { isDirectory: options.isDirectory } : {}) });
450
+ const restorePlan = async () => {
451
+ const parked = await (options.readSnapshotShares ?? readShareManifest)(snapshotDir);
452
+ try {
453
+ return planFor(true, parked);
454
+ }
455
+ catch (error) {
456
+ throw new DeskError(`its restore, with the shares it was parked with, could not be planned: ${errorMessage(error)}`);
457
+ }
458
+ };
443
459
  let resuming = parkWithMemory && pathExists(snapshotDir);
444
460
  if (resuming) {
445
461
  const saved = await (options.readSnapshotSignature ?? readSnapshotSignature)(snapshotDir);
@@ -452,7 +468,7 @@ export function createDeskHost(options) {
452
468
  let machine;
453
469
  try {
454
470
  machine = resuming
455
- ? await backend.boot(planFor(true), {
471
+ ? await backend.boot(await restorePlan(), {
456
472
  // The guest is restored and PAUSED here. Destroying the snapshot in
457
473
  // this window is what stops a memory image ever outliving the disk
458
474
  // state it belongs to. If it fails the restore is abandoned rather
@@ -762,12 +778,13 @@ export function createDeskHost(options) {
762
778
  requireMachine(record, epoch);
763
779
  if (!Number.isInteger(port) || port < 1 || port > 65535)
764
780
  throw new DeskError('Guest TCP port must be between 1 and 65535.');
781
+ const machine = requireMachine(record, epoch);
765
782
  const address = record.startOptions.network?.guestAddress;
766
- if (!address || isIP(address) !== 4)
783
+ if (!machine.connect && (!address || isIP(address) !== 4))
767
784
  throw new DeskError('A guest IPv4 address is required to connect to a desk port.');
768
785
  touch(record);
769
786
  try {
770
- const stream = await (options.connectGuest ?? connectGuestTcp)(address, port);
787
+ const stream = machine.connect ? await machine.connect(port) : await (options.connectGuest ?? connectGuestTcp)(address, port);
771
788
  try {
772
789
  requireMachine(record, epoch);
773
790
  }
@@ -1283,6 +1300,7 @@ export function createDeskHost(options) {
1283
1300
  }
1284
1301
  /**
1285
1302
  * Whether the overlay directory's filesystem can make reflink clones.
1303
+ * On macOS this probes APFS cloning with `cp -c`. On Linux:
1286
1304
  *
1287
1305
  * This exists because `cp --reflink=auto` — what `buildDeskLaunchPlan` uses,
1288
1306
  * deliberately, so a desk still boots on a filesystem without reflink — is
@@ -1315,7 +1333,7 @@ export async function verifyOverlayReflink(options) {
1315
1333
  + `support could not be determined: ${errorMessage(error)}`);
1316
1334
  }
1317
1335
  await copy(source, destination);
1318
- return { supported: true, overlayDirectory, detail: 'cp --reflink=always succeeded.' };
1336
+ return { supported: true, overlayDirectory, detail: process.platform === 'darwin' ? 'cp -c succeeded.' : 'cp --reflink=always succeeded.' };
1319
1337
  }
1320
1338
  catch (error) {
1321
1339
  if (error instanceof DeskError)
@@ -1334,16 +1352,18 @@ export async function verifyOverlayReflink(options) {
1334
1352
  export async function assertOverlayReflink(options) {
1335
1353
  const check = await verifyOverlayReflink(options);
1336
1354
  if (!check.supported) {
1337
- throw new DeskError(`The overlay directory ${check.overlayDirectory} is on a filesystem without reflink `
1338
- + 'support, so every desk would FULL-COPY the base image instead of sharing its '
1339
- + 'blocks — gigabytes and minutes per desk, and a disk sized for sharing fills after '
1340
- + `a handful of them. Put the overlay directory on XFS or Btrfs. Probe said: ${check.detail}`);
1355
+ throw new DeskError(process.platform === 'darwin'
1356
+ ? `The overlay directory ${check.overlayDirectory} does not support cloning. Put it on APFS. Probe said: ${check.detail}`
1357
+ : `The overlay directory ${check.overlayDirectory} is on a filesystem without reflink `
1358
+ + 'support, so every desk would FULL-COPY the base image instead of sharing its '
1359
+ + 'blocks — gigabytes and minutes per desk, and a disk sized for sharing fills after '
1360
+ + `a handful of them. Put the overlay directory on XFS or Btrfs. Probe said: ${check.detail}`);
1341
1361
  }
1342
1362
  return check;
1343
1363
  }
1344
1364
  async function reflinkAlways(source, destination) {
1345
1365
  await new Promise((resolvePromise, rejectPromise) => {
1346
- execFile('cp', ['--reflink=always', source, destination], (error, _stdout, stderr) => {
1366
+ execFile('cp', [process.platform === 'darwin' ? '-c' : '--reflink=always', source, destination], (error, _stdout, stderr) => {
1347
1367
  if (error) {
1348
1368
  rejectPromise(new Error(stderr.trim() || errorMessage(error)));
1349
1369
  return;
@@ -1366,13 +1386,14 @@ export async function verifyDeskHost(options) {
1366
1386
  const deviceExists = options.deviceExists ?? existsSync;
1367
1387
  const idFactory = options.idFactory ?? randomUUID;
1368
1388
  const backend = options.backend ?? cloudHypervisorBackend;
1369
- const vmmPath = options.vmmPath ?? DEFAULT_VMM_PATH;
1389
+ const isVirtualization = backend.hypervisor === 'virtualization';
1390
+ const vmmPath = options.vmmPath ?? backend.vmmPath ?? DEFAULT_VMM_PATH;
1370
1391
  const kvmPath = options.kvmPath ?? DEFAULT_KVM_PATH;
1371
- if (platform !== 'linux') {
1372
- throw new DeskError(`The AppKit desk requires Linux; received ${platform}.`);
1392
+ if (platform !== (isVirtualization ? 'darwin' : 'linux')) {
1393
+ throw new DeskError(isVirtualization ? `Virtualization requires macOS; received ${platform}.` : `The AppKit desk requires Linux; received ${platform}.`);
1373
1394
  }
1374
1395
  if (!pathExists(vmmPath)) {
1375
- throw new DeskError(`Cloud Hypervisor was not found at ${vmmPath}.`);
1396
+ throw new DeskError(isVirtualization ? `Virtualization helper was not found at ${vmmPath}.` : `Cloud Hypervisor was not found at ${vmmPath}.`);
1376
1397
  }
1377
1398
  if (!pathExists(options.kernelPath)) {
1378
1399
  throw new DeskError(`Guest kernel does not exist: ${options.kernelPath}`);
@@ -1380,7 +1401,7 @@ export async function verifyDeskHost(options) {
1380
1401
  if (!pathExists(options.baseImagePath)) {
1381
1402
  throw new DeskError(`Base image does not exist: ${options.baseImagePath}`);
1382
1403
  }
1383
- if (!deviceExists(kvmPath)) {
1404
+ if (!isVirtualization && !deviceExists(kvmPath)) {
1384
1405
  return { supported: false, vmmPath, kvm: false, vsock: false, virtioGpu: false };
1385
1406
  }
1386
1407
  const scratchDir = options.scratchDir ?? tmpdir();
@@ -1388,6 +1409,7 @@ export async function verifyDeskHost(options) {
1388
1409
  const overlayPath = join(scratchDir, `${deskId}.raw`);
1389
1410
  const plan = buildDeskLaunchPlan({
1390
1411
  deskId,
1412
+ hypervisor: backend.hypervisor ?? 'cloud-hypervisor',
1391
1413
  vmmPath,
1392
1414
  kvmPath,
1393
1415
  kernelPath: options.kernelPath,
@@ -1434,7 +1456,7 @@ export async function verifyDeskHost(options) {
1434
1456
  }
1435
1457
  await rm(overlayPath, { force: true });
1436
1458
  }
1437
- return { supported: vsock, vmmPath, kvm: true, vsock, virtioGpu };
1459
+ return { supported: vsock, vmmPath, kvm: !isVirtualization, vsock, virtioGpu };
1438
1460
  }
1439
1461
  function nonEmptyString(value, field) {
1440
1462
  if (typeof value !== 'string' || value.length === 0) {
@@ -1485,6 +1507,12 @@ export function isIncompatibleCpuSnapshotError(error) {
1485
1507
  }
1486
1508
  /** Hash the host's CPU identity and advertised feature set, excluding per-core counters. */
1487
1509
  export async function hostCpuSignature() {
1510
+ if (process.platform === 'darwin') {
1511
+ const model = cpus()[0]?.model;
1512
+ if (!model)
1513
+ throw new DeskError('Cannot determine host CPU signature.');
1514
+ return createHash('sha256').update(`darwin:${arch()}:${release()}:${model}`).digest('hex');
1515
+ }
1488
1516
  const cpuinfo = await readFile('/proc/cpuinfo', 'utf8');
1489
1517
  const first = cpuinfo.split(/\n\s*\n/)[0] ?? '';
1490
1518
  const identity = first.split('\n').filter((line) => /^(vendor_id|cpu family|model\s|model name|stepping|flags|microcode)\s*:/.test(line)).join('\n');