feat: update POST /api/sandboxes to use command execution#195
Conversation
Updated dependencies to fix Next.js and React CVE vulnerabilities. The fix-react2shell-next tool automatically updated the following packages to their secure versions: - next - react-server-dom-webpack - react-server-dom-parcel - react-server-dom-turbopack All package.json files have been scanned and vulnerable versions have been patched to the correct fixed versions based on the official React advisory. Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
- Change request body from { prompt } to { command, args?, cwd? }
- Add account snapshot support for creating sandboxes from snapshots
- Update trigger payload to include command, args, cwd, sandboxId, accountId
- Return runId in response from triggered task
- Update tests to reflect new API structure
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Braintrust eval reportCatalog Opportunity Analysis Evaluation (HEAD-1770154219)
Catalog Songs Count Evaluation (HEAD-1770154218)
First Week Album Sales Evaluation (HEAD-1770154219)
Memory & Storage Tools Evaluation (HEAD-1770154219)
Monthly Listeners Tracking Evaluation (HEAD-1770154219)
Search Web Tool Evaluation (HEAD-1770154219)
Social Scraping Evaluation (HEAD-1770154219)
Spotify Followers Evaluation (HEAD-1770154219)
Spotify Tools Evaluation (HEAD-1770154219)
TikTok Analytics Questions Evaluation (HEAD-1770154218)
|
The Vercel Sandbox SDK requires snapshotId to be passed via the source parameter with type: 'snapshot', not as a direct property. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📝 WalkthroughWalkthroughThe changes transition sandbox creation from prompt-based to command-based execution, introducing account-level snapshot detection. The workflow now fetches snapshots when available and conditionally applies defaults based on the presence of a snapshot source during sandbox creation. Changes
Sequence DiagramsequenceDiagram
participant Client
participant Handler as POST /api/sandboxes
participant DB as Supabase
participant SandboxSDK as Sandbox.create
participant Trigger as triggerRunSandboxCommand
Client->>Handler: POST {command, args, cwd}
Handler->>DB: selectAccountSnapshot(accountId)
alt Snapshot Exists
DB-->>Handler: AccountSnapshot
Handler->>SandboxSDK: create({source: {type: "snapshot", snapshotId}})
else No Snapshot
DB-->>Handler: null
Handler->>SandboxSDK: create({vcpus: DEFAULT_VCPUS, runtime: DEFAULT_RUNTIME})
end
SandboxSDK-->>Handler: SandboxCreatedResponse {sandboxId}
Handler->>DB: Save to account_sandboxes
Handler->>Trigger: triggerRunSandboxCommand({command, args, cwd, sandboxId, accountId})
Trigger-->>Handler: {handle with runId}
Handler-->>Client: {sandboxes: [...], runId}
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ❌ 1❌ Failed checks (1 warning)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@lib/supabase/account_snapshots/selectAccountSnapshot.ts`:
- Around line 1-31: The file is misnamed: it targets the account_snapshots table
but is named selectAccountSnapshot.ts; rename the file to
selectAccountSnapshots.ts to follow the select[TableName].ts convention, keep
the exported function selectAccountSnapshot unchanged, and then update all
imports that reference the old filename to the new path (including any
barrels/indices). Search the repo for imports of "selectAccountSnapshot" and
occurrences of the old filename and replace them with the new filename so builds
and type imports resolve correctly.
- Around line 1-31: The file defines a local AccountSnapshot interface and uses
it as the return type for selectAccountSnapshot; replace that with the generated
DB types: import Tables from '@/types/database.types' (or the correct named
export) and change the function signature to return
Promise<Tables<"account_snapshots"> | null>, removing the local AccountSnapshot
interface so the function (selectAccountSnapshot) uses the canonical
Tables<"account_snapshots"> type used elsewhere (e.g., selectSocials.ts) to keep
typings in sync with the DB schema.
🧹 Nitpick comments (2)
lib/sandbox/createSandbox.ts (1)
25-39: Extract the shared timeout value to avoid drift between branches.Both branches use the same timeout literal; hoisting it reduces duplication and future divergence risk.
♻️ Suggested refactor
export async function createSandbox( options: CreateSandboxOptions = {}, ): Promise<SandboxCreatedResponse> { const { snapshotId } = options; + const timeout = ms("10m"); const sandbox = snapshotId ? await Sandbox.create({ source: { type: "snapshot", snapshotId }, - timeout: ms("10m"), + timeout, }) : await Sandbox.create({ resources: { vcpus: 4 }, - timeout: ms("10m"), + timeout, runtime: "node22", });As per coding guidelines, DRY: Consolidate similar logic into shared utilities.
lib/sandbox/validateSandboxBody.ts (1)
8-12: Harden validation for whitespace-only command/cwd and empty args entries.
min(1)allows strings of spaces. Consider trimming and enforcing non-empty entries forargsitems.♻️ Suggested validation tightening
export const sandboxBodySchema = z.object({ - command: z.string({ message: "command is required" }).min(1, "command cannot be empty"), - args: z.array(z.string()).optional(), - cwd: z.string().optional(), + command: z + .string({ message: "command is required" }) + .trim() + .min(1, "command cannot be empty"), + args: z.array(z.string().min(1)).optional(), + cwd: z.string().trim().min(1).optional(), });
Update test payloads to match new API: command, args, cwd, sandboxId, accountId Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Export CreateSandboxParams type extracted from Sandbox.create signature - Accept full SDK params instead of custom snapshotId option - Apply sensible defaults for timeout, resources, and runtime - Update handler to pass source object for snapshots - Add tests for new parameter combinations Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use 'in' operator to check for runtime/resources properties since they don't exist on the snapshot variant of the union type. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Spread defaults first, then override with params. This avoids type conflicts between snapshot and base param variants. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@lib/sandbox/createSandbox.ts`:
- Around line 28-44: createSandbox currently spreads ...params which can
overwrite the default resources object and drop DEFAULT_VCPUS when callers pass
a partial params.resources; update the non-snapshot branch that calls
Sandbox.create so resources are merged with defaults instead of overwritten
(e.g., set resources to an object that starts with vcpus: DEFAULT_VCPUS and then
spreads params.resources), ensuring you still spread ...params for other fields
and preserve timeout/default runtime handling; refer to createSandbox,
Sandbox.create, params, resources, and DEFAULT_VCPUS when making this change.
| export async function createSandbox(params: CreateSandboxParams = {}): Promise<SandboxCreatedResponse> { | ||
| const hasSnapshotSource = params.source && "type" in params.source && params.source.type === "snapshot"; | ||
|
|
||
| // Pass params directly to SDK - it handles all the type variants | ||
| const sandbox = await Sandbox.create( | ||
| hasSnapshotSource | ||
| ? { | ||
| ...params, | ||
| timeout: params.timeout ?? DEFAULT_TIMEOUT, | ||
| } | ||
| : { | ||
| resources: { vcpus: DEFAULT_VCPUS }, | ||
| timeout: params.timeout ?? DEFAULT_TIMEOUT, | ||
| runtime: DEFAULT_RUNTIME, | ||
| ...params, | ||
| }, | ||
| ); |
There was a problem hiding this comment.
Preserve default vCPU when partial resources are provided.
In the non-snapshot branch, ...params overwrites resources entirely. If a caller passes a partial resources (e.g., only memoryMB), the default vcpus is dropped, which can lead to missing/invalid resource settings. Consider merging defaults after spreading params.
✅ Suggested fix (merge resources after spreading params)
: {
- resources: { vcpus: DEFAULT_VCPUS },
timeout: params.timeout ?? DEFAULT_TIMEOUT,
runtime: DEFAULT_RUNTIME,
...params,
+ resources: { vcpus: DEFAULT_VCPUS, ...(params.resources ?? {}) },
},🤖 Prompt for AI Agents
In `@lib/sandbox/createSandbox.ts` around lines 28 - 44, createSandbox currently
spreads ...params which can overwrite the default resources object and drop
DEFAULT_VCPUS when callers pass a partial params.resources; update the
non-snapshot branch that calls Sandbox.create so resources are merged with
defaults instead of overwritten (e.g., set resources to an object that starts
with vcpus: DEFAULT_VCPUS and then spreads params.resources), ensuring you still
spread ...params for other fields and preserve timeout/default runtime handling;
refer to createSandbox, Sandbox.create, params, resources, and DEFAULT_VCPUS
when making this change.
- Wrap triggerRunSandboxCommand in try-catch to prevent hanging - Return 200 with sandbox info even if trigger fails (runId omitted) - Update JSDoc to document new command/args/cwd request format - Update tests to reflect new graceful failure behavior Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add console.log at each step to identify where the request is hanging: - Request received - Validation - Snapshot lookup - Sandbox creation - DB insert - Task trigger - Response building Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Follow the select[TableName].ts naming convention where the table name is account_snapshots (plural). Function name unchanged. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@app/api/sandboxes/route.ts`:
- Around line 29-36: Update the response documentation to indicate that runId
may be absent/null: modify the Response schema comment in the file to show runId
as optional (e.g., runId?: string | null) for the sandboxes array returned by
createSandboxPostHandler, since createSandboxPostHandler can omit runId when the
trigger fails; mention it may be null or undefined to reflect trigger failure
behavior.
🧹 Nitpick comments (2)
lib/sandbox/createSandboxPostHandler.ts (2)
27-55: Consider extracting snapshot lookup + trigger orchestration into helpers.This handler now does validation, snapshot selection, sandbox creation, DB insert, trigger, and response shaping. Splitting into small helpers would improve single responsibility and keep this under 50 lines.
As per coding guidelines,
lib/**/*.ts: For domain functions, ensure: Single responsibility per function ... Keep functions under 50 lines ... Avoid side effects.
58-65: Keep response shape consistent when trigger fails.Right now
runIdis omitted on failure, forcing clients to branch on missing fields. Consider returning a stable shape (e.g.,runId: nullandrunStatus) to make parsing predictable.As per coding guidelines, `app/api/**/*.ts: Return consistent response formats`.Suggested response shape update
- return NextResponse.json( - { - status: "success", - sandboxes: [ - { - ...result, - ...(runId && { runId }), - }, - ], - }, + const runStatus = runId ? "triggered" : "failed"; + return NextResponse.json( + { + status: "success", + sandboxes: [ + { + ...result, + runId: runId ?? null, + runStatus, + }, + ], + }, { status: 200, headers: getCorsHeaders() }, );
| * Request body: | ||
| * - prompt: string (required, min length 1) - The prompt to send to Claude Code | ||
| * - command: string (required) - The command to execute in the sandbox | ||
| * - args: string[] (optional) - Arguments to pass to the command | ||
| * - cwd: string (optional) - Working directory for command execution | ||
| * | ||
| * Response (200): | ||
| * - status: "success" | ||
| * - sandboxes: [{ sandboxId, sandboxStatus, timeout, createdAt }] | ||
| * - sandboxes: [{ sandboxId, sandboxStatus, timeout, createdAt, runId }] |
There was a problem hiding this comment.
Document runId as optional.
createSandboxPostHandler omits runId when the trigger fails, but the docs imply it’s always present. Please update the response docs to clarify it’s optional (or may be null) on trigger failure.
Doc tweak
- * - sandboxes: [{ sandboxId, sandboxStatus, timeout, createdAt, runId }]
+ * - sandboxes: [{ sandboxId, sandboxStatus, timeout, createdAt, runId? }]🤖 Prompt for AI Agents
In `@app/api/sandboxes/route.ts` around lines 29 - 36, Update the response
documentation to indicate that runId may be absent/null: modify the Response
schema comment in the file to show runId as optional (e.g., runId?: string |
null) for the sandboxes array returned by createSandboxPostHandler, since
createSandboxPostHandler can omit runId when the trigger fails; mention it may
be null or undefined to reflect trigger failure behavior.
- Rename function from selectAccountSnapshot to selectAccountSnapshots - Remove .limit(1).single() to return full array - Update handler to get first element from array - Update tests with new function name and array return values Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Regenerate types with supabase gen types to include account_snapshots table - Replace local AccountSnapshot interface with Tables<"account_snapshots"> - DRY: single source of truth for type definitions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Summary
{ prompt }to{ command, args?, cwd? }command,args,cwd,sandboxId,accountIdrunIdin response from triggered taskselectAccountSnapshotfunction for retrieving account snapshotsTest plan
pnpm test lib/sandbox/){ command: "ls", args: ["-la"] }runIdfield🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
API Changes
commandinstead ofprompt.