@lizard-build/sdk 0.1.40 → 0.1.41

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
@@ -1,293 +1,93 @@
1
- # Lizard SDK
1
+ # Lizard TypeScript SDK
2
2
 
3
- TypeScript and Python clients for Lizard apps, managed databases, storage and Linux sandboxes.
4
-
5
- The current platform runs sandboxes on Kubernetes. Persistent volumes keep files across sandbox lifetimes. Pause, resume, fork and snapshot restore currently return HTTP 501; the SDK exposes those endpoints but does not promise state preservation.
6
-
7
- Requires Node.js 18+ or Python 3.10+. The optional `LizardCLI` adapter and `billing.payX402()` / `billing.pay_x402()` require Lizard CLI 4.0.8+ on PATH.
8
-
9
- See [CLI coverage and migration notes](https://github.com/lizard-build/lizard-sdk/blob/main/docs/cli-parity.md) for native methods, CLI-backed operations and test scope.
3
+ Run commands, work with files and execute code in Linux sandboxes on Kubernetes. The SDK also manages Lizard apps, databases, storage and projects.
10
4
 
11
5
  ## Install
12
6
 
13
- ```bash
14
- # JavaScript / TypeScript
15
- npm install @lizard-build/sdk
16
-
17
- # Python
18
- pip install lizard-sdk
19
- ```
20
-
21
- ## Quickstart
22
-
23
- ### JavaScript / TypeScript
24
-
25
- ```ts
26
- import { Lizard } from '@lizard-build/sdk'
7
+ Requires Node.js 18+.
27
8
 
28
- // A client is pinned to one project — sandboxes are billed per project, so a
29
- // project is required. It can be the project's ID, slug, or name.
30
- // apiKey defaults to the LIZARD_API_KEY env var.
31
- const lizard = new Lizard({ project: 'my-project' })
32
-
33
- // Boot a sandbox from the 'base' template (Debian + Node.js 26)
34
- const sandbox = await lizard.create('base')
35
-
36
- // Write a file directly into the sandbox filesystem
37
- await sandbox.fs.write('/app/server.js', `
38
- const http = require('http')
39
- http.createServer((_, res) => res.end('hello from Lizard')).listen(3000)
40
- `)
41
-
42
- // Execute a process inside the sandbox
43
- await sandbox.process.exec('node /app/server.js &')
44
-
45
- // Get a public HTTPS URL for port 3000 inside the sandbox
46
- const url = await sandbox.getHost(3000)
47
- console.log(`Live at https://${url}`)
48
-
49
- // Tear down the sandbox when done
50
- await sandbox.kill()
9
+ ```sh
10
+ npm install @lizard-build/sdk
51
11
  ```
52
12
 
53
- ### Python
54
-
55
- ```python
56
- from lizard import Lizard
57
-
58
- # A client is pinned to one project — sandboxes are billed per project, so a
59
- # project is required (its ID, slug, or name). api_key defaults to LIZARD_API_KEY.
60
- lizard = Lizard(project="my-project")
61
-
62
- # Boot a Python sandbox from the 'code-interpreter-v1' template
63
- sandbox = lizard.create("code-interpreter-v1")
64
-
65
- # Write a script into the sandbox filesystem
66
- sandbox.fs.write("/app/main.py", """
67
- import http.server, socketserver
68
-
69
- class Handler(http.server.SimpleHTTPRequestHandler):
70
- def do_GET(self):
71
- self.send_response(200)
72
- self.end_headers()
73
- self.wfile.write(b"hello from Lizard")
13
+ Create an API key and project at [lizard.build](https://lizard.build), then set:
74
14
 
75
- with socketserver.TCPServer(("", 3000), Handler) as httpd:
76
- httpd.serve_forever()
77
- """)
78
-
79
- # Execute a process inside the sandbox
80
- sandbox.process.exec_("python /app/main.py &")
81
-
82
- print(f"Live at https://{sandbox.get_host(3000)}")
83
-
84
- sandbox.kill()
15
+ ```sh
16
+ export LIZARD_API_KEY="your-api-key"
85
17
  ```
86
18
 
87
- ## Persisting Work Across Sandboxes
88
-
89
- Sandboxes are **ephemeral**: killing one, or letting it hit its timeout, discards everything written inside it. State that has to outlive a sandbox goes on a **volume** — a separate disk mounted at `/workspace` that a later sandbox re-attaches.
90
-
91
- A volume is node-local, so it fixes the region too. You don't thread a region through both calls: the sandbox is placed wherever its volume already lives.
92
-
93
- ```ts
94
- const vol = await lizard.volumes.getOrCreate('agent-scratch', { sizeGb: 10 })
95
-
96
- const first = await lizard.create('codex', { volumeName: 'agent-scratch' })
97
- await first.process.exec('pip install numpy pandas && echo "notes" > /workspace/notes.txt')
98
- await first.kill() // sandbox gone, /workspace survives
99
-
100
- const second = await lizard.create('codex', { volumeName: 'agent-scratch' })
101
- console.log(await second.fs.read('/workspace/notes.txt')) // "notes"
102
- ```
19
+ ## Create a sandbox
103
20
 
104
- ```python
105
- vol = lizard.volumes.get_or_create("agent-scratch", size_gb=10)
21
+ Save this as `quickstart.mjs`. Replace `my-project` with your project's ID, slug or unique name, then run `node quickstart.mjs`.
106
22
 
107
- first = lizard.create("codex", volume_name="agent-scratch")
108
- first.process.exec_('echo "notes" > /workspace/notes.txt')
109
- first.kill() # sandbox gone, /workspace survives
23
+ ```js
24
+ import { Lizard } from '@lizard-build/sdk'
110
25
 
111
- second = lizard.create("codex", volume_name="agent-scratch")
112
- print(second.fs.read("/workspace/notes.txt")) # "notes"
26
+ const lizard = new Lizard({ project: 'my-project' })
27
+ const sandbox = await lizard.create('base', { timeoutMs: 300_000 })
28
+
29
+ try {
30
+ await sandbox.fs.write('/tmp/hello.txt', 'Hello from Lizard!')
31
+ const result = await sandbox.process.exec('cat /tmp/hello.txt')
32
+ if (result.exitCode !== 0) throw new Error(result.stderr)
33
+ console.log(result.stdout)
34
+ } finally {
35
+ await sandbox.kill()
36
+ }
113
37
  ```
114
38
 
115
- Note that only `/workspace` survives — installed packages and in-memory state do not. Bake tooling into a template instead of reinstalling it per sandbox.
39
+ The package includes TypeScript declarations and ESM and CommonJS builds. For CommonJS, use `const { Lizard } = require('@lizard-build/sdk')` inside a program with an async entry point.
116
40
 
117
- > `pause()` / `resume()` exist on the client but are **not implemented** for the current runtime and always fail with HTTP 501. Use a volume.
41
+ ## Stream command output
118
42
 
119
- ## Giving Each of Your Users Their Own Workspace
120
-
121
- If you are building on top of Lizard and your users each need isolated resources, give each one a workspace and an API key scoped to it. They get isolation from each other; you keep one bill.
43
+ Inside the `try` block above:
122
44
 
123
45
  ```ts
124
- const lizard = new Lizard({ apiKey: process.env.LIZARD_API_KEY })
125
-
126
- const ws = await lizard.workspaces.create({ name: `user-${userId}` })
127
- const prj = await lizard.projects.create({ workspaceId: ws.id, name: 'default' })
128
- const key = await lizard.apiKeys.create({ name: `user-${userId}`, workspaces: [ws.id] })
129
-
130
- // key.key is returned exactly once. Store it now.
131
- await db.users.update(userId, { lizardKey: key.key })
132
- ```
133
-
134
- That key reaches nothing outside `ws`, and it cannot mint a broader one — the server rejects that with an exact subset check. So it is safe to hand to the user, and safe to put **inside a sandbox** so agent code can use the CLI as that user:
135
-
136
- ```ts
137
- const sandbox = await Sandbox.create('codex', {
138
- projectId: prj.id,
139
- lizardToken: key.key, // scoped — bounded if it leaks
46
+ const result = await sandbox.process.exec('printf "hello\\n"', {
47
+ timeoutMs: 30_000,
48
+ onStdout: chunk => process.stdout.write(chunk),
49
+ onStderr: chunk => process.stderr.write(chunk),
140
50
  })
141
- await sandbox.process.exec('lizard volume list') // sees only this workspace
51
+ if (result.exitCode !== 0) throw new Error(result.stderr)
142
52
  ```
143
53
 
144
- ```python
145
- ws = lizard.workspaces.create(name=f"user-{user_id}")
146
- prj = lizard.projects.create(workspace_id=ws.id, name="default")
147
- key = lizard.api_keys.create(name=f"user-{user_id}", workspaces=[ws.id])
148
-
149
- sandbox = Sandbox.create("codex", project_id=prj.id, lizard_token=key.key)
150
- sandbox.process.exec_("lizard volume list")
151
- ```
152
-
153
- A key with **no** scope has full access to everything the creating account can reach — pass `workspaces` or `projects` unless you mean that.
154
-
155
- ### What a scoped key deliberately cannot see
54
+ ## Run code
156
55
 
157
- Billing is account-level: one balance, one ledger, one set of saved cards, shared by every workspace. There is no per-workspace view of it, so a scoped key is refused outright (403 `ACCOUNT_SCOPE_REQUIRED`) on `lizard.billing.*` and on account-wide usage, and `whoami()` returns only identity plus the key's own scopes — not the account's email, balance or plan.
158
-
159
- That matters because of where these keys end up. A key you hand to a user, or inject into a sandbox with `lizardToken`, is readable by anything running there. It should not be a way to read your card details or spend against them.
160
-
161
- For per-workspace spend, use `lizard.metrics` — that is scoped, and is what you would bill a user from.
162
-
163
- ## API
164
-
165
- ### `new Lizard({ project, apiKey?, apiUrl?, timeoutMs? })`
166
-
167
- Create a client pinned to a project. Every sandbox is billed per project, so `project` is required — pass its ID, slug, or name (resolved to an ID on first use and cached). `apiKey` defaults to the `LIZARD_API_KEY` env var.
168
-
169
- ```ts
170
- const lizard = new Lizard({ project: 'my-project' })
171
- const sandbox = await lizard.create('base')
172
- const sandbox = await lizard.create('code-interpreter-v1', { timeoutMs: 10 * 60 * 1000 })
173
- ```
174
-
175
- ### `Sandbox.create(template?, opts?)`
176
-
177
- Create a Lizard sandbox using a template such as `base` or `code-interpreter-v1`. Template availability depends on the platform and region. A project is required — pass `project` (ID, slug, or name) or an exact `projectId` in `opts`, or use a `Lizard` client, which pins one for you.
56
+ `CodeSandbox` uses the `code-interpreter-v1` template by default. Code errors appear in `result.error`; request failures throw.
178
57
 
179
58
  ```ts
180
- const sandbox = await Sandbox.create('base', { project: 'my-project' })
181
- const sandbox = await Sandbox.create('code-interpreter-v1', { project: 'my-project', timeoutMs: 10 * 60 * 1000 })
59
+ import { CodeSandbox } from '@lizard-build/sdk'
60
+
61
+ const sandbox = await CodeSandbox.create({ project: 'my-project' })
62
+ try {
63
+ const result = await sandbox.runCode('print(6 * 7)', {
64
+ language: 'python',
65
+ timeoutMs: 60_000,
66
+ onStdout: chunk => process.stdout.write(chunk),
67
+ })
68
+ if (result.error) throw result.error
69
+ } finally {
70
+ await sandbox.kill()
71
+ }
182
72
  ```
183
73
 
184
- ### `Sandbox.connect(sandboxId, opts?)`
185
-
186
- Connect to an existing sandbox by ID. Throws `NotFoundError` if it has been killed or has expired.
187
-
188
- ### `Sandbox.list(opts?)`
189
-
190
- List all running sandboxes for the authenticated account.
191
-
192
- ---
193
-
194
- ### Account and provisioning
195
-
196
- | Namespace | Methods |
197
- |---|---|
198
- | `lizard.workspaces` | `list()`, `create({ name })`, `delete(id, { force? })`, `find(nameOrSlugOrId)` |
199
- | `lizard.apiKeys` | `list()`, `create({ name, workspaces?, projects? })`, `delete(id)` |
200
- | `lizard.projects` | `list({ workspaceId? })`, `get(id)`, `create({ workspaceId, name })`, `update(id, { name })`, `delete(id)` |
201
- | `lizard.regions` | `list()` |
202
- | `lizard.billing` | `balance()`, `transactions({ limit?, cursor?, includeUsage? })`, `summary()`, `live()` — **unscoped keys only** |
203
- | `lizard.whoami()` | The account behind the credential; a scoped key gets identity and its own scopes, not the account's email or balance |
204
- | `lizard.platform` | The raw HTTP client, for endpoints not wrapped yet |
205
-
206
- `workspaces.delete()` is empty-only by default; the server refuses while any project, sandbox or volume remains. `{ force: true }` deletes the workspace and everything in it, irreversibly.
207
-
208
- In Python the namespaces are `lizard.workspaces`, `lizard.api_keys`, `lizard.regions`, `lizard.billing`, and arguments are snake_case (`workspace_id`, `include_usage`).
209
-
210
- ### Volumes
211
-
212
- | Method | Description |
213
- |---|---|
214
- | `lizard.volumes.getOrCreate(name, { sizeGb?, region? })` | The volume with this name, created if absent |
215
- | `lizard.volumes.create(name, { sizeGb?, region? })` | Create; throws `ConflictError` if the name is taken |
216
- | `lizard.volumes.get(nameOrId)` | Look one up |
217
- | `lizard.volumes.list()` | Every volume in the client's project |
218
- | `lizard.volumes.delete(nameOrId)` | Delete |
219
-
220
- A volume's **name** is its key inside a project, so an agent can reconstruct it between runs without storing an id. The static `Volume.*` forms take an explicit `projectId` as their first argument.
221
-
222
- `region` places the volume (see `lizard.regions.list()` for valid ids) and, because a volume is node-local, also fixes the region of any sandbox that mounts it. You normally set it here or nowhere.
74
+ ## Configuration
223
75
 
224
- ---
76
+ The SDK reads `LIZARD_API_KEY` and, optionally, `LIZARD_API_URL` (default: `https://lizard.build`). You can also pass `apiKey` and `apiUrl` to the constructor or static sandbox methods.
225
77
 
226
- ### `sandbox.fs`
78
+ Pass `timeoutMs` to `create()` to set the sandbox lifetime. The default is five minutes. `timeoutMs` on `process.exec()` or `runCode()` controls that call, not the sandbox lifetime.
227
79
 
228
- Read and write files inside the sandbox filesystem.
80
+ ## Runtime limits
229
81
 
230
- | Method | Description |
231
- |---|---|
232
- | `fs.write(path, data)` | Write a file (string or bytes) |
233
- | `fs.read(path)` | Read a file as a string |
234
- | `fs.list(path)` | List directory contents |
235
- | `fs.remove(path)` | Delete a file or directory |
236
- | `fs.makeDir(path)` | Create a directory and parents |
82
+ Sandboxes run on Kubernetes. Use volumes mounted at `/workspace` to keep files after a sandbox ends. Pause, resume, fork, snapshot creation and snapshot restore return HTTP 501 on the current backend.
237
83
 
238
- ### `sandbox.process`
239
-
240
- Execute commands inside the sandbox.
241
-
242
- | Method | Description |
243
- |---|---|
244
- | `process.exec(cmd, opts?)` | Run a command and wait for it to finish |
245
-
246
- `exec` returns `{ stdout, stderr, exitCode }` (JS) or `ProcessResult` (Python). In Python the method is named `exec_` because `exec` is a reserved keyword.
247
-
248
- ### `sandbox.getHost(port)`
249
-
250
- Returns a public HTTPS URL for a port listening inside the sandbox — no tunneling required.
251
-
252
- ```ts
253
- await sandbox.process.exec('npx -y serve -p 3000 &')
254
- const url = await sandbox.getHost(3000)
255
- // https://{sandboxId}-3000.sandbox.{region}.onlizard.com
256
- ```
257
-
258
- ### `sandbox.pause()` / `sandbox.resume()`
259
-
260
- **Not implemented** for the current runtime — both always fail with HTTP 501. Use a [volume](#persisting-work-across-sandboxes) to carry work across sandboxes.
261
-
262
- ### `sandbox.kill()`
263
-
264
- Terminate the sandbox and release all resources.
265
-
266
- ### `sandbox.setTimeout(ms)`
267
-
268
- Extend or reduce the sandbox timeout.
269
-
270
- ---
271
-
272
- ## Environment Variables
273
-
274
- | Variable | Description |
275
- |---|---|
276
- | `LIZARD_API_KEY` | API key (required — get one at [lizard.build](https://lizard.build)) |
277
- | `LIZARD_API_URL` | Override the API base URL (default: `https://lizard.build`) |
278
-
279
- The `X-API-Key` header is used for all authenticated requests.
280
-
281
- ## Deploy What You Build
282
-
283
- Once your agent has produced a working app inside a sandbox, deploy it as a persistent Lizard service — no Dockerfile needed:
284
-
285
- ```bash
286
- lizard up
287
- ```
84
+ `getHost()` returns a hostname without `https://`. File writes accept UTF-8 text or valid UTF-8 bytes; `readBytes()` supports binary downloads.
288
85
 
289
- Lizard manages the sandbox runtime.
86
+ ## Learn more
290
87
 
291
- ## License
88
+ - [Sandbox reference](https://github.com/lizard-build/lizard-sdk/blob/main/docs/sandboxes.md)
89
+ - [Platform guide](https://github.com/lizard-build/lizard-sdk/blob/main/docs/platform.md)
90
+ - [CLI coverage and migration](https://github.com/lizard-build/lizard-sdk/blob/main/docs/cli-parity.md)
91
+ - [Source and issues](https://github.com/lizard-build/lizard-sdk)
292
92
 
293
- Apache-2.0
93
+ [Apache-2.0](https://github.com/lizard-build/lizard-sdk/blob/main/LICENSE)
package/dist/index.d.mts CHANGED
@@ -114,7 +114,7 @@ declare class AddonsAPI {
114
114
  memoryMb?: number;
115
115
  storageGb?: number;
116
116
  }): Promise<Addon>;
117
- /** Restart an addon VM. */
117
+ /** Restart an addon. */
118
118
  redeploy(projectId: string, addonId: string): Promise<void>;
119
119
  rename(projectId: string, addonId: string, name: string): Promise<unknown>;
120
120
  secrets(projectId: string, addonId: string): Promise<Array<{
@@ -395,7 +395,7 @@ interface ProcessInfo {
395
395
  /** Signals accepted by {@link Process.kill}. */
396
396
  type ProcessSignal = 'SIGTERM' | 'SIGKILL' | 'SIGINT' | 'SIGHUP' | 'SIGQUIT' | 'SIGUSR1' | 'SIGUSR2';
397
397
  /**
398
- * The result of a process execution inside a Lizard microVM.
398
+ * The result of a process execution inside a Lizard sandbox.
399
399
  */
400
400
  interface ProcessResult {
401
401
  stdout: string;
@@ -418,7 +418,7 @@ interface ProcessOpts {
418
418
  onPid?: (pid: number) => void;
419
419
  }
420
420
  /**
421
- * Runs processes inside a Lizard sandbox microVM.
421
+ * Runs processes inside a Lizard sandbox.
422
422
  *
423
423
  * Access via `sandbox.process`.
424
424
  */
@@ -427,12 +427,12 @@ declare class Process {
427
427
  private readonly config;
428
428
  constructor(sandboxId: string, config: ConnectionConfig);
429
429
  /**
430
- * Execute a command inside the microVM and wait for it to complete.
430
+ * Execute a command inside the sandbox and wait for it to complete.
431
431
  *
432
432
  * The command runs in a shell inside the Lizard sandbox and returns
433
433
  * stdout, stderr, and the exit code when it finishes.
434
434
  *
435
- * @param cmd Shell command to run inside the microVM.
435
+ * @param cmd Shell command to run inside the sandbox.
436
436
  * @param opts Optional execution options — environment variables, working
437
437
  * directory, user, and timeout.
438
438
  *
@@ -511,7 +511,7 @@ interface FileInfo {
511
511
  modTime?: number;
512
512
  }
513
513
  /**
514
- * Read and write files inside a Lizard sandbox microVM.
514
+ * Read and write files inside a Lizard sandbox.
515
515
  *
516
516
  * Access via `sandbox.fs`.
517
517
  */
@@ -520,23 +520,24 @@ declare class Fs {
520
520
  private readonly config;
521
521
  constructor(sandboxId: string, config: ConnectionConfig);
522
522
  /**
523
- * Write a file into the microVM filesystem.
523
+ * Write a file into the sandbox filesystem.
524
524
  *
525
525
  * Creates parent directories automatically if they don't exist.
526
+ * Byte input must contain valid UTF-8; arbitrary binary uploads are unsupported.
526
527
  *
527
528
  * @example
528
529
  * ```ts
529
530
  * await sandbox.fs.write('/app/index.js', 'console.log("hello")')
530
531
  * ```
531
532
  *
532
- * @example Write binary data:
533
+ * @example Write UTF-8 bytes:
533
534
  * ```ts
534
- * await sandbox.fs.write('/app/data.bin', buffer)
535
+ * await sandbox.fs.write('/app/notes.txt', new TextEncoder().encode('hello'))
535
536
  * ```
536
537
  */
537
538
  write(path: string, data: string | Uint8Array, opts?: FsOpts): Promise<void>;
538
539
  /**
539
- * Read a file from the microVM filesystem.
540
+ * Read a file from the sandbox filesystem.
540
541
  *
541
542
  * @returns The file contents as a UTF-8 string.
542
543
  *
@@ -546,22 +547,23 @@ declare class Fs {
546
547
  * ```
547
548
  */
548
549
  read(path: string, opts?: FsOpts): Promise<string>;
550
+ /** Read a file as bytes. */
551
+ readBytes(path: string, opts?: FsOpts): Promise<Uint8Array>;
549
552
  /**
550
- * List files and directories at the given path inside the microVM.
553
+ * List files and directories at the given path inside the sandbox.
551
554
  *
552
555
  * @example
553
556
  * ```ts
554
557
  * const entries = await sandbox.fs.list('/app')
555
558
  * ```
556
559
  */
557
- readBytes(path: string, opts?: FsOpts): Promise<Uint8Array>;
558
560
  list(path: string, opts?: FsOpts): Promise<FileInfo[]>;
559
561
  /**
560
- * Remove a file or directory from the microVM filesystem.
562
+ * Remove a file or directory from the sandbox filesystem.
561
563
  */
562
564
  remove(path: string, opts?: FsOpts): Promise<void>;
563
565
  /**
564
- * Create a directory (and any missing parents) inside the microVM.
566
+ * Create a directory (and any missing parents) inside the sandbox.
565
567
  */
566
568
  makeDir(path: string, opts?: FsOpts): Promise<void>;
567
569
  /**
@@ -730,10 +732,9 @@ declare class SandboxClient {
730
732
  }
731
733
 
732
734
  /**
733
- * A Lizard sandbox — an isolated Linux environment that starts in under a second.
735
+ * A Linux sandbox running on Kubernetes.
734
736
  *
735
- * Each sandbox is a full Linux environment with its own filesystem, network, and
736
- * process namespace, restored from a pre-warmed template snapshot.
737
+ * Run commands, read and write files, and expose HTTP ports in a sandbox.
737
738
  *
738
739
  * Sandboxes are **ephemeral**: killing one, or letting it hit its timeout, discards
739
740
  * everything written inside it. State that has to outlive a sandbox belongs on a
@@ -745,10 +746,14 @@ declare class SandboxClient {
745
746
  * import { Sandbox } from '@lizard-build/sdk'
746
747
  *
747
748
  * const sandbox = await Sandbox.create('base', { project: 'my-project' })
748
- * await sandbox.fs.write('/app/index.js', 'console.log("hello world")')
749
- * const result = await sandbox.process.exec('node /app/index.js')
750
- * console.log(result.stdout) // "hello world"
751
- * await sandbox.kill()
749
+ * try {
750
+ * await sandbox.fs.write('/tmp/hello.txt', 'hello world')
751
+ * const result = await sandbox.process.exec('cat /tmp/hello.txt')
752
+ * if (result.exitCode !== 0) throw new Error(result.stderr)
753
+ * console.log(result.stdout)
754
+ * } finally {
755
+ * await sandbox.kill()
756
+ * }
752
757
  * ```
753
758
  *
754
759
  * @example Carry work across sandboxes with a volume:
@@ -769,11 +774,11 @@ declare class Sandbox extends SandboxClient {
769
774
  protected static readonly defaultTemplate: string;
770
775
  protected static readonly defaultTimeoutMs: number;
771
776
  /**
772
- * Unique identifier of this sandbox microVM.
777
+ * Unique identifier of this sandbox.
773
778
  */
774
779
  readonly sandboxId: string;
775
780
  /**
776
- * Read and write files inside the microVM filesystem.
781
+ * Read and write files inside the sandbox filesystem.
777
782
  *
778
783
  * @example
779
784
  * ```ts
@@ -783,7 +788,7 @@ declare class Sandbox extends SandboxClient {
783
788
  */
784
789
  readonly fs: Fs;
785
790
  /**
786
- * Execute processes inside the microVM.
791
+ * Execute processes inside the sandbox.
787
792
  *
788
793
  * @example
789
794
  * ```ts
@@ -807,16 +812,15 @@ declare class Sandbox extends SandboxClient {
807
812
  /**
808
813
  * Create a new Lizard sandbox from the specified template.
809
814
  *
810
- * Available templates: `base` (Debian + Node.js 26) and `code-interpreter-v1`
811
- * (Python 3.14 + Node.js 26). Custom templates can be built and pushed via
812
- * `lizard push`.
815
+ * Template availability and installed tools depend on the platform and region.
816
+ * Use `base` for shell commands or `CodeSandbox` for stateful code execution.
813
817
  *
814
818
  * @param template Name of the sandbox template to boot from.
815
819
  *
816
820
  * @example
817
821
  * ```ts
818
822
  * const sandbox = await Sandbox.create('base', { project: 'my-project' })
819
- * const sandbox = await Sandbox.create('code-interpreter-v1', { project: 'my-project', timeoutMs: 10 * 60 * 1000 })
823
+ * const pythonSandbox = await Sandbox.create('code-interpreter-v1', { project: 'my-project', timeoutMs: 10 * 60 * 1000 })
820
824
  * ```
821
825
  */
822
826
  static create(template: string, opts?: SandboxOpts): Promise<Sandbox>;
@@ -826,11 +830,7 @@ declare class Sandbox extends SandboxClient {
826
830
  * Verifies the sandbox exists and is reachable, then returns a handle to it.
827
831
  * Throws `NotFoundError` if it has been killed or has expired.
828
832
  *
829
- * This used to call `resume` first, on the assumption that a sandbox you are
830
- * reconnecting to might be paused. Sandboxes are pods now and pause/resume is a
831
- * `501` on every one of them, so that call turned every `connect()` into an
832
- * error against a perfectly healthy sandbox. Connecting does not need to change
833
- * a sandbox's state, so it no longer tries to.
833
+ * Connecting reads sandbox metadata without changing its state.
834
834
  *
835
835
  * @example
836
836
  * ```ts
@@ -852,25 +852,21 @@ declare class Sandbox extends SandboxClient {
852
852
  /**
853
853
  * Kill the sandbox and release its resources immediately.
854
854
  *
855
- * @returns `true` if the microVM was terminated, `false` if it was already gone.
855
+ * @returns `true` if the sandbox was terminated, `false` if it was already gone.
856
856
  */
857
857
  kill(opts?: ConnectionOpts): Promise<boolean>;
858
858
  /**
859
- * Pause the sandbox by freezing it in place.
859
+ * Request sandbox pause. The Kubernetes backend returns HTTP 501.
860
860
  *
861
- * @deprecated Not implemented for the current runtime — always throws
862
- * `LizardError` with HTTP 501. Sandboxes run as pods, and the equivalent is a CRIU
863
- * checkpoint of the pod, which is not built. To park work across a gap, put it on a
864
- * {@link Volume} and create a fresh sandbox on that volume later; the volume is the
865
- * part that is meant to outlive a sandbox.
861
+ * @deprecated Unsupported on Kubernetes. Save files to a {@link Volume}
862
+ * and attach it to a new sandbox instead.
866
863
  */
867
864
  pause(opts?: ConnectionOpts): Promise<boolean>;
868
865
  /**
869
- * Resume a paused sandbox.
866
+ * Request sandbox resume. The Kubernetes backend returns HTTP 501.
870
867
  *
871
- * @deprecated Not implemented for the current runtime — always throws
872
- * `LizardError` with HTTP 501. See {@link pause}. `Sandbox.connect()` no longer
873
- * calls this, so reconnecting to a running sandbox works without it.
868
+ * @deprecated Unsupported on Kubernetes. Use {@link Sandbox.connect} to
869
+ * reconnect to a running sandbox.
874
870
  */
875
871
  resume(opts?: ConnectionOpts): Promise<boolean>;
876
872
  /**
@@ -884,23 +880,25 @@ declare class Sandbox extends SandboxClient {
884
880
  */
885
881
  setTimeout(timeoutMs: number, opts?: ConnectionOpts): Promise<void>;
886
882
  /**
887
- * Get the public HTTPS URL for a port exposed inside the sandbox.
883
+ * Register a public HTTPS route and return its hostname without a scheme.
888
884
  *
889
- * Useful for accessing HTTP servers started inside the microVM from your
885
+ * Useful for accessing HTTP servers started inside the sandbox from your
890
886
  * agent or tests without additional tunneling.
891
887
  *
892
888
  * @example
893
889
  * ```ts
894
- * await sandbox.process.exec('npx -y serve -p 3000 &')
895
- * const url = sandbox.getHost(3000)
896
- * // https://{sandboxId}-3000.sandbox.{region}.onlizard.com
890
+ * // Start an HTTP server on port 3000 before exposing it.
891
+ * const hostname = await sandbox.getHost(3000)
892
+ * console.log(`https://${hostname}`)
897
893
  * ```
898
894
  */
899
895
  getHost(port: number, opts?: ConnectionOpts): Promise<string>;
896
+ /** Fork is unsupported on Kubernetes; the backend returns HTTP 501. */
900
897
  fork(opts?: {
901
898
  count?: number;
902
899
  timeoutMs?: number;
903
900
  }): Promise<unknown>;
901
+ /** Snapshot creation is unsupported on Kubernetes; the backend returns HTTP 501. */
904
902
  snapshot(name?: string): Promise<unknown>;
905
903
  unexpose(port: number): Promise<void>;
906
904
  logs(opts?: {
@@ -908,6 +906,7 @@ declare class Sandbox extends SandboxClient {
908
906
  signal?: AbortSignal;
909
907
  }): AsyncGenerator<StreamEvent, any, any>;
910
908
  static snapshots(projectId: string, opts?: ConnectionOpts): Promise<unknown[]>;
909
+ /** Snapshot restore is unsupported on Kubernetes; the backend returns HTTP 501. */
911
910
  static restore(snapshotId: string, opts?: ConnectionOpts & {
912
911
  timeoutMs?: number;
913
912
  }): Promise<Sandbox>;
@@ -1602,7 +1601,7 @@ type OutputItem = {
1602
1601
  message: string;
1603
1602
  traceback: string;
1604
1603
  };
1605
- /** Error thrown when executed code raises an exception. */
1604
+ /** Error returned in Execution.error when executed code raises an exception. */
1606
1605
  declare class ExecutionError extends Error {
1607
1606
  readonly name: string;
1608
1607
  readonly traceback: string;
@@ -1637,7 +1636,7 @@ interface RunCodeOpts {
1637
1636
  context?: CodeContext;
1638
1637
  /** Extra environment variables available to the code. */
1639
1638
  envs?: Record<string, string>;
1640
- /** Max time to wait for the code to finish, in ms. Default: 60_000. */
1639
+ /** Client timeout in milliseconds. No client timer is set when omitted. */
1641
1640
  timeoutMs?: number;
1642
1641
  onStdout?: (data: string) => void;
1643
1642
  onStderr?: (data: string) => void;
@@ -1659,7 +1658,7 @@ interface CreateContextOpts {
1659
1658
  *
1660
1659
  * @example
1661
1660
  * ```ts
1662
- * import { CodeSandbox } from 'lizard/code-interpreter'
1661
+ * import { CodeSandbox } from '@lizard-build/sdk'
1663
1662
  *
1664
1663
  * const sandbox = await CodeSandbox.create({ project: 'my-project' })
1665
1664
  *
@@ -1690,8 +1689,7 @@ declare class CodeSandbox extends Sandbox {
1690
1689
  *
1691
1690
  * @returns Execution result with stdout, stderr, results, and any error.
1692
1691
  *
1693
- * @throws {ExecutionError} if you pass neither language nor context and no
1694
- * default context exists — which cannot happen in normal usage.
1692
+ * Code errors appear in `execution.error`; request failures throw.
1695
1693
  *
1696
1694
  * @example
1697
1695
  * ```ts