withruntime 0.11.0 → 0.11.1

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 (60) hide show
  1. package/BLAXEL.md +7 -8
  2. package/DAYTONA.md +7 -8
  3. package/E2B.md +16 -2
  4. package/README.md +3 -3
  5. package/README.src.md +3 -3
  6. package/VERCEL.md +3 -2
  7. package/compatibility-lock.json +55 -0
  8. package/dist/api-defaults.d.ts +1 -1
  9. package/dist/api-defaults.js +1 -1
  10. package/dist/blaxel/lookup.d.ts +2 -1
  11. package/dist/blaxel/lookup.js +2 -1
  12. package/dist/blaxel/sandbox.d.ts +7 -5
  13. package/dist/blaxel/sandbox.js +15 -10
  14. package/dist/cli.js +39 -15
  15. package/dist/client.d.ts +5 -1
  16. package/dist/client.js +19 -3
  17. package/dist/compat/files.js +5 -1
  18. package/dist/compat/lease.d.ts +1 -0
  19. package/dist/compat/lease.js +14 -8
  20. package/dist/credentials.d.ts +9 -0
  21. package/dist/credentials.js +26 -4
  22. package/dist/daytona/daytona.js +29 -12
  23. package/dist/daytona/sandbox.d.ts +8 -3
  24. package/dist/daytona/sandbox.js +23 -4
  25. package/dist/e2b/sandbox.d.ts +7 -3
  26. package/dist/e2b/sandbox.js +21 -7
  27. package/dist/errors.d.ts +6 -0
  28. package/dist/errors.js +6 -0
  29. package/dist/identity.js +2 -1
  30. package/dist/index.d.ts +2 -1
  31. package/dist/index.js +2 -1
  32. package/dist/modal/index.js +6 -2
  33. package/dist/network-cli.js +7 -7
  34. package/dist/products/browser.d.ts +2 -1
  35. package/dist/products/browser.js +9 -19
  36. package/dist/products/desktop.d.ts +2 -1
  37. package/dist/products/desktop.js +5 -30
  38. package/dist/products/jobs.d.ts +7 -5
  39. package/dist/products/jobs.js +7 -3
  40. package/dist/products/mcp.d.ts +5 -1
  41. package/dist/products/mcp.js +29 -8
  42. package/dist/products/network-products.d.ts +1 -1
  43. package/dist/products/network-products.js +1 -1
  44. package/dist/products/observability.d.ts +1 -1
  45. package/dist/products/private-network.d.ts +1 -1
  46. package/dist/products/secrets.d.ts +2 -2
  47. package/dist/runloop/index.js +6 -2
  48. package/dist/sandbox.d.ts +27 -19
  49. package/dist/sandbox.js +121 -54
  50. package/dist/tar.d.ts +2 -0
  51. package/dist/tar.js +74 -16
  52. package/dist/transport.d.ts +33 -7
  53. package/dist/transport.js +171 -36
  54. package/dist/types.d.ts +54 -9
  55. package/dist/vercel/filesystem.js +3 -1
  56. package/dist/vercel/sandbox.d.ts +17 -10
  57. package/dist/vercel/sandbox.js +26 -13
  58. package/dist/wait.d.ts +12 -0
  59. package/dist/wait.js +47 -0
  60. package/package.json +1 -1
@@ -13,7 +13,9 @@ import { Snapshot } from "./snapshot.js";
13
13
  * 2026). Runtime gives 2 vCPU and 4096 MiB. */
14
14
  export const DEFAULT_VCPUS = 2;
15
15
  export const MEMORY_MIB_PER_VCPU = 2048;
16
- /** Vercel's default timeout, 5 minutes. */
16
+ /** Vercel's default timeout, 5 minutes. Not sent: a sandbox created with no
17
+ * timeout has no time limit on Runtime, running while it works and pausing
18
+ * when idle (0300). Kept for code that imports it. */
17
19
  export const DEFAULT_TIMEOUT_MS = 300_000;
18
20
  const MIN_LEASE_SECONDS = 60;
19
21
  /** Extend a long timeout's lease when less than this is left. */
