withruntime 0.11.1 → 0.12.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 (176) hide show
  1. package/BLAXEL.md +3 -3
  2. package/COMPATIBILITY.md +74 -9
  3. package/DAYTONA.md +33 -5
  4. package/E2B.md +137 -48
  5. package/README.md +11 -9
  6. package/README.src.md +11 -9
  7. package/VERCEL.md +43 -5
  8. package/compatibility-lock.json +58 -49
  9. package/dist/billing-cli.d.ts +1 -1
  10. package/dist/billing-cli.js +77 -1
  11. package/dist/blaxel/errors.d.ts +14 -0
  12. package/dist/blaxel/errors.js +33 -4
  13. package/dist/blaxel/index.d.ts +2 -2
  14. package/dist/blaxel/index.js +4 -4
  15. package/dist/blaxel/interpreter.d.ts +2 -5
  16. package/dist/blaxel/sandbox.d.ts +15 -7
  17. package/dist/blaxel/sandbox.js +64 -9
  18. package/dist/blaxel/snapshot.d.ts +4 -0
  19. package/dist/blaxel/snapshot.js +4 -0
  20. package/dist/cli/logs.d.ts +21 -0
  21. package/dist/cli/logs.js +188 -0
  22. package/dist/cli/logs.test.d.ts +1 -0
  23. package/dist/cli/logs.test.js +272 -0
  24. package/dist/cli-extras.d.ts +3 -3
  25. package/dist/cli-extras.js +17 -12
  26. package/dist/cli-words.d.ts +10 -1
  27. package/dist/cli-words.js +18 -0
  28. package/dist/cli.d.ts +6 -2
  29. package/dist/cli.js +468 -219
  30. package/dist/client.d.ts +13 -63
  31. package/dist/client.js +35 -124
  32. package/dist/cloudflare/errors.d.ts +143 -0
  33. package/dist/cloudflare/errors.js +167 -0
  34. package/dist/cloudflare/index.d.ts +175 -6
  35. package/dist/cloudflare/index.js +416 -26
  36. package/dist/cloudflare/interpreter.d.ts +119 -0
  37. package/dist/cloudflare/interpreter.js +223 -0
  38. package/dist/cloudflare/streams.d.ts +38 -0
  39. package/dist/cloudflare/streams.js +145 -0
  40. package/dist/cloudflare/watch.d.ts +9 -0
  41. package/dist/cloudflare/watch.js +24 -0
  42. package/dist/codesandbox/index.d.ts +216 -28
  43. package/dist/codesandbox/index.js +478 -78
  44. package/dist/compat/core.d.ts +18 -0
  45. package/dist/compat/core.js +29 -0
  46. package/dist/compat/files.d.ts +1 -0
  47. package/dist/compat/files.js +25 -5
  48. package/dist/daytona/computer-use.d.ts +13 -4
  49. package/dist/daytona/computer-use.js +66 -8
  50. package/dist/daytona/daytona.d.ts +7 -2
  51. package/dist/daytona/daytona.js +31 -5
  52. package/dist/daytona/errors.d.ts +11 -0
  53. package/dist/daytona/errors.js +18 -0
  54. package/dist/daytona/filesystem.js +2 -1
  55. package/dist/daytona/image.d.ts +13 -9
  56. package/dist/daytona/image.js +18 -12
  57. package/dist/daytona/index.d.ts +1 -1
  58. package/dist/daytona/sandbox.d.ts +9 -3
  59. package/dist/daytona/sandbox.js +17 -3
  60. package/dist/domains/index.d.ts +9 -0
  61. package/dist/domains/index.js +10 -0
  62. package/dist/e2b/client.d.ts +15 -2
  63. package/dist/e2b/client.js +61 -7
  64. package/dist/e2b/commands.d.ts +4 -1
  65. package/dist/e2b/commands.js +17 -7
  66. package/dist/e2b/errors.d.ts +43 -2
  67. package/dist/e2b/errors.js +78 -6
  68. package/dist/e2b/filesystem.d.ts +1 -1
  69. package/dist/e2b/filesystem.js +82 -16
  70. package/dist/e2b/index.d.ts +3 -3
  71. package/dist/e2b/index.js +2 -2
  72. package/dist/e2b/sandbox.d.ts +50 -35
  73. package/dist/e2b/sandbox.js +228 -185
  74. package/dist/e2b/unsupported.d.ts +19 -0
  75. package/dist/e2b/unsupported.js +22 -1
  76. package/dist/e2b/users.d.ts +9 -0
  77. package/dist/e2b/users.js +22 -0
  78. package/dist/errors.d.ts +14 -5
  79. package/dist/errors.js +28 -14
  80. package/dist/freestyle/index.d.ts +285 -37
  81. package/dist/freestyle/index.js +656 -144
  82. package/dist/identity.d.ts +3 -0
  83. package/dist/identity.js +27 -0
  84. package/dist/images/index.d.ts +9 -0
  85. package/dist/images/index.js +10 -0
  86. package/dist/index.d.ts +1 -0
  87. package/dist/ips/index.d.ts +9 -0
  88. package/dist/ips/index.js +10 -0
  89. package/dist/jobs/index.d.ts +9 -0
  90. package/dist/jobs/index.js +10 -0
  91. package/dist/keys.js +2 -0
  92. package/dist/login.js +2 -0
  93. package/dist/machine.d.ts +189 -0
  94. package/dist/machine.js +521 -0
  95. package/dist/mcp.js +1 -1
  96. package/dist/modal/index.d.ts +339 -61
  97. package/dist/modal/index.js +781 -121
  98. package/dist/network-cli.d.ts +3 -1
  99. package/dist/network-cli.js +78 -36
  100. package/dist/networks/index.d.ts +11 -0
  101. package/dist/networks/index.js +10 -0
  102. package/dist/openai-agents/index.d.ts +1 -1
  103. package/dist/openai-agents/index.js +1 -1
  104. package/dist/ports/index.d.ts +9 -0
  105. package/dist/ports/index.js +10 -0
  106. package/dist/product-names.d.ts +52 -0
  107. package/dist/product-names.js +164 -0
  108. package/dist/product-renames.d.ts +103 -0
  109. package/dist/product-renames.js +189 -0
  110. package/dist/products/apps.d.ts +176 -0
  111. package/dist/products/apps.js +165 -0
  112. package/dist/products/billing.d.ts +41 -0
  113. package/dist/products/billing.js +13 -0
  114. package/dist/products/browser.d.ts +10 -0
  115. package/dist/products/browsers.d.ts +71 -0
  116. package/dist/products/browsers.js +102 -0
  117. package/dist/products/builds.d.ts +126 -0
  118. package/dist/products/builds.js +221 -0
  119. package/dist/products/index.d.ts +3 -16
  120. package/dist/products/index.js +3 -14
  121. package/dist/products/jobs.d.ts +19 -3
  122. package/dist/products/jobs.js +28 -12
  123. package/dist/products/limits.d.ts +4 -10
  124. package/dist/products/logs.d.ts +245 -0
  125. package/dist/products/logs.js +274 -0
  126. package/dist/products/logs.test.d.ts +1 -0
  127. package/dist/products/logs.test.js +343 -0
  128. package/dist/products/network-products.d.ts +38 -7
  129. package/dist/products/network-products.js +31 -18
  130. package/dist/products/network.d.ts +1 -1
  131. package/dist/products/observability.d.ts +3 -2
  132. package/dist/products/private-network.js +1 -1
  133. package/dist/products/queues.d.ts +149 -0
  134. package/dist/products/queues.js +213 -0
  135. package/dist/products/referrals.d.ts +21 -7
  136. package/dist/products/referrals.js +3 -3
  137. package/dist/products/secrets.d.ts +8 -4
  138. package/dist/products/secrets.js +39 -18
  139. package/dist/products/signed-requests.d.ts +21 -0
  140. package/dist/products/signed-requests.js +11 -0
  141. package/dist/products/streams.d.ts +73 -0
  142. package/dist/products/streams.js +17 -0
  143. package/dist/products/switching.d.ts +37 -10
  144. package/dist/products/switching.js +4 -3
  145. package/dist/products/volumes.d.ts +3 -0
  146. package/dist/record-cli.d.ts +23 -0
  147. package/dist/record-cli.js +308 -0
  148. package/dist/renamed-products.d.ts +38 -0
  149. package/dist/renamed-products.js +34 -0
  150. package/dist/runloop/index.d.ts +9 -2
  151. package/dist/runloop/index.js +29 -8
  152. package/dist/runloop/sdk.d.ts +131 -0
  153. package/dist/runloop/sdk.js +167 -0
  154. package/dist/sandbox.d.ts +10 -94
  155. package/dist/sandbox.js +22 -371
  156. package/dist/sandboxes/index.d.ts +11 -0
  157. package/dist/sandboxes/index.js +12 -0
  158. package/dist/snapshots/index.d.ts +9 -0
  159. package/dist/snapshots/index.js +11 -0
  160. package/dist/snapshots.d.ts +7 -4
  161. package/dist/sprites/index.d.ts +343 -31
  162. package/dist/sprites/index.js +866 -84
  163. package/dist/ssh.d.ts +1 -1
  164. package/dist/ssh.js +11 -11
  165. package/dist/terminal.d.ts +24 -0
  166. package/dist/terminal.js +32 -0
  167. package/dist/transport.d.ts +18 -2
  168. package/dist/transport.js +61 -6
  169. package/dist/tunnel.js +1 -1
  170. package/dist/types.d.ts +116 -8
  171. package/dist/vercel/sandbox.d.ts +1 -2
  172. package/dist/vercel/sandbox.js +6 -5
  173. package/dist/vercel/snapshot.js +10 -2
  174. package/dist/volumes/index.d.ts +9 -0
  175. package/dist/volumes/index.js +10 -0
  176. package/package.json +48 -2
@@ -0,0 +1,167 @@
1
+ import { Runloop, } from "./index.js";
2
+ import { only } from "../compat/core.js";
3
+ export class RunloopError extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = "RunloopError";
7
+ }
8
+ }
9
+ /** The high-level command handles share the low-level adapter's validation,
10
+ * cancellation and process input path; they never send a provider request. */
11
+ export class RunloopSDK {
12
+ api;
13
+ devbox;
14
+ constructor(options = {}) {
15
+ this.api = new Runloop(options);
16
+ this.devbox = {
17
+ fromId: (id) => new Devbox(this.api, id),
18
+ create: async (params = {}, request) => {
19
+ const info = await this.api.devboxes.createAndAwaitRunning(params, request);
20
+ return new Devbox(this.api, info.id);
21
+ },
22
+ };
23
+ }
24
+ get secret() {
25
+ return this.api.secrets;
26
+ }
27
+ get storageObject() {
28
+ return this.api.objects;
29
+ }
30
+ get axon() {
31
+ return this.api.axons;
32
+ }
33
+ get gatewayConfig() {
34
+ return this.api.gatewayConfigs;
35
+ }
36
+ get mcpConfig() {
37
+ return this.api.mcpConfigs;
38
+ }
39
+ get networkPolicy() {
40
+ return this.api.networkPolicies;
41
+ }
42
+ }
43
+ export class Devbox {
44
+ client;
45
+ id;
46
+ cmd;
47
+ constructor(client, id) {
48
+ this.client = client;
49
+ this.id = id;
50
+ const start = async (command, params = {}, options) => {
51
+ only("Runloop command; use devboxes.executions streams for output callbacks", params, [
52
+ "shell_name",
53
+ "attach_stdin",
54
+ ]);
55
+ const initial = await client.devboxes.executeAsync(id, { command, ...params }, options);
56
+ return new Execution(client, id, initial.execution_id, initial);
57
+ };
58
+ this.cmd = {
59
+ execAsync: start,
60
+ exec: async (command, params = {}, options) => {
61
+ if (params.attach_stdin)
62
+ throw new TypeError("attach_stdin requires cmd.execAsync");
63
+ return (await start(command, params, options)).result(options);
64
+ },
65
+ };
66
+ }
67
+ getInfo(options) {
68
+ return this.client.devboxes.retrieve(this.id, options);
69
+ }
70
+ shutdown(options) {
71
+ return this.client.devboxes.shutdown(this.id, {}, options);
72
+ }
73
+ suspend(options) {
74
+ return this.client.devboxes.suspend(this.id, options);
75
+ }
76
+ resume(options) {
77
+ return this.client.devboxes.resume(this.id, options);
78
+ }
79
+ }
80
+ export class Execution {
81
+ client;
82
+ devboxId;
83
+ executionId;
84
+ streamingPromise;
85
+ constructor(client, devboxId, executionId, _initialResult, streamingPromise) {
86
+ this.client = client;
87
+ this.devboxId = devboxId;
88
+ this.executionId = executionId;
89
+ this.streamingPromise = streamingPromise;
90
+ }
91
+ async sendStdIn(input, options) {
92
+ await this.input({ text: input }, options);
93
+ }
94
+ async closeStdIn(options) {
95
+ await this.input({ signal: "EOF" }, options);
96
+ }
97
+ async input(body, options) {
98
+ const response = await this.client.devboxes.executions.sendStdIn(this.devboxId, this.executionId, body, options);
99
+ if (!response.success)
100
+ throw new RunloopError(`Failed to send stdin to execution ${this.executionId}`);
101
+ }
102
+ getState(options) {
103
+ return this.client.devboxes.executions.retrieve(this.devboxId, this.executionId, {}, options);
104
+ }
105
+ async kill(options) {
106
+ await this.client.devboxes.executions.kill(this.devboxId, this.executionId, {}, options);
107
+ }
108
+ async result(options = {}) {
109
+ only("Runloop execution result; use Runtime process waits for other wait options", options, [
110
+ "signal",
111
+ "timeout",
112
+ "idempotencyKey",
113
+ ]);
114
+ const [command] = await Promise.allSettled([
115
+ this.client.devboxes.executions.awaitCompleted(this.devboxId, this.executionId, options),
116
+ this.streamingPromise,
117
+ ]);
118
+ if (command.status === "rejected")
119
+ throw command.reason;
120
+ return new ExecutionResult(this.client, this.devboxId, this.executionId, command.value);
121
+ }
122
+ }
123
+ export class ExecutionResult {
124
+ client;
125
+ devboxId;
126
+ executionId;
127
+ result;
128
+ constructor(client, devboxId, executionId, result) {
129
+ this.client = client;
130
+ this.devboxId = devboxId;
131
+ this.executionId = executionId;
132
+ this.result = result;
133
+ }
134
+ get exitCode() {
135
+ return this.result.exit_status ?? null;
136
+ }
137
+ get success() {
138
+ return this.exitCode === 0;
139
+ }
140
+ get failed() {
141
+ return this.exitCode !== null && this.exitCode !== 0;
142
+ }
143
+ stdout(numLines) {
144
+ return this.output("stdout", numLines);
145
+ }
146
+ stderr(numLines) {
147
+ return this.output("stderr", numLines);
148
+ }
149
+ async output(channel, numLines) {
150
+ let output = this.result[channel] ?? "";
151
+ if (this.result[`${channel}_truncated`] &&
152
+ (numLines === undefined || output.split("\n").filter(Boolean).length < numLines)) {
153
+ output = "";
154
+ const executions = this.client.devboxes.executions;
155
+ const stream = channel === "stdout"
156
+ ? executions.streamStdoutUpdates(this.devboxId, this.executionId)
157
+ : executions.streamStderrUpdates(this.devboxId, this.executionId);
158
+ for await (const chunk of stream)
159
+ output += chunk.output;
160
+ }
161
+ if (numLines === undefined)
162
+ return output;
163
+ if (numLines <= 0)
164
+ return "";
165
+ return output.replace(/\n+$/, "").split("\n").slice(-numLines).join("\n");
166
+ }
167
+ }
package/dist/sandbox.d.ts CHANGED
@@ -1,27 +1,26 @@
1
- import { Page } from "./page.js";
1
+ import type { Page } from "./page.js";
2
2
  import { type FileEvent, type WatchHandle, type WatchOptions } from "./products/watch.js";
