feat(boxlite): make box disk size configurable (#4072)

The boxlite SDK's BoxOptions already supports disk_size_gb, but the
omnigent wrapper never threaded it through — every box got the SDK's
own default disk size with no way to override it. Add
sandbox.boxlite.disk_size_gb to the server config, alongside the
existing image/env knobs.

Signed-off-by: Avri Chen-Roth <11185446+the-mentor@users.noreply.github.com>
Co-authored-by: Avri Chen-Roth <11185446+the-mentor@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Avri Chen-Roth
2026-08-05 02:52:14 +03:00
committed by GitHub
parent b02449cd40
commit 6b17f23c2e
6 changed files with 39 additions and 2 deletions
+1
View File
@@ -83,6 +83,7 @@ sandbox:
boxlite:
image: docker.io/me/omnigent-host:latest # optional, shared; default: official
env: [OPENAI_API_KEY, GIT_TOKEN] # optional, shared; SERVER env var NAMES
disk_size_gb: 100 # optional, shared; default: SDK default
cloud:
endpoint: https://boxlite.example.com:8100 # selects CLOUD mode
```
+6
View File
@@ -215,6 +215,7 @@ class BoxliteSandboxLauncher(SandboxLauncher):
env: Sequence[str] | None = None,
home_dir: str | None = None,
registry: Mapping[str, object] | None = None,
disk_size_gb: int | None = None,
) -> None:
"""
Initialize the launcher.
@@ -244,6 +245,9 @@ class BoxliteSandboxLauncher(SandboxLauncher):
``password_env`` / ``token_env``. The ``*_env`` keys NAME server
environment variables holding the credentials (12-factor; values
never live in config). ``None`` uses anonymous pulls.
:param disk_size_gb: Box disk size in GB — the server's
``sandbox.boxlite.disk_size_gb`` config. ``None`` uses the SDK's
own default.
When ``home_dir`` or ``registry`` is set the launcher builds a
customized ``Boxlite(Options(...))`` runtime; otherwise it uses the
@@ -254,6 +258,7 @@ class BoxliteSandboxLauncher(SandboxLauncher):
self._env_names = tuple(env) if env is not None else None
self._home_dir = home_dir
self._registry = dict(registry) if registry is not None else None
self._disk_size_gb = disk_size_gb
self._runtime: boxlite_sdk.Boxlite | None = None
async def _aruntime(self) -> boxlite_sdk.Boxlite:
@@ -411,6 +416,7 @@ class BoxliteSandboxLauncher(SandboxLauncher):
image=resolved_ref,
cpus=_SANDBOX_CPU,
memory_mib=_SANDBOX_MEMORY_MIB,
disk_size_gb=self._disk_size_gb,
env=env,
auto_remove=False,
detach=True,
+13 -2
View File
@@ -60,6 +60,7 @@ stores into ``create_app``):
boxlite: # optional block (provider: boxlite)
image: docker.io/me/omnigent-host:latest # shared; default: official
env: [OPENAI_API_KEY, GIT_TOKEN] # shared; SERVER env var NAMES
disk_size_gb: 100 # shared; default: SDK default
# exactly one mode (mutually exclusive):
cloud: {endpoint: https://boxlite.example.com:8100} # CLOUD; key: BOXLITE_API_KEY env
# local: {home_dir: /data/boxlite, registry: {...}} # LOCAL (default if omitted)
@@ -770,7 +771,9 @@ def parse_sandbox_config(raw: object) -> ManagedSandboxConfig | None:
token_ttl_s = DAYTONA_MANAGED_TOKEN_TTL_S
elif provider == "boxlite":
section = _boxlite_section(raw)
_reject_unknown_keys(section, {"image", "env", "local", "cloud"}, "sandbox.boxlite")
_reject_unknown_keys(
section, {"image", "env", "local", "cloud", "disk_size_gb"}, "sandbox.boxlite"
)
endpoint, home_dir, registry = _parse_boxlite_mode(section)
launcher_factory = _boxlite_launcher_factory(
endpoint,
@@ -778,6 +781,7 @@ def parse_sandbox_config(raw: object) -> ManagedSandboxConfig | None:
_parse_boxlite_env(section),
home_dir,
registry,
_parse_provider_positive_int(raw, "boxlite", "disk_size_gb"),
)
token_ttl_s = BOXLITE_MANAGED_TOKEN_TTL_S
elif provider == "cwsandbox":
@@ -1055,6 +1059,7 @@ def _boxlite_launcher_factory(
env: list[str] | None,
home_dir: str | None,
registry: dict[str, object] | None,
disk_size_gb: int | None,
) -> Callable[[], SandboxHostLauncher]:
"""
Build the launcher factory for the YAML ``provider: boxlite`` path.
@@ -1073,6 +1078,7 @@ def _boxlite_launcher_factory(
:param registry: LOCAL-mode private-registry config for the host image
(``host`` + optional ``transport`` / ``skip_verify`` / ``*_env``
credential names), or ``None`` for anonymous pulls.
:param disk_size_gb: Box disk size in GB, or ``None`` for the SDK default.
:returns: A factory producing parameterized boxlite launchers.
"""
@@ -1081,7 +1087,12 @@ def _boxlite_launcher_factory(
from omnigent.onboarding.sandboxes.boxlite import BoxliteSandboxLauncher
return BoxliteSandboxLauncher(
endpoint=endpoint, image=image, env=env, home_dir=home_dir, registry=registry
endpoint=endpoint,
image=image,
env=env,
home_dir=home_dir,
registry=registry,
disk_size_gb=disk_size_gb,
)
return _build
@@ -149,6 +149,7 @@ class _FakeBoxOptions:
image: str | None = None,
cpus: int | None = None,
memory_mib: int | None = None,
disk_size_gb: int | None = None,
env: object = None,
auto_remove: bool | None = None,
detach: bool | None = None,
@@ -157,6 +158,7 @@ class _FakeBoxOptions:
self.image = image
self.cpus = cpus
self.memory_mib = memory_mib
self.disk_size_gb = disk_size_gb
self.env = env if env is not None else []
self.auto_remove = auto_remove
self.detach = detach
@@ -372,11 +374,22 @@ def test_provision_defaults_official_image_and_persists(
assert create.options.detach is True
assert create.options.cpus == 2
assert create.options.memory_mib == 4096
assert create.options.disk_size_gb is None
assert create.options.env == []
assert create.name == "managed-abc"
assert fake_boxlite.mode == "local"
def test_provision_disk_size_gb_reaches_box_options(
fake_boxlite: _FakeBoxliteState,
) -> None:
"""``disk_size_gb`` passed to the launcher reaches ``BoxOptions`` verbatim."""
BoxliteSandboxLauncher(disk_size_gb=100).provision("managed-abc")
[create] = fake_boxlite.create_calls
assert create.options.disk_size_gb == 100
def test_provision_image_resolution_order(
fake_boxlite: _FakeBoxliteState, monkeypatch: pytest.MonkeyPatch
) -> None:
+3
View File
@@ -194,6 +194,7 @@ class FakeSandboxLauncher(SandboxLauncher):
self.endpoint: str | None = None
self.home_dir: str | None = None
self.registry: dict[str, object] | None = None
self.disk_size_gb: int | None = None
self.base_url: str | None = None
self.gateway_profile: str | None = None
self.snapshot_name: str | None = None
@@ -392,6 +393,7 @@ def install_fake_boxlite_launcher(
env: list[str] | None = None,
home_dir: str | None = None,
registry: dict[str, object] | None = None,
disk_size_gb: int | None = None,
) -> FakeSandboxLauncher:
"""Stand-in constructor recording the construction wiring."""
fake.endpoint = endpoint
@@ -399,6 +401,7 @@ def install_fake_boxlite_launcher(
fake.env = env
fake.home_dir = home_dir
fake.registry = registry
fake.disk_size_gb = disk_size_gb
return fake
monkeypatch.setattr(boxlite_mod, "BoxliteSandboxLauncher", _ctor)
+3
View File
@@ -228,6 +228,7 @@ def test_parse_valid_boxlite_cloud_config_builds_parameterized_factory(
"image": "docker.io/me/omnigent-host:latest",
"env": ["OPENAI_API_KEY", "GIT_TOKEN"],
"cloud": {"endpoint": "https://boxlite.example.com:8100"},
"disk_size_gb": 100,
},
}
)
@@ -242,6 +243,7 @@ def test_parse_valid_boxlite_cloud_config_builds_parameterized_factory(
assert fake.endpoint == "https://boxlite.example.com:8100"
assert fake.image == "docker.io/me/omnigent-host:latest"
assert fake.env == ["OPENAI_API_KEY", "GIT_TOKEN"]
assert fake.disk_size_gb == 100
def test_parse_boxlite_without_section_defaults_local(
@@ -260,6 +262,7 @@ def test_parse_boxlite_without_section_defaults_local(
assert fake.endpoint is None
assert fake.image is None
assert fake.env is None
assert fake.disk_size_gb is None
def test_parse_boxlite_local_customization_reaches_launcher(