@sailresearch/sdk
v0.13.0
Published
TypeScript SDK for Sail sandboxes (Sailboxes). Create, exec in, and manage sandboxes from agent harnesses on Node and Bun.
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");
await sb.fs.writeFiles({
"/tmp/a.txt": "first\n",
"/tmp/b.txt": "second\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);
}
// Sleep saves the Sailbox to disk; a command or inbound traffic wakes it.
// See also pause(), resume(), and checkpoint().
await sb.sleep();
// Terminate ends the Sailbox for good.
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 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 transport 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,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 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.
