Solari

Sandboxes

@solarisdk/sandbox: classes SandboxClient and Sandbox. A sandbox is a headless microVM for commands, code, files, and git. See the TypeScript SDK hub for install and configuration.

npm install @solarisdk/sandbox
Sandbox is the shared base of Desktop
Every Sandbox method below is inherited from SessionHandle and is also available on Desktop. A Desktop is a Sandbox plus the GUI surface.

Contents

SandboxClient

The sandbox entry point. Talks the gateway HTTP API.

Properties

  • volumes VolumeClient: persistent volume CRUD. Attach one at create time via create({ volumes }).

Constructors

new SandboxClient()

new SandboxClient(options: SandboxClientOptions, kind?: "sandbox" | "desktop"): SandboxClient

Creates a client. Does not open any connection.

Parameters:

  • options.apiKey string: required.
  • options.baseUrl string: required, e.g. https://api.getsolari.com.
  • options.fetch? typeof fetch: defaults to the global.
  • options.callTimeoutMs? number: per-RPC timeout for handles this client creates.
  • kind? "sandbox" | "desktop": the flavour create() makes. Default "sandbox".

Returns: SandboxClient

Throws: SolariError if apiKey or baseUrl is missing.

Example:

import { SandboxClient } from "@solarisdk/sandbox";

const client = new SandboxClient({
  apiKey: process.env.SOLARI_API_KEY!,
  baseUrl: "https://api.getsolari.com",
});

Methods

create()

create(opts?: CreateSandboxOptions): Promise<Sandbox>

Creates a sandbox and returns a live handle. The call sends an idempotency key, so retrying is safe.

Parameters:

Returns: Promise<Sandbox>

Throws: AuthError, PlanError, ConcurrencyLimitError, NoCapacityError, or GatewayError.

Example:

const sbx = await client.create({
  template: "base",
  cpu: 2,
  memMb: 4096,
  envs: { NODE_ENV: "test" },
});

createDesktop()

createDesktop(opts?: CreateDesktopV2Options): Promise<Desktop>

Creates a GUI session over the same /sandboxes route and returns a Desktop handle. Adds resolution and record to the sandbox options.

Parameters:

  • opts? CreateDesktopV2Options: CreateSandboxOptions plus resolution? string and record? boolean.

Returns: Promise<Desktop>

Example:

const vm = await client.createDesktop({
  template: "office",
  resolution: "1280x720",
});

connect()

connect(sandboxId: string): Promise<Sandbox>

Re-attaches to a running sandbox by id.

Parameters:

  • sandboxId string: the sandbox id.

Returns: Promise<Sandbox>

Throws: GatewayError if the id is unknown.

Example:

const sbx = await client.connect("sbx_abc123");
await sbx.commands.run("uptime");
connect() does not resume a paused sandbox
Unlike DesktopClient.connect(), this only rebuilds a handle from get(). Call resume() on it if the sandbox is paused.

get()

get(sandboxId: string): Promise<SandboxView>

Fetches a sandbox’s current record: state, kind, metadata, resources. Does not return a handle.

Parameters:

  • sandboxId string: the sandbox id.

Returns: Promise<SandboxView>

Example:

const view = await client.get("sbx_abc123");
console.log(view.state);  // "running" | "paused" | ...

list()

list(opts?: ListSandboxesOptions): Promise<ListSandboxesResponse>

Lists sandboxes with optional filters. One page. Follow nextCursor, or use listAll().

Parameters:

  • opts.metadata? Record<string, string>: match on caller-set metadata.
  • opts.state? SandboxState: starting, running, paused, archived, releasing, gone.
  • opts.kind? SandboxKind: sandbox or desktop.
  • opts.limit? number: page size.
  • opts.cursor? string: page cursor.

Returns: Promise<ListSandboxesResponse>: sandboxes + optional nextCursor.

Example:

const page = await client.list({ state: "running", limit: 50 });
console.log(page.sandboxes.length, page.nextCursor);

listAll()

listAll(opts?: Omit<ListSandboxesOptions, "cursor">): AsyncGenerator<SandboxView>

Auto-paginates list(), following nextCursor until exhausted.