3
3
  import { Transport, type RequestOptions } from "./transport.js";
4
- import type { CommandResult, DeletedSandbox, ExecOptions, FileEntry, OutputEvent, BinaryOutputEvent, KeepAliveOptions, ProcessInfo, SandboxInfo, SandboxSettings } from "./types.js";
4
+ import type { CommandResult, DeletedSandbox, ExecOptions, FileEntry, OutputEvent, BinaryOutputEvent, ProcessInfo, SandboxInfo, SandboxSettings } from "./types.js";
5
5
  import { type SandboxExtensions } from "./products/index.js";
6
- import type { Snapshot, SnapshotOptions } from "./snapshots.js";
7
6
  import { Tunnel, type PortForward } from "./tunnel.js";
8
7
  import type { StreamUploadSource } from "./file-source.js";
9
- /** DELETE /v1/sandboxes/{id}, for `sandbox.delete()` and
10
- * `runtime.sandboxes.delete(id)`. */
11
- export declare function deleteSandbox(t: Transport, id: string, options?: RequestOptions): Promise<DeletedSandbox>;
8
+ import { Machine, type MachineProduct } from "./machine.js";
9
+ /** Sandboxes, as a product of the machine engine (machine.ts). */
10
+ export declare const SANDBOXES: MachineProduct;
12
11
  /** A running (or stopped) sandbox. Every method is one API call or a short
13
- * sequence of them; `await using sbx = await Sandbox.create()` stops it for
14
- * you at the end of the block. */
12
+ * sequence of them; `await using sbx = await Sandbox.create()` deletes it at
13
+ * the end of the block, or stops it, keeping its disk, if created with
14
+ * `keep: true`. */
15
15
  export interface Sandbox extends SandboxExtensions {
16
16
  }
