@lizard-build/sdk 0.1.77 → 0.1.79

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,6 +1,15 @@
1
- # Lizard SDK for TypeScript
1
+ # Lizard SDK for TypeScript and JavaScript
2
2
 
3
- Run commands, work with files and execute code in Linux sandboxes that run as Firecracker microVMs. The SDK also manages Lizard apps, databases, storage and projects.
3
+ Lizard (lizard.build) provides cloud sandboxes for AI agents and code execution. Lizard SDK is the official TypeScript and JavaScript client for creating and controlling Lizard Sandboxes.
4
+
5
+ Run AI-generated Python in a stateful code interpreter, execute shell commands, read and write files, or control a browser and desktop. Each sandbox runs as a Firecracker microVM with its own kernel.
6
+
7
+ ## Use cases
8
+
9
+ - Execute AI-generated code with a [stateful Python code interpreter](https://lizard.build/docs/sandboxes/code-interpreter/).
10
+ - Run coding agents such as [Claude Code](https://lizard.build/docs/sandboxes/guides/claude-code/) in a cloud sandbox.
11
+ - Automate a browser or desktop with [screenshots, clicks and keyboard input](https://github.com/lizard-build/lizard-sdk/blob/main/docs/sandboxes.md#desktop-computer-use).
12
+ - Open an agent's dev server through a [private preview URL](https://lizard.build/docs/sandboxes/preview-urls/).
4
13
 
5
14
  ## Install
6
15
 
@@ -10,7 +19,7 @@ Requires Node.js 18+.
10
19
  npm install @lizard-build/sdk
11
20
  ```
12
21
 
13
- Create an API key and project at [lizard.build](https://lizard.build), then set:
22
+ Start with Lizard Pro or its trial; see [billing and trial terms](https://lizard.build/docs/platform/billing/). Create an API key and project at [lizard.build](https://lizard.build), then set:
14
23
 
15
24
  ```sh
16
25
  export LIZARD_API_KEY="your-api-key"
@@ -38,17 +47,50 @@ try {
38
47
 
39
48
  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.
40
49
 
41
- ## Machine size
50
+ ## Run code with state (CodeSandbox)
51
+
52
+ `CodeSandbox` boots the `interpreter` template and runs Python in a persistent Jupyter kernel inside the sandbox: variables survive between calls, the value of the last expression and matplotlib charts come back in `results`, and exceptions in `error`.
53
+
54
+ ```ts
55
+ import { CodeSandbox } from '@lizard-build/sdk'
56
+
57
+ const sandbox = await CodeSandbox.create({ project: 'my-project', timeoutMs: 300_000 })
58
+ try {
59
+ const setup = await sandbox.runCode('x = 21')
60
+ if (setup.error) throw setup.error
61
+ const run = await sandbox.runCode('x * 2')
62
+ if (run.error) throw run.error
63
+ const out = run.results[0]
64
+ if (out?.type === 'result') console.log(out.data) // "42"
65
+ } finally {
66
+ await sandbox.kill()
67
+ }
68
+ ```
69
+
70
+ `language: 'bash'` and `language: 'javascript'` (the `interpreter` template ships Node 22) run each call as a fresh process with no shared state. See the [code interpreter guide](https://lizard.build/docs/sandboxes/code-interpreter/).
71
+
72
+ ## Account, pricing and machine size
73
+
74
+ Lizard Pro costs $19 a month, taxes included, with $19 of usage each month. Unused usage does not carry over. Usage above that amount is pay as you go at the published rates. New accounts can start a 7-day trial with $5 of usage; a card is required, and nothing is charged until the trial ends. If trial usage runs out, workloads pause until Pro starts. See the current [billing and trial terms](https://lizard.build/docs/platform/billing/).
75
+
76
+ Sandbox rates below were checked on October 23, 2026. See [current sizes and prices](https://lizard.build/docs/platform/limits/#sandbox-sizes) before estimating costs.
42
77
 
43
78
  Sandboxes come in three sizes: `small` (2 vCPU / 4 GB, $0.0162/h), `medium` (4 vCPU / 8 GB, $0.0324/h, the default) and `large` (8 vCPU / 16 GB, $0.0648/h). Billing is flat by size, per second while the sandbox runs, including idle time. CPU and RAM are included. The sandbox egress allowance is [2 TB per account over a rolling 30 days](https://lizard.build/docs/platform/limits/#sandbox-egress), with no separate per-GB charge. A successful pause stops compute charges; storage in Persistent Volumes is billed separately, including while paused.
44
79
 
45
80
  ```ts
46
81
  const sandbox = await lizard.create('base', { size: 'large' })
82
+ try {
83
+ const result = await sandbox.process.exec("python -c 'print(2 ** 10)'")
84
+ if (result.exitCode !== 0) throw new Error(result.stderr)
85
+ console.log(result.stdout)
86
+ } finally {
87
+ await sandbox.kill()
88
+ }
47
89
  ```
48
90
 
49
91
  ## Stream command output
50
92
 
51
- Inside the `try` block above:
93
+ Inside the `try` block in the first sandbox example:
52
94
 
53
95
  ```ts
54
96
  const result = await sandbox.process.exec('printf "hello\\n"', {
@@ -67,55 +109,40 @@ The `desktop` template runs an Openbox desktop with Chromium, streamed to a brow
67
109
  import { Sandbox } from '@lizard-build/sdk'
68
110
 
69
111
  const sandbox = await Sandbox.create('desktop', { project: 'my-project' })
70
- const { url, viewOnlyUrl } = await sandbox.desktop.start() // open `url` in a browser
71
- await sandbox.desktop.openUrl('https://example.com')
72
- await sandbox.desktop.click(640, 400)
73
- await sandbox.desktop.type('hello')
74
- await sandbox.desktop.press('Return')
75
- const png: Uint8Array = await sandbox.desktop.screenshot()
76
- ```
77
-
78
- Treat `url` like a credential: anyone with it can see and control the desktop. Share `viewOnlyUrl` when someone only needs to watch. See the [desktop guide](../../docs/sandboxes.md#desktop-computer-use).
79
-
80
- ## Run Python
81
-
82
- Run Python with the `interpreter` template. Each process command starts a separate Python process, so Python variables do not survive between calls; use `CodeSandbox` (below) when they should.
83
-
84
- ```ts
85
- import { Sandbox } from '@lizard-build/sdk';
86
-
87
- const sandbox = await Sandbox.create('interpreter', {
88
- projectId: 'proj_123', timeoutMs: 300_000,
89
- });
90
112
  try {
91
- const result = await sandbox.process.exec("python -c 'print(2 ** 10)'");
92
- console.log(result.stdout, result.stderr, result.exitCode);
113
+ const { url, viewOnlyUrl } = await sandbox.desktop.start() // open `url` in a browser
114
+ await sandbox.desktop.openUrl('https://example.com')
115
+ await sandbox.desktop.click(640, 400)
116
+ await sandbox.desktop.type('hello')
117
+ await sandbox.desktop.press('Return')
118
+ const png: Uint8Array = await sandbox.desktop.screenshot()
93
119
  } finally {
94
- await sandbox.kill();
120
+ await sandbox.kill()
95
121
  }
96
122
  ```
97
123
 
98
- To run code snippets with state carried between calls, use `CodeSandbox` (`runCode` / `run_code`): it boots the `interpreter` template and runs Python in a persistent Jupyter kernel inside the sandbox. See [Run code](https://github.com/lizard-build/lizard-sdk/blob/main/docs/sandboxes.md#run-code-codesandbox).
124
+ Treat `url` like a credential: anyone with it can see and control the desktop. Share `viewOnlyUrl` when someone only needs to watch. See the [desktop guide](https://github.com/lizard-build/lizard-sdk/blob/main/docs/sandboxes.md#desktop-computer-use).
99
125
 
100
- ## Run code with state (CodeSandbox)
126
+ ## Run Python
101
127
 
102
- `CodeSandbox` boots the `interpreter` template and runs Python in a persistent Jupyter kernel inside the sandbox: variables survive between calls, the value of the last expression and matplotlib charts come back in `results`, and exceptions in `error`.
128
+ Run Python with the `interpreter` template. Each process command starts a separate Python process, so Python variables do not survive between calls; use `CodeSandbox` when they should.
103
129
 
104
130
  ```ts
105
- import { CodeSandbox } from '@lizard-build/sdk';
131
+ import { Sandbox } from '@lizard-build/sdk'
106
132
 
107
- const sandbox = await CodeSandbox.create({ projectId: 'proj_123' });
133
+ const sandbox = await Sandbox.create('interpreter', {
134
+ project: 'my-project', timeoutMs: 300_000,
135
+ })
108
136
  try {
109
- await sandbox.runCode('x = 21');
110
- const run = await sandbox.runCode('x * 2');
111
- const out = run.results[0];
112
- if (out?.type === 'result') console.log(out.data); // "42"
137
+ const result = await sandbox.process.exec("python -c 'print(2 ** 10)'")
138
+ if (result.exitCode !== 0) throw new Error(result.stderr)
139
+ console.log(result.stdout, result.stderr, result.exitCode)
113
140
  } finally {
114
- await sandbox.kill();
141
+ await sandbox.kill()
115
142
  }
116
143
  ```
117
144
 
118
- `language: 'bash'` and `language: 'javascript'` (the `interpreter` template ships Node 22) run each call as a fresh process with no shared state.
145
+ To keep state between calls, use `CodeSandbox.runCode()`. See the [code execution reference](https://github.com/lizard-build/lizard-sdk/blob/main/docs/sandboxes.md#run-code-codesandbox).
119
146
 
120
147
  ## Configuration
121
148
 
@@ -127,18 +154,25 @@ Pass `timeoutMs` to `create()` to set the sandbox lifetime. The default is five
127
154
 
128
155
  ## Runtime limits
129
156
 
130
- Lizard Sandboxes run as Firecracker microVMs, each 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 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).
157
+ Lizard Sandboxes run as Firecracker microVMs, each with its own kernel. Use Persistent 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 yet. Volumes can grow but not shrink, and a sandbox with a volume attached cannot be forked. See the [snapshot guide](https://github.com/lizard-build/lizard-sdk/blob/main/docs/sandboxes.md#pause-resume-snapshots-and-fork).
131
158
 
132
159
  `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.
133
160
 
134
161
  ## Current command and lifetime limits
135
162
 
136
- Command results contain stdout, stderr, and the exit code. Command timeouts are limited to 1–600 seconds. The command options `envs`, `workdir` and `user` apply to that command; create-time `envs` apply to every command, and create-time `metadata` is returned by `getInfo()` / `get_info()`.
163
+ Command results contain stdout, stderr, and the exit code. Command timeouts are limited to 1–600 seconds. The command options `envs`, `workdir` and `user` apply to that command; create-time `envs` apply to every command, and create-time `metadata` is returned by `getInfo()`.
164
+
165
+ The SDK and CLI default to a five-minute lifetime. The raw API and dashboard default to no expiration. Set an explicit lifetime: `timeoutMs: 0` at creation disables expiration; a positive value sets a deadline. `setTimeout()` accepts 1000–2147483647 ms, not zero. Commands do not reset the deadline. This is not an idle timer.
166
+
167
+ ## Other SDK capabilities
137
168
 
138
- The SDK and CLI default to a five-minute lifetime. The raw API and dashboard default to no expiration. Set an explicit lifetime: `timeoutMs: 0` at creation disables expiration; a positive value sets a deadline. `setTimeout` accepts 1000–2147483647 ms, not zero. Commands do not reset the deadline. This is not an idle timer.
169
+ The SDK also manages apps, Managed Postgres, Managed Redis, Managed Object Storage and projects. See the [platform guide](https://github.com/lizard-build/lizard-sdk/blob/main/docs/platform.md).
139
170
 
140
171
  ## Learn more
141
172
 
173
+ - [Sandboxes overview and current limits](https://lizard.build/docs/sandboxes/)
174
+ - [Sandboxes quickstart](https://lizard.build/docs/sandboxes/quickstart/)
175
+ - [Billing and trial terms](https://lizard.build/docs/platform/billing/)
142
176
  - [Sandbox reference](https://github.com/lizard-build/lizard-sdk/blob/main/docs/sandboxes.md)
143
177
  - [Platform guide](https://github.com/lizard-build/lizard-sdk/blob/main/docs/platform.md)
144
178
  - [CLI coverage and migration](https://github.com/lizard-build/lizard-sdk/blob/main/docs/cli-parity.md)
package/dist/index.d.mts CHANGED
@@ -895,13 +895,11 @@ declare class GitReposAPI {
895
895
  name: string;
896
896
  }>;
897
897
  get(repo: RepoRef): Promise<RepoMeta>;
898
- /** `apiCommits`: also accept commits written through the API (`commitFiles`, `land`); new repositories take pushes only. */
899
898
  update(repo: RepoRef, opts: {
900
899
  name?: string;
901
900
  description?: string;
902
901
  private?: boolean;
903
902
  defaultBranch?: string;
904
- apiCommits?: boolean;
905
903
  }): Promise<{
906
904
  ok: boolean;
907
905
  }>;
@@ -952,9 +950,10 @@ declare class GitReposAPI {
952
950
  }>;
953
951
  compare(repo: RepoRef, base: string, head: string): Promise<unknown>;
954
952
  /**
955
- * Commit files on the server: no clone, no push. Only on a repository whose admin allowed
956
- * it (`update(repo, { apiCommits: true })`); new repositories take changes by git push only
957
- * (403 otherwise). Answers 409 when `expect` no longer matches.
953
+ * Commit files on the server: no clone, no push (every commit to Lizard Git is made this
954
+ * way). With `expect` (the commit your change was made against), a branch that moved since
955
+ * is the server's to resolve: other files go on the new tip, the same files are merged line
956
+ * by line, and only a real conflict answers 409 with the paths.
958
957
  */
959
958
  commitFiles(repo: RepoRef, opts: CommitFilesOpts): Promise<{
960
959
  sha: string;
@@ -974,9 +973,9 @@ declare class GitReposAPI {
974
973
  reflog(repo: RepoRef, ref?: string): Promise<unknown[]>;
975
974
  archive(repo: RepoRef, ref: string, format?: 'zip' | 'tar.gz'): Promise<Uint8Array>;
976
975
  /**
977
- * The HTTPS clone URL. With `withKey`, it carries the API key, so plain `git clone` works
978
- * without a helper; git then keeps it in .git/config. For anything shared or logged, use a
979
- * limited Lizard Git token (`tokens.create`) as `apiKey` instead of the account's key.
976
+ * @deprecated Lizard Git no longer serves the git protocol (contents are kept as changesets,
977
+ * files by hash): this address answers 410. Read with `tree`/`file`/`raw`/`archive`, write
978
+ * with `commitFiles`, and check out with the Lizard CLI (`lizard git clone`).
980
979
  */
981
980
  cloneUrl(repo: RepoRef, opts?: {
982
981
  withKey?: boolean;
package/dist/index.d.ts CHANGED
@@ -895,13 +895,11 @@ declare class GitReposAPI {
895
895
  name: string;
896
896
  }>;
897
897
  get(repo: RepoRef): Promise<RepoMeta>;
898
- /** `apiCommits`: also accept commits written through the API (`commitFiles`, `land`); new repositories take pushes only. */
899
898
  update(repo: RepoRef, opts: {
900
899
  name?: string;
901
900
  description?: string;
902
901
  private?: boolean;
903
902
  defaultBranch?: string;
904
- apiCommits?: boolean;
905
903
  }): Promise<{
906
904
  ok: boolean;
907
905
  }>;
@@ -952,9 +950,10 @@ declare class GitReposAPI {
952
950
  }>;
953
951
  compare(repo: RepoRef, base: string, head: string): Promise<unknown>;
954
952
  /**
955
- * Commit files on the server: no clone, no push. Only on a repository whose admin allowed
956
- * it (`update(repo, { apiCommits: true })`); new repositories take changes by git push only
957
- * (403 otherwise). Answers 409 when `expect` no longer matches.
953
+ * Commit files on the server: no clone, no push (every commit to Lizard Git is made this
954
+ * way). With `expect` (the commit your change was made against), a branch that moved since
955
+ * is the server's to resolve: other files go on the new tip, the same files are merged line
956
+ * by line, and only a real conflict answers 409 with the paths.
958
957
  */
959
958
  commitFiles(repo: RepoRef, opts: CommitFilesOpts): Promise<{
960
959
  sha: string;
@@ -974,9 +973,9 @@ declare class GitReposAPI {
974
973
  reflog(repo: RepoRef, ref?: string): Promise<unknown[]>;
975
974
  archive(repo: RepoRef, ref: string, format?: 'zip' | 'tar.gz'): Promise<Uint8Array>;
976
975
  /**
977
- * The HTTPS clone URL. With `withKey`, it carries the API key, so plain `git clone` works
978
- * without a helper; git then keeps it in .git/config. For anything shared or logged, use a
979
- * limited Lizard Git token (`tokens.create`) as `apiKey` instead of the account's key.
976
+ * @deprecated Lizard Git no longer serves the git protocol (contents are kept as changesets,
977
+ * files by hash): this address answers 410. Read with `tree`/`file`/`raw`/`archive`, write
978
+ * with `commitFiles`, and check out with the Lizard CLI (`lizard git clone`).
980
979
  */
981
980
  cloneUrl(repo: RepoRef, opts?: {
982
981
  withKey?: boolean;
package/dist/index.js CHANGED
@@ -632,7 +632,6 @@ var GitReposAPI = class {
632
632
  get(repo) {
633
633
  return this.c.get(repoPath(repo));
634
634
  }
635
- /** `apiCommits`: also accept commits written through the API (`commitFiles`, `land`); new repositories take pushes only. */
636
635
  update(repo, opts) {
637
636
  return this.c.patch(repoPath(repo), opts);
638
637
  }
@@ -692,9 +691,10 @@ var GitReposAPI = class {
692
691
  return this.c.get(qs(`${repoPath(repo)}/compare`, { base, head }));
693
692
  }
694
693
  /**
695
- * Commit files on the server: no clone, no push. Only on a repository whose admin allowed
696
- * it (`update(repo, { apiCommits: true })`); new repositories take changes by git push only
697
- * (403 otherwise). Answers 409 when `expect` no longer matches.
694
+ * Commit files on the server: no clone, no push (every commit to Lizard Git is made this
695
+ * way). With `expect` (the commit your change was made against), a branch that moved since
696
+ * is the server's to resolve: other files go on the new tip, the same files are merged line
697
+ * by line, and only a real conflict answers 409 with the paths.
698
698
  */
699
699
  commitFiles(repo, opts) {
700
700
  const { idempotencyKey, ...body } = opts;
@@ -712,9 +712,9 @@ var GitReposAPI = class {
712
712
  return this.c.bytes(`${repoPath(repo)}/archive/${seg(ref)}.${format}`);
713
713
  }
714
714
  /**
715
- * The HTTPS clone URL. With `withKey`, it carries the API key, so plain `git clone` works
716
- * without a helper; git then keeps it in .git/config. For anything shared or logged, use a
717
- * limited Lizard Git token (`tokens.create`) as `apiKey` instead of the account's key.
715
+ * @deprecated Lizard Git no longer serves the git protocol (contents are kept as changesets,
716
+ * files by hash): this address answers 410. Read with `tree`/`file`/`raw`/`archive`, write
717
+ * with `commitFiles`, and check out with the Lizard CLI (`lizard git clone`).
718
718
  */
719
719
  cloneUrl(repo, opts = {}) {
720
720
  repoPath(repo);
package/dist/index.mjs CHANGED
@@ -560,7 +560,6 @@ var GitReposAPI = class {
560
560
  get(repo) {
561
561
  return this.c.get(repoPath(repo));
562
562
  }
563
- /** `apiCommits`: also accept commits written through the API (`commitFiles`, `land`); new repositories take pushes only. */
564
563
  update(repo, opts) {
565
564
  return this.c.patch(repoPath(repo), opts);
566
565
  }
@@ -620,9 +619,10 @@ var GitReposAPI = class {
620
619
  return this.c.get(qs(`${repoPath(repo)}/compare`, { base, head }));
621
620
  }
622
621
  /**
623
- * Commit files on the server: no clone, no push. Only on a repository whose admin allowed
624
- * it (`update(repo, { apiCommits: true })`); new repositories take changes by git push only
625
- * (403 otherwise). Answers 409 when `expect` no longer matches.
622
+ * Commit files on the server: no clone, no push (every commit to Lizard Git is made this
623
+ * way). With `expect` (the commit your change was made against), a branch that moved since
624
+ * is the server's to resolve: other files go on the new tip, the same files are merged line
625
+ * by line, and only a real conflict answers 409 with the paths.
626
626
  */
627
627
  commitFiles(repo, opts) {
628
628
  const { idempotencyKey, ...body } = opts;
@@ -640,9 +640,9 @@ var GitReposAPI = class {
640
640
  return this.c.bytes(`${repoPath(repo)}/archive/${seg(ref)}.${format}`);
641
641
  }
642
642
  /**
643
- * The HTTPS clone URL. With `withKey`, it carries the API key, so plain `git clone` works
644
- * without a helper; git then keeps it in .git/config. For anything shared or logged, use a
645
- * limited Lizard Git token (`tokens.create`) as `apiKey` instead of the account's key.
643
+ * @deprecated Lizard Git no longer serves the git protocol (contents are kept as changesets,
644
+ * files by hash): this address answers 410. Read with `tree`/`file`/`raw`/`archive`, write
645
+ * with `commitFiles`, and check out with the Lizard CLI (`lizard git clone`).
646
646
  */
647
647
  cloneUrl(repo, opts = {}) {
648
648
  repoPath(repo);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lizard-build/sdk",
3
- "version": "0.1.77",
4
- "description": "TypeScript SDK for Lizard: Firecracker microVM sandboxes, code execution and cloud apps.",
3
+ "version": "0.1.79",
4
+ "description": "TypeScript and JavaScript SDK for Lizard (lizard.build): cloud sandboxes for AI agents and code execution.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.ts",
@@ -41,6 +41,9 @@
41
41
  "directory": "packages/js"
42
42
  },
43
43
  "homepage": "https://lizard.build",
44
+ "bugs": {
45
+ "url": "https://github.com/lizard-build/lizard-sdk/issues"
46
+ },
44
47
  "devDependencies": {
45
48
  "@types/node": "^20.0.0",
46
49
  "tsup": "^8.0.0",