Skip to content

feat(packages/sandbox): sandbox sdk poc#101

Open
jamie-at-bunny wants to merge 4 commits into
poc/sandboxfrom
sandbox-sdk
Open

feat(packages/sandbox): sandbox sdk poc#101
jamie-at-bunny wants to merge 4 commits into
poc/sandboxfrom
sandbox-sdk

Conversation

@jamie-at-bunny

Copy link
Copy Markdown
Member

No description provided.

@changeset-bot

changeset-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 68a89ea

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 7 packages
Name Type
@bunny.net/sandbox Minor
@bunny.net/cli Minor
@bunny.net/cli-linux-x64 Minor
@bunny.net/cli-linux-arm64 Minor
@bunny.net/cli-darwin-x64 Minor
@bunny.net/cli-darwin-arm64 Minor
@bunny.net/cli-windows-x64 Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@jamie-at-bunny

Copy link
Copy Markdown
Member Author

@greptile-apps review

@greptile-apps

greptile-apps Bot commented Jun 24, 2026

Copy link
Copy Markdown

Greptile Summary

This PR introduces @bunny.net/sandbox, a standalone SDK for programmatically creating and managing ephemeral bunny.net sandboxes (Magic Containers apps) over SSH/SFTP, and wires three new CLI commands — sandbox create, sandbox delete, and sandbox url add — onto it. Several issues flagged in earlier review rounds have been resolved: the env-var key injection path, the SFTP channel leak, the exposePort orphan-on-failure case, the waitForPublicHost missing terminal-state fast-fail, and the throw lastErr dead code.

  • @bunny.net/sandbox (sandbox.ts, transport.ts, provision.ts, command.ts) — provides Sandbox.create/get/fromHandle, runCommand (blocking + detached streaming), writeFiles/readFile, exposePort, and delete, backed by a single reused ssh2 connection.
  • CLI commands (sandbox/create.ts, sandbox/delete.ts, sandbox/url/add.ts) — thin wrappers that resolve config, invoke the SDK, and persist sandbox handles (app ID + agent token + SSH host) in the CLI profile file under a sandboxes map.

Confidence Score: 4/5

Safe to merge after adding a duplicate-name guard in sandbox create; all other previously flagged issues have been addressed.

The sandbox create CLI command has no check for whether the requested name is already registered in the config. Running it twice with the same name provisions a new MC app, overwrites the config entry, and leaves the old app running with no CLI handle. Because sandboxes are billable infrastructure, this is a concrete defect on the changed code path rather than a theoretical risk.

packages/cli/src/commands/sandbox/create.ts — needs a pre-flight check against the config before calling Sandbox.create().

Important Files Changed

Filename Overview
packages/sandbox/src/sandbox.ts Core Sandbox class with create/get/fromHandle/runCommand/writeFiles/exposePort/delete. Previously flagged issues (env-key injection, exposePort orphan on failure) are now addressed in this version.
packages/sandbox/src/transport.ts SSH/SFTP transport over a single reused connection. SFTP channel is now lazily cached and reused (previously flagged per-operation leak addressed). SSH host verification remains absent per a deliberate tracked follow-up.
packages/sandbox/src/provision.ts MC API provisioning helpers. Registry retry loop is now clean (dead lastErr removed). waitForPublicHost now fast-fails on terminal app states. Two POST calls still use (client as any) for undocumented MC endpoints.
packages/sandbox/src/command.ts Detached Command streaming abstraction over a ClientChannel; correctly drains the queue after close. CommandFinished wraps already-complete data.
packages/cli/src/commands/sandbox/create.ts CLI sandbox create command. Missing a duplicate-name guard before Sandbox.create() — a repeated invocation with the same name orphans the previously-tracked MC app and overwrites the config entry.
packages/cli/src/commands/sandbox/delete.ts CLI sandbox delete command. Looks correct: reads from config, confirms with --force bypass, deletes via SDK, removes config entry.
packages/cli/src/commands/sandbox/url/add.ts CLI sandbox url add command. Correctly resolves sandbox from config, calls exposePort, and outputs the public URL.
packages/sandbox/src/types.ts Type definitions for the sandbox SDK: SandboxAuth, CreateOptions, SandboxHandle, RunCommandOptions, FileToWrite. Clean and well-documented.
packages/sandbox/src/integration.test.ts Live integration test guarded by SANDBOX_INTEGRATION=1 env var; skipped in CI. Covers create, exec, file I/O, detached streaming, and cleanup.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant CLI as bunny sandbox create
    participant SDK as Sandbox.create()
    participant MC as Magic Containers API
    participant SSH as SshTransport

    CLI->>SDK: "create({ name, region, apiKey })"
    SDK->>MC: POST /apps (createApp)
    MC-->>SDK: appId
    SDK->>MC: "poll GET /apps/{appId} (waitForSshHost)"
    MC-->>SDK: anycast SSH host
    SDK->>SSH: waitUntilReachable(120s)
    SSH-->>SDK: connected
    loop options.ports
        SDK->>MC: "POST /apps/{appId}/containers/{id}/endpoints"
        MC-->>SDK: endpointId
        SDK->>MC: "poll GET /apps/{appId}/endpoints (waitForPublicHost)"
        MC-->>SDK: publicHost
    end
    SDK-->>CLI: Sandbox instance
    CLI->>CLI: toHandle() + disconnect()
    CLI->>CLI: setSandbox(name, handle) to config file
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant CLI as bunny sandbox create
    participant SDK as Sandbox.create()
    participant MC as Magic Containers API
    participant SSH as SshTransport

    CLI->>SDK: "create({ name, region, apiKey })"
    SDK->>MC: POST /apps (createApp)
    MC-->>SDK: appId
    SDK->>MC: "poll GET /apps/{appId} (waitForSshHost)"
    MC-->>SDK: anycast SSH host
    SDK->>SSH: waitUntilReachable(120s)
    SSH-->>SDK: connected
    loop options.ports
        SDK->>MC: "POST /apps/{appId}/containers/{id}/endpoints"
        MC-->>SDK: endpointId
        SDK->>MC: "poll GET /apps/{appId}/endpoints (waitForPublicHost)"
        MC-->>SDK: publicHost
    end
    SDK-->>CLI: Sandbox instance
    CLI->>CLI: toHandle() + disconnect()
    CLI->>CLI: setSandbox(name, handle) to config file
Loading

Reviews (4): Last reviewed commit: "fmt" | Re-trigger Greptile

Comment thread packages/sandbox/src/sandbox.ts
Comment thread packages/sandbox/src/transport.ts Outdated
Comment thread packages/sandbox/src/sandbox.ts
Comment thread packages/sandbox/src/transport.ts
Comment thread packages/sandbox/src/provision.ts Outdated
Comment thread packages/sandbox/src/provision.ts
- validate env var names before shell interpolation (block injection)
- reuse a single SFTP channel instead of opening one per file op
- delete the app if exposePort fails during create() to avoid orphans
- fast-fail waitForPublicHost on terminal app states
- drop the unreachable throw lastErr in withRegistryRetry
@jamie-at-bunny jamie-at-bunny marked this pull request as ready for review June 26, 2026 13:35
@bogdan-at-bunny

Copy link
Copy Markdown

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bfb149c111

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/sandbox/src/transport.ts Outdated
Comment thread packages/sandbox/src/sandbox.ts Outdated
Comment thread packages/sandbox/src/provision.ts Outdated
Comment thread packages/sandbox/src/sandbox.ts Outdated
Comment thread packages/sandbox/package.json Outdated
Comment thread packages/sandbox/src/command.ts
- readFile returns null on SFTP NO_SUCH_FILE (numeric 2), not just ENOENT
- resolve the MC client lazily so fromHandle reconnects need no API key
- make port exposure idempotent and treat a slow host assignment as pending, not a failed create
- thread the abort signal through the SSH reachability wait
- ship @types/ssh2 as a runtime dependency for downstream consumers
- settle detached commands on stream error instead of leaving wait() hanging
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants