@lizard-build/sdk 0.1.48 → 0.1.51

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,6 @@
1
1
  # Lizard SDK for TypeScript
2
2
 
3
- Run commands, work with files and execute code in Linux sandboxes on Kubernetes. The SDK also manages Lizard apps, databases, storage and projects.
3
+ Run commands, work with files and execute code in Linux sandboxes (Firecracker microVMs). The SDK also manages Lizard apps, databases, storage and projects.
4
4
 
5
5
  ## Install
6
6
 
@@ -79,7 +79,7 @@ Treat `url` like a credential: anyone with it can see and control the desktop. S
79
79
 
80
80
  ## Run Python
81
81
 
82
- Run Python with the `interpreter` template. Each process command starts a separate Python process, so Python variables do not survive between calls. Save intermediate results to files. The API returns stdout, stderr, and an exit code; it does not return typed notebook results or chart objects.
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
83
 
84
84
  ```ts
85
85
  import { Sandbox } from '@lizard-build/sdk';
@@ -95,7 +95,26 @@ try {
95
95
  }
96
96
  ```
97
97
 
98
- `CodeSandbox`, `runCode` / `run_code`, and execution-context methods remain in the SDK, but the hosted template catalog does not provide their required execution server. The legacy default `code-interpreter-v1` is unavailable. Changing its name to `interpreter` does not enable this API. Use `Sandbox.create("interpreter")` and process commands as shown below.
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).
99
+
100
+ ## Run code with state (CodeSandbox)
101
+
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`.
103
+
104
+ ```ts
105
+ import { CodeSandbox } from '@lizard-build/sdk';
106
+
107
+ const sandbox = await CodeSandbox.create({ projectId: 'proj_123' });
108
+ try {
109
+ await sandbox.runCode('x = 21');
110
+ const run = await sandbox.runCode('x * 2');
111
+ console.log(run.results[0].data); // "42"
112
+ } finally {
113
+ await sandbox.kill();
114
+ }
115
+ ```
116
+
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.
99
118
 
100
119
  ## Configuration
101
120
 
@@ -105,13 +124,13 @@ Pass `timeoutMs` to `create()` to set the sandbox lifetime. The default is five
105
124
 
106
125
  ## Runtime limits
107
126
 
108
- Sandboxes run on Kubernetes. Use volumes mounted at `/workspace` to keep files after a sandbox ends. CRIU pause/resume and private snapshots preserve running memory and workspace files. Snapshot pools keep five copies warm by default and can be paused to release idle compute. Fork and file watching remain unsupported. See the [snapshot lifecycle guide](../../docs/sandboxes.md#snapshots-and-criu-pause-resume).
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).
109
128
 
110
- `getHost()` returns a hostname without `https://`. File writes accept UTF-8 text or valid UTF-8 bytes; `readBytes()` supports binary downloads.
129
+ `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.
111
130
 
112
131
  ## Current command and lifetime limits
113
132
 
114
- Command results contain stdout, stderr, and the exit code. Command timeouts are limited to 1–600 seconds. The current runtime does not apply the SDK command options `envs`, `workdir`, or `user`; set the directory and environment in the shell command when needed. Create-time `envs` and `metadata` are also not applied.
133
+ 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()`.
115
134
 
116
135
  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.
117
136
 
package/dist/index.d.mts CHANGED
@@ -385,12 +385,19 @@ declare class GitHubAPI {
385
385
  checkout(serviceId: string, branch: string): Promise<DeployHandle>;
386
386
  }
387
387
 
388
- /** A process this sandbox is currently running. */
388
+ /** A process this sandbox is currently running (one started through `exec`). */
389
389
  interface ProcessInfo {
390
390
  pid: number;
391
+ /** The argv: `['/bin/sh', '-c', 'npm test']` for a shell command. */
391
392
  cmd: string[];
392
- /** When the process started, unix milliseconds. */
393
- startedAt: number;
393
+ /** `cmd` joined with spaces, for display. */
394
+ command?: string;
395
+ /** Working directory the process was started in, when known. */
396
+ cwd?: string | null;
397
+ /** The process's tag, when it was started with one. */
398
+ tag?: string | null;
399
+ /** When the process started, unix milliseconds. Not reported by every sandbox. */
400
+ startedAt?: number;
394
401
  }
395
402
  /** Signals accepted by {@link Process.kill}. */
396
403
  type ProcessSignal = 'SIGTERM' | 'SIGKILL' | 'SIGINT' | 'SIGHUP' | 'SIGQUIT' | 'SIGUSR1' | 'SIGUSR2';
