@buddy-works/sandbox-sdk

中文文档
0.3.0发布于 2026/10/27MITTypeScriptESM + CJS近 30 天下载 6222

TypeScript SDK for managing sandboxes through the Buddy API

原始使用文档
文档目录11 节
  1. Installation
  2. Usage
  3. Running commands
  4. Waiting for readiness
  5. Apps
  6. Fetching repositories and artifacts
  7. Updating a sandbox
  8. Snapshots
  9. Regions
  10. Connection overrides
  11. HTTP/2

Buddy Sandbox SDK

TypeScript SDK for managing Buddy sandboxes - isolated Ubuntu environments for running commands.

Installation

npm install @buddy-works/sandbox-sdk

Usage

import { Sandbox } from "@buddy-works/sandbox-sdk";

const identifier = "my-sandbox";

let sandbox: Sandbox;

try {
    sandbox = await Sandbox.getByIdentifier(identifier);
} catch {
    sandbox = await Sandbox.create({
        identifier,
        name: "My Sandbox",
        os: "ubuntu:24.04",
    });
}

await sandbox.start();

await sandbox.runCommand({
    command: "ping -c 5 buddy.works",
});

await sandbox.stop();

Set required environment variables:

export BUDDY_TOKEN="your-api-token"
export BUDDY_WORKSPACE="your-workspace"
export BUDDY_PROJECT="your-project"
export BUDDY_REGION="US"  # Optional: US (default), EU, or AS

Running commands

exec() runs a command and resolves when it finishes, with its exit code and output:

const result = await sandbox.exec({ command: "npm test" });

console.log(result.exit_code, result.stdout, result.stderr);

A command that exits non-zero still resolves, so check exit_code.

runtime picks the interpreter. It defaults to BASH, and also takes JAVASCRIPT, TYPESCRIPT or PYTHON:

await sandbox.exec({ command: "print(1 + 1)", runtime: "PYTHON" });

The request stays open while the command runs, and the API fails it after 60 seconds. Whatever it does, a command run this way leaves no entry in the sandbox command history, streams no logs, and cannot be terminated.

Use runCommand() for anything longer than a minute, to follow the output as it arrives, or to leave a command running after the call returns:

// streams to process.stdout as it runs, resolves once it finishes
const command = await sandbox.runCommand({ command: "npm test" });
console.log(command.data.status, command.data.exit_code);

// returns immediately, leaving the command running
const detached = await sandbox.runCommand({
    command: "npm run build",
    detached: true,
    output: null,
});

for await (const line of detached.logs({ follow: true })) {
    console.log(line);
}

const finished = await detached.wait(); // SUCCESSFUL, FAILED or CANCELLED

A detached command still streams to process.stdout by default, which keeps the Node process alive until the command ends - pass output: null to let it exit sooner.

The log of a command holds stdout and stderr interleaved, in the order the command wrote them: logs() yields its lines and output() returns it as one string. Use exec() when you need the two apart.

kill() cancels a running command and resolves once it has stopped, with the status CANCELLED in data, or after 30 seconds if it is slow to stop - then data.status is still INPROGRESS. listCommands() returns the sandbox's command history, newest first - the API keeps the 200 most recent.

wait(), logs() and output() take an AbortSignal that stops waiting, which gives a command a deadline:

try {
    await detached.wait({ signal: AbortSignal.timeout(5 * 60_000) });
} catch (error) {
    if (!(error instanceof DOMException && error.name === "TimeoutError")) {
        throw error;
    }
    await detached.kill(); // the signal only stops waiting, not the command
}

The API runs at most 3 commands at once per member in one sandbox, and 10 per workspace across sandboxes and targets. A command over either limit is rejected with HTTP 409, as is one that would exceed the 100 the API node runs at once; runCommand() then throws a BuddySDKError with the code CONCURRENCY_LIMIT, so you can wait for a running command to finish and try again.

A command may be up to 16 KB long and is stopped after 90 minutes, so run servers and other long-lived processes as apps. Its log is capped at 10 MiB, past which data.truncated is set.

Starting and cancelling commands needs the SANDBOX_USE token scope, which SANDBOX_WRITE includes.

Waiting for readiness

Sandbox.create() blocks until the sandbox has finished setup and reached RUNNING, so the instance it returns is ready to use. While waiting it polls the API on a backoff starting at 100ms and growing to 500ms.

Pass wait: false to skip the wait and drive it yourself. The instance you get back carries everything the create call returned - id, identifier, url, resources - but connection details are not settled yet.

const sandbox = await Sandbox.create({ identifier: "my-sandbox", wait: false });

// ... do other work while the sandbox boots ...

await sandbox.waitUntilReady();   // setup_status: SUCCESS
await sandbox.waitUntilRunning(); // status: RUNNING

The waiters accept an explicit interval, which pins polling to that fixed value instead of backing off:

await sandbox.waitUntilRunning(500); // check every 500ms

Apps

Sandboxes can run multiple apps simultaneously. Each app is a long-running process defined by a run command, an optional identifier (generated from the command when omitted) and an optional path it runs in (defaults to app_dir).

const sandbox = await Sandbox.create({
    identifier: "my-sandbox",
    name: "My Sandbox",
    os: "ubuntu:24.04",
    first_boot_commands: "apt-get update && apt-get install -y curl",
    apps: [
        { identifier: "api", command: "node server.js" },
        { identifier: "worker", command: "python worker.py", path: "/app/jobs" },
    ],
    timeout: 600, // auto-stop after 10 minutes of inactivity
});