Parameters:

  • opts? ListSandboxesOptions: same filters as list(), minus cursor.

Returns: AsyncGenerator<SandboxView>

Example:

for await (const s of client.listAll({ state: "running" })) {
  console.log(s.sandboxId, s.cpu, s.memMb);
}

kill()

kill(sandboxId: string): Promise<void>

Destroys a sandbox. Idempotent.

Parameters:

  • sandboxId string: the sandbox id.

Returns: Promise<void>

Example:

await client.kill("sbx_abc123");

listSnapshots()

listSnapshots(opts?: ListSnapshotsOptions): Promise<{ snapshots: SnapshotView[] }>

Lists snapshots, optionally filtered by template or kind.

Parameters:

  • opts.template? string, opts.kind? SandboxKind, opts.limit? number.

Returns: Promise<{ snapshots: SnapshotView[] }>

Example:

const { snapshots } = await client.listSnapshots({ template: "base" });

getSnapshot()

getSnapshot(snapshotId: string): Promise<SnapshotView>

Fetches one snapshot record.

Parameters:

  • snapshotId string: e.g. snap_abc123.

Returns: Promise<SnapshotView>

Example:

const snap = await client.getSnapshot("snap_abc123");
console.log(snap.sizeBytes, snap.parent);

deleteSnapshot()

deleteSnapshot(snapshotId: string): Promise<void>

Deletes a snapshot.

Parameters:

  • snapshotId string: the snapshot id.

Returns: Promise<void>

Throws: GatewayError if the snapshot has live children.

Example:

await client.deleteSnapshot("snap_abc123");

promoteSnapshot()

promoteSnapshot(snapshotId: string, name: string): Promise<{ templateId: string; name: string }>

Promotes a snapshot to a reusable template you can pass as template on future creates.

Parameters:

  • snapshotId string: the snapshot id.
  • name string: template name.

Returns: Promise<{ templateId: string; name: string }>

Example:

const id = await sbx.snapshot("deps-installed");
const tpl = await client.promoteSnapshot(id, "my-stack");
const fresh = await client.create({ template: tpl.templateId });

volumes.create()

volumes.create(opts?: CreateVolumeOptions): Promise<VolumeView>

Creates a persistent, S3-backed volume your org owns. It outlives any session.

Parameters:

  • opts.name? string: defaults to "volume".
  • opts.sizeMb? number: advisory hint; storage is elastic, not a hard cap.
  • opts.metadata? Record<string, string>: opaque labels.

Returns: Promise<VolumeView>

Example:

const vol = await client.volumes.create({ name: "datasets" });
const sbx = await client.create({
  volumes: [{ volumeId: vol.volumeId, path: "/data" }],
});

volumes.list()

volumes.list(): Promise<VolumeView[]>

Lists the org’s volumes, newest first.

Returns: Promise<VolumeView[]>

Example:

for (const v of await client.volumes.list()) console.log(v.volumeId, v.name);

volumes.get()

volumes.get(volumeId: string): Promise<VolumeView>

Fetches one volume’s metadata.

Parameters:

  • volumeId string: e.g. vol_abc123.

Returns: Promise<VolumeView>

Example:

const vol = await client.volumes.get("vol_abc123");

volumes.delete()

volumes.delete(volumeId: string): Promise<void>

Deletes a volume’s metadata. Idempotent.

Parameters:

  • volumeId string: the volume id.

Returns: Promise<void>

Example:

await client.volumes.delete("vol_abc123");

Sandbox

A live headless session. Construct via SandboxClient.create().

Properties

  • id string: the session id.
  • sandboxId string: alias of id.
  • controlUrl string: wss:// JSON-RPC control channel.
  • expiresAt string: ISO 8601 expiry.
  • connected boolean: whether the control channel is open.

Channel

connect()

connect(): Promise<void>

Opens the control WebSocket. Idempotent. Call it before any control-channel method — files.*, runCode(), pty.create(), git.*, env(), volumes.mount(), and commands.start(). These do not open the channel for you; they throw ConnectionError if it is not open.

Returns: Promise<void>

Example:

const sbx = await client.create({ template: "base" });
await sbx.connect();
await sbx.files.write("/tmp/note.txt", "hi");
commands.run() is the one exception
A plain commands.run() (no streaming callbacks, not background, no per-command env/user) runs over a warm HTTP fast path and needs no open channel — which is why the quickstart can call it right after create(). Every other control-channel method, and commands.run() once the channel is already open, requires connect() first.

reconnect()

reconnect(): Promise<void>

Re-opens the control socket after a drop.

Returns: Promise<void>

Example:

if (!sbx.connected) await sbx.reconnect();

close()

close(): void

Closes the control channel locally. Does not release the remote session. Use kill() for that.

Returns: void

Example:

sbx.close();  // session keeps running until its timeout

Commands

commands.run()

commands.run(cmd: string, opts?: CommandOptions): Promise<CommandResult>

Runs a command to completion and returns its exit code and captured output.

Parameters:

  • cmd string: the program to run.
  • opts? CommandOptions: see CommandOptions.

Returns: Promise<CommandResult>: exitCode, stdout, stderr. A non-zero exit resolves; it does not throw.

Example:

const r = await sbx.commands.run("ls", { args: ["-la", "/tmp"] });
console.log(r.exitCode, r.stdout);

// stream output as it arrives
await sbx.commands.run("npm", {
  args: ["install"],
  cwd: "/app",
  onStdout: (d) => process.stdout.write(d),
});
No shell by default
cmd is executed directly with args, not through a shell. Pipes, globs, and && will not expand. For shell syntax use run("sh", { args: ["-c", "…"] }).

commands.start()

commands.start(cmd: string, opts?: CommandOptions): Promise<CommandHandle>

Starts a command and returns a handle immediately, without waiting for exit. Use it for interactive or long-running processes.

Parameters:

  • cmd string: the program to run.
  • opts? CommandOptions: see CommandOptions.

Returns: Promise<CommandHandle>: cmdId, stdin(), onData(), wait(), kill().

Example:

const proc = await sbx.commands.start("python3", { args: ["-u", "worker.py"] });
proc.onData((c) => console.log(c.stream, c.data));
await proc.stdin("input\n");
const exitCode = await proc.wait();

commands.connect()

commands.connect(): Promise<void>

Re-opens the control channel so command frames flow again. An alias of reconnect().

Returns: Promise<void>

Example:

if (!sbx.connected) await sbx.commands.connect();

pty.create()

pty.create(opts: PtyOptions): Promise<PtyHandle>

Opens a pseudo-terminal (a real TTY), so interactive programs and curses UIs behave normally.

Parameters:

  • opts.cols number, opts.rows number: required terminal size.
  • opts.cmd? string: program to run; defaults to the guest shell.
  • opts.cwd? string, opts.env? Record<string, string>.

Returns: Promise<PtyHandle>: ptyId, write(), resize(), onData(), kill().

Example:

const pty = await sbx.pty.create({ cols: 80, rows: 24 });
pty.onData((bytes) => process.stdout.write(new TextDecoder().decode(bytes)));
await pty.write("ls -la\n");
await pty.kill();

Code

runCode()

runCode(code: string, opts?: RunCodeOptions): Promise<RunCodeResult>

Runs code in a stateful kernel. Rich outputs (PNG, HTML, JSON, structured charts) come back as results items.

Parameters:

  • code string: source to execute.
  • opts.language? CodeLanguage: python (default), javascript, typescript, bash, r.
  • opts.contextId? string: kernel context to reuse; see createCodeContext().
  • opts.onStdout?, opts.onStderr? (data: string) => void: streamed text.

Returns: Promise<RunCodeResult>: results, charts, optional error. A runtime error in the code lands in error; it does not throw.

Example:

const r = await sbx.runCode(`
import matplotlib.pyplot as plt
plt.plot([1, 2, 3], [4, 5, 6])
plt.title("demo")
plt.show()
`);

console.log(r.charts[0]?.type);   // "line"
console.log(r.charts[0]?.title);  // "demo"
const png = r.results.find((i) => i.png)?.png;  // base64

createCodeContext()

createCodeContext(language?: "python" | "javascript" | "typescript" | "bash" | "r"): Promise<string>