17
- export declare class Sandbox implements AsyncDisposable {
17
+ export declare class Sandbox extends Machine<SandboxInfo, SandboxSettings, DeletedSandbox> {
18
18
  #private;
19
19
  readonly files: Files;
20
20
  readonly processes: Processes;
21
21
  /** Short-lived tokens that let a browser reach this sandbox directly. */
22
22
  readonly sessions: SandboxSessions;
23
23
  constructor(transport: Transport, info: SandboxInfo);
24
- get id(): string;
25
24
  /** A sandbox reached with a session token instead of an API key, for code in
26
25
  * a browser: commands and their streams, processes, files and previews of
27
26
  * this one sandbox. Makes no call; `info` holds only the id until
@@ -39,14 +38,6 @@ export declare class Sandbox implements AsyncDisposable {
39
38
  apiUrl?: string;
40
39
  fetch?: typeof fetch;
41
40
  }): Sandbox;
42
- /** What the API last said about this sandbox. `refresh()` asks again. */
43
- get info(): SandboxInfo;
44
- get state(): SandboxInfo["state"];
45
- refresh(options?: RequestOptions): Promise<this>;
46
- /** Waits (server-side, no polling) until the sandbox reaches `state`. */
47
- waitFor(state: "running" | "paused" | "stopped", options?: {
48
- timeoutSeconds?: number;
49
- } & RequestOptions): Promise<this>;
50
41
  /** Runs a command and returns its exit code and output. A string runs under
51
42
  * `bash -c`; an array runs without a shell. With onStdout/onStderr the output
52
43
  * streams as it happens. A timeout is a result (timedOut: true, the output so
@@ -94,76 +85,6 @@ export declare class Sandbox implements AsyncDisposable {
94
85
  localPort?: number;
95
86
  host?: string;
96
87
  }): Promise<PortForward>;
97
- stop(options?: RequestOptions & {
98
- wait?: boolean;
99
- }): Promise<this>;
100
- pause(options?: RequestOptions & {
101
- wait?: boolean;
102
- }): Promise<this>;
103
- /** Deletes it for good: stops it if it runs or is paused, deletes its disk
104
- * and paused memory, revokes its previews and ports, and removes it from
105
- * lists. Its snapshots, usage and audit entries stay. Deleting it again
106
- * answers the same. */
107
- delete(options?: RequestOptions): Promise<DeletedSandbox>;
108
- /** Carries on a paused sandbox; for one with a time limit,
109
- * `timeoutSeconds` is its new one. */
110
- wake(options?: RequestOptions & {
111
- wait?: boolean;
112
- timeoutSeconds?: number;
113
- }): Promise<this>;
114
- /** More time before its time limit ends (at most an hour ahead of now). A
115
- * sandbox with no time limit needs none: the call answers at once and
116
- * changes nothing. */
117
- extend(seconds: number, options?: RequestOptions): Promise<this>;
118
- /** Changes its name, labels, automatic wake, idle pause or persistence;
119
- * fields left out stay as they are. `persistent: true` keeps it running
120
- * while credit lasts (paid only). */
121
- update(settings: SandboxSettings, options?: RequestOptions): Promise<this>;
122
- /** Moves it to another image (id, name, name:tag or name@version), keeping
123
- * its id, /workspace (its home: dotfiles, pip --user and npm -g installs),
124
- * volumes, environment, name and previews. Its processes restart, and
125
- * everything else on its old disk (sudo installs, apt packages, /etc) is
126
- * lost; snapshot it first to keep everything. A running sandbox is paused
127
- * first. A switch that fails is undone (`switch_undone`), the sandbox on
128
- * its old image with nothing lost. Charged as a wake. `keep` is optional:
129
- * "workspace" is the only value, and it is sent either way, so an API from
130
- * before it was optional accepts the call too. */
131
- switchImage(image: string, options?: RequestOptions & {
132
- keep?: "workspace";
133
- }): Promise<this>;
134
- /** Gives it more or fewer vCPUs or more or less memory by a restart: its id,
135
- * whole disk, volumes, environment, name and previews stay, and its
136
- * programs stop (snapshot it first to keep its memory too). A running sandbox is paused, or stopped if it is
137
- * persistent, and comes back running at the new size on the same server; a
138
- * paused or stopped one is started. Memory bills on the new size from then.
139
- * Refused, with nothing changed, when the server has no room
140
- * (`no_capacity`), above your quota or the trial's 2 vCPU and 4 GiB, or
141
- * while a snapshot or fork of it is being taken. `restart` is optional:
142
- * true is the only value, and it is sent either way, so an API from before
143
- * it was optional accepts the call too. */
144
- resize(size: {
145
- vcpu?: number;
146
- memoryMiB?: number;
147
- }, options?: RequestOptions & {
148
- restart?: true;
149
- }): Promise<this>;
150
- /** Keeps a sandbox with a time limit running past it, in the background,
151
- * until stop() or the returned function ends it: every `everySeconds` (60)
152
- * it extends the limit so that `marginSeconds` (600) remain, never more
153
- * than the hour ahead the API allows. A sandbox with no time limit
154
- * (`endsAt` null) needs none, and is only watched. Running time is billed
155
- * as it is used. A paused sandbox is left paused (a request wakes it,
156
- * unless autoWake is off); a stopped one ends the loop. It does not keep a
157
- * Node or Bun process alive by itself. */
158
- keepAlive(options?: KeepAliveOptions): () => void;
159
- /** Days a paused sandbox is kept before deletion (1 to 365). */
160
- setRetention(days: number, options?: RequestOptions): Promise<this>;
161
- /** Keeps this sandbox's whole machine (files, memory, running processes) as
162
- * a snapshot to start new sandboxes from. A running sandbox is paused for
163
- * the moment it takes, then woken; a paused one stays paused. Past
164
- * `timeoutMs` (ten minutes unless given) mid-capture, it stays paused and
165
- * the error names the snapshot. */
166
- snapshot(options?: SnapshotOptions & RequestOptions): Promise<Snapshot>;
167
88
  /** Copies of this sandbox as it is now (files, memory, running processes),
168
89
  * each its own sandbox, answered once they run. A running sandbox is paused
169
90
  * for the moment its snapshot takes, then woken. One without `count`; an
@@ -182,12 +103,6 @@ export declare class Sandbox implements AsyncDisposable {
182
103
  keepSnapshot?: boolean;
183
104
  funding?: "trial" | "paid";
184
105
  } & RequestOptions): Promise<Sandbox[]>;
185
- /** Starts a stopped persistent sandbox again from its disk (memory is not kept). */
186
- restart(options?: RequestOptions & {
187
- wait?: boolean;
188
- }): Promise<this>;
189
- [Symbol.asyncDispose](): Promise<void>;
190
- toJSON(): SandboxInfo;
191
106
  }
192
107
  /** A sandbox session, as the API answers it. `token` is set only in the
193
108
  * answer to `create`. */
@@ -411,6 +326,7 @@ export declare class Files {
411
326
  * the API's folder route. */
412
327
  download(remotePath: string, localPath: string, options?: RequestOptions): Promise<void>;
413
328
  }
329
+ /** One page of `runtime.sandboxes.list()`, and the pages after it. */
414
330
  export declare function sandboxPage(t: Transport, body: {
415
331
  data: SandboxInfo[];
416
332
  nextCursor: string | null;