@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 +58 -258
- package/dist/index.d.mts +52 -54
- package/dist/index.d.ts +52 -54
- package/dist/index.js +36 -40
- package/dist/index.mjs +36 -40
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,293 +1,93 @@
|
|
|
1
|
-
# Lizard SDK
|
|
1
|
+
# Lizard TypeScript SDK
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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
|
-
|
|
76
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
first.kill() # sandbox gone, /workspace survives
|
|
23
|
+
```js
|
|
24
|
+
import { Lizard } from '@lizard-build/sdk'
|
|
110
25
|
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
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
|
-
|
|
41
|
+
## Stream command output
|
|
118
42
|
|
|
119
|
-
|
|
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
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
51
|
+
if (result.exitCode !== 0) throw new Error(result.stderr)
|
|
142
52
|
```
|
|
143
53
|
|
|
144
|
-
|
|
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
|
-
|
|
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
|
-
|
|
181
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
80
|
+
## Runtime limits
|
|
229
81
|
|
|
230
|
-
|
|
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
|
-
|
|
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
|
-
|
|
86
|
+
## Learn more
|
|
290
87
|
|
|
291
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
533
|
+
* @example Write UTF-8 bytes:
|
|
533
534
|
* ```ts
|
|
534
|
-
* await sandbox.fs.write('/app/
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
735
|
+
* A Linux sandbox running on Kubernetes.
|
|
734
736
|
*
|
|
735
|
-
*
|
|
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
|
-
*
|
|
749
|
-
*
|
|
750
|
-
*
|
|
751
|
-
*
|
|
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
|
|
777
|
+
* Unique identifier of this sandbox.
|
|
773
778
|
*/
|
|
774
779
|
readonly sandboxId: string;
|
|
775
780
|
/**
|
|
776
|
-
* Read and write files inside the
|
|
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
|
|
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
|
-
*
|
|
811
|
-
*
|
|
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
|
|
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
|
-
*
|
|
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
|
|
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
|
-
*
|
|
859
|
+
* Request sandbox pause. The Kubernetes backend returns HTTP 501.
|
|
860
860
|
*
|
|
861
|
-
* @deprecated
|
|
862
|
-
*
|
|
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
|
-
*
|
|
866
|
+
* Request sandbox resume. The Kubernetes backend returns HTTP 501.
|
|
870
867
|
*
|
|
871
|
-
* @deprecated
|
|
872
|
-
*
|
|
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
|
-
*
|
|
883
|
+
* Register a public HTTPS route and return its hostname without a scheme.
|
|
888
884
|
*
|
|
889
|
-
* Useful for accessing HTTP servers started inside the
|
|
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
|
-
*
|
|
895
|
-
* const
|
|
896
|
-
*
|
|
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
|
|
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
|
-
/**
|
|
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/
|
|
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
|
-
*
|
|
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
|