@braedonsaunders/appkit-desk 0.10.0 → 0.11.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.
- package/README.md +117 -0
- package/backend.d.ts +13 -1
- package/backend.d.ts.map +1 -1
- package/backend.js +23 -9
- package/backend.js.map +1 -1
- package/host.d.ts +33 -5
- package/host.d.ts.map +1 -1
- package/host.js +216 -18
- package/host.js.map +1 -1
- package/index.d.ts +3 -0
- package/index.d.ts.map +1 -1
- package/index.js +3 -0
- package/index.js.map +1 -1
- package/machine.d.ts +4 -2
- package/machine.d.ts.map +1 -1
- package/machine.js +5 -3
- package/machine.js.map +1 -1
- package/package.json +1 -1
- package/plan.d.ts +6 -1
- package/plan.d.ts.map +1 -1
- package/plan.js +7 -3
- package/plan.js.map +1 -1
- package/template-mapping.d.ts +5 -0
- package/template-mapping.d.ts.map +1 -0
- package/template-mapping.js +48 -0
- package/template-mapping.js.map +1 -0
- package/template-plan.d.ts +18 -0
- package/template-plan.d.ts.map +1 -0
- package/template-plan.js +52 -0
- package/template-plan.js.map +1 -0
- package/template-registry.d.ts +37 -0
- package/template-registry.d.ts.map +1 -0
- package/template-registry.js +190 -0
- package/template-registry.js.map +1 -0
- package/template-source.d.ts +15 -0
- package/template-source.d.ts.map +1 -0
- package/template-source.js +95 -0
- package/template-source.js.map +1 -0
package/index.d.ts
CHANGED
|
@@ -10,4 +10,7 @@ export * from './conformance.js';
|
|
|
10
10
|
export * from './network.js';
|
|
11
11
|
export * from './virtualization.js';
|
|
12
12
|
export * from './port-relay.js';
|
|
13
|
+
export * from './template-source.js';
|
|
14
|
+
export * from './template-plan.js';
|
|
15
|
+
export * from './template-registry.js';
|
|
13
16
|
//# sourceMappingURL=index.d.ts.map
|
package/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,QAAQ,CAAA;AACtB,cAAc,eAAe,CAAA;AAC7B,cAAc,WAAW,CAAA;AACzB,cAAc,QAAQ,CAAA;AACtB,cAAc,UAAU,CAAA;AACxB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,WAAW,CAAA;AAEzB,cAAc,kBAAkB,CAAA;AAChC,cAAc,cAAc,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,QAAQ,CAAA;AACtB,cAAc,eAAe,CAAA;AAC7B,cAAc,WAAW,CAAA;AACzB,cAAc,QAAQ,CAAA;AACtB,cAAc,UAAU,CAAA;AACxB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,WAAW,CAAA;AAEzB,cAAc,kBAAkB,CAAA;AAChC,cAAc,cAAc,CAAA;AAE5B,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,qBAAqB,CAAA"}
|
package/index.js
CHANGED
|
@@ -10,4 +10,7 @@ export * from './conformance.js';
|
|
|
10
10
|
export * from './network.js';
|
|
11
11
|
export * from './virtualization.js';
|
|
12
12
|
export * from './port-relay.js';
|
|
13
|
+
export * from './template-source.js';
|
|
14
|
+
export * from './template-plan.js';
|
|
15
|
+
export * from './template-registry.js';
|
|
13
16
|
//# sourceMappingURL=index.js.map
|
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,QAAQ,CAAA;AACtB,cAAc,eAAe,CAAA;AAC7B,cAAc,WAAW,CAAA;AACzB,cAAc,QAAQ,CAAA;AACtB,cAAc,UAAU,CAAA;AACxB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,WAAW,CAAA;AAEzB,cAAc,kBAAkB,CAAA;AAChC,cAAc,cAAc,CAAA","sourcesContent":["export * from './events'\nexport * from './protocol'\nexport * from './plan'\nexport * from './guest-agent'\nexport * from './backend'\nexport * from './host'\nexport * from './frames'\nexport * from './portable-home'\nexport * from './conformance'\nexport * from './network'\n\nexport * from './virtualization'\nexport * from './port-relay'\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,QAAQ,CAAA;AACtB,cAAc,eAAe,CAAA;AAC7B,cAAc,WAAW,CAAA;AACzB,cAAc,QAAQ,CAAA;AACtB,cAAc,UAAU,CAAA;AACxB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,WAAW,CAAA;AAEzB,cAAc,kBAAkB,CAAA;AAChC,cAAc,cAAc,CAAA;AAE5B,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,qBAAqB,CAAA","sourcesContent":["export * from './events'\nexport * from './protocol'\nexport * from './plan'\nexport * from './guest-agent'\nexport * from './backend'\nexport * from './host'\nexport * from './frames'\nexport * from './portable-home'\nexport * from './conformance'\nexport * from './network'\n\nexport * from './virtualization'\nexport * from './port-relay'\n\nexport * from './template-source'\nexport * from './template-plan'\nexport * from './template-registry'\n"]}
|
package/machine.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { type ChildProcess } from 'node:child_process';
|
|
10
10
|
import type { Duplex } from 'node:stream';
|
|
11
|
-
import { type DeskMachine } from './backend.js';
|
|
11
|
+
import { type DeskMachine, type DeskParkHooks } from './backend.js';
|
|
12
12
|
import { type DeskLauncherIdentity, type DeskShare } from './plan.js';
|
|
13
13
|
export declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
14
14
|
export declare const DEFAULT_CONNECT_TIMEOUT_MS = 20000;
|
|
@@ -63,7 +63,8 @@ export declare function createGuestMachine(options: {
|
|
|
63
63
|
* Pause, snapshot into `snapshotDir`, and stop the VMM. Called after the
|
|
64
64
|
* guest channel has been closed. Must leave the VMM stopped on failure too.
|
|
65
65
|
*/
|
|
66
|
-
park?: (snapshotDir: string) => Promise<void>;
|
|
66
|
+
park?: (snapshotDir: string, hooks?: DeskParkHooks) => Promise<void>;
|
|
67
|
+
supportsPausedParkHook?: true;
|
|
67
68
|
pause?: () => Promise<void>;
|
|
68
69
|
resume?: () => Promise<void>;
|
|
69
70
|
/** Open a host-to-guest TCP stream, for backends with no routable guest. */
|
|
@@ -149,6 +150,7 @@ export declare function readShareManifest(snapshotDir: string): Promise<DeskShar
|
|
|
149
150
|
export declare function assertShareManifest(snapshotDir: string, shares: readonly DeskShare[]): Promise<void>;
|
|
150
151
|
export declare function defaultLauncher(command: string, args: readonly string[], options: {
|
|
151
152
|
identity?: DeskLauncherIdentity;
|
|
153
|
+
inheritedFds?: readonly number[];
|
|
152
154
|
}): ChildProcess;
|
|
153
155
|
export declare function defaultConnect(socketPath: string): Duplex;
|
|
154
156
|
export declare function delay(ms: number): Promise<void>;
|
package/machine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"machine.d.ts","sourceRoot":"","sources":["../src/machine.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAK7D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAA0D,KAAK,WAAW,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"machine.d.ts","sourceRoot":"","sources":["../src/machine.ts"],"names":[],"mappings":"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"}
|
package/machine.js
CHANGED
|
@@ -269,14 +269,16 @@ export function createGuestMachine(options) {
|
|
|
269
269
|
}
|
|
270
270
|
if (options.park) {
|
|
271
271
|
const park = options.park;
|
|
272
|
-
|
|
272
|
+
if (options.supportsPausedParkHook)
|
|
273
|
+
machine.supportsPausedParkHook = true;
|
|
274
|
+
machine.park = async (snapshotDir, hooks) => {
|
|
273
275
|
// Close the guest channel FIRST. Pausing a VM strands whatever was in
|
|
274
276
|
// flight on the vsock, and a reconnect attempt against a paused guest
|
|
275
277
|
// would burn the whole reconnect window before giving up — so the channel
|
|
276
278
|
// is made terminal before the VM stops answering, exactly as shutdown
|
|
277
279
|
// does it.
|
|
278
280
|
close(new DeskError(`Desk ${deskId} is being parked.`), true);
|
|
279
|
-
await park(snapshotDir);
|
|
281
|
+
await park(snapshotDir, hooks);
|
|
280
282
|
};
|
|
281
283
|
}
|
|
282
284
|
if (options.connect) {
|
|
@@ -594,7 +596,7 @@ function shareManifest(shares) {
|
|
|
594
596
|
}
|
|
595
597
|
export function defaultLauncher(command, args, options) {
|
|
596
598
|
return spawn(command, [...args], {
|
|
597
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
599
|
+
stdio: ['ignore', 'pipe', 'pipe', ...(options.inheritedFds ?? [])],
|
|
598
600
|
uid: options.identity?.uid,
|
|
599
601
|
gid: options.identity?.gid,
|
|
600
602
|
});
|
package/machine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"machine.js","sourceRoot":"","sources":["../src/machine.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The guest channel every backend shares: the hybrid-vsock handshake, the\n * proof that a guest is really behind it, the reconnecting machine, and the\n * child-process plumbing around a VMM. Cloud Hypervisor and the\n * Virtualization.framework helper both expose the guest agent through a host\n * Unix socket with the same `CONNECT <port>` preamble, so everything from the\n * socket inwards is one implementation. Internal to the package.\n */\nimport { spawn, type ChildProcess } from 'node:child_process'\nimport { readFile, writeFile } from 'node:fs/promises'\nimport { createConnection } from 'node:net'\nimport { join } from 'node:path'\nimport { Buffer } from 'node:buffer'\nimport type { Duplex } from 'node:stream'\nimport { DeskRequestFateUnknownError, type DeskConnectionChange, type DeskMachine } from './backend'\nimport { DeskError, type DeskLauncherIdentity, type DeskShare } from './plan'\nimport {\n encodeFrame,\n FrameDecoder,\n parseHostBoundMessage,\n type GuestEventMessage,\n type HostBoundMessage,\n} from './protocol'\n\nexport const DEFAULT_REQUEST_TIMEOUT_MS = 30_000\nexport const DEFAULT_CONNECT_TIMEOUT_MS = 20_000\nexport const DEFAULT_CONNECT_RETRY_DELAY_MS = 100\nexport const DEFAULT_HANDSHAKE_TIMEOUT_MS = 5_000\nexport const DEFAULT_RECONNECT_WINDOW_MS = 30_000\nexport const 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 */\nexport const RECONNECT_BACKOFF_CEILING = 8\nexport const DEFAULT_KILL_GRACE_MS = 2_000\nconst HANDSHAKE_LINE_LIMIT = 128\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 *\n * What differs between hypervisors is only how the VM stops, parks, and\n * reaches guest ports, and those arrive as functions.\n */\nexport function createGuestMachine(options: {\n deskId: string\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 /** Stop the VMM; resolves once its process has exited. */\n stop: () => Promise<void>\n /**\n * Pause, snapshot into `snapshotDir`, and stop the VMM. Called after the\n * guest channel has been closed. Must leave the VMM stopped on failure too.\n */\n park?: (snapshotDir: string) => Promise<void>\n pause?: () => Promise<void>\n resume?: () => Promise<void>\n /** Open a host-to-guest TCP stream, for backends with no routable guest. */\n connect?: (port: number) => Promise<Duplex>\n}): DeskMachine {\n const { deskId, exit, now, idFactory, requestTimeoutMs, reconnectWindowMs } = 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 let paused = false\n let deferredDrop: { reason: string; at: number } | 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 if (paused) deferredDrop = { reason, at: lostAt }\n else 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 const machine: DeskMachine = {\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 if (paused) throw new DeskError(`Desk ${deskId} is paused; resume it first.`)\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 (paused) throw new DeskError(`Desk ${deskId} is paused; resume it first.`)\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 options.stop()\n },\n }\n if (options.pause && options.resume) {\n const pause = options.pause\n const resume = options.resume\n machine.pause = async () => {\n if (paused) return\n if (state !== 'live') throw new DeskError(`Desk ${deskId} is not connected.`)\n paused = true\n failAll(new DeskRequestFateUnknownError(`Desk ${deskId} paused with a request in flight; its fate is unknown.`))\n // On failure the VMM state may be unknown; retain the fence until resume succeeds.\n await pause()\n }\n machine.resume = async () => {\n if (!paused) return\n await resume()\n paused = false\n if (deferredDrop && state === 'reconnecting') {\n const drop = deferredDrop\n deferredDrop = null\n reconnecting = restore(drop.reason, drop.at)\n }\n }\n }\n if (options.park) {\n const park = options.park\n machine.park = async (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 await park(snapshotDir)\n }\n }\n if (options.connect) {\n const connect = options.connect\n machine.connect = async (port: number) => {\n if (paused) throw new DeskError(`Desk ${deskId} is paused; resume it first.`)\n if (state === 'closed') throw new DeskError(`Desk ${deskId} is no longer connected.`)\n const connection = await connect(port)\n if ((state as string) === 'closed') { connection.destroy(); throw new DeskError(`Desk ${deskId} is no longer connected.`) }\n return connection\n }\n }\n return machine\n}\n\nexport interface ExitWatcher {\n done: boolean\n promise: Promise<void>\n}\n\n/**\n * Read a child's output and keep only the last few kilobytes.\n *\n * This is not for logging — it is load-bearing. A child spawned with its\n * streams as PIPES blocks once a pipe nobody reads fills, after about 64KB.\n * Cloud Hypervisor logs while it runs, so an unread pipe freezes the guest\n * partway through boot: the process is alive, the disk is untouched, and the\n * guest agent never reaches vsock. Every symptom points at the image and none\n * of them are its fault.\n *\n * Draining costs nothing and buys the tail of what the process said, which is\n * the difference between \"the VMM exited\" and knowing why. `streams` names the\n * pipes to drain: a helper that speaks a protocol on stdout drains only stderr.\n */\nexport function drainOutput(\n child: ChildProcess,\n limit = 8 * 1024,\n streams: readonly ('stdout' | 'stderr')[] = ['stdout', 'stderr'],\n): { 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 for (const name of streams) {\n const pipe = child[name]\n pipe?.on('data', absorb)\n // A pipe that errors must not take the process down with it.\n pipe?.on('error', () => undefined)\n }\n return { tail: () => buffered.trim() }\n}\n\nexport function 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\nexport async 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\nexport function 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. The Virtualization.framework helper speaks the\n * same preamble on its own socket.\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 */\nexport async 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 * 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\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 * A close before the reply line is a refusal: that is how a port with nothing\n * listening presents through Cloud Hypervisor, and the Virtualization.framework\n * helper presents a refused vsock or TCP port the same way.\n */\nexport function 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\n/**\n * The shares a snapshot was taken with, kept beside it.\n *\n * A snapshot holds the machine's devices, shares among them, so it can only\n * come back with the shares it was parked with: Cloud Hypervisor would resume\n * without a share that was added since, and Virtualization.framework refuses a\n * changed device list. The host restores with this list rather than the one it\n * was given, and a backend refuses a plan that disagrees with it.\n */\nconst SHARE_MANIFEST = 'shares.json'\n\nexport async function writeShareManifest(snapshotDir: string, shares: readonly DeskShare[]): Promise<void> {\n await writeFile(join(snapshotDir, SHARE_MANIFEST), `${shareManifest(shares)}\\n`, { mode: 0o600 })\n}\n\n/**\n * The shares recorded beside a snapshot. A snapshot with no record predates\n * shares, so it was taken with none. Paths are returned as recorded; whether\n * they still exist is the launch plan's to check.\n */\nexport async function readShareManifest(snapshotDir: string): Promise<DeskShare[]> {\n let text: string\n try {\n text = await readFile(join(snapshotDir, SHARE_MANIFEST), 'utf8')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return []\n throw error\n }\n let value: unknown\n try {\n value = JSON.parse(text)\n } catch {\n throw new DeskError(`${join(snapshotDir, SHARE_MANIFEST)} is not valid JSON.`)\n }\n const valid = Array.isArray(value) && value.every((entry: unknown) =>\n typeof entry === 'object' && entry !== null\n && typeof (entry as DeskShare).tag === 'string'\n && typeof (entry as DeskShare).path === 'string'\n && typeof (entry as DeskShare).readOnly === 'boolean')\n if (!valid) throw new DeskError(`${join(snapshotDir, SHARE_MANIFEST)} is not a list of shares.`)\n return (value as DeskShare[]).map(({ tag, path, readOnly }) => ({ tag, path, readOnly }))\n}\n\n/** Throws unless the snapshot was parked with exactly `shares`, in order. */\nexport async function assertShareManifest(snapshotDir: string, shares: readonly DeskShare[]): Promise<void> {\n const saved = shareManifest(await readShareManifest(snapshotDir))\n const wanted = shareManifest(shares)\n if (saved !== wanted) throw new DeskError(`the snapshot was taken with shares ${saved}, not ${wanted}`)\n}\n\nfunction shareManifest(shares: readonly DeskShare[]): string {\n return JSON.stringify(shares.map(({ tag, path, readOnly }) => ({ tag, path, readOnly })))\n}\n\nexport function 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\nexport function defaultConnect(socketPath: string): Duplex {\n return createConnection(socketPath)\n}\n\nexport function delay(ms: number): Promise<void> {\n return new Promise<void>((resolvePromise) => {\n const timer = setTimeout(resolvePromise, ms)\n timer.unref()\n })\n}\n\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"machine.js","sourceRoot":"","sources":["../src/machine.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The guest channel every backend shares: the hybrid-vsock handshake, the\n * proof that a guest is really behind it, the reconnecting machine, and the\n * child-process plumbing around a VMM. Cloud Hypervisor and the\n * Virtualization.framework helper both expose the guest agent through a host\n * Unix socket with the same `CONNECT <port>` preamble, so everything from the\n * socket inwards is one implementation. Internal to the package.\n */\nimport { spawn, type ChildProcess } from 'node:child_process'\nimport { readFile, writeFile } from 'node:fs/promises'\nimport { createConnection } from 'node:net'\nimport { join } from 'node:path'\nimport { Buffer } from 'node:buffer'\nimport type { Duplex } from 'node:stream'\nimport { DeskRequestFateUnknownError, type DeskConnectionChange, type DeskMachine, type DeskParkHooks } from './backend'\nimport { DeskError, type DeskLauncherIdentity, type DeskShare } from './plan'\nimport {\n encodeFrame,\n FrameDecoder,\n parseHostBoundMessage,\n type GuestEventMessage,\n type HostBoundMessage,\n} from './protocol'\n\nexport const DEFAULT_REQUEST_TIMEOUT_MS = 30_000\nexport const DEFAULT_CONNECT_TIMEOUT_MS = 20_000\nexport const DEFAULT_CONNECT_RETRY_DELAY_MS = 100\nexport const DEFAULT_HANDSHAKE_TIMEOUT_MS = 5_000\nexport const DEFAULT_RECONNECT_WINDOW_MS = 30_000\nexport const 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 */\nexport const RECONNECT_BACKOFF_CEILING = 8\nexport const DEFAULT_KILL_GRACE_MS = 2_000\nconst HANDSHAKE_LINE_LIMIT = 128\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 *\n * What differs between hypervisors is only how the VM stops, parks, and\n * reaches guest ports, and those arrive as functions.\n */\nexport function createGuestMachine(options: {\n deskId: string\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 /** Stop the VMM; resolves once its process has exited. */\n stop: () => Promise<void>\n /**\n * Pause, snapshot into `snapshotDir`, and stop the VMM. Called after the\n * guest channel has been closed. Must leave the VMM stopped on failure too.\n */\n park?: (snapshotDir: string, hooks?: DeskParkHooks) => Promise<void>\n supportsPausedParkHook?: true\n pause?: () => Promise<void>\n resume?: () => Promise<void>\n /** Open a host-to-guest TCP stream, for backends with no routable guest. */\n connect?: (port: number) => Promise<Duplex>\n}): DeskMachine {\n const { deskId, exit, now, idFactory, requestTimeoutMs, reconnectWindowMs } = 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 let paused = false\n let deferredDrop: { reason: string; at: number } | 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 if (paused) deferredDrop = { reason, at: lostAt }\n else 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 const machine: DeskMachine = {\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 if (paused) throw new DeskError(`Desk ${deskId} is paused; resume it first.`)\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 (paused) throw new DeskError(`Desk ${deskId} is paused; resume it first.`)\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 options.stop()\n },\n }\n if (options.pause && options.resume) {\n const pause = options.pause\n const resume = options.resume\n machine.pause = async () => {\n if (paused) return\n if (state !== 'live') throw new DeskError(`Desk ${deskId} is not connected.`)\n paused = true\n failAll(new DeskRequestFateUnknownError(`Desk ${deskId} paused with a request in flight; its fate is unknown.`))\n // On failure the VMM state may be unknown; retain the fence until resume succeeds.\n await pause()\n }\n machine.resume = async () => {\n if (!paused) return\n await resume()\n paused = false\n if (deferredDrop && state === 'reconnecting') {\n const drop = deferredDrop\n deferredDrop = null\n reconnecting = restore(drop.reason, drop.at)\n }\n }\n }\n if (options.park) {\n const park = options.park\n if (options.supportsPausedParkHook) machine.supportsPausedParkHook = true\n machine.park = async (snapshotDir: string, hooks?: DeskParkHooks) => {\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 await park(snapshotDir, hooks)\n }\n }\n if (options.connect) {\n const connect = options.connect\n machine.connect = async (port: number) => {\n if (paused) throw new DeskError(`Desk ${deskId} is paused; resume it first.`)\n if (state === 'closed') throw new DeskError(`Desk ${deskId} is no longer connected.`)\n const connection = await connect(port)\n if ((state as string) === 'closed') { connection.destroy(); throw new DeskError(`Desk ${deskId} is no longer connected.`) }\n return connection\n }\n }\n return machine\n}\n\nexport interface ExitWatcher {\n done: boolean\n promise: Promise<void>\n}\n\n/**\n * Read a child's output and keep only the last few kilobytes.\n *\n * This is not for logging — it is load-bearing. A child spawned with its\n * streams as PIPES blocks once a pipe nobody reads fills, after about 64KB.\n * Cloud Hypervisor logs while it runs, so an unread pipe freezes the guest\n * partway through boot: the process is alive, the disk is untouched, and the\n * guest agent never reaches vsock. Every symptom points at the image and none\n * of them are its fault.\n *\n * Draining costs nothing and buys the tail of what the process said, which is\n * the difference between \"the VMM exited\" and knowing why. `streams` names the\n * pipes to drain: a helper that speaks a protocol on stdout drains only stderr.\n */\nexport function drainOutput(\n child: ChildProcess,\n limit = 8 * 1024,\n streams: readonly ('stdout' | 'stderr')[] = ['stdout', 'stderr'],\n): { 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 for (const name of streams) {\n const pipe = child[name]\n pipe?.on('data', absorb)\n // A pipe that errors must not take the process down with it.\n pipe?.on('error', () => undefined)\n }\n return { tail: () => buffered.trim() }\n}\n\nexport function 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\nexport async 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\nexport function 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. The Virtualization.framework helper speaks the\n * same preamble on its own socket.\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 */\nexport async 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 * 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\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 * A close before the reply line is a refusal: that is how a port with nothing\n * listening presents through Cloud Hypervisor, and the Virtualization.framework\n * helper presents a refused vsock or TCP port the same way.\n */\nexport function 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\n/**\n * The shares a snapshot was taken with, kept beside it.\n *\n * A snapshot holds the machine's devices, shares among them, so it can only\n * come back with the shares it was parked with: Cloud Hypervisor would resume\n * without a share that was added since, and Virtualization.framework refuses a\n * changed device list. The host restores with this list rather than the one it\n * was given, and a backend refuses a plan that disagrees with it.\n */\nconst SHARE_MANIFEST = 'shares.json'\n\nexport async function writeShareManifest(snapshotDir: string, shares: readonly DeskShare[]): Promise<void> {\n await writeFile(join(snapshotDir, SHARE_MANIFEST), `${shareManifest(shares)}\\n`, { mode: 0o600 })\n}\n\n/**\n * The shares recorded beside a snapshot. A snapshot with no record predates\n * shares, so it was taken with none. Paths are returned as recorded; whether\n * they still exist is the launch plan's to check.\n */\nexport async function readShareManifest(snapshotDir: string): Promise<DeskShare[]> {\n let text: string\n try {\n text = await readFile(join(snapshotDir, SHARE_MANIFEST), 'utf8')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return []\n throw error\n }\n let value: unknown\n try {\n value = JSON.parse(text)\n } catch {\n throw new DeskError(`${join(snapshotDir, SHARE_MANIFEST)} is not valid JSON.`)\n }\n const valid = Array.isArray(value) && value.every((entry: unknown) =>\n typeof entry === 'object' && entry !== null\n && typeof (entry as DeskShare).tag === 'string'\n && typeof (entry as DeskShare).path === 'string'\n && typeof (entry as DeskShare).readOnly === 'boolean')\n if (!valid) throw new DeskError(`${join(snapshotDir, SHARE_MANIFEST)} is not a list of shares.`)\n return (value as DeskShare[]).map(({ tag, path, readOnly }) => ({ tag, path, readOnly }))\n}\n\n/** Throws unless the snapshot was parked with exactly `shares`, in order. */\nexport async function assertShareManifest(snapshotDir: string, shares: readonly DeskShare[]): Promise<void> {\n const saved = shareManifest(await readShareManifest(snapshotDir))\n const wanted = shareManifest(shares)\n if (saved !== wanted) throw new DeskError(`the snapshot was taken with shares ${saved}, not ${wanted}`)\n}\n\nfunction shareManifest(shares: readonly DeskShare[]): string {\n return JSON.stringify(shares.map(({ tag, path, readOnly }) => ({ tag, path, readOnly })))\n}\n\nexport function defaultLauncher(\n command: string,\n args: readonly string[],\n options: { identity?: DeskLauncherIdentity; inheritedFds?: readonly number[] },\n): ChildProcess {\n return spawn(command, [...args], {\n stdio: ['ignore', 'pipe', 'pipe', ...(options.inheritedFds ?? [])],\n uid: options.identity?.uid,\n gid: options.identity?.gid,\n })\n}\n\nexport function defaultConnect(socketPath: string): Duplex {\n return createConnection(socketPath)\n}\n\nexport function delay(ms: number): Promise<void> {\n return new Promise<void>((resolvePromise) => {\n const timer = setTimeout(resolvePromise, ms)\n timer.unref()\n })\n}\n\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@braedonsaunders/appkit-desk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Per-agent Debian microVMs on Linux and macOS, with a framed guest-agent protocol, leases, snapshots, and governed networking.",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"type": "module",
|
package/plan.d.ts
CHANGED
|
@@ -171,7 +171,7 @@ export interface DeskLaunchPlanOptions {
|
|
|
171
171
|
*/
|
|
172
172
|
restore?: boolean;
|
|
173
173
|
/** Cloud Hypervisor copy-on-write restore; the source must remain unchanged while resident. */
|
|
174
|
-
memoryRestoreMode?: 'copyonwrite';
|
|
174
|
+
memoryRestoreMode?: 'copyonwrite' | 'ondemand';
|
|
175
175
|
}
|
|
176
176
|
/** What every plan carries, whichever hypervisor runs it. */
|
|
177
177
|
export interface DeskLaunchPlanBase {
|
|
@@ -198,6 +198,7 @@ export interface DeskLaunchPlanBase {
|
|
|
198
198
|
snapshot: {
|
|
199
199
|
dir: string;
|
|
200
200
|
restoring: boolean;
|
|
201
|
+
requirePrivateMemoryMapping?: boolean;
|
|
201
202
|
};
|
|
202
203
|
/** Host directories shared into the guest, in the order they were given. */
|
|
203
204
|
shares: readonly DeskShare[];
|
|
@@ -311,4 +312,8 @@ export declare function buildDeskLaunchPlan(options: DeskLaunchPlanOptions, depe
|
|
|
311
312
|
/** Reject ambiguous Cloud Hypervisor key-value arguments and unpinned images. */
|
|
312
313
|
export declare function cleanAttachments(attachments: readonly DeskReadOnlyAttachment[] | undefined, pathExists: (path: string) => boolean): DeskReadOnlyAttachment[];
|
|
313
314
|
export declare function cleanLauncherIdentity(identity: DeskLauncherIdentity | undefined): DeskLauncherIdentity | undefined;
|
|
315
|
+
/** Snapshot restore modes implemented with source lifetime and failure fencing. */
|
|
316
|
+
export declare const DESK_MEMORY_RESTORE_MODES: readonly ["copyonwrite", "ondemand"];
|
|
317
|
+
/** Suspend hooks include positive completion after snapshot durability. */
|
|
318
|
+
export declare const DESK_PARK_HOOK_VERSION: 1;
|
|
314
319
|
//# sourceMappingURL=plan.d.ts.map
|
package/plan.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"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"}
|
package/plan.js
CHANGED
|
@@ -85,8 +85,8 @@ export function cleanDeskId(value) {
|
|
|
85
85
|
}
|
|
86
86
|
export function buildDeskLaunchPlan(options, dependencies = {}) {
|
|
87
87
|
const hypervisor = options.hypervisor ?? 'cloud-hypervisor';
|
|
88
|
-
if (options.memoryRestoreMode !== undefined && (options.memoryRestoreMode
|
|
89
|
-
throw new DeskError('
|
|
88
|
+
if (options.memoryRestoreMode !== undefined && (!['copyonwrite', 'ondemand'].includes(options.memoryRestoreMode) || hypervisor !== 'cloud-hypervisor')) {
|
|
89
|
+
throw new DeskError('Memory restore mode requires Cloud Hypervisor and must be copyonwrite or ondemand.');
|
|
90
90
|
}
|
|
91
91
|
if (hypervisor === 'virtualization')
|
|
92
92
|
return buildVirtualizationPlan(options, dependencies);
|
|
@@ -206,7 +206,7 @@ function buildCloudHypervisorPlan(options, dependencies) {
|
|
|
206
206
|
const args = restoring
|
|
207
207
|
? [
|
|
208
208
|
'--api-socket', apiSocketPath,
|
|
209
|
-
'--restore', `source_url=file://${snapshotDir},resume=false${options.memoryRestoreMode
|
|
209
|
+
'--restore', `source_url=file://${snapshotDir},resume=false${options.memoryRestoreMode ? `,memory_restore_mode=${options.memoryRestoreMode}` : ''}`,
|
|
210
210
|
]
|
|
211
211
|
: [
|
|
212
212
|
'--api-socket', apiSocketPath,
|
|
@@ -537,4 +537,8 @@ function positiveInteger(value, field) {
|
|
|
537
537
|
}
|
|
538
538
|
return value;
|
|
539
539
|
}
|
|
540
|
+
/** Snapshot restore modes implemented with source lifetime and failure fencing. */
|
|
541
|
+
export const DESK_MEMORY_RESTORE_MODES = ['copyonwrite', 'ondemand'];
|
|
542
|
+
/** Suspend hooks include positive completion after snapshot durability. */
|
|
543
|
+
export const DESK_PARK_HOOK_VERSION = 1;
|
|
540
544
|
//# sourceMappingURL=plan.js.map
|