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.

Support Daytona images and async sandbox creation

+120 -31
+21
apps/sandbox/src/images.ts
··· 1 + export const images: Record<string, string> = { 2 + openclaw: "ghcr.io/pocketenv-io/daytona-openclaw:0.6.0", 3 + amp: "ghcr.io/pocketenv-io/daytona-amp:0.6.0", 4 + claude: "ghcr.io/pocketenv-io/daytona-claude:0.6.0", 5 + codex: "ghcr.io/pocketenv-io/daytona-codex:0.6.0", 6 + copilot: "ghcr.io/pocketenv-io/daytona-copilot:0.6.0", 7 + crush: "ghcr.io/pocketenv-io/daytona-crush:0.6.0", 8 + cursor: "ghcr.io/pocketenv-io/daytona-cursor:0.6.0", 9 + gemini: "ghcr.io/pocketenv-io/daytona-gemini:0.6.0", 10 + kilo: "ghcr.io/pocketenv-io/daytona-kilo:0.6.0", 11 + kiro: "ghcr.io/pocketenv-io/daytona-kiro:0.6.0", 12 + mise: "ghcr.io/pocketenv-io/daytona-mise:0.6.0", 13 + nix: "ghcr.io/pocketenv-io/daytona-nix:0.6.0", 14 + nullclaw: "ghcr.io/pocketenv-io/daytona-nullclaw:0.6.0", 15 + opencode: "ghcr.io/pocketenv-io/daytona-opencode:0.6.0", 16 + opencrust: "ghcr.io/pocketenv-io/daytona-opencrust:0.6.0", 17 + picoclaw: "ghcr.io/pocketenv-io/daytona-picoclaw:0.6.0", 18 + pkgx: "ghcr.io/pocketenv-io/daytona-pkgx:0.6.0", 19 + wasmer: "ghcr.io/pocketenv-io/daytona-wasmer:0.6.0", 20 + zeroclaw: "ghcr.io/pocketenv-io/daytona-zeroclaw:0.6.0", 21 + };
+23
apps/sandbox/src/lib/parseImageRef.ts
··· 1 + export default function parseImageRef(ref: string): { 2 + registry?: string; 3 + name: string; 4 + tag: string; 5 + } { 6 + const tagSeparatorIndex = ref.lastIndexOf(":"); 7 + const slashBeforeTag = ref.lastIndexOf("/"); 8 + 9 + const hasTag = tagSeparatorIndex > slashBeforeTag; 10 + 11 + const imageWithoutTag = hasTag ? ref.slice(0, tagSeparatorIndex) : ref; 12 + const tag = hasTag ? ref.slice(tagSeparatorIndex + 1) : "latest"; 13 + 14 + const parts = imageWithoutTag.split("/"); 15 + 16 + const isRegistry = (s: string) => s.includes(".") || s.includes(":"); 17 + const registry = isRegistry(parts[0]!) ? parts[0] : ""; 18 + const name = isRegistry(parts[0]!) 19 + ? parts.slice(1).join("/") 20 + : parts.join("/"); 21 + 22 + return { registry, name, tag }; 23 + }
+37 -3
apps/sandbox/src/providers/daytona/mod.ts
··· 1 1 import BaseProvider, { BaseSandbox, SandboxOptions } from "../mod.ts"; 2 - import { Daytona, Sandbox } from "@daytonaio/sdk"; 2 + import { Daytona, Sandbox, Image } from "@daytonaio/sdk"; 3 3 import process, { env } from "node:process"; 4 4 import consola from "consola"; 5 5 import path from "node:path"; 6 6 import { Buffer } from "node:buffer"; 7 + import { images } from "../../images.ts"; 8 + import parseImageRef from "../../lib/parseImageRef.ts"; 7 9 8 10 export class DaytonaSandbox implements BaseSandbox { 9 11 constructor(private sandbox: Sandbox) {} ··· 145 147 _experimental: {}, 146 148 }); 147 149 150 + const DEFAULT_IMAGE = "ghcr.io/pocketenv-io/daytona-openclaw:0.6.0"; 151 + const name = options.image 152 + ? images[options.image] || DEFAULT_IMAGE 153 + : DEFAULT_IMAGE; 154 + const image = Image.base(name); 155 + const metadata = parseImageRef(name); 156 + const snapshotName = metadata.name.split("/").pop()!; 157 + try { 158 + const snapshot = await daytona.snapshot.get(snapshotName); 159 + if (snapshot.state !== "active") { 160 + consola.warn( 161 + "Snapshot found in Daytona but not active, activating snapshot", 162 + snapshotName, 163 + ); 164 + await daytona.snapshot.activate(snapshot); 165 + } 166 + } catch (error) { 167 + consola.warn( 168 + "Snapshot not found in Daytona, building image and creating snapshot", 169 + snapshotName, 170 + error, 171 + ); 172 + await daytona.snapshot.create({ 173 + name: snapshotName, 174 + image, 175 + resources: { 176 + cpu: 2, 177 + memory: 4, 178 + disk: 8, 179 + }, 180 + }); 181 + } 182 + 148 183 const sandbox = await daytona.create({ 149 184 language: "typescript", 150 - // snapshot: process.env.DAYTONA_SNAPSHOT, 151 - snapshot: "daytona-medium", 185 + snapshot: snapshotName, 152 186 envVars: options.envVars, 153 187 }); 154 188
+39 -28
apps/sandbox/src/routes/sandboxes.ts
··· 42 42 import crypto from "node:crypto"; 43 43 import process from "node:process"; 44 44 import prepareSandbox from "../lib/prepare-sandbox.ts"; 45 + import { InsertSandbox } from "../schema/sandboxes.ts"; 45 46 46 47 const SUPPORTED_PROVIDERS = ["daytona", "vercel", "deno", "sprites"]; 47 48 ··· 169 170 }, 170 171 ); 171 172 172 - const sandbox = await createSandbox(params.provider, { 173 - id: initialRecord.id, 174 - keepAlive: params.keepAlive, 175 - sleepAfter: params.sleepAfter, 176 - snapshotRoot: process.env.DENO_SNAPSHOT_ROOT, 177 - spriteToken: decrypt(params.spriteToken), 178 - spriteName, 179 - daytonaApiKey: decrypt(params.daytonaApiKey), 180 - organizationId: params.daytonaOrganizationId, 181 - denoDeployToken: decrypt(params.denoDeployToken), 182 - vercelApiToken: decrypt(params.vercelApiToken), 183 - vercelProjectId: params.vercelProjectId, 184 - vercelTeamId: params.vercelTeamId, 185 - }); 186 - const sandboxId = await sandbox.id(); 173 + (async () => { 174 + const sandbox = await createSandbox(params.provider, { 175 + id: initialRecord.id, 176 + keepAlive: params.keepAlive, 177 + sleepAfter: params.sleepAfter, 178 + snapshotRoot: process.env.DENO_SNAPSHOT_ROOT, 179 + spriteToken: decrypt(params.spriteToken), 180 + spriteName, 181 + daytonaApiKey: decrypt(params.daytonaApiKey), 182 + organizationId: params.daytonaOrganizationId, 183 + denoDeployToken: decrypt(params.denoDeployToken), 184 + vercelApiToken: decrypt(params.vercelApiToken), 185 + vercelProjectId: params.vercelProjectId, 186 + vercelTeamId: params.vercelTeamId, 187 + }); 188 + const sandboxId = await sandbox.id(); 187 189 188 - const [record] = await c.var.db 189 - .update(sandboxes) 190 - .set({ 191 - status: "RUNNING", 192 - sandboxId: sandboxId, 193 - startedAt: new Date(), 194 - vcpus: params.vcpus, 195 - memory: params.memory, 196 - disk: params.disk, 190 + const [record] = await c.var.db 191 + .update(sandboxes) 192 + .set({ 193 + status: "RUNNING", 194 + sandboxId: sandboxId, 195 + startedAt: new Date(), 196 + vcpus: params.vcpus, 197 + memory: params.memory, 198 + disk: params.disk, 199 + }) 200 + .where(eq(sandboxes.id, initialRecord.id)) 201 + .returning() 202 + .execute(); 203 + 204 + return record; 205 + })() 206 + .then((record) => { 207 + consola.success(`Sandbox ${record.id} is created and running`); 197 208 }) 198 - .where(eq(sandboxes.id, initialRecord.id)) 199 - .returning() 200 - .execute(); 209 + .catch((e) => { 210 + consola.error(`Failed to create sandbox ${initialRecord.id}: ${e}`); 211 + }); 201 212 202 - return c.json(record); 213 + return c.json(initialRecord); 203 214 } catch (err) { 204 215 console.log(err); 205 216 return c.json(