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 organizationId to sandbox provider prefs

Persist Daytona organizationId in daytona_auth, lexicon, and types.
Include the field in get/put preferences handlers.
Collect and validate it in the web settings form.

+89 -31
+4
apps/api/lexicons/sandbox/defs.json
··· 318 318 "redactedApiKey": { 319 319 "type": "string", 320 320 "description": "The redacted API key for the sandbox provider, returned in API responses." 321 + }, 322 + "organizationId": { 323 + "type": "string", 324 + "description": "The ID of the organization in the sandbox provider, if applicable. This can be used to associate the sandbox with a specific organization or team within the provider's platform." 321 325 } 322 326 } 323 327 },
+5
apps/api/pkl/defs/sandbox/defs.pkl
··· 320 320 type = "string" 321 321 description = "The redacted API key for the sandbox provider, returned in API responses." 322 322 } 323 + ["organizationId"] = new StringType { 324 + type = "string" 325 + description = 326 + "The ID of the organization in the sandbox provider, if applicable. This can be used to associate the sandbox with a specific organization or team within the provider's platform." 327 + } 323 328 } 324 329 } 325 330 ["preferences"] = new Array {
+5
apps/api/src/lexicon/lexicons.ts
··· 942 942 description: 943 943 "The redacted API key for the sandbox provider, returned in API responses.", 944 944 }, 945 + organizationId: { 946 + type: "string", 947 + description: 948 + "The ID of the organization in the sandbox provider, if applicable. This can be used to associate the sandbox with a specific organization or team within the provider's platform.", 949 + }, 945 950 }, 946 951 }, 947 952 preferences: {
+2
apps/api/src/lexicon/types/io/pocketenv/sandbox/defs.ts
··· 223 223 apiKey?: string; 224 224 /** The redacted API key for the sandbox provider, returned in API responses. */ 225 225 redactedApiKey?: string; 226 + /** The ID of the organization in the sandbox provider, if applicable. This can be used to associate the sandbox with a specific organization or team within the provider's platform. */ 227 + organizationId?: string; 226 228 [k: string]: unknown; 227 229 } 228 230
+1 -3
apps/api/src/schema/daytona-auth.ts
··· 6 6 const daytonaAuth = pgTable( 7 7 "daytona_auth", 8 8 { 9 - id: text("id") 10 - .primaryKey() 11 - .default(sql`xata_id()`), 9 + id: text("id").primaryKey().default(sql`xata_id()`), 12 10 sandboxId: text("sandbox_id") 13 11 .notNull() 14 12 .references(() => sandboxes.id, { onDelete: "cascade" }),
+1
apps/api/src/xrpc/io/pocketenv/sandbox/getPreferences.ts
··· 75 75 $type: "io.pocketenv.sandbox.defs#sandboxProviderPref" as const, 76 76 name: "daytona" as const, 77 77 redactedApiKey: daytona.redactedApiKey, 78 + organizationId: daytona.organizationId, 78 79 }) || 79 80 (deno && { 80 81 $type: "io.pocketenv.sandbox.defs#sandboxProviderPref" as const,
+1
apps/api/src/xrpc/io/pocketenv/sandbox/putPreferences.ts
··· 133 133 sandboxId: input.body.sandboxId, 134 134 apiKey: pref.apiKey!, 135 135 redactedApiKey: pref.redactedApiKey!, 136 + organizationId: pref.organizationId!, 136 137 }) 137 138 .onConflictDoUpdate({ 138 139 target: [daytonaAuth.sandboxId, daytonaAuth.userId],
+7
apps/sandbox/src/types/sandbox.ts
··· 39 39 keepAlive: z.boolean().optional().default(false), 40 40 spriteToken: z.string().optional(), 41 41 redacredSpriteToken: z.string().optional(), 42 + daytonaOrganizationId: z.string().optional(), 43 + denoDeployToken: z.string().optional(), 44 + redactedDenoDeployToken: z.string().optional(), 45 + redactedDaytonaApiKey: z.string().optional(), 46 + daytonaApiKey: z.string().optional(), 47 + vercelToken: z.string().optional(), 48 + redactedVercelToken: z.string().optional(), 42 49 vcpus: z.number().optional().default(2), 43 50 memory: z.number().optional().default(4), 44 51 disk: z.number().optional().default(3),
+62 -28
apps/web/src/pages/settings/provider/Provider.tsx
··· 29 29 .object({ 30 30 provider: z.enum(["cloudflare", "daytona", "vercel", "deno", "sprites"]), 31 31 apiKey: z.string().optional(), 32 + organizationId: z.uuid().optional(), 32 33 }) 33 34 .superRefine((data, ctx) => { 34 35 if (data.provider !== "cloudflare" && !data.apiKey?.trim()) { ··· 36 37 code: z.ZodIssueCode.custom, 37 38 message: `${LABELS[data.provider as keyof typeof LABELS]} is required`, 38 39 path: ["apiKey"], 40 + }); 41 + } 42 + if (data.provider === "daytona" && !data.organizationId?.trim()) { 43 + ctx.addIssue({ 44 + code: z.ZodIssueCode.custom, 45 + message: "Daytona Organization ID is required", 46 + path: ["organizationId"], 39 47 }); 40 48 } 41 49 }); ··· 76 84 if (providerPref) { 77 85 setValue("provider", providerPref.name as FormValues["provider"]); 78 86 setValue("apiKey", providerPref.redactedApiKey ?? ""); 87 + setValue("organizationId", providerPref.organizationId ?? ""); 79 88 } 80 89 }, [preferences, setValue]); 81 90 82 91 const provider = watch("provider") as Provider; 83 92 84 - const { onChange: onProviderChange, ...providerRegister } = register("provider"); 93 + const { onChange: onProviderChange, ...providerRegister } = 94 + register("provider"); 85 95 86 96 const onSubmit = async (values: FormValues) => { 87 97 const pref: SandboxProvider = { ··· 89 99 name: values.provider, 90 100 }; 91 101 102 + if (values.provider === "daytona" && values.organizationId?.trim()) { 103 + pref.organizationId = values.organizationId.trim(); 104 + } 105 + 92 106 if (values.apiKey?.includes("**") && values.provider !== "cloudflare") { 93 107 return; 94 108 } ··· 138 152 </p> 139 153 <form onSubmit={handleSubmit(onSubmit)}> 140 154 <div className="w-full overflow-x-auto"> 141 - <div className="flex flex-row"> 142 - <div className="w-96 mr-6"> 143 - <label className="label-text"> 144 - Pick your Sandbox Provider 145 - </label> 146 - <select 147 - {...providerRegister} 148 - onChange={(e) => { 149 - onProviderChange(e); 150 - setValue("apiKey", ""); 151 - }} 152 - className="select select-lg font-medium text-[15px]" 153 - > 154 - <option value="cloudflare"> 155 - Cloudflare Sandbox (Recommended) 156 - </option> 157 - <option value="daytona">Daytona</option> 158 - <option value="vercel">Vercel Sandbox</option> 159 - <option value="deno">Deno Sandbox</option> 160 - <option value="sprites">Sprites</option> 161 - </select> 155 + <div className="w-fit"> 156 + <div className="flex flex-row"> 157 + <div className="w-96 mr-6"> 158 + <label className="label-text"> 159 + Pick your Sandbox Provider 160 + </label> 161 + <select 162 + {...providerRegister} 163 + onChange={(e) => { 164 + onProviderChange(e); 165 + setValue("apiKey", ""); 166 + setValue("organizationId", ""); 167 + }} 168 + className="select select-lg font-medium text-[15px]" 169 + > 170 + <option value="cloudflare"> 171 + Cloudflare Sandbox (Recommended) 172 + </option> 173 + <option value="daytona">Daytona</option> 174 + <option value="vercel">Vercel Sandbox</option> 175 + <option value="deno">Deno Sandbox</option> 176 + <option value="sprites">Sprites</option> 177 + </select> 178 + </div> 179 + {provider !== "cloudflare" && ( 180 + <div className="w-96"> 181 + <label className="label-text">{LABELS[provider]}</label> 182 + <input 183 + {...register("apiKey")} 184 + type="text" 185 + className="input input-lg font-medium text-[15px]" 186 + /> 187 + {errors.apiKey && ( 188 + <p className="text-error text-sm mt-1"> 189 + {errors.apiKey.message} 190 + </p> 191 + )} 192 + </div> 193 + )} 162 194 </div> 163 - {provider !== "cloudflare" && ( 164 - <div className="w-96"> 165 - <label className="label-text">{LABELS[provider]}</label> 195 + {provider === "daytona" && ( 196 + <div className="w-full mt-4"> 197 + <label className="label-text"> 198 + Daytona Organization ID 199 + </label> 166 200 <input 167 - {...register("apiKey")} 201 + {...register("organizationId")} 168 202 type="text" 169 203 className="input input-lg font-medium text-[15px]" 170 204 /> 171 - {errors.apiKey && ( 205 + {errors.organizationId && ( 172 206 <p className="text-error text-sm mt-1"> 173 - {errors.apiKey.message} 207 + {errors.organizationId.message} 174 208 </p> 175 209 )} 176 210 </div>
+1
apps/web/src/types/preferences.ts
··· 10 10 name: string; 11 11 apiKey?: string; 12 12 redactedApiKey?: string; 13 + organizationId?: string; 13 14 $type: "io.pocketenv.sandbox.defs#sandboxProviderPref"; 14 15 }; 15 16