// List apps
for (const app of sandbox.data.apps ?? []) {
    console.log(`${app.identifier}: "${app.command}" -> ${app.app_status}`);
}

// Control individual apps - by identifier or by the generated id
await sandbox.stopApp("api");
await sandbox.startApp("api");

const { logs } = await sandbox.getAppLogs("api");
console.log(logs);

Fetching repositories and artifacts

Use fetch to clone repositories or download artifacts into the sandbox on first boot. Each entry sets a type (PROJECT_REPO, PUBLIC_REPO, or ARTIFACT) plus the fields relevant to it.

await Sandbox.create({
    identifier: "my-sandbox",
    fetch: [
        {
            type: "PUBLIC_REPO",
            repository: "https://github.com/octocat/Hello-World",
            ref: "master",
            path: "/workspace/hello",
            build_command: "echo built",
        },
    ],
});

Updating a sandbox

Use sandbox.update() to change configuration after creation - timeout, apps, endpoints, variables, tags, etc. The internal state is updated with the API's response.

const sandbox = await Sandbox.getByIdentifier("my-sandbox");

await sandbox.update({
    timeout: 1200,
    tags: ["staging", "feature-x"],
});

Snapshots

Take point-in-time snapshots of a sandbox and restore from them later. Each snapshot is returned as a Snapshot instance with its own methods.

const sandbox = await Sandbox.getByIdentifier("my-sandbox");

// Create a snapshot. Returns immediately with status "CREATING".
const snapshot = await sandbox.createSnapshot({ name: "before-deploy" });

// Wait until the snapshot is "CREATED" before using it.
await snapshot.waitUntilReady();

// Create a new sandbox from the snapshot.
const restored = await Sandbox.createFromSnapshot(snapshot.id, {
    name: "restored-sandbox",
});

// List all snapshots for this sandbox.
const snapshots = await sandbox.listSnapshots();

// Delete a snapshot.
await snapshot.delete();

If you already have a snapshot ID from elsewhere (e.g. persisted in your own storage), get the entity directly:

const snapshot = await Sandbox.getSnapshotById(sandboxId, snapshotId);

Equivalent convenience methods on Sandbox are available when you only have an ID and don't want to fetch the Snapshot first:

await sandbox.waitForSnapshotReady(snapshotId);
await sandbox.deleteSnapshot(snapshotId);

Regions

Configure the API region:

# Via environment variable (recommended)
export BUDDY_REGION="EU"
// Or via connection config
const sandbox = await Sandbox.create({
    identifier: "my-sandbox",
    name: "My Sandbox",
    os: "ubuntu:24.04",
    connection: {
        region: "EU"  // US, EU, or AS
    }
});

Connection overrides

Override workspace/auth per call:

await Sandbox.create({
    identifier: "my-sandbox",
    name: "My Sandbox",
    os: "ubuntu:24.04",
    connection: {
        workspace: "different-workspace",
        project: "different-project",
        token: "custom-token",
        region: "EU"
    }
});

HTTP/2

The SDK sends API requests over HTTP/2, using a pool of 16 connections per process shared by all sandboxes. To change the pool size, or to use fetch (HTTP/1.1) instead:

export BUDDY_HTTP2_SESSIONS="32"  # 1-64, default 16
export BUDDY_HTTP2="0"            # use fetch instead

Set them before the first request. If a proxy variable is set (HTTPS_PROXY, HTTP_PROXY, ALL_PROXY), the SDK uses fetch: Node's http2 module never connects through a proxy, while fetch does when Node runs with --use-env-proxy or NODE_USE_ENV_PROXY=1.

版本与发布记录

@buddy-works/sandbox-sdk 共发布 25 个版本,首次发布于 2026/01/26;过去 12 个月发布了 25 个版本。

版本发布日期距今
0.3.0latest2026/10/2712 天前
0.2.02026/10/1426 天前
0.1.112026/10/0930 天前
0.1.102026/10/071 个月前
0.1.92026/09/091 个月前
0.1.82026/08/182 个月前

其他发布通道:rc → 0.1.8-rc.0。

关于 @buddy-works/sandbox-sdk 的常见问题

@buddy-works/sandbox-sdk 是什么?

TypeScript SDK for managing sandboxes through the Buddy API

如何安装 @buddy-works/sandbox-sdk?

在项目目录运行 npm install @buddy-works/sandbox-sdk,或使用 pnpm add / yarn add / bun add。

@buddy-works/sandbox-sdk 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@buddy-works/sandbox-sdk 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@buddy-works/sandbox-sdk 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js ^20.19.0 || >=22.12.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@buddy-works/sandbox-sdk 可以商用吗?

@buddy-works/sandbox-sdk 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@buddy-works/sandbox-sdk 的下载量有多少?

根据 npm 官方下载统计,@buddy-works/sandbox-sdk 在 2026/10/09 – 2026/11/07 这 30 天内被下载 6,222 次,日均约 207 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@buddy-works/sandbox-sdk 还在维护吗?

最新版本 0.3.0 发布于 2026/10/27(12 天前);至今共发布 25 个版本,其中过去 12 个月发布了 25 个;首次发布于 2026/01/26。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。