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 Daytona auth and provider token support

Persist Daytona API keys and organization IDs when creating sandboxes
Query daytona_auth alongside sprite_auth and use its API key when sprite
token is missing
Pass decrypted Daytona key to the provider and add daytona options to
SandboxOptions

+99 -36
+86 -31
apps/sandbox/src/index.ts
··· 43 43 import jwt from "@tsndr/cloudflare-worker-jwt"; 44 44 import decrypt from "./lib/decrypt.ts"; 45 45 import { InsertSpriteAuth } from "./schema/sprite-auth.ts"; 46 + import daytonaAuth, { InsertDaytonaAuth } from "./schema/daytona-auth.ts"; 46 47 47 48 const app = new Hono<{ Variables: Context }>(); 48 49 ··· 165 166 .execute(); 166 167 } 167 168 169 + if (params.daytonaApiKey && user?.id) { 170 + await tx 171 + .insert(daytonaAuth) 172 + .values({ 173 + sandboxId: record.id, 174 + apiKey: params.daytonaApiKey, 175 + redactedApiKey: params.redactedDaytonaApiKey ?? "", 176 + userId: user.id, 177 + organizationId: params.daytonaOrganizationId!, 178 + } satisfies InsertDaytonaAuth) 179 + .execute(); 180 + } 181 + 168 182 const sandbox = await createSandbox(params.provider, { 169 183 id: record.id, 170 184 keepAlive: params.keepAlive, 171 185 sleepAfter: params.sleepAfter, 172 - organizationId: process.env.DAYTONA_ORGANIZATION_ID, 173 186 snapshotRoot: process.env.DENO_SNAPSHOT_ROOT, 174 187 spriteToken: decrypt(params.spriteToken), 175 188 spriteName, 189 + daytonaApiKey: decrypt(params.daytonaApiKey), 190 + organizationId: params.daytonaOrganizationId, 176 191 }); 177 192 const sandboxId = await sandbox.id(); 178 193 ··· 233 248 const body = await c.req.json<StartSandboxInput>(); 234 249 const { repo } = StartSandboxInputSchema.parse(body); 235 250 236 - const [spriteAuthParams] = await c.var.db 237 - .select() 238 - .from(spriteAuth) 239 - .where(eq(spriteAuth.sandboxId, record.id)) 240 - .execute(); 251 + const [[spriteAuthParams], [daytonaAuthParams]] = await Promise.all([ 252 + c.var.db 253 + .select() 254 + .from(spriteAuth) 255 + .where(eq(spriteAuth.sandboxId, record.id)) 256 + .execute(), 257 + c.var.db 258 + .select() 259 + .from(daytonaAuth) 260 + .where(eq(daytonaAuth.sandboxId, record.id)) 261 + .execute(), 262 + ]); 241 263 242 264 sandbox = await getSandboxById( 243 265 record.provider as Provider, 244 266 record.sandboxId!, 245 - decrypt(spriteAuthParams?.spriteToken), 267 + decrypt(spriteAuthParams?.spriteToken || daytonaAuthParams?.apiKey), 268 + daytonaAuthParams?.organizationId, 246 269 ); 247 270 248 271 if (!sandbox) { ··· 350 373 return c.json({ error: "Sandbox provider not supported" }, 400); 351 374 } 352 375 353 - const [spriteAuthParams] = await c.var.db 354 - .select() 355 - .from(spriteAuth) 356 - .where(eq(spriteAuth.sandboxId, record.id)) 357 - .execute(); 376 + const [[spriteAuthParams], [daytonaAuthParams]] = await Promise.all([ 377 + c.var.db 378 + .select() 379 + .from(spriteAuth) 380 + .where(eq(spriteAuth.sandboxId, record.id)) 381 + .execute(), 382 + c.var.db 383 + .select() 384 + .from(daytonaAuth) 385 + .where(eq(daytonaAuth.sandboxId, record.id)) 386 + .execute(), 387 + ]); 358 388 359 389 sandbox = await getSandboxById( 360 390 record.provider as Provider, 361 391 record.sandboxId!, 362 - decrypt(spriteAuthParams?.spriteToken), 392 + decrypt(spriteAuthParams?.spriteToken || daytonaAuthParams?.apiKey), 393 + daytonaAuthParams?.organizationId, 363 394 ); 364 395 365 396 if (!sandbox) { ··· 388 419 return c.json({ error: "Sandbox provider not supported" }, 400); 389 420 } 390 421 391 - const [spriteAuthParams] = await c.var.db 392 - .select() 393 - .from(spriteAuth) 394 - .where(eq(spriteAuth.sandboxId, record.id)) 395 - .execute(); 422 + const [[spriteAuthParams], [daytonaAuthParams]] = await Promise.all([ 423 + c.var.db 424 + .select() 425 + .from(spriteAuth) 426 + .where(eq(spriteAuth.sandboxId, record.id)) 427 + .execute(), 428 + c.var.db 429 + .select() 430 + .from(daytonaAuth) 431 + .where(eq(daytonaAuth.sandboxId, record.id)) 432 + .execute(), 433 + ]); 396 434 397 435 sandbox = await getSandboxById( 398 436 record.provider as Provider, 399 437 record.sandboxId!, 400 - decrypt(spriteAuthParams?.spriteToken), 438 + decrypt(spriteAuthParams?.spriteToken || daytonaAuthParams?.apiKey), 439 + daytonaAuthParams?.organizationId, 401 440 ); 402 441 403 442 if (!sandbox) { ··· 422 461 return c.json({ error: "Sandbox provider not supported" }, 400); 423 462 } 424 463 425 - const [spriteAuthParams] = await c.var.db 426 - .select() 427 - .from(spriteAuth) 428 - .where(eq(spriteAuth.sandboxId, record.id)) 429 - .execute(); 464 + const [[spriteAuthParams], [daytonaAuthParams]] = await Promise.all([ 465 + c.var.db 466 + .select() 467 + .from(spriteAuth) 468 + .where(eq(spriteAuth.sandboxId, record.id)) 469 + .execute(), 470 + c.var.db 471 + .select() 472 + .from(daytonaAuth) 473 + .where(eq(daytonaAuth.sandboxId, record.id)) 474 + .execute(), 475 + ]); 430 476 431 477 sandbox = await getSandboxById( 432 478 record.provider as Provider, 433 479 record.sandboxId!, 434 - decrypt(spriteAuthParams?.spriteToken), 480 + decrypt(spriteAuthParams?.spriteToken || daytonaAuthParams?.apiKey), 481 + daytonaAuthParams?.organizationId, 435 482 ); 436 483 437 484 if (!sandbox) { ··· 461 508 return c.json({ error: "Sandbox provider not supported" }, 400); 462 509 } 463 510 464 - const [spriteAuthParams] = await c.var.db 465 - .select() 466 - .from(spriteAuth) 467 - .where(eq(spriteAuth.sandboxId, record.id)) 468 - .execute(); 511 + const [[spriteAuthParams], [daytonaAuthParams]] = await Promise.all([ 512 + c.var.db 513 + .select() 514 + .from(spriteAuth) 515 + .where(eq(spriteAuth.sandboxId, record.id)) 516 + .execute(), 517 + c.var.db 518 + .select() 519 + .from(daytonaAuth) 520 + .where(eq(daytonaAuth.sandboxId, record.id)) 521 + .execute(), 522 + ]); 469 523 470 524 sandbox = await getSandboxById( 471 525 record.provider as Provider, 472 526 record.sandboxId!, 473 - decrypt(spriteAuthParams?.spriteToken), 527 + decrypt(spriteAuthParams?.spriteToken || daytonaAuthParams?.apiKey), 528 + daytonaAuthParams?.organizationId, 474 529 ); 475 530 476 531 if (!sandbox) {
+9 -4
apps/sandbox/src/providers/daytona/mod.ts
··· 135 135 } 136 136 const daytona = new Daytona({ 137 137 organizationId: options.organizationId, 138 - apiKey: process.env.DAYTONA_API_KEY, 138 + apiKey: options.daytonaApiKey, 139 139 apiUrl: process.env.DAYTONA_API_URL, 140 140 _experimental: {}, 141 141 }); 142 142 143 143 const sandbox = await daytona.create({ 144 144 language: "typescript", 145 - snapshot: process.env.DAYTONA_SNAPSHOT, 145 + // snapshot: process.env.DAYTONA_SNAPSHOT, 146 146 envVars: options.envVars, 147 147 }); 148 148 149 149 return new DaytonaSandbox(sandbox); 150 150 } 151 151 152 - async get(id: string): Promise<BaseSandbox> { 152 + async get( 153 + id: string, 154 + apiKey?: string, 155 + organizationId?: string, 156 + ): Promise<BaseSandbox> { 153 157 const daytona = new Daytona({ 154 - apiKey: process.env.DAYTONA_API_KEY, 158 + apiKey, 155 159 apiUrl: process.env.DAYTONA_API_URL, 160 + organizationId, 156 161 _experimental: {}, 157 162 }); 158 163
+4 -1
apps/sandbox/src/providers/mod.ts
··· 45 45 memory?: Memory; 46 46 spriteToken?: string; 47 47 spriteName?: string; 48 + daytonaApiKey?: string; 49 + organizationId?: string; 48 50 [key: string]: any; 49 51 } 50 52 ··· 79 81 provider: Provider, 80 82 id: string, 81 83 token?: string, 84 + organizationId?: string, 82 85 ): Promise<BaseSandbox> { 83 86 switch (provider) { 84 87 case "daytona": 85 88 return import("./daytona/mod.ts").then((module) => 86 - new module.default().get(id), 89 + new module.default().get(id, token, organizationId), 87 90 ); 88 91 case "deno": { 89 92 const module = await import("./deno/mod.ts");