@lizard-build/sdk 0.1.59 → 0.1.60

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/README.md CHANGED
@@ -61,7 +61,7 @@ if (result.exitCode !== 0) throw new Error(result.stderr)
61
61
 
62
62
  ## Desktop (computer use)
63
63
 
64
- The `desktop` template runs XFCE and Chromium you can watch in a browser and an agent can drive:
64
+ The `desktop` template runs an Openbox desktop with Chromium, streamed to a browser over WebRTC (with noVNC as the fallback). You can watch it, and an agent can drive it:
65
65
 
66
66
  ```ts
67
67
  import { Sandbox } from '@lizard-build/sdk'
@@ -108,13 +108,14 @@ const sandbox = await CodeSandbox.create({ projectId: 'proj_123' });
108
108
  try {
109
109
  await sandbox.runCode('x = 21');
110
110
  const run = await sandbox.runCode('x * 2');
111
- console.log(run.results[0].data); // "42"
111
+ const out = run.results[0];
112
+ if (out?.type === 'result') console.log(out.data); // "42"
112
113
  } finally {
113
114
  await sandbox.kill();
114
115
  }
115
116
  ```
116
117
 
117
- `language: 'bash'` (and `'javascript'` where `node` is installed; the `interpreter` template does not ship it) runs each call as a fresh process with no shared state.
118
+ `language: 'bash'` and `language: 'javascript'` (the `interpreter` template ships Node 22) run each call as a fresh process with no shared state.
118
119
 
119
120
  ## Configuration
120
121
 
@@ -124,7 +125,7 @@ Pass `timeoutMs` to `create()` to set the sandbox lifetime. The default is five
124
125
 
125
126
  ## Runtime limits
126
127
 
