@sailresearch/sdk
v0.9.0
Published
TypeScript SDK for Sail sandboxes (Sailboxes). Create, exec in, and manage sandboxes from agent harnesses on Node and Bun.
Downloads
22,507
Readme
@sailresearch/sdk
TypeScript SDK for Sail sandboxes (Sailboxes). Create, run commands in, move files to and from, and manage sandboxes from a TS agent harness. It runs on Node 22+ and Bun. It is not a browser library.
Install
npm install @sailresearch/sdk
# or: pnpm add @sailresearch/sdk / bun add @sailresearch/sdkThe SDK supports Linux x64/arm64 (glibc and musl), macOS x64/arm64, and
Windows x64. Use it from CommonJS require or ESM import.
Configure
Set SAIL_API_KEY in the environment. The SDK also reads ~/.sail. The
object-model statics use this environment configuration by default.
Quickstart
import { App, Sailbox } from "@sailresearch/sdk";
// Look up (or create) the app your sandboxes belong to.
const app = await App.find("example-app", { mintIfMissing: true });
// Boot a sandbox.
const sb = await Sailbox.create({ app, name: "worker-1" });
// Run a command and stream its output. A string runs via `/bin/sh -lc`; pass a
// string[] to exec directly without a shell.
const proc = await sb.exec("echo hello && ls /");
for await (const chunk of proc.stdout) process.stdout.write(chunk);
const result = await proc.wait();
console.log("exit code:", result.exitCode);
// Move files.
await sb.fs.write("/tmp/note.txt", "hi from the harness\n");
const contents = await sb.fs.read("/tmp/note.txt");
// Expose a port and wait until it's routable.
await sb.expose(8080, { protocol: "http" });
const listener = await sb.waitForListener(8080);
if (listener.endpoint?.kind === "http") {
console.log("reachable at:", listener.endpoint.url);
}
// Lifecycle.
await sb.sleep(); // or pause() / resume() / checkpoint() / terminate()
await sb.terminate();Conventions
- Fields are camelCase (
memoryMib,result.exitCode,listener.endpoint,info.sailboxId). Object-model helpers accept ergonomic handles likeapp; lower-levelClientrequest objects use generated fields likeappId. - Bytes cross as
Buffer; astringpassed towrite/writeStdinis UTF-8. - Errors raised by the SDK extend
SailError, with subclasses (NotFoundError,SailboxExecutionError, ...) for specific failures; a truly unexpected error from the native layer is rethrown unchanged. Thecodeproperty is the stable discriminator, and every error carries an advisoryretryableflag (truewhen retrying the same call may succeed).ApiErrorandSailboxCreationErroralso carry the HTTPstatusand parsed responsebody; exec failures carry the RPC status asrpcStatus. Whereinstanceofcan lie (across realms, or with two SDK copies loaded), use the exportedisSailError(err)check instead:
import { isSailError } from "@sailresearch/sdk";
try {
await sb.fs.read("/missing.txt");
} catch (err) {
if (isSailError(err) && err.code === "FileNotFound") {
// handle the missing file
} else {
throw err;
}
}Explicit configuration
Instead of the environment, construct a Client and pass it to the statics (or
call its methods directly):
import { Client, Sailbox } from "@sailresearch/sdk";
const client = Client.fromConfig({ apiKey: "sk_..." });
const sb = await Sailbox.create({ app: "app_...", name: "w", client });Custom images
Build a custom image with the fluent Image builder and pass it to
Sailbox.create; local files/dirs are hashed and uploaded when the box is
created:
import { App, Image, Sailbox } from "@sailresearch/sdk";
const app = await App.find("example-app", { mintIfMissing: true });
const image = Image.debian("arm64")
.aptInstall("git")
.pipInstall("numpy")
.addLocalDir("./app", "/app", { ignore: ["node_modules/", ".git/"] })
.runCommand("pip install -e /app");
const sb = await Sailbox.create({ app, name: "worker", image });API
Sailbox:create/get/fromId/list/listPage/fromCheckpoint; instanceexec/runand an interactiveshell, anfsnamespace (read/write/readStream/writeStream/mkdir/remove/exists/ls),expose/unexpose/listeners/listener/waitForListener/ingressAuthHeaders,enableSsh, andterminate/pause/sleep/resume/checkpoint/upgrade.App:find/list.Volume:find/list; instancedelete.Image:debian/devbox,aptInstall/pipInstall/runCommand/env,addLocalFile/addLocalDir,build,toSpec. (Sailbox.createbuilds a custom image for you.)Client: the lower-level surface with the same operations.ExecProcess/ExecStream/FileStream/FileWriter: streaming handles.- Errors:
SailErrorand typed subclasses (NotFoundError,SailboxCreationError,SailboxExecutionError, ...). Errors the SDK raises map to aSailErrorsubclass with a stablecodeand an advisoryretryableflag; a truly unexpected native error is rethrown unchanged.isSailErroris the realm-safe alternative toinstanceof.
Documentation
- Sailbox TypeScript guide and TypeScript API reference.
- Sail documentation: quickstarts, guides, and examples.
- Other languages: Python, Rust.
License
Apache-2.0.