Creates a fresh kernel context so state persists across runCode() calls, like a REPL.

Parameters:

  • language? CodeLanguage: default "python".

Returns: Promise<string>, the context id.

Example:

const ctx = await sbx.createCodeContext("python");
await sbx.runCode("x = 41", { contextId: ctx });
const r = await sbx.runCode("print(x + 1)", { contextId: ctx });  // 42

Files

files.* moves bytes over the control channel. For large files prefer downloadUrl() / uploadUrl().

files.read() / files.readText()

files.read(path: string): Promise<Uint8Array>
files.readText(path: string): Promise<string>

Reads a file’s bytes, or its UTF-8 decoded text.

Parameters:

  • path string: absolute in-guest path.

Returns: Promise<Uint8Array> / Promise<string>

Example:

const text = await sbx.files.readText("/etc/hostname");
const bytes = await sbx.files.read("/tmp/out.bin");

files.write()

files.write(path: string, data: Uint8Array | string, mode?: number): Promise<void>

Writes a file, creating or truncating it.

Parameters:

  • path string: absolute in-guest path.
  • data Uint8Array | string: content.
  • mode? number: permission bits, e.g. 0o755.

Returns: Promise<void>

Example:

await sbx.files.write("/app/run.sh", "#!/bin/sh\necho hi\n", 0o755);

files.list()

files.list(path: string): Promise<FsEntry[]>

Lists a directory’s entries (non-recursive).

Parameters:

  • path string: directory path.

Returns: Promise<FsEntry[]>: each with name, dir, size.

Example:

for (const e of await sbx.files.list("/app")) {
  console.log(e.dir ? "d" : "-", e.name, e.size);
}

files.stat()

files.stat(path: string): Promise<FsStat>

Returns one entry’s metadata.

Parameters:

  • path string: file or directory path.

Returns: Promise<FsStat>: name, dir, size, mode, modTimeMs.

Example:

const st = await sbx.files.stat("/app/run.sh");
console.log(st.size, st.mode.toString(8), st.modTimeMs);

files.rename()

files.rename(from: string, to: string): Promise<void>

Renames or moves a path.

Parameters:

  • from string, to string: source and destination paths.

Returns: Promise<void>

Example:

await sbx.files.rename("/tmp/a.txt", "/tmp/b.txt");

files.remove()

files.remove(path: string, recursive?: boolean): Promise<void>

Deletes a file or directory.

Parameters:

  • path string: path to delete.
  • recursive? boolean: default false; required for non-empty directories.

Returns: Promise<void>

Example:

await sbx.files.remove("/tmp/build", true);

files.mkdir()

files.mkdir(path: string): Promise<void>

Creates a directory.

Parameters:

  • path string: directory to create.

Returns: Promise<void>

Example:

await sbx.files.mkdir("/app/data");
files.search(path: string, query: string, maxResults?: number): Promise<FsSearchMatch[]>

Recursively greps for query under path.

Parameters:

  • path string: root to search.
  • query string: the pattern.
  • maxResults? number: cap on matches.

Returns: Promise<FsSearchMatch[]>: each with path, line, text.

Example:

for (const m of await sbx.files.search("/app", "TODO", 20)) {
  console.log(`${m.path}:${m.line}: ${m.text}`);
}

files.watch()

files.watch(path: string, cb: (ev: FsWatchEvent) => void, recursive?: boolean): Promise<() => Promise<void>>

Watches a path for changes. Returns an unwatch function. Call it to stop.

Parameters:

  • path string: path to watch.
  • cb (ev: FsWatchEvent) => void: invoked per event with type and path.
  • recursive? boolean: default false.

Returns: Promise<() => Promise<void>>, the unwatch function.

Example:

const unwatch = await sbx.files.watch("/app", (ev) => {
  console.log(ev.type, ev.path);
}, true);

await unwatch();

files.upload() / files.download()

files.upload(path: string, data: Uint8Array | string): Promise<void>
files.download(path: string): Promise<Uint8Array>

Transfer a file over the control channel. Same bytes as write() / read().

Parameters:

  • path string: in-guest path.
  • data Uint8Array | string: upload content.

Returns: Promise<void> / Promise<Uint8Array>

Example:

await sbx.files.upload("/app/data.csv", csvString);
const out = await sbx.files.download("/app/result.json");

downloadUrl()

downloadUrl(path: string): Promise<{ url: string; expiresAt?: string }>

Signed, time-limited URL to download an in-guest file directly over HTTP, bypassing the control channel. Use for large files.

Parameters:

  • path string: in-guest file path.

Returns: Promise<{ url: string; expiresAt?: string }>

Throws: if the handle was not created by a client.

Example:

const { url } = await sbx.downloadUrl("/app/big.tar.gz");
const res = await fetch(url);

uploadUrl()

uploadUrl(path?: string): Promise<{ url: string; expiresAt?: string }>

Signed, time-limited URL to upload a file into the guest directly over HTTP.

Parameters:

  • path? string: destination path; optional, some backends return a prefix.

Returns: Promise<{ url: string; expiresAt?: string }>

Throws: if the handle was not created by a client.

Example:

const { url } = await sbx.uploadUrl("/app/big.zip");
await fetch(url, { method: "PUT", body: bytes });

Git

Every git.* call is a non-shell git invocation in the guest, with no injection surface. Requires git on PATH, which the base template ships.

git.clone()

git.clone(url: string, opts?: GitCloneOptions): Promise<void>

Clones a repository into the guest.

Parameters:

  • url string: remote URL.
  • opts.path? string: destination dir.
  • opts.branch? string: branch or tag.
  • opts.depth? number: shallow-clone depth.
  • opts.username?, opts.password? string: basic auth for private HTTPS remotes.
  • opts.cwd? string: working directory.

Returns: Promise<void>

Throws: Error with git’s stderr on a non-zero exit.

Example:

await sbx.git.clone("https://github.com/org/repo.git", {
  path: "/app/repo",
  branch: "main",
  depth: 1,
});

git.status()

git.status(cwd?: string): Promise<GitStatus>

Parsed working-tree status.

Parameters:

  • cwd? string: repository directory.

Returns: Promise<GitStatus>: see GitStatus.

Example:

const st = await sbx.git.status("/app/repo");
console.log(st.branch, st.clean, st.ahead, st.modified);

git.add()

git.add(paths: string[], cwd?: string): Promise<void>

Stages paths. An empty array is a no-op.

Parameters:

  • paths string[]: use ["."] for everything.
  • cwd? string: repository directory.

Returns: Promise<void>

Example:

await sbx.git.add(["."], "/app/repo");

git.commit()

git.commit(message: string, opts?: GitCommitOptions): Promise<{ hash: string }>

Commits staged changes and returns the new hash. Set author/email. An ephemeral session has no git identity.

Parameters:

  • message string: commit message.
  • opts.cwd? string: repository directory.
  • opts.author?, opts.email? string: identity, scoped to this commit only.
  • opts.all? boolean: stage all tracked modifications first.

Returns: Promise<{ hash: string }>

Throws: Error on a non-zero exit.

Example:

const { hash } = await sbx.git.commit("add feature", {
  cwd: "/app/repo",
  author: "CI Bot",
  email: "ci@example.com",
});

git.push() / git.pull()

git.push(opts?: GitRemoteOptions): Promise<void>
git.pull(opts?: GitRemoteOptions): Promise<void>

Pushes to or pulls from a remote. Credentials are used for this one invocation and never persisted to the repo config.

Parameters:

  • opts.cwd? string: repository directory.
  • opts.remote? string: default "origin".
  • opts.branch? string: defaults to the current branch’s upstream.
  • opts.username?, opts.password? string: one-off HTTPS auth.

Returns: Promise<void>

Example:

await sbx.git.push({
  cwd: "/app/repo",
  username: "x-access-token",
  password: process.env.GITHUB_TOKEN!,
});

git.checkout()

git.checkout(ref: string, opts?: { cwd?: string; create?: boolean }): Promise<void>

Checks out an existing ref, or creates a branch.

Parameters:

  • ref string: branch, tag, or commit.
  • opts.cwd? string: repository directory.
  • opts.create? boolean: create the branch (-b).

Returns: Promise<void>

Example:

await sbx.git.checkout("feature/x", { cwd: "/app/repo", create: true });

git.branches()

git.branches(cwd?: string): Promise<GitBranch[]>

Lists local branches.

Parameters:

  • cwd? string: repository directory.

Returns: Promise<GitBranch[]>: each with name, commit, current.

Example:

for (const b of await sbx.git.branches("/app/repo")) {
  console.log(b.current ? "*" : " ", b.name, b.commit);
}

git.log()

git.log(opts?: GitLogOptions): Promise<GitCommit[]>

Recent commits, newest first.

Parameters:

  • opts.cwd? string: repository directory.
  • opts.maxCount? number: cap the number returned.

Returns: Promise<GitCommit[]>: each with hash, author, email, date, message.

Example:

const commits = await sbx.git.log({ cwd: "/app/repo", maxCount: 10 });

Lifecycle & admin

env()

env(vars: Record<string, string>): Promise<void>

Injects or replaces per-session environment variables in the guest.

Parameters:

  • vars Record<string, string>: the variables to set.

Returns: Promise<void>

Example:

await sbx.env({ API_URL: "https://staging.example.com" });

volumes.mount()

volumes.mount(volId: string, path: string): Promise<void>

Mounts a persistent volume into the running guest. Prefer attaching at create time via create({ volumes }).

Parameters:

  • volId string: volume id.
  • path string: absolute in-guest mount point.

Returns: Promise<void>

Example:

await sbx.volumes.mount("vol_abc123", "/data");

metrics()

metrics(): Promise<MetricsResult>

Live resource usage for this session.

Returns: Promise<MetricsResult>: cpuPct, memBytes, memTotalBytes, diskBytes.

Throws: if the handle was not created by a client.

Example:

const m = await sbx.metrics();
console.log(m.cpuPct, m.memBytes / m.memTotalBytes);

snapshot()

snapshot(name?: string): Promise<string>

Checkpoints this running session and returns the snapshot id. The session keeps running.

Parameters:

  • name? string: optional label.

Returns: Promise<string>, the snapshot id.

Throws: if the handle was not created by a client.

Example:

await sbx.commands.run("pip", { args: ["install", "pandas"] });
const snapId = await sbx.snapshot("deps-installed");

// boot future sandboxes from it
const fast = await client.create({ fromSnapshot: snapId });

revert()

revert(snapshotId: string): Promise<void>

Restores this session in place from a snapshot. The session id stays stable.

Parameters:

  • snapshotId string: snapshot to restore.

Returns: Promise<void>

Throws: if the handle was not created by a client.

Example:

await sbx.revert(snapId);  // back to a known-good state

pause()

pause(): Promise<void>

Saves full RAM + disk state and closes the control channel. Resume later with resume().

Returns: Promise<void>

Throws: if the handle was not created by a client.

Example:

await sbx.pause();   // stops billing for compute
await sbx.resume();  // full state restored

resume()

resume(): Promise<void>

Resumes a paused session and re-points the control channel at the fresh slot.

Returns: Promise<void>

Throws: if the handle was not created by a client.

Example:

await sbx.resume();
const r = await sbx.commands.run("cat", { args: ["/tmp/state"] });

setTimeout()

setTimeout(timeoutMs: number): Promise<{ expiresAt: string }>

Extends the rolling idle window. Every use of the session resets it.

Parameters:

  • timeoutMs number: new idle window in ms.

Returns: Promise<{ expiresAt: string }>

Throws: if the handle was not created by a client.

Example:

const { expiresAt } = await sbx.setTimeout(30 * 60_000);  // 30 min

previewUrl()

previewUrl(port: number): Promise<{ url: string; token?: string }>

Resolves a public URL for a port listening inside the guest, e.g. a dev server.

Parameters:

  • port number: the in-guest port.

Returns: Promise<{ url: string; token?: string }>: token present when the gateway signs one.

Throws: if the handle was not created by a client.

Example:

await sbx.commands.start("python3", { args: ["-m", "http.server", "3000"] });
const { url } = await sbx.previewUrl(3000);

kill()

kill(): Promise<void>

Destroys the remote session and closes the channel. Idempotent.