@@ -460,11 +467,6 @@ declare class Process {
460
467
  * ```
461
468
  */
462
469
  exec(cmd: string | string[], opts?: ProcessOpts): Promise<ProcessResult>;
463
- /**
464
- * Read an SSE exec stream, handing each line to the caller as it arrives while
465
- * still accumulating the full result. Events are `{stream, line}` for output and
466
- * `{exitCode}` at the end.
467
- */
468
470
  /**
469
471
  * List the processes this sandbox is currently running.
470
472
  *
@@ -482,8 +484,8 @@ declare class Process {
482
484
  /**
483
485
  * Signal a running process. Defaults to `SIGTERM`.
484
486
  *
485
- * The signal goes to the process group, so a shell's children die with it —
486
- * killing `sh -c 'sleep 100'` otherwise leaves the sleep running.
487
+ * The signal goes to the whole process tree, so a shell's children die with
488
+ * it — killing `sh -c 'sleep 100'` otherwise leaves the sleep running.
487
489
  *
488
490
  * The pid comes from {@link list}, or from the `pid` event at the start of a
489
491
  * streaming `exec`.
@@ -495,6 +497,11 @@ declare class Process {
495
497
  * ```
496
498
  */
497
499
  kill(pid: number, signal?: ProcessSignal): Promise<void>;
500
+ /**
501
+ * Read an SSE exec stream, handing each line to the caller as it arrives while
502
+ * still accumulating the full result. Events are `{pid}` first (when asked for
503
+ * with `pidEvent`), `{stream, line}` for output and `{exitCode}` at the end.
504
+ */
498
505
  private consumeStream;
499
506
  }
500
507
 
@@ -514,10 +521,20 @@ interface FileInfo {
514
521
  path: string;
515
522
  type: 'file' | 'dir' | 'symlink';
516
523
  size: number;
517
- /** Permission bits as a string, e.g. `-rw-r--r--`. Absent on older sandboxes. */
518
- mode?: string;
519
- /** Last modification time, unix milliseconds. Absent on older sandboxes. */
524
+ /** Permission bits as a number, e.g. `0o644` (420). */
525
+ mode?: number;
526
+ /** Permission bits as a string, e.g. `-rw-r--r--`. */
527
+ permissions?: string;
528
+ /** Owning user name. */
529
+ owner?: string;
530
+ /** Owning group name. */
531
+ group?: string;
532
+ /** Last modification time, unix milliseconds. */
520
533
  modTime?: number;
534
+ /** Same as `modTime`, unix milliseconds. */
535
+ modifiedAt?: number;
536
+ /** Where a symlink points, for a symlink. */
537
+ symlinkTarget?: string;
521
538
  }
522
539
  /**
523
540
  * Read and write files inside a Lizard sandbox.
@@ -531,25 +548,29 @@ declare class Fs {
531
548
  /**
532
549
  * Write a file into the sandbox filesystem.
533
550
  *
534
- * Creates parent directories automatically if they don't exist.
535
- * Byte input must contain valid UTF-8; arbitrary binary uploads are unsupported.
551
+ * Creates parent directories automatically if they don't exist. A string is
552
+ * written as UTF-8; bytes (`Uint8Array`, `Buffer` or `ArrayBuffer`) are written
553
+ * exactly as given, so binary files round-trip with {@link readBytes}.
554
+ *
555
+ * With `opts.user` the file is written as (and owned by) that user.
536
556
  *
537
557
  * @example
538
558
  * ```ts
539
559
  * await sandbox.fs.write('/app/index.js', 'console.log("hello")')
540
560
  * ```
541
561
  *
542
- * @example Write UTF-8 bytes:
562
+ * @example Write binary data:
543
563
  * ```ts
544
- * await sandbox.fs.write('/app/notes.txt', new TextEncoder().encode('hello'))
564
+ * await sandbox.fs.write('/app/logo.png', await readFile('logo.png'))
545
565
  * ```
546
566
  */
547
- write(path: string, data: string | Uint8Array, opts?: FsOpts): Promise<void>;
567
+ write(path: string, data: string | Uint8Array | ArrayBuffer, opts?: FsOpts): Promise<void>;
548
568
  /**
549
569
  * Read a file from the sandbox filesystem.
550
570
  *
551
- * @returns The file contents as a UTF-8 string.
571
+ * @returns The file contents as a UTF-8 string. Use {@link readBytes} for binary files.
552
572
  *
573
+ * Throws `NotFoundError` if the path does not exist.
553
574
  * @example
554
575
  * ```ts
555
576
  * const content = await sandbox.fs.read('/app/index.js')
@@ -587,9 +608,7 @@ declare class Fs {
587
608
  * console.log(info.size, info.type)
588
609
  * ```
589
610
  *
590
- * Throws `NotFoundError` if the path does not exist. Sandboxes created before
591
- * this shipped run a guest agent without it and throw `LizardError` (501) —
592
- * recreate the sandbox to use it.
611
+ * Throws `NotFoundError` if the path does not exist.
593
612
  */
594
613
  stat(path: string, opts?: FsOpts): Promise<FileInfo>;
