/ sdks / sandbox / javascript / README.md
README.md
  1  # Alibaba Sandbox SDK for JavaScript/TypeScript
  2  
  3  English | [中文](README_zh.md)
  4  
  5  A TypeScript/JavaScript SDK for low-level interaction with OpenSandbox. It provides the ability to create, manage, and interact with secure sandbox environments, including executing shell commands, managing files, and reading resource metrics.
  6  
  7  ## Installation
  8  
  9  ### npm
 10  
 11  ```bash
 12  npm install @alibaba-group/opensandbox
 13  ```
 14  
 15  ### pnpm
 16  
 17  ```bash
 18  pnpm add @alibaba-group/opensandbox
 19  ```
 20  
 21  ### yarn
 22  
 23  ```bash
 24  yarn add @alibaba-group/opensandbox
 25  ```
 26  
 27  ## Quick Start
 28  
 29  The following example shows how to create a sandbox and execute a shell command.
 30  
 31  > **Note**: Before running this example, ensure the OpenSandbox service is running. See the root [README.md](../../../README.md) for startup instructions.
 32  
 33  ```ts
 34  import { ConnectionConfig, Sandbox, SandboxException } from "@alibaba-group/opensandbox";
 35  
 36  const config = new ConnectionConfig({
 37    domain: "api.opensandbox.io",
 38    apiKey: "your-api-key",
 39    // protocol: "https",
 40    // requestTimeoutSeconds: 60,
 41  });
 42  
 43  try {
 44    const sandbox = await Sandbox.create({
 45      connectionConfig: config,
 46      image: "ubuntu",
 47      timeoutSeconds: 10 * 60,
 48    });
 49  
 50    const execution = await sandbox.commands.run("echo 'Hello Sandbox!'");
 51    console.log(execution.logs.stdout[0]?.text);
 52  
 53    // Optional but recommended: terminate the remote instance when you are done.
 54    await sandbox.kill();
 55    await sandbox.close();
 56  } catch (err) {
 57    if (err instanceof SandboxException) {
 58      console.error(
 59        `Sandbox Error: [${err.error.code}] ${err.error.message ?? ""}`,
 60      );
 61      console.error(`Request ID: ${err.requestId ?? "N/A"}`);
 62    } else {
 63      console.error(err);
 64    }
 65  }
 66  ```
 67  
 68  ## Usage Examples
 69  
 70  ### 1. Lifecycle Management
 71  
 72  Manage the sandbox lifecycle, including renewal, pausing, and resuming.
 73  
 74  ```ts
 75  const info = await sandbox.getInfo();
 76  console.log("State:", info.status.state);
 77  console.log("Created:", info.createdAt);
 78  console.log("Expires:", info.expiresAt); // null when manual cleanup mode is used
 79  
 80  await sandbox.pause();
 81  
 82  // Resume returns a fresh, connected Sandbox instance.
 83  const resumed = await sandbox.resume();
 84  
 85  // Renew: expiresAt = now + timeoutSeconds
 86  await resumed.renew(30 * 60);
 87  ```
 88  
 89  Create a non-expiring sandbox by passing `timeoutSeconds: null`:
 90  
 91  ```ts
 92  const manual = await Sandbox.create({
 93    connectionConfig: config,
 94    image: "ubuntu",
 95    timeoutSeconds: null,
 96  });
 97  ```
 98  
 99  ### 2. Custom Health Check
100  
101  Define custom logic to determine whether the sandbox is ready/healthy. This overrides the default ping check used during readiness checks.
102  
103  ```ts
104  const sandbox = await Sandbox.create({
105    connectionConfig: config,
106    image: "nginx:latest",
107    healthCheck: async (sbx) => {
108      // Example: consider the sandbox healthy when port 80 endpoint becomes available
109      const ep = await sbx.getEndpoint(80);
110      return !!ep.endpoint;
111    },
112  });
113  ```
114  
115  ### 3. Command Execution & Streaming
116  
117  Execute commands and handle output streams in real-time.
118  
119  ```ts
120  import type { ExecutionHandlers } from "@alibaba-group/opensandbox";
121  
122  const handlers: ExecutionHandlers = {
123    onStdout: (m) => console.log("STDOUT:", m.text),
124    onStderr: (m) => console.error("STDERR:", m.text),
125    onExecutionComplete: (c) =>
126      console.log("Finished in", c.executionTimeMs, "ms"),
127  };
128  
129  await sandbox.commands.run(
130    'for i in 1 2 3; do echo "Count $i"; sleep 0.2; done',
131    undefined,
132    handlers,
133  );
134  ```
135  
136  ### 4. Comprehensive File Operations
137  
138  Manage files and directories, including read, write, list/search, and delete.
139  
140  ```ts
141  await sandbox.files.createDirectories([{ path: "/tmp/demo", mode: 755 }]);
142  
143  await sandbox.files.writeFiles([
144    { path: "/tmp/demo/hello.txt", data: "Hello World", mode: 644 },
145  ]);
146  
147  const content = await sandbox.files.readFile("/tmp/demo/hello.txt");
148  console.log("Content:", content);
149  
150  const files = await sandbox.files.search({
151    path: "/tmp/demo",
152    pattern: "*.txt",
153  });
154  console.log(files.map((f) => f.path));
155  
156  await sandbox.files.deleteDirectories(["/tmp/demo"]);
157  ```
158  
159  ### 5. Endpoints
160  
161  `getEndpoint()` returns an endpoint **without a scheme** (for example `"localhost:44772"`). Use `getEndpointUrl()` if you want a ready-to-use absolute URL (for example `"http://localhost:44772"`).
162  
163  ```ts
164  const { endpoint } = await sandbox.getEndpoint(44772);
165  const url = await sandbox.getEndpointUrl(44772);
166  ```
167  
168  ### 6. Volume Mounts
169  
170  `volumes` supports `host`, `pvc`, and `ossfs` backends. Each volume must specify exactly one backend.
171  
172  ```ts
173  const sandbox = await Sandbox.create({
174    connectionConfig: config,
175    image: "ubuntu",
176    volumes: [
177      {
178        name: "oss-data",
179        ossfs: {
180          bucket: "bucket-a",
181          endpoint: "oss-cn-hangzhou.aliyuncs.com",
182          accessKeyId: process.env.OSS_ACCESS_KEY_ID!,
183          accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET!,
184          version: "2.0",
185        },
186        mountPath: "/mnt/oss",
187        subPath: "prefix",
188      },
189    ],
190  });
191  ```
192  
193  ### 7. Sandbox Management (Admin)
194  
195  Use `SandboxManager` for administrative tasks and finding existing sandboxes.
196  
197  ```ts
198  import { SandboxManager } from "@alibaba-group/opensandbox";
199  
200  const manager = SandboxManager.create({ connectionConfig: config });
201  const list = await manager.listSandboxInfos({
202    states: ["Running"],
203    pageSize: 10,
204  });
205  console.log(list.items.map((s) => s.id));
206  await manager.close();
207  ```
208  
209  ## Configuration
210  
211  ### 1. Connection Configuration
212  
213  The `ConnectionConfig` class manages API server connection settings.
214  
215  Runtime notes:
216  
217  - In browsers, the SDK uses the global `fetch` implementation.
218  - In Node.js, every `Sandbox` and `SandboxManager` clones the base `ConnectionConfig` via `withTransportIfMissing()`, so each instance gets an isolated `undici` keep-alive pool. Call `sandbox.close()` or `manager.close()` when you are done so the SDK can release the associated agent.
219  
220  | Parameter               | Description                                                                                                  | Default          | Environment Variable   |
221  | ----------------------- | ------------------------------------------------------------------------------------------------------------ | ---------------- | ---------------------- |
222  | `apiKey`                | API key for authentication                                                                                   | Optional         | `OPEN_SANDBOX_API_KEY` |
223  | `domain`                | Sandbox service domain (`host[:port]`)                                                                       | `localhost:8080` | `OPEN_SANDBOX_DOMAIN`  |
224  | `protocol`              | HTTP protocol (`http`/`https`)                                                                               | `http`           | -                      |
225  | `requestTimeoutSeconds` | Request timeout applied to SDK HTTP calls                                                                    | `30`             | -                      |
226  | `debug`                 | Enable basic HTTP debug logging                                                                              | `false`          | -                      |
227  | `headers`               | Extra headers applied to every request                                                                       | `{}`             | -                      |
228  | `useServerProxy`        | Use sandbox server as proxy for execd/endpoint requests (e.g. when client cannot reach the sandbox directly) | `false`          | -                      |
229  
230  ```ts
231  import { ConnectionConfig } from "@alibaba-group/opensandbox";
232  
233  // 1. Basic configuration
234  const config = new ConnectionConfig({
235    domain: "api.opensandbox.io",
236    apiKey: "your-key",
237    requestTimeoutSeconds: 60,
238  });
239  
240  // 2. Advanced: custom headers
241  const config2 = new ConnectionConfig({
242    domain: "api.opensandbox.io",
243    apiKey: "your-key",
244    headers: { "X-Custom-Header": "value" },
245  });
246  ```
247  
248  ### 2. Sandbox Creation Configuration
249  
250  `Sandbox.create()` allows configuring the sandbox environment.
251  
252  | Parameter                    | Description                                      | Default                      |
253  | ---------------------------- | ------------------------------------------------ | ---------------------------- |
254  | `image`                      | Docker image to use                              | Required                     |
255  | `timeoutSeconds`             | Automatic termination timeout (server-side TTL)  | 10 minutes                   |
256  | `entrypoint`                 | Container entrypoint command                     | `["tail","-f","/dev/null"]`  |
257  | `resource`                   | CPU and memory limits (string map)               | `{"cpu":"1","memory":"2Gi"}` |
258  | `env`                        | Environment variables                            | `{}`                         |
259  | `metadata`                   | Custom metadata tags                             | `{}`                         |
260  | `networkPolicy`              | Optional outbound network policy (egress)        | -                            |
261  | `extensions`                 | Extra server-defined fields                      | `{}`                         |
262  | `skipHealthCheck`            | Skip readiness checks (`Running` + health check) | `false`                      |
263  | `healthCheck`                | Custom readiness check                           | -                            |
264  | `readyTimeoutSeconds`        | Max time to wait for readiness                   | 30 seconds                   |
265  | `healthCheckPollingInterval` | Poll interval while waiting (milliseconds)       | 200 ms                       |
266  
267  Note: metadata keys under `opensandbox.io/` are reserved for system-managed
268  labels and will be rejected by the server.
269  
270  ```ts
271  const sandbox = await Sandbox.create({
272    connectionConfig: config,
273    image: "python:3.11",
274    networkPolicy: {
275      defaultAction: "deny",
276      egress: [{ action: "allow", target: "pypi.org" }],
277    },
278  });
279  ```
280  
281  ### 3. Runtime Egress Policy Updates
282  
283  Runtime egress reads and patches go directly to the sandbox egress sidecar.
284  The SDK first resolves the sandbox endpoint on port `18080`, then calls the sidecar `/policy` API.
285  
286  Patch uses merge semantics:
287  - Incoming rules take priority over existing rules with the same `target`.
288  - Existing rules for other targets remain unchanged.
289  - Within a single patch payload, the first rule for a `target` wins.
290  - The current `defaultAction` is preserved.
291  
292  ```ts
293  const policy = await sandbox.getEgressPolicy();
294  
295  await sandbox.patchEgressRules([
296    { action: "allow", target: "www.github.com" },
297    { action: "deny", target: "pypi.org" },
298  ]);
299  ```
300  
301  ### 4. Resource cleanup
302  
303  Both `Sandbox` and `SandboxManager` own a scoped HTTP agent when running on Node.js
304  so you can safely reuse the same `ConnectionConfig`. Once you are finished interacting
305  with the sandbox or administration APIs, call `sandbox.close()` / `manager.close()` to
306  release the underlying agent.
307  
308  ## Browser Notes
309  
310  - The SDK can run in browsers, but **streaming file uploads are Node-only**.
311  - If you pass `ReadableStream` or `AsyncIterable` for `writeFiles`, the browser will fall back to **buffering in memory** before upload.
312  - Reason: browsers do not support streaming `multipart/form-data` bodies with custom boundaries (required by the execd upload API).