Returns: Promise<void>

Example:

try {
  await sbx.commands.run("./build.sh");
} finally {
  await sbx.kill();
}

Types

CreateSandboxOptions

  • template? string: e.g. "base", "code", "python-kernel", or a promoted template id.
  • cpu? number: vCPUs; the host clamps to slot capacity.
  • memMb? number: memory in MiB.
  • diskGb? number: disk in GiB.
  • envs? Record<string, string>: per-session env.
  • metadata? Record<string, string>: opaque labels, filterable on list().
  • timeoutMs? number: idle window before auto-release.
  • fromSnapshot? string: boot from a snapshot instead of the template.
  • lifecycle? SandboxLifecycle: { onTimeout: "pause" | "kill", autoResume?: boolean }.
  • volumes? VolumeAttachment[]: each { volumeId, path }, mounted before the session starts.

SandboxView

  • sandboxId string, kind SandboxKind, state SandboxState.
  • metadata Record<string, string>, expiresAt string.
  • cpu number, memMb number.

CommandOptions

  • args? string[]: arguments; no shell expansion.
  • cwd? string, user? string, timeoutMs? number.
  • env? Record<string, string>: per-command env.
  • background? boolean: return immediately; output still streams via callbacks.
  • onStdout?, onStderr? (data: string) => void: streamed output.

CommandResult

  • exitCode number, stdout string, stderr string.

CommandHandle

  • cmdId string: the command id.
  • stdin(data): write to the command’s stdin.
  • onData(cb): subscribe to { stream, data } chunks.
  • wait(): Promise<number>, resolves with the exit code.
  • kill(signal?): signal the process; default SIGTERM.

PtyOptions / PtyHandle

  • PtyOptions: cols, rows (required), cmd?, cwd?, env?.
  • PtyHandle: ptyId, write(data), resize(cols, rows), onData(cb), kill().

RunCodeResult

  • results CodeResultItem[]: each has type (stdout/stderr/result) plus any of text, png, jpeg, svg, html, latex, json, markdown, chart.
  • charts Chart[]: every chart across results, flattened.
  • error?: { name?, message?, traceback? } or a string.

Chart

  • type ChartType: line, scatter, bar, pie, box_and_whisker, composite, unknown.
  • title?, xLabel?, yLabel? string.
  • x?, y? ChartAxis: label, ticks, scale.
  • elements? unknown[]: per-type data (points, bars, slices).

FsEntry / FsStat / FsSearchMatch / FsWatchEvent

  • FsEntry: name string, dir boolean, size number.
  • FsStat: FsEntry plus mode number (permission bits) and modTimeMs number (unix millis).
  • FsSearchMatch: path string, line number, text string.
  • FsWatchEvent: type string, path string.

GitStatus

  • branch string: empty on a detached HEAD or a repo with no commits.
  • detached boolean, clean boolean.
  • ahead, behind number: relative to the upstream, when set.
  • staged, modified, untracked string[].

MetricsResult

  • cpuPct number, memBytes number, memTotalBytes number, diskBytes number.

SnapshotView

  • id string, parent string | null, name string | null.
  • sizeBytes number, createdAt string, kind SandboxKind, template string.

VolumeView

  • volumeId string, name string, createdAt string.
  • sizeMb? number, metadata? Record<string, string>.

Errors

All extend SolariError. Gateway errors also carry status, code, and body.

ClassStatusMeaning
AuthError401API key missing, malformed, or rejected.
PlanError402The plan does not allow this.
ConcurrencyLimitError429Too many live sessions.
NoCapacityError503No host available right now.
GatewayErrorotherAny other non-2xx response.
ActionErrorNoneAn RPC returned ok: false. Carries method.
TimeoutErrorNoneNo reply within the per-call timeout. Carries method, timeoutMs.
ConnectionErrorNoneThe control channel is not open.
import { NoCapacityError, ConcurrencyLimitError } from "@solarisdk/sandbox";

try {
  const sbx = await client.create({ template: "base" });
} catch (e) {
  if (e instanceof NoCapacityError) await retryLater();
  else if (e instanceof ConcurrencyLimitError) await waitForSlot();
  else throw e;
}