@lizard-build/sdk 0.1.40 → 0.1.42

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.
package/dist/index.mjs CHANGED
@@ -61,17 +61,23 @@ var TimeoutError = class extends LizardError {
61
61
  };
62
62
  async function handleApiError(res) {
63
63
  let message;
64
+ let code;
64
65
  try {
65
66
  const body = await res.json();
66
67
  message = body.error ?? res.statusText;
68
+ if (typeof body.code === "string") code = body.code;
67
69
  } catch {
68
70
  message = res.statusText;
69
71
  }
70
- if (res.status === 401 || res.status === 403) throw new AuthenticationError(message);
71
- if (res.status === 404) throw new NotFoundError(message);
72
- if (res.status === 409) throw new ConflictError(message);
73
- if (res.status === 408 || res.status === 504) throw new TimeoutError(message);
74
- throw new LizardError(`API error ${res.status}: ${message}`);
72
+ let err;
73
+ if (res.status === 401 || res.status === 403) err = new AuthenticationError(message);
74
+ else if (res.status === 404) err = new NotFoundError(message);
75
+ else if (res.status === 409) err = new ConflictError(message);
76
+ else if (res.status === 408 || res.status === 504) err = new TimeoutError(message);
77
+ else err = new LizardError(`API error ${res.status}: ${message}`);
78
+ err.status = res.status;
79
+ err.code = code;
80
+ throw err;
75
81
  }
76
82
 
77
83
  // src/platform/client.ts
@@ -450,12 +456,12 @@ var Process = class {
450
456
  this.config = config;
451
457
  }
452
458
  /**
453
- * Execute a command inside the microVM and wait for it to complete.
459
+ * Execute a command inside the sandbox and wait for it to complete.
454
460
  *
455
461
  * The command runs in a shell inside the Lizard sandbox and returns
456
462
  * stdout, stderr, and the exit code when it finishes.
457
463
  *
458
- * @param cmd Shell command to run inside the microVM.
464
+ * @param cmd Shell command to run inside the sandbox.
459
465
  * @param opts Optional execution options — environment variables, working
460
466
  * directory, user, and timeout.
461
467
  *
@@ -587,18 +593,19 @@ var Fs = class {
587
593
  this.config = config;
588
594
  }
589
595
  /**
590
- * Write a file into the microVM filesystem.
596
+ * Write a file into the sandbox filesystem.
591
597
  *
592
598
  * Creates parent directories automatically if they don't exist.
599
+ * Byte input must contain valid UTF-8; arbitrary binary uploads are unsupported.
593
600
  *
594
601
  * @example
595
602
  * ```ts
596
603
  * await sandbox.fs.write('/app/index.js', 'console.log("hello")')
597
604
  * ```
598
605
  *
599
- * @example Write binary data:
606
+ * @example Write UTF-8 bytes:
600
607
  * ```ts
601
- * await sandbox.fs.write('/app/data.bin', buffer)
608
+ * await sandbox.fs.write('/app/notes.txt', new TextEncoder().encode('hello'))
602
609
  * ```
603
610
  */