@@ -235,7 +237,8 @@ export class Sandbox {
235
237
  return statusOf(this.#rt.state);
236
238
  }
237
239
  /** Milliseconds from the start of the session: the timeout given at
238
- * create, with every extension, or the lease of a sandbox found by name. */
240
+ * create, with every extension, or the time limit of a sandbox found by
241
+ * name. 0 for one with no time limit, which runs while it works. */
239
242
  get timeout() {
240
243
  return this.#timeoutMs ?? this.#rt.info.timeoutSeconds * 1000;
241
244
  }
@@ -255,7 +258,8 @@ export class Sandbox {
255
258
  }
256
259
  // ---- create, get, list ------------------------------------------------
257
260
  /** Creates a sandbox and waits until it runs. Vercel's defaults: 2 vCPUs,
258
- * 2048 MiB per vCPU, a 5-minute timeout, persistent. Funding is left to
261
+ * 2048 MiB per vCPU, persistent. With no `timeout` it has no time limit: it
262
+ * runs while it works and pauses when idle. A `timeout` is kept as given. Funding is left to
259
263
  * Runtime: the free trial while the account has trial time, then prepaid
260
264
  * credit, exactly as withruntime's own create. */
261
265
  static async create(params = {}) {
@@ -268,7 +272,8 @@ export class Sandbox {
268
272
  const source = await guard(() => resolveImage(client, params));
269
273
  const input = {
270
274
  ...(source.snapshot ? {} : { vcpu: vcpus, memoryMiB: vcpus * MEMORY_MIB_PER_VCPU }),
271
- timeoutSeconds: leaseSeconds(params.timeout ?? DEFAULT_TIMEOUT_MS),
275
+ // No timeout, no time limit: it runs while it works (0300).
276
+ ...(params.timeout === undefined ? {} : { timeoutSeconds: leaseSeconds(params.timeout) }),
272
277
  onLeaseEnd: persistent ? "pause" : "stop",
273
278
  ...(params.name ? { name: params.name } : {}),
274
279
  ...(params.tags && Object.keys(params.tags).length ? { labels: params.tags } : {}),
@@ -280,7 +285,7 @@ export class Sandbox {
280
285
  const sandbox = new Sandbox({
281
286
  runtime,
282
287
  client,
283
- params: { ...params, persistent, timeout: params.timeout ?? DEFAULT_TIMEOUT_MS },
288
+ params: { ...params, persistent },
284
289
  });
285
290
  try {
286
291
  await sandbox.#setUp(params);
@@ -643,20 +648,25 @@ export class Sandbox {
643
648
  return;
644
649
  await this.stop().catch(() => undefined);
645
650
  }
646
- /** Moves the end `duration` milliseconds later. The lease moves at once
647
- * as far as it may (an hour ahead of now); past that it is extended while
648
- * this object lives. */
651
+ /** Moves the end `duration` milliseconds later. The time limit moves at
652
+ * once as far as it may (an hour ahead of now); past that it is extended
653
+ * while this object lives. A sandbox with no time limit has no end to
654
+ * move, and nothing changes. */
649
655
  async extendTimeout(duration, opts = {}) {
650
656
  if (!Number.isFinite(duration) || duration <= 0)
651
657
  throw new RangeError(`duration must be a positive number of milliseconds, not ${duration}.`);
652
658
  await this.#withResume(async (runtime) => {
659
+ if (runtime.info.endsAt === null)
660
+ return;
653
661
  this.#timeoutMs = this.timeout + duration;
654
662
  await this.#leaseToTimeout(runtime, opts);
655
663
  }, opts);
656
664
  }
657
665
  /** Extends the lease now toward the timeout, an hour ahead at most. */
658
666
  async #leaseToTimeout(runtime, opts) {
659
- const seconds = extensionSeconds(Date.parse(runtime.info.expiresAt), this.#until(), Date.now(), Infinity);
667
+ if (runtime.info.endsAt === null)
668
+ return;
669
+ const seconds = extensionSeconds(Date.parse(runtime.info.endsAt ?? runtime.info.expiresAt), this.#until(), Date.now(), Infinity);
660
670
  if (seconds >= 1)
661
671
  await runtime.extend(seconds, signalOf(opts));
662
672
  }
@@ -669,7 +679,8 @@ export class Sandbox {
669
679
  }
670
680
  /** Changes what Runtime can change on a sandbox: `timeout`, `ports`,
671
681
  * `networkPolicy` and `snapshotExpiration`. Anything else is refused before
672
- * anything changes. */
682
+ * anything changes. A `timeout` on a sandbox with no time limit changes
683
+ * nothing: it runs while it works. */
673
684
  async update(params, opts = {}) {
674
685
  opts.signal?.throwIfAborted();
675
686
  const allowed = new Set(["timeout", "ports", "networkPolicy", "snapshotExpiration"]);
@@ -683,13 +694,15 @@ export class Sandbox {
683
694
  if (params.ports !== undefined)
684
695
  validatePorts(params.ports);
685
696
  const wanted = params.ports === undefined ? undefined : new Set(params.ports);
686
- if (params.timeout !== undefined) {
697
+ // A sandbox with no time limit has none to change (0300).
698
+ if (params.timeout !== undefined && this.#rt.info.endsAt !== null) {
687
699
  const timeout = checkTimeout(params.timeout);
688
700
  // A shorter timeout is kept when the lease has not reached it yet: the
689
701
  // lease then stops being extended there. Runtime cannot end a lease
690
702
  // earlier than it was given.
691
- if (this.#sessionStart + timeout < Date.parse(this.#rt.info.expiresAt) - 1000)
692
- throw new NotSupportedError("A timeout that ends earlier than the current lease", `The lease runs to ${this.#rt.info.expiresAt}; call stop() when the work is done, or pass a timeout that ends after it.`);
703
+ const end = this.#rt.info.endsAt ?? this.#rt.info.expiresAt;
704
+ if (this.#sessionStart + timeout < Date.parse(end) - 1000)
705
+ throw new NotSupportedError("A timeout that ends earlier than the current lease", `The lease runs to ${end}; call stop() when the work is done, or pass a timeout that ends after it.`);
693
706
  const longer = timeout > this.timeout;
694
707
  this.#timeoutMs = timeout;
695
708
  if (longer)
package/dist/wait.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ import type { RequestOptions } from "./transport.js";
2
+ /** A wait that an abort ends at once, rejecting with the signal's reason. */
3
+ export declare function pause(ms: number, signal?: AbortSignal): Promise<void>;
4
+ export declare function abortError(signal: AbortSignal): Error;
5
+ /**
6
+ * Calls `attempt` until it stops answering one of `codes` (something is
7
+ * still installing), within one budget: the caller's `timeoutMs`, else ten
8
+ * minutes. Each try gets what is left of it; a wait between tries ends at once
9
+ * on abort, and no try starts once the budget cannot cover the wait the API
10
+ * asked for: the installing error is thrown instead.
11
+ */
12
+ export declare function whileInstalling<T>(codes: readonly string[], options: RequestOptions | undefined, attempt: (options: RequestOptions) => Promise<T>): Promise<T>;
package/dist/wait.js ADDED
@@ -0,0 +1,47 @@
1
+ /** A wait that an abort ends at once, rejecting with the signal's reason. */
2
+ export function pause(ms, signal) {
3
+ return new Promise((resolve, reject) => {
4
+ if (signal?.aborted)
5
+ return reject(abortError(signal));
6
+ const timer = setTimeout(() => {
7
+ signal?.removeEventListener("abort", stop);
8
+ resolve();
9
+ }, ms);
10
+ function stop() {
11
+ clearTimeout(timer);
12
+ reject(abortError(signal));
13
+ }
14
+ signal?.addEventListener("abort", stop, { once: true });
15
+ });
16
+ }
17
+ export function abortError(signal) {
18
+ return signal.reason instanceof Error ? signal.reason : new Error("Aborted");
19
+ }
20
+ /** How long a start waits for a first install (Chromium, the desktop) when
21
+ * the caller sets no deadline. */
22
+ const INSTALL_WAIT_MS = 600_000;
23
+ /**
24
+ * Calls `attempt` until it stops answering one of `codes` (something is
25
+ * still installing), within one budget: the caller's `timeoutMs`, else ten
26
+ * minutes. Each try gets what is left of it; a wait between tries ends at once
27
+ * on abort, and no try starts once the budget cannot cover the wait the API
28
+ * asked for: the installing error is thrown instead.
29
+ */
30
+ export async function whileInstalling(codes, options, attempt) {
31
+ const deadline = performance.now() + (options?.timeoutMs || INSTALL_WAIT_MS);
32
+ for (;;) {
33
+ if (options?.signal?.aborted)
34
+ throw abortError(options.signal);
35
+ const left = Math.max(1, Math.ceil(deadline - performance.now()));
36
+ try {
37
+ return await attempt(options?.timeoutMs ? { ...options, timeoutMs: left } : { ...options });
38
+ }
39
+ catch (error) {
40
+ const code = error.code;
41
+ const wait = error.retryAfterMs ?? 10_000;
42
+ if (!code || !codes.includes(code) || performance.now() + wait > deadline)
43
+ throw error;
44
+ await pause(wait, options?.signal);
45
+ }
46
+ }
47
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "withruntime",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
4
4
  "license": "Apache-2.0",
5
5
  "mcpName": "com.withruntime/runtime",
6
6
  "type": "module",