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 pref and deletion filter

Add a SandboxProvider type and include it in the Preference union.
Expose a getPreferences client API.
Allow deleting auth rows by matching sandbox id, uri, or name.
Change default provider to cloudflare and re-enable provider sidebar
link.

+46 -5
+24 -1
apps/api/src/xrpc/io/pocketenv/sandbox/putPreferences.ts
··· 1 1 import { XRPCError, type HandlerAuth } from "@atproto/xrpc-server"; 2 2 import { updateSandbox } from "atproto/sandbox"; 3 3 import type { Context } from "context"; 4 - import { and, eq, type ExtractTablesWithRelations } from "drizzle-orm"; 4 + import { and, eq, or, type ExtractTablesWithRelations } from "drizzle-orm"; 5 5 import type { Server } from "lexicon"; 6 6 import { 7 7 isSandboxDetailsPref, ··· 189 189 .execute(); 190 190 break; 191 191 default: 192 + const sandboxFilter = or( 193 + eq(sandboxes.sandboxId, input.body.sandboxId), 194 + eq(sandboxes.uri, input.body.sandboxId), 195 + eq(sandboxes.name, input.body.sandboxId), 196 + ); 197 + await Promise.all([ 198 + tx 199 + .delete(daytonaAuth) 200 + .where(and(eq(daytonaAuth.userId, user.id), sandboxFilter)) 201 + .execute(), 202 + tx 203 + .delete(denoAuth) 204 + .where(and(eq(denoAuth.userId, user.id), sandboxFilter)) 205 + .execute(), 206 + tx 207 + .delete(vercelAuth) 208 + .where(and(eq(vercelAuth.userId, user.id), sandboxFilter)) 209 + .execute(), 210 + tx 211 + .delete(spriteAuth) 212 + .where(and(eq(spriteAuth.userId, user.id), sandboxFilter)) 213 + .execute(), 214 + ]); 192 215 break; 193 216 } 194 217 };
+11
apps/web/src/api/preferences.ts
··· 11 11 }, 12 12 }, 13 13 ); 14 + 15 + export const getPreferences = (sandboxId: string) => 16 + client.get<{ preferences: Preference[] }>( 17 + "/xrpc/io.pocketenv.sandbox.getPreferences", 18 + { 19 + params: { id: sandboxId }, 20 + headers: { 21 + Authorization: `Bearer ${localStorage.getItem("token")}`, 22 + }, 23 + }, 24 + );
+1 -1
apps/web/src/pages/settings/provider/Provider.tsx
··· 46 46 formState: { errors }, 47 47 } = useForm<FormValues>({ 48 48 resolver: zodResolver(schema), 49 - defaultValues: { provider: "daytona", apiKey: "" }, 49 + defaultValues: { provider: "cloudflare", apiKey: "" }, 50 50 }); 51 51 52 52 const provider = useWatch({ control, name: "provider" }) as Provider;
+2 -2
apps/web/src/pages/settings/sidebar/Sidebar.tsx
··· 70 70 {!isCollapsed && "General"} 71 71 </Link> 72 72 </li> 73 - {/*<li> 73 + <li> 74 74 <Link 75 75 to={`/${did}/sandbox/${rkey}/provider`} 76 76 className={`${ ··· 85 85 ></span> 86 86 {!isCollapsed && "Sandbox Provider"} 87 87 </Link> 88 - </li>*/} 88 + </li> 89 89 <li> 90 90 <Link 91 91 to={`/${did}/sandbox/${rkey}/repository`}
+8 -1
apps/web/src/types/preferences.ts
··· 6 6 $type: "io.pocketenv.sandbox.defs#sandboxDetailsPref"; 7 7 }; 8 8 9 - export type Preference = SandboxDetails; 9 + export type SandboxProvider = { 10 + name: string; 11 + apiKey?: string; 12 + redactedApiKey?: string; 13 + $type: "io.pocketenv.sandbox.defs#sandboxProviderPref"; 14 + }; 15 + 16 + export type Preference = SandboxDetails | SandboxProvider;