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 backup commands to CLI

Implement create, list and restore backup functions using
@pocketenv/sdk. Validate TTL with zod and format output with
cli-table3/dayjs. Bump @pocketenv/sdk to v0.2.10 and set the
default backup TTL to 3d

+121 -78
+4 -73
apps/cli/package-lock.json
··· 10 10 "license": "MPL-2.0", 11 11 "dependencies": { 12 12 "@inquirer/prompts": "^8.3.0", 13 - "@pocketenv/sdk": "^0.2.7", 13 + "@pocketenv/sdk": "^0.2.10", 14 14 "axios": "^1.13.6", 15 15 "chalk": "^5.6.2", 16 16 "cli-table3": "^0.6.5", ··· 54 54 "optional": true, 55 55 "engines": { 56 56 "node": ">=0.1.90" 57 - } 58 - }, 59 - "node_modules/@emnapi/core": { 60 - "version": "1.9.2", 61 - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", 62 - "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", 63 - "dev": true, 64 - "license": "MIT", 65 - "optional": true, 66 - "peer": true, 67 - "dependencies": { 68 - "@emnapi/wasi-threads": "1.2.1", 69 - "tslib": "^2.4.0" 70 - } 71 - }, 72 - "node_modules/@emnapi/runtime": { 73 - "version": "1.9.2", 74 - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", 75 - "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", 76 - "dev": true, 77 - "license": "MIT", 78 - "optional": true, 79 - "peer": true, 80 - "dependencies": { 81 - "tslib": "^2.4.0" 82 57 } 83 58 }, 84 59 "node_modules/@emnapi/wasi-threads": { ··· 949 924 } 950 925 }, 951 926 "node_modules/@pocketenv/sdk": { 952 - "version": "0.2.7", 953 - "resolved": "https://registry.npmjs.org/@pocketenv/sdk/-/sdk-0.2.7.tgz", 954 - "integrity": "sha512-jwqhTNGzNPcRwGpUjzh/EG9CKRsASxJlcw/b7P5MMxqyM2qx9Dr35SnDDpAgPUEKYUzvUohljuzPcBePo9F/gw==", 927 + "version": "0.2.10", 928 + "resolved": "https://registry.npmjs.org/@pocketenv/sdk/-/sdk-0.2.10.tgz", 929 + "integrity": "sha512-t6/8cbifJS5AZMZT+l2kHf6Dx8t77LAk+sQxtZa4KmZrdnNszGLLcS0QK0EKILKW2Yg4z80XVRomtSdvMItEPw==", 955 930 "license": "MIT", 956 931 "dependencies": { 957 932 "ignore": "^7.0.5", ··· 5754 5729 "vite": { 5755 5730 "optional": true 5756 5731 } 5757 - } 5758 - }, 5759 - "node_modules/vitest/node_modules/esbuild": { 5760 - "version": "0.28.0", 5761 - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", 5762 - "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", 5763 - "dev": true, 5764 - "hasInstallScript": true, 5765 - "license": "MIT", 5766 - "optional": true, 5767 - "peer": true, 5768 - "bin": { 5769 - "esbuild": "bin/esbuild" 5770 - }, 5771 - "engines": { 5772 - "node": ">=18" 5773 - }, 5774 - "optionalDependencies": { 5775 - "@esbuild/aix-ppc64": "0.28.0", 5776 - "@esbuild/android-arm": "0.28.0", 5777 - "@esbuild/android-arm64": "0.28.0", 5778 - "@esbuild/android-x64": "0.28.0", 5779 - "@esbuild/darwin-arm64": "0.28.0", 5780 - "@esbuild/darwin-x64": "0.28.0", 5781 - "@esbuild/freebsd-arm64": "0.28.0", 5782 - "@esbuild/freebsd-x64": "0.28.0", 5783 - "@esbuild/linux-arm": "0.28.0", 5784 - "@esbuild/linux-arm64": "0.28.0", 5785 - "@esbuild/linux-ia32": "0.28.0", 5786 - "@esbuild/linux-loong64": "0.28.0", 5787 - "@esbuild/linux-mips64el": "0.28.0", 5788 - "@esbuild/linux-ppc64": "0.28.0", 5789 - "@esbuild/linux-riscv64": "0.28.0", 5790 - "@esbuild/linux-s390x": "0.28.0", 5791 - "@esbuild/linux-x64": "0.28.0", 5792 - "@esbuild/netbsd-arm64": "0.28.0", 5793 - "@esbuild/netbsd-x64": "0.28.0", 5794 - "@esbuild/openbsd-arm64": "0.28.0", 5795 - "@esbuild/openbsd-x64": "0.28.0", 5796 - "@esbuild/openharmony-arm64": "0.28.0", 5797 - "@esbuild/sunos-x64": "0.28.0", 5798 - "@esbuild/win32-arm64": "0.28.0", 5799 - "@esbuild/win32-ia32": "0.28.0", 5800 - "@esbuild/win32-x64": "0.28.0" 5801 5732 } 5802 5733 }, 5803 5734 "node_modules/vitest/node_modules/estree-walker": {
+1 -1
apps/cli/package.json
··· 42 42 }, 43 43 "dependencies": { 44 44 "@inquirer/prompts": "^8.3.0", 45 - "@pocketenv/sdk": "^0.2.7", 45 + "@pocketenv/sdk": "^0.2.10", 46 46 "axios": "^1.13.6", 47 47 "chalk": "^5.6.2", 48 48 "cli-table3": "^0.6.5",
+115 -3
apps/cli/src/cmd/backup.ts
··· 1 - export function createBackup() { 1 + import { Sandbox } from '@pocketenv/sdk'; 2 + import consola from 'consola'; 3 + import z from 'zod'; 4 + import { c } from '../theme'; 5 + import process from "node:process"; 6 + import Table from "cli-table3"; 7 + import dayjs from "dayjs"; 8 + import relativeTime from "dayjs/plugin/relativeTime"; 9 + 10 + dayjs.extend(relativeTime); 11 + 12 + const ttlSchema = z 13 + .string() 14 + .regex(/^\d+(m|h|d)$/, 'Invalid TTL format (e.g. 10m, 2h, 7d)') 15 + .transform((value) => { 16 + const amount = parseInt(value.slice(0, -1), 10); 17 + const unit = value.slice(-1); 18 + 19 + switch (unit) { 20 + case 'm': 21 + return amount * 60; 22 + case 'h': 23 + return amount * 60 * 60; 24 + case 'd': 25 + return amount * 60 * 60 * 24; 26 + default: 27 + throw new Error('Invalid TTL unit'); 28 + } 29 + }); 30 + 31 + const backupOptionsSchema = z.object({ 32 + description: z.string().optional(), 33 + ttl: ttlSchema.optional(), 34 + }); 35 + 36 + export type BackupOptions = z.infer<typeof backupOptionsSchema>; 37 + 38 + export async function createBackup(sandboxId: string, directory: string, options: BackupOptions) { 39 + try { 40 + const { data, error } = backupOptionsSchema.safeParse(options); 41 + if (error) { 42 + consola.error(`Invalid backup options: ${error.issues[0]?.message}`); 43 + process.exit(1); 44 + } 45 + 46 + const sandbox = await Sandbox.get(sandboxId); 47 + const { description, ttl } = data; 2 48 49 + await sandbox.backup.create( 50 + directory, 51 + description, 52 + ttl, 53 + ); 54 + consola.success(`Backup request for sandbox ${c.primary(sandboxId)} at directory ${c.primary(directory)} created successfully`); 55 + consola.log(` This may take a few moments to complete.\n Run ${c.primary(`pocketenv backup ls ${sandboxId}`)} to check the status of your backup.`); 56 + } catch (e) { 57 + consola.error(`Failed to create backup for sandbox`, e); 58 + process.exit(1); 59 + } 3 60 } 4 61 5 - export function restoreBackup() { 62 + export async function restoreBackup(backupId: string) { 63 + try { 64 + await Sandbox.restoreBackup(backupId); 65 + consola.success(`Backup ${c.primary(backupId)} restored successfully`); 66 + } catch { 67 + consola.error(`Failed to restore backup ${c.primary(backupId)}`); 68 + process.exit(1); 69 + } 6 70 } 7 71 8 - export function listBackups() { 72 + export async function listBackups(sandboxId: string) { 73 + const sandbox = await Sandbox.get(sandboxId); 74 + try { 75 + const { backups } = await sandbox.backup.list(); 76 + 77 + const table = new Table({ 78 + head: [ 79 + c.primary("BACKUP ID"), 80 + c.primary("DIRECTORY"), 81 + c.primary("CREATED AT"), 82 + c.primary("EXPIRES AT"), 83 + ], 84 + chars: { 85 + top: "", 86 + "top-mid": "", 87 + "top-left": "", 88 + "top-right": "", 89 + bottom: "", 90 + "bottom-mid": "", 91 + "bottom-left": "", 92 + "bottom-right": "", 93 + left: "", 94 + "left-mid": "", 95 + mid: "", 96 + "mid-mid": "", 97 + right: "", 98 + "right-mid": "", 99 + middle: " ", 100 + }, 101 + style: { 102 + border: [], 103 + head: [], 104 + }, 105 + }); 106 + 107 + for (const backup of backups) { 108 + table.push([ 109 + c.secondary(backup.id), 110 + backup.directory, 111 + dayjs(backup.createdAt).fromNow(), 112 + backup.expiresAt ? dayjs(backup.expiresAt).fromNow() : "Never", 113 + ]); 114 + } 115 + 116 + consola.log(table.toString()); 117 + } catch { 118 + consola.error(`Failed to list backups for sandbox ${c.primary(sandboxId)}`); 119 + process.exit(1); 120 + } 9 121 }
+1 -1
apps/cli/src/index.ts
··· 206 206 .argument("<sandbox>", "the sandbox to create a backup for") 207 207 .argument("<directory>", "the directory to backup") 208 208 .option("--description, -d <description>", "an optional description for the backup") 209 - .option("--ttl, -t <ttl>", "time to live for the backup (e.g., 24h, 7d)", "7d") 209 + .option("--ttl, -t <ttl>", "time to live for the backup (e.g., 24h, 7d)", "3d") 210 210 .description("create a backup for the given sandbox") 211 211 .action(createBackup); 212 212