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.

Make backup creation asynchronous and add description

Rename response field ttl -> expiresAt and add an optional description
to the createBackup input and BackupViewBasic. Run backup and DB insert
in a background task (waitUntil); endpoint now returns an empty JSON
and the handler returns void, removing the immediate output schema.

+46 -66
+4 -7
apps/api/lexicons/sandbox/createBackup.json
··· 29 29 "type": "string", 30 30 "description": "The directory to backup." 31 31 }, 32 + "description": { 33 + "type": "string", 34 + "description": "An optional description for the backup." 35 + }, 32 36 "ttl": { 33 37 "type": "integer", 34 38 "description": "The time-to-live (TTL) for the backup in seconds. After this time, the backup will be automatically deleted. If not provided, the backup will expire after 3 days." 35 39 } 36 40 } 37 - } 38 - }, 39 - "output": { 40 - "encoding": "application/json", 41 - "schema": { 42 - "type": "ref", 43 - "ref": "io.pocketenv.sandbox.defs#backupViewBasic" 44 41 } 45 42 } 46 43 }
+6 -2
apps/api/lexicons/sandbox/defs.json
··· 448 448 "type": "string", 449 449 "description": "The directory that was backed up." 450 450 }, 451 - "ttl": { 451 + "description": { 452 + "type": "string", 453 + "description": "An optional description for the backup." 454 + }, 455 + "expiresAt": { 452 456 "type": "integer", 453 - "description": "The time-to-live (TTL) for the backup in seconds. After this time, the backup will be automatically deleted." 457 + "description": "Datetime when the backup will expire and be automatically deleted." 454 458 }, 455 459 "createdAt": { 456 460 "type": "string",
-7
apps/api/pkl/defs/sandbox/createBackup.pkl
··· 38 38 } 39 39 } 40 40 } 41 - output { 42 - encoding = "application/json" 43 - schema = new Ref { 44 - type = "ref" 45 - ref = "io.pocketenv.sandbox.defs#backupViewBasic" 46 - } 47 - } 48 41 } 49 42 }
+10 -9
apps/api/src/lexicon/lexicons.ts
··· 497 497 type: "string", 498 498 description: "The directory to backup.", 499 499 }, 500 + description: { 501 + type: "string", 502 + description: "An optional description for the backup.", 503 + }, 500 504 ttl: { 501 505 type: "integer", 502 506 description: 503 507 "The time-to-live (TTL) for the backup in seconds. After this time, the backup will be automatically deleted. If not provided, the backup will expire after 3 days.", 504 508 }, 505 509 }, 506 - }, 507 - }, 508 - output: { 509 - encoding: "application/json", 510 - schema: { 511 - type: "ref", 512 - ref: "lex:io.pocketenv.sandbox.defs#backupViewBasic", 513 510 }, 514 511 }, 515 512 }, ··· 1166 1163 type: "string", 1167 1164 description: "The directory that was backed up.", 1168 1165 }, 1169 - ttl: { 1166 + description: { 1167 + type: "string", 1168 + description: "An optional description for the backup.", 1169 + }, 1170 + expiresAt: { 1170 1171 type: "integer", 1171 1172 description: 1172 - "The time-to-live (TTL) for the backup in seconds. After this time, the backup will be automatically deleted.", 1173 + "Datetime when the backup will expire and be automatically deleted.", 1173 1174 }, 1174 1175 createdAt: { 1175 1176 type: "string",
+4 -11
apps/api/src/lexicon/types/io/pocketenv/sandbox/createBackup.ts
··· 6 6 import { lexicons } from "../../../../lexicons"; 7 7 import { isObj, hasProp } from "../../../../util"; 8 8 import { CID } from "multiformats/cid"; 9 - import type { HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 10 - import type * as IoPocketenvSandboxDefs from "./defs"; 9 + import { type HandlerAuth, HandlerPipeThrough } from "@atproto/xrpc-server"; 11 10 12 11 export interface QueryParams { 13 12 /** The sandbox ID. */ ··· 17 16 export interface InputSchema { 18 17 /** The directory to backup. */ 19 18 directory: string; 19 + /** An optional description for the backup. */ 20 + description?: string; 20 21 /** The time-to-live (TTL) for the backup in seconds. After this time, the backup will be automatically deleted. If not provided, the backup will expire after 3 days. */ 21 22 ttl?: number; 22 23 [k: string]: unknown; 23 24 } 24 25 25 - export type OutputSchema = IoPocketenvSandboxDefs.BackupViewBasic; 26 - 27 26 export interface HandlerInput { 28 27 encoding: "application/json"; 29 28 body: InputSchema; 30 29 } 31 30 32 - export interface HandlerSuccess { 33 - encoding: "application/json"; 34 - body: OutputSchema; 35 - headers?: { [key: string]: string }; 36 - } 37 - 38 31 export interface HandlerError { 39 32 status: number; 40 33 message?: string; 41 34 } 42 35 43 - export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough; 36 + export type HandlerOutput = HandlerError | void; 44 37 export type HandlerReqCtx<HA extends HandlerAuth = never> = { 45 38 auth: HA; 46 39 params: QueryParams;
+4 -2
apps/api/src/lexicon/types/io/pocketenv/sandbox/defs.ts
··· 339 339 id?: string; 340 340 /** The directory that was backed up. */ 341 341 directory?: string; 342 - /** The time-to-live (TTL) for the backup in seconds. After this time, the backup will be automatically deleted. */ 343 - ttl?: number; 342 + /** An optional description for the backup. */ 343 + description?: string; 344 + /** Datetime when the backup will expire and be automatically deleted. */ 345 + expiresAt?: number; 344 346 /** datetime when the backup was created. */ 345 347 createdAt?: string; 346 348 [k: string]: unknown;
+2 -17
apps/api/src/xrpc/io/pocketenv/sandbox/createBackup.ts
··· 6 6 import type { 7 7 InputSchema, 8 8 QueryParams, 9 - OutputSchema, 10 9 } from "lexicon/types/io/pocketenv/sandbox/createBackup"; 11 10 import generateJwt from "lib/generateJwt"; 12 11 import schema from "schema"; ··· 51 50 ? ctx.cfsandbox(record.sandboxes.base!) 52 51 : ctx.sandbox(); 53 52 54 - const { data: backup } = await sandbox.post<SelectBakcup>( 53 + await sandbox.post<SelectBakcup>( 55 54 `/v1/sandboxes/${record.sandboxes.id}/backup`, 56 55 { 57 56 directory: input.directory, ··· 64 63 }, 65 64 }, 66 65 ); 67 - 68 - return { 69 - id: backup.backupId, 70 - directory: backup.directory, 71 - description: backup.description, 72 - expiresAt: backup.expiresAt 73 - ? new Date(backup.expiresAt).toISOString() 74 - : undefined, 75 - createdAt: new Date(backup.createdAt).toISOString(), 76 - } satisfies OutputSchema; 77 66 }; 78 67 server.io.pocketenv.sandbox.createBackup({ 79 68 auth: ctx.authVerifier, 80 69 handler: async ({ input, params, auth }) => { 81 - const result = await createBackup(input.body, params, auth); 82 - return { 83 - encoding: "application/json", 84 - body: result, 85 - }; 70 + await createBackup(input.body, params, auth); 86 71 }, 87 72 }); 88 73 }
+16 -11
apps/cf-sandbox/src/routes/sandboxes.ts
··· 540 540 const params = await c.req.json<BackupParams>(); 541 541 542 542 const sandbox = await createSandbox("cloudflare", { id: record.sandboxId! }); 543 - const backupId = await sandbox.backup(params.directory, params.ttl); 543 + 544 + c.executionCtx.waitUntil( 545 + (async () => { 546 + const backupId = await sandbox.backup(params.directory, params.ttl); 547 + 548 + await c.var.db.insert(backups).values({ 549 + backupId, 550 + sandboxId: record.id, 551 + directory: params.directory, 552 + description: params.description, 553 + expiresAt: params.ttl ? dayjs().add(params.ttl, "second").toDate() : dayjs().add(3, "days").toDate(), 554 + }) 555 + .execute(); 556 + })() 557 + ); 544 558 545 - const backup = await c.var.db.insert(backups).values({ 546 - backupId, 547 - sandboxId: record.id, 548 - directory: params.directory, 549 - description: params.description, 550 - expiresAt: params.ttl ? dayjs().add(params.ttl, "second").toDate() : dayjs().add(3, "days").toDate(), 551 - }) 552 - .returning() 553 - .execute(); 554 559 555 - return c.json(backup); 560 + return c.json({}); 556 561 }); 557 562 558 563 sandboxRoutes.post("/v1/sandboxes/:sandboxId/restore", async (c) => {