Repository navigation
feat: initial implementation - #1
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✅ Files skipped from review due to trivial changes (2)
📝 WalkthroughWalkthroughAdds the Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Dev as Developer
participant Client as Leap0Client
participant Sandbox as Leap0 Sandbox
participant Backend as Leap0Sandbox
participant Agent as Deep Agent
participant FS as Filesystem
Dev->>Client: construct (LEAP0_API_KEY)
Dev->>Client: create sandbox
Client-->>Sandbox: sandbox handle
Dev->>Backend: Leap0Sandbox.fromConnected(client, sandbox)
Dev->>Backend: initialize(initialFiles?)
Backend->>FS: create dirs + writeBytes (uploadFiles)
Backend-->>Dev: initialized (isRunning, id)
Dev->>Agent: create/use agent with Backend
Agent->>Backend: execute(command)
Backend->>Sandbox: box.process.execute(command, timeout)
Sandbox-->>Backend: stdout/stderr, exitCode
Backend-->>Agent: ExecuteResponse(output, exitCode)
Dev->>Backend: downloadFiles(path)
Backend->>FS: readBytes(path)
Backend-->>Dev: content or file_not_found
Dev->>Backend: close()
Backend->>Sandbox: delete()
Backend->>Client: client.close() (if owned)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (2)
src/sandbox.test.ts (1)
34-37: Reset mock implementations between tests.
vi.clearAllMocks()only clears history. Stubs likemockSandbox.filesystem.mkdir.mockResolvedValue(...)survive into later cases, so this suite can become order-dependent as it grows. RecreatemockSandboxinbeforeEachor usevi.resetAllMocks()plus explicit re-stubbing.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/sandbox.test.ts` around lines 34 - 37, The tests currently call vi.clearAllMocks() which only clears call history and lets stub implementations (e.g., mockSandbox.filesystem.mkdir) persist; change the setup in beforeEach to either recreate mockSandbox (re-initialize mockSandbox and reattach its nested mocks) before calling mockCreate.mockResolvedValue(mockSandbox) or switch to vi.resetAllMocks() and then explicitly re-stub the needed methods (e.g., mockSandbox.filesystem.mkdir.mockResolvedValue(...)) so each test gets a fresh set of mock implementations; update the beforeEach that references mockCreate and mockSandbox to perform one of these two approaches.src/sandbox.int.test.ts (1)
27-31: Use a much shorter sandbox TTL for integration runs.
timeoutMin: 60is far longer than the 3-minute test budget. Combined with retries and the best-effort cleanup below, a flaky CI run can leave chargeable sandboxes alive for up to an hour. Prefer a low test default or make it env-configurable.Suggested change
function createParams() { return { templateName: templateName(), - timeoutMin: 60, + timeoutMin: Number(process.env.LEAP0_TEST_TIMEOUT_MIN ?? 10), } as const; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/sandbox.int.test.ts` around lines 27 - 31, The createParams function currently sets timeoutMin: 60 which is too long for CI; change createParams to use a much smaller default TTL (e.g., 3 minutes) or read the TTL from an environment variable (e.g., SANDBOX_TTL_MIN) so tests can override it; update the code that constructs the params in createParams (and any callers expecting a const shape, referencing templateName()) to parse and validate the env value (fall back to the short default like 3) to avoid long-lived chargeable sandboxes during flaky CI runs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@examples/basic_sandbox.ts`:
- Around line 45-47: The current promise handling for main() silently ignores
rejections; update the invocation of main() (the void main() call) to explicitly
handle rejected promises by attaching a .catch handler (or use an async IIFE
with try/catch) that logs the error (including stack/message) and sets
process.exitCode to a non-zero value before exiting; ensure you reference the
main function and process.exitCode in the fix so all rejections produce
deterministic logging and exit behavior.
In `@examples/deep_agent_sandbox.ts`:
- Around line 85-87: The top-level promise from main() is not handling
rejections; update the invocation of main() so it attaches a .catch handler that
logs the error (e.g., console.error or processLogger.error) and sets
process.exitCode to a non-zero value (e.g., 1) for deterministic failure
handling—i.e., replace or augment the current void main().then(...) call with a
terminal .catch that references the same main() invocation and ensures errors
are logged and process.exitCode is set.
In `@README.md`:
- Around line 40-43: The final cleanup block should not skip client.close() if
sandbox.delete() throws; replace the single finally with nested try/finally so
sandbox.delete() is attempted first and client.close() is always executed
regardless of delete errors — update the snippet that currently calls
sandbox.delete() and client.close() in the same finally to use a try { await
sandbox.delete(); } finally { await client.close(); } (referencing
sandbox.delete() and client.close() to locate the code).
In `@src/sandbox.ts`:
- Around line 243-245: The timeout error currently includes the raw command
string; update the throw of Leap0SandboxError so it does not emit the full
command variable — instead provide a non-sensitive identifier (e.g., "command
timed out", an obscured/truncated summary, or a stable hash/fingerprint of
command) while keeping the error code "COMMAND_TIMEOUT"; locate the throw that
constructs Leap0SandboxError with the message `Command timed out: ${command}`
and replace the message portion with a sanitized/hashed identifier (or a static
placeholder plus metadata) to avoid leaking secrets.
- Around line 179-205: When initialization fails after sandboxes.create (e.g.,
`#uploadInitialFiles` throws), ensure you roll back the created sandbox before
rethrowing: in the catch block (where you currently check error instanceof
Leap0SandboxError and perform client-close/cleanup), first check if
this.#sandbox is set and attempt to delete the remote sandbox via the client API
(e.g., call this.#client.sandboxes.delete(this.#sandbox.id or
this.#sandbox.delete()) inside a try/catch), then clear this.#sandbox, and only
afterwards close/clear this.#client and this.#ownsClient and rethrow the
original error (or wrap as Leap0SandboxError) so created resources are not left
running.
- Around line 143-153: The catch in fromId() is converting any error from
sandboxes.get() into a SANDBOX_NOT_FOUND Leap0SandboxError, which hides
auth/network/5xx errors; change the error handling to detect a true "not found"
(e.g., check error.status === 404 or the SDK's NotFound error type) and only
throw Leap0SandboxError with "SANDBOX_NOT_FOUND" for that case, otherwise
preserve and rethrow the original error (or wrap it without changing its
type/code); also move/ensure client.close() runs in a finally block so the
client is always closed regardless of error type.
In `@tsdown.config.ts`:
- Around line 10-20: The second build target object (the config with entry:
["./src/index.ts"], format: ["cjs"], dts: true) currently has clean: true;
change that property to clean: false so the shared outDir cleanup only runs once
up front and the secondary target does not remove artifacts from the primary
target, avoiding race conditions when multiple configs write to the same outDir.
In `@vitest.config.ts`:
- Line 5: The config currently calls dotenv.config({ path:
path.resolve(__dirname, ".env") }) but __dirname is undefined in ESM; replace
use of __dirname by deriving the directory from import.meta.url (e.g., use
fileURLToPath(import.meta.url) and path.dirname on that) and then pass
path.resolve(<derivedDir>, ".env") to dotenv.config; update any imports to
ensure fileURLToPath is imported from "url" and keep the dotenv.config call
using the new resolved path (refer to dotenv.config, path.resolve, __dirname,
import.meta.url, and fileURLToPath to locate the change).
---
Nitpick comments:
In `@src/sandbox.int.test.ts`:
- Around line 27-31: The createParams function currently sets timeoutMin: 60
which is too long for CI; change createParams to use a much smaller default TTL
(e.g., 3 minutes) or read the TTL from an environment variable (e.g.,
SANDBOX_TTL_MIN) so tests can override it; update the code that constructs the
params in createParams (and any callers expecting a const shape, referencing
templateName()) to parse and validate the env value (fall back to the short
default like 3) to avoid long-lived chargeable sandboxes during flaky CI runs.
In `@src/sandbox.test.ts`:
- Around line 34-37: The tests currently call vi.clearAllMocks() which only
clears call history and lets stub implementations (e.g.,
mockSandbox.filesystem.mkdir) persist; change the setup in beforeEach to either
recreate mockSandbox (re-initialize mockSandbox and reattach its nested mocks)
before calling mockCreate.mockResolvedValue(mockSandbox) or switch to
vi.resetAllMocks() and then explicitly re-stub the needed methods (e.g.,
mockSandbox.filesystem.mkdir.mockResolvedValue(...)) so each test gets a fresh
set of mock implementations; update the beforeEach that references mockCreate
and mockSandbox to perform one of these two approaches.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e600040a-b720-4f7e-b377-629b8b63d8ae
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (13)
.gitignoreREADME.mdexamples/basic_sandbox.tsexamples/deep_agent_sandbox.tspackage.jsonsrc/index.tssrc/sandbox.int.test.tssrc/sandbox.test.tssrc/sandbox.tssrc/types.tstsconfig.jsontsdown.config.tsvitest.config.ts
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/sandbox.ts (1)
335-340: Consider distinguishing truly unknown errors.The fallback to
"permission_denied"for non-Leap0Errorexceptions could mask unexpected failure modes. If theFileOperationErrortype supports it, an"unknown_error"code would provide better diagnostics.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/sandbox.ts` around lines 335 - 340, mapFilesystemError currently returns "permission_denied" for any non-Leap0Error which can mask unexpected failures; update static mapFilesystemError(error: unknown) to return a distinct fallback like "unknown_error" (or another appropriate FileOperationError variant) instead of "permission_denied", keeping the existing special-case call to Leap0Sandbox.mapFilesystemApiError(error) for Leap0Error instances to preserve existing mapping logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/sandbox.ts`:
- Around line 241-258: The catch block contains a duplicated nested `if (error
instanceof Leap0TimeoutError)` and stray tokens (`error,` and `);`) causing a
syntax error; fix it by removing the inner duplicate check and the orphaned
tokens and replace with a single timeout branch that throws `new
Leap0SandboxError("Command timed out", "COMMAND_TIMEOUT", error)` when `error
instanceof Leap0TimeoutError`, otherwise throw `new Leap0SandboxError(\`Command
execution failed: ${...}\`, "COMMAND_FAILED", error instanceof Error ? error :
undefined)`; look for these symbols (`Leap0TimeoutError`, `Leap0SandboxError`,
the catch block around the command execution) to apply the change.
---
Nitpick comments:
In `@src/sandbox.ts`:
- Around line 335-340: mapFilesystemError currently returns "permission_denied"
for any non-Leap0Error which can mask unexpected failures; update static
mapFilesystemError(error: unknown) to return a distinct fallback like
"unknown_error" (or another appropriate FileOperationError variant) instead of
"permission_denied", keeping the existing special-case call to
Leap0Sandbox.mapFilesystemApiError(error) for Leap0Error instances to preserve
existing mapping logic.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: eb17b37b-b978-4e7b-acca-c54820ddbf32
📒 Files selected for processing (4)
README.mdexamples/basic_sandbox.tssrc/sandbox.tsvitest.config.ts
✅ Files skipped from review due to trivial changes (1)
- README.md
🚧 Files skipped from review as they are similar to previous changes (2)
- examples/basic_sandbox.ts
- vitest.config.ts
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/sandbox.ts`:
- Around line 100-110: fromConnected() and fromId() attach to an existing
sandbox but the close() path always deletes the sandbox (line ~359), risking
removal of sandboxes this instance didn't create; add an ownership flag (e.g.,
this.ownsSandbox) on the Leap0Sandbox class set to true only when the instance
actually creates the sandbox (constructor path that creates new sandboxes) and
set to false in factory attachers (fromConnected, fromId), then update close()
to call the deletion logic only if this.ownsSandbox is true; ensure constructor,
fromConnected, fromId, and close() are the referenced symbols updated so
attached-but-not-created sandboxes are not deleted.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: eca0734a-47dc-4310-8d12-af5d58e8b46b
📒 Files selected for processing (3)
src/sandbox.tstsdown.config.tsvitest.config.ts
✅ Files skipped from review due to trivial changes (1)
- tsdown.config.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- vitest.config.ts
initial implementation
Summary by CodeRabbit
New Features
Documentation
Examples
Tests
Chores