withruntime 0.10.0 → 0.11.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 (65) hide show
  1. package/DAYTONA.md +40 -34
  2. package/E2B.md +56 -57
  3. package/VERCEL.md +21 -19
  4. package/compatibility-lock.json +8 -84
  5. package/dist/cli.js +73 -2
  6. package/dist/client.d.ts +12 -0
  7. package/dist/client.js +27 -0
  8. package/dist/compat/lease.d.ts +36 -0
  9. package/dist/compat/lease.js +87 -0
  10. package/dist/daytona/computer-use.d.ts +162 -0
  11. package/dist/daytona/computer-use.js +324 -0
  12. package/dist/daytona/context.d.ts +10 -0
  13. package/dist/daytona/context.js +24 -0
  14. package/dist/daytona/daytona.d.ts +20 -0
  15. package/dist/daytona/daytona.js +123 -16
  16. package/dist/daytona/filesystem.js +8 -0
  17. package/dist/daytona/index.d.ts +2 -26
  18. package/dist/daytona/index.js +2 -7
  19. package/dist/daytona/process.d.ts +63 -6
  20. package/dist/daytona/process.js +224 -23
  21. package/dist/daytona/sandbox.d.ts +19 -5
  22. package/dist/daytona/sandbox.js +39 -25
  23. package/dist/e2b/client.d.ts +5 -0
  24. package/dist/e2b/client.js +5 -1
  25. package/dist/e2b/commands.d.ts +15 -6
  26. package/dist/e2b/commands.js +83 -18
  27. package/dist/e2b/errors.d.ts +7 -0
  28. package/dist/e2b/errors.js +20 -0
  29. package/dist/e2b/filesystem.d.ts +2 -1
  30. package/dist/e2b/filesystem.js +43 -6
  31. package/dist/e2b/index.d.ts +2 -2
  32. package/dist/e2b/index.js +1 -1
  33. package/dist/e2b/pty.js +6 -3
  34. package/dist/e2b/sandbox.d.ts +15 -5
  35. package/dist/e2b/sandbox.js +190 -28
  36. package/dist/e2b/users.d.ts +30 -0
  37. package/dist/e2b/users.js +185 -0
  38. package/dist/index.d.ts +1 -0
  39. package/dist/mcp.d.ts +2 -1
  40. package/dist/mcp.js +6 -1
  41. package/dist/network-cli.js +28 -0
  42. package/dist/products/browser.d.ts +60 -0
  43. package/dist/products/browser.js +37 -0
  44. package/dist/products/desktop.d.ts +2 -2
  45. package/dist/products/desktop.js +2 -2
  46. package/dist/products/images.d.ts +3 -1
  47. package/dist/products/images.js +14 -2
  48. package/dist/products/index.d.ts +2 -0
  49. package/dist/products/index.js +2 -0
  50. package/dist/products/network-products.d.ts +7 -0
  51. package/dist/products/network-products.js +3 -0
  52. package/dist/products/private-network.d.ts +22 -0
  53. package/dist/products/private-network.js +10 -0
  54. package/dist/sandbox.d.ts +22 -0
  55. package/dist/sandbox.js +47 -0
  56. package/dist/transport.d.ts +1 -1
  57. package/dist/transport.js +1 -1
  58. package/dist/types.d.ts +5 -2
  59. package/dist/vercel/index.d.ts +1 -7
  60. package/dist/vercel/index.js +1 -3
  61. package/dist/vercel/sandbox-user.d.ts +70 -0
  62. package/dist/vercel/sandbox-user.js +206 -0
  63. package/dist/vercel/sandbox.d.ts +47 -12
  64. package/dist/vercel/sandbox.js +201 -42
  65. package/package.json +1 -1
@@ -1,8 +1,10 @@
1
+ import { extensionSeconds, LeaseKeeper, LEASE_MAX_SECONDS } from "../compat/lease.js";
1
2
  import { RuntimeError } from "../errors.js";
2
3
  import { clientFor, signalOf, } from "./client.js";
3
4
  import { Command, CommandFinished, exitCodeOf } from "./command.js";
4
5
  import { APIError, guard, NotSupportedError, translate } from "./errors.js";