595
614
  /**
@@ -599,9 +618,6 @@ declare class Fs {
599
618
  * ```ts
600
619
  * await sandbox.fs.move('/tmp/build.log', '/app/logs/build.log')
601
620
  * ```
602
- *
603
- * Sandboxes created before this shipped run a guest agent without it and throw
604
- * `LizardError` (501) — recreate the sandbox to use it.
605
621
  */
606
622
  move(from: string, to: string): Promise<void>;
607
623
  /**
@@ -623,8 +639,7 @@ declare class Fs {
623
639
  * }, 1000)
624
640
  * ```
625
641
  *
626
- * Sandboxes created before this shipped run a guest agent without it and throw
627
- * `LizardError` (501) — recreate the sandbox to use it.
642
+ * Not available on Firecracker sandboxes yet: the call throws `LizardError` (501).
628
643
  */
629
644
  watch(path: string, opts?: {
630
645
  recursive?: boolean;
@@ -710,7 +725,8 @@ declare class Desktop {
710
725
  private readonly sandboxId;
711
726
  private readonly config;
712
727
  private readonly process;
713
- constructor(sandboxId: string, config: ConnectionConfig, process: Process);
728
+ private readonly fs;
729
+ constructor(sandboxId: string, config: ConnectionConfig, process: Process, fs: Fs);
714
730
  private get client();
715
731
  private get path();
716
732
  /**
@@ -772,6 +788,8 @@ interface SandboxInfo {
772
788
  /** Region the sandbox runs in — the volume's region when one is attached. */
773
789
  region?: string;
774
790
  status?: string;
791
+ /** The sandbox runtime: `'firecracker'` (a microVM), `'container'`, or `null` when unknown. */
792
+ runtime?: 'firecracker' | 'container' | null;
775
793
  pauseError?: string | null;
776
794
  /** Machine size (`'small' | 'medium' | 'large'`); may be `null` for a sandbox created from a private snapshot. */
777
795
  size?: 'small' | 'medium' | 'large' | null;
@@ -781,6 +799,19 @@ interface SandboxInfo {
781
799
  memoryMb?: number;
782
800
  metadata?: Record<string, string>;
783
801
  }
802
+ /** A sandbox port published on a public HTTPS hostname. See {@link Sandbox.exposePort}. */
803
+ interface ExposedPort {
804
+ /** Hostname without a scheme, e.g. `abc-3000.sandbox.eu-west-lim-a.onlizard.com`. */
805
+ hostname: string;
806
+ /** A browser URL that carries the access token (`?lizard_token=...`). */
807
+ url: string;
808
+ port: number;
809
+ /**
810
+ * The port is private: every request needs this token, either as the
811
+ * `X-Lizard-Access-Token` header or once as `?lizard_token=` (a browser then gets a cookie).
812
+ */
813
+ accessToken: string;
814
+ }
784
815
  interface SandboxOpts extends ConnectionOpts {
785
816
  /**
786
817
  * The project this sandbox belongs to — its ID, slug, or name. Required:
@@ -807,7 +838,9 @@ interface SandboxOpts extends ConnectionOpts {
807
838
  * Measured CPU/RAM are not charged and egress is free. Ignored with {@link SandboxOpts.snapshotId}.
808
839
  */
809
840
  size?: 'small' | 'medium' | 'large';
841
+ /** Your own key/value labels for the sandbox, returned by `getInfo()` and `list()`. */
810
842
  metadata?: Record<string, string>;
843
+ /** Environment variables set in the sandbox, visible to every command it runs. */
811
844
  envs?: Record<string, string>;
812
845
  timeoutMs?: number;
813
846
  /**
@@ -867,10 +900,7 @@ declare class SandboxClient {
867
900
  }): Promise<SandboxInfo[]>;
868
901
  protected static getSandboxInfo(sandboxId: string, opts?: ConnectionOpts): Promise<SandboxInfo>;
869
902
  protected static setTimeoutSandbox(sandboxId: string, timeoutMs: number, opts?: ConnectionOpts): Promise<void>;
870
- protected static exposeSandboxPort(sandboxId: string, port: number, opts?: ConnectionOpts): Promise<{
871
- hostname: string;
872
- url: string;
873
- }>;
903
+ protected static exposeSandboxPort(sandboxId: string, port: number, opts?: ConnectionOpts): Promise<ExposedPort>;
874
904
  }
875
905
 
876
906
  interface SandboxSnapshot {
@@ -886,18 +916,37 @@ interface SandboxSnapshot {
886
916
  error: string | null;
887
917
  cpus: number;
888
918
  memoryMb: number;
919
+ /** Unix milliseconds. */
889
920
  createdAt: number;
890
921
  }
891
922
  interface SnapshotOpts {
923
+ /** Warm copies to keep ready (container sandboxes only; ignored for Firecracker, whose snapshots restore from disk). */
892
924
  poolSize?: number;
893
925
  }
926
+ /**
927
+ * One entry of {@link Sandbox.fork}'s result: the new sandbox, or why that copy
928
+ * could not be made (e.g. the account's sandbox limit).
929
+ */
930
+ type ForkResult<S extends Sandbox = Sandbox> = {
931
+ sandbox: S;
932
+ info: SandboxInfo;
933
+ error?: undefined;
934
+ } | {
935
+ sandbox?: undefined;
936
+ info?: undefined;
937
+ error: string;
938
+ };
894
939
  interface SnapshotWaitOpts extends ConnectionOpts {
895
940
  waitTimeoutMs?: number;
896
941
  }
897
942
  /**
898
- * A Linux sandbox running on Kubernetes.
943
+ * A Linux sandbox: a Firecracker microVM with its own kernel.
899
944
  *
900
- * Run commands, read and write files, and expose HTTP ports in a sandbox.
945
+ * Run commands, read and write files, and expose HTTP ports in a sandbox. A
946
+ * sandbox boots in well under a second; {@link pause} and {@link resume} keep its
947
+ * memory and running processes, {@link snapshot} saves it in about 2 s and
948
+ * {@link restore} starts a copy in about 0.4 s, and {@link fork} clones a running
949
+ * sandbox, processes and all.
901
950
  *
902
951
  * Sandboxes are **ephemeral**: killing one, or letting it hit its timeout, discards
903
952
  * everything written inside it. State that has to outlive a sandbox belongs on a
@@ -989,8 +1038,9 @@ declare class Sandbox extends SandboxClient {
989
1038
  * Create a new Lizard sandbox from the specified template.
990
1039
  *
991
1040
  * Template availability and installed tools depend on the platform and region.
992
- * Use `base` for shell commands or `interpreter` for Python process commands.
993
- * Hosted templates do not support the legacy `CodeSandbox` execution API.
1041
+ * Use `base` for shell commands, `interpreter` for Python with the data stack
1042
+ * (use {@link CodeSandbox} to run code snippets in it), or `desktop` for a
1043
+ * graphical desktop.
994
1044
  *
995
1045
  * @param template Name of the sandbox template to boot from.
996
1046
  *
@@ -1032,9 +1082,15 @@ declare class Sandbox extends SandboxClient {
1032
1082
  * @returns `true` if the sandbox was terminated, `false` if it was already gone.
1033
1083
  */
1034
1084
  kill(opts?: ConnectionOpts): Promise<boolean>;
1035
- /** Queue a CRIU checkpoint and stop compute. Use waitForStatus('paused') before resuming. */
1085
+ /**
1086
+ * Pause the sandbox: its memory, running processes and files are kept and
1087
+ * compute stops. Wait with `waitForStatus('paused')` before resuming.
1088
+ */
1036
1089
  pause(opts?: ConnectionOpts): Promise<boolean>;
1037
- /** Queue restoration of saved memory and files. Use waitForStatus('running') before executing. */
1090
+ /**
1091
+ * Resume a paused sandbox exactly where it stopped, processes included. Wait
1092
+ * with `waitForStatus('running')` before running commands.
1093
+ */
1038
1094
  resume(opts?: ConnectionOpts): Promise<boolean>;
1039
1095
  /** Wait for an asynchronous pause/resume operation to finish. */
1040
1096
  waitForStatus(status: 'paused' | 'running', opts?: SnapshotWaitOpts): Promise<SandboxInfo>;
@@ -1049,39 +1105,81 @@ declare class Sandbox extends SandboxClient {
1049
1105
  */
1050
1106
  setTimeout(timeoutMs: number, opts?: ConnectionOpts): Promise<void>;
1051
1107
  /**
1052
- * Register a public HTTPS route and return its hostname without a scheme.
1108
+ * The token that opens this sandbox's published ports. Set by {@link getHost}
1109
+ * and {@link exposePort}; send it as the `X-Lizard-Access-Token` header.
1110
+ */
1111
+ accessToken?: string;
1112
+ /**
1113
+ * Publish a port on a public HTTPS hostname and return the hostname without a scheme.
1053
1114
  *
1054
- * Useful for accessing HTTP servers started inside the sandbox from your
1055
- * agent or tests without additional tunneling.
1115
+ * The port is private: requests need {@link accessToken} (set by this call) as
1116
+ * the `X-Lizard-Access-Token` header, or `?lizard_token=<token>` in a browser.
1117
+ * {@link exposePort} returns the token and a ready-made browser URL together.
1056
1118
  *
1057
1119
  * @example
1058
1120
  * ```ts
1059
1121
  * // Start an HTTP server on port 3000 before exposing it.
1060
1122
  * const hostname = await sandbox.getHost(3000)
1061
- * console.log(`https://${hostname}`)
1123
+ * const res = await fetch(`https://${hostname}/`, {
1124
+ * headers: { 'X-Lizard-Access-Token': sandbox.accessToken! },
1125
+ * })
1062
1126
  * ```
1063
1127
  */
1064
1128
  getHost(port: number, opts?: ConnectionOpts): Promise<string>;
1065
- /** Fork is unsupported on Kubernetes; the backend returns HTTP 501. */
1129
+ /**
1130
+ * Publish a port and return everything needed to reach it: the hostname, a
1131
+ * browser `url` that carries the token, and the `accessToken` for API clients.
1132
+ *
1133
+ * @example
1134
+ * ```ts
1135
+ * const { url, hostname, accessToken } = await sandbox.exposePort(3000)
1136
+ * console.log(url) // open in a browser
1137
+ * await fetch(`https://${hostname}/api`, { headers: { 'X-Lizard-Access-Token': accessToken } })
1138
+ * ```
1139
+ */
1140
+ exposePort(port: number, opts?: ConnectionOpts): Promise<ExposedPort>;
1141
+ /**
1142
+ * Clone this running sandbox `count` times (1-10). Each fork is a copy of the
1143
+ * microVM at this instant -- memory, running processes and files -- and is
1144
+ * billed like its source. The source keeps running.
1145
+ *
1146
+ * Returns one entry per requested fork, in order: `{ sandbox, info }`, or
1147
+ * `{ error }` for a copy that could not be made (e.g. the account's sandbox
1148
+ * limit). A sandbox with a volume attached cannot be forked (`ConflictError`, 409).
1149
+ *
1150
+ * @param opts.timeoutMs Lifetime of each fork; defaults to the source's timeout.
1151
+ *
1152
+ * @example
1153
+ * ```ts
1154
+ * const forks = await sandbox.fork({ count: 2 })
1155
+ * for (const f of forks) if (f.sandbox) await f.sandbox.process.exec('echo hi')
1156
+ * ```
1157
+ */
1066
1158
  fork(opts?: {
1067
1159
  count?: number;
1068
1160
  timeoutMs?: number;
1069
- }): Promise<unknown>;
1070
- /** Capture memory and workspace files. Disconnect active clients before capturing. */
1161
+ }): Promise<ForkResult<this>[]>;
1162
+ /**
1163
+ * Save this sandbox -- memory, running processes and files -- as a private
1164
+ * snapshot. A Firecracker snapshot is `ready` as soon as this returns (about 2 s);
1165
+ * start copies of it with {@link Sandbox.restore} (about 0.4 s each). The source
1166
+ * keeps running. `poolSize` applies to container sandboxes only.
1167
+ */
1071
1168
  snapshot(name?: string, opts?: SnapshotOpts): Promise<SandboxSnapshot>;
1072
1169
  unexpose(port: number): Promise<void>;
1170
+ /** Stream the sandbox's logs. Not available on Firecracker sandboxes yet (`LizardError`, 501). */
1073
1171
  logs(opts?: {
1074
1172
  tail?: number;
1075
1173
  signal?: AbortSignal;
1076
1174
  }): AsyncGenerator<StreamEvent, any, any>;
1077
1175
  static snapshots(projectId: string, opts?: ConnectionOpts): Promise<SandboxSnapshot[]>;
1078
1176
  static getSnapshot(snapshotId: string, opts?: ConnectionOpts): Promise<SandboxSnapshot>;
1079
- /** Start a sandbox from an available warm copy in the snapshot's project and region. */
1177
+ /** Start a new sandbox from a snapshot, in the snapshot's project and region. */
1080
1178
  static restore(snapshotId: string, opts?: ConnectionOpts & {
1081
1179
  timeoutMs?: number;
1082
1180
  }): Promise<Sandbox>;
1083
1181
  static setSnapshotWarmPool(snapshotId: string, poolSize: number, opts?: ConnectionOpts): Promise<SandboxSnapshot>;
1084
- /** Release idle warm copies; retain saved state and existing claimed sandboxes. */
1182
+ /** Release idle warm copies (container snapshots); retain saved state and existing claimed sandboxes. */
1085
1183
  static pauseSnapshot(snapshotId: string, opts?: ConnectionOpts): Promise<SandboxSnapshot>;
1086
1184
  /** Refill the configured warm pool without another capture. */
1087
1185
  static resumeSnapshot(snapshotId: string, opts?: ConnectionOpts): Promise<SandboxSnapshot>;
@@ -1526,8 +1624,10 @@ interface VolumeInfo {
1526
1624
  /** Region the volume's node lives in. A sandbox mounting it runs here too. */
1527
1625
  region?: string;
1528
1626
  status: string;
1627
+ /** Id of the sandbox the volume is attached to, or `null` when it is free. */
1529
1628
  attachedTo?: string | null;
1530
- createdAt: number;
1629
+ /** Creation time: an ISO-8601 string for Firecracker volumes, unix milliseconds for container volumes. */
1630
+ createdAt: string | number;
1531
1631
  /**
1532
1632
  * Whether `sizeGb` is actually enforced as a hard limit on the volume's node.
1533
1633
  * Returned by {@link Volume.resize}; `false` means the size was recorded but the
@@ -1536,7 +1636,7 @@ interface VolumeInfo {
1536
1636
  sizeEnforced?: boolean;
1537
1637
  }
1538
1638
  interface CreateVolumeOpts extends ConnectionOpts {
1539
- /** Whole GB, default 5. New volumes allow 1–50 GB unless the server config sets another maximum. Creating never changes an existing volume's size; use `Volume.resize` for that. */
1639
+ /** Whole GB, default 5. New volumes allow 1–50 GB unless the server config sets another maximum. Creating never changes an existing volume's size. */
1540
1640
  sizeGb?: number;
1541
1641
  /**
1542
1642
  * Region to place the volume in, e.g. `'us-east-1'`. A volume is node-local, so
@@ -1559,7 +1659,9 @@ interface CreateVolumeOpts extends ConnectionOpts {
1559
1659
  * Names are slugs: lowercase letters, digits and dashes, starting and ending with a
1560
1660
  * letter or digit, up to 64 characters.
1561
1661
  *
1562
- * Mount one to a sandbox via `Sandbox.create({ volumeName: 'my-data' })`.
1662
+ * Mount one to a sandbox via `Sandbox.create({ volumeName: 'my-data' })`; it appears
1663
+ * at `/workspace`. A volume is attached to one sandbox at a time (`attachedTo`), and
1664
+ * a sandbox with a volume attached cannot be forked.
1563
1665
  */
1564
1666
  declare class Volume {
1565
1667
  /** The volume's generated id. Stable, but you rarely need it — address by name. */
@@ -1582,7 +1684,7 @@ declare class Volume {
1582
1684
  * "the scratch disk for this task" no longer has to store an id between runs.
1583
1685
  *
1584
1686
  * An existing volume is returned as-is — `sizeGb` applies only to a fresh create,
1585
- * so an existing volume keeps its size. Call {@link Volume.resize} to change it.
1687
+ * so an existing volume keeps its size.
1586
1688
  */
1587
1689
  static getOrCreate(projectId: string, name: string, opts?: CreateVolumeOpts): Promise<Volume>;
1588
1690
  /** Look a volume up by name or by id. */
@@ -1593,11 +1695,15 @@ declare class Volume {
1593
1695
  /**
1594
1696
  * Resize a volume, by name or by id, to `sizeGb`.
1595
1697
  *
1596
- * The change is in place and online: the size is a quota, so no data is copied,
1597
- * it completes in well under a second, and a sandbox that has the volume mounted
1598
- * keeps running and sees the new size immediately. Both growing and shrinking are
1599
- * allowed, but a shrink must leave at least 10% of the new size free — otherwise
1600
- * it throws `ConflictError` with `code: 'volume_too_full_to_shrink'`.
1698
+ * **Firecracker volumes cannot be resized yet**: volumes created for the default
1699
+ * Firecracker sandboxes throw `LizardError` (400) with `code: 'volume_not_resizable'`.
1700
+ * Create the volume at the size you need, or copy the data to a new, larger one.
1701
+ *
1702
+ * On a container volume the change is in place and online: the size is a quota,
1703
+ * so no data is copied and a mounted sandbox sees the new size immediately. Both
1704
+ * growing and shrinking are allowed, but a shrink must leave at least 10% of the
1705
+ * new size free — otherwise it throws `ConflictError` with
1706
+ * `code: 'volume_too_full_to_shrink'`.
1601
1707
  *
1602
1708
  * Other failures carry a `code` too: `invalid_volume_size`, `volume_not_resizable`,
1603
1709
  * `volume_resize_in_progress`, `volume_not_provisioned`,
@@ -1608,7 +1714,7 @@ declare class Volume {
1608
1714
  */
1609
1715
  static resize(projectId: string, nameOrId: string, sizeGb: number, opts?: ConnectionOpts): Promise<VolumeInfo>;
1610
1716
  getInfo(projectId: string): Promise<VolumeInfo>;
1611
- /** Resize this volume in place — see the static {@link Volume.resize}. */
1717
+ /** Resize this volume — see the static {@link Volume.resize} (not supported for Firecracker volumes yet). */
1612
1718
  resize(projectId: string, sizeGb: number): Promise<VolumeInfo>;
1613
1719
  delete(projectId: string): Promise<void>;
1614
1720
  }
@@ -1826,7 +1932,7 @@ declare class Execution {
1826
1932
  results: OutputItem[];
1827
1933
  /** Execution error if the code threw an exception. */
1828
1934
  error?: ExecutionError;
1829
- /** Monotonically increasing counter for this context. */
1935
+ /** Monotonically increasing counter for this context (the Python kernel's `In[n]`). */
1830
1936
  executionCount: number;
1831
1937
  get success(): boolean;
1832
1938
  get text(): string;
@@ -1841,11 +1947,15 @@ type RunCodeLanguage = 'python' | 'javascript' | 'bash' | (string & Record<never
1841
1947
  interface RunCodeOpts {
1842
1948
  /** Language to run in. Defaults to python. */
1843
1949
  language?: RunCodeLanguage;
1844
- /** Use a specific context instead of the per-language default. */
1950
+ /** Use a specific context (from `createContext`) instead of the per-language default. */
1845
1951
  context?: CodeContext;
1846
1952
  /** Extra environment variables available to the code. */
1847
1953
  envs?: Record<string, string>;
1848
- /** Client timeout in milliseconds. No client timer is set when omitted. */
1954
+ /**
1955
+ * Execution timeout in milliseconds, default 60 000. A Python kernel that runs
1956
+ * past it is interrupted (its state is kept) and `execution.error.name` is
1957
+ * `'TimeoutError'`; a process is killed.
1958
+ */
1849
1959
  timeoutMs?: number;
1850
1960
  onStdout?: (data: string) => void;
1851
1961
  onStderr?: (data: string) => void;
@@ -1858,31 +1968,47 @@ interface CreateContextOpts {
1858
1968
  }
1859
1969
 
1860
1970
  /**
1861
- * Legacy client for the code-interpreter execution server.
1971
+ * A sandbox for running code snippets, built on {@link Sandbox.process}.
1862
1972
  *
1863
- * Current hosted templates do not provide this server. The default
1864
- * `code-interpreter-v1` template is unavailable; selecting `interpreter`
1865
- * does not enable this API. Use `Sandbox.create('interpreter', options)`
1866
- * and `sandbox.process.exec()` for Python commands instead.
1973
+ * Boots the `interpreter` template by default. Python runs in a persistent
1974
+ * Jupyter kernel per context, so variables, imports and functions carry over
1975
+ * between calls, `execution.results` holds rich output (the value of the last
1976
+ * expression, matplotlib charts as `image/png`, HTML, ...) and errors carry
1977
+ * the exception name and traceback.
1867
1978
  *
1868
- * The methods remain for compatibility with a separately provided server.
1979
+ * Limits, honestly:
1980
+ * - `javascript` (node) and `bash` run each call as a fresh process: no state
1981
+ * carries over and `results` stays empty. `node` must be installed in the
1982
+ * template; the `interpreter` template does not ship it.
1983
+ * - On a template without Jupyter (e.g. `base`), Python also runs as a fresh
1984
+ * process per call.
1985
+ * - `stdout`/`stderr` are delivered in the chunks the kernel or process
1986
+ * produced them, not per character.
1987
+ *
1988
+ * Kernels live inside the sandbox's microVM, so pause/resume, snapshots and
1989
+ * forks keep their state.
1990
+ *
1991
+ * @example
1992
+ * ```ts
1993
+ * const sandbox = await CodeSandbox.create({ project: 'my-project' })
1994
+ * await sandbox.runCode('x = 21')
1995
+ * const run = await sandbox.runCode('x * 2')
1996
+ * console.log(run.results[0].data) // "42"
1997
+ * await sandbox.kill()
1998
+ * ```
1869
1999
  */
1870
2000
  declare class CodeSandbox extends Sandbox {
1871
- protected static readonly defaultTemplate = "code-interpreter-v1";
1872
- private get serverUrl();
2001
+ protected static readonly defaultTemplate: string;
1873
2002
  /**
1874
- * Execute code in a persistent kernel.
2003
+ * Execute code and wait for it to finish.
1875
2004
  *
1876
- * Variables, imports, and function definitions from previous calls are
1877
- * available in subsequent calls within the same context.
2005
+ * Python keeps its state per context (the default context, per language,
2006
+ * when none is given). Code errors appear in `execution.error`; request
2007
+ * failures throw.
1878
2008
  *
1879
2009
  * @param code Source code to run.
1880
2010
  * @param opts Language, context, env vars, timeout, and streaming callbacks.
1881
2011
  *
1882
- * @returns Execution result with stdout, stderr, results, and any error.
1883
- *
1884
- * Code errors appear in `execution.error`; request failures throw.
1885
- *
1886
2012
  * @example
1887
2013
  * ```ts
1888
2014
  * const result = await sandbox.runCode(`
@@ -1896,8 +2022,8 @@ declare class CodeSandbox extends Sandbox {
1896
2022
  /**
1897
2023
  * Create a new isolated execution context.
1898
2024
  *
1899
- * Each context maintains its own variable namespace and process state.
1900
- * Useful for running multiple independent sessions in the same sandbox.
2025
+ * A Python context is its own kernel: its own variables, and `cwd` as its
2026
+ * working directory. Other languages get the working directory only.
1901
2027
  *
1902
2028
  * @example
1903
2029
  * ```ts
@@ -1907,18 +2033,14 @@ declare class CodeSandbox extends Sandbox {
1907
2033
  * ```
1908
2034
  */
1909
2035
  createContext(opts?: CreateContextOpts): Promise<CodeContext>;
1910
- /**
1911
- * List all active execution contexts in this sandbox.
1912
- */
2036
+ /** List the contexts created with {@link createContext} in this sandbox. */
1913
2037
  listContexts(): Promise<CodeContext[]>;
1914
- /**
1915
- * Delete an execution context and free its resources.
1916
- */
2038
+ /** Delete an execution context, stopping its kernel. */
1917
2039
  deleteContext(context: CodeContext | string): Promise<void>;
1918
- /**
1919
- * Restart a context, clearing all variables and state.
1920
- */
2040
+ /** Restart a context, clearing all variables and state. */
1921
2041
  restartContext(context: CodeContext | string): Promise<void>;
2042
+ /** Run the in-sandbox runner with `args` and hand each of its events to `onEvent`. */
2043
+ private runner;
1922
2044
  static create(opts?: SandboxOpts): Promise<CodeSandbox>;
1923
2045
  static create(template: string, opts?: SandboxOpts): Promise<CodeSandbox>;
1924
2046
  static connect(sandboxId: string, opts?: ConnectionOpts): Promise<CodeSandbox>;
@@ -1948,4 +2070,4 @@ declare class LizardCLI {
1948
2070
  }): Promise<CLIResult>;
1949
2071
  }
1950
2072
 
1951
- export { type Account, type Addon, type AddonType, AddonsAPI, type ApiKey, type ApiKeyScope, ApiKeysAPI, AuthenticationError, type Balance, BillingAPI, type CLIOptions, type CLIResult, type ClickOpts, type CodeContext, CodeSandbox, ConfigApplyError, type ConfigResult, ConflictError, type ConnectionOpts, type CostMetrics, type CreateAddonOpts, type CreateApiKeyOpts, type CreateContextOpts, type CreateProjectOpts, type CreateServiceOpts, type CreateVolumeOpts, type CreateWorkspaceOpts, type CreatedApiKey, type DeployEvent, DeployHandle, Desktop, type DesktopInfo, type DesktopStartOpts, type DomainInfo, type DomainStatus, DomainsAPI, Execution, ExecutionError, type FileInfo, type FsOpts, GitHubAPI, type ListTransactionsOpts, Lizard, LizardCLI, LizardError, type LizardOpts, type LogLine, type MetricPoint, type MetricRange, MetricsAPI, type MouseButton, NotFoundError, PlatformClient, type ProcessOpts, type ProcessResult, type Project, type ProjectConfig, type ProjectCost, ProjectsAPI, type RawMetrics, type Region, RegionsAPI, type RunCodeLanguage, type RunCodeOpts, Sandbox, type SandboxInfo, type SandboxOpts, type SandboxSnapshot, type ScaleOpts, type ScrollDirection, type Secret, SecretsAPI, type Service, type ServiceMetrics, type ServiceUpdate, ServicesAPI, type SetSecretOpts, type SnapshotOpts, type SnapshotWaitOpts, StorageAPI, type StreamEvent, TimeoutError, type Transaction, type TransactionPage, Volume, type VolumeInfo, type Workspace, WorkspacesAPI, resolveProjectId };
2073
+ export { type Account, type Addon, type AddonType, AddonsAPI, type ApiKey, type ApiKeyScope, ApiKeysAPI, AuthenticationError, type Balance, BillingAPI, type CLIOptions, type CLIResult, type ClickOpts, type CodeContext, CodeSandbox, ConfigApplyError, type ConfigResult, ConflictError, type ConnectionOpts, type CostMetrics, type CreateAddonOpts, type CreateApiKeyOpts, type CreateContextOpts, type CreateProjectOpts, type CreateServiceOpts, type CreateVolumeOpts, type CreateWorkspaceOpts, type CreatedApiKey, type DeployEvent, DeployHandle, Desktop, type DesktopInfo, type DesktopStartOpts, type DomainInfo, type DomainStatus, DomainsAPI, Execution, ExecutionError, type ExposedPort, type FileInfo, type ForkResult, type FsEvent, type FsOpts, GitHubAPI, type ListTransactionsOpts, Lizard, LizardCLI, LizardError, type LizardOpts, type LogLine, type MetricPoint, type MetricRange, MetricsAPI, type MouseButton, NotFoundError, PlatformClient, type ProcessInfo, type ProcessOpts, type ProcessResult, type ProcessSignal, type Project, type ProjectConfig, type ProjectCost, ProjectsAPI, type RawMetrics, type Region, RegionsAPI, type RunCodeLanguage, type RunCodeOpts, Sandbox, type SandboxInfo, type SandboxOpts, type SandboxSnapshot, type ScaleOpts, type ScrollDirection, type Secret, SecretsAPI, type Service, type ServiceMetrics, type ServiceUpdate, ServicesAPI, type SetSecretOpts, type SnapshotOpts, type SnapshotWaitOpts, StorageAPI, type StreamEvent, TimeoutError, type Transaction, type TransactionPage, Volume, type VolumeInfo, type Workspace, WorkspacesAPI, resolveProjectId };