@namzu/sandbox 24.0.0 → 26.0.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 (158) hide show
  1. package/CHANGELOG.md +284 -0
  2. package/dist/backends/http-worker-client.d.ts +3 -1
  3. package/dist/backends/http-worker-client.d.ts.map +1 -1
  4. package/dist/backends/http-worker-client.js +15 -4
  5. package/dist/backends/http-worker-client.js.map +1 -1
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/local-virtual-computer/client.d.ts +21 -0
  11. package/dist/local-virtual-computer/client.d.ts.map +1 -0
  12. package/dist/local-virtual-computer/client.js +503 -0
  13. package/dist/local-virtual-computer/client.js.map +1 -0
  14. package/dist/local-virtual-computer/detached-process.d.ts +15 -0
  15. package/dist/local-virtual-computer/detached-process.d.ts.map +1 -0
  16. package/dist/local-virtual-computer/detached-process.js +111 -0
  17. package/dist/local-virtual-computer/detached-process.js.map +1 -0
  18. package/dist/local-virtual-computer/detached-worker.d.ts +11 -0
  19. package/dist/local-virtual-computer/detached-worker.d.ts.map +1 -0
  20. package/dist/local-virtual-computer/detached-worker.js +97 -0
  21. package/dist/local-virtual-computer/detached-worker.js.map +1 -0
  22. package/dist/local-virtual-computer/dialect.d.ts +26 -0
  23. package/dist/local-virtual-computer/dialect.d.ts.map +1 -0
  24. package/dist/local-virtual-computer/dialect.js +61 -0
  25. package/dist/local-virtual-computer/dialect.js.map +1 -0
  26. package/dist/local-virtual-computer/engine.d.ts +63 -0
  27. package/dist/local-virtual-computer/engine.d.ts.map +1 -0
  28. package/dist/local-virtual-computer/engine.js +125 -0
  29. package/dist/local-virtual-computer/engine.js.map +1 -0
  30. package/dist/local-virtual-computer/guest-stdio.d.ts +15 -0
  31. package/dist/local-virtual-computer/guest-stdio.d.ts.map +1 -0
  32. package/dist/local-virtual-computer/guest-stdio.js +330 -0
  33. package/dist/local-virtual-computer/guest-stdio.js.map +1 -0
  34. package/dist/local-virtual-computer/index.d.ts +36 -0
  35. package/dist/local-virtual-computer/index.d.ts.map +1 -0
  36. package/dist/local-virtual-computer/index.js +341 -0
  37. package/dist/local-virtual-computer/index.js.map +1 -0
  38. package/dist/local-virtual-computer/podman-machine.d.ts +17 -0
  39. package/dist/local-virtual-computer/podman-machine.d.ts.map +1 -0
  40. package/dist/local-virtual-computer/podman-machine.js +181 -0
  41. package/dist/local-virtual-computer/podman-machine.js.map +1 -0
  42. package/dist/local-virtual-computer/setup/contract.d.ts +360 -0
  43. package/dist/local-virtual-computer/setup/contract.d.ts.map +1 -0
  44. package/dist/local-virtual-computer/setup/contract.js +3 -0
  45. package/dist/local-virtual-computer/setup/contract.js.map +1 -0
  46. package/dist/local-virtual-computer/setup/elevated.d.ts +78 -0
  47. package/dist/local-virtual-computer/setup/elevated.d.ts.map +1 -0
  48. package/dist/local-virtual-computer/setup/elevated.js +548 -0
  49. package/dist/local-virtual-computer/setup/elevated.js.map +1 -0
  50. package/dist/local-virtual-computer/setup/host.d.ts +12 -0
  51. package/dist/local-virtual-computer/setup/host.d.ts.map +1 -0
  52. package/dist/local-virtual-computer/setup/host.js +93 -0
  53. package/dist/local-virtual-computer/setup/host.js.map +1 -0
  54. package/dist/local-virtual-computer/setup/idle.d.ts +18 -0
  55. package/dist/local-virtual-computer/setup/idle.d.ts.map +1 -0
  56. package/dist/local-virtual-computer/setup/idle.js +38 -0
  57. package/dist/local-virtual-computer/setup/idle.js.map +1 -0
  58. package/dist/local-virtual-computer/setup/image.d.ts +7 -0
  59. package/dist/local-virtual-computer/setup/image.d.ts.map +1 -0
  60. package/dist/local-virtual-computer/setup/image.js +124 -0
  61. package/dist/local-virtual-computer/setup/image.js.map +1 -0
  62. package/dist/local-virtual-computer/setup/index.d.ts +17 -0
  63. package/dist/local-virtual-computer/setup/index.d.ts.map +1 -0
  64. package/dist/local-virtual-computer/setup/index.js +17 -0
  65. package/dist/local-virtual-computer/setup/index.js.map +1 -0
  66. package/dist/local-virtual-computer/setup/limits.d.ts +30 -0
  67. package/dist/local-virtual-computer/setup/limits.d.ts.map +1 -0
  68. package/dist/local-virtual-computer/setup/limits.js +58 -0
  69. package/dist/local-virtual-computer/setup/limits.js.map +1 -0
  70. package/dist/local-virtual-computer/setup/plan.d.ts +8 -0
  71. package/dist/local-virtual-computer/setup/plan.d.ts.map +1 -0
  72. package/dist/local-virtual-computer/setup/plan.js +131 -0
  73. package/dist/local-virtual-computer/setup/plan.js.map +1 -0
  74. package/dist/local-virtual-computer/setup/readiness.d.ts +11 -0
  75. package/dist/local-virtual-computer/setup/readiness.d.ts.map +1 -0
  76. package/dist/local-virtual-computer/setup/readiness.js +320 -0
  77. package/dist/local-virtual-computer/setup/readiness.js.map +1 -0
  78. package/dist/local-virtual-computer/setup/registry.d.ts +18 -0
  79. package/dist/local-virtual-computer/setup/registry.d.ts.map +1 -0
  80. package/dist/local-virtual-computer/setup/registry.js +50 -0
  81. package/dist/local-virtual-computer/setup/registry.js.map +1 -0
  82. package/dist/local-virtual-computer/setup/restart.d.ts +8 -0
  83. package/dist/local-virtual-computer/setup/restart.d.ts.map +1 -0
  84. package/dist/local-virtual-computer/setup/restart.js +60 -0
  85. package/dist/local-virtual-computer/setup/restart.js.map +1 -0
  86. package/dist/local-virtual-computer/setup/resume.d.ts +14 -0
  87. package/dist/local-virtual-computer/setup/resume.d.ts.map +1 -0
  88. package/dist/local-virtual-computer/setup/resume.js +58 -0
  89. package/dist/local-virtual-computer/setup/resume.js.map +1 -0
  90. package/dist/local-virtual-computer/setup/run.d.ts +8 -0
  91. package/dist/local-virtual-computer/setup/run.d.ts.map +1 -0
  92. package/dist/local-virtual-computer/setup/run.js +200 -0
  93. package/dist/local-virtual-computer/setup/run.js.map +1 -0
  94. package/dist/local-virtual-computer/setup/windows-folder.d.ts +11 -0
  95. package/dist/local-virtual-computer/setup/windows-folder.d.ts.map +1 -0
  96. package/dist/local-virtual-computer/setup/windows-folder.js +14 -0
  97. package/dist/local-virtual-computer/setup/windows-folder.js.map +1 -0
  98. package/dist/local-virtual-computer/setup/wsl-package.d.ts +19 -0
  99. package/dist/local-virtual-computer/setup/wsl-package.d.ts.map +1 -0
  100. package/dist/local-virtual-computer/setup/wsl-package.js +242 -0
  101. package/dist/local-virtual-computer/setup/wsl-package.js.map +1 -0
  102. package/dist/local-virtual-computer/setup/wslc-shared.d.ts +27 -0
  103. package/dist/local-virtual-computer/setup/wslc-shared.d.ts.map +1 -0
  104. package/dist/local-virtual-computer/setup/wslc-shared.js +137 -0
  105. package/dist/local-virtual-computer/setup/wslc-shared.js.map +1 -0
  106. package/dist/local-virtual-computer/ssh-forward.d.ts +19 -0
  107. package/dist/local-virtual-computer/ssh-forward.d.ts.map +1 -0
  108. package/dist/local-virtual-computer/ssh-forward.js +179 -0
  109. package/dist/local-virtual-computer/ssh-forward.js.map +1 -0
  110. package/dist/local-virtual-computer/wslc-engine.d.ts +27 -0
  111. package/dist/local-virtual-computer/wslc-engine.d.ts.map +1 -0
  112. package/dist/local-virtual-computer/wslc-engine.js +98 -0
  113. package/dist/local-virtual-computer/wslc-engine.js.map +1 -0
  114. package/local-computer/Dockerfile +99 -0
  115. package/local-computer/app-catalog.cjs +240 -0
  116. package/local-computer/app-native-host.cjs +144 -0
  117. package/local-computer/browser-home.cjs +249 -0
  118. package/local-computer/build-image.mjs +310 -0
  119. package/local-computer/desktop-worker.cjs +477 -0
  120. package/local-computer/entrypoint.sh +161 -0
  121. package/local-computer/home.html +28 -0
  122. package/local-computer/new-tab/home.css +255 -0
  123. package/local-computer/new-tab/home.js +65 -0
  124. package/local-computer/new-tab/manifest.json +12 -0
  125. package/local-computer/new-tab/wordmark.svg +33 -0
  126. package/local-computer/openbox-rc.xml +41 -0
  127. package/local-computer/prepare-home.cjs +121 -0
  128. package/local-computer/tint2rc +44 -0
  129. package/package.json +13 -4
  130. package/src/backends/http-worker-client.ts +31 -3
  131. package/src/index.ts +13 -0
  132. package/src/local-virtual-computer/client.ts +573 -0
  133. package/src/local-virtual-computer/detached-process.ts +133 -0
  134. package/src/local-virtual-computer/detached-worker.ts +124 -0
  135. package/src/local-virtual-computer/dialect.ts +83 -0
  136. package/src/local-virtual-computer/engine.ts +197 -0
  137. package/src/local-virtual-computer/guest-stdio.ts +332 -0
  138. package/src/local-virtual-computer/index.ts +447 -0
  139. package/src/local-virtual-computer/podman-machine.ts +264 -0
  140. package/src/local-virtual-computer/setup/contract.ts +395 -0
  141. package/src/local-virtual-computer/setup/elevated.ts +655 -0
  142. package/src/local-virtual-computer/setup/host.ts +100 -0
  143. package/src/local-virtual-computer/setup/idle.ts +39 -0
  144. package/src/local-virtual-computer/setup/image.ts +159 -0
  145. package/src/local-virtual-computer/setup/index.ts +16 -0
  146. package/src/local-virtual-computer/setup/limits.ts +68 -0
  147. package/src/local-virtual-computer/setup/plan.ts +144 -0
  148. package/src/local-virtual-computer/setup/readiness.ts +386 -0
  149. package/src/local-virtual-computer/setup/registry.ts +50 -0
  150. package/src/local-virtual-computer/setup/restart.ts +64 -0
  151. package/src/local-virtual-computer/setup/resume.ts +65 -0
  152. package/src/local-virtual-computer/setup/run.ts +225 -0
  153. package/src/local-virtual-computer/setup/windows-folder.ts +13 -0
  154. package/src/local-virtual-computer/setup/wsl-package.ts +242 -0
  155. package/src/local-virtual-computer/setup/wslc-shared.ts +147 -0
  156. package/src/local-virtual-computer/ssh-forward.ts +195 -0
  157. package/src/local-virtual-computer/wslc-engine.ts +124 -0
  158. package/worker/server.js +1499 -0
