feat(sandbox-daytona): Daytona sandbox provider worker#93
Conversation
Mirrors the sandbox::e2b::* ABI under sandbox::daytona::*. Same 8 functions (create, exec, stop, list, snapshot, expose_port, fs::read, fs::write), same S-code error space, same concurrency tracking. HTTP call bodies are stubbed pending a verified pass against Daytona's REST surface. Auth header: Authorization: Bearer DAYTONA_API_KEY. Default base URL: https://app.daytona.io/api. Tests pass; clippy clean; fmt clean. Part of the sandbox-as-worker family.
Aligns the handler surface with the sandbox-e2b structural fixes so every Rust sandbox provider exposes the same self-healing list contract. Specifically: - `do_list` now reconciles the in-flight counter against the upstream count when the client succeeds, and falls back to the local counter when the provider is unreachable. The response gains a `reconciled` boolean so callers can tell the two cases apart. - `SandboxRecord.started_at` switches from i64 to a pass-through RFC3339 String. Matches the e2b shape; spares us a bespoke ISO parser and removes a heap of clippy noise. The Daytona REST client itself is still stubbed pending live verification with a real DAYTONA_API_KEY. The 404-idempotent stop and `timeout` body field will be wired with the same pattern the e2b client now ships once the credentials land. The existing wiremock suite (5 cases) keeps passing.
Three behaviour changes verified live against app.daytona.io/api:
1. `create` POSTs to /sandbox with body
{snapshot?, autoStopInterval}. `autoStopInterval` is in MINUTES on
Daytona's surface — not seconds — so we round up from
idle_timeout_secs. `image` falls through to Daytona's default
snapshot when the caller passes 'default' or empty. Response shape
is {id, snapshot, state, createdAt, ...}; we map id → sandbox_id,
snapshot → image.
2. DELETE /sandbox/{id} treats 200, 404, AND 409 as success. 409
'Sandbox state change in progress' is Daytona-specific: the
platform deletes asynchronously, so a second stop racing the
cleanup hits a sandbox in a transitional state. From the caller's
perspective the post-state is what they wanted, so we surface
success and release the in-flight slot. The wiremock suite gains
an explicit regression test covering this path.
3. GET /sandbox returns the array we feed into list reconciliation.
The response includes sandboxes mid-cleanup (state: destroying,
destroyed, etc.), which is intentional — those slots still consume
account resources until Daytona finishes their teardown, so the
reconciled in_flight reflects them.
Live verification cycle: create → list (reconciled, in_flight=1) →
stop (200) → stop again (409, treated as success) → stop a third
time (404, treated as success) → stop random uuid (404, treated as
success). Upstream account drains to zero.
Tests: 10 wiremock-backed cases now mirror the e2b suite plus a
Daytona-specific 409 case. clippy + fmt clean.
…ng engine Verified the register_function/register_function_with handler signatures are unchanged between 0.11.3 and 0.11.6 (handler closure stays `Fn(R) -> Result<O,E>` — single-arg, no engine-supplied ctx; our HandlerCtx is captured by the closure, not an SDK parameter). All wiremock tests still pass. Pin moves to whatever the engine actually ships.
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ 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. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (9)
✨ 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 |
Stops shadowing the bare sandbox::* namespace. Routes through the sandbox router worker (provider="daytona"); direct invocation stays supported and stable. Handler logic, tests, S-code mapping unchanged.
|
Refactored to register only Validation: |
Member of the sandbox worker family. Read
sandbox-CONTEXT.md(lands via #92) for family terminology and ABI contract.What ships
sandbox-daytona/— narrow Rust iii worker wrapping Daytona sandboxes. Live verified end-to-end againstapp.daytona.io/api.Functions
sandbox::daytona::createPOST /sandboxbody{snapshot?, autoStopInterval}(autoStopInterval is minutes, rounded up fromidle_timeout_secs)sandbox::daytona::exec/sandbox/{id}/process; streaming wiring is a follow-upsandbox::daytona::stopDELETE /sandbox/{id}; 200 / 404 / 409 all treated as idempotent successsandbox::daytona::listGET /sandbox; reconcilesin_flightsandbox::daytona::snapshot/expose_port/fs::*Capabilities advertised:
["snapshot","expose_port","fs"].Daytona-specific bug found live
Daytona returns
409 Conflict"Sandbox state change in progress" when a stop races their async deletion (different from E2B's 404). This PR's stop treats 409 as idempotent success alongside 404 — the post-state is what the caller wanted either way. Pinned bystop_treats_409_as_successin the test suite.Live verification cycle
Tests
tests/integration.rs— 10 wiremock cases mirroring the e2b suite plus the Daytona-specific 409 case.cargo test,clippy,fmtclean.Pin
iii-sdk = "=0.11.6".