5
6
  import { FileSystem } from "./filesystem.js";
7
+ import { rootOrThrow, SandboxUser, SandboxUserAlreadyExistsError, validateName, } from "./sandbox-user.js";
6
8
  import { Snapshot } from "./snapshot.js";
7
9
  /* Vercel Sandbox's `Sandbox` over Runtime's. Every call is one or a few calls
8
10
  of the withruntime SDK; nothing here speaks HTTP. What Runtime cannot do the
@@ -14,7 +16,8 @@ export const MEMORY_MIB_PER_VCPU = 2048;
14
16
  /** Vercel's default timeout, 5 minutes. */
15
17
  export const DEFAULT_TIMEOUT_MS = 300_000;
16
18
  const MIN_LEASE_SECONDS = 60;
17
- const MAX_LEASE_SECONDS = 3600;
19
+ /** Extend a long timeout's lease when less than this is left. */
20
+ const LEASE_MARGIN_MS = 600_000;
18
21
  /** Commands without a timeout run until the sandbox ends: Runtime's longest. */
19
22
  const LONGEST_MS = 86_400_000;
20
23
  /** Vercel's working directory; `/workspace` on Runtime. */
@@ -27,13 +30,14 @@ const LEGACY_RUNTIMES = new Set(["node22", "node24", "node26", "python3.13"]);
27
30
  /** Vercel regions in the United States, where Runtime runs. */
28
31
  const US_REGIONS = new Set(["iad1", "sfo1", "cle1", "pdx1"]);
29
32
  const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
30
- function leaseSeconds(timeoutMs) {
33
+ function checkTimeout(timeoutMs) {
31
34
  if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)
32
35
  throw new RangeError(`timeout must be a positive number of milliseconds, not ${timeoutMs}.`);
33
- const seconds = Math.ceil(timeoutMs / 1000);
34
- if (seconds > MAX_LEASE_SECONDS)
35
- throw new NotSupportedError(`A sandbox timeout of ${timeoutMs} ms (over one hour)`, "Runtime leases last up to an hour (3_600_000 ms); call sandbox.extendTimeout(ms) before it ends to keep going, as often as needed.");
36
- return Math.max(seconds, MIN_LEASE_SECONDS);
36
+ return Math.max(timeoutMs, MIN_LEASE_SECONDS * 1000);
37
+ }
38
+ /** The first lease for a timeout: all of it, or the hour a lease may run. */
39
+ function leaseSeconds(timeoutMs) {
40
+ return Math.min(Math.ceil(checkTimeout(timeoutMs) / 1000), LEASE_MAX_SECONDS);
37
41
  }