@@ -0,0 +1,195 @@
1
+ import { spawn } from 'node:child_process'
2
+ import { mkdtemp, rmdir, unlink, writeFile } from 'node:fs/promises'
3
+ import { createServer } from 'node:net'
4
+ import { tmpdir } from 'node:os'
5
+ import { join, win32 } from 'node:path'
6
+ import type { LocalPortForward } from './engine.js'
7
+
8
+ /** Internal retained authority for retrying an unconfirmed owned startup cleanup. */
9
+ export class LocalForwardCleanupError extends Error {
10
+ constructor(
11
+ readonly forward: LocalPortForward,
12
+ cause: unknown,
13
+ ) {
14
+ super('The owned local computer forward requires cleanup recovery', { cause })
15
+ }
16
+ }
17
+
18
+ async function removeIfPresent(
19
+ path: string,
20
+ remove: (path: string) => Promise<void>,
21
+ ): Promise<void> {
22
+ try {
23
+ await remove(path)
24
+ } catch (error) {
25
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error
26
+ }
27
+ }
28
+
29
+ interface LocalSshForwardOptions {
30
+ readonly identity: string
31
+ readonly port: number
32
+ readonly user: string
33
+ readonly hostPublicKey: string
34
+ readonly ports: readonly number[]
35
+ readonly signal?: AbortSignal
36
+ readonly onLost: () => void
37
+ }
38
+
39
+ async function reservePort(): Promise<number> {
40
+ const server = createServer()
41
+ return await new Promise<number>((resolve, reject) => {
42
+ server.once('error', reject)
43
+ server.listen(0, '127.0.0.1', () => {
44
+ const address = server.address()
45
+ server.close((error) => {
46
+ if (error) reject(error)
47
+ else if (!address || typeof address === 'string')
48
+ reject(new Error('No local forwarding port'))
49
+ else resolve(address.port)
50
+ })
51
+ })
52
+ })
53
+ }
54
+
55
+ /** An owned loopback tunnel to an already verified local WSL machine only. */
56
+ export async function startLocalSshForward(
57
+ options: LocalSshForwardOptions,
58
+ ): Promise<LocalPortForward> {
59
+ options.signal?.throwIfAborted()
60
+ if (!/^ssh-ed25519 [A-Za-z0-9+/=]+$/.test(options.hostPublicKey))
61
+ throw new Error('The registered local machine did not provide its SSH host public key')
62
+ const ports = await Promise.all(options.ports.map(() => reservePort()))
63
+ const directory = await mkdtemp(join(tmpdir(), 'namzu-pal-forward-'))
64
+ const knownHosts = join(directory, 'known_hosts')
65
+ await writeFile(knownHosts, `[127.0.0.1]:${options.port} ${options.hostPublicKey}\n`, {
66
+ mode: 0o600,
67
+ flag: 'wx',
68
+ })
69
+ const child = spawn(
70
+ win32.join(process.env.SystemRoot ?? 'C:\\Windows', 'System32', 'OpenSSH', 'ssh.exe'),
71
+ [
72
+ '-N',
73
+ '-T',
74
+ '-v',
75
+ '-F',
76
+ 'NUL',
77
+ '-o',
78
+ 'BatchMode=yes',
79
+ '-o',
80
+ 'ExitOnForwardFailure=yes',
81
+ '-o',
82
+ 'StrictHostKeyChecking=yes',
83
+ '-o',
84
+ `UserKnownHostsFile=${knownHosts}`,
85
+ '-o',
86
+ 'HostKeyAlgorithms=ssh-ed25519',
87
+ '-o',
88
+ 'IdentitiesOnly=yes',
89
+ '-o',
90
+ 'ProxyCommand=none',
91
+ '-o',
92
+ 'ProxyJump=none',
93
+ '-i',
94
+ options.identity,
95
+ '-p',
96
+ String(options.port),
97
+ ...ports.flatMap((port, index) => [
98
+ '-L',
99
+ `127.0.0.1:${port}:127.0.0.1:${options.ports[index]}`,
100
+ ]),
101
+ `${options.user}@127.0.0.1`,
102
+ ],
103
+ { windowsHide: true, stdio: ['ignore', 'ignore', 'pipe'] },
104
+ )
105
+ let ended = false
106
+ let closing = false
107
+ let ready = false
108
+ let cleanup: Promise<void> | undefined
109
+ const closed = new Promise<void>((resolve) => {
110
+ child.once('close', () => {
111
+ ended = true
112
+ if (ready && !closing) {
113
+ try {
114
+ options.onLost()
115
+ } catch {
116
+ // A host callback must not crash the process-close event dispatch.
117
+ // The ended handle remains fenced and close() retains recovery.
118
+ }
119
+ }
120
+ resolve()
121
+ })
122
+ })
123
+ const close = (): Promise<void> => {
124
+ if (cleanup) return cleanup
125
+ closing = true
126
+ cleanup = (async () => {
127
+ if (
128
+ !ended &&
129
+ child.pid !== undefined &&
130
+ child.exitCode === null &&
131
+ child.signalCode === null &&
132
+ !child.kill()
133
+ )
134
+ throw new Error('The owned local computer forwarding process could not be stopped')
135
+ await closed
136
+ await removeIfPresent(knownHosts, unlink)
137
+ await removeIfPresent(directory, rmdir)
138
+ })().catch((error: unknown) => {
139
+ cleanup = undefined
140
+ throw error
141
+ })
142
+ return cleanup
143
+ }
144
+ const forward: LocalPortForward = {
145
+ ports,
146
+ assertAlive() {
147
+ if (ended || closing) throw new Error('The local computer forwarding process has ended')
148
+ },
149
+ close,
150
+ }
151
+ try {
152
+ await new Promise<void>((resolve, reject) => {
153
+ let diagnostic = ''
154
+ const opened = new Set<number>()
155
+ const detach = () => {
156
+ options.signal?.removeEventListener('abort', abort)
157
+ child.stderr.removeListener('data', data)
158
+ child.removeListener('close', fail)
159
+ }
160
+ const abort = () => {
161
+ detach()
162
+ reject(options.signal?.reason)
163
+ }
164
+ const fail = () => {
165
+ detach()
166
+ reject(new Error('The verified local machine SSH forwarding process failed'))
167
+ }
168
+ const data = (chunk: Buffer) => {
169
+ diagnostic = (diagnostic + chunk.toString('utf8')).slice(-16_384)
170
+ for (const port of ports)
171
+ if (diagnostic.includes(`Local forwarding listening on 127.0.0.1 port ${port}.`))
172
+ opened.add(port)
173
+ if (opened.size === ports.length) {
174
+ ready = true
175
+ detach()
176
+ resolve()
177
+ }
178
+ }
179
+ child.stderr.on('data', data)
180
+ child.once('error', fail)
181
+ child.once('close', fail)
182
+ options.signal?.addEventListener('abort', abort, { once: true })
183
+ if (options.signal?.aborted) abort()
184
+ }).finally(() => child.stderr.resume())
185
+ options.signal?.throwIfAborted()
186
+ return forward
187
+ } catch (error) {
188
+ try {
189
+ await close()
190
+ } catch {
191
+ throw new LocalForwardCleanupError(forward, error)
192
+ }
193
+ throw error
194
+ }
195
+ }
@@ -0,0 +1,124 @@
1
+ import { access } from 'node:fs/promises'
2
+ import { win32 } from 'node:path'
3
+ import {
4
+ LocalCommandError,
5
+ type LocalComputerCommandRunner,
6
+ type LocalComputerSetupCode,
7
+ LocalComputerSetupError,
8
+ type LocalEngine,
9
+ nodeCommandRunner,
10
+ } from './engine.js'
11
+
12
+ /** The oldest wslc runtime the provider is verified against. */
13
+ export const WSLC_MINIMUM_VERSION = [2, 9, 3] as const
14
+
15
+ export interface LocalWslcEngineOptions {
16
+ /** Path to wslc.exe. Defaults to the WSL install directory, then PATH. */
17
+ readonly wslcBinary?: string
18
+ readonly runner?: LocalComputerCommandRunner
19
+ }
20
+
21
+ const MESSAGES: Record<LocalComputerSetupCode, string> = {
22
+ 'engine-missing': 'Linux container support is not installed on this PC.',
23
+ 'engine-too-old': 'Linux container support on this PC is too old for Namzu.',
24
+ 'virtualization-off':
25
+ 'This PC has virtualization switched off. Turn it on in the PC startup settings, then try again.',
26
+ 'blocked-by-policy': 'Your organisation does not allow Linux containers on this PC.',
27
+ 'image-missing':
28
+ 'The Pal computer is not set up on this PC yet. Namzu sets it up; finish the setup from the Pal computer and try again.',
29
+ 'sdk-mismatch': "Namzu needs an update to work with this version of Windows' Linux support.",
30
+ }
31
+
32
+ export function wslcSetupError(code: LocalComputerSetupCode, cause?: unknown) {
33
+ return new LocalComputerSetupError(MESSAGES[code], code, cause ? { cause } : undefined)
34
+ }
35
+
36
+ /**
37
+ * Turn the machine-readable codes of a failed wslc command into a typed
38
+ * setup error. Anything unrecognised stays an ordinary failure.
39
+ */
40
+ export function mapWslcFailure(error: unknown): LocalComputerSetupError | undefined {
41
+ if (error instanceof LocalComputerSetupError) return undefined
42
+ const codes = new Set(error instanceof LocalCommandError ? error.errorCodes : [])
43
+ if (codes.has('0x80370102') || codes.has('HCS_E_HYPERV_NOT_INSTALLED'))
44
+ return wslcSetupError('virtualization-off', error)
45
+ if (codes.has('0x8004060C') || codes.has('WSLC_E_CONTAINER_DISABLED'))
46
+ return wslcSetupError('blocked-by-policy', error)
47
+ if (codes.has('0x8004060B') || codes.has('WSLC_E_SDK_UPDATE_NEEDED'))
48
+ return wslcSetupError('sdk-mismatch', error)
49
+ if (codes.has('WSLC_E_IMAGE_NOT_FOUND')) return wslcSetupError('image-missing', error)
50
+ if (codes.has('ENOENT')) return wslcSetupError('engine-missing', error)
51
+ return undefined
52
+ }
53
+
54
+ /** Parse the first line of `wslc version`, for example `wslc 3.0.1.0`. */
55
+ export function parseWslcVersion(output: string): [number, number, number] | undefined {
56
+ const match = /^\s*wslc\s+(\d+)\.(\d+)\.(\d+)/im.exec(output)
57
+ if (!match) return undefined
58
+ return [Number(match[1]), Number(match[2]), Number(match[3])]
59
+ }
60
+
61
+ function atLeast(found: readonly number[], floor: readonly number[]): boolean {
62
+ for (let i = 0; i < floor.length; i++) {
63
+ if ((found[i] ?? 0) !== floor[i]) return (found[i] ?? 0) > (floor[i] ?? 0)
64
+ }
65
+ return true
66
+ }
67
+
68
+ async function exists(path: string): Promise<boolean> {
69
+ try {
70
+ await access(path)
71
+ return true
72
+ } catch {
73
+ return false
74
+ }
75
+ }
76
+
77
+ export interface WslcHost {
78
+ readonly platform?: NodeJS.Platform
79
+ readonly env?: NodeJS.ProcessEnv
80
+ readonly exists?: (path: string) => Promise<boolean>
81
+ }
82
+
83
+ async function resolveBinary(options: LocalWslcEngineOptions, host: WslcHost): Promise<string> {
84
+ if (options.wslcBinary) return options.wslcBinary
85
+ const platform = host.platform ?? process.platform
86
+ const env = host.env ?? process.env
87
+ const programFiles = env.ProgramW6432 ?? env.ProgramFiles
88
+ if (platform === 'win32' && programFiles) {
89
+ const installed = win32.join(programFiles, 'WSL', 'wslc.exe')
90
+ if (await (host.exists ?? exists)(installed)) return installed
91
+ }
92
+ return 'wslc'
93
+ }
94
+
95
+ /**
96
+ * Find a working wslc and prove its session starts. Never installs or updates
97
+ * anything: setup is a separate, consented operation.
98
+ */
99
+ export async function resolveLocalWslcEngine(
100
+ options: LocalWslcEngineOptions,
101
+ signal?: AbortSignal,
102
+ host: WslcHost = {},
103
+ ): Promise<LocalEngine> {
104
+ const binary = await resolveBinary(options, host)
105
+ const runner = options.runner ?? nodeCommandRunner
106
+ const run: LocalEngine['run'] = async (args, call) => {
107
+ try {
108
+ return await runner.run(binary, args, { ...call, env: { ...process.env, ...call?.env } })
109
+ } catch (error) {
110
+ throw mapWslcFailure(error) ?? error
111
+ }
112
+ }
113
+ const version = parseWslcVersion(await run(['version'], { signal }))
114
+ if (!version) throw wslcSetupError('engine-missing')
115
+ if (!atLeast(version, WSLC_MINIMUM_VERSION))
116
+ throw new LocalComputerSetupError(
117
+ `Linux container support on this PC (${version.join('.')}) is too old; ${WSLC_MINIMUM_VERSION.join('.')} or newer is required.`,
118
+ 'engine-too-old',
119
+ )
120
+ // The first command that needs the session starts its virtual machine, so
121
+ // this is where "virtualization is off" and "blocked by policy" surface.
122
+ await run(['ps', '-a', '-q'], { signal })
123
+ return { endpoint: 'wslc://local', dialect: 'wslc', run }
124
+ }