Skip to content

Commit 53fd084

Browse files
Close sandbox API parity gaps: snapshot get/delete, image build list, volume list filters (#110)
* Add snapshot get/delete, image build list, and volume list filters for sandbox API parity --------- Co-authored-by: nikhil <nshahi1998@gmail.com>
1 parent 65086de commit 53fd084

21 files changed

Lines changed: 693 additions & 58 deletions

hyperbrowser/client/managers/async_manager/sandbox.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
SandboxExposeResult,
1616
SandboxImageBuild,
1717
SandboxImageBuildCreateResult,
18+
SandboxImageBuildListParams,
19+
SandboxImageBuildListResponse,
1820
SandboxImageListParams,
1921
SandboxImageListResponse,
2022
SandboxImageInit,
@@ -25,8 +27,10 @@
2527
SandboxNetworkPolicy,
2628
SandboxNetworkUpdateResult,
2729
SandboxRuntimeSession,
30+
SandboxSnapshotDeleteResult,
2831
SandboxSnapshotListParams,
2932
SandboxSnapshotListResponse,
33+
SandboxSnapshotSummary,
3034
SandboxUnexposeResult,
3135
StartSandboxFromSnapshotParams,
3236
)
@@ -37,6 +41,7 @@
3741
CreateSandboxParams as CreateSandboxParamsDict,
3842
SandboxExecParams as SandboxExecParamsDict,
3943
SandboxExposeParams as SandboxExposeParamsDict,
44+
SandboxImageBuildListParams as SandboxImageBuildListParamsDict,
4045
SandboxImageInit as SandboxImageInitDict,
4146
SandboxImageListParams as SandboxImageListParamsDict,
4247
SandboxListParams as SandboxListParamsDict,
@@ -167,6 +172,10 @@ def memory_mib(self):
167172
def disk_mib(self):
168173
return self._detail.disk_mib
169174

175+
@property
176+
def timeout_minutes(self):
177+
return self._detail.timeout_minutes
178+
170179
@property
171180
def exposed_ports(self):
172181
return self._detail.exposed_ports
@@ -346,7 +355,7 @@ def _clear_runtime_session(self, status: Optional[str] = None) -> None:
346355
)
347356

348357
def _assert_runtime_available(self) -> None:
349-
if self._detail.status in {"closed", "error"}:
358+
if self._detail.status in {"closed", "error", "close-error"}:
350359
raise HyperbrowserError(
351360
f"Sandbox {self.id} is not running",
352361
status_code=409,
@@ -394,7 +403,7 @@ async def start_from_snapshot(
394403
],
395404
) -> SandboxHandle:
396405
normalized = coerce_request(params, StartSandboxFromSnapshotParams)
397-
return await self.create(normalized)
406+
return await self.create(normalized.model_dump(exclude_none=True))
398407

399408
async def get(self, sandbox_id: str) -> SandboxHandle:
400409
return self.attach(await self.get_detail(sandbox_id))
@@ -450,6 +459,14 @@ async def list_snapshots(
450459
)
451460
return SandboxSnapshotListResponse(**payload)
452461

462+
async def get_snapshot(self, snapshot: str) -> SandboxSnapshotSummary:
463+
payload = await self._request("GET", f"/snapshots/{snapshot}")
464+
return SandboxSnapshotSummary(**payload["snapshot"])
465+
466+
async def delete_snapshot(self, snapshot: str) -> SandboxSnapshotDeleteResult:
467+
payload = await self._request("DELETE", f"/snapshots/{snapshot}")
468+
return SandboxSnapshotDeleteResult(**payload)
469+
453470
async def stop(self, sandbox_id: str) -> BasicResponse:
454471
payload = await self._request("PUT", f"/sandbox/{sandbox_id}/stop")
455472
return BasicResponse(**payload)
@@ -491,6 +508,22 @@ async def cancel_image_build(self, build_id: str) -> SandboxImageBuild:
491508
payload = await self._request("POST", f"/images/builds/{build_id}/cancel")
492509
return SandboxImageBuild(**payload["build"])
493510

511+
async def list_image_builds(
512+
self,
513+
params: Optional[
514+
Union[SandboxImageBuildListParamsDict, SandboxImageBuildListParams]
515+
] = None,
516+
) -> SandboxImageBuildListResponse:
517+
payload = await self._request(
518+
"GET",
519+
"/images/builds",
520+
params=dump_request(
521+
params if params is not None else {},
522+
SandboxImageBuildListParams,
523+
),
524+
)
525+
return SandboxImageBuildListResponse(**payload)
526+
494527
async def wait_for_image_build(
495528
self,
496529
build_id: str,

hyperbrowser/client/managers/async_manager/volume.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
from typing import Union
1+
from typing import Optional, Union
22

33
from hyperbrowser.client._request import dump_request
4-
from hyperbrowser.models.volume import CreateVolumeParams, Volume, VolumeListResponse
4+
from hyperbrowser.models.volume import (
5+
CreateVolumeParams,
6+
Volume,
7+
VolumeListParams,
8+
VolumeListResponse,
9+
)
510
from hyperbrowser.types import CreateVolumeParams as CreateVolumeParamsDict
11+
from hyperbrowser.types import VolumeListParams as VolumeListParamsDict
612

713

814
class VolumeManager:
@@ -19,8 +25,17 @@ async def create(
1925
)
2026
return Volume(**response.data)
2127

22-
async def list(self) -> VolumeListResponse:
23-
response = await self._client.transport.get(self._client._build_url("/volume"))
28+
async def list(
29+
self,
30+
params: Optional[Union[VolumeListParamsDict, VolumeListParams]] = None,
31+
) -> VolumeListResponse:
32+
response = await self._client.transport.get(
33+
self._client._build_url("/volume"),
34+
params=dump_request(
35+
params if params is not None else {},
36+
VolumeListParams,
37+
),
38+
)
2439
return VolumeListResponse(**response.data)
2540

2641
async def get(self, volume_id: str) -> Volume:

hyperbrowser/client/managers/sandboxes/image_build.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
IMAGE_BUILD_INPUT_FORMAT: SandboxImageBuildInputFormat = "rootfs_export_tar_gz"
2323
IMAGE_BUILD_SOURCE_PLATFORM = "linux/amd64"
2424
TERMINAL_IMAGE_BUILD_STATUSES: FrozenSet[SandboxImageBuildStatus] = frozenset(
25-
{"completed", "failed", "canceled", "cancelled"}
25+
{"completed", "failed", "canceled"}
2626
)
2727
_IMAGE_INIT_ENV_KEY_PATTERN = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
2828
_RESERVED_IMAGE_INIT_ENV_KEYS = {

hyperbrowser/client/managers/sync_manager/sandbox.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
SandboxExposeResult,
1414
SandboxImageBuild,
1515
SandboxImageBuildCreateResult,
16+
SandboxImageBuildListParams,
17+
SandboxImageBuildListResponse,
1618
SandboxImageListParams,
1719
SandboxImageListResponse,
1820
SandboxImageInit,
@@ -23,8 +25,10 @@
2325
SandboxNetworkPolicy,
2426
SandboxNetworkUpdateResult,
2527
SandboxRuntimeSession,
28+
SandboxSnapshotDeleteResult,
2629
SandboxSnapshotListParams,
2730
SandboxSnapshotListResponse,
31+
SandboxSnapshotSummary,
2832
SandboxUnexposeResult,
2933
StartSandboxFromSnapshotParams,
3034
)
@@ -35,6 +39,7 @@
3539
CreateSandboxParams as CreateSandboxParamsDict,
3640
SandboxExecParams as SandboxExecParamsDict,
3741
SandboxExposeParams as SandboxExposeParamsDict,
42+
SandboxImageBuildListParams as SandboxImageBuildListParamsDict,
3843
SandboxImageInit as SandboxImageInitDict,
3944
SandboxImageListParams as SandboxImageListParamsDict,
4045
SandboxListParams as SandboxListParamsDict,
@@ -159,6 +164,10 @@ def memory_mib(self):
159164
def disk_mib(self):
160165
return self._detail.disk_mib
161166

167+
@property
168+
def timeout_minutes(self):
169+
return self._detail.timeout_minutes
170+
162171
@property
163172
def exposed_ports(self):
164173
return self._detail.exposed_ports
@@ -338,7 +347,7 @@ def _clear_runtime_session(self, status: Optional[str] = None) -> None:
338347
)
339348

340349
def _assert_runtime_available(self) -> None:
341-
if self._detail.status in {"closed", "error"}:
350+
if self._detail.status in {"closed", "error", "close-error"}:
342351
raise HyperbrowserError(
343352
f"Sandbox {self.id} is not running",
344353
status_code=409,
@@ -386,7 +395,7 @@ def start_from_snapshot(
386395
],
387396
) -> SandboxHandle:
388397
normalized = coerce_request(params, StartSandboxFromSnapshotParams)
389-
return self.create(normalized)
398+
return self.create(normalized.model_dump(exclude_none=True))
390399

391400
def get(self, sandbox_id: str) -> SandboxHandle:
392401
return self.attach(self.get_detail(sandbox_id))
@@ -442,6 +451,14 @@ def list_snapshots(
442451
)
443452
return SandboxSnapshotListResponse(**payload)
444453

454+
def get_snapshot(self, snapshot: str) -> SandboxSnapshotSummary:
455+
payload = self._request("GET", f"/snapshots/{snapshot}")
456+
return SandboxSnapshotSummary(**payload["snapshot"])
457+
458+
def delete_snapshot(self, snapshot: str) -> SandboxSnapshotDeleteResult:
459+
payload = self._request("DELETE", f"/snapshots/{snapshot}")
460+
return SandboxSnapshotDeleteResult(**payload)
461+
445462
def stop(self, sandbox_id: str) -> BasicResponse:
446463
payload = self._request("PUT", f"/sandbox/{sandbox_id}/stop")
447464
return BasicResponse(**payload)
@@ -483,6 +500,22 @@ def cancel_image_build(self, build_id: str) -> SandboxImageBuild:
483500
payload = self._request("POST", f"/images/builds/{build_id}/cancel")
484501
return SandboxImageBuild(**payload["build"])
485502

503+
def list_image_builds(
504+
self,
505+
params: Optional[
506+
Union[SandboxImageBuildListParamsDict, SandboxImageBuildListParams]
507+
] = None,
508+
) -> SandboxImageBuildListResponse:
509+
payload = self._request(
510+
"GET",
511+
"/images/builds",
512+
params=dump_request(
513+
params if params is not None else {},
514+
SandboxImageBuildListParams,
515+
),
516+
)
517+
return SandboxImageBuildListResponse(**payload)
518+
486519
def wait_for_image_build(
487520
self,
488521
build_id: str,

hyperbrowser/client/managers/sync_manager/volume.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
from typing import Union
1+
from typing import Optional, Union
22

33
from hyperbrowser.client._request import dump_request
4-
from hyperbrowser.models.volume import CreateVolumeParams, Volume, VolumeListResponse
4+
from hyperbrowser.models.volume import (
5+
CreateVolumeParams,
6+
Volume,
7+
VolumeListParams,
8+
VolumeListResponse,
9+
)
510
from hyperbrowser.types import CreateVolumeParams as CreateVolumeParamsDict
11+
from hyperbrowser.types import VolumeListParams as VolumeListParamsDict
612

713

814
class VolumeManager:
@@ -19,8 +25,17 @@ def create(
1925
)
2026
return Volume(**response.data)
2127

22-
def list(self) -> VolumeListResponse:
23-
response = self._client.transport.get(self._client._build_url("/volume"))
28+
def list(
29+
self,
30+
params: Optional[Union[VolumeListParamsDict, VolumeListParams]] = None,
31+
) -> VolumeListResponse:
32+
response = self._client.transport.get(
33+
self._client._build_url("/volume"),
34+
params=dump_request(
35+
params if params is not None else {},
36+
VolumeListParams,
37+
),
38+
)
2439
return VolumeListResponse(**response.data)
2540

2641
def get(self, volume_id: str) -> Volume:

hyperbrowser/models/__init__.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@
210210
ProfileListResponse,
211211
ProfileResponse,
212212
)
213-
from .volume import CreateVolumeParams, Volume, VolumeListResponse
213+
from .volume import CreateVolumeParams, Volume, VolumeListParams, VolumeListResponse
214214
from .scrape import (
215215
BatchScrapeJobResponse,
216216
BatchScrapeJobStatusResponse,
@@ -310,19 +310,23 @@
310310
SandboxImageListResponse,
311311
SandboxImageSummary,
312312
SandboxSnapshotStatus,
313+
SandboxSnapshotDeleteResult,
313314
SandboxSnapshotListResponse,
314315
SandboxSnapshotSummary,
315316
SandboxSnapshotListParams,
316317
SandboxMemorySnapshotParams,
317318
SandboxMemorySnapshotResult,
318319
SandboxImageBuildInputFormat,
320+
SandboxImageBuildSourcePlatform,
319321
SandboxImageBuildStatus,
320322
SandboxImageInit,
321323
CreateSandboxImageBuildParams,
322324
CompleteSandboxImageBuildParams,
323325
SandboxImageBuildUpload,
324326
SandboxImageBuild,
325327
SandboxImageBuildCreateResult,
328+
SandboxImageBuildListParams,
329+
SandboxImageBuildListResponse,
326330
SandboxExposeParams,
327331
SandboxExposeResult,
328332
SandboxUnexposeResult,
@@ -530,6 +534,7 @@
530534
# volume
531535
"CreateVolumeParams",
532536
"Volume",
537+
"VolumeListParams",
533538
"VolumeListResponse",
534539
# scrape
535540
"BatchScrapeJobResponse",
@@ -604,19 +609,23 @@
604609
"SandboxImageListResponse",
605610
"SandboxImageSummary",
606611
"SandboxSnapshotStatus",
612+
"SandboxSnapshotDeleteResult",
607613
"SandboxSnapshotListResponse",
608614
"SandboxSnapshotSummary",
609615
"SandboxSnapshotListParams",
610616
"SandboxMemorySnapshotParams",
611617
"SandboxMemorySnapshotResult",
612618
"SandboxImageBuildInputFormat",
619+
"SandboxImageBuildSourcePlatform",
613620
"SandboxImageBuildStatus",
614621
"SandboxImageInit",
615622
"CreateSandboxImageBuildParams",
616623
"CompleteSandboxImageBuildParams",
617624
"SandboxImageBuildUpload",
618625
"SandboxImageBuild",
619626
"SandboxImageBuildCreateResult",
627+
"SandboxImageBuildListParams",
628+
"SandboxImageBuildListResponse",
620629
"SandboxExposeParams",
621630
"SandboxExposeResult",
622631
"SandboxUnexposeResult",

hyperbrowser/models/consts.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
"high",
101101
]
102102
SessionRegion = Literal[
103+
"us",
103104
"us-central",
104105
"asia-south",
105106
"us-dev",

0 commit comments

Comments
 (0)