38
42
  function refuseCreate(params) {
39
43
  if (params.keepLastSnapshots !== undefined)
@@ -140,6 +144,12 @@ export class Sandbox {
140
144
  #persistent;
141
145
  #routes = new Map();
142
146
  #onResume;
147
+ #keeper;
148
+ /** Epoch ms the current session started, as the timeout counts from it. */
149
+ #sessionStart;
150
+ /** Vercel's timeout, when this object was given one or extended it. */
151
+ #timeoutMs;
152
+ #defaultUser;
143
153
  #linked;
144
154
  #deleted = false;
145
155
  /** Use Sandbox.create, Sandbox.get or Sandbox.getOrCreate. */
@@ -149,6 +159,17 @@ export class Sandbox {
149
159
  this.#env = { ...resolved.params.env };
150
160
  this.#persistent = resolved.params.persistent ?? resolved.runtime.info.onLeaseEnd !== "stop";
151
161
  this.#onResume = resolved.params.onResume;
162
+ const timeout = resolved.params.timeout;
163
+ this.#timeoutMs = timeout === undefined ? undefined : checkTimeout(timeout);
164
+ this.#sessionStart =
165
+ timeout === undefined
166
+ ? Date.parse(this.#rt.info.expiresAt) - this.#rt.info.timeoutSeconds * 1000
167
+ : Date.now();
168
+ this.#keeper = new LeaseKeeper({
169
+ sandbox: () => this.#rt,
170
+ until: () => this.#until(),
171
+ marginMs: () => Math.min(LEASE_MARGIN_MS, this.timeout / 2),
172
+ });
152
173
  this.fs = new FileSystem({
153
174
  name: this.name,
154
175
  files: async () => (await this.#live()).files,
@@ -213,9 +234,14 @@ export class Sandbox {
213
234
  get status() {
214
235
  return statusOf(this.#rt.state);
215
236
  }
216
- /** Milliseconds. */
237
+ /** 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. */
217
239
  get timeout() {
218
- return this.#rt.info.timeoutSeconds * 1000;
240
+ return this.#timeoutMs ?? this.#rt.info.timeoutSeconds * 1000;
241
+ }
242
+ /** Epoch ms the session should end: its start plus the timeout. */
243
+ #until() {
244
+ return this.#sessionStart + this.timeout;
219
245
  }
220
246
  get expiresAt() {
221
247
  return new Date(this.#rt.info.expiresAt);
@@ -251,11 +277,18 @@ export class Sandbox {
251
277
  ...params.withruntime?.create,
252
278
  };
253
279
  const runtime = await guard(() => client.sandboxes.create(input, signalOf(params)));
254
- const sandbox = new Sandbox({ runtime, client, params: { ...params, persistent } });
280
+ const sandbox = new Sandbox({
281
+ runtime,
282
+ client,
283
+ params: { ...params, persistent, timeout: params.timeout ?? DEFAULT_TIMEOUT_MS },
284
+ });
255
285
  try {
256
286
  await sandbox.#setUp(params);
287
+ // Starts renewing at once when the timeout runs past the first lease.
288
+ await sandbox.#keeper.check();
257
289
  }
258
290
  catch (error) {
291
+ sandbox.#keeper.end();
259
292
  await runtime.stop({ wait: false }).catch(() => undefined);
260
293
  throw translate(error, sandbox.name);
261
294
  }
@@ -367,13 +400,16 @@ export class Sandbox {
367
400
  });
368
401
  }
369
402
  /** Running and stopped (paused) sandboxes, oldest first, with Vercel's
370
- * paginator: `for await (const s of await Sandbox.list())`. */
403
+ * paginator: `for await (const s of await Sandbox.list())`. A name prefix,
404
+ * creation-time bounds (`since`, `until`, epoch ms), `sortBy` and
405
+ * `sortOrder`, and a `cursor` from an earlier page are applied here, over
406
+ * every sandbox the key sees. */
371
407
  static async list(params = {}) {
372
408
  params.signal?.throwIfAborted();
373
- for (const field of ["since", "until", "cursor", "sortBy", "sortOrder", "namePrefix"])
374
- if (params[field] !== undefined)
375
- throw new NotSupportedError(`Listing sandboxes by ${field}`, "List them all (oldest first) and filter the result yourself.");
376
409
  const client = clientFor(params);
410
+ const local = ["namePrefix", "sortBy", "sortOrder", "cursor", "since", "until"].some((field) => params[field] !== undefined);
411
+ if (local)
412
+ return listHere(client, params);
377
413
  const page = await guard(() => client.sandboxes.list({
378
414
  ...(params.tags ? { labels: params.tags } : {}),
379
415
  ...(params.limit ? { limit: Math.min(params.limit, 100) } : {}),
@@ -393,10 +429,15 @@ export class Sandbox {
393
429
  const state = this.#rt.state;
394
430
  if (state === "paused" || state === "pausing")
395
431
  await this.#wake(opts);
432
+ await guard(() => this.#keeper.check(), this.name);
396
433
  return this.#rt;
397
434
  }
435
+ /** A woken sandbox starts a new session, whose timeout counts from now,
436
+ * as Vercel's resumed sessions do. */
398
437
  async #wake(opts = {}) {
399
438
  await guard(() => this.#rt.wake(signalOf(opts)), this.name);
439
+ if (this.#timeoutMs !== undefined)
440
+ this.#sessionStart = Date.now();
400
441
  await this.#onResume?.(this);
401
442
  }
402
443
  /** Runs `work` on the live sandbox; if the lease paused it meanwhile, wakes
@@ -583,6 +624,8 @@ export class Sandbox {
583
624
  else
584
625
  await guard(() => this.#rt.stop(signalOf(opts)), this.name);
585
626
  }
627
+ if (!this.#persistent)
628
+ this.#keeper.end();
586
629
  return { name: this.name, status: this.status };
587
630
  }
588
631
  /** Ends the sandbox for good. */
@@ -593,18 +636,29 @@ export class Sandbox {
593
636
  if (this.#rt.state !== "stopped")
594
637
  await guard(() => this.#rt.stop({ wait: false, ...signalOf(opts) }), this.name);
595
638
  this.#deleted = true;
639
+ this.#keeper.end();
596
640
  }
597
641
  async [Symbol.asyncDispose]() {
598
642
  if (this.#deleted)
599
643
  return;
600
644
  await this.stop().catch(() => undefined);
601
645
  }
602
- /** Moves the end `duration` milliseconds later (at most an hour ahead of
603
- * now on Runtime). */
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. */
604
649
  async extendTimeout(duration, opts = {}) {
605
650
  if (!Number.isFinite(duration) || duration <= 0)
606
651
  throw new RangeError(`duration must be a positive number of milliseconds, not ${duration}.`);
607
- await this.#withResume((runtime) => runtime.extend(Math.ceil(duration / 1000), signalOf(opts)), opts);
652
+ await this.#withResume(async (runtime) => {
653
+ this.#timeoutMs = this.timeout + duration;
654
+ await this.#leaseToTimeout(runtime, opts);
655
+ }, opts);
656
+ }
657
+ /** Extends the lease now toward the timeout, an hour ahead at most. */
658
+ async #leaseToTimeout(runtime, opts) {
659
+ const seconds = extensionSeconds(Date.parse(runtime.info.expiresAt), this.#until(), Date.now(), Infinity);
660
+ if (seconds >= 1)
661
+ await runtime.extend(seconds, signalOf(opts));
608
662
  }
609
663
  /** Replaces the network rules; returns the policy given. */
610
664
  async updateNetworkPolicy(networkPolicy, opts = {}) {
@@ -630,11 +684,16 @@ export class Sandbox {
630
684
  validatePorts(params.ports);
631
685
  const wanted = params.ports === undefined ? undefined : new Set(params.ports);
632
686
  if (params.timeout !== undefined) {
633
- const current = this.timeout;
634
- if (params.timeout < current)
635
- throw new NotSupportedError("Shortening a sandbox's timeout", "Runtime leases only move later. Call stop() when the work is done.");
636
- if (params.timeout > current)
637
- await this.extendTimeout(params.timeout - current, opts);
687
+ const timeout = checkTimeout(params.timeout);
688
+ // A shorter timeout is kept when the lease has not reached it yet: the
689
+ // lease then stops being extended there. Runtime cannot end a lease
690
+ // 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.`);
693
+ const longer = timeout > this.timeout;
694
+ this.#timeoutMs = timeout;
695
+ if (longer)
696
+ await this.#withResume((runtime) => this.#leaseToTimeout(runtime, opts), opts);
638
697
  }
639
698
  if (rules !== undefined)
640
699
  await this.#withResume((runtime) => runtime.network.set(rules, signalOf(opts)), opts);
@@ -700,26 +759,68 @@ export class Sandbox {
700
759
  return new Snapshot(taken, this.#client);
701
760
  }
702
761
  // ---- what Runtime does differently -----------------------------------
762
+ /** Refused: Vercel hands back a token scoped to this one shell, and
763
+ * Runtime's terminal opens with the account key, which must not leave the
764
+ * server. */
703
765
  openInteractive() {
704
766
  return Promise.reject(new NotSupportedError("Vercel's interactive shell (openInteractive)", "Use `await sandbox.withruntime.terminal({ cols, rows, onData })`, or `npx withruntime sandbox shell <id>`."));
705
767
  }
706
- getDefaultUser() {
707
- return Promise.resolve({ username: "runtime", group: "runtime" });
708
- }
709
- createUser() {
710
- return usersRefused();
711
- }
712
- asUser() {
713
- throw usersError();
714
- }
715
- createGroup() {
716
- return usersRefused();
768
+ /** The user commands run as, `runtime`, and its group. */
769
+ getDefaultUser(_opts = {}) {
770
+ this.#defaultUser ??= Promise.resolve({ username: "runtime", group: "runtime" });
771
+ return this.#defaultUser;
772
+ }
773
+ /** Makes a Linux user with its own home, as Vercel does: `useradd -m`,
774
+ * then the home given to the user and the sandbox's group, mode 770. */
775
+ async createUser(username, opts = {}) {
776
+ validateName(username, "username");
777
+ const { group } = await this.getDefaultUser(opts);
778
+ const signal = opts.signal;
779
+ const useradd = await this.runCommand({
780
+ cmd: "useradd",
781
+ args: ["-m", "-s", "/bin/bash", username],
782
+ sudo: true,
783
+ ...(signal ? { signal } : {}),
784
+ });
785
+ if (useradd.exitCode !== 0) {
786
+ if (useradd.exitCode === 9)
787
+ throw new SandboxUserAlreadyExistsError(username);
788
+ throw new Error(`Failed to create user "${username}": ${await useradd.stderr()}`);
789
+ }
790
+ const home = `/home/${username}`;
791
+ await rootOrThrow(this, "chown", [`${username}:${group}`, home], signal, () => `Failed to set ownership on ${home}`);
792
+ await rootOrThrow(this, "chmod", ["770", home], signal, () => `Failed to set permissions on ${home}`);
793
+ return new SandboxUser({ sandbox: this, username });
794
+ }
795
+ /** A handle for an existing user, such as `root`. */
796
+ asUser(username) {
797
+ validateName(username, "username");
798
+ return new SandboxUser({ sandbox: this, username });
799
+ }
800
+ /** Makes a group and its shared directory, `/shared/<group>`, setgid 2770. */
801
+ async createGroup(groupname, opts = {}) {
802
+ validateName(groupname, "group name");
803
+ const { username } = await this.getDefaultUser(opts);
804
+ const sharedDir = `/shared/${groupname}`;
805
+ const steps = [
806
+ ["groupadd", [groupname], `Failed to create group "${groupname}"`],
807
+ ["mkdir", ["-p", sharedDir], `Failed to create shared directory ${sharedDir}`],
808
+ ["chown", [`${username}:${groupname}`, sharedDir], `Failed to set ownership on ${sharedDir}`],
809
+ ["chmod", ["2770", sharedDir], `Failed to set permissions on ${sharedDir}`],
810
+ ];
811
+ for (const [cmd, args, what] of steps)
812
+ await rootOrThrow(this, cmd, args, opts.signal, () => what);
813
+ return { groupname, sharedDir };
717
814
  }
718
- addUserToGroup() {
719
- return usersRefused();
815
+ async addUserToGroup(username, groupname, opts = {}) {
816
+ validateName(username, "username");
817
+ validateName(groupname, "group name");
818
+ await rootOrThrow(this, "usermod", ["-aG", groupname, username], opts.signal, () => `Failed to add "${username}" to group "${groupname}"`);
720
819
  }
721
- removeUserFromGroup() {
722
- return usersRefused();
820
+ async removeUserFromGroup(username, groupname, opts = {}) {
821
+ validateName(username, "username");
822
+ validateName(groupname, "group name");
823
+ await rootOrThrow(this, "gpasswd", ["-d", username, groupname], opts.signal, () => `Failed to remove "${username}" from group "${groupname}"`);
723
824
  }
724
825
  currentSession() {
725
826
  throw new NotSupportedError("Vercel sessions (currentSession)", "A Runtime sandbox is its own session: call the methods on the sandbox.");
@@ -731,12 +832,6 @@ export class Sandbox {
731
832
  return Promise.reject(new NotSupportedError("Listing a sandbox's snapshots from the sandbox", "Use Snapshot.list({ name }) or runtime.snapshots.list({ sandboxId })."));
732
833
  }
733
834
  }
734
- function usersError() {
735
- return new NotSupportedError("Extra Linux users and groups (createUser, asUser, groups)", "Commands run as the sandbox owner with passwordless sudo; run `sudo useradd ...` and `sudo -u <user> ...` with runCommand.");
736
- }
737
- function usersRefused() {
738
- return Promise.reject(usersError());
739
- }
740
835
  /** Milliseconds as whole days for Runtime's retention: 1 to 365, and 0 (no
741
836
  * expiration) as 365. */
742
837
  export function retentionDays(ms) {
@@ -791,6 +886,70 @@ function itemOf(runtime) {
791
886
  tags: { ...info.labels },
792
887
  };
793
888
  }
889
+ const CURSOR = /^rt\.([0-9]+)$/;
890
+ /** Sandbox.list with a name prefix, time bounds, sorting or a cursor:
891
+ * applied here over every sandbox the key sees, since Runtime's list filters
892
+ * only by labels and name. The cursor is this adapter's own, an offset into
893
+ * the same query. */
894
+ async function listHere(client, params) {
895
+ let offset = 0;
896
+ if (params.cursor !== undefined) {
897
+ const match = CURSOR.exec(params.cursor);
898
+ if (!match)
899
+ throw new RangeError(`The cursor "${params.cursor}" was not made by Sandbox.list here; pass pagination.next from an earlier page of the same query.`);
900
+ offset = Number(match[1]);
901
+ }
902
+ if (params.sortBy !== undefined &&
903
+ !["createdAt", "name", "statusUpdatedAt"].includes(params.sortBy))
904
+ throw new RangeError("sortBy must be createdAt, name or statusUpdatedAt.");
905
+ if (params.sortOrder !== undefined && !["asc", "desc"].includes(params.sortOrder))
906
+ throw new RangeError("sortOrder must be asc or desc.");
907
+ const page = await guard(() => client.sandboxes.list(params.tags ? { labels: params.tags } : {}, signalOf(params)));
908
+ const all = (await guard(() => page.toArray(10_000))).map(itemOf);
909
+ params.signal?.throwIfAborted();
910
+ const time = (value) => value === undefined ? undefined : value instanceof Date ? value.getTime() : value;
911
+ const since = time(params.since);
912
+ const until = time(params.until);
913
+ const items = all.filter((one) => (params.namePrefix === undefined || one.name.startsWith(params.namePrefix)) &&
914
+ (since === undefined || one.createdAt >= since) &&
915
+ (until === undefined || one.createdAt <= until));
916
+ const by = params.sortBy ?? "createdAt";
917
+ const key = (one) => by === "name" ? one.name : by === "statusUpdatedAt" ? one.updatedAt : one.createdAt;
918
+ const direction = params.sortOrder === "desc" ? -1 : 1;
919
+ items.sort((a, b) => {
920
+ const [x, y] = [key(a), key(b)];
921
+ return (x < y ? -1 : x > y ? 1 : 0) * direction;
922
+ });
923
+ const size = Math.max(1, Math.min(params.limit ?? 50, 100));
924
+ const pageAt = (from) => {
925
+ const sandboxes = items.slice(from, from + size);
926
+ return {
927
+ sandboxes,
928
+ pagination: {
929
+ count: sandboxes.length,
930
+ next: from + size < items.length ? `rt.${from + size}` : null,
931
+ },
932
+ };
933
+ };
934
+ return Object.assign(pageAt(offset), {
935
+ async *pages() {
936
+ for (let from = offset; from === offset || from < items.length; from += size) {
937
+ params.signal?.throwIfAborted();
938
+ yield pageAt(from);
939
+ }
940
+ },
941
+ async *[Symbol.asyncIterator]() {
942
+ for (const item of items.slice(offset)) {
943
+ params.signal?.throwIfAborted();
944
+ yield item;
945
+ }
946
+ },
947
+ async toArray() {
948
+ params.signal?.throwIfAborted();
949
+ return items.slice(offset);
950
+ },
951
+ });
952
+ }
794
953
  /** Vercel's paginator over a Runtime page: the first page's fields, async
795
954
  * iteration over every item, `pages()` and `toArray()`. */
796
955
  function paginate(first, signal) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "withruntime",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "license": "Apache-2.0",
5
5
  "mcpName": "com.withruntime/runtime",
6
6
  "type": "module",