the universal sandbox runtime for agents and humans. pocketenv.io
sandbox openclaw agent claude-code vercel-sandbox deno-sandbox cloudflare-sandbox atproto sprites daytona
7
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add sandbox provider support

Introduce Providers enum and DefaultResources
Add provider field to createSandbox schema and types
Default provider to DENO in handler; handle deno sandbox_id on start
Lazy-import the deno provider and add stop logs for providers
Add enum support to StringType

+75 -6
+10
apps/api/lexicons/sandbox/createSandbox.json
··· 26 26 "type": "string", 27 27 "description": "A description for the sandbox" 28 28 }, 29 + "provider": { 30 + "type": "string", 31 + "description": "The provider to create the sandbox on, e.g. 'daytona', 'vercel', 'cloudflare', etc.", 32 + "enum": [ 33 + "daytona", 34 + "vercel", 35 + "cloudflare", 36 + "deno" 37 + ] 38 + }, 29 39 "topics": { 30 40 "type": "array", 31 41 "description": "A list of topics/tags to associate with the sandbox",
+6
apps/api/pkl/defs/sandbox/createSandbox.pkl
··· 26 26 type = "string" 27 27 description = "A description for the sandbox" 28 28 } 29 + ["provider"] = new StringType { 30 + type = "string" 31 + enum = List("daytona", "vercel", "cloudflare", "deno") 32 + description = 33 + "The provider to create the sandbox on, e.g. 'daytona', 'vercel', 'cloudflare', etc." 34 + } 29 35 ["topics"] = new Array { 30 36 type = "array" 31 37 description = "A list of topics/tags to associate with the sandbox"
+1
apps/api/pkl/schema/lexicon.pkl
··· 8 8 maxGraphemes: Int? 9 9 minLength: Int? 10 10 maxLength: Int? 11 + enum: List<String>? 11 12 format: "uri" | "datetime" | "cid" | "at-uri" | "at-identifier" | "did" | Null = null 12 13 } 13 14
+35
apps/api/src/consts.ts
··· 1 + export enum Providers { 2 + DAYTONA = "daytona", 3 + DENO = "deno", 4 + VERCEL = "vercel", 5 + CLOUDFLARE = "cloudflare", 6 + } 7 + 8 + export type Resources = { 9 + vcpus: number; 10 + memory: number; 11 + disk: number; 12 + }; 13 + 14 + export const DefaultResources: Record<Providers, Resources> = { 15 + daytona: { 16 + vcpus: 2, 17 + memory: 4, 18 + disk: 8, 19 + }, 20 + deno: { 21 + vcpus: 2, 22 + memory: 4, 23 + disk: 10, 24 + }, 25 + vercel: { 26 + vcpus: 2, 27 + memory: 4, 28 + disk: 8, 29 + }, 30 + cloudflare: { 31 + vcpus: 2, 32 + memory: 4, 33 + disk: 8, 34 + }, 35 + };
+6
apps/api/src/lexicon/lexicons.ts
··· 182 182 type: "string", 183 183 description: "A description for the sandbox", 184 184 }, 185 + provider: { 186 + type: "string", 187 + description: 188 + "The provider to create the sandbox on, e.g. 'daytona', 'vercel', 'cloudflare', etc.", 189 + enum: ["daytona", "vercel", "cloudflare", "deno"], 190 + }, 185 191 topics: { 186 192 type: "array", 187 193 description:
+2
apps/api/src/lexicon/types/io/pocketenv/sandbox/createSandbox.ts
··· 18 18 name?: string; 19 19 /** A description for the sandbox */ 20 20 description?: string; 21 + /** The provider to create the sandbox on, e.g. 'daytona', 'vercel', 'cloudflare', etc. */ 22 + provider?: "daytona" | "vercel" | "cloudflare" | "deno"; 21 23 /** A list of topics/tags to associate with the sandbox */ 22 24 topics?: string[]; 23 25 /** A git repository URL to clone into the sandbox, e.g. a GitHub/Tangled repo. */
+1 -3
apps/api/src/schema/sandboxes.ts
··· 9 9 import users from "./users"; 10 10 11 11 const sandboxes = pgTable("sandboxes", { 12 - id: text("id") 13 - .primaryKey() 14 - .default(sql`sandbox_id()`), 12 + id: text("id").primaryKey().default(sql`sandbox_id()`), 15 13 base: text("base"), 16 14 name: text("name").unique().notNull(), 17 15 displayName: text("display_name"),
+2 -1
apps/api/src/xrpc/io/pocketenv/sandbox/createSandbox.ts
··· 14 14 validateMain, 15 15 type Main, 16 16 } from "lexicon/types/com/atproto/repo/strongRef"; 17 + import { Providers } from "consts"; 17 18 18 19 export default function (server: Server, ctx: Context) { 19 20 const createSandbox = async (input: HandlerInput, auth: HandlerAuth) => { 20 21 const res = await ctx.sandbox.post( 21 22 "/v1/sandboxes", 22 23 { 23 - provider: "daytona", 24 + provider: input.body.provider || Providers.DENO, 24 25 }, 25 26 { 26 27 ...(auth?.credentials && {
+6 -1
apps/sandbox/src/index.ts
··· 202 202 await sandbox.start(); 203 203 await c.var.db 204 204 .update(sandboxes) 205 - .set({ status: "RUNNING", startedAt: new Date() }) 205 + .set({ 206 + status: "RUNNING", 207 + startedAt: new Date(), 208 + sandbox_id: 209 + record.provider === "deno" ? await sandbox.id() : record.sandbox_id, 210 + }) 206 211 .where(eq(sandboxes.id, c.req.param("sandboxId"))) 207 212 .execute(); 208 213 return c.json({});
+1
apps/sandbox/src/providers/daytona/mod.ts
··· 12 12 13 13 async stop(): Promise<void> { 14 14 try { 15 + consola.info("Stopping Daytona sandbox with ID:", await this.id()); 15 16 await this.sandbox.stop(); 16 17 } catch (error) { 17 18 consola.error("Error stopping Daytona sandbox:", error);
+1
apps/sandbox/src/providers/deno/mod.ts
··· 12 12 13 13 async stop(): Promise<void> { 14 14 try { 15 + consola.info("Stopping Deno sandbox with ID:", await this.id()); 15 16 await this.sandbox.kill(); 16 17 } catch (error) { 17 18 consola.error("Error killing sandbox:", error);
+3 -1
apps/sandbox/src/providers/mod.ts
··· 61 61 new module.default().get(id), 62 62 ); 63 63 case "deno": 64 - return createSandbox("deno", { id }); 64 + return import("./deno/mod.ts").then((module) => 65 + new module.default().get(id), 66 + ); 65 67 case "vercel": 66 68 return import("./vercel/mod.ts").then((module) => 67 69 new module.default().get(id),
+1
apps/sandbox/src/providers/vercel/mod.ts
··· 12 12 13 13 async stop(): Promise<void> { 14 14 try { 15 + consola.info("Stopping Vercel sandbox with ID:", await this.id()); 15 16 await this.sandbox.stop(); 16 17 } catch (error) { 17 18 consola.error("Error stopping Vercel sandbox:", error);