127
- Each sandbox is a Firecracker microVM with its own kernel. Use volumes mounted at `/workspace` to keep files after a sandbox ends. `pause()`/`resume()` keep memory and running processes; `snapshot()` is ready in about 2 s and `Sandbox.restore()` starts a copy in about 0.4 s; `fork()` clones a running sandbox. File watching and sandbox log streaming are not available on Firecracker sandboxes yet, volumes cannot be resized yet, and a sandbox with a volume attached cannot be forked. See the [snapshot guide](../../docs/sandboxes.md#pause-resume-snapshots-and-fork).
128
+ Each sandbox is a Firecracker microVM with its own kernel. Use volumes mounted at `/workspace` to keep files after a sandbox ends. `pause()`/`resume()` keep memory and running processes; `snapshot()` is ready in about 2 s and `Sandbox.restore()` starts a copy in about 0.4 s; `fork()` clones a running sandbox. File watching and sandbox log streaming are not available on Firecracker sandboxes yet. Volumes can grow but not shrink, and a sandbox with a volume attached cannot be forked. See the [snapshot guide](../../docs/sandboxes.md#pause-resume-snapshots-and-fork).
128
129
 
129
130
  `getHost()` returns a hostname without `https://`; the port is private, so send `sandbox.accessToken` as the `X-Lizard-Access-Token` header (or use `exposePort()`, which also returns a browser `url` carrying the token). File writes accept text or bytes (binary is sent exactly); `readBytes()` supports binary downloads.
130
131
 
package/dist/index.d.mts CHANGED
@@ -145,7 +145,10 @@ declare class ProjectsAPI {
145
145
  }): Promise<Project[]>;
146
146
  /** Get a project by ID. */
147
147
  get(id: string): Promise<Project>;
148
- /** Create a new project. */
148
+ /**
149
+ * Create a new project. A scoped key can create one only in a workspace it
150
+ * covers; anywhere else it gets HTTP 403 with `code: 'PROJECT_OUT_OF_SCOPE'`.
151
+ */
149
152
  create(opts: CreateProjectOpts): Promise<Project>;
150
153
  /** Update a project's name. */
151
154
  update(id: string, opts: {
@@ -671,7 +674,7 @@ declare class Watcher {
671
674
  * State of a sandbox's desktop, and the URLs that stream it.
672
675
  *
673
676
  * **Both URLs are credentials.** The port is published on a public hostname, and each
674
- * URL carries the stream token and a VNC password. Anyone holding `url` can see and
677
+ * URL carries the stream token and a password. Anyone holding `url` can see and
675
678
  * control the desktop; anyone holding `viewOnlyUrl` can watch it. Do not log them or
676
679
  * put them anywhere you would not put an API key.
677
680
  */
@@ -680,9 +683,9 @@ interface DesktopInfo {
680
683
  /** Screen width in pixels, or `null` when the desktop has never been started. */
681
684
  width: number | null;
682
685
  height: number | null;
683
- /** noVNC page with full control (mouse and keyboard). Treat like a credential. */
686
+ /** Viewer page with full control (mouse and keyboard): WebRTC, with noVNC as the fallback. Treat like a credential. */
684
687
  url: string;
685
- /** noVNC page that can only watch; enforced by the VNC server, not the page. */
688
+ /** Viewer page that can only watch; enforced by the stream server in the sandbox, not the page. */
686
689
  viewOnlyUrl: string;
687
690
  }
688
691
  interface DesktopStartOpts {
@@ -702,8 +705,9 @@ interface ClickOpts {
702
705
  /**
703
706
  * A graphical desktop in the sandbox, for computer-use agents and for people to watch.
704
707
  *
705
- * Only the `desktop` template has one (Xvfb + XFCE + Chromium on `DISPLAY=:1`); on
706
- * any other template the calls fail with `code === 'DESKTOP_NOT_SUPPORTED'`.
708
+ * Only the `desktop` template has one (Xvfb + Openbox + Chromium on `DISPLAY=:1`,
709
+ * streamed over WebRTC with noVNC as the fallback); on any other template the calls
710
+ * fail with `code === 'DESKTOP_NOT_SUPPORTED'`.
707
711
  *
708
712
  * Input and screenshots run inside the sandbox with `xdotool` and `scrot`. Every
709
713
  * argument goes to the sandbox as an argv array and is never parsed by a shell, so
@@ -792,9 +796,9 @@ interface SandboxInfo {
792
796
  /** The sandbox runtime: `'firecracker'` (a microVM), `'container'`, or `null` when unknown. */
793
797
  runtime?: 'firecracker' | 'container' | null;
794
798
  pauseError?: string | null;
795
- /** Machine size (`'small' | 'medium' | 'large'`); may be `null` for a sandbox created from a private snapshot. */
799
+ /** Machine size (`'small' | 'medium' | 'large'`); may be `null` for a sandbox billed by measured usage, such as one restored from a container snapshot. */
796
800
  size?: 'small' | 'medium' | 'large' | null;
797
- /** Flat hourly price in USD for `size`, billed per second while running; may be `null` when billed by measured usage (private snapshot). */
801
+ /** Flat hourly price in USD for `size`, billed per second while running; may be `null` when billed by measured usage (a container snapshot). */
798
802
  pricePerHour?: number | null;
799
803
  cpus?: number;
800
804
  memoryMb?: number;
@@ -826,8 +830,8 @@ interface SandboxOpts extends ConnectionOpts {
826
830
  projectId?: string;
827
831
  template?: string;
828
832
  /**
829
- * Create from a private saved snapshot. The sandbox runs on the machine the
830
- * snapshot was captured on and is billed by measured usage; `size` is ignored.
833
+ * Create from a private saved snapshot. The sandbox runs at the source sandbox's
834
+ * size and is billed flat at that size; `size` is ignored.
831
835
  */
832
836
  snapshotId?: string;
833
837
  /**
@@ -1154,9 +1158,10 @@ declare class Sandbox extends SandboxClient {
1154
1158
  */
1155
1159
  exposePort(port: number, opts?: ConnectionOpts): Promise<ExposedPort>;
1156
1160
  /**
1157
- * Clone this running sandbox `count` times (1-10). Each fork is a copy of the
1161
+ * Clone this running sandbox `count` times (1-100). Each fork is a copy of the
1158
1162
  * microVM at this instant -- memory, running processes and files -- and is
1159
- * billed like its source. The source keeps running.
1163
+ * billed like its source. The source keeps running. Each copy counts as a
1164
+ * sandbox start, so the account's running-sandbox limit is the real cap.
1160
1165
  *
1161
1166
  * Returns one entry per requested fork, in order: `{ sandbox, info }`, or
1162
1167
  * `{ error }` for a copy that could not be made (e.g. the account's sandbox
@@ -1420,14 +1425,22 @@ declare class WorkspacesAPI {
1420
1425
  * project. A full key returns every workspace the account belongs to.
1421
1426
  */
1422
1427
  list(): Promise<Workspace[]>;
1423
- /** Create a workspace. The caller becomes its owner. */
1428
+ /**
1429
+ * Create a workspace. The caller becomes its owner.
1430
+ *
1431
+ * Needs a key with no scopes: a scoped key gets HTTP 403 with
1432
+ * `code: 'ACCOUNT_SCOPE_REQUIRED'`.
1433
+ */
1424
1434
  create(opts: CreateWorkspaceOpts): Promise<Workspace>;
1425
1435
  /**
1426
1436
  * Delete a workspace.
1427
1437
  *
1428
- * Empty-only by default — the server refuses while any project or sandbox remains,
1429
- * so this cannot quietly destroy a user's work. Pass `{ force: true }` to delete a
1430
- * workspace and everything in it; that is irreversible.
1438
+ * Empty-only by default — the server refuses while any project, sandbox or volume
1439
+ * remains, so this cannot quietly destroy a user's work. `{ force: true }` deletes
1440
+ * the workspace with its apps, addons and projects; that is irreversible. It does not
1441
+ * delete sandboxes, sandbox volumes or snapshots: a running sandbox keeps running with
1442
+ * no workspace left to find it in. Kill the workspace's sandboxes and delete its
1443
+ * volumes and snapshots first.
1431
1444
  */
1432
1445
  delete(id: string, opts?: {
1433
1446
  force?: boolean;
@@ -1842,9 +1855,9 @@ declare class Volume {
1842
1855
  /**
1843
1856
  * Resize a volume, by name or by id, to `sizeGb`.
1844
1857
  *
1845
- * **Firecracker volumes cannot be resized yet**: volumes created for the default
1846
- * Firecracker sandboxes throw `LizardError` (400) with `code: 'volume_not_resizable'`.
1847
- * Create the volume at the size you need, or copy the data to a new, larger one.
1858
+ * A Firecracker volume (the default) can only grow. The change is in place and
1859
+ * online: a sandbox with the volume attached keeps running and sees the new size.
1860
+ * A smaller size throws `LizardError` (400) with `code: 'volume_shrink_unsupported'`.
1848
1861
  *
1849
1862
  * On a container volume the change is in place and online: the size is a quota,
1850
1863
  * so no data is copied and a mounted sandbox sees the new size immediately. Both
@@ -1852,16 +1865,17 @@ declare class Volume {
1852
1865
  * new size free — otherwise it throws `ConflictError` with
1853
1866
  * `code: 'volume_too_full_to_shrink'`.
1854
1867
  *
1855
- * Other failures carry a `code` too: `invalid_volume_size`, `volume_not_resizable`,
1868
+ * Other failures carry a `code` too: `invalid_volume_size`, `volume_too_large`,
1869
+ * `volume_not_resizable` (an app's volume, not a sandbox volume),
1856
1870
  * `volume_resize_in_progress`, `volume_not_provisioned`,
1857
1871
  * `volume_capacity_unavailable`, and `volume_resize_timeout` (a `TimeoutError`;
1858
1872
  * the size is left unchanged).
1859
1873
  *
1860
- * @returns The volume's updated info, including `sizeEnforced`.
1874
+ * @returns The volume's updated info, including `sizeEnforced` for a container volume.
1861
1875
  */
1862
1876
  static resize(projectId: string, nameOrId: string, sizeGb: number, opts?: ConnectionOpts): Promise<VolumeInfo>;
1863
1877
  getInfo(projectId: string): Promise<VolumeInfo>;
1864
- /** Resize this volume — see the static {@link Volume.resize} (not supported for Firecracker volumes yet). */
1878
+ /** Resize this volume — see the static {@link Volume.resize} (a Firecracker volume can only grow). */
1865
1879
  resize(projectId: string, sizeGb: number): Promise<VolumeInfo>;
1866
1880
  delete(projectId: string): Promise<void>;
1867
1881
  }
@@ -2154,7 +2168,7 @@ interface CreateContextOpts {
2154
2168
  * Limits, honestly:
2155
2169
  * - `javascript` (node) and `bash` run each call as a fresh process: no state
2156
2170
  * carries over and `results` stays empty. `node` must be installed in the
2157
- * template; the `interpreter` template does not ship it.
2171
+ * template; the `interpreter` template ships Node 22.
2158
2172
  * - On a template without Jupyter (e.g. `base`), Python also runs as a fresh
2159
2173
  * process per call.
2160
2174
  * - `stdout`/`stderr` are delivered in the chunks the kernel or process
@@ -2168,7 +2182,8 @@ interface CreateContextOpts {
2168
2182
  * const sandbox = await CodeSandbox.create({ project: 'my-project' })
2169
2183
  * await sandbox.runCode('x = 21')
2170
2184
  * const run = await sandbox.runCode('x * 2')
2171
- * console.log(run.results[0].data) // "42"
2185
+ * const out = run.results[0]
2186
+ * if (out?.type === 'result') console.log(out.data) // "42"
2172
2187
  * await sandbox.kill()
2173
2188
  * ```
2174
2189
  */
package/dist/index.d.ts CHANGED
@@ -145,7 +145,10 @@ declare class ProjectsAPI {
145
145
  }): Promise<Project[]>;
146
146
  /** Get a project by ID. */
147
147
  get(id: string): Promise<Project>;
148
- /** Create a new project. */
148
+ /**
149
+ * Create a new project. A scoped key can create one only in a workspace it
150
+ * covers; anywhere else it gets HTTP 403 with `code: 'PROJECT_OUT_OF_SCOPE'`.
151
+ */
149
152
  create(opts: CreateProjectOpts): Promise<Project>;
150
153
  /** Update a project's name. */
151
154
  update(id: string, opts: {
@@ -671,7 +674,7 @@ declare class Watcher {
671
674
  * State of a sandbox's desktop, and the URLs that stream it.
672
675
  *
673
676
  * **Both URLs are credentials.** The port is published on a public hostname, and each
674
- * URL carries the stream token and a VNC password. Anyone holding `url` can see and
677
+ * URL carries the stream token and a password. Anyone holding `url` can see and
675
678
  * control the desktop; anyone holding `viewOnlyUrl` can watch it. Do not log them or
676
679
  * put them anywhere you would not put an API key.
677
680
  */
@@ -680,9 +683,9 @@ interface DesktopInfo {
680
683
  /** Screen width in pixels, or `null` when the desktop has never been started. */
681
684
  width: number | null;
682
685
  height: number | null;
683
- /** noVNC page with full control (mouse and keyboard). Treat like a credential. */
686
+ /** Viewer page with full control (mouse and keyboard): WebRTC, with noVNC as the fallback. Treat like a credential. */
684
687
  url: string;
685
- /** noVNC page that can only watch; enforced by the VNC server, not the page. */
688
+ /** Viewer page that can only watch; enforced by the stream server in the sandbox, not the page. */
686
689
  viewOnlyUrl: string;
687
690
  }
688
691
  interface DesktopStartOpts {
@@ -702,8 +705,9 @@ interface ClickOpts {
702
705
  /**
703
706
  * A graphical desktop in the sandbox, for computer-use agents and for people to watch.
704
707
  *
705
- * Only the `desktop` template has one (Xvfb + XFCE + Chromium on `DISPLAY=:1`); on
706
- * any other template the calls fail with `code === 'DESKTOP_NOT_SUPPORTED'`.
708
+ * Only the `desktop` template has one (Xvfb + Openbox + Chromium on `DISPLAY=:1`,
709
+ * streamed over WebRTC with noVNC as the fallback); on any other template the calls
710
+ * fail with `code === 'DESKTOP_NOT_SUPPORTED'`.
707
711
  *
708
712
  * Input and screenshots run inside the sandbox with `xdotool` and `scrot`. Every
709
713
  * argument goes to the sandbox as an argv array and is never parsed by a shell, so
@@ -792,9 +796,9 @@ interface SandboxInfo {
792
796
  /** The sandbox runtime: `'firecracker'` (a microVM), `'container'`, or `null` when unknown. */
793
797
  runtime?: 'firecracker' | 'container' | null;
794
798
  pauseError?: string | null;
795
- /** Machine size (`'small' | 'medium' | 'large'`); may be `null` for a sandbox created from a private snapshot. */
799
+ /** Machine size (`'small' | 'medium' | 'large'`); may be `null` for a sandbox billed by measured usage, such as one restored from a container snapshot. */
796
800
  size?: 'small' | 'medium' | 'large' | null;
797
- /** Flat hourly price in USD for `size`, billed per second while running; may be `null` when billed by measured usage (private snapshot). */
801
+ /** Flat hourly price in USD for `size`, billed per second while running; may be `null` when billed by measured usage (a container snapshot). */
798
802
  pricePerHour?: number | null;
799
803
  cpus?: number;
800
804
  memoryMb?: number;
@@ -826,8 +830,8 @@ interface SandboxOpts extends ConnectionOpts {
826
830
  projectId?: string;
827
831
  template?: string;
828
832
  /**
829
- * Create from a private saved snapshot. The sandbox runs on the machine the
830
- * snapshot was captured on and is billed by measured usage; `size` is ignored.
833
+ * Create from a private saved snapshot. The sandbox runs at the source sandbox's
834
+ * size and is billed flat at that size; `size` is ignored.
831
835
  */
832
836
  snapshotId?: string;
833
837
  /**
@@ -1154,9 +1158,10 @@ declare class Sandbox extends SandboxClient {
1154
1158
  */
1155
1159
  exposePort(port: number, opts?: ConnectionOpts): Promise<ExposedPort>;
1156
1160
  /**
1157
- * Clone this running sandbox `count` times (1-10). Each fork is a copy of the
1161
+ * Clone this running sandbox `count` times (1-100). Each fork is a copy of the
1158
1162
  * microVM at this instant -- memory, running processes and files -- and is
1159
- * billed like its source. The source keeps running.
1163
+ * billed like its source. The source keeps running. Each copy counts as a
1164
+ * sandbox start, so the account's running-sandbox limit is the real cap.
1160
1165
  *
1161
1166
  * Returns one entry per requested fork, in order: `{ sandbox, info }`, or
1162
1167
  * `{ error }` for a copy that could not be made (e.g. the account's sandbox
@@ -1420,14 +1425,22 @@ declare class WorkspacesAPI {
1420
1425
  * project. A full key returns every workspace the account belongs to.
1421
1426
  */
1422
1427
  list(): Promise<Workspace[]>;
1423
- /** Create a workspace. The caller becomes its owner. */
1428
+ /**
1429
+ * Create a workspace. The caller becomes its owner.
1430
+ *
1431
+ * Needs a key with no scopes: a scoped key gets HTTP 403 with
1432
+ * `code: 'ACCOUNT_SCOPE_REQUIRED'`.
1433
+ */
1424
1434
  create(opts: CreateWorkspaceOpts): Promise<Workspace>;
1425
1435
  /**
1426
1436
  * Delete a workspace.
1427
1437
  *
1428
- * Empty-only by default — the server refuses while any project or sandbox remains,
1429
- * so this cannot quietly destroy a user's work. Pass `{ force: true }` to delete a
1430
- * workspace and everything in it; that is irreversible.
1438
+ * Empty-only by default — the server refuses while any project, sandbox or volume
1439
+ * remains, so this cannot quietly destroy a user's work. `{ force: true }` deletes
1440
+ * the workspace with its apps, addons and projects; that is irreversible. It does not
1441
+ * delete sandboxes, sandbox volumes or snapshots: a running sandbox keeps running with
1442
+ * no workspace left to find it in. Kill the workspace's sandboxes and delete its
1443
+ * volumes and snapshots first.
1431
1444
  */
1432
1445
  delete(id: string, opts?: {
1433
1446
  force?: boolean;
@@ -1842,9 +1855,9 @@ declare class Volume {
1842
1855
  /**
1843
1856
  * Resize a volume, by name or by id, to `sizeGb`.
1844
1857
  *
1845
- * **Firecracker volumes cannot be resized yet**: volumes created for the default
1846
- * Firecracker sandboxes throw `LizardError` (400) with `code: 'volume_not_resizable'`.
1847
- * Create the volume at the size you need, or copy the data to a new, larger one.
1858
+ * A Firecracker volume (the default) can only grow. The change is in place and
1859
+ * online: a sandbox with the volume attached keeps running and sees the new size.
1860
+ * A smaller size throws `LizardError` (400) with `code: 'volume_shrink_unsupported'`.
1848
1861
  *
1849
1862
  * On a container volume the change is in place and online: the size is a quota,
1850
1863
  * so no data is copied and a mounted sandbox sees the new size immediately. Both
@@ -1852,16 +1865,17 @@ declare class Volume {
1852
1865
  * new size free — otherwise it throws `ConflictError` with
1853
1866
  * `code: 'volume_too_full_to_shrink'`.
1854
1867
  *
1855
- * Other failures carry a `code` too: `invalid_volume_size`, `volume_not_resizable`,
1868
+ * Other failures carry a `code` too: `invalid_volume_size`, `volume_too_large`,
1869
+ * `volume_not_resizable` (an app's volume, not a sandbox volume),
1856
1870
  * `volume_resize_in_progress`, `volume_not_provisioned`,
1857
1871
  * `volume_capacity_unavailable`, and `volume_resize_timeout` (a `TimeoutError`;
1858
1872
  * the size is left unchanged).
1859
1873
  *
1860
- * @returns The volume's updated info, including `sizeEnforced`.
1874
+ * @returns The volume's updated info, including `sizeEnforced` for a container volume.
1861
1875
  */
1862
1876
  static resize(projectId: string, nameOrId: string, sizeGb: number, opts?: ConnectionOpts): Promise<VolumeInfo>;
1863
1877
  getInfo(projectId: string): Promise<VolumeInfo>;
1864
- /** Resize this volume — see the static {@link Volume.resize} (not supported for Firecracker volumes yet). */
1878
+ /** Resize this volume — see the static {@link Volume.resize} (a Firecracker volume can only grow). */
1865
1879
  resize(projectId: string, sizeGb: number): Promise<VolumeInfo>;
1866
1880
  delete(projectId: string): Promise<void>;
1867
1881
  }
@@ -2154,7 +2168,7 @@ interface CreateContextOpts {
2154
2168
  * Limits, honestly:
2155
2169
  * - `javascript` (node) and `bash` run each call as a fresh process: no state
2156
2170
  * carries over and `results` stays empty. `node` must be installed in the
2157
- * template; the `interpreter` template does not ship it.
2171
+ * template; the `interpreter` template ships Node 22.
2158
2172
  * - On a template without Jupyter (e.g. `base`), Python also runs as a fresh
2159
2173
  * process per call.
2160
2174
  * - `stdout`/`stderr` are delivered in the chunks the kernel or process
@@ -2168,7 +2182,8 @@ interface CreateContextOpts {
2168
2182
  * const sandbox = await CodeSandbox.create({ project: 'my-project' })
2169
2183
  * await sandbox.runCode('x = 21')
2170
2184
  * const run = await sandbox.runCode('x * 2')
2171
- * console.log(run.results[0].data) // "42"
2185
+ * const out = run.results[0]
2186
+ * if (out?.type === 'result') console.log(out.data) // "42"
2172
2187
  * await sandbox.kill()
2173
2188
  * ```
2174
2189
  */
package/dist/index.js CHANGED
@@ -1258,9 +1258,10 @@ var Sandbox = class extends SandboxClient {
1258
1258
  return exposed;
1259
1259
  }
1260
1260
  /**
1261
- * Clone this running sandbox `count` times (1-10). Each fork is a copy of the
1261
+ * Clone this running sandbox `count` times (1-100). Each fork is a copy of the
1262
1262
  * microVM at this instant -- memory, running processes and files -- and is
1263
- * billed like its source. The source keeps running.
1263
+ * billed like its source. The source keeps running. Each copy counts as a
1264
+ * sandbox start, so the account's running-sandbox limit is the real cap.
1264
1265
  *
1265
1266
  * Returns one entry per requested fork, in order: `{ sandbox, info }`, or
1266
1267
  * `{ error }` for a copy that could not be made (e.g. the account's sandbox
@@ -1381,7 +1382,10 @@ var ProjectsAPI = class {
1381
1382
  get(id) {
1382
1383
  return this.client.get(`/api/projects/${id}`);
1383
1384
  }
1384
- /** Create a new project. */
1385
+ /**
1386
+ * Create a new project. A scoped key can create one only in a workspace it
1387
+ * covers; anywhere else it gets HTTP 403 with `code: 'PROJECT_OUT_OF_SCOPE'`.
1388
+ */
1385
1389
  create(opts) {
1386
1390
  return this.client.post("/api/projects", opts);
1387
1391
  }
@@ -1650,16 +1654,24 @@ var WorkspacesAPI = class {
1650
1654
  list() {
1651
1655
  return this.client.get("/api/workspaces");
1652
1656
  }
1653
- /** Create a workspace. The caller becomes its owner. */
1657
+ /**
1658
+ * Create a workspace. The caller becomes its owner.
1659
+ *
1660
+ * Needs a key with no scopes: a scoped key gets HTTP 403 with
1661
+ * `code: 'ACCOUNT_SCOPE_REQUIRED'`.
1662
+ */
1654
1663
  create(opts) {
1655
1664
  return this.client.post("/api/workspaces", opts);
1656
1665
  }
1657
1666
  /**
1658
1667
  * Delete a workspace.
1659
1668
  *
1660
- * Empty-only by default — the server refuses while any project or sandbox remains,
1661
- * so this cannot quietly destroy a user's work. Pass `{ force: true }` to delete a
1662
- * workspace and everything in it; that is irreversible.
1669
+ * Empty-only by default — the server refuses while any project, sandbox or volume
1670
+ * remains, so this cannot quietly destroy a user's work. `{ force: true }` deletes
1671
+ * the workspace with its apps, addons and projects; that is irreversible. It does not
1672
+ * delete sandboxes, sandbox volumes or snapshots: a running sandbox keeps running with
1673
+ * no workspace left to find it in. Kill the workspace's sandboxes and delete its
1674
+ * volumes and snapshots first.
1663
1675
  */
1664
1676
  delete(id, opts) {
1665
1677
  const qs = opts?.force ? "" : "?requireEmpty=true";
@@ -1853,9 +1865,9 @@ var Volume = class _Volume {
1853
1865
  /**
1854
1866
  * Resize a volume, by name or by id, to `sizeGb`.
1855
1867
  *
1856
- * **Firecracker volumes cannot be resized yet**: volumes created for the default
1857
- * Firecracker sandboxes throw `LizardError` (400) with `code: 'volume_not_resizable'`.
1858
- * Create the volume at the size you need, or copy the data to a new, larger one.
1868
+ * A Firecracker volume (the default) can only grow. The change is in place and
1869
+ * online: a sandbox with the volume attached keeps running and sees the new size.
1870
+ * A smaller size throws `LizardError` (400) with `code: 'volume_shrink_unsupported'`.
1859
1871
  *
1860
1872
  * On a container volume the change is in place and online: the size is a quota,
1861
1873
  * so no data is copied and a mounted sandbox sees the new size immediately. Both
@@ -1863,12 +1875,13 @@ var Volume = class _Volume {
1863
1875
  * new size free — otherwise it throws `ConflictError` with
1864
1876
  * `code: 'volume_too_full_to_shrink'`.
1865
1877
  *
1866
- * Other failures carry a `code` too: `invalid_volume_size`, `volume_not_resizable`,
1878
+ * Other failures carry a `code` too: `invalid_volume_size`, `volume_too_large`,
1879
+ * `volume_not_resizable` (an app's volume, not a sandbox volume),
1867
1880
  * `volume_resize_in_progress`, `volume_not_provisioned`,
1868
1881
  * `volume_capacity_unavailable`, and `volume_resize_timeout` (a `TimeoutError`;
1869
1882
  * the size is left unchanged).
1870
1883
  *
1871
- * @returns The volume's updated info, including `sizeEnforced`.
1884
+ * @returns The volume's updated info, including `sizeEnforced` for a container volume.
1872
1885
  */
1873
1886
  static async resize(projectId, nameOrId, sizeGb, opts) {
1874
1887
  const config = new ConnectionConfig(opts);
@@ -1887,7 +1900,7 @@ var Volume = class _Volume {
1887
1900
  if (!res.ok) await handleApiError(res);
1888
1901
  return res.json();
1889
1902
  }
1890
- /** Resize this volume — see the static {@link Volume.resize} (not supported for Firecracker volumes yet). */
1903
+ /** Resize this volume — see the static {@link Volume.resize} (a Firecracker volume can only grow). */
1891
1904
  async resize(projectId, sizeGb) {
1892
1905
  return _Volume.resize(projectId, this.volumeId, sizeGb, {
1893
1906
  apiKey: this.config.apiKey,
package/dist/index.mjs CHANGED
@@ -1205,9 +1205,10 @@ var Sandbox = class extends SandboxClient {
1205
1205
  return exposed;
1206
1206
  }
1207
1207
  /**
1208
- * Clone this running sandbox `count` times (1-10). Each fork is a copy of the
1208
+ * Clone this running sandbox `count` times (1-100). Each fork is a copy of the
1209
1209
  * microVM at this instant -- memory, running processes and files -- and is
1210
- * billed like its source. The source keeps running.
1210
+ * billed like its source. The source keeps running. Each copy counts as a
1211
+ * sandbox start, so the account's running-sandbox limit is the real cap.
1211
1212
  *
1212
1213
  * Returns one entry per requested fork, in order: `{ sandbox, info }`, or
1213
1214
  * `{ error }` for a copy that could not be made (e.g. the account's sandbox
@@ -1328,7 +1329,10 @@ var ProjectsAPI = class {
1328
1329
  get(id) {
1329
1330
  return this.client.get(`/api/projects/${id}`);
1330
1331
  }
1331
- /** Create a new project. */
1332
+ /**
1333
+ * Create a new project. A scoped key can create one only in a workspace it
1334
+ * covers; anywhere else it gets HTTP 403 with `code: 'PROJECT_OUT_OF_SCOPE'`.
1335
+ */
1332
1336
  create(opts) {
1333
1337
  return this.client.post("/api/projects", opts);
1334
1338
  }
@@ -1597,16 +1601,24 @@ var WorkspacesAPI = class {
1597
1601
  list() {
1598
1602
  return this.client.get("/api/workspaces");
1599
1603
  }
1600
- /** Create a workspace. The caller becomes its owner. */
1604
+ /**
1605
+ * Create a workspace. The caller becomes its owner.
1606
+ *
1607
+ * Needs a key with no scopes: a scoped key gets HTTP 403 with
1608
+ * `code: 'ACCOUNT_SCOPE_REQUIRED'`.
1609
+ */
1601
1610
  create(opts) {
1602
1611
  return this.client.post("/api/workspaces", opts);
1603
1612
  }
1604
1613
  /**
1605
1614
  * Delete a workspace.
1606
1615
  *
1607
- * Empty-only by default — the server refuses while any project or sandbox remains,
1608
- * so this cannot quietly destroy a user's work. Pass `{ force: true }` to delete a
1609
- * workspace and everything in it; that is irreversible.
1616
+ * Empty-only by default — the server refuses while any project, sandbox or volume
1617
+ * remains, so this cannot quietly destroy a user's work. `{ force: true }` deletes
1618
+ * the workspace with its apps, addons and projects; that is irreversible. It does not
1619
+ * delete sandboxes, sandbox volumes or snapshots: a running sandbox keeps running with
1620
+ * no workspace left to find it in. Kill the workspace's sandboxes and delete its
1621
+ * volumes and snapshots first.
1610
1622
  */
1611
1623
  delete(id, opts) {
1612
1624
  const qs = opts?.force ? "" : "?requireEmpty=true";
@@ -1800,9 +1812,9 @@ var Volume = class _Volume {
1800
1812
  /**
1801
1813
  * Resize a volume, by name or by id, to `sizeGb`.
1802
1814
  *
1803
- * **Firecracker volumes cannot be resized yet**: volumes created for the default
1804
- * Firecracker sandboxes throw `LizardError` (400) with `code: 'volume_not_resizable'`.
1805
- * Create the volume at the size you need, or copy the data to a new, larger one.
1815
+ * A Firecracker volume (the default) can only grow. The change is in place and
1816
+ * online: a sandbox with the volume attached keeps running and sees the new size.
1817
+ * A smaller size throws `LizardError` (400) with `code: 'volume_shrink_unsupported'`.
1806
1818
  *
1807
1819
  * On a container volume the change is in place and online: the size is a quota,
1808
1820
  * so no data is copied and a mounted sandbox sees the new size immediately. Both
@@ -1810,12 +1822,13 @@ var Volume = class _Volume {
1810
1822
  * new size free — otherwise it throws `ConflictError` with
1811
1823
  * `code: 'volume_too_full_to_shrink'`.
1812
1824
  *
1813
- * Other failures carry a `code` too: `invalid_volume_size`, `volume_not_resizable`,
1825
+ * Other failures carry a `code` too: `invalid_volume_size`, `volume_too_large`,
1826
+ * `volume_not_resizable` (an app's volume, not a sandbox volume),
1814
1827
  * `volume_resize_in_progress`, `volume_not_provisioned`,
1815
1828
  * `volume_capacity_unavailable`, and `volume_resize_timeout` (a `TimeoutError`;
1816
1829
  * the size is left unchanged).
1817
1830
  *
1818
- * @returns The volume's updated info, including `sizeEnforced`.
1831
+ * @returns The volume's updated info, including `sizeEnforced` for a container volume.
1819
1832
  */
1820
1833
  static async resize(projectId, nameOrId, sizeGb, opts) {
1821
1834
  const config = new ConnectionConfig(opts);
@@ -1834,7 +1847,7 @@ var Volume = class _Volume {
1834
1847
  if (!res.ok) await handleApiError(res);
1835
1848
  return res.json();
1836
1849
  }
1837
- /** Resize this volume — see the static {@link Volume.resize} (not supported for Firecracker volumes yet). */
1850
+ /** Resize this volume — see the static {@link Volume.resize} (a Firecracker volume can only grow). */
1838
1851
  async resize(projectId, sizeGb) {
1839
1852
  return _Volume.resize(projectId, this.volumeId, sizeGb, {
1840
1853
  apiKey: this.config.apiKey,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lizard-build/sdk",
3
- "version": "0.1.59",
3
+ "version": "0.1.60",
4
4
  "description": "TypeScript SDK for Lizard: Firecracker microVM sandboxes, code execution and cloud apps.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",