the universal sandbox runtime for agents and humans.
pocketenv.io
sandbox
openclaw
agent
claude-code
vercel-sandbox
deno-sandbox
cloudflare-sandbox
atproto
sprites
daytona
1import consola from "consola";
2import { Sandbox } from "@pocketenv/sdk";
3import connectToSandbox from "./ssh";
4import { c } from "../theme";
5import { expandRepo } from "../lib/expandRepo";
6import encrypt from "../lib/sodium";
7import redact from "../lib/redact";
8import { configureSdk } from "../lib/sdk";
9
10async function createSandbox(
11 name: string,
12 {
13 provider,
14 ssh,
15 base,
16 repo,
17 }: {
18 provider?: string;
19 ssh?: boolean;
20 base?: string;
21 repo?: string;
22 },
23) {
24 await configureSdk();
25 if (repo) repo = expandRepo(repo);
26
27 const providerOptions: Record<string, any> = {};
28
29 if (
30 ![
31 "sprites",
32 "daytona",
33 "deno",
34 "vercel",
35 "cloudflare",
36 "modal",
37 "e2b",
38 "hopx",
39 "runloop",
40 "blaxel",
41 ].includes(provider ?? "cloudflare")
42 ) {
43 consola.error(
44 `Unsupported provider: ${provider}. Supported providers are: sprites, daytona, deno, vercel, modal, e2b, hopx, runloop, blaxel, cloudflare (default).`,
45 );
46 process.exit(1);
47 }
48
49 if (provider === "sprites") {
50 const spriteToken = process.env.SPRITE_TOKEN;
51 if (!spriteToken) {
52 consola.error(
53 "SPRITE_TOKEN environment variable is required for Sprites provider.",
54 );
55 process.exit(1);
56 }
57 providerOptions.spriteToken = await encrypt(spriteToken);
58 providerOptions.redactedSpriteToken = redact(spriteToken);
59 }
60
61 if (provider === "daytona") {
62 const daytonaApiKey = process.env.DAYTONA_API_KEY;
63 const daytonaOrganizationId = process.env.DAYTONA_ORGANIZATION_ID;
64 if (!daytonaApiKey || !daytonaOrganizationId) {
65 consola.error(
66 "DAYTONA_API_KEY and DAYTONA_ORGANIZATION_ID environment variables are required for Daytona provider.",
67 );
68 process.exit(1);
69 }
70 providerOptions.daytonaApiKey = await encrypt(daytonaApiKey);
71 providerOptions.redactedDaytonaApiKey = redact(daytonaApiKey);
72 providerOptions.daytonaOrganizationId = daytonaOrganizationId;
73 }
74
75 if (provider === "deno") {
76 const denoDeployToken = process.env.DENO_DEPLOY_TOKEN;
77 if (!denoDeployToken) {
78 consola.error(
79 "DENO_DEPLOY_TOKEN environment variable is required for Deno provider.",
80 );
81 process.exit(1);
82 }
83 providerOptions.denoDeployToken = await encrypt(denoDeployToken);
84 providerOptions.redactedDenoDeployToken = redact(denoDeployToken);
85 }
86
87 if (provider === "vercel") {
88 const vercelApiToken = process.env.VERCEL_API_TOKEN;
89 const vercelProjectId = process.env.VERCEL_PROJECT_ID;
90 const vercelTeamId = process.env.VERCEL_TEAM_ID;
91 if (!vercelApiToken || !vercelProjectId || !vercelTeamId) {
92 consola.error(
93 "VERCEL_API_TOKEN, VERCEL_PROJECT_ID and VERCEL_TEAM_ID environment variables are required for Vercel provider.",
94 );
95 process.exit(1);
96 }
97 providerOptions.vercelApiToken = await encrypt(vercelApiToken);
98 providerOptions.redactedVercelApiToken = redact(vercelApiToken);
99 providerOptions.vercelProjectId = vercelProjectId;
100 providerOptions.vercelTeamId = vercelTeamId;
101 }
102
103 if (provider === "modal") {
104 const modalTokenId = process.env.MODAL_TOKEN_ID;
105 const modalTokenSecret = process.env.MODAL_TOKEN_SECRET;
106 if (!modalTokenId || !modalTokenSecret) {
107 consola.error(
108 "MODAL_TOKEN_ID and MODAL_TOKEN_SECRET environment variables are required for Modal provider.",
109 );
110 process.exit(1);
111 }
112 providerOptions.modalTokenId = await encrypt(modalTokenId);
113 providerOptions.redactedModalTokenId = redact(modalTokenId);
114 providerOptions.modalTokenSecret = await encrypt(modalTokenSecret);
115 providerOptions.redactedModalTokenSecret = redact(modalTokenSecret);
116 }
117
118 if (provider === "e2b") {
119 const e2bApiKey = process.env.E2B_ACCESS_TOKEN || process.env.E2B_API_KEY;
120 if (!e2bApiKey) {
121 consola.error(
122 "E2B_API_KEY environment variable is required for E2B provider.",
123 );
124 process.exit(1);
125 }
126 providerOptions.e2bApiKey = await encrypt(e2bApiKey);
127 providerOptions.redactedE2bApiKey = redact(e2bApiKey);
128 }
129
130 if (provider == "hopx") {
131 const hopxApiKey = process.env.HOPX_API_KEY;
132 if (!hopxApiKey) {
133 consola.error(
134 "HOPX_API_KEY environment variable is required for HopX provider.",
135 );
136 process.exit(1);
137 }
138 providerOptions.hopxApiKey = await encrypt(hopxApiKey);
139 providerOptions.redactedHopxApiKey = redact(hopxApiKey);
140 }
141
142 if (provider == "runloop") {
143 const runloopApiKey = process.env.RUNLOOP_API_KEY;
144 if (!runloopApiKey) {
145 consola.error(
146 "RUNLOOP_API_KEY environment variable is required for RunLoop provider.",
147 );
148 process.exit(1);
149 }
150 providerOptions.runloopApiKey = await encrypt(runloopApiKey);
151 providerOptions.redactedRunloopApiKey = redact(runloopApiKey);
152 }
153
154 try {
155 const sandbox = await Sandbox.create({
156 name,
157 base:
158 base ??
159 "at://did:plc:aturpi2ls3yvsmhc6wybomun/io.pocketenv.sandbox/openclaw",
160 provider: provider ?? "cloudflare",
161 repo,
162 providerOptions,
163 });
164 if (!ssh) {
165 consola.success(
166 `Sandbox created successfully: ${c.primary(sandbox.data.name)}`,
167 );
168 return;
169 }
170 await sandbox.waitUntilRunning();
171 await connectToSandbox(sandbox.data.name);
172 } catch (error) {
173 consola.error(`Failed to create sandbox: ${error}`);
174 }
175}
176
177export default createSandbox;