Skip to content

feat(cubesandbox): exec-only provider (Phase 1, stacked on RFC #136) - #137

Open
JackWeiw wants to merge 1 commit into
feat/cubesandbox-provider-rfcfrom
feat/cubesandbox-provider-exec
Open

feat(cubesandbox): exec-only provider (Phase 1, stacked on RFC #136)#137
JackWeiw wants to merge 1 commit into
feat/cubesandbox-provider-rfcfrom
feat/cubesandbox-provider-exec

Conversation

@JackWeiw

@JackWeiw JackWeiw commented Sep 3, 2026

Copy link
Copy Markdown
Owner

What

Phase 1 of the cubesandbox provider (Cloud Hypervisor microVM, lifecycle-capable), stacked on RFC #136 (feat/cubesandbox-provider-rfc). This PR ships the exec-only baseline — the same surface cloud e2b has today — over the native cubesandbox PyPI SDK.

Scope (Phase 1 — exec-only)

New opt-in leaf submodule src/env_provider/cubesandbox/:

  • config.pyConfig (CUBE_API_URL/KEY/TEMPLATE_ID/SANDBOX_DOMAIN env vars, template, idle TTL, ids_file, snapshot_dir) + setup_cube_env() (placeholder-credential skip; cube auth is optional, no CLI fallback)
  • schemas.pyCubeSandboxState (handle cube_sandbox + lifecycle flags + creation metrics)
  • manager.pyCubesandboxManager(BaseSandboxManager) SDK seams + class attrs + detect_from_file + check_alive (control-plane get_info)
  • __init__.pyCubesandboxProvider(EnvironmentProvider) adapter: create_all/detect_existing/detect_from_ids/save_ids/check_alive/cleanup_all/cleanup_existing/prepare_env/exec + _to_instance + build_provider

Registration + config:

  • bench_core.bench: --provider cubesandbox (CLI choices + lazy _build_provider import; kernel stays SDK-free)
  • pyproject.toml: cubesandbox optional-dependency (cubesandbox>=0.7.0)
  • config/common/replay{,-exec-only,-trajectory}.yaml: cubesandbox: blocks (CUBE_* env, idle TTL)

default_replay_mode="exec_only", vmm_type=None (vm_monitor deferred).

SDK surface (verified against source)

Read directly from CubeSandbox/sdk/python/cubesandbox/ (v0.7.0) — the manager seams bind to the real API, not assumptions:

  • Sandbox.list() returns a flat list[dict] (key sandboxID) — not a paginator like e2b
  • commands.run(cmd, user, timeout, cwd, envs)CommandResult(stdout, stderr, exit_code); raises only on transport/timeout (no partial output) → exec maps timeout → exit 124 so the replay runner records a timed_out step and continues
  • native pause(wait=True); resume via Sandbox.connect(id) (deprecated resume()); get_info() 404 → dead (clean check_alive, no envd exec round-trip)
  • SnapshotInfo has no size fieldssnapshot_sizes deferred to Phase 2 (returns None)

Tests

36 new tests, no live SDK/cluster needed (fakes injected via monkeypatch, mirroring test_e2b_manager.py / test_aenv_provider.py):

  • test_cubesandbox_manager.py — SDK seams (create/envs=None, flat-list _list_existing, dict-key _external_id, connect _attach, exec probe, get_info check_alive) + base lifecycle (create_all / detect_existing / detect_from_file / cleanup_all / cleanup_existing)
  • test_cubesandbox_provider.py — name/replay-mode/protocols, build_provider reads cubesandbox block, setup_cube_env placeholder skip, exec success/timeout→124/transport-raise/cwd+envs forwarding, save_ids, _to_instance template+id

Verification

python -m pytest src/bench_core/tests src/env_provider/tests   # full suite green
python -m ruff check src/env_provider/cubesandbox src/bench_core/bench.py
python -m ruff format --check src/env_provider/cubesandbox
python -m pre_commit run --files <phase-1 files>              # all hooks pass

Stacking

  • Base: RFC 0002: Add cubesandbox provider (lifecycle-capable, Cloud Hypervisor microVM) #136 (RFC feat/cubesandbox-provider-rfc) — this PR targets that branch.
  • Next: Phase 2 (separate PR off this branch) adds LifecycleCapable (pause/resume), EphemeralCapable (create_one/kill_one), SnapshotSizeCapable (returns None for v1), and flips default_replay_mode to lifecycle. vm_monitor integration (cube-hypervisor VMM type) is a follow-on.

When #136 merges to main, this PR retargets to main.

Add the cubesandbox provider (Cloud Hypervisor microVM, lifecycle-capable)
as an opt-in leaf submodule of env_provider, backed by the native
cubesandbox PyPI SDK (>=0.7.0). Phase 1 ships the exec-only surface:
create/detect/check_alive/cleanup/exec/save_ids over BaseSandboxManager
SDK seams (Sandbox.create/list/connect/kill + commands.run + get_info).

- src/env_provider/cubesandbox/{config,schemas,manager,__init__}.py +
  tests (test_cubesandbox_manager.py + test_cubesandbox_provider.py,
  36 tests; no live SDK needed -- fakes injected via monkeypatch).
- bench_core.bench: register --provider cubesandbox (CLI choices + lazy
  _build_provider import; kernel stays SDK-free).
- pyproject: cubesandbox optional-dependency.
- config/common/replay*.yaml: cubesandbox: blocks (CUBE_* env, idle TTL).

default_replay_mode="exec_only", vmm_type=None (vm_monitor deferred).
SDK surface verified against CubeSandbox/sdk/python/cubesandbox 0.7.0:
Sandbox.list() returns a flat list[dict] (sandboxID key), commands.run ->
CommandResult(stdout, stderr, exit_code), native pause(wait=True), resume
via Sandbox.connect (deprecated resume()), get_info 404 -> dead. exec
maps a transport timeout to exit 124 (timeout(1) convention) so the
replay runner records a timed_out step and continues.

Phase 2 (separate stacked PR) adds LifecycleCapable/EphemeralCapable/
SnapshotSizeCapable and flips default_replay_mode to "lifecycle".
See RFC #136.
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.

1 participant