@braedonsaunders/appkit-desk 0.6.0 → 0.8.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 +83 -1
- package/backend.d.ts +28 -7
- package/backend.d.ts.map +1 -1
- package/backend.js +18 -521
- package/backend.js.map +1 -1
- package/events.d.ts +3 -0
- package/events.d.ts.map +1 -1
- package/events.js.map +1 -1
- package/host.d.ts +32 -1
- package/host.d.ts.map +1 -1
- package/host.js +141 -23
- 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 +145 -0
- package/machine.d.ts.map +1 -0
- package/machine.js +518 -0
- package/machine.js.map +1 -0
- package/network.d.ts +37 -0
- package/network.d.ts.map +1 -0
- package/network.js +173 -0
- package/network.js.map +1 -0
- package/package.json +2 -2
- package/plan.d.ts +129 -31
- package/plan.d.ts.map +1 -1
- package/plan.js +165 -9
- package/plan.js.map +1 -1
- package/port-relay.d.ts +13 -0
- package/port-relay.d.ts.map +1 -0
- package/port-relay.js +56 -0
- package/port-relay.js.map +1 -0
- package/virtualization.d.ts +16 -0
- package/virtualization.d.ts.map +1 -0
- package/virtualization.js +148 -0
- package/virtualization.js.map +1 -0
- package/vz/Package.swift +23 -0
- package/vz/Sources/DeskNet/Addresses.swift +106 -0
- package/vz/Sources/DeskNet/DHCP.swift +111 -0
- package/vz/Sources/DeskNet/Fence.swift +57 -0
- package/vz/Sources/DeskNet/NetworkStack.swift +547 -0
- package/vz/Sources/DeskNet/Packets.swift +355 -0
- package/vz/Sources/DeskNet/Plumbing.swift +312 -0
- package/vz/Sources/DeskNet/Sockets.swift +221 -0
- package/vz/Sources/DeskNet/TCPFlow.swift +448 -0
- package/vz/Sources/appkit-desk-vz/Control.swift +152 -0
- package/vz/Sources/appkit-desk-vz/Helper.swift +505 -0
- package/vz/Sources/appkit-desk-vz/main.swift +38 -0
- package/vz/Tests/DeskNetTests/DeskNetTests.swift +426 -0
- package/vz/appkit-desk-vz.entitlements +8 -0
- package/vz/build.sh +23 -0
- package/vz/check/extract-boot.py +221 -0
package/index.d.ts
CHANGED
|
@@ -7,4 +7,7 @@ export * from './host.js';
|
|
|
7
7
|
export * from './frames.js';
|
|
8
8
|
export * from './portable-home.js';
|
|
9
9
|
export * from './conformance.js';
|
|
10
|
+
export * from './network.js';
|
|
11
|
+
export * from './virtualization.js';
|
|
12
|
+
export * from './port-relay.js';
|
|
10
13
|
//# 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"}
|
|
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"}
|
package/index.js
CHANGED
|
@@ -7,4 +7,7 @@ export * from './host.js';
|
|
|
7
7
|
export * from './frames.js';
|
|
8
8
|
export * from './portable-home.js';
|
|
9
9
|
export * from './conformance.js';
|
|
10
|
+
export * from './network.js';
|
|
11
|
+
export * from './virtualization.js';
|
|
12
|
+
export * from './port-relay.js';
|
|
10
13
|
//# 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","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'\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","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"]}
|
package/machine.d.ts
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The guest channel every backend shares: the hybrid-vsock handshake, the
|
|
3
|
+
* proof that a guest is really behind it, the reconnecting machine, and the
|
|
4
|
+
* child-process plumbing around a VMM. Cloud Hypervisor and the
|
|
5
|
+
* Virtualization.framework helper both expose the guest agent through a host
|
|
6
|
+
* Unix socket with the same `CONNECT <port>` preamble, so everything from the
|
|
7
|
+
* socket inwards is one implementation. Internal to the package.
|
|
8
|
+
*/
|
|
9
|
+
import { type ChildProcess } from 'node:child_process';
|
|
10
|
+
import type { Duplex } from 'node:stream';
|
|
11
|
+
import { type DeskMachine } from './backend.js';
|
|
12
|
+
import { type DeskLauncherIdentity } from './plan.js';
|
|
13
|
+
export declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
14
|
+
export declare const DEFAULT_CONNECT_TIMEOUT_MS = 20000;
|
|
15
|
+
export declare const DEFAULT_CONNECT_RETRY_DELAY_MS = 100;
|
|
16
|
+
export declare const DEFAULT_HANDSHAKE_TIMEOUT_MS = 5000;
|
|
17
|
+
export declare const DEFAULT_RECONNECT_WINDOW_MS = 30000;
|
|
18
|
+
export declare const DEFAULT_RECONNECT_RETRY_DELAY_MS = 250;
|
|
19
|
+
/**
|
|
20
|
+
* How far the reconnect delay is allowed to double. A guest agent that systemd
|
|
21
|
+
* is restarting is not helped by being hammered four times a second for the
|
|
22
|
+
* whole window, and an unbounded doubling would spend most of the window
|
|
23
|
+
* asleep after two failures.
|
|
24
|
+
*/
|
|
25
|
+
export declare const RECONNECT_BACKOFF_CEILING = 8;
|
|
26
|
+
export declare const DEFAULT_KILL_GRACE_MS = 2000;
|
|
27
|
+
/**
|
|
28
|
+
* The machine, and the small state machine that keeps it connected.
|
|
29
|
+
*
|
|
30
|
+
* A vsock channel is not a fact the host learns once at boot — the transport
|
|
31
|
+
* can lie about being connected, and it can go away mid-lease for reasons that
|
|
32
|
+
* have nothing to do with the desk being unusable: the guest agent restarts,
|
|
33
|
+
* the socat bridge drops, a transient vsock error, the guest wedges for a
|
|
34
|
+
* moment. Treating any of those as terminal stranded the desk for the rest of
|
|
35
|
+
* its lease with a perfectly healthy guest behind it, answering every call
|
|
36
|
+
* with "no longer connected". So a live connection is something this machine
|
|
37
|
+
* RE-ESTABLISHES:
|
|
38
|
+
*
|
|
39
|
+
* live ──unexpected drop──▶ reconnecting ──confirmed guest──▶ live
|
|
40
|
+
* │ │
|
|
41
|
+
* │ └──window exhausted / VMM exited──▶ closed
|
|
42
|
+
* └──shutdown(), VMM already exited, protocol violation───────────▶ closed
|
|
43
|
+
*
|
|
44
|
+
* `closed` is terminal. Nothing reconnects out of it, which is what makes
|
|
45
|
+
* `shutdown()` and a dead VMM final rather than the start of a retry loop.
|
|
46
|
+
*
|
|
47
|
+
* What differs between hypervisors is only how the VM stops, parks, and
|
|
48
|
+
* reaches guest ports, and those arrive as functions.
|
|
49
|
+
*/
|
|
50
|
+
export declare function createGuestMachine(options: {
|
|
51
|
+
deskId: string;
|
|
52
|
+
exit: ExitWatcher;
|
|
53
|
+
stream: Duplex;
|
|
54
|
+
/** Re-establishes the channel; resolves only once a guest has answered. */
|
|
55
|
+
reconnect: (abandoned: () => boolean) => Promise<Duplex>;
|
|
56
|
+
reconnectWindowMs: number;
|
|
57
|
+
now: () => number;
|
|
58
|
+
idFactory: () => string;
|
|
59
|
+
requestTimeoutMs: number;
|
|
60
|
+
/** Stop the VMM; resolves once its process has exited. */
|
|
61
|
+
stop: () => Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Pause, snapshot into `snapshotDir`, and stop the VMM. Called after the
|
|
64
|
+
* guest channel has been closed. Must leave the VMM stopped on failure too.
|
|
65
|
+
*/
|
|
66
|
+
park?: (snapshotDir: string) => Promise<void>;
|
|
67
|
+
/** Open a host-to-guest TCP stream, for backends with no routable guest. */
|
|
68
|
+
connect?: (port: number) => Promise<Duplex>;
|
|
69
|
+
}): DeskMachine;
|
|
70
|
+
export interface ExitWatcher {
|
|
71
|
+
done: boolean;
|
|
72
|
+
promise: Promise<void>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Read a child's output and keep only the last few kilobytes.
|
|
76
|
+
*
|
|
77
|
+
* This is not for logging — it is load-bearing. A child spawned with its
|
|
78
|
+
* streams as PIPES blocks once a pipe nobody reads fills, after about 64KB.
|
|
79
|
+
* Cloud Hypervisor logs while it runs, so an unread pipe freezes the guest
|
|
80
|
+
* partway through boot: the process is alive, the disk is untouched, and the
|
|
81
|
+
* guest agent never reaches vsock. Every symptom points at the image and none
|
|
82
|
+
* of them are its fault.
|
|
83
|
+
*
|
|
84
|
+
* Draining costs nothing and buys the tail of what the process said, which is
|
|
85
|
+
* the difference between "the VMM exited" and knowing why. `streams` names the
|
|
86
|
+
* pipes to drain: a helper that speaks a protocol on stdout drains only stderr.
|
|
87
|
+
*/
|
|
88
|
+
export declare function drainOutput(child: ChildProcess, limit?: number, streams?: readonly ('stdout' | 'stderr')[]): {
|
|
89
|
+
tail: () => string;
|
|
90
|
+
};
|
|
91
|
+
export declare function watchExit(child: ChildProcess): ExitWatcher;
|
|
92
|
+
export declare function terminate(child: ChildProcess, exit: ExitWatcher, killGraceMs: number): Promise<void>;
|
|
93
|
+
export declare function runToCompletion(child: ChildProcess, label: string): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Cloud Hypervisor exposes guest vsock ports through a host Unix socket with
|
|
96
|
+
* a line-oriented preamble: the client writes `CONNECT <port>\n` and the VMM
|
|
97
|
+
* answers `OK <assigned>\n` before the byte stream becomes the guest
|
|
98
|
+
* connection. The VMM creates the socket during boot, so connection attempts
|
|
99
|
+
* retry until the deadline. The Virtualization.framework helper speaks the
|
|
100
|
+
* same preamble on its own socket.
|
|
101
|
+
*
|
|
102
|
+
* The same loop serves the first connection and every later reconnect, so a
|
|
103
|
+
* reconnect gets `confirmGuest` too — a mid-lease reconnect that trusted the
|
|
104
|
+
* banner would re-adopt exactly the hollow stream the boot path learned not to.
|
|
105
|
+
*/
|
|
106
|
+
export declare function connectWithRetry(options: {
|
|
107
|
+
connect: (socketPath: string) => Duplex;
|
|
108
|
+
now: () => number;
|
|
109
|
+
socketPath: string;
|
|
110
|
+
port: number;
|
|
111
|
+
timeoutMs: number;
|
|
112
|
+
retryDelayMs: number;
|
|
113
|
+
/** Ceiling the delay doubles toward; omitted keeps the delay constant. */
|
|
114
|
+
maxRetryDelayMs?: number;
|
|
115
|
+
attemptTimeoutMs: number;
|
|
116
|
+
exited: ExitWatcher;
|
|
117
|
+
/** A dead VMM means something different at boot than it does mid-lease. */
|
|
118
|
+
exitedMessage?: string;
|
|
119
|
+
/** Stops the loop early when the caller no longer wants the connection. */
|
|
120
|
+
abandoned?: () => boolean;
|
|
121
|
+
}): Promise<Duplex>;
|
|
122
|
+
/**
|
|
123
|
+
* One handshake attempt, which MUST settle.
|
|
124
|
+
*
|
|
125
|
+
* Cloud Hypervisor accepts a connection on the vsock socket whether or not
|
|
126
|
+
* anything in the guest is listening on the port, and when nothing is it may
|
|
127
|
+
* answer nothing at all: no reply, no error, no close. Without a deadline of
|
|
128
|
+
* its own this promise then never settles — and because the caller awaits it
|
|
129
|
+
* inside the retry loop, the loop stops iterating, the overall deadline is
|
|
130
|
+
* never re-checked, and the boot hangs forever rather than failing. That is
|
|
131
|
+
* indistinguishable from a wedged host and it is why every attempt is bounded
|
|
132
|
+
* here as well as in aggregate.
|
|
133
|
+
*
|
|
134
|
+
* A close before the reply line is a refusal: that is how a port with nothing
|
|
135
|
+
* listening presents through Cloud Hypervisor, and the Virtualization.framework
|
|
136
|
+
* helper presents a refused vsock or TCP port the same way.
|
|
137
|
+
*/
|
|
138
|
+
export declare function attemptHandshake(connect: (socketPath: string) => Duplex, socketPath: string, port: number, attemptTimeoutMs: number): Promise<Duplex>;
|
|
139
|
+
export declare function defaultLauncher(command: string, args: readonly string[], options: {
|
|
140
|
+
identity?: DeskLauncherIdentity;
|
|
141
|
+
}): ChildProcess;
|
|
142
|
+
export declare function defaultConnect(socketPath: string): Duplex;
|
|
143
|
+
export declare function delay(ms: number): Promise<void>;
|
|
144
|
+
export declare function errorMessage(error: unknown): string;
|
|
145
|
+
//# sourceMappingURL=machine.d.ts.map
|
package/machine.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"machine.d.ts","sourceRoot":"","sources":["../src/machine.ts"],"names":[],"mappings":"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"}
|