604
611
  async write(path, data, opts) {
@@ -611,7 +618,7 @@ var Fs = class {
611
618
  if (!res.ok) await handleApiError(res);
612
619
  }
613
620
  /**
614
- * Read a file from the microVM filesystem.
621
+ * Read a file from the sandbox filesystem.
615
622
  *
616
623
  * @returns The file contents as a UTF-8 string.
617
624
  *
@@ -628,14 +635,7 @@ var Fs = class {
628
635
  if (!res.ok) await handleApiError(res);
629
636
  return res.text();
630
637
  }
631
- /**
632
- * List files and directories at the given path inside the microVM.
633
- *
634
- * @example
635
- * ```ts
636
- * const entries = await sandbox.fs.list('/app')
637
- * ```
638
- */
638
+ /** Read a file as bytes. */
639
639
  async readBytes(path, opts) {
640
640
  const url = new URL(`${this.config.apiUrl}/api/sandboxes/${this.sandboxId}/files`);
641
641
  url.searchParams.set("path", path);
@@ -644,6 +644,14 @@ var Fs = class {
644
644
  if (!res.ok) await handleApiError(res);
645
645
  return new Uint8Array(await res.arrayBuffer());
646
646
  }
647
+ /**
648
+ * List files and directories at the given path inside the sandbox.
649
+ *
650
+ * @example
651
+ * ```ts
652
+ * const entries = await sandbox.fs.list('/app')
653
+ * ```
654
+ */
647
655
  async list(path, opts) {
648
656
  const url = new URL(`${this.config.apiUrl}/api/sandboxes/${this.sandboxId}/files/list`);
649
657
  url.searchParams.set("path", path);
@@ -653,7 +661,7 @@ var Fs = class {
653
661
  return res.json();
654
662
  }
655
663
  /**
656
- * Remove a file or directory from the microVM filesystem.
664
+ * Remove a file or directory from the sandbox filesystem.
657
665
  */
658
666
  async remove(path, opts) {
659
667
  const res = await fetch(`${this.config.apiUrl}/api/sandboxes/${this.sandboxId}/files`, {
@@ -664,7 +672,7 @@ var Fs = class {
664
672
  if (!res.ok) await handleApiError(res);
665
673
  }
666
674
  /**
667
- * Create a directory (and any missing parents) inside the microVM.
675
+ * Create a directory (and any missing parents) inside the sandbox.
668
676
  */
669
677
  async makeDir(path, opts) {
670
678
  await this.execInternal(`mkdir -p ${JSON.stringify(path)}`, opts?.user);
@@ -892,11 +900,7 @@ var Sandbox = class extends SandboxClient {
892
900
  * Verifies the sandbox exists and is reachable, then returns a handle to it.
893
901
  * Throws `NotFoundError` if it has been killed or has expired.
894
902
  *
895
- * This used to call `resume` first, on the assumption that a sandbox you are
896
- * reconnecting to might be paused. Sandboxes are pods now and pause/resume is a
897
- * `501` on every one of them, so that call turned every `connect()` into an
898
- * error against a perfectly healthy sandbox. Connecting does not need to change
899
- * a sandbox's state, so it no longer tries to.
903
+ * Connecting reads sandbox metadata without changing its state.
900
904
  *
901
905
  * @example
902
906
  * ```ts
@@ -921,29 +925,25 @@ var Sandbox = class extends SandboxClient {
921
925
  /**
922
926
  * Kill the sandbox and release its resources immediately.
923
927
  *
924
- * @returns `true` if the microVM was terminated, `false` if it was already gone.
928
+ * @returns `true` if the sandbox was terminated, `false` if it was already gone.
925
929
  */
926
930
  async kill(opts) {
927
931
  return SandboxClient.killSandbox(this.sandboxId, this.resolveOpts(opts));
928
932
  }
929
933
  /**
930
- * Pause the sandbox by freezing it in place.
934
+ * Request sandbox pause. The Kubernetes backend returns HTTP 501.
931
935
  *
932
- * @deprecated Not implemented for the current runtime — always throws
933
- * `LizardError` with HTTP 501. Sandboxes run as pods, and the equivalent is a CRIU
934
- * checkpoint of the pod, which is not built. To park work across a gap, put it on a
935
- * {@link Volume} and create a fresh sandbox on that volume later; the volume is the
936
- * part that is meant to outlive a sandbox.
936
+ * @deprecated Unsupported on Kubernetes. Save files to a {@link Volume}
937
+ * and attach it to a new sandbox instead.
937
938
  */
938
939
  async pause(opts) {
939
940
  return SandboxClient.pauseSandbox(this.sandboxId, this.resolveOpts(opts));
940
941
  }
941
942
  /**
942
- * Resume a paused sandbox.
943
+ * Request sandbox resume. The Kubernetes backend returns HTTP 501.
943
944
  *
944
- * @deprecated Not implemented for the current runtime — always throws
945
- * `LizardError` with HTTP 501. See {@link pause}. `Sandbox.connect()` no longer
946
- * calls this, so reconnecting to a running sandbox works without it.
945
+ * @deprecated Unsupported on Kubernetes. Use {@link Sandbox.connect} to
946
+ * reconnect to a running sandbox.
947
947
  */
948
948
  async resume(opts) {
949
949
  return SandboxClient.resumeSandbox(this.sandboxId, this.resolveOpts(opts));
@@ -963,25 +963,27 @@ var Sandbox = class extends SandboxClient {
963
963
  return SandboxClient.setTimeoutSandbox(this.sandboxId, timeoutMs, this.resolveOpts(opts));
964
964
  }
965
965
  /**
966
- * Get the public HTTPS URL for a port exposed inside the sandbox.
966
+ * Register a public HTTPS route and return its hostname without a scheme.
967
967
  *
968
- * Useful for accessing HTTP servers started inside the microVM from your
968
+ * Useful for accessing HTTP servers started inside the sandbox from your
969
969
  * agent or tests without additional tunneling.
970
970
  *
971
971
  * @example
972
972
  * ```ts
973
- * await sandbox.process.exec('npx -y serve -p 3000 &')
974
- * const url = sandbox.getHost(3000)
975
- * // https://{sandboxId}-3000.sandbox.{region}.onlizard.com
973
+ * // Start an HTTP server on port 3000 before exposing it.
974
+ * const hostname = await sandbox.getHost(3000)
975
+ * console.log(`https://${hostname}`)
976
976
  * ```
977
977
  */
978
978
  async getHost(port, opts) {
979
979
  const { hostname } = await SandboxClient.exposeSandboxPort(this.sandboxId, port, this.resolveOpts(opts));
980
980
  return hostname;
981
981
  }
982
+ /** Fork is unsupported on Kubernetes; the backend returns HTTP 501. */
982
983
  fork(opts = {}) {
983
984
  return new PlatformClient(this.resolveOpts()).post(`/api/sandboxes/${this.sandboxId}/fork`, { count: opts.count ?? 1, timeoutMs: opts.timeoutMs ?? 0 });
984
985
  }
986
+ /** Snapshot creation is unsupported on Kubernetes; the backend returns HTTP 501. */
985
987
  snapshot(name) {
986
988
  return new PlatformClient(this.resolveOpts()).post(`/api/sandboxes/${this.sandboxId}/snapshot`, { name });
987
989
  }
@@ -994,6 +996,7 @@ var Sandbox = class extends SandboxClient {
994
996
  static snapshots(projectId, opts) {
995
997
  return new PlatformClient(opts ?? {}).get(`/api/projects/${projectId}/snapshots`);
996
998
  }
999
+ /** Snapshot restore is unsupported on Kubernetes; the backend returns HTTP 501. */
997
1000
  static async restore(snapshotId, opts) {
998
1001
  const result = await new PlatformClient(opts ?? {}).post(`/api/sandbox-snapshots/${encodeURIComponent(snapshotId)}/fork`, { timeoutMs: opts?.timeoutMs ?? 0 });
999
1002
  return new this({ ...opts, sandboxId: result.sandboxId ?? result.id });
@@ -1106,7 +1109,7 @@ var AddonsAPI = class {
1106
1109
  });
1107
1110
  return this.get(projectId, addonId);
1108
1111
  }
1109
- /** Restart an addon VM. */
1112
+ /** Restart an addon. */
1110
1113
  redeploy(projectId, addonId) {
1111
1114
  return this.client.post(`/api/projects/${projectId}/addons/${addonId}/redeploy`, {});
1112
1115
  }
@@ -1517,8 +1520,8 @@ var Volume = class _Volume {
1517
1520
  * exist yet. This is the reason a volume's name is its key: an agent that wants
1518
1521
  * "the scratch disk for this task" no longer has to store an id between runs.
1519
1522
  *
1520
- * An existing volume is returned as-is — `sizeGb` applies only to a fresh create
1521
- * and never resizes one that is already there.
1523
+ * An existing volume is returned as-is — `sizeGb` applies only to a fresh create,
1524
+ * so an existing volume keeps its size. Call {@link Volume.resize} to change it.
1522
1525
  */
1523
1526
  static async getOrCreate(projectId, name, opts) {
1524
1527
  const config = new ConnectionConfig(opts);
@@ -1558,6 +1561,32 @@ var Volume = class _Volume {
1558
1561
  });
1559
1562
  if (!res.ok) await handleApiError(res);
1560
1563
  }
1564
+ /**
1565
+ * Resize a volume, by name or by id, to `sizeGb`.
1566
+ *
1567
+ * The change is in place and online: the size is a quota, so no data is copied,
1568
+ * it completes in well under a second, and a sandbox that has the volume mounted
1569
+ * keeps running and sees the new size immediately. Both growing and shrinking are
1570
+ * allowed, but a shrink must leave at least 10% of the new size free — otherwise
1571
+ * it throws `ConflictError` with `code: 'volume_too_full_to_shrink'`.
1572
+ *
1573
+ * Other failures carry a `code` too: `invalid_volume_size`, `volume_not_resizable`,
1574
+ * `volume_resize_in_progress`, `volume_not_provisioned`,
1575
+ * `volume_capacity_unavailable`, and `volume_resize_timeout` (a `TimeoutError`;
1576
+ * the size is left unchanged).
1577
+ *
1578
+ * @returns The volume's updated info, including `sizeEnforced`.
1579
+ */
1580
+ static async resize(projectId, nameOrId, sizeGb, opts) {
1581
+ const config = new ConnectionConfig(opts);
1582
+ const res = await fetch(`${config.apiUrl}/api/projects/${projectId}/volumes/${encodeURIComponent(nameOrId)}`, {
1583
+ method: "PATCH",
1584
+ headers: config.headers,
1585
+ body: JSON.stringify({ sizeGb })
1586
+ });
1587
+ if (!res.ok) await handleApiError(res);
1588
+ return res.json();
1589
+ }
1561
1590
  async getInfo(projectId) {
1562
1591
  const res = await fetch(`${this.config.apiUrl}/api/projects/${projectId}/volumes/${this.volumeId}`, {
1563
1592
  headers: this.config.headers
@@ -1565,6 +1594,13 @@ var Volume = class _Volume {
1565
1594
  if (!res.ok) await handleApiError(res);
1566
1595
  return res.json();
1567
1596
  }
1597
+ /** Resize this volume in place — see the static {@link Volume.resize}. */
1598
+ async resize(projectId, sizeGb) {
1599
+ return _Volume.resize(projectId, this.volumeId, sizeGb, {
1600
+ apiKey: this.config.apiKey,
1601
+ apiUrl: this.config.apiUrl
1602
+ });
1603
+ }
1568
1604
  async delete(projectId) {
1569
1605
  const res = await fetch(`${this.config.apiUrl}/api/projects/${projectId}/volumes/${this.volumeId}`, {
1570
1606
  method: "DELETE",
@@ -1598,6 +1634,7 @@ var Lizard = class {
1598
1634
  getOrCreate: async (name, opts) => Volume.getOrCreate(await this.projectId(), name, { ...await this.connectionOpts(), ...opts }),
1599
1635
  get: async (nameOrId, opts) => Volume.get(await this.projectId(), nameOrId, { ...await this.connectionOpts(), ...opts }),
1600
1636
  list: async (opts) => Volume.list(await this.projectId(), { ...await this.connectionOpts(), ...opts }),
1637
+ resize: async (nameOrId, sizeGb, opts) => Volume.resize(await this.projectId(), nameOrId, sizeGb, { ...await this.connectionOpts(), ...opts }),
1601
1638
  delete: async (nameOrId, opts) => Volume.delete(await this.projectId(), nameOrId, { ...await this.connectionOpts(), ...opts })
1602
1639
  };
1603
1640
  this.config = new ConnectionConfig(opts);
@@ -1708,8 +1745,7 @@ var CodeSandbox = class extends Sandbox {
1708
1745
  *
1709
1746
  * @returns Execution result with stdout, stderr, results, and any error.
1710
1747
  *
1711
- * @throws {ExecutionError} if you pass neither language nor context and no
1712
- * default context exists — which cannot happen in normal usage.
1748
+ * Code errors appear in `execution.error`; request failures throw.
1713
1749
  *
1714
1750
  * @example
1715
1751
  * ```ts
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lizard-build/sdk",
3
- "version": "0.1.40",
4
- "description": "TypeScript SDK for Lizard apps, managed databases, storage and Linux sandboxes.",
3
+ "version": "0.1.42",
4
+ "description": "TypeScript SDK for Lizard: Kubernetes sandboxes, code execution and cloud apps.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.ts",
@@ -24,8 +24,8 @@
24
24
  "keywords": [
25
25
  "sandbox",
26
26
  "sandboxes",
27
- "microvm",
28
- "firecracker",
27
+ "kubernetes",
28
+ "containers",
29
29
  "ai-agents",
30
30
  "code-interpreter",
31
31
  "code-execution",