@sandboxrouter/sdk 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -434
- package/dist/catalog.d.ts +13 -0
- package/dist/catalog.js +14 -0
- package/dist/cli.js +14 -6
- package/dist/client.d.ts +58 -0
- package/dist/client.js +140 -0
- package/dist/cost.d.ts +7 -0
- package/dist/cost.js +13 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/leaderboard.js +3 -2
- package/dist/results.d.ts +12 -0
- package/dist/results.js +49 -3
- package/dist/router.d.ts +8 -0
- package/dist/router.js +18 -6
- package/dist/server.d.ts +29 -2
- package/dist/server.js +32 -6
- package/dist/types.d.ts +10 -6
- package/dist/verified.d.ts +2 -0
- package/dist/verified.js +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
**One API for every sandbox.** A free, open-source router for the places an AI agent can run code: Agent37, Alibaba Cloud, AWS (Bedrock AgentCore and Lambda MicroVMs), Azure Container Apps, Beam, Blaxel, Boat, Cloudflare, CodeSandbox, Daytona, Dedalus Machines, Deno, E2B, exe.dev, Fly.io Sprites, Freestyle, HOPX, InstaBox, Koyeb, Maritime, Modal, Morph, Northflank, Novita, OpenScale, Runloop, Sail, Superserve, Tensorlake, Upstash Box and Vercel, plus Docker on your own machine. It picks a provider by price, speed or capability, and falls back to the next one when a provider is down or rate-limited.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
```bash
|
|
6
|
+
npm i @sandboxrouter/sdk # plus the SDK of each provider you use: see Install below
|
|
7
|
+
```
|
|
6
8
|
|
|
7
9
|
```ts
|
|
8
10
|
import { Router } from "@sandboxrouter/sdk";
|
|
@@ -19,85 +21,60 @@ try {
|
|
|
19
21
|
|
|
20
22
|
Runs as plain JavaScript on Node 20+. (In TypeScript 5.2+, or Node 24+, `await using sbx = ...` kills the sandbox for you.)
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
Switch providers by changing which keys are set, not your code (custom images are the exception: each provider names its own). If one returns a 429, the next one takes the sandbox.
|
|
24
|
+
Set the keys of the providers you use. `create()` starts the sandbox on the cheapest one that fits among those that passed the real-account check, then the untested ones, by public list price (ties alphabetical, and no provider pays for placement). If that provider is down, busy or out of credit, the next one takes it, and every attempt is on `sbx.attempts`. The sandbox stays on that provider until you `kill()` it.
|
|
25
25
|
|
|
26
|
-
Site: [sandboxrouter.vercel.app](https://sandboxrouter.vercel.app) · npm: [`@sandboxrouter/sdk`](https://www.npmjs.com/package/@sandboxrouter/sdk)
|
|
26
|
+
Site and docs: [sandboxrouter.vercel.app](https://sandboxrouter.vercel.app) · npm: [`@sandboxrouter/sdk`](https://www.npmjs.com/package/@sandboxrouter/sdk)
|
|
27
27
|
|
|
28
28
|
## Why
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
So every agent team either locks into one provider or writes its own adapter layer. sandboxrouter is that adapter layer, done once, in the open:
|
|
33
|
-
|
|
34
|
-
- **One interface.** `create`, `exec`, `writeFile`, `readFile`, `pause`, `kill`, the same on every provider. `exec` streams stdout and stderr as they happen.
|
|
35
|
-
- **Routing.** `cheapest` (the default: lowest list price for your size) or `fastest` (lowest cold start you measured with `bench`), or name providers yourself.
|
|
36
|
-
- **Fallback.** A failed create moves to the next candidate, and every attempt is recorded on `sbx.attempts`.
|
|
37
|
-
- **Capability filters.** Ask for a GPU and only GPU-capable providers are tried. Ask for 32 vCPU and providers that can't do it are skipped, with the reason.
|
|
38
|
-
- **Your keys.** Your accounts, your bills. Nothing is proxied through a third party.
|
|
39
|
-
- **HTTP server and Python client.** Run `sandboxrouter serve` and any language gets the same API.
|
|
40
|
-
- **Computers.** Linux and Windows desktops, Android phones and iPhone simulators for computer-use models, from 7 providers, routed by price per OS: actions, screenshots, and (where the provider has one) a link for a person to watch or take over.
|
|
41
|
-
- **Open benchmark.** `bench --share` produces a report; cold starts on the site come from reports submitted to [`results/`](results/).
|
|
42
|
-
|
|
43
|
-
## Fair by construction
|
|
30
|
+
AI agents need a disposable computer to run code a model wrote: install packages, run tests, do an RL rollout. Dozens of providers sell one, each with its own SDK and auth, and the same 2 vCPU / 4 GB sandbox costs from under a cent to over thirty cents an hour. sandboxrouter is one interface over all of them:
|
|
44
31
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
- Docker is never picked automatically, because it always "costs" $0 and isolates less than a cloud micro-VM. Name it to use it.
|
|
51
|
-
|
|
52
|
-
If a price is wrong, open a PR with the source link.
|
|
32
|
+
- **Same code everywhere.** `create`, `exec` (streamed), `writeFile`, `readFile`, `pause`, `kill`. Switch providers by changing which keys are set.
|
|
33
|
+
- **Tested first, then cheapest, with fallback.** Or `cheapest` for price alone, `fastest` by cold starts you measured, or a list of providers you name.
|
|
34
|
+
- **Your keys.** Your accounts and your bills; nothing is proxied through anyone else.
|
|
35
|
+
- **Cleanup.** A local ledger of every sandbox started, so `sandboxrouter gc` kills what a crashed script left behind.
|
|
36
|
+
- **TypeScript, Python, HTTP and a CLI.** Plus desktops and phones for computer-use models.
|
|
53
37
|
|
|
54
38
|
## Prices
|
|
55
39
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
|
66
|
-
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
|
|
|
75
|
-
|
|
|
76
|
-
|
|
|
77
|
-
|
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
|
|
|
81
|
-
|
|
|
82
|
-
|
|
|
83
|
-
|
|
|
84
|
-
|
|
|
85
|
-
|
|
|
86
|
-
|
|
|
87
|
-
|
|
|
88
|
-
|
|
|
89
|
-
|
|
|
90
|
-
|
|
|
91
|
-
|
|
|
92
|
-
|
|
|
93
|
-
|
|
|
94
|
-
| Dedalus Machines (sign-up waitlisted) | $0.149 | none | not run yet | Per second while awake; sleeping machines bill storage only (10 GiB included on Hobby, 20 on Pro); Hobby plan max 4 vCPU / 16 GiB; private beta | [pricing](https://docs.dedaluslabs.ai/dcs/dm/pricing) |
|
|
95
|
-
| OpenScale Sandboxes | skipped (default image is 2 vCPU / 1 GB; a bigger size needs image.openscale) | none | passed (2026-11-06) | Per second on allocated size; no platform fee or minimum; paused = free; size comes from the template (E2B-compatible API); Singapore only; needs a pay-as-you-go key | [pricing](https://openscale.so/sandboxes) |
|
|
96
|
-
| Azure Container Apps dynamic sessions | skipped (max 1 vCPU) | none | not run yet | Flat $0.03 per session-hour (built-in Shell pools), billed per allocated session in 1-hour increments, East US; needs a session pool you create once | [pricing](https://learn.microsoft.com/en-us/azure/container-apps/billing#dynamic-sessions) |
|
|
97
|
-
| Daytona | skipped (default image is 1 vCPU / 1 GB; a bigger size needs image.daytona) | none | failed 1 step (2026-11-06) | Per second, plus disk above 5 GiB | [pricing](https://www.daytona.io/pricing) |
|
|
98
|
-
| Alibaba Cloud Agent Sandbox | skipped (max 2 GB memory) | none | not run yet | Per second; Default QoS, Hong Kong and regions outside mainland China; disk separately $0.00025308/GiB-h; code-interpreter-v1 template is 2 vCPU / 2 GiB with 10 GiB disk (E2B-compatible API) | [pricing](https://www.alibabacloud.com/help/en/agent-sandbox/product-overview/billing-overview) |
|
|
99
|
-
| E2B | skipped (default image is 2 vCPU / 0.5 GB; a bigger size needs image.e2b) | none | passed (2026-11-06) | Per second; free Hobby tier ($100 one-time credit, 1-hour sessions, 20 at once); Pro $150/mo for longer sessions | [pricing](https://e2b.dev/pricing) |
|
|
100
|
-
| Docker (local) | free | none | passed (2026-11-06) | Runs on your own machine; free, but shares your kernel, so weaker isolation than a cloud micro-VM | [pricing](https://docs.docker.com/engine/) |
|
|
40
|
+
List price while running, 2 vCPU / 4 GB, cheapest first; waitlisted sign-ups follow. The router tries the ones that passed the real-account check first. "Up to" = CPU billed only while busy, shown with every core busy. "Not run yet" = built from the provider's docs and tested against mocks only. Click a provider for its billing details; more in [Reading the price table](https://sandboxrouter.vercel.app/docs#price-table).
|
|
41
|
+
|
|
42
|
+
| Provider | $/hr | Plan needed | Real-account check | Source |
|
|
43
|
+
|---|---|---|---|---|
|
|
44
|
+
| [Agent37](https://sandboxrouter.vercel.app/providers/agent37) | $0.007 | none | not run yet | [pricing](https://www.agent37.com/docs/agents-api/billing) |
|
|
45
|
+
| [Boat](https://sandboxrouter.vercel.app/providers/boat) | $0.018 | $20/mo (credited) | not run yet | [pricing](https://docs.boat.dev/pricing.md) |
|
|
46
|
+
| [Maritime](https://sandboxrouter.vercel.app/providers/maritime) | $0.022 | $20/mo (credited) | passed (2026-11-10) | [pricing](https://maritime.sh/pricing) |
|
|
47
|
+
| [Sailboxes (Sail Research)](https://sandboxrouter.vercel.app/providers/sail) | up to $0.062 | none | passed (2026-11-10) | [pricing](https://docs.sailresearch.com/sailboxes-pricing.md) |
|
|
48
|
+
| [Northflank](https://sandboxrouter.vercel.app/providers/northflank) | $0.067 | none | not run yet | [pricing](https://northflank.com/pricing) |
|
|
49
|
+
| [Morph Cloud](https://sandboxrouter.vercel.app/providers/morph) | $0.100 | none | not run yet | [pricing](https://cloud.morph.so/web/pricing) |
|
|
50
|
+
| [exe.dev](https://sandboxrouter.vercel.app/providers/exedev) | $0.105 | $15/mo | not run yet | [pricing](https://exe.dev/docs/standalone-vms.md) |
|
|
51
|
+
| [Novita AI Agent Sandbox](https://sandboxrouter.vercel.app/providers/novita) | $0.117 | none | not run yet | [pricing](https://docs.novita.ai/guides/sandbox-pricing) |
|
|
52
|
+
| [Freestyle](https://sandboxrouter.vercel.app/providers/freestyle) | $0.134 | none | not run yet | [pricing](https://www.freestyle.sh/docs/vms/pricing-and-limits) |
|
|
53
|
+
| [Beam](https://sandboxrouter.vercel.app/providers/beam) | $0.140 | $20/mo (credited) | not run yet | [pricing](https://www.beam.cloud/pricing) |
|
|
54
|
+
| [CodeSandbox SDK (Together)](https://sandboxrouter.vercel.app/providers/codesandbox) | $0.149 | none | not run yet | [pricing](https://docs.together.ai/docs/together-code-sandbox-legacy) |
|
|
55
|
+
| [Fly.io Sprites](https://sandboxrouter.vercel.app/providers/sprites) | up to $0.164 | none | not run yet | [pricing](https://fly.io/sprites) |
|
|
56
|
+
| [Blaxel](https://sandboxrouter.vercel.app/providers/blaxel) | $0.166 | none | passed (2026-11-10) | [pricing](https://blaxel.ai/pricing) |
|
|
57
|
+
| [HOPX](https://sandboxrouter.vercel.app/providers/hopx) | $0.166 | none | not run yet | [pricing](https://hopx.ai/pricing) |
|
|
58
|
+
| [Superserve](https://sandboxrouter.vercel.app/providers/superserve) | $0.166 | maybe $20/mo (credited) | not run yet | [pricing](https://superserve.ai/pricing) |
|
|
59
|
+
| [Tensorlake](https://sandboxrouter.vercel.app/providers/tensorlake) | up to $0.200 | none | not run yet | [pricing](https://www.tensorlake.ai/pricing) |
|
|
60
|
+
| [Upstash Box](https://sandboxrouter.vercel.app/providers/upstash) | up to $0.200 | none | not run yet | [pricing](https://upstash.com/pricing/box) |
|
|
61
|
+
| [Cloudflare Sandbox](https://sandboxrouter.vercel.app/providers/cloudflare) | up to $0.216 | $5/mo | not run yet | [pricing](https://developers.cloudflare.com/containers/platform/pricing/) |
|
|
62
|
+
| [Amazon Bedrock AgentCore Code Interpreter](https://sandboxrouter.vercel.app/providers/agentcore) | up to $0.217 | none | not run yet | [pricing](https://aws.amazon.com/bedrock/agentcore/pricing/) |
|
|
63
|
+
| [Modal](https://sandboxrouter.vercel.app/providers/modal) | up to $0.238 | none | not run yet | [pricing](https://modal.com/pricing) |
|
|
64
|
+
| [AWS Lambda MicroVMs](https://sandboxrouter.vercel.app/providers/lambda) | $0.252 | none | not run yet | [pricing](https://aws.amazon.com/lambda/pricing/) |
|
|
65
|
+
| [Deno Sandbox](https://sandboxrouter.vercel.app/providers/deno) | up to $0.300 | $20/mo | not run yet | [pricing](https://deno.com/sandbox) |
|
|
66
|
+
| [Runloop](https://sandboxrouter.vercel.app/providers/runloop) | $0.317 | none | not run yet | [pricing](https://www.runloop.ai/pricing) |
|
|
67
|
+
| [Vercel Sandbox](https://sandboxrouter.vercel.app/providers/vercel) | up to $0.341 | none | not run yet | [pricing](https://vercel.com/docs/vercel-sandbox/pricing) |
|
|
68
|
+
| [InstaBox (sign-up waitlisted)](https://sandboxrouter.vercel.app/providers/instabox) | $0.017 | none | not run yet | [pricing](https://instabox.sh/docs/pricing.md) |
|
|
69
|
+
| [Koyeb Sandboxes (not taking new accounts)](https://sandboxrouter.vercel.app/providers/koyeb) | $0.058 | $29/mo | not run yet | [pricing](https://www.koyeb.com/docs/reference/instances) |
|
|
70
|
+
| [Dedalus Machines (sign-up waitlisted)](https://sandboxrouter.vercel.app/providers/dedalus) | $0.149 | none | not run yet | [pricing](https://docs.dedaluslabs.ai/dcs/dm/pricing) |
|
|
71
|
+
| [LangSmith Sandboxes (sign-up waitlisted)](https://sandboxrouter.vercel.app/providers/langsmith) | $0.189 | none | not run yet | [pricing](https://www.langchain.com/pricing) |
|
|
72
|
+
| [OpenScale Sandboxes](https://sandboxrouter.vercel.app/providers/openscale) | skipped (default image is 2 vCPU / 1 GB; a bigger size needs image.openscale) | none | failed 1 step (2026-11-10) | [pricing](https://openscale.so/sandboxes) |
|
|
73
|
+
| [Azure Container Apps dynamic sessions](https://sandboxrouter.vercel.app/providers/azure) | skipped (max 1 vCPU) | none | not run yet | [pricing](https://learn.microsoft.com/en-us/azure/container-apps/billing#dynamic-sessions) |
|
|
74
|
+
| [Daytona](https://sandboxrouter.vercel.app/providers/daytona) | skipped (default image is 1 vCPU / 1 GB; a bigger size needs image.daytona) | none | passed (2026-11-10) | [pricing](https://www.daytona.io/pricing) |
|
|
75
|
+
| [Alibaba Cloud Agent Sandbox](https://sandboxrouter.vercel.app/providers/alibaba) | skipped (max 2 GB memory) | none | not run yet | [pricing](https://www.alibabacloud.com/help/en/agent-sandbox/product-overview/billing-overview) |
|
|
76
|
+
| [E2B](https://sandboxrouter.vercel.app/providers/e2b) | skipped (default image is 2 vCPU / 0.5 GB; a bigger size needs image.e2b) | none | passed (2026-11-10) | [pricing](https://e2b.dev/pricing) |
|
|
77
|
+
| [Docker (local)](https://sandboxrouter.vercel.app/providers/docker) | free | none | passed (2026-11-09) | [pricing](https://docs.docker.com/engine/) |
|
|
101
78
|
|
|
102
79
|
Checked 2026-11-04 to 2026-11-06.
|
|
103
80
|
|
|
@@ -105,369 +82,13 @@ Checked 2026-11-04 to 2026-11-06.
|
|
|
105
82
|
|
|
106
83
|
```bash
|
|
107
84
|
npm i @sandboxrouter/sdk
|
|
108
|
-
# then the SDK of each provider you use (Agent37, Azure, AgentCore, Boat, Cloudflare, Dedalus, exe.dev, Freestyle, InstaBox, Koyeb,
|
|
109
|
-
# Lambda, Maritime, Sail, Superserve, Upstash and Docker need none; the AWS ones need the signing packages below):
|
|
110
|
-
npm i e2b # E2B (also Alibaba Cloud and OpenScale)
|
|
111
|
-
npm i @daytonaio/sdk # Daytona
|
|
112
|
-
npm i modal # Modal
|
|
113
|
-
npm i @vercel/sandbox # Vercel Sandbox
|
|
114
|
-
npm i morphcloud # Morph Cloud
|
|
115
|
-
npm i @fly/sprites # Fly.io Sprites (its package asks for Node 24+)
|
|
116
|
-
npm i @blaxel/core # Blaxel
|
|
117
|
-
npm i @tensorlakeai/tensorlake # Tensorlake
|
|
118
|
-
npm i @runloop/api-client # Runloop
|
|
119
|
-
npm i @hopx-ai/sdk # HOPX
|
|
120
|
-
npm i @northflank/js-client # Northflank
|
|
121
|
-
npm i @beamcloud/beam-js # Beam
|
|
122
|
-
npm i @codesandbox/sdk # CodeSandbox
|
|
123
|
-
npm i @deno/sandbox # Deno Sandbox
|
|
124
|
-
npm i novita-sandbox # Novita
|
|
125
|
-
# Alibaba Cloud uses the e2b package
|
|
126
|
-
npm i @smithy/signature-v4 @aws-crypto/sha256-js @aws-sdk/credential-provider-node # AgentCore, Lambda MicroVMs
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
Cloudflare sandboxes only run inside a Cloudflare Worker, so you deploy a small bridge Worker to your own account first: see [`bridges/cloudflare`](bridges/cloudflare).
|
|
130
|
-
|
|
131
|
-
Three providers need a one-time setup in your cloud account, described at the top of each adapter: Azure needs a Shell session pool ([`src/providers/azure.ts`](src/providers/azure.ts)), Lambda MicroVMs need an image built from the bundled server ([`src/providers/lambda.ts`](src/providers/lambda.ts), `writeLambdaMicrovmBundle()`), and AgentCore is opt-in: set `AGENTCORE_CODE_INTERPRETER_ID=aws.codeinterpreter.v1` so plain AWS credentials don't route work there by surprise.
|
|
132
|
-
|
|
133
|
-
Set the keys for the providers you want. Providers without keys are skipped.
|
|
134
|
-
|
|
135
|
-
| Provider | Env |
|
|
136
|
-
|---|---|
|
|
137
|
-
| Agent37 | `AGENT37_API_KEY` |
|
|
138
|
-
| Alibaba Cloud Agent Sandbox | `ALIBABA_SANDBOX_API_KEY` |
|
|
139
|
-
| Amazon Bedrock AgentCore Code Interpreter | `AGENTCORE_CODE_INTERPRETER_ID` + `AWS_REGION` + `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY`, or `AGENTCORE_CODE_INTERPRETER_ID` + `AWS_REGION` + `AWS_PROFILE` |
|
|
140
|
-
| AWS Lambda MicroVMs | `AWS_REGION` + `LAMBDA_MICROVM_IMAGE` + `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY`, or `AWS_REGION` + `LAMBDA_MICROVM_IMAGE` + `AWS_PROFILE` |
|
|
141
|
-
| Azure Container Apps dynamic sessions | `AZURE_SESSIONS_POOL_ENDPOINT` + `AZURE_SESSIONS_TOKEN`, or `AZURE_SESSIONS_POOL_ENDPOINT` + `AZURE_TENANT_ID` + `AZURE_CLIENT_ID` + `AZURE_CLIENT_SECRET` |
|
|
142
|
-
| Beam | `BEAM_TOKEN` + `BEAM_WORKSPACE_ID` |
|
|
143
|
-
| Blaxel | `BL_API_KEY` + `BL_WORKSPACE` |
|
|
144
|
-
| Boat | `BOAT_API_KEY` |
|
|
145
|
-
| Cloudflare Sandbox | `CLOUDFLARE_SANDBOX_URL` + `CLOUDFLARE_SANDBOX_TOKEN` |
|
|
146
|
-
| CodeSandbox SDK (Together) | `CSB_API_KEY` |
|
|
147
|
-
| Daytona | `DAYTONA_API_KEY` |
|
|
148
|
-
| Dedalus Machines | `DEDALUS_API_KEY` |
|
|
149
|
-
| Deno Sandbox | `DENO_DEPLOY_TOKEN` |
|
|
150
|
-
| Docker (local) | none: Docker running locally |
|
|
151
|
-
| E2B | `E2B_API_KEY` |
|
|
152
|
-
| exe.dev | `EXE_TOKEN` |
|
|
153
|
-
| Fly.io Sprites | `SPRITES_TOKEN` |
|
|
154
|
-
| Freestyle | `FREESTYLE_API_KEY` |
|
|
155
|
-
| HOPX | `HOPX_API_KEY` |
|
|
156
|
-
| InstaBox | `INSTABOX_API_KEY` |
|
|
157
|
-
| Koyeb Sandboxes | `KOYEB_API_TOKEN` |
|
|
158
|
-
| LangSmith Sandboxes | `LANGSMITH_API_KEY` |
|
|
159
|
-
| Maritime | `MARITIME_API_KEY` |
|
|
160
|
-
| Modal | `MODAL_TOKEN_ID` + `MODAL_TOKEN_SECRET` |
|
|
161
|
-
| Morph Cloud | `MORPH_API_KEY` |
|
|
162
|
-
| Northflank | `NORTHFLANK_API_TOKEN` + `NORTHFLANK_PROJECT_ID` |
|
|
163
|
-
| Novita AI Agent Sandbox | `NOVITA_API_KEY` |
|
|
164
|
-
| OpenScale Sandboxes | `OPENSCALE_API_KEY` |
|
|
165
|
-
| Runloop | `RUNLOOP_API_KEY` |
|
|
166
|
-
| Sailboxes (Sail Research) | `SAIL_API_KEY` |
|
|
167
|
-
| Superserve | `SUPERSERVE_API_KEY` |
|
|
168
|
-
| Tensorlake | `TENSORLAKE_API_KEY` |
|
|
169
|
-
| Upstash Box | `UPSTASH_BOX_API_KEY` |
|
|
170
|
-
| Vercel Sandbox | `VERCEL_OIDC_TOKEN`, or `VERCEL_TOKEN` + `VERCEL_TEAM_ID` + `VERCEL_PROJECT_ID` |
|
|
171
|
-
|
|
172
|
-
## Library
|
|
173
|
-
|
|
174
|
-
```ts
|
|
175
|
-
const router = new Router();
|
|
176
|
-
|
|
177
|
-
// Cheapest provider that can do 8 vCPU / 16 GB, falling back on failure
|
|
178
|
-
const a = await router.create({ vcpu: 8, memoryGb: 16 }, { strategy: "cheapest" });
|
|
179
|
-
|
|
180
|
-
// GPU: only GPU-capable providers are tried (Modal today)
|
|
181
|
-
const b = await router.create({ gpu: "A10G", image: { modal: "pytorch/pytorch:latest" } });
|
|
182
|
-
|
|
183
|
-
// Pin providers in order and turn fallback off
|
|
184
|
-
const c = await router.create({}, { provider: ["e2b", "daytona"], fallback: false });
|
|
185
|
-
|
|
186
|
-
// Fallback moves on when a provider is down: a timeout, a network error, 429 or 5xx, and a create
|
|
187
|
-
// that takes longer than createTimeoutSeconds (default 120; a sandbox that turns up later is
|
|
188
|
-
// killed, or left in the ledger for `gc`). A setup error (400, 401, 402, 403, 404, 422: a bad key,
|
|
189
|
-
// no billing, a bad image) stops instead, so a mistake isn't hidden by a pricier provider.
|
|
190
|
-
// fallback: "any" falls back on every error.
|
|
191
|
-
const e = await router.create({}, { fallback: "any", createTimeoutSeconds: 300 });
|
|
192
|
-
|
|
193
|
-
// Per-provider images: each provider names its templates differently
|
|
194
|
-
const d = await router.create({ image: { e2b: "code-interpreter-v1", modal: "python:3.13-slim" } });
|
|
195
|
-
|
|
196
|
-
// Files
|
|
197
|
-
await a.writeFile("/tmp/data.csv", "x,y\n1,2\n");
|
|
198
|
-
console.log(await a.readText("/tmp/data.csv"));
|
|
199
|
-
|
|
200
|
-
// Stream output as it happens (the full output is still returned)
|
|
201
|
-
await a.exec("npm test", { onStdout: (c) => process.stdout.write(c), onStderr: (c) => process.stderr.write(c) });
|
|
202
|
-
|
|
203
|
-
// Pause keeps the disk (and memory, where the provider snapshots it); router.get() resumes
|
|
204
|
-
const p = await router.create({}, { require: ["pause"] });
|
|
205
|
-
await p.pause();
|
|
206
|
-
|
|
207
|
-
// Ids are portable across processes: "provider:nativeId"
|
|
208
|
-
const again = await router.get(a.id);
|
|
209
|
-
|
|
210
|
-
// Dry run: who would be tried, in what order, and why anything is skipped
|
|
211
|
-
console.table(router.plan({ vcpu: 32 }));
|
|
212
|
-
|
|
213
|
-
await a.kill();
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
`create` throws a `RouterError` whose `attempts` lists every provider it tried and why each failed.
|
|
217
|
-
|
|
218
|
-
## Computers (desktops and phones)
|
|
219
|
-
|
|
220
|
-
For computer-use agents: a machine with a screen that your model drives with clicks and keys. `router.computer({ os })` starts one on the cheapest provider for that OS among those you have keys for, ties alphabetical, with fallback. Actions use the Anthropic computer-use shape on every provider.
|
|
221
|
-
|
|
222
|
-
| Provider | Linux | Windows | Android | iOS | Default size | Live view |
|
|
223
|
-
|---|---|---|---|---|---|---|
|
|
224
|
-
| AWS WorkSpaces (agent access) | $0.138 | $0.150 | | | 2 vCPU · 4 GB | watch only |
|
|
225
|
-
| Daytona | $0.067 | | | | 1 vCPU · 1 GB | watch/control link |
|
|
226
|
-
| E2B Desktop | $0.533 | | | | 8 vCPU · 8 GB | watch/control link |
|
|
227
|
-
| Limrun | | | $3.00 | $6.00 | phone emulator / iPhone simulator | interactive link |
|
|
228
|
-
| Maritime | $0.120 | $0.180 | $0.150 | | 2 vCPU · 4 GB | view-only and takeover links |
|
|
229
|
-
| Mobilerun | | | $1.80 | | cloud phone | embedded player |
|
|
230
|
-
| Orgo | $0.055 | plan | | | 2 vCPU · 8 GB | VNC (full control) |
|
|
231
|
-
|
|
232
|
-
Per hour while awake, from each provider's pricing page (sources in [`src/computers.ts`](src/computers.ts)). Sizes differ, so compare price and size together. Only Maritime and AWS enforce view-only on the server; on the others a "watch" link can be turned into control, so treat it like a password. Mobilerun and AWS have no shell; Limrun's iOS has no shell. AWS needs a fleet and stack you provision once (steps at the top of [`src/providers/workspaces.ts`](src/providers/workspaces.ts)).
|
|
233
|
-
|
|
234
|
-
```ts
|
|
235
|
-
const desk = await router.computer({ os: "linux", externalUserId: "user_42" }); // same id, same desktop
|
|
236
|
-
const shot = await desk.act({ action: "screenshot" }); // { frameId, width, height, imageB64 }
|
|
237
|
-
await desk.act({ action: "left_click", coordinate: [412, 230] });
|
|
238
|
-
await desk.actBatch([{ action: "type", text: "hello" }, { action: "key", text: "Return" }]);
|
|
239
|
-
await desk.exec("ls /data");
|
|
240
|
-
|
|
241
|
-
// Hit a login or CAPTCHA? Hand the desktop to a person, then carry on.
|
|
242
|
-
const { url } = await desk.viewer("control", { reason: "Please sign in" });
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
```bash
|
|
246
|
-
npx @sandboxrouter/sdk desktop --os linux --user user_42 --control # prints a takeover link
|
|
247
|
-
npx @sandboxrouter/sdk screenshot <provider>:<id> --out screen.png
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
## CLI
|
|
251
|
-
|
|
252
|
-
```bash
|
|
253
|
-
npx @sandboxrouter/sdk providers # price table + which keys are set
|
|
254
|
-
npx @sandboxrouter/sdk route --gpu T4 # dry run
|
|
255
|
-
npx @sandboxrouter/sdk run "uname -a && nproc" # start, run, print, kill
|
|
256
|
-
npx @sandboxrouter/sdk run "pytest -q" --provider e2b,daytona --keep
|
|
257
|
-
npx @sandboxrouter/sdk bench --runs 5 # cold start + exec latency table
|
|
258
|
-
npx @sandboxrouter/sdk bench --share --label "us-east-1" # also write a report for results/
|
|
259
|
-
npx @sandboxrouter/sdk leaderboard results --out leaderboard.html
|
|
260
|
-
npx @sandboxrouter/sdk serve --port 8787 # HTTP API
|
|
261
|
-
npx @sandboxrouter/sdk ls # sandboxes started here that were never killed
|
|
262
|
-
npx @sandboxrouter/sdk gc # kill the ones past their lifetime (--all: every one)
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
### Cleaning up leaks
|
|
266
|
-
|
|
267
|
-
If a script crashes before `kill()`, its sandbox keeps billing. Every router keeps a ledger of the sandboxes it starts in `~/.sandboxrouter/sandboxes.jsonl` (set `SANDBOXROUTER_HOME` to move it, `SANDBOXROUTER_LEDGER=off` or `new Router({ ledger: false })` to turn it off), and `kill()` closes the entry.
|
|
268
|
-
|
|
269
|
-
```bash
|
|
270
|
-
sandboxrouter ls # id, age, lifetime left, cost so far
|
|
271
|
-
sandboxrouter gc --dry-run # what would be killed
|
|
272
|
-
sandboxrouter gc # kill sandboxes past their requested lifetime
|
|
273
|
-
sandboxrouter gc --all # kill every open one (end of a CI job)
|
|
274
|
-
sandboxrouter gc --older-than 60 # kill those up for an hour or more
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
A sandbox the provider already stopped is just dropped from the ledger. `sandboxrouter run` kills its sandbox on Ctrl-C. The ledger only knows sandboxes started on this machine (or wherever `SANDBOXROUTER_HOME` points), so run `gc --all` at the end of CI jobs. Every adapter hands `timeoutMinutes` to the provider, which stops, deletes or pauses the sandbox when it runs out (the table under "How each adapter maps the spec" says which), except Fly.io Sprites, which has none (an idle sprite stops billing CPU but keeps its storage), and exe.dev, where the VM powers itself off when the lifetime runs out (best effort; a stopped VM bills disk until it's deleted).
|
|
278
|
-
|
|
279
|
-
### Bench
|
|
280
|
-
|
|
281
|
-
`bench` starts real sandboxes on every configured provider, measures time to a usable sandbox and the round trip of `echo ok`, kills them, and prints a markdown leaderboard. Results are saved to `~/.sandboxrouter/bench.json`, which is what `strategy: "fastest"` routes on. Latency is measured from wherever you run it, so network distance counts.
|
|
282
|
-
|
|
283
|
-
`--share` writes a JSON report (timings, your label, Node version and platform; never keys, env or error text), and `--by` adds your GitHub username so the website can credit you. Open a PR adding it to [`results/`](results/); CI checks every result file for keys, addresses and impossible numbers (`npm run results:check`), see [Sending results](CONTRIBUTING.md#sending-results). `sandboxrouter leaderboard` builds one static HTML page from every report there, taking the median of each submission's medians so one submitter can't dominate. Host the page anywhere static files go.
|
|
284
|
-
|
|
285
|
-
### Real cost
|
|
286
|
-
|
|
287
|
-
Prices in the catalog are list prices. `cost` checks them against real bills: it starts one sandbox per provider at a known size, records the size the sandbox really has (`nproc` and `MemTotal`), keeps every core busy for a fixed time (some providers bill active CPU only), kills it, and predicts the cost from list price × wall time. `cost settle` then reads what the provider charged for that sandbox, from Daytona's usage analytics, Blaxel's metrics API, Maritime's metered usage and Sail's spend API. E2B and OpenScale have no billing API, so their bill is read from the dashboard. Bills post late on some providers (Daytona can take up to 48 h), so rerun `settle` until every row has one.
|
|
288
|
-
|
|
289
|
-
```bash
|
|
290
|
-
sandboxrouter cost run --minutes 10 # every configured cloud provider, 2 vCPU / 4 GB
|
|
291
|
-
sandboxrouter cost settle results/cost/cost-2026-11-06-16-58.json
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
### Compatibility
|
|
295
|
-
|
|
296
|
-
`check` runs the same 14 steps against a real account: create; stdout, stderr and exit code; env and cwd; UTF-8 output; live streaming; a binary file round-trip; a 1 MB file; reading a missing file is an error; files written by `writeFile` are visible to commands and the other way round; a command timeout rejects with the contract's error; the timed-out command was really stopped; pause and resume; kill; and the killed sandbox is really gone.
|
|
297
|
-
|
|
298
|
-
```bash
|
|
299
|
-
sandboxrouter check --provider koyeb # one provider
|
|
300
|
-
sandboxrouter check --all --save # every configured provider; writes results/conformance/<id>.json
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
It prints one line per step and exits 1 if any fails. `checkProvider(router, id)` does the same from code. Saved runs make this table (`node --import tsx scripts/readme-tables.ts --write`), and a nightly workflow re-runs it with the repo's provider keys, committing only when a result changes (so the date is when the current result was first seen). Every adapter also has mock-API tests in `npm test`; this table is only real runs.
|
|
304
|
-
|
|
305
|
-
| Provider | Real-account check (14 steps) | Result since |
|
|
306
|
-
|---|---|---|
|
|
307
|
-
| Agent37 | not run yet | |
|
|
308
|
-
| Boat | not run yet | |
|
|
309
|
-
| Maritime | 14/14 passed | 2026-11-06, v0.2.0 |
|
|
310
|
-
| Sailboxes (Sail Research) | 14/14 passed | 2026-11-06, v0.3.0 |
|
|
311
|
-
| Northflank | not run yet | |
|
|
312
|
-
| Morph Cloud | not run yet | |
|
|
313
|
-
| exe.dev | not run yet | |
|
|
314
|
-
| Novita AI Agent Sandbox | not run yet | |
|
|
315
|
-
| Freestyle | not run yet | |
|
|
316
|
-
| Beam | not run yet | |
|
|
317
|
-
| CodeSandbox SDK (Together) | not run yet | |
|
|
318
|
-
| Fly.io Sprites | not run yet | |
|
|
319
|
-
| Blaxel | 13/13 passed; n/a: pause and resume | 2026-11-06, v0.2.0 |
|
|
320
|
-
| HOPX | not run yet | |
|
|
321
|
-
| Superserve | not run yet | |
|
|
322
|
-
| LangSmith Sandboxes | not run yet | |
|
|
323
|
-
| Tensorlake | not run yet | |
|
|
324
|
-
| Upstash Box | not run yet | |
|
|
325
|
-
| Cloudflare Sandbox | not run yet | |
|
|
326
|
-
| Amazon Bedrock AgentCore Code Interpreter | not run yet | |
|
|
327
|
-
| Modal | not run yet | |
|
|
328
|
-
| AWS Lambda MicroVMs | not run yet | |
|
|
329
|
-
| Deno Sandbox | not run yet | |
|
|
330
|
-
| Runloop | not run yet | |
|
|
331
|
-
| Vercel Sandbox | not run yet | |
|
|
332
|
-
| InstaBox | not run yet | |
|
|
333
|
-
| Koyeb Sandboxes | not run yet | |
|
|
334
|
-
| Dedalus Machines | not run yet | |
|
|
335
|
-
| OpenScale Sandboxes | 14/14 passed | 2026-11-06, v0.2.0 |
|
|
336
|
-
| Azure Container Apps dynamic sessions | not run yet | |
|
|
337
|
-
| Daytona | 0/14 passed; failed: create | 2026-11-06, v0.2.0 |
|
|
338
|
-
| Alibaba Cloud Agent Sandbox | not run yet | |
|
|
339
|
-
| E2B | 14/14 passed | 2026-11-06, v0.2.0 |
|
|
340
|
-
| Docker (local) | 14/14 passed | 2026-11-06, v0.2.0 |
|
|
341
|
-
|
|
342
|
-
## HTTP API
|
|
343
|
-
|
|
344
|
-
```bash
|
|
345
|
-
SANDBOXROUTER_TOKEN=secret npx @sandboxrouter/sdk serve
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
| Method | Path | Body |
|
|
349
|
-
|---|---|---|
|
|
350
|
-
| `GET` | `/v1/providers` | |
|
|
351
|
-
| `POST` | `/v1/route` | same as create; returns the candidate order |
|
|
352
|
-
| `POST` | `/v1/sandboxes` | `{ vcpu, memoryGb, gpu, env, timeoutMinutes, image, metadata, provider, strategy, fallback, createTimeoutSeconds, require }` |
|
|
353
|
-
| `POST` | `/v1/sandboxes/:id/exec` | `{ cmd, cwd, env, timeoutSeconds, stream }`. With `stream: true` the answer is `text/event-stream`: `stdout` and `stderr` events, then `result` |
|
|
354
|
-
| `POST` | `/v1/sandboxes/:id/pause` | |
|
|
355
|
-
| `PUT` | `/v1/sandboxes/:id/files?path=` | raw bytes |
|
|
356
|
-
| `GET` | `/v1/sandboxes/:id/files?path=` | |
|
|
357
|
-
| `DELETE` | `/v1/sandboxes/:id` | |
|
|
358
|
-
| `POST` | `/v1/computers` | `{ os, externalUserId, name, screen, arch, provider }` |
|
|
359
|
-
| `POST` | `/v1/computers/:id/actions` | one action, or `{ actions: [...] }` (up to 50) |
|
|
360
|
-
| `GET` | `/v1/computers/:id/screenshot` | image bytes (`?format=jpeg&quality=80`) |
|
|
361
|
-
| `POST` | `/v1/computers/:id/exec` | `{ command, timeoutSeconds }` (max 30 s) |
|
|
362
|
-
| `PUT` / `GET` | `/v1/computers/:id/files?path=` | raw bytes |
|
|
363
|
-
| `POST` | `/v1/computers/:id/viewer` | `{ mode: "watch" \| "control", ttlSeconds, reason }` |
|
|
364
|
-
| `POST` | `/v1/computers/:id/sleep` | |
|
|
365
|
-
| `DELETE` | `/v1/computers/:id` | |
|
|
366
|
-
|
|
367
|
-
```bash
|
|
368
|
-
curl -s localhost:8787/v1/sandboxes -H "Authorization: Bearer secret" \
|
|
369
|
-
-d '{"vcpu":2,"strategy":"cheapest"}'
|
|
370
|
-
# {"id":"e2b:iz5sb5a4bt3pmjx7c5ms2","provider":"e2b","estimatedHourlyUsd":0.1656,"attempts":[...]}
|
|
371
|
-
```
|
|
372
|
-
|
|
373
|
-
Provider keys stay on the server. Clients only hold `SANDBOXROUTER_TOKEN`. It binds to `127.0.0.1` by default. Without a token it only starts on a loopback address with CORS off: `--cors` (any browser page could otherwise start sandboxes on your keys) and a public `--host` both need `SANDBOXROUTER_TOKEN`.
|
|
374
|
-
|
|
375
|
-
### Python
|
|
376
|
-
|
|
377
|
-
A standard-library client for the HTTP API lives in [`python/`](python/):
|
|
378
|
-
|
|
379
|
-
```python
|
|
380
|
-
from sandboxrouter import Client
|
|
381
|
-
|
|
382
|
-
sr = Client("http://127.0.0.1:8787", token="secret")
|
|
383
|
-
with sr.create(strategy="cheapest") as sbx:
|
|
384
|
-
sbx.exec("pip install rich && python -c 'import rich'", on_stdout=lambda c: print(c, end=""))
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
## How each adapter maps the spec
|
|
388
|
-
|
|
389
|
-
| | vCPU / memory | Lifetime | Image | Pause | Notes |
|
|
390
|
-
|---|---|---|---|---|---|
|
|
391
|
-
| Maritime | `vcpu`, `memoryMib` | `timeLimitMinutes` (5 to 1,440) | `from` | yes | Plain REST, no SDK. Streams over SSE. Any data-plane call resumes a paused sandbox. |
|
|
392
|
-
| E2B | set by the template | `timeoutMs` | template | yes (memory + disk) | Nonzero exits are returned as results, not thrown. |
|
|
393
|
-
| Daytona | only with `image.daytona` | `autoStopInterval` | `image` | yes (stop/start, disk only) | Non-streaming exec merges stdout and stderr; streaming uses a session and keeps them apart. |
|
|
394
|
-
| Modal | `cpu`, `memoryMiB`, `gpu` | `timeoutMs` | registry tag | no | Uses Modal app `sandboxrouter` (override with `SANDBOXROUTER_MODAL_APP`). |
|
|
395
|
-
| Vercel | `vcpus` (2 GB each) | `timeout` | `runtime` | no | Max 8 vCPU. |
|
|
396
|
-
| Morph | via a cached base snapshot per shape | `ttlSeconds` | snapshot id | yes (memory + disk) | First create of a new shape builds a snapshot. Files over SSH. |
|
|
397
|
-
| Fly.io Sprites | `cpus`, `ramMB` (may be ignored) | none: `kill()` or `gc` deletes | no | no | Sprites idle for free on their own. |
|
|
398
|
-
| Blaxel | memory (CPU follows) | `ttl` | image | no (archiving is a per-workspace preview) | Idle sandboxes go to free standby on their own. |
|
|
399
|
-
| Tensorlake | `cpus`, `memoryMb` | idle timeout | image | yes (memory + disk) | Output is line-based. |
|
|
400
|
-
| Runloop | snapped to Runloop's custom sizes | `keep_alive_time_seconds` (max 48 h) | blueprint | yes (Pro plan) | Commands start in the home directory. |
|
|
401
|
-
| HOPX | set by the template | timeout | template | yes (memory + disk) | Size requests aren't applied, so the router only sends default-size work. |
|
|
402
|
-
| Northflank | cheapest `nf-compute` plan that fits | `expiryTime` (5 min to 7 days) | any public image | no | Runs as a deployment service; files move as base64 over exec. |
|
|
403
|
-
| Cloudflare | smallest instance type that fits | alarm in the bridge Worker | Dockerfile in the bridge | no | Through the bridge Worker you deploy ([`bridges/cloudflare`](bridges/cloudflare)). |
|
|
404
|
-
| Boat | smallest of 4 fixed sizes that fits | auto-stop timer | saved snapshot | yes (stop: disk kept, processes end) | Plain REST. Commands that wait are capped at 600 s; files API up to 5 MiB. |
|
|
405
|
-
| exe.dev | `--cpu`, `--memory` (2 GiB per vCPU max) | the VM powers itself off (best effort); `kill()` or `gc` deletes | `--image` | no | Plain REST over exe.dev's `/exec`; long commands run detached and are polled. |
|
|
406
|
-
| Agent37 | smallest of 4 fixed shapes that fits (max 4 vCPU) | auto-sleep after idle (5 min to 24 h) | template (`agent37-base` default) | yes (sleep: memory + disk) | Plain REST; no streaming; files move as base64 over exec in chunks. |
|
|
407
|
-
| Alibaba Cloud | set by the template | `timeoutMs` (max 24 h) | template | no (allowlist only) | E2B-compatible API via the `e2b` SDK; region from `ALIBABA_SANDBOX_REGION` (default ap-southeast-1). |
|
|
408
|
-
| AgentCore | fixed 2 vCPU / 8 GB | `sessionTimeoutSeconds` (max 8 h) | no | no | SigV4, plain fetch; files move as base64 over commands. Opt-in. |
|
|
409
|
-
| Azure sessions | fixed by the pool | pool cooldown (5 to 60 min idle) | no | no | Shell session pool; output over 4 KB is fetched from files. |
|
|
410
|
-
| Beam | `cpu`, `memory` | `keepWarmSeconds` | registry image | no | Uses Beam app `sandboxrouter` (override with `SANDBOXROUTER_BEAM_APP`). |
|
|
411
|
-
| CodeSandbox | smallest of 7 fixed tiers that fits | hibernation timeout | template | yes (hibernate: memory + disk) | Exec output goes through files because the SDK's terminal merges streams. |
|
|
412
|
-
| Dedalus Machines | `vcpu`, `memory_mib` | autosleep after idle (compute stops, disk kept) | no | yes (sleep/wake) | Plain REST; streams by polling the events feed; files move as base64 over stdin/stdout. Private beta. |
|
|
413
|
-
| Deno | fixed 2 vCPU, 0.75 to 4 GB | up to 30 min | snapshot or volume | no | WebSocket RPC via `@deno/sandbox`. |
|
|
414
|
-
| Freestyle | smallest Ubuntu snapshot that fits | `ttlSeconds` | snapshot | yes (memory + disk) | Plain REST; long commands run as background requests and are polled. |
|
|
415
|
-
| InstaBox | smallest fixed size that fits (2/4, 4/8, 8/16; free plan falls back to 4/8) | `deleteAfterSeconds` (60 s to 24 h) | template or snapshot (`sn_...`) | yes (suspend: memory + disk) | Plain REST; exec via `bash -lc`, max 600 s, SSE streaming; files API raw bytes. Pauses itself after 30 idle min. |
|
|
416
|
-
| Koyeb | smallest Standard instance that fits | auto-delete delay (max 24 h) | any Docker image | no | Plain REST; streams over SSE. Public preview. |
|
|
417
|
-
| Lambda MicroVMs | image baseline (2 GB per vCPU) | `maximumDurationInSeconds` | your MicroVM image | yes (suspend/resume) | Runs the bundled in-VM server for exec and files. |
|
|
418
|
-
| Novita | set by the template | `timeoutMs` | template | yes (memory + disk) | `novita-sandbox` SDK (an E2B fork). |
|
|
419
|
-
| Sail | `vcpu_count`; memory is a limit of at least 16 GiB | `max_lifetime_seconds` (counts paused time) | `debian` (default) or `devbox` base | yes (memory + disk) | Plain REST, no SDK. Streams NDJSON. Billed on what the box uses, not the limits. A paused box resumes on the next call. |
|
|
420
|
-
| Upstash Box | smallest of 3 fixed sizes that fits | TTL (max 3 days) | runtime name (node, python, ...) | no | Plain REST; billed on active CPU only. |
|
|
421
|
-
| Docker (local) | `--cpus`, `--memory` | container exits at the lifetime | any image | yes (`docker pause`) | Your machine, shared kernel. Never auto-routed; use `{ provider: "docker" }`. |
|
|
422
|
-
|
|
423
|
-
Capabilities in the catalog describe what each **adapter** implements, not everything a provider sells. Size limits are self-serve defaults and may be higher on your plan.
|
|
424
|
-
|
|
425
|
-
## Roadmap
|
|
426
|
-
|
|
427
|
-
- [x] More providers: Morph, Fly.io Sprites, Blaxel, Tensorlake, Runloop, HOPX, Northflank, Boat, exe.dev, Cloudflare (via a bridge Worker), Docker (local)
|
|
428
|
-
- [x] More providers: Koyeb, Freestyle, Deno, Beam, CodeSandbox, Upstash Box, Novita, Alibaba Cloud, Azure Container Apps sessions, AWS Bedrock AgentCore, AWS Lambda MicroVMs
|
|
429
|
-
- [x] YC sandbox startups: Agent37 (F26), Dedalus Machines (S25), InstaBox (P26), OpenScale (S22), Superserve (F26)
|
|
430
|
-
- [x] Sail Research Sailboxes
|
|
431
|
-
- [ ] More E2B-compatible clouds (PPIO, UCloud, Baidu) and Google Agent Engine sandboxes
|
|
432
|
-
- [x] Neutral routing: price decides, ties alphabetical, no privileged slot
|
|
433
|
-
- [x] Desktops and phones from 7 providers (Linux, Windows, Android, iOS), routed by price per OS
|
|
434
|
-
- [ ] Windows 365 for Agents and Corellium as opt-in computer providers
|
|
435
|
-
- [x] Streaming exec output
|
|
436
|
-
- [x] Pause/resume where providers support it
|
|
437
|
-
- [x] Python client
|
|
438
|
-
- [x] Leaderboard built from `bench --share` reports
|
|
439
|
-
- [x] Leak cleanup: a local ledger, `ls` and `gc`
|
|
440
|
-
- [x] One 14-step real-account check for every adapter, with a compatibility table and a nightly run
|
|
441
|
-
- [ ] Every adapter run against a real account (see the Compatibility table)
|
|
442
|
-
- [ ] Snapshots and forks as a routed operation
|
|
443
|
-
- [ ] A hosted copy of the leaderboard page
|
|
444
|
-
|
|
445
|
-
## Website
|
|
446
|
-
|
|
447
|
-
[`web/`](web/) is the project site: home, a browsable Sandboxes list with live price-per-size, a page per provider, Rankings, Docs, and a Playground that drives your own `sandboxrouter serve` from the browser. Its data is generated from the library (`npm run site:data`), so prices never drift from the code.
|
|
448
|
-
|
|
449
|
-
```bash
|
|
450
|
-
cd web && npm i && npm run dev # http://localhost:3000
|
|
451
|
-
# Playground without any provider keys (in-memory providers, nothing real starts):
|
|
452
|
-
node --import tsx test/demo-server.ts # token: demo
|
|
453
|
-
# Playground against real providers:
|
|
454
|
-
SANDBOXROUTER_TOKEN=secret npx @sandboxrouter/sdk serve --cors http://localhost:3000
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
## Development
|
|
458
|
-
|
|
459
|
-
```bash
|
|
460
|
-
npm i
|
|
461
|
-
npm test # router, HTTP server, and every adapter against a local mock of its API
|
|
462
|
-
npm run test:py # Python client against the real HTTP server
|
|
463
|
-
npm run build
|
|
464
|
-
npm run dev -- providers
|
|
465
|
-
npm run price:check # compare every provider's pricing page with the last check
|
|
466
|
-
node --import tsx scripts/readme-tables.ts --write # regenerate the README's Prices, Env and Compatibility tables
|
|
467
85
|
```
|
|
468
86
|
|
|
469
|
-
|
|
87
|
+
Then the SDK of each provider you use (many need none) and its keys: both are listed in [the reference](docs/reference.md#install). `npx @sandboxrouter/sdk providers` shows which keys are set.
|
|
470
88
|
|
|
471
|
-
|
|
89
|
+
## More
|
|
472
90
|
|
|
473
|
-
[
|
|
91
|
+
- [Docs](https://sandboxrouter.vercel.app/docs): routing, fallback, streaming, files, pause, the HTTP server and Python.
|
|
92
|
+
- [Reference](docs/reference.md): the full library, CLI and HTTP API, desktops and phones, the real-account compatibility table, and how each adapter maps the spec.
|
|
93
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md): run a provider for real and send the result, fix a price, or add a provider.
|
|
94
|
+
- [CHANGELOG.md](CHANGELOG.md) · [SECURITY.md](SECURITY.md) · [MIT](LICENSE)
|
package/dist/catalog.d.ts
CHANGED
|
@@ -58,7 +58,20 @@ export interface ProviderInfo {
|
|
|
58
58
|
note: string;
|
|
59
59
|
checked: string;
|
|
60
60
|
};
|
|
61
|
+
/**
|
|
62
|
+
* Set when the provider's terms forbid publishing benchmarks or performance information about it.
|
|
63
|
+
* Its cold starts, success rate, measured size and real bills are then left out of shared reports,
|
|
64
|
+
* results/ and the website, and saved checks keep pass/fail without timings. List prices (public
|
|
65
|
+
* pricing pages) and the adapter itself are unaffected.
|
|
66
|
+
*/
|
|
67
|
+
noBenchmarks?: {
|
|
68
|
+
clause: string;
|
|
69
|
+
source: string;
|
|
70
|
+
checked: string;
|
|
71
|
+
};
|
|
61
72
|
}
|
|
73
|
+
/** False when the provider's terms forbid publishing its benchmarks (see `noBenchmarks`). */
|
|
74
|
+
export declare function benchmarksPublishable(id: string): boolean;
|
|
62
75
|
/** Short label for a gated provider's sign-up status, or null when anyone can sign up. */
|
|
63
76
|
export declare function accessLabel(id: ProviderId): string | null;
|
|
64
77
|
/** Short label for the plan a provider needs before you can use it: "none", "$20/mo (credited)", "$5/mo". */
|
package/dist/catalog.js
CHANGED
|
@@ -3,6 +3,10 @@ import { AGENT37_SHAPES, agent37Shape } from "./providers/agent37.js";
|
|
|
3
3
|
import { INSTABOX_SIZES, instaboxSize } from "./providers/instabox.js";
|
|
4
4
|
import { FREESTYLE_SIZES, freestyleSize } from "./providers/freestyle.js";
|
|
5
5
|
import { langsmithSize } from "./providers/langsmith.js";
|
|
6
|
+
/** False when the provider's terms forbid publishing its benchmarks (see `noBenchmarks`). */
|
|
7
|
+
export function benchmarksPublishable(id) {
|
|
8
|
+
return !CATALOG.find((c) => c.id === id)?.noBenchmarks;
|
|
9
|
+
}
|
|
6
10
|
/** Short label for a gated provider's sign-up status, or null when anyone can sign up. */
|
|
7
11
|
export function accessLabel(id) {
|
|
8
12
|
const a = CATALOG.find((c) => c.id === id)?.access;
|
|
@@ -172,6 +176,11 @@ const ENTRIES = [
|
|
|
172
176
|
limits: { maxVcpu: 8, maxMemoryGb: 16 },
|
|
173
177
|
credentials: [["BL_API_KEY", "BL_WORKSPACE"]],
|
|
174
178
|
docs: "https://docs.blaxel.ai/Sandboxes/Overview",
|
|
179
|
+
noBenchmarks: {
|
|
180
|
+
clause: "Blaxel's terms (§2.6(f)) don't allow publishing benchmarks or performance information about it",
|
|
181
|
+
source: "https://blaxel.ai/legal/terms/2025-12-10-blaxel-terms-and-conditions.pdf",
|
|
182
|
+
checked: "2026-11-07",
|
|
183
|
+
},
|
|
175
184
|
},
|
|
176
185
|
{
|
|
177
186
|
id: "tensorlake",
|
|
@@ -524,6 +533,11 @@ ENTRIES.push({
|
|
|
524
533
|
limits: { maxVcpu: 16, maxMemoryGb: 96 },
|
|
525
534
|
credentials: [["LANGSMITH_API_KEY"]],
|
|
526
535
|
docs: "https://docs.langchain.com/langsmith/sandboxes",
|
|
536
|
+
access: {
|
|
537
|
+
status: "waitlist",
|
|
538
|
+
note: "Sandboxes are a private preview: until LangChain enables them for your organization, the API answers \"Sandbox feature is not enabled for this organization\"",
|
|
539
|
+
checked: "2026-11-07",
|
|
540
|
+
},
|
|
527
541
|
}, {
|
|
528
542
|
id: "novita",
|
|
529
543
|
name: "Novita AI Agent Sandbox",
|