withruntime 0.8.3 → 0.9.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 (92) hide show
  1. package/COMPATIBILITY.md +145 -0
  2. package/E2B.md +23 -13
  3. package/README.md +8 -5
  4. package/README.src.md +8 -5
  5. package/compatibility-lock.json +281 -0
  6. package/dist/ai-harness/index.js +2 -2
  7. package/dist/api-defaults.d.ts +17 -0
  8. package/dist/api-defaults.js +22 -0
  9. package/dist/blaxel/preview.d.ts +1 -1
  10. package/dist/blaxel/preview.js +1 -1
  11. package/dist/blaxel/process.d.ts +1 -1
  12. package/dist/blaxel/process.js +11 -2
  13. package/dist/cli-words.d.ts +27 -0
  14. package/dist/cli-words.js +135 -0
  15. package/dist/cli.js +162 -31
  16. package/dist/client.d.ts +4 -1
  17. package/dist/client.js +8 -2
  18. package/dist/cloudflare/index.d.ts +294 -0
  19. package/dist/cloudflare/index.js +554 -0
  20. package/dist/cloudflare/worker.d.ts +415 -0
  21. package/dist/cloudflare/worker.js +297 -0
  22. package/dist/codesandbox/guest-service.d.ts +31 -0
  23. package/dist/codesandbox/guest-service.js +453 -0
  24. package/dist/codesandbox/index.d.ts +386 -0
  25. package/dist/codesandbox/index.js +1313 -0
  26. package/dist/compat/core.d.ts +39 -0
  27. package/dist/compat/core.js +199 -0
  28. package/dist/compat/files.d.ts +26 -0
  29. package/dist/compat/files.js +115 -0
  30. package/dist/compat/shell.d.ts +4 -0
  31. package/dist/compat/shell.js +165 -0
  32. package/dist/daytona/context.js +2 -1
  33. package/dist/daytona/process.d.ts +1 -1
  34. package/dist/daytona/process.js +27 -4
  35. package/dist/e2b/code-interpreter.d.ts +4 -5
  36. package/dist/e2b/code-interpreter.js +54 -56
  37. package/dist/e2b/commands.d.ts +5 -4
  38. package/dist/e2b/commands.js +37 -29
  39. package/dist/e2b/errors.js +1 -1
  40. package/dist/e2b/filesystem.d.ts +8 -6
  41. package/dist/e2b/filesystem.js +127 -28
  42. package/dist/e2b/sandbox.d.ts +4 -3
  43. package/dist/e2b/sandbox.js +10 -10
  44. package/dist/errors.d.ts +8 -0
  45. package/dist/errors.js +15 -2
  46. package/dist/freestyle/index.d.ts +301 -0
  47. package/dist/freestyle/index.js +310 -0
  48. package/dist/index.d.ts +1 -0
  49. package/dist/mcp.js +2 -2
  50. package/dist/modal/index.d.ts +205 -0
  51. package/dist/modal/index.js +485 -0
  52. package/dist/network-cli.js +1 -1
  53. package/dist/openai-agents/index.js +9 -26
  54. package/dist/products/audit.d.ts +3 -0
  55. package/dist/products/index.d.ts +2 -0
  56. package/dist/products/index.js +2 -0
  57. package/dist/products/interpreter.d.ts +11 -4
  58. package/dist/products/interpreter.js +33 -9
  59. package/dist/products/jobs.d.ts +1 -1
  60. package/dist/products/jobs.js +5 -4
  61. package/dist/products/limits.d.ts +10 -0
  62. package/dist/products/previews.d.ts +10 -2
  63. package/dist/products/previews.js +3 -2
  64. package/dist/products/tailscale.d.ts +57 -0
  65. package/dist/products/tailscale.js +17 -0
  66. package/dist/products/watch.d.ts +25 -4
  67. package/dist/products/watch.js +75 -14
  68. package/dist/runloop/index.d.ts +340 -0
  69. package/dist/runloop/index.js +438 -0
  70. package/dist/runloop/pagination.d.ts +46 -0
  71. package/dist/runloop/pagination.js +82 -0
  72. package/dist/runloop/snapshots.d.ts +52 -0
  73. package/dist/runloop/snapshots.js +244 -0
  74. package/dist/sandbox.d.ts +40 -9
  75. package/dist/sandbox.js +273 -76
  76. package/dist/snapshots.d.ts +11 -1
  77. package/dist/snapshots.js +11 -1
  78. package/dist/sprites/index.d.ts +251 -0
  79. package/dist/sprites/index.js +552 -0
  80. package/dist/tar.d.ts +8 -1
  81. package/dist/tar.js +185 -35
  82. package/dist/tools/index.js +2 -1
  83. package/dist/transport.d.ts +20 -2
  84. package/dist/transport.js +48 -11
  85. package/dist/types.d.ts +52 -3
  86. package/dist/vercel/command.js +12 -1
  87. package/dist/vercel/errors.d.ts +2 -2
  88. package/dist/vercel/errors.js +12 -1
  89. package/dist/vercel/filesystem.d.ts +21 -18
  90. package/dist/vercel/filesystem.js +95 -48
  91. package/dist/vercel/sandbox.js +5 -5
  92. package/package.json +45 -3
@@ -0,0 +1,438 @@
1
+ import { PagePromise, DevboxViewsCursorIDPage } from "./pagination.js";
2
+ import { Sandbox } from "../sandbox.js";
3
+ import { DiskSnapshots, requestOptions, snapshotOptions, snapshotView, nativeOptions, } from "./snapshots.js";
4
+ export { DiskSnapshots, } from "./snapshots.js";
5
+ export { PagePromise, DevboxSnapshotViewsDiskSnapshotsCursorIDPage, DevboxViewsCursorIDPage, } from "./pagination.js";
6
+ import { client, CompatibilityError, create, destroy, execOptions, lookup, only, shellCommand, } from "../compat/core.js";
7
+ export { CompatibilityError };
8
+ export class LongPollAbortError extends Error {
9
+ lastResult;
10
+ constructor(message, lastResult) {
11
+ super(message);
12
+ this.lastResult = lastResult;
13
+ this.name = "LongPollAbortError";
14
+ }
15
+ }
16
+ export class PollingTimeoutError extends Error {
17
+ lastResult;
18
+ constructor(message, lastResult) {
19
+ super(`${message}. Last result: ${JSON.stringify(lastResult, null, 2)}`);
20
+ this.lastResult = lastResult;
21
+ this.name = "PollingTimeoutError";
22
+ }
23
+ }
24
+ export class MaxAttemptsExceededError extends Error {
25
+ lastResult;
26
+ constructor(message, lastResult) {
27
+ super(`${message}. Last result: ${JSON.stringify(lastResult, null, 2)}`);
28
+ this.lastResult = lastResult;
29
+ this.name = "MaxAttemptsExceededError";
30
+ }
31
+ }
32
+ const sizes = {
33
+ X_SMALL: [0.5, 1024, 4096],
34
+ SMALL: [1, 2048, 4096],
35
+ MEDIUM: [2, 4096, 8192],
36
+ LARGE: [2, 8192, 16384],
37
+ X_LARGE: [4, 16384, 16384],
38
+ XX_LARGE: [8, 32768, 16384],
39
+ };
40
+ const result = (id, r) => ({
41
+ devbox_id: id,
42
+ exit_status: r.exitCode,
43
+ stdout: r.stdout,
44
+ stderr: r.stderr,
45
+ stdout_truncated: r.stdoutTruncated ?? false,
46
+ stderr_truncated: r.stderrTruncated ?? false,
47
+ });
48
+ const SUSPENDED = "compat.runloop.suspended";
49
+ function view(sandbox) {
50
+ const i = sandbox.info;
51
+ return {
52
+ id: i.id,
53
+ name: i.name,
54
+ metadata: Object.fromEntries(Object.entries(i.labels).filter(([k]) => k !== "compat.provider" && k !== SUSPENDED)),
55
+ status: i.state === "paused"
56
+ ? "suspended"
57
+ : i.state === "stopped"
58
+ ? i.persistent && i.labels[SUSPENDED] === "true"
59
+ ? "suspended"
60
+ : "shutdown"
61
+ : i.state === "stopping" && i.labels[SUSPENDED] === "true"
62
+ ? "suspending"
63
+ : i.state === "pausing"
64
+ ? "suspending"
65
+ : i.state === "starting"
66
+ ? i.labels[SUSPENDED] === "true"
67
+ ? "resuming"
68
+ : "provisioning"
69
+ : i.state,
70
+ create_time_ms: Date.parse(i.createdAt),
71
+ end_time_ms: i.endedAt ? Date.parse(i.endedAt) : null,
72
+ capabilities: [],
73
+ state_transitions: [],
74
+ launch_parameters: {
75
+ custom_cpu_cores: i.vcpu,
76
+ custom_gb_memory: i.memoryMiB / 1024,
77
+ custom_disk_size: i.diskMiB / 1024,
78
+ keep_alive_time_seconds: i.timeoutSeconds,
79
+ resource_size_request: "CUSTOM_SIZE",
80
+ },
81
+ };
82
+ }
83
+ export class Runloop {
84
+ devboxes;
85
+ constructor(options = {}) {
86
+ const { bearerToken, baseURL, ...rest } = options;
87
+ this.devboxes = new Devboxes(client({ ...rest, apiKey: rest.apiKey ?? bearerToken, baseUrl: rest.baseUrl ?? baseURL }));
88
+ }
89
+ }
90
+ export default Runloop;
91
+ export class Devboxes {
92
+ runtime;
93
+ executions;
94
+ diskSnapshots;
95
+ transitions = new Map();
96
+ transition(id, operation) {
97
+ const pending = (this.transitions.get(id) ?? Promise.resolve())
98
+ .catch(() => undefined)
99
+ .then(operation);
100
+ this.transitions.set(id, pending);
101
+ void pending
102
+ .finally(() => {
103
+ if (this.transitions.get(id) === pending)
104
+ this.transitions.delete(id);
105
+ })
106
+ .catch(() => undefined);
107
+ return pending;
108
+ }
109
+ constructor(runtime) {
110
+ this.runtime = runtime;
111
+ this.executions = new Executions(runtime);
112
+ this.diskSnapshots = new DiskSnapshots(runtime);
113
+ }
114
+ async create(input = {}, options = {}) {
115
+ only("Runloop", input, [
116
+ "name",
117
+ "metadata",
118
+ "environment_variables",
119
+ "file_mounts",
120
+ "blueprint_id",
121
+ "blueprint_name",
122
+ "snapshot_id",
123
+ "launch_parameters",
124
+ ]);
125
+ const p = input.launch_parameters ?? {};
126
+ only("Runloop launch_parameters", p, [
127
+ "architecture",
128
+ "custom_cpu_cores",
129
+ "custom_gb_memory",
130
+ "custom_disk_size",
131
+ "resource_size_request",
132
+ "keep_alive_time_seconds",
133
+ "launch_commands",
134
+ ]);
135
+ if (p.architecture === "arm64")
136
+ throw new CompatibilityError("Runloop", "arm64 architecture");
137
+ if ([input.blueprint_id, input.blueprint_name, input.snapshot_id].filter(Boolean).length > 1)
138
+ throw new TypeError("Specify only one blueprint or snapshot.");
139
+ const size = p.resource_size_request && p.resource_size_request !== "CUSTOM_SIZE"
140
+ ? sizes[p.resource_size_request]
141
+ : undefined;
142
+ if ((p.custom_cpu_cores ?? size?.[0]) === 0.5)
143
+ throw new CompatibilityError("Runloop", "fractional CPU shape; Runtime sandbox CPU counts are whole cores");
144
+ const sandbox = await create(this.runtime, "runloop", {
145
+ name: input.name ?? undefined,
146
+ labels: input.metadata ?? undefined,
147
+ image: input.blueprint_id ?? input.blueprint_name ?? undefined,
148
+ snapshot: input.snapshot_id ?? undefined,
149
+ vcpu: p.custom_cpu_cores ?? size?.[0],
150
+ memoryMiB: p.custom_gb_memory ? p.custom_gb_memory * 1024 : size?.[1],
151
+ diskMiB: p.custom_disk_size ? p.custom_disk_size * 1024 : size?.[2],
152
+ timeoutSeconds: p.keep_alive_time_seconds ?? 3600,
153
+ idlePauseSeconds: 0,
154
+ onLeaseEnd: "stop",
155
+ }, input.environment_variables ?? undefined, async (sandbox) => {
156
+ for (const [path, content] of Object.entries(input.file_mounts ?? {}))
157
+ await sandbox.files.write(path, content);
158
+ for (const command of p.launch_commands ?? [])
159
+ await sandbox.exec(command, {
160
+ ...(await execOptions(sandbox)),
161
+ check: true,
162
+ signal: options.signal,
163
+ });
164
+ });
165
+ return view(sandbox);
166
+ }
167
+ createAndAwaitRunning(input, options) {
168
+ return this.create(input, options);
169
+ }
170
+ async retrieve(id) {
171
+ return view(await lookup(this.runtime, id));
172
+ }
173
+ async awaitRunning(id, options = {}) {
174
+ return this.waitForStatus(id, "running", options);
175
+ }
176
+ awaitDevboxRunning(id, options = {}) {
177
+ return this.awaitRunning(id, options);
178
+ }
179
+ async awaitSuspended(id, options = {}) {
180
+ return this.waitForStatus(id, "suspended", options);
181
+ }
182
+ async waitForStatus(id, target, options) {
183
+ const timeoutMs = options.longPoll?.timeoutMs ?? options.polling?.timeoutMs;
184
+ if (timeoutMs !== undefined && (!Number.isFinite(timeoutMs) || timeoutMs <= 0))
185
+ throw new Error("timeoutMs must be positive");
186
+ if (options.signal?.aborted)
187
+ throw new LongPollAbortError("Long poll aborted", undefined);
188
+ const deadline = timeoutMs === undefined ? undefined : AbortSignal.timeout(timeoutMs);
189
+ const signal = deadline && options.signal
190
+ ? AbortSignal.any([deadline, options.signal])
191
+ : (deadline ?? options.signal);
192
+ let lastResult;
193
+ try {
194
+ const s = await this.runtime.sandboxes.get(id, { signal, timeoutMs: options.timeout });
195
+ while (true) {
196
+ signal?.throwIfAborted();
197
+ const result = view(s);
198
+ if (result.status === target)
199
+ return result;
200
+ const transitional = target === "running"
201
+ ? ["starting", "resuming"].includes(s.state)
202
+ : s.state === "stopping" && s.info.labels[SUSPENDED] === "true";
203
+ if (!transitional)
204
+ throw new Error(`Devbox ${id} is in non-${target} state ${result.status}`);
205
+ // A status wait never wakes, restarts or otherwise changes a resource.
206
+ await s.waitFor(target === "running" ? "running" : "stopped", {
207
+ signal,
208
+ timeoutSeconds: 60,
209
+ timeoutMs: options.timeout ?? 65000,
210
+ });
211
+ lastResult = view(s);
212
+ }
213
+ }
214
+ catch (error) {
215
+ if (options.signal?.aborted)
216
+ throw new LongPollAbortError("Long poll aborted", lastResult);
217
+ if (deadline?.aborted)
218
+ throw new PollingTimeoutError(`Long poll timed out after ${timeoutMs}ms`, lastResult);
219
+ throw error;
220
+ }
221
+ }
222
+ list(query = {}, options = {}) {
223
+ if (requestOptions(query))
224
+ return this.list({}, query);
225
+ return new PagePromise(this.listPage(query, options));
226
+ }
227
+ async listPage(query, options) {
228
+ only("Runloop list", query, [
229
+ "name",
230
+ "limit",
231
+ "starting_after",
232
+ "metadata",
233
+ "status",
234
+ "include_total_count",
235
+ ]);
236
+ if (query.limit !== undefined && (!Number.isInteger(query.limit) || query.limit <= 0))
237
+ throw new TypeError("limit must be positive");
238
+ const values = [];
239
+ let cursor = null;
240
+ do {
241
+ const page = await this.runtime.transport.json({
242
+ method: "GET",
243
+ path: "/v1/sandboxes",
244
+ query: {
245
+ includeStopped: true,
246
+ label: ["compat.provider:runloop"],
247
+ cursor: cursor ?? undefined,
248
+ limit: 100,
249
+ },
250
+ ...nativeOptions(options),
251
+ });
252
+ for (const info of page.data) {
253
+ if (info.labels["compat.provider"] !== "runloop")
254
+ continue;
255
+ const value = view(new Sandbox(this.runtime.transport, info));
256
+ if ((!query.name || value.name === query.name) &&
257
+ (!query.status || value.status === query.status) &&
258
+ Object.entries(query.metadata ?? {}).every(([key, expected]) => value.metadata[key] === expected))
259
+ values.push(value);
260
+ }
261
+ cursor = page.nextCursor;
262
+ } while (cursor !== null);
263
+ const offset = query.starting_after
264
+ ? values.findIndex((value) => value.id === query.starting_after) + 1
265
+ : 0;
266
+ if (query.starting_after && offset === 0)
267
+ throw new TypeError("starting_after does not identify a matching devbox");
268
+ const data = values.slice(offset, offset + (query.limit ?? 20));
269
+ return new DevboxViewsCursorIDPage(data, offset + data.length < values.length, query.include_total_count === false ? 0 : values.length, (after) => this.listPage({ ...query, starting_after: after }, options));
270
+ }
271
+ suspend(id) {
272
+ return this.transition(id, async () => {
273
+ const s = await lookup(this.runtime, id);
274
+ if (s.state === "stopped") {
275
+ if (s.info.persistent && s.info.labels[SUSPENDED] === "true")
276
+ return view(s);
277
+ throw new Error("A shut down Devbox cannot be suspended");
278
+ }
279
+ // ARCHITECTURE.md section 10: paid disk persistence must be admitted
280
+ // before stop; an uncertain stop keeps retention enabled to preserve bytes.
281
+ await s.update({ persistent: true, labels: { ...s.info.labels, [SUSPENDED]: "true" } });
282
+ await s.stop();
283
+ return view(s);
284
+ });
285
+ }
286
+ resume(id) {
287
+ return this.transition(id, async () => {
288
+ const s = await lookup(this.runtime, id);
289
+ if (s.state === "stopped") {
290
+ if (!s.info.persistent || s.info.labels[SUSPENDED] !== "true")
291
+ throw new Error("A shut down Devbox cannot be resumed");
292
+ await s.restart();
293
+ }
294
+ else if (s.state === "paused" || s.state === "pausing") {
295
+ // Migrate older adapter pauses through the same disk-only boundary.
296
+ await s.update({ persistent: true, labels: { ...s.info.labels, [SUSPENDED]: "true" } });
297
+ await s.stop();
298
+ await s.restart();
299
+ }
300
+ else
301
+ await s.waitFor("running");
302
+ if (s.info.labels[SUSPENDED] !== undefined) {
303
+ const labels = { ...s.info.labels };
304
+ delete labels[SUSPENDED];
305
+ await s.update({ labels });
306
+ }
307
+ return view(s);
308
+ });
309
+ }
310
+ shutdown(id) {
311
+ return this.transition(id, async () => {
312
+ const s = await lookup(this.runtime, id);
313
+ await destroy(s);
314
+ return view(s);
315
+ });
316
+ }
317
+ executeSync(id, body, options) {
318
+ return this.executions.executeSync(id, body, options);
319
+ }
320
+ executeAsync(id, body, options) {
321
+ return this.executions.executeAsync(id, body, options);
322
+ }
323
+ async executeAndAwaitCompletion(id, body, options) {
324
+ const execution = await this.executeAsync(id, body, options);
325
+ return this.executions.awaitCompleted(id, execution.execution_id, options);
326
+ }
327
+ async readFileContents(id, body) {
328
+ only("Runloop read", body, ["file_path"]);
329
+ return (await lookup(this.runtime, id)).files.readText(body.file_path);
330
+ }
331
+ async writeFileContents(id, body) {
332
+ only("Runloop write", body, ["file_path", "contents"]);
333
+ await (await lookup(this.runtime, id)).files.write(body.file_path, body.contents);
334
+ return result(id, { stdout: "", stderr: "", exitCode: 0, timedOut: false });
335
+ }
336
+ async snapshotDisk(id, body = {}, options = {}) {
337
+ if (requestOptions(body))
338
+ return this.snapshotDisk(id, {}, body);
339
+ const input = snapshotOptions(body);
340
+ options.signal?.throwIfAborted();
341
+ return this.transition(id, async () => {
342
+ const sandbox = await this.runtime.sandboxes.get(id, nativeOptions(options));
343
+ return snapshotView(await sandbox.snapshot({ ...input, ...nativeOptions(options) }));
344
+ });
345
+ }
346
+ async snapshotDiskAsync(_id, _body, _options) {
347
+ throw new CompatibilityError("Runloop", "asynchronous snapshot capture; use snapshotDisk until server-side capture/resume coordination is available");
348
+ }
349
+ deleteDiskSnapshot(id, options) {
350
+ return this.diskSnapshots.delete(id, options);
351
+ }
352
+ listDiskSnapshots(query = {}, options) {
353
+ return requestOptions(query)
354
+ ? this.diskSnapshots.list(query)
355
+ : this.diskSnapshots.list(query, options);
356
+ }
357
+ }
358
+ export class Executions {
359
+ runtime;
360
+ constructor(runtime) {
361
+ this.runtime = runtime;
362
+ }
363
+ async executeSync(id, body, options = {}) {
364
+ only("Runloop execute", body, ["command", "shell_name", "attach_stdin"]);
365
+ if (body.attach_stdin)
366
+ throw new TypeError("attach_stdin requires executeAsync");
367
+ const s = await lookup(this.runtime, id);
368
+ return result(id, await s.exec(await shellCommand(s, body.command, body.shell_name), await execOptions(s, {
369
+ signal: options.signal,
370
+ timeoutMs: options.timeout,
371
+ idempotencyKey: options.idempotencyKey,
372
+ onStdout: () => { },
373
+ })));
374
+ }
375
+ async executeAsync(id, body, options = {}) {
376
+ only("Runloop execute", body, ["command", "shell_name", "attach_stdin"]);
377
+ if (body.shell_name && body.attach_stdin)
378
+ throw new CompatibilityError("Runloop", "interactive stdin in a named shell");
379
+ const s = await lookup(this.runtime, id);
380
+ const p = await s.spawn(await shellCommand(s, body.command, body.shell_name), {
381
+ ...(await execOptions(s, { signal: options.signal, idempotencyKey: options.idempotencyKey })),
382
+ stdin: body.attach_stdin ? "pipe" : undefined,
383
+ });
384
+ return { devbox_id: id, execution_id: p.id, status: "running" };
385
+ }
386
+ async retrieve(id, execution) {
387
+ const p = await (await lookup(this.runtime, id)).processes.get(execution);
388
+ return p.info.state === "running"
389
+ ? { devbox_id: id, execution_id: execution, status: "running" }
390
+ : { ...result(id, await p.wait()), execution_id: execution, status: "completed" };
391
+ }
392
+ async awaitCompleted(id, execution, options = {}) {
393
+ const p = await (await lookup(this.runtime, id)).processes.get(execution);
394
+ return {
395
+ ...result(id, await p.wait({ signal: options.signal })),
396
+ execution_id: execution,
397
+ status: "completed",
398
+ };
399
+ }
400
+ async kill(id, execution, body = {}) {
401
+ only("Runloop kill", body, ["kill_process_group"]);
402
+ if (body.kill_process_group)
403
+ throw new CompatibilityError("Runloop", "kill_process_group");
404
+ await (await (await lookup(this.runtime, id)).processes.get(execution)).kill("SIGKILL");
405
+ return this.retrieve(id, execution);
406
+ }
407
+ async sendStdIn(id, execution, body = {}) {
408
+ const p = await (await lookup(this.runtime, id)).processes.get(execution);
409
+ if (body.text || body.signal === "EOF")
410
+ await p.write(body.text ?? "", { eof: body.signal === "EOF" });
411
+ if (body.signal === "INTERRUPT")
412
+ await p.kill("SIGINT");
413
+ return { devbox_id: id, execution_id: execution, success: true };
414
+ }
415
+ async *streamStdoutUpdates(id, execution, query = {}, options = {}) {
416
+ yield* this.stream(id, execution, "stdout", query, options);
417
+ }
418
+ async *streamStderrUpdates(id, execution, query = {}, options = {}) {
419
+ yield* this.stream(id, execution, "stderr", query, options);
420
+ }
421
+ async *stream(id, execution, channel, query, options) {
422
+ const p = await (await lookup(this.runtime, id)).processes.get(execution);
423
+ let offset = 0;
424
+ const from = Number(query.offset ?? 0);
425
+ for await (const event of p.output({ signal: options.signal }))
426
+ if (event.type === channel) {
427
+ const bytes = new TextEncoder().encode(event.data);
428
+ if (offset + bytes.length > from)
429
+ yield {
430
+ output: new TextDecoder().decode(bytes.subarray(Math.max(0, from - offset))),
431
+ offset: Math.max(offset, from),
432
+ };
433
+ offset += bytes.length;
434
+ }
435
+ else if (event.type === "truncated")
436
+ throw new Error("Runloop execution output was truncated by the host.");
437
+ }
438
+ }
@@ -0,0 +1,46 @@
1
+ import type { DevboxSnapshotView } from "./snapshots.js";
2
+ import type { DevboxView } from "./index.js";
3
+ /** Runloop's cursor pages support both awaited pages and unawaited iteration. */
4
+ export declare class CursorPage<T extends {
5
+ id: string;
6
+ }> implements AsyncIterable<T> {
7
+ readonly data: T[];
8
+ readonly has_more: boolean;
9
+ readonly total_count: number;
10
+ private readonly fetchNext;
11
+ constructor(data: T[], has_more: boolean, total_count: number, fetchNext: (after: string) => Promise<CursorPage<T>>);
12
+ getPaginatedItems(): T[];
13
+ hasNextPage(): boolean;
14
+ nextPageInfo(): {
15
+ params: {
16
+ starting_after: string;
17
+ };
18
+ } | null;
19
+ nextPageParams(): {
20
+ starting_after: string;
21
+ } | null;
22
+ getNextPage(): Promise<CursorPage<T>>;
23
+ iterPages(): AsyncGenerator<CursorPage<T>>;
24
+ [Symbol.asyncIterator](): AsyncGenerator<T>;
25
+ }
26
+ export declare class DevboxSnapshotViewsDiskSnapshotsCursorIDPage<T extends {
27
+ id: string;
28
+ } = DevboxSnapshotView> extends CursorPage<T> {
29
+ get snapshots(): T[];
30
+ getNextPage(): Promise<DevboxSnapshotViewsDiskSnapshotsCursorIDPage<T>>;
31
+ iterPages(): AsyncGenerator<DevboxSnapshotViewsDiskSnapshotsCursorIDPage<T>>;
32
+ }
33
+ export declare class DevboxViewsCursorIDPage<T extends {
34
+ id: string;
35
+ } = DevboxView> extends CursorPage<T> {
36
+ get devboxes(): T[];
37
+ getNextPage(): Promise<DevboxViewsCursorIDPage<T>>;
38
+ iterPages(): AsyncGenerator<DevboxViewsCursorIDPage<T>>;
39
+ }
40
+ export declare class PagePromise<T extends {
41
+ id: string;
42
+ }, P extends CursorPage<T>> extends Promise<P> implements AsyncIterable<T> {
43
+ static get [Symbol.species](): PromiseConstructor;
44
+ constructor(promise: Promise<P>);
45
+ [Symbol.asyncIterator](): AsyncGenerator<T>;
46
+ }
@@ -0,0 +1,82 @@
1
+ /** Runloop's cursor pages support both awaited pages and unawaited iteration. */
2
+ export class CursorPage {
3
+ data;
4
+ has_more;
5
+ total_count;
6
+ fetchNext;
7
+ constructor(data, has_more, total_count, fetchNext) {
8
+ this.data = data;
9
+ this.has_more = has_more;
10
+ this.total_count = total_count;
11
+ this.fetchNext = fetchNext;
12
+ }
13
+ getPaginatedItems() {
14
+ return this.data;
15
+ }
16
+ hasNextPage() {
17
+ return this.has_more && this.data.length > 0;
18
+ }
19
+ nextPageInfo() {
20
+ const last = this.data.at(-1);
21
+ return last ? { params: { starting_after: last.id } } : null;
22
+ }
23
+ nextPageParams() {
24
+ return this.nextPageInfo()?.params ?? null;
25
+ }
26
+ getNextPage() {
27
+ const next = this.nextPageInfo();
28
+ if (!next)
29
+ return Promise.reject(new Error("No next page expected; please check `.hasNextPage()` before calling `.getNextPage()`."));
30
+ return this.fetchNext(next.params.starting_after);
31
+ }
32
+ async *iterPages() {
33
+ yield this;
34
+ if (!this.hasNextPage())
35
+ return;
36
+ let page = await this.getNextPage();
37
+ yield page;
38
+ while (page.hasNextPage()) {
39
+ page = await page.getNextPage();
40
+ yield page;
41
+ }
42
+ }
43
+ async *[Symbol.asyncIterator]() {
44
+ for await (const page of this.iterPages())
45
+ yield* page.getPaginatedItems();
46
+ }
47
+ }
48
+ export class DevboxSnapshotViewsDiskSnapshotsCursorIDPage extends CursorPage {
49
+ get snapshots() {
50
+ return this.data;
51
+ }
52
+ getNextPage() {
53
+ return super.getNextPage();
54
+ }
55
+ async *iterPages() {
56
+ for await (const page of super.iterPages())
57
+ yield page;
58
+ }
59
+ }
60
+ export class DevboxViewsCursorIDPage extends CursorPage {
61
+ get devboxes() {
62
+ return this.data;
63
+ }
64
+ getNextPage() {
65
+ return super.getNextPage();
66
+ }
67
+ async *iterPages() {
68
+ for await (const page of super.iterPages())
69
+ yield page;
70
+ }
71
+ }
72
+ export class PagePromise extends Promise {
73
+ static get [Symbol.species]() {
74
+ return Promise;
75
+ }
76
+ constructor(promise) {
77
+ super((resolve, reject) => promise.then(resolve, reject));
78
+ }
79
+ async *[Symbol.asyncIterator]() {
80
+ yield* await this;
81
+ }
82
+ }
@@ -0,0 +1,52 @@
1
+ import type { Runtime } from "../client.js";
2
+ import type { Snapshot, SnapshotOptions } from "../snapshots.js";
3
+ import { type RequestOptions, type LongPollRequestOptions } from "./index.js";
4
+ import { DevboxSnapshotViewsDiskSnapshotsCursorIDPage, PagePromise } from "./pagination.js";
5
+ export interface DevboxSnapshotDiskParams {
6
+ name?: string | null;
7
+ metadata?: Record<string, string> | null;
8
+ commit_message?: string | null;
9
+ }
10
+ export interface DevboxSnapshotView {
11
+ id: string;
12
+ create_time_ms: number;
13
+ metadata: Record<string, string>;
14
+ source_devbox_id: string;
15
+ commit_message?: string | null;
16
+ name?: string | null;
17
+ size_bytes?: number | null;
18
+ source_blueprint_id?: string | null;
19
+ }
20
+ export interface DevboxSnapshotAsyncStatusView {
21
+ status: "in_progress" | "error" | "complete" | "deleted";
22
+ error_message?: string | null;
23
+ snapshot?: DevboxSnapshotView | null;
24
+ }
25
+ export interface DiskSnapshotListParams {
26
+ starting_after?: string;
27
+ limit?: number;
28
+ devbox_id?: string;
29
+ include_total_count?: boolean;
30
+ source_blueprint_id?: string;
31
+ [key: `metadata[${string}]`]: string | undefined;
32
+ }
33
+ export declare function requestOptions(value: object): value is RequestOptions;
34
+ export declare function nativeOptions(options: RequestOptions): {
35
+ signal: AbortSignal | undefined;
36
+ timeoutMs: number | undefined;
37
+ idempotencyKey: string | undefined;
38
+ };
39
+ export declare function snapshotOptions(body: DevboxSnapshotDiskParams): SnapshotOptions;
40
+ export declare function snapshotView(snapshot: Snapshot): DevboxSnapshotView;
41
+ export declare class DiskSnapshots {
42
+ private readonly runtime;
43
+ constructor(runtime: Runtime);
44
+ update(id: string, body?: DevboxSnapshotDiskParams, options?: RequestOptions): Promise<DevboxSnapshotView>;
45
+ update(id: string, options?: RequestOptions): Promise<DevboxSnapshotView>;
46
+ awaitCompleted(id: string, options?: LongPollRequestOptions): Promise<DevboxSnapshotAsyncStatusView>;
47
+ queryStatus(id: string, options?: RequestOptions): Promise<DevboxSnapshotAsyncStatusView>;
48
+ delete(id: string, options?: RequestOptions): Promise<unknown>;
49
+ list(query?: DiskSnapshotListParams, options?: RequestOptions): PagePromise<DevboxSnapshotView, DevboxSnapshotViewsDiskSnapshotsCursorIDPage<DevboxSnapshotView>>;
50
+ list(options?: RequestOptions): PagePromise<DevboxSnapshotView, DevboxSnapshotViewsDiskSnapshotsCursorIDPage<DevboxSnapshotView>>;
51
+ private page;
52
+ }