withruntime 0.12.0 → 0.16.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 (94) hide show
  1. package/BLAXEL.md +97 -32
  2. package/COMPATIBILITY.md +120 -5
  3. package/DAYTONA.md +34 -3
  4. package/E2B.md +34 -5
  5. package/README.md +36 -3
  6. package/README.src.md +35 -2
  7. package/VERCEL.md +52 -5
  8. package/compatibility-lock.json +71 -111
  9. package/dist/billing-cli.js +8 -4
  10. package/dist/blaxel/client.d.ts +11 -0
  11. package/dist/blaxel/client.js +28 -0
  12. package/dist/blaxel/errors.d.ts +65 -0
  13. package/dist/blaxel/errors.js +114 -0
  14. package/dist/blaxel/index.d.ts +10 -3
  15. package/dist/blaxel/index.js +23 -4
  16. package/dist/blaxel/preview.d.ts +29 -4
  17. package/dist/blaxel/preview.js +79 -5
  18. package/dist/blaxel/sandbox.d.ts +1 -1
  19. package/dist/blaxel/sandbox.js +2 -1
  20. package/dist/blaxel/session.js +4 -2
  21. package/dist/cli-extras.js +2 -1
  22. package/dist/cli-words.js +1 -0
  23. package/dist/cli.d.ts +17 -2
  24. package/dist/cli.js +291 -85
  25. package/dist/client.d.ts +1 -1
  26. package/dist/credentials.d.ts +3 -0
  27. package/dist/credentials.js +15 -0
  28. package/dist/daytona/client.d.ts +4 -0
  29. package/dist/daytona/client.js +8 -3
  30. package/dist/daytona/daytona.d.ts +4 -1
  31. package/dist/daytona/daytona.js +3 -2
  32. package/dist/e2b/sandbox.d.ts +7 -6
  33. package/dist/e2b/sandbox.js +9 -12
  34. package/dist/errors.d.ts +1 -1
  35. package/dist/errors.js +1 -1
  36. package/dist/freestyle/index.d.ts +77 -5
  37. package/dist/freestyle/index.js +380 -48
  38. package/dist/identity.js +36 -18
  39. package/dist/index.d.ts +4 -3
  40. package/dist/machine.d.ts +39 -13
  41. package/dist/machine.js +72 -25
  42. package/dist/mcp.js +157 -20
  43. package/dist/modal/index.d.ts +15 -0
  44. package/dist/modal/index.js +35 -7
  45. package/dist/morph/index.d.ts +255 -0
  46. package/dist/morph/index.js +641 -0
  47. package/dist/network-cli.js +2 -2
  48. package/dist/product-names.d.ts +2 -2
  49. package/dist/product-names.js +2 -2
  50. package/dist/products/api-keys.d.ts +73 -0
  51. package/dist/products/api-keys.js +23 -0
  52. package/dist/products/apps.d.ts +12 -31
  53. package/dist/products/apps.js +8 -113
  54. package/dist/products/balancers.d.ts +87 -0
  55. package/dist/products/balancers.js +8 -0
  56. package/dist/products/builds.d.ts +30 -0
  57. package/dist/products/builds.js +6 -0
  58. package/dist/products/index.d.ts +2 -0
  59. package/dist/products/index.js +2 -0
  60. package/dist/products/jobs.d.ts +2 -2
  61. package/dist/products/jobs.js +1 -1
  62. package/dist/products/limits.d.ts +16 -2
  63. package/dist/products/limits.js +2 -2
  64. package/dist/products/mcp.d.ts +5 -0
  65. package/dist/products/network-products.d.ts +21 -3
  66. package/dist/products/network-products.js +3 -1
  67. package/dist/products/observability.d.ts +7 -2
  68. package/dist/products/previews.d.ts +6 -3
  69. package/dist/products/previews.js +1 -1
  70. package/dist/products/resources.d.ts +54 -0
  71. package/dist/products/resources.js +122 -0
  72. package/dist/products/secrets.d.ts +22 -0
  73. package/dist/products/secrets.js +8 -0
  74. package/dist/products/volumes.d.ts +3 -0
  75. package/dist/sandbox.d.ts +4 -0
  76. package/dist/sandbox.js +27 -2
  77. package/dist/snapshots.d.ts +4 -0
  78. package/dist/ssh.d.ts +35 -0
  79. package/dist/ssh.js +93 -1
  80. package/dist/transport.d.ts +14 -8
  81. package/dist/transport.js +70 -15
  82. package/dist/types.d.ts +33 -5
  83. package/dist/vercel/command.d.ts +48 -1
  84. package/dist/vercel/command.js +237 -6
  85. package/dist/vercel/sandbox-user.js +2 -1
  86. package/dist/vercel/sandbox.d.ts +17 -3
  87. package/dist/vercel/sandbox.js +46 -3
  88. package/dist/wallet.d.ts +4 -1
  89. package/dist/wallet.js +2 -1
  90. package/package.json +7 -14
  91. package/dist/cli/logs.test.d.ts +0 -1
  92. package/dist/cli/logs.test.js +0 -272
  93. package/dist/products/logs.test.d.ts +0 -1
  94. package/dist/products/logs.test.js +0 -343
package/dist/client.d.ts CHANGED
@@ -6,7 +6,7 @@ import { Sandbox } from "./sandbox.js";
6
6
  import { Snapshots } from "./snapshots.js";
7
7
  import { Transport, type RequestOptions } from "./transport.js";
8
8
  import type { CreateSandbox, DeletedSandbox, FeedbackKind, SandboxInfo, Usage } from "./types.js";
9
- export type { CreateOptions } from "./machine.js";
9
+ export type { CreateOptions, LifecycleOptions } from "./machine.js";
10
10
  export type RuntimeOptions = {
11
11
  /** Default: the RUNTIME_API_KEY environment variable. */
12
12
  apiKey?: string;
@@ -26,6 +26,9 @@ export declare function connectionOrigins(env: NodeJS.ProcessEnv): {
26
26
  /** Browser-issued production credentials must only reach the production API.
27
27
  * CLI and MCP share this boundary, including resumed device requests. */
28
28
  export declare function checkBrowserOrigins(origins: ReturnType<typeof connectionOrigins>): void;
29
+ /** A wallet account's key, from its claim, saved as this machine's connection
30
+ * (the MCP bridge's runtime_wallet_claim; `billing claim` saves the same). */
31
+ export declare function saveWalletConnection(env: NodeJS.ProcessEnv, key: string, orgId: string): Promise<void>;
29
32
  /** Private user storage, outside project files. Nothing reading this module
30
33
  * returns a secret to CLI output. Store names bind credentials to both origins. */
31
34
  export declare function connectionStore(env: NodeJS.ProcessEnv, directory?: string): {
@@ -37,6 +37,21 @@ export function checkBrowserOrigins(origins) {
37
37
  (local(origins.auth) && local(origins.api))))
38
38
  throw new Error("Browser login supports Runtime's production endpoints or local test endpoints. Check RUNTIME_AUTH_URL and RUNTIME_API_URL.");
39
39
  }
40
+ /** A wallet account's key, from its claim, saved as this machine's connection
41
+ * (the MCP bridge's runtime_wallet_claim; `billing claim` saves the same). */
42
+ export function saveWalletConnection(env, key, orgId) {
43
+ const origins = connectionOrigins(env);
44
+ checkBrowserOrigins(origins);
45
+ return connectionStore(env).save({
46
+ version: 1,
47
+ apiOrigin: origins.api,
48
+ authOrigin: origins.auth,
49
+ key,
50
+ connectionId: "pasted",
51
+ orgId,
52
+ agentName: "Wallet agent",
53
+ });
54
+ }
40
55
  function defaultDirectory(env) {
41
56
  const root = env.XDG_CONFIG_HOME ?? join(homedir(), ".config");
42
57
  if (!isAbsolute(root))
@@ -40,5 +40,9 @@ export interface DaytonaConfig {
40
40
  export declare function pickKey(explicit?: string): string | undefined;
41
41
  /** The Runtime client for this configuration. */
42
42
  export declare function clientFor(config?: DaytonaConfig): Runtime;
43
+ /** Runtime runs in one US region: a Daytona target (the client's, or a
44
+ * create's own since @daytona/sdk 0.224) other than "us" is refused, and
45
+ * none or "" means that region. */
46
+ export declare function refuseTarget(target: string | undefined): void;
43
47
  /** Test hook: forget the cached clients. */
44
48
  export declare function resetClients(): void;
@@ -21,9 +21,7 @@ const clients = new Map();
21
21
  export function clientFor(config = {}) {
22
22
  if (config.jwtToken !== undefined || (config.organizationId !== undefined && !config.apiKey))
23
23
  throw new NotSupportedError("Daytona JWT sign-in (jwtToken, organizationId)", "Use a Runtime key: set RUNTIME_API_KEY, or run `npx withruntime login` once.");
24
- const target = config.target ?? env("DAYTONA_TARGET");
25
- if (target !== undefined && target !== "" && target !== "us")
26
- throw new NotSupportedError(`The Daytona target "${target}"`, 'Runtime runs in one US region; remove target or use "us".');
24
+ refuseTarget(config.target ?? env("DAYTONA_TARGET"));
27
25
  if (config.withruntime?.client)
28
26
  return config.withruntime.client;
29
27
  const key = pickKey(config.apiKey);
@@ -34,6 +32,13 @@ export function clientFor(config = {}) {
34
32
  }
35
33
  return client;
36
34
  }
35
+ /** Runtime runs in one US region: a Daytona target (the client's, or a
36
+ * create's own since @daytona/sdk 0.224) other than "us" is refused, and
37
+ * none or "" means that region. */
38
+ export function refuseTarget(target) {
39
+ if (target !== undefined && target !== "" && target !== "us")
40
+ throw new NotSupportedError(`The Daytona target "${target}"`, 'Runtime runs in one US region; remove target or use "us".');
41
+ }
37
42
  /** Test hook: forget the cached clients. */
38
43
  export function resetClients() {
39
44
  clients.clear();
@@ -62,6 +62,9 @@ export type CreateSandboxBaseParams = {
62
62
  spot?: boolean;
63
63
  linkedSandbox?: string;
64
64
  secrets?: Record<string, string>;
65
+ /** The region for this create, over the client's target: "us" (Runtime's
66
+ * one region) or none; any other is refused. */
67
+ target?: string;
65
68
  /** Runtime-only: an explicit client, or fields for this create. */
66
69
  withruntime?: WithRuntime;
67
70
  };
@@ -183,7 +186,7 @@ export declare class Daytona implements AsyncDisposable {
183
186
  [Symbol.asyncDispose](): Promise<void>;
184
187
  /** Creates a sandbox and waits until it runs, with Daytona's defaults: 1
185
188
  * vCPU, 1 GiB, 3 GiB disk, pausing after 15 minutes without calls. Funding
186
- * is left to Runtime: the included usage first, then prepaid credit, exactly as withruntime's own create. */
189
+ * is left to Runtime: the free usage first, then prepaid credit, exactly as withruntime's own create. */
187
190
  create(params?: CreateSandboxFromSnapshotParams | CreateSandboxFromImageParams, options?: {
188
191
  timeout?: number;
189
192
  onSnapshotCreateLogs?: (chunk: string) => void;
@@ -1,4 +1,4 @@
1
- import { clientFor, } from "./client.js";
1
+ import { clientFor, refuseTarget, } from "./client.js";
2
2
  import { DaytonaError, DaytonaInvalidArgumentError, DaytonaNotFoundError, DaytonaQueueTimeoutError, guard, NotSupportedError, } from "./errors.js";
3
3
  import { RuntimeError } from "../errors.js";
4
4
  import { LEASE_MAX_SECONDS } from "../compat/lease.js";
@@ -184,7 +184,7 @@ export class Daytona {
184
184
  async [Symbol.asyncDispose]() { }
185
185
  /** Creates a sandbox and waits until it runs, with Daytona's defaults: 1
186
186
  * vCPU, 1 GiB, 3 GiB disk, pausing after 15 minutes without calls. Funding
187
- * is left to Runtime: the included usage first, then prepaid credit, exactly as withruntime's own create. */
187
+ * is left to Runtime: the free usage first, then prepaid credit, exactly as withruntime's own create. */
188
188
  async create(params = {}, options = {}) {
189
189
  refuseCreate(params);
190
190
  const timeout = options.timeout;
@@ -433,6 +433,7 @@ function refuseCreate(params) {
433
433
  gpuType !== "" &&
434
434
  (!Array.isArray(gpuType) || gpuType.length > 0)))
435
435
  throw new NotSupportedError("GPUs", "Runtime runs sandboxes on CPUs; remove gpu and gpuType from resources.");
436
+ refuseTarget(params.target);
436
437
  if (params.volumes?.some((mount) => mount.subpath))
437
438
  throw new NotSupportedError("Mounting part of a volume (subpath)", "Mount the whole volume.");
438
439
  if (params.kvm)
@@ -133,7 +133,7 @@ export declare class Sandbox {
133
133
  protected get client(): Runtime;
134
134
  /** Creates a sandbox from `template` (default "base", Runtime's stock image)
135
135
  * with E2B's default machine, 2 vCPU and 512 MiB, and waits until it runs.
136
- * Funding is left to Runtime: the included usage first, then prepaid credit, exactly as withruntime's own create. */
136
+ * Funding is left to Runtime: the free usage first, then prepaid credit, exactly as withruntime's own create. */
137
137
  static create<S extends typeof Sandbox>(this: S, opts?: SandboxOpts): Promise<InstanceType<S>>;
138
138
  static create<S extends typeof Sandbox>(this: S, template: string, opts?: SandboxOpts): Promise<InstanceType<S>>;
139
139
  /** Connects to a sandbox by id, waking it if it is paused, as E2B does. A
@@ -148,11 +148,12 @@ export declare class Sandbox {
148
148
  * deleted. */
149
149
  static kill(sandboxId: string, opts?: SandboxApiOpts): Promise<boolean>;
150
150
  kill(opts?: Pick<ConnectionOpts, "requestTimeoutMs" | "signal">): Promise<boolean>;
151
- /** Sets the sandbox to end `timeoutMs` from now, up to 24 hours, as E2B
152
- * does; the server keeps the deadline. Runtime cannot bring an end
153
- * sooner, so that is refused. A sandbox with no time limit (one on a pilot
154
- * account, kept running by the server) has no end to move, and nothing
155
- * changes. */
151
+ /** Sets the sandbox to end `timeoutMs` from now, sooner or later, up to 24
152
+ * hours, as E2B does: the server keeps the new time limit (an update's
153
+ * timeoutSeconds), and what the end does stays as created. Under a minute
154
+ * is a minute, Runtime's shortest limit. A sandbox with no time limit gets
155
+ * one; a persistent one (a pilot's, kept running by the server) has none to
156
+ * set, and nothing changes. */
156
157
  static setTimeout(sandboxId: string, timeoutMs: number, opts?: SandboxApiOpts): Promise<void>;
157
158
  setTimeout(timeoutMs: number, opts?: Pick<ConnectionOpts, "requestTimeoutMs" | "signal">): Promise<void>;
158
159
  static getInfo(sandboxId: string, opts?: SandboxApiOpts): Promise<SandboxInfo>;
@@ -414,11 +414,12 @@ export class Sandbox {
414
414
  throw error;
415
415
  }
416
416
  }
417
- /** Sets the sandbox to end `timeoutMs` from now, up to 24 hours, as E2B
418
- * does; the server keeps the deadline. Runtime cannot bring an end
419
- * sooner, so that is refused. A sandbox with no time limit (one on a pilot
420
- * account, kept running by the server) has no end to move, and nothing
421
- * changes. */
417
+ /** Sets the sandbox to end `timeoutMs` from now, sooner or later, up to 24
418
+ * hours, as E2B does: the server keeps the new time limit (an update's
419
+ * timeoutSeconds), and what the end does stays as created. Under a minute
420
+ * is a minute, Runtime's shortest limit. A sandbox with no time limit gets
421
+ * one; a persistent one (a pilot's, kept running by the server) has none to
422
+ * set, and nothing changes. */
422
423
  static async setTimeout(sandboxId, timeoutMs, opts = {}) {
423
424
  const client = clientFor(opts);
424
425
  const runtime = await guard("sandbox", () => client.sandboxes.get(sandboxId, request(opts)));
@@ -617,14 +618,10 @@ function secondsLater(runtime, timeoutMs) {
617
618
  return (Date.now() + timeoutMs - end) / 1000;
618
619
  }
619
620
  async function extendTo(runtime, timeoutMs, opts) {
620
- checkTimeout(timeoutMs);
621
- if (runtime.info.endsAt === null)
621
+ const seconds = timeoutSeconds(timeoutMs);
622
+ if (runtime.info.persistent === true)
622
623
  return;
623
- const later = secondsLater(runtime, timeoutMs);
624
- if (later < -1)
625
- throw new NotSupportedError("Ending a sandbox sooner than its time limit (a shorter setTimeout)", "Runtime cannot bring a time limit forward. Call kill() when the work is done.");
626
- if (later > 1)
627
- await guard("sandbox", () => runtime.extend(Math.ceil(later), request(opts)));
624
+ await guard("sandbox", () => runtime.update({ timeoutSeconds: seconds }, request(opts)));
628
625
  }
629
626
  async function pause(runtime, opts) {
630
627
  if (snapshotMemory("mode", opts.mode, opts.keepMemory) === false)
package/dist/errors.d.ts CHANGED
@@ -13,7 +13,7 @@ export declare const DELIBERATE: ReadonlySet<string>;
13
13
  * minutes when it has none), however many retries it allows otherwise. */
14
14
  export declare const NOTHING_RAN: ReadonlySet<string>;
15
15
  /** Refusals of a create that clear when a sandbox stops or pauses, or a host
16
- * frees room: an account without credit's eight at once and its total, an
16
+ * frees room: an account without credit's four at once and its total, an
17
17
  * email domain's, the account's quota and the region's capacity. `sandboxes.create` waits them out, retrying
18
18
  * with the same key and input, for up to `waitForCapacityMs`. */
19
19
  export declare const WAITS_FOR_ROOM: ReadonlySet<string>;
package/dist/errors.js CHANGED
@@ -27,7 +27,7 @@ export const NOTHING_RAN = new Set(["guest_busy", "busy", "rate_limited"]);
27
27
  * account without credit stops. */
28
28
  const PASSING = new Set(["no_capacity", "no_credit_running_limit", "no_credit_total_limit"]);
29
29
  /** Refusals of a create that clear when a sandbox stops or pauses, or a host
30
- * frees room: an account without credit's eight at once and its total, an
30
+ * frees room: an account without credit's four at once and its total, an
31
31
  * email domain's, the account's quota and the region's capacity. `sandboxes.create` waits them out, retrying
32
32
  * with the same key and input, for up to `waitForCapacityMs`. */
33
33
  export const WAITS_FOR_ROOM = new Set([
@@ -1,5 +1,5 @@
1
1
  import type { Runtime } from "../client.js";
2
- import type { Sandbox, Terminal } from "../sandbox.js";
2
+ import type { Sandbox } from "../sandbox.js";
3
3
  import type { Snapshot } from "../snapshots.js";
4
4
  import { CompatibilityError, type ClientOptions, type Refused, type RefusedAsync } from "../compat/core.js";
5
5
  export { CompatibilityError };
@@ -341,7 +341,30 @@ export interface PtyAttachOptions {
341
341
  session: PtySessionSelector;
342
342
  linuxUser?: string;
343
343
  }
344
+ export type PtyConnectionState = "connecting" | "connected" | "reconnecting" | "closed" | "exited";
345
+ export interface PtyReconnectOptions {
346
+ /** Total time to recover one dropped connection. Default: five minutes. */
347
+ timeoutMs?: number;
348
+ /** Initial backoff, with 30% jitter. Default: 400ms. */
349
+ initialDelayMs?: number;
350
+ /** Backoff ceiling before jitter. Default: 10 seconds. */
351
+ maxDelayMs?: number;
352
+ }
344
353
  export interface PtySessionEvents {
354
+ /** Reattach by itself after a dropped connection (Freestyle 0.2.18). */
355
+ reconnect?: boolean | PtyReconnectOptions;
356
+ /** Longest wait for each connection's terminal. Recovery waits at most 15
357
+ * seconds by default; a first connection, only when this is given. */
358
+ connectTimeoutMs?: number;
359
+ onStateChange?: (state: PtyConnectionState) => void;
360
+ /** Freestyle's exact replay boundaries. A Runtime terminal replays the
361
+ * session's output from its start on each connection without marking where
362
+ * the replay ends, as Freestyle's older guest agents do, so these never
363
+ * fire: the replay begins at the "connected" state. */
364
+ onReplayStart?: (info: {
365
+ exact: boolean;
366
+ }) => void;
367
+ onReplayEnd?: () => void;
345
368
  onData?: (data: Uint8Array) => void;
346
369
  onExit?: (exitCode: number) => void;
347
370
  onClose?: (info: {
@@ -353,25 +376,37 @@ export interface PtySessionEvents {
353
376
  }
354
377
  /** A session's number: FNV-1a of its process id, 31 bits, never 0. */
355
378
  export declare function sessionNumber(processId: string): number;
379
+ /** A stable handle across reattachments; input is never retried or buffered.
380
+ * Each connection is a Runtime terminal attached to the session's process. */
356
381
  export declare class PtySession {
357
382
  #private;
358
- private readonly terminal;
359
383
  private readonly sandbox;
360
384
  private readonly processId;
361
385
  readonly sessionId: number;
362
386
  readonly slug: string | undefined;
363
387
  readonly created: boolean;
364
- constructor(terminal: Terminal, sandbox: Sandbox, processId: string, sessionId: number, slug: string | undefined, created: boolean, events: PtySessionEvents);
365
- private readonly onError;
388
+ private readonly events;
389
+ /** Resolves once the first connection is up. */
390
+ readonly ready: Promise<void>;
391
+ constructor(sandbox: Sandbox, processId: string, sessionId: number, slug: string | undefined, created: boolean, events: PtySessionEvents, dimensions?: {
392
+ cols: number;
393
+ rows: number;
394
+ });
395
+ /** Retry now after a network change or a spent retry budget. Never opens a new shell. */
396
+ reconnect(): void;
397
+ /** Send input once. Throws while disconnected; uncertain keystrokes are never replayed. */
366
398
  write(data: Uint8Array | string): void;
399
+ /** Remembers the size and applies it again after a reattachment. */
367
400
  resize(options: {
368
401
  cols: number;
369
402
  rows: number;
370
403
  }): void;
371
404
  signal(sig: "sigint" | "sigkill"): void;
372
- /** Closes the connection; the shell keeps running and attach() finds it. */
405
+ /** Cancels recovery and closes the connection; the shell keeps running and
406
+ * attach() finds it. */
373
407
  detach(): void;
374
408
  get readyState(): number;
409
+ get state(): PtyConnectionState;
375
410
  }
376
411
  export declare class VmPty {
377
412
  private readonly get;
@@ -414,6 +449,43 @@ export declare class VmLinuxUserPty {
414
449
  exitCode?: undefined;
415
450
  }>;
416
451
  }
452
+ interface XtermDisposable {
453
+ dispose(): void;
454
+ }
455
+ interface XtermSequence {
456
+ prefix?: string;
457
+ intermediates?: string;
458
+ final: string;
459
+ }
460
+ /** The xterm.js surface connectXterm uses; xterm itself is not a dependency. */
461
+ export interface XtermTerminal {
462
+ cols: number;
463
+ rows: number;
464
+ write(data: string | Uint8Array, callback?: () => void): void;
465
+ onData(listener: (data: string) => void): XtermDisposable;
466
+ onResize(listener: (size: {
467
+ cols: number;
468
+ rows: number;
469
+ }) => void): XtermDisposable;
470
+ parser: {
471
+ registerCsiHandler(id: XtermSequence, handler: () => boolean): XtermDisposable;
472
+ registerDcsHandler(id: XtermSequence, handler: () => boolean): XtermDisposable;
473
+ };
474
+ }
475
+ export type ConnectXtermOptions = PtyOpenOptions & PtySessionEvents & {
476
+ pty: VmPty | VmLinuxUserPty;
477
+ terminal: XtermTerminal;
478
+ slug: string;
479
+ };
480
+ /** Freestyle 0.2.18's connectXterm: xterm.js on a named session that
481
+ * reattaches by itself unless `reconnect: false`. Each connection replays the
482
+ * session's output from its start, so the screen is reset first and the
483
+ * replay's terminal queries go unanswered until it has been written (150ms
484
+ * quiet, at most 2 seconds). dispose() detaches; the shell keeps running. */
485
+ export declare function connectXterm({ pty, terminal, ...options }: ConnectXtermOptions): Promise<{
486
+ session: PtySession;
487
+ dispose(): void;
488
+ }>;
417
489
  export declare class VmLinuxUser {
418
490
  private readonly vm;
419
491
  private readonly linuxUser;