From e50f61160404f670dd3f6fe128cd86ea64912a51 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 14 May 2026 01:43:04 +0000 Subject: [PATCH 1/6] Add SDK API parity methods Co-authored-by: shri --- .../managers/async_manager/agents/__init__.py | 2 + .../async_manager/agents/browser_use.py | 11 ++ .../agents/claude_computer_use.py | 11 ++ .../managers/async_manager/agents/cua.py | 11 ++ .../agents/gemini_computer_use.py | 11 ++ .../async_manager/agents/hyper_agent.py | 11 ++ .../managers/async_manager/agents/task.py | 62 +++++++++ .../client/managers/async_manager/profile.py | 18 +++ .../client/managers/async_manager/sandbox.py | 119 +++++++++++++++- .../client/managers/async_manager/session.py | 56 ++++++++ .../client/managers/async_manager/volume.py | 17 ++- .../managers/sync_manager/agents/__init__.py | 2 + .../sync_manager/agents/browser_use.py | 11 ++ .../agents/claude_computer_use.py | 11 ++ .../managers/sync_manager/agents/cua.py | 11 ++ .../agents/gemini_computer_use.py | 11 ++ .../sync_manager/agents/hyper_agent.py | 11 ++ .../managers/sync_manager/agents/task.py | 62 +++++++++ .../client/managers/sync_manager/profile.py | 18 +++ .../client/managers/sync_manager/sandbox.py | 119 +++++++++++++++- .../client/managers/sync_manager/session.py | 54 ++++++++ .../client/managers/sync_manager/volume.py | 15 +- hyperbrowser/models/__init__.py | 80 ++++++++++- hyperbrowser/models/agents/task.py | 104 ++++++++++++++ hyperbrowser/models/consts.py | 11 ++ hyperbrowser/models/profile.py | 12 ++ hyperbrowser/models/sandbox.py | 130 ++++++++++++++++-- hyperbrowser/models/session.py | 113 +++++++++++++++ hyperbrowser/models/volume.py | 9 ++ 29 files changed, 1089 insertions(+), 24 deletions(-) create mode 100644 hyperbrowser/client/managers/async_manager/agents/task.py create mode 100644 hyperbrowser/client/managers/sync_manager/agents/task.py create mode 100644 hyperbrowser/models/agents/task.py diff --git a/hyperbrowser/client/managers/async_manager/agents/__init__.py b/hyperbrowser/client/managers/async_manager/agents/__init__.py index 199bef50..1c9eae48 100644 --- a/hyperbrowser/client/managers/async_manager/agents/__init__.py +++ b/hyperbrowser/client/managers/async_manager/agents/__init__.py @@ -3,6 +3,7 @@ from .claude_computer_use import ClaudeComputerUseManager from .hyper_agent import HyperAgentManager from .gemini_computer_use import GeminiComputerUseManager +from .task import TaskManager class Agents: @@ -12,3 +13,4 @@ def __init__(self, client): self.claude_computer_use = ClaudeComputerUseManager(client) self.hyper_agent = HyperAgentManager(client) self.gemini_computer_use = GeminiComputerUseManager(client) + self.task = TaskManager(client) diff --git a/hyperbrowser/client/managers/async_manager/agents/browser_use.py b/hyperbrowser/client/managers/async_manager/agents/browser_use.py index 4844d565..3ecd75ec 100644 --- a/hyperbrowser/client/managers/async_manager/agents/browser_use.py +++ b/hyperbrowser/client/managers/async_manager/agents/browser_use.py @@ -6,6 +6,8 @@ from .....models import ( POLLING_ATTEMPTS, BasicResponse, + AgentTaskListParams, + AgentTaskListResponse, BrowserUseTaskResponse, BrowserUseTaskStatusResponse, StartBrowserUseTaskParams, @@ -51,6 +53,15 @@ async def stop(self, job_id: str) -> BasicResponse: ) return BasicResponse(**response.data) + async def list( + self, params: AgentTaskListParams = AgentTaskListParams() + ) -> AgentTaskListResponse: + response = await self._client.transport.get( + self._client._build_url("/task/browser-use"), + params=params.model_dump(exclude_none=True, by_alias=True), + ) + return AgentTaskListResponse(**response.data) + async def start_and_wait( self, params: StartBrowserUseTaskParams ) -> BrowserUseTaskResponse: diff --git a/hyperbrowser/client/managers/async_manager/agents/claude_computer_use.py b/hyperbrowser/client/managers/async_manager/agents/claude_computer_use.py index e1461d0a..398873fc 100644 --- a/hyperbrowser/client/managers/async_manager/agents/claude_computer_use.py +++ b/hyperbrowser/client/managers/async_manager/agents/claude_computer_use.py @@ -5,6 +5,8 @@ from .....models import ( POLLING_ATTEMPTS, BasicResponse, + AgentTaskListParams, + AgentTaskListResponse, ClaudeComputerUseTaskResponse, ClaudeComputerUseTaskStatusResponse, StartClaudeComputerUseTaskParams, @@ -43,6 +45,15 @@ async def stop(self, job_id: str) -> BasicResponse: ) return BasicResponse(**response.data) + async def list( + self, params: AgentTaskListParams = AgentTaskListParams() + ) -> AgentTaskListResponse: + response = await self._client.transport.get( + self._client._build_url("/task/claude-computer-use"), + params=params.model_dump(exclude_none=True, by_alias=True), + ) + return AgentTaskListResponse(**response.data) + async def start_and_wait( self, params: StartClaudeComputerUseTaskParams ) -> ClaudeComputerUseTaskResponse: diff --git a/hyperbrowser/client/managers/async_manager/agents/cua.py b/hyperbrowser/client/managers/async_manager/agents/cua.py index bce5a18b..d2f43e98 100644 --- a/hyperbrowser/client/managers/async_manager/agents/cua.py +++ b/hyperbrowser/client/managers/async_manager/agents/cua.py @@ -5,6 +5,8 @@ from .....models import ( POLLING_ATTEMPTS, BasicResponse, + AgentTaskListParams, + AgentTaskListResponse, CuaTaskResponse, CuaTaskStatusResponse, StartCuaTaskParams, @@ -41,6 +43,15 @@ async def stop(self, job_id: str) -> BasicResponse: ) return BasicResponse(**response.data) + async def list( + self, params: AgentTaskListParams = AgentTaskListParams() + ) -> AgentTaskListResponse: + response = await self._client.transport.get( + self._client._build_url("/task/cua"), + params=params.model_dump(exclude_none=True, by_alias=True), + ) + return AgentTaskListResponse(**response.data) + async def start_and_wait(self, params: StartCuaTaskParams) -> CuaTaskResponse: job_start_resp = await self.start(params) job_id = job_start_resp.job_id diff --git a/hyperbrowser/client/managers/async_manager/agents/gemini_computer_use.py b/hyperbrowser/client/managers/async_manager/agents/gemini_computer_use.py index 99e4a949..05f8f80a 100644 --- a/hyperbrowser/client/managers/async_manager/agents/gemini_computer_use.py +++ b/hyperbrowser/client/managers/async_manager/agents/gemini_computer_use.py @@ -5,6 +5,8 @@ from .....models import ( POLLING_ATTEMPTS, BasicResponse, + AgentTaskListParams, + AgentTaskListResponse, GeminiComputerUseTaskResponse, GeminiComputerUseTaskStatusResponse, StartGeminiComputerUseTaskParams, @@ -43,6 +45,15 @@ async def stop(self, job_id: str) -> BasicResponse: ) return BasicResponse(**response.data) + async def list( + self, params: AgentTaskListParams = AgentTaskListParams() + ) -> AgentTaskListResponse: + response = await self._client.transport.get( + self._client._build_url("/task/gemini-computer-use"), + params=params.model_dump(exclude_none=True, by_alias=True), + ) + return AgentTaskListResponse(**response.data) + async def start_and_wait( self, params: StartGeminiComputerUseTaskParams ) -> GeminiComputerUseTaskResponse: diff --git a/hyperbrowser/client/managers/async_manager/agents/hyper_agent.py b/hyperbrowser/client/managers/async_manager/agents/hyper_agent.py index 5ff74bce..a49c5d64 100644 --- a/hyperbrowser/client/managers/async_manager/agents/hyper_agent.py +++ b/hyperbrowser/client/managers/async_manager/agents/hyper_agent.py @@ -5,6 +5,8 @@ from .....models import ( POLLING_ATTEMPTS, BasicResponse, + AgentTaskListParams, + AgentTaskListResponse, HyperAgentTaskResponse, HyperAgentTaskStatusResponse, StartHyperAgentTaskParams, @@ -43,6 +45,15 @@ async def stop(self, job_id: str) -> BasicResponse: ) return BasicResponse(**response.data) + async def list( + self, params: AgentTaskListParams = AgentTaskListParams() + ) -> AgentTaskListResponse: + response = await self._client.transport.get( + self._client._build_url("/task/hyper-agent"), + params=params.model_dump(exclude_none=True, by_alias=True), + ) + return AgentTaskListResponse(**response.data) + async def start_and_wait( self, params: StartHyperAgentTaskParams ) -> HyperAgentTaskResponse: diff --git a/hyperbrowser/client/managers/async_manager/agents/task.py b/hyperbrowser/client/managers/async_manager/agents/task.py new file mode 100644 index 00000000..80d4a5b2 --- /dev/null +++ b/hyperbrowser/client/managers/async_manager/agents/task.py @@ -0,0 +1,62 @@ +import asyncio + +from hyperbrowser.exceptions import HyperbrowserError +from .....models import ( + POLLING_ATTEMPTS, + BasicResponse, + StartTaskParams, + StartTaskResponse, + TaskResponse, + TaskStatusResponse, +) + + +class TaskManager: + def __init__(self, client): + self._client = client + + async def start(self, params: StartTaskParams) -> StartTaskResponse: + response = await self._client.transport.post( + self._client._build_url("/task"), + data=params.model_dump(exclude_none=True, by_alias=True), + ) + return StartTaskResponse(**response.data) + + async def get(self, job_id: str) -> TaskResponse: + response = await self._client.transport.get( + self._client._build_url(f"/task/{job_id}") + ) + return TaskResponse(**response.data) + + async def get_status(self, job_id: str) -> TaskStatusResponse: + response = await self._client.transport.get( + self._client._build_url(f"/task/{job_id}/status") + ) + return TaskStatusResponse(**response.data) + + async def stop(self, job_id: str) -> BasicResponse: + response = await self._client.transport.put( + self._client._build_url(f"/task/{job_id}/stop") + ) + return BasicResponse(**response.data) + + async def start_and_wait(self, params: StartTaskParams) -> TaskResponse: + job_start_resp = await self.start(params) + job_id = job_start_resp.job_id + if not job_id: + raise HyperbrowserError("Failed to start task job") + + failures = 0 + while True: + try: + job_response = await self.get_status(job_id) + if job_response.status in ("completed", "failed", "stopped"): + return await self.get(job_id) + failures = 0 + except Exception as e: + failures += 1 + if failures >= POLLING_ATTEMPTS: + raise HyperbrowserError( + f"Failed to poll task job {job_id} after {POLLING_ATTEMPTS} attempts: {e}" + ) + await asyncio.sleep(2) diff --git a/hyperbrowser/client/managers/async_manager/profile.py b/hyperbrowser/client/managers/async_manager/profile.py index 81628dd3..a93a3701 100644 --- a/hyperbrowser/client/managers/async_manager/profile.py +++ b/hyperbrowser/client/managers/async_manager/profile.py @@ -4,6 +4,8 @@ ProfileListParams, ProfileListResponse, ProfileResponse, + UpdateProfileParams, + BatchDeleteProfilesParams, ) from hyperbrowser.models.session import BasicResponse @@ -35,6 +37,22 @@ async def delete(self, id: str) -> BasicResponse: ) return BasicResponse(**response.data) + async def update(self, id: str, params: UpdateProfileParams) -> ProfileResponse: + response = await self._client.transport.put( + self._client._build_url(f"/profile/{id}"), + data=params.model_dump(exclude_none=True, by_alias=True), + ) + return ProfileResponse(**response.data) + + async def delete_many(self, params: BatchDeleteProfilesParams) -> BasicResponse: + response = await self._client.transport.client.request( + "DELETE", + self._client._build_url("/profiles"), + json=params.model_dump(exclude_none=True, by_alias=True), + ) + handled = await self._client.transport._handle_response(response) + return BasicResponse(**handled.data) + async def list( self, params: ProfileListParams = ProfileListParams() ) -> ProfileListResponse: diff --git a/hyperbrowser/client/managers/async_manager/sandbox.py b/hyperbrowser/client/managers/async_manager/sandbox.py index 6c68707a..cb83a249 100644 --- a/hyperbrowser/client/managers/async_manager/sandbox.py +++ b/hyperbrowser/client/managers/async_manager/sandbox.py @@ -10,6 +10,14 @@ SandboxImageListResponse, SandboxListParams, SandboxListResponse, + SandboxImageListParams, + SandboxRuntimeBrowserAuthResponse, + CreateFirecrackerImageBuildParams, + CompleteFirecrackerImageBuildParams, + CreateFirecrackerImageBuildResponse, + FirecrackerImageBuildResponse, + FirecrackerImageBuildListParams, + FirecrackerImageBuildListResponse, SandboxMemorySnapshotParams, SandboxMemorySnapshotResult, SandboxRuntimeSession, @@ -149,6 +157,56 @@ async def connect(self) -> "SandboxHandle": await self.create_runtime_session(force_refresh=True) return self + async def get_runtime_browser_auth( + self, sandbox_id: str, allowed_origin: str + ) -> SandboxRuntimeBrowserAuthResponse: + response = await self._client.transport.client.request( + "POST", + self._client._build_url(f"/sandbox/{sandbox_id}/runtime/browser-auth"), + headers={"Origin": allowed_origin}, + ) + payload = await self._client.transport._handle_response(response) + return SandboxRuntimeBrowserAuthResponse(**payload.data) + + async def create_image_build( + self, params: CreateFirecrackerImageBuildParams + ) -> CreateFirecrackerImageBuildResponse: + payload = await self._request( + "POST", + "/images/builds", + data=params.model_dump(exclude_none=True, by_alias=True), + ) + return CreateFirecrackerImageBuildResponse(**payload) + + async def list_image_builds( + self, + params: FirecrackerImageBuildListParams = FirecrackerImageBuildListParams(), + ) -> FirecrackerImageBuildListResponse: + payload = await self._request( + "GET", + "/images/builds", + params=params.model_dump(exclude_none=True, by_alias=True), + ) + return FirecrackerImageBuildListResponse(**payload) + + async def get_image_build(self, build_id: str) -> FirecrackerImageBuildResponse: + payload = await self._request("GET", f"/images/builds/{build_id}") + return FirecrackerImageBuildResponse(**payload) + + async def complete_image_build( + self, build_id: str, params: CompleteFirecrackerImageBuildParams + ) -> FirecrackerImageBuildResponse: + payload = await self._request( + "POST", + f"/images/builds/{build_id}/complete", + data=params.model_dump(exclude_none=True, by_alias=True), + ) + return FirecrackerImageBuildResponse(**payload) + + async def cancel_image_build(self, build_id: str) -> FirecrackerImageBuildResponse: + payload = await self._request("POST", f"/images/builds/{build_id}/cancel") + return FirecrackerImageBuildResponse(**payload) + async def stop(self) -> BasicResponse: response = await self._service.stop(self.id) self._clear_runtime_session("closed") @@ -350,8 +408,15 @@ async def list( ) return SandboxListResponse(**payload) - async def list_images(self) -> SandboxImageListResponse: - payload = await self._request("GET", "/images") + async def list_images( + self, params: SandboxImageListParams = SandboxImageListParams() + ) -> SandboxImageListResponse: + query = params.model_dump(exclude_none=True, by_alias=True) + payload = await self._request( + "GET", + "/images", + params=query or None, + ) return SandboxImageListResponse(**payload) async def list_snapshots( @@ -366,6 +431,56 @@ async def list_snapshots( ) return SandboxSnapshotListResponse(**payload) + async def get_runtime_browser_auth( + self, sandbox_id: str, allowed_origin: str + ) -> SandboxRuntimeBrowserAuthResponse: + response = await self._client.transport.client.request( + "POST", + self._client._build_url(f"/sandbox/{sandbox_id}/runtime/browser-auth"), + headers={"Origin": allowed_origin}, + ) + payload = await self._client.transport._handle_response(response) + return SandboxRuntimeBrowserAuthResponse(**payload.data) + + async def create_image_build( + self, params: CreateFirecrackerImageBuildParams + ) -> CreateFirecrackerImageBuildResponse: + payload = await self._request( + "POST", + "/images/builds", + data=params.model_dump(exclude_none=True, by_alias=True), + ) + return CreateFirecrackerImageBuildResponse(**payload) + + async def list_image_builds( + self, + params: FirecrackerImageBuildListParams = FirecrackerImageBuildListParams(), + ) -> FirecrackerImageBuildListResponse: + payload = await self._request( + "GET", + "/images/builds", + params=params.model_dump(exclude_none=True, by_alias=True), + ) + return FirecrackerImageBuildListResponse(**payload) + + async def get_image_build(self, build_id: str) -> FirecrackerImageBuildResponse: + payload = await self._request("GET", f"/images/builds/{build_id}") + return FirecrackerImageBuildResponse(**payload) + + async def complete_image_build( + self, build_id: str, params: CompleteFirecrackerImageBuildParams + ) -> FirecrackerImageBuildResponse: + payload = await self._request( + "POST", + f"/images/builds/{build_id}/complete", + data=params.model_dump(exclude_none=True, by_alias=True), + ) + return FirecrackerImageBuildResponse(**payload) + + async def cancel_image_build(self, build_id: str) -> FirecrackerImageBuildResponse: + payload = await self._request("POST", f"/images/builds/{build_id}/cancel") + return FirecrackerImageBuildResponse(**payload) + async def stop(self, sandbox_id: str) -> BasicResponse: payload = await self._request("PUT", f"/sandbox/{sandbox_id}/stop") return BasicResponse(**payload) diff --git a/hyperbrowser/client/managers/async_manager/session.py b/hyperbrowser/client/managers/async_manager/session.py index eb0d2350..5e9f825a 100644 --- a/hyperbrowser/client/managers/async_manager/session.py +++ b/hyperbrowser/client/managers/async_manager/session.py @@ -18,6 +18,14 @@ UpdateSessionProxyParams, UpdateSessionScreenParams, SessionGetParams, + SessionLogsTokenResponse, + SessionInfoResponse, + SessionCreditUsageResponse, + SessionMetricsResponse, + SessionConsoleParams, + SessionConsoleResponse, + SessionNetworkParams, + SessionNetworkResponse, ) @@ -125,6 +133,54 @@ async def upload_file( return UploadFileResponse(**response.data) + async def stop_all(self) -> BasicResponse: + response = await self._client.transport.put( + self._client._build_url("/sessions/stop") + ) + return BasicResponse(**response.data) + + async def get_logs_token(self, id: str) -> SessionLogsTokenResponse: + response = await self._client.transport.get( + self._client._build_url(f"/session/{id}/logs-token") + ) + return SessionLogsTokenResponse(**response.data) + + async def get_info(self, id: str) -> SessionInfoResponse: + response = await self._client.transport.get( + self._client._build_url(f"/session/{id}/info") + ) + return SessionInfoResponse(**response.data) + + async def get_credit_usage(self, id: str) -> SessionCreditUsageResponse: + response = await self._client.transport.get( + self._client._build_url(f"/session/{id}/credit-usage") + ) + return SessionCreditUsageResponse(**response.data) + + async def get_metrics(self, id: str) -> SessionMetricsResponse: + response = await self._client.transport.get( + self._client._build_url(f"/session/{id}/metrics") + ) + return SessionMetricsResponse(**response.data) + + async def get_console_logs( + self, id: str, params: SessionConsoleParams = SessionConsoleParams() + ) -> SessionConsoleResponse: + response = await self._client.transport.get( + self._client._build_url(f"/session/{id}/console"), + params=params.model_dump(exclude_none=True, by_alias=True), + ) + return SessionConsoleResponse(**response.data) + + async def get_network_logs( + self, id: str, params: SessionNetworkParams = SessionNetworkParams() + ) -> SessionNetworkResponse: + response = await self._client.transport.get( + self._client._build_url(f"/session/{id}/network"), + params=params.model_dump(exclude_none=True, by_alias=True), + ) + return SessionNetworkResponse(**response.data) + async def extend_session(self, id: str, duration_minutes: int) -> BasicResponse: response = await self._client.transport.put( self._client._build_url(f"/session/{id}/extend-session"), diff --git a/hyperbrowser/client/managers/async_manager/volume.py b/hyperbrowser/client/managers/async_manager/volume.py index 4c9d11cf..7cf972c3 100644 --- a/hyperbrowser/client/managers/async_manager/volume.py +++ b/hyperbrowser/client/managers/async_manager/volume.py @@ -1,4 +1,9 @@ -from hyperbrowser.models.volume import CreateVolumeParams, Volume, VolumeListResponse +from hyperbrowser.models.volume import ( + CreateVolumeParams, + Volume, + VolumeListParams, + VolumeListResponse, +) class VolumeManager: @@ -15,8 +20,14 @@ async def create(self, params: CreateVolumeParams) -> Volume: ) return Volume(**response.data) - async def list(self) -> VolumeListResponse: - response = await self._client.transport.get(self._client._build_url("/volume")) + async def list( + self, params: VolumeListParams = VolumeListParams() + ) -> VolumeListResponse: + query = params.model_dump(exclude_none=True, by_alias=True) + response = await self._client.transport.get( + self._client._build_url("/volume"), + params=query or None, + ) return VolumeListResponse(**response.data) async def get(self, volume_id: str) -> Volume: diff --git a/hyperbrowser/client/managers/sync_manager/agents/__init__.py b/hyperbrowser/client/managers/sync_manager/agents/__init__.py index 199bef50..1c9eae48 100644 --- a/hyperbrowser/client/managers/sync_manager/agents/__init__.py +++ b/hyperbrowser/client/managers/sync_manager/agents/__init__.py @@ -3,6 +3,7 @@ from .claude_computer_use import ClaudeComputerUseManager from .hyper_agent import HyperAgentManager from .gemini_computer_use import GeminiComputerUseManager +from .task import TaskManager class Agents: @@ -12,3 +13,4 @@ def __init__(self, client): self.claude_computer_use = ClaudeComputerUseManager(client) self.hyper_agent = HyperAgentManager(client) self.gemini_computer_use = GeminiComputerUseManager(client) + self.task = TaskManager(client) diff --git a/hyperbrowser/client/managers/sync_manager/agents/browser_use.py b/hyperbrowser/client/managers/sync_manager/agents/browser_use.py index 60212b0d..48434015 100644 --- a/hyperbrowser/client/managers/sync_manager/agents/browser_use.py +++ b/hyperbrowser/client/managers/sync_manager/agents/browser_use.py @@ -6,6 +6,8 @@ from .....models import ( POLLING_ATTEMPTS, BasicResponse, + AgentTaskListParams, + AgentTaskListResponse, BrowserUseTaskResponse, BrowserUseTaskStatusResponse, StartBrowserUseTaskParams, @@ -49,6 +51,15 @@ def stop(self, job_id: str) -> BasicResponse: ) return BasicResponse(**response.data) + def list( + self, params: AgentTaskListParams = AgentTaskListParams() + ) -> AgentTaskListResponse: + response = self._client.transport.get( + self._client._build_url("/task/browser-use"), + params=params.model_dump(exclude_none=True, by_alias=True), + ) + return AgentTaskListResponse(**response.data) + def start_and_wait( self, params: StartBrowserUseTaskParams ) -> BrowserUseTaskResponse: diff --git a/hyperbrowser/client/managers/sync_manager/agents/claude_computer_use.py b/hyperbrowser/client/managers/sync_manager/agents/claude_computer_use.py index 81c34b1b..42e8e18f 100644 --- a/hyperbrowser/client/managers/sync_manager/agents/claude_computer_use.py +++ b/hyperbrowser/client/managers/sync_manager/agents/claude_computer_use.py @@ -5,6 +5,8 @@ from .....models import ( POLLING_ATTEMPTS, BasicResponse, + AgentTaskListParams, + AgentTaskListResponse, ClaudeComputerUseTaskResponse, ClaudeComputerUseTaskStatusResponse, StartClaudeComputerUseTaskParams, @@ -43,6 +45,15 @@ def stop(self, job_id: str) -> BasicResponse: ) return BasicResponse(**response.data) + def list( + self, params: AgentTaskListParams = AgentTaskListParams() + ) -> AgentTaskListResponse: + response = self._client.transport.get( + self._client._build_url("/task/claude-computer-use"), + params=params.model_dump(exclude_none=True, by_alias=True), + ) + return AgentTaskListResponse(**response.data) + def start_and_wait( self, params: StartClaudeComputerUseTaskParams ) -> ClaudeComputerUseTaskResponse: diff --git a/hyperbrowser/client/managers/sync_manager/agents/cua.py b/hyperbrowser/client/managers/sync_manager/agents/cua.py index d8b955c9..82104fba 100644 --- a/hyperbrowser/client/managers/sync_manager/agents/cua.py +++ b/hyperbrowser/client/managers/sync_manager/agents/cua.py @@ -5,6 +5,8 @@ from .....models import ( POLLING_ATTEMPTS, BasicResponse, + AgentTaskListParams, + AgentTaskListResponse, CuaTaskResponse, CuaTaskStatusResponse, StartCuaTaskParams, @@ -41,6 +43,15 @@ def stop(self, job_id: str) -> BasicResponse: ) return BasicResponse(**response.data) + def list( + self, params: AgentTaskListParams = AgentTaskListParams() + ) -> AgentTaskListResponse: + response = self._client.transport.get( + self._client._build_url("/task/cua"), + params=params.model_dump(exclude_none=True, by_alias=True), + ) + return AgentTaskListResponse(**response.data) + def start_and_wait(self, params: StartCuaTaskParams) -> CuaTaskResponse: job_start_resp = self.start(params) job_id = job_start_resp.job_id diff --git a/hyperbrowser/client/managers/sync_manager/agents/gemini_computer_use.py b/hyperbrowser/client/managers/sync_manager/agents/gemini_computer_use.py index 766514a0..7f3d8d61 100644 --- a/hyperbrowser/client/managers/sync_manager/agents/gemini_computer_use.py +++ b/hyperbrowser/client/managers/sync_manager/agents/gemini_computer_use.py @@ -5,6 +5,8 @@ from .....models import ( POLLING_ATTEMPTS, BasicResponse, + AgentTaskListParams, + AgentTaskListResponse, GeminiComputerUseTaskResponse, GeminiComputerUseTaskStatusResponse, StartGeminiComputerUseTaskParams, @@ -43,6 +45,15 @@ def stop(self, job_id: str) -> BasicResponse: ) return BasicResponse(**response.data) + def list( + self, params: AgentTaskListParams = AgentTaskListParams() + ) -> AgentTaskListResponse: + response = self._client.transport.get( + self._client._build_url("/task/gemini-computer-use"), + params=params.model_dump(exclude_none=True, by_alias=True), + ) + return AgentTaskListResponse(**response.data) + def start_and_wait( self, params: StartGeminiComputerUseTaskParams ) -> GeminiComputerUseTaskResponse: diff --git a/hyperbrowser/client/managers/sync_manager/agents/hyper_agent.py b/hyperbrowser/client/managers/sync_manager/agents/hyper_agent.py index d2bde824..ce79cb15 100644 --- a/hyperbrowser/client/managers/sync_manager/agents/hyper_agent.py +++ b/hyperbrowser/client/managers/sync_manager/agents/hyper_agent.py @@ -5,6 +5,8 @@ from .....models import ( POLLING_ATTEMPTS, BasicResponse, + AgentTaskListParams, + AgentTaskListResponse, HyperAgentTaskResponse, HyperAgentTaskStatusResponse, StartHyperAgentTaskParams, @@ -41,6 +43,15 @@ def stop(self, job_id: str) -> BasicResponse: ) return BasicResponse(**response.data) + def list( + self, params: AgentTaskListParams = AgentTaskListParams() + ) -> AgentTaskListResponse: + response = self._client.transport.get( + self._client._build_url("/task/hyper-agent"), + params=params.model_dump(exclude_none=True, by_alias=True), + ) + return AgentTaskListResponse(**response.data) + def start_and_wait( self, params: StartHyperAgentTaskParams ) -> HyperAgentTaskResponse: diff --git a/hyperbrowser/client/managers/sync_manager/agents/task.py b/hyperbrowser/client/managers/sync_manager/agents/task.py new file mode 100644 index 00000000..6a3d7f24 --- /dev/null +++ b/hyperbrowser/client/managers/sync_manager/agents/task.py @@ -0,0 +1,62 @@ +import time + +from hyperbrowser.exceptions import HyperbrowserError +from .....models import ( + POLLING_ATTEMPTS, + BasicResponse, + StartTaskParams, + StartTaskResponse, + TaskResponse, + TaskStatusResponse, +) + + +class TaskManager: + def __init__(self, client): + self._client = client + + def start(self, params: StartTaskParams) -> StartTaskResponse: + response = self._client.transport.post( + self._client._build_url("/task"), + data=params.model_dump(exclude_none=True, by_alias=True), + ) + return StartTaskResponse(**response.data) + + def get(self, job_id: str) -> TaskResponse: + response = self._client.transport.get( + self._client._build_url(f"/task/{job_id}") + ) + return TaskResponse(**response.data) + + def get_status(self, job_id: str) -> TaskStatusResponse: + response = self._client.transport.get( + self._client._build_url(f"/task/{job_id}/status") + ) + return TaskStatusResponse(**response.data) + + def stop(self, job_id: str) -> BasicResponse: + response = self._client.transport.put( + self._client._build_url(f"/task/{job_id}/stop") + ) + return BasicResponse(**response.data) + + def start_and_wait(self, params: StartTaskParams) -> TaskResponse: + job_start_resp = self.start(params) + job_id = job_start_resp.job_id + if not job_id: + raise HyperbrowserError("Failed to start task job") + + failures = 0 + while True: + try: + job_response = self.get_status(job_id) + if job_response.status in ("completed", "failed", "stopped"): + return self.get(job_id) + failures = 0 + except Exception as e: + failures += 1 + if failures >= POLLING_ATTEMPTS: + raise HyperbrowserError( + f"Failed to poll task job {job_id} after {POLLING_ATTEMPTS} attempts: {e}" + ) + time.sleep(2) diff --git a/hyperbrowser/client/managers/sync_manager/profile.py b/hyperbrowser/client/managers/sync_manager/profile.py index 0d194dee..6b1f4ea0 100644 --- a/hyperbrowser/client/managers/sync_manager/profile.py +++ b/hyperbrowser/client/managers/sync_manager/profile.py @@ -4,6 +4,8 @@ ProfileListParams, ProfileListResponse, ProfileResponse, + UpdateProfileParams, + BatchDeleteProfilesParams, ) from hyperbrowser.models.session import BasicResponse @@ -35,6 +37,22 @@ def delete(self, id: str) -> BasicResponse: ) return BasicResponse(**response.data) + def update(self, id: str, params: UpdateProfileParams) -> ProfileResponse: + response = self._client.transport.put( + self._client._build_url(f"/profile/{id}"), + data=params.model_dump(exclude_none=True, by_alias=True), + ) + return ProfileResponse(**response.data) + + def delete_many(self, params: BatchDeleteProfilesParams) -> BasicResponse: + response = self._client.transport.client.request( + "DELETE", + self._client._build_url("/profiles"), + json=params.model_dump(exclude_none=True, by_alias=True), + ) + handled = self._client.transport._handle_response(response) + return BasicResponse(**handled.data) + def list( self, params: ProfileListParams = ProfileListParams() ) -> ProfileListResponse: diff --git a/hyperbrowser/client/managers/sync_manager/sandbox.py b/hyperbrowser/client/managers/sync_manager/sandbox.py index 1856bc8b..02b488d8 100644 --- a/hyperbrowser/client/managers/sync_manager/sandbox.py +++ b/hyperbrowser/client/managers/sync_manager/sandbox.py @@ -10,6 +10,14 @@ SandboxImageListResponse, SandboxListParams, SandboxListResponse, + SandboxImageListParams, + SandboxRuntimeBrowserAuthResponse, + CreateFirecrackerImageBuildParams, + CompleteFirecrackerImageBuildParams, + CreateFirecrackerImageBuildResponse, + FirecrackerImageBuildResponse, + FirecrackerImageBuildListParams, + FirecrackerImageBuildListResponse, SandboxMemorySnapshotParams, SandboxMemorySnapshotResult, SandboxRuntimeSession, @@ -149,6 +157,56 @@ def connect(self) -> "SandboxHandle": self.create_runtime_session(force_refresh=True) return self + def get_runtime_browser_auth( + self, sandbox_id: str, allowed_origin: str + ) -> SandboxRuntimeBrowserAuthResponse: + response = self._client.transport.client.request( + "POST", + self._client._build_url(f"/sandbox/{sandbox_id}/runtime/browser-auth"), + headers={"Origin": allowed_origin}, + ) + payload = self._client.transport._handle_response(response) + return SandboxRuntimeBrowserAuthResponse(**payload.data) + + def create_image_build( + self, params: CreateFirecrackerImageBuildParams + ) -> CreateFirecrackerImageBuildResponse: + payload = self._request( + "POST", + "/images/builds", + data=params.model_dump(exclude_none=True, by_alias=True), + ) + return CreateFirecrackerImageBuildResponse(**payload) + + def list_image_builds( + self, + params: FirecrackerImageBuildListParams = FirecrackerImageBuildListParams(), + ) -> FirecrackerImageBuildListResponse: + payload = self._request( + "GET", + "/images/builds", + params=params.model_dump(exclude_none=True, by_alias=True), + ) + return FirecrackerImageBuildListResponse(**payload) + + def get_image_build(self, build_id: str) -> FirecrackerImageBuildResponse: + payload = self._request("GET", f"/images/builds/{build_id}") + return FirecrackerImageBuildResponse(**payload) + + def complete_image_build( + self, build_id: str, params: CompleteFirecrackerImageBuildParams + ) -> FirecrackerImageBuildResponse: + payload = self._request( + "POST", + f"/images/builds/{build_id}/complete", + data=params.model_dump(exclude_none=True, by_alias=True), + ) + return FirecrackerImageBuildResponse(**payload) + + def cancel_image_build(self, build_id: str) -> FirecrackerImageBuildResponse: + payload = self._request("POST", f"/images/builds/{build_id}/cancel") + return FirecrackerImageBuildResponse(**payload) + def stop(self) -> BasicResponse: response = self._service.stop(self.id) self._clear_runtime_session("closed") @@ -350,8 +408,15 @@ def list( ) return SandboxListResponse(**payload) - def list_images(self) -> SandboxImageListResponse: - payload = self._request("GET", "/images") + def list_images( + self, params: SandboxImageListParams = SandboxImageListParams() + ) -> SandboxImageListResponse: + query = params.model_dump(exclude_none=True, by_alias=True) + payload = self._request( + "GET", + "/images", + params=query or None, + ) return SandboxImageListResponse(**payload) def list_snapshots( @@ -366,6 +431,56 @@ def list_snapshots( ) return SandboxSnapshotListResponse(**payload) + def get_runtime_browser_auth( + self, sandbox_id: str, allowed_origin: str + ) -> SandboxRuntimeBrowserAuthResponse: + response = self._client.transport.client.request( + "POST", + self._client._build_url(f"/sandbox/{sandbox_id}/runtime/browser-auth"), + headers={"Origin": allowed_origin}, + ) + payload = self._client.transport._handle_response(response) + return SandboxRuntimeBrowserAuthResponse(**payload.data) + + def create_image_build( + self, params: CreateFirecrackerImageBuildParams + ) -> CreateFirecrackerImageBuildResponse: + payload = self._request( + "POST", + "/images/builds", + data=params.model_dump(exclude_none=True, by_alias=True), + ) + return CreateFirecrackerImageBuildResponse(**payload) + + def list_image_builds( + self, + params: FirecrackerImageBuildListParams = FirecrackerImageBuildListParams(), + ) -> FirecrackerImageBuildListResponse: + payload = self._request( + "GET", + "/images/builds", + params=params.model_dump(exclude_none=True, by_alias=True), + ) + return FirecrackerImageBuildListResponse(**payload) + + def get_image_build(self, build_id: str) -> FirecrackerImageBuildResponse: + payload = self._request("GET", f"/images/builds/{build_id}") + return FirecrackerImageBuildResponse(**payload) + + def complete_image_build( + self, build_id: str, params: CompleteFirecrackerImageBuildParams + ) -> FirecrackerImageBuildResponse: + payload = self._request( + "POST", + f"/images/builds/{build_id}/complete", + data=params.model_dump(exclude_none=True, by_alias=True), + ) + return FirecrackerImageBuildResponse(**payload) + + def cancel_image_build(self, build_id: str) -> FirecrackerImageBuildResponse: + payload = self._request("POST", f"/images/builds/{build_id}/cancel") + return FirecrackerImageBuildResponse(**payload) + def stop(self, sandbox_id: str) -> BasicResponse: payload = self._request("PUT", f"/sandbox/{sandbox_id}/stop") return BasicResponse(**payload) diff --git a/hyperbrowser/client/managers/sync_manager/session.py b/hyperbrowser/client/managers/sync_manager/session.py index 4756a9e6..2638ec45 100644 --- a/hyperbrowser/client/managers/sync_manager/session.py +++ b/hyperbrowser/client/managers/sync_manager/session.py @@ -17,6 +17,14 @@ UpdateSessionProxyParams, UpdateSessionScreenParams, SessionGetParams, + SessionLogsTokenResponse, + SessionInfoResponse, + SessionCreditUsageResponse, + SessionMetricsResponse, + SessionConsoleParams, + SessionConsoleResponse, + SessionNetworkParams, + SessionNetworkResponse, ) @@ -120,6 +128,52 @@ def upload_file(self, id: str, file_input: Union[str, IO]) -> UploadFileResponse return UploadFileResponse(**response.data) + def stop_all(self) -> BasicResponse: + response = self._client.transport.put(self._client._build_url("/sessions/stop")) + return BasicResponse(**response.data) + + def get_logs_token(self, id: str) -> SessionLogsTokenResponse: + response = self._client.transport.get( + self._client._build_url(f"/session/{id}/logs-token") + ) + return SessionLogsTokenResponse(**response.data) + + def get_info(self, id: str) -> SessionInfoResponse: + response = self._client.transport.get( + self._client._build_url(f"/session/{id}/info") + ) + return SessionInfoResponse(**response.data) + + def get_credit_usage(self, id: str) -> SessionCreditUsageResponse: + response = self._client.transport.get( + self._client._build_url(f"/session/{id}/credit-usage") + ) + return SessionCreditUsageResponse(**response.data) + + def get_metrics(self, id: str) -> SessionMetricsResponse: + response = self._client.transport.get( + self._client._build_url(f"/session/{id}/metrics") + ) + return SessionMetricsResponse(**response.data) + + def get_console_logs( + self, id: str, params: SessionConsoleParams = SessionConsoleParams() + ) -> SessionConsoleResponse: + response = self._client.transport.get( + self._client._build_url(f"/session/{id}/console"), + params=params.model_dump(exclude_none=True, by_alias=True), + ) + return SessionConsoleResponse(**response.data) + + def get_network_logs( + self, id: str, params: SessionNetworkParams = SessionNetworkParams() + ) -> SessionNetworkResponse: + response = self._client.transport.get( + self._client._build_url(f"/session/{id}/network"), + params=params.model_dump(exclude_none=True, by_alias=True), + ) + return SessionNetworkResponse(**response.data) + def extend_session(self, id: str, duration_minutes: int) -> BasicResponse: response = self._client.transport.put( self._client._build_url(f"/session/{id}/extend-session"), diff --git a/hyperbrowser/client/managers/sync_manager/volume.py b/hyperbrowser/client/managers/sync_manager/volume.py index e0496376..be565a03 100644 --- a/hyperbrowser/client/managers/sync_manager/volume.py +++ b/hyperbrowser/client/managers/sync_manager/volume.py @@ -1,4 +1,9 @@ -from hyperbrowser.models.volume import CreateVolumeParams, Volume, VolumeListResponse +from hyperbrowser.models.volume import ( + CreateVolumeParams, + Volume, + VolumeListParams, + VolumeListResponse, +) class VolumeManager: @@ -15,8 +20,12 @@ def create(self, params: CreateVolumeParams) -> Volume: ) return Volume(**response.data) - def list(self) -> VolumeListResponse: - response = self._client.transport.get(self._client._build_url("/volume")) + def list(self, params: VolumeListParams = VolumeListParams()) -> VolumeListResponse: + query = params.model_dump(exclude_none=True, by_alias=True) + response = self._client.transport.get( + self._client._build_url("/volume"), + params=query or None, + ) return VolumeListResponse(**response.data) def get(self, volume_id: str) -> Volume: diff --git a/hyperbrowser/models/__init__.py b/hyperbrowser/models/__init__.py index f1e1e24d..c2814673 100644 --- a/hyperbrowser/models/__init__.py +++ b/hyperbrowser/models/__init__.py @@ -126,6 +126,17 @@ CuaApiKeys, CuaTaskStatus, ) +from .agents.task import ( + TaskStatus, + AgentTaskListParams, + AgentTaskSummary, + AgentTaskListResponse, + StartTaskParams, + StartTaskResponse, + TaskStatusResponse, + TaskMetadata, + TaskResponse, +) from .agents.hyper_agent import ( HyperAgentActionOutput, HyperAgentOutput, @@ -170,6 +181,7 @@ SessionRegion, BrowserUseVersion, HyperAgentVersion, + TaskLlm, ) from .crawl import ( CrawledPage, @@ -196,8 +208,10 @@ ProfileListParams, ProfileListResponse, ProfileResponse, + UpdateProfileParams, + BatchDeleteProfilesParams, ) -from .volume import CreateVolumeParams, Volume, VolumeListResponse +from .volume import CreateVolumeParams, Volume, VolumeListParams, VolumeListResponse from .scrape import ( BatchScrapeJobResponse, BatchScrapeJobStatusResponse, @@ -257,6 +271,18 @@ SessionEventLog, SessionEventLogListParams, SessionEventLogListResponse, + SessionLogsTokenResponse, + SessionInfoProfile, + SessionInfoResponse, + SessionCreditUsageResponse, + SessionMetricsResponse, + SessionLogLevel, + SessionLogOrder, + SessionNetworkMethod, + SessionConsoleParams, + SessionConsoleResponse, + SessionNetworkParams, + SessionNetworkResponse, SessionCreditBreakdown, SessionProfile, SessionLaunchState, @@ -280,12 +306,25 @@ StartSandboxFromSnapshotParams, SandboxListParams, SandboxListResponse, + SandboxImageSource, + SandboxImageListParams, SandboxImageListResponse, SandboxImageSummary, SandboxSnapshotStatus, SandboxSnapshotListResponse, SandboxSnapshotSummary, SandboxSnapshotListParams, + SandboxRuntimeBrowserAuthResponse, + FirecrackerImageBuildStatus, + FirecrackerImageInit, + CreateFirecrackerImageBuildParams, + CompleteFirecrackerImageBuildParams, + FirecrackerImageBuild, + FirecrackerImageBuildUpload, + CreateFirecrackerImageBuildResponse, + FirecrackerImageBuildResponse, + FirecrackerImageBuildListParams, + FirecrackerImageBuildListResponse, SandboxMemorySnapshotParams, SandboxMemorySnapshotResult, SandboxExposeParams, @@ -385,6 +424,7 @@ "SessionRegion", "BrowserUseVersion", "HyperAgentVersion", + "TaskLlm", # agents "HyperAgentTaskStatus", "HyperAgentActionOutput", @@ -448,6 +488,15 @@ "HyperAgentApiKeys", "HyperAgentOutputV110", "HyperAgentStepV110", + "TaskStatus", + "AgentTaskListParams", + "AgentTaskSummary", + "AgentTaskListResponse", + "StartTaskParams", + "StartTaskResponse", + "TaskStatusResponse", + "TaskMetadata", + "TaskResponse", # crawl "CrawledPage", "CrawlJobResponse", @@ -481,9 +530,12 @@ "ProfileListParams", "ProfileListResponse", "ProfileResponse", + "UpdateProfileParams", + "BatchDeleteProfilesParams", # volume "CreateVolumeParams", "Volume", + "VolumeListParams", "VolumeListResponse", # scrape "BatchScrapeJobResponse", @@ -519,6 +571,18 @@ "SessionEventLog", "SessionEventLogListParams", "SessionEventLogListResponse", + "SessionLogsTokenResponse", + "SessionInfoProfile", + "SessionInfoResponse", + "SessionCreditUsageResponse", + "SessionMetricsResponse", + "SessionLogLevel", + "SessionLogOrder", + "SessionNetworkMethod", + "SessionConsoleParams", + "SessionConsoleResponse", + "SessionNetworkParams", + "SessionNetworkResponse", "SessionCreditBreakdown", "SessionProfile", "SessionLaunchState", @@ -541,16 +605,30 @@ "StartSandboxFromSnapshotParams", "SandboxListParams", "SandboxListResponse", + "SandboxImageSource", + "SandboxImageListParams", "SandboxImageListResponse", "SandboxImageSummary", "SandboxSnapshotStatus", "SandboxSnapshotListResponse", "SandboxSnapshotSummary", "SandboxSnapshotListParams", + "SandboxRuntimeBrowserAuthResponse", + "FirecrackerImageBuildStatus", + "FirecrackerImageInit", + "CreateFirecrackerImageBuildParams", + "CompleteFirecrackerImageBuildParams", + "FirecrackerImageBuild", + "FirecrackerImageBuildUpload", + "CreateFirecrackerImageBuildResponse", + "FirecrackerImageBuildResponse", + "FirecrackerImageBuildListParams", + "FirecrackerImageBuildListResponse", "SandboxMemorySnapshotParams", "SandboxMemorySnapshotResult", "SandboxExposeParams", "SandboxExposeResult", + "SandboxUnexposeResult", "SandboxProcessStatus", "SandboxExecParams", "SandboxProcessSummary", diff --git a/hyperbrowser/models/agents/task.py b/hyperbrowser/models/agents/task.py new file mode 100644 index 00000000..e89b5bd7 --- /dev/null +++ b/hyperbrowser/models/agents/task.py @@ -0,0 +1,104 @@ +from typing import Any, Dict, List, Optional + +from pydantic import BaseModel, ConfigDict, Field + +from ..consts import TaskLlm +from ..session import CreateSessionParams +from .browser_use import BrowserUseTaskStatus + +TaskStatus = BrowserUseTaskStatus + + +class AgentTaskListParams(BaseModel): + model_config = ConfigDict(populate_by_alias=True) + + task: Optional[str] = Field(default=None, serialization_alias="task") + page: Optional[int] = Field(default=None, serialization_alias="page") + limit: Optional[int] = Field(default=None, serialization_alias="limit") + + +class AgentTaskSummary(BaseModel): + model_config = ConfigDict(populate_by_alias=True) + + id: str + created_at: str = Field(alias="createdAt") + status: TaskStatus + task: str + + +class AgentTaskListResponse(BaseModel): + model_config = ConfigDict(populate_by_alias=True) + + tasks: List[AgentTaskSummary] + total_count: int = Field(alias="totalCount") + page: int + per_page: int = Field(alias="perPage") + + +class StartTaskParams(BaseModel): + model_config = ConfigDict(populate_by_alias=True) + + task: str + llm: Optional[TaskLlm] = Field(default=None, serialization_alias="llm") + session_id: Optional[str] = Field(default=None, serialization_alias="sessionId") + validate_output: Optional[bool] = Field( + default=None, serialization_alias="validateOutput" + ) + use_vision: Optional[bool] = Field(default=None, serialization_alias="useVision") + use_vision_for_planner: Optional[bool] = Field( + default=None, serialization_alias="useVisionForPlanner" + ) + max_actions_per_step: Optional[int] = Field( + default=None, serialization_alias="maxActionsPerStep" + ) + max_input_tokens: Optional[int] = Field( + default=None, serialization_alias="maxInputTokens" + ) + planner_llm: Optional[TaskLlm] = Field( + default=None, serialization_alias="plannerLlm" + ) + page_extraction_llm: Optional[TaskLlm] = Field( + default=None, serialization_alias="pageExtractionLlm" + ) + planner_interval: Optional[int] = Field( + default=None, serialization_alias="plannerInterval" + ) + max_steps: Optional[int] = Field(default=None, serialization_alias="maxSteps") + keep_browser_open: Optional[bool] = Field( + default=None, serialization_alias="keepBrowserOpen" + ) + session_options: Optional[CreateSessionParams] = Field( + default=None, serialization_alias="sessionOptions" + ) + + +class StartTaskResponse(BaseModel): + model_config = ConfigDict(populate_by_alias=True) + + job_id: str = Field(alias="jobId") + + +class TaskStatusResponse(BaseModel): + model_config = ConfigDict(populate_by_alias=True) + + status: TaskStatus + + +class TaskMetadata(BaseModel): + model_config = ConfigDict(populate_by_alias=True) + + input_tokens: Optional[int] = Field(default=None, alias="inputTokens") + output_tokens: Optional[int] = Field(default=None, alias="outputTokens") + num_task_steps_completed: Optional[int] = Field( + default=None, alias="numTaskStepsCompleted" + ) + + +class TaskResponse(BaseModel): + model_config = ConfigDict(populate_by_alias=True) + + job_id: str = Field(alias="jobId") + status: TaskStatus + metadata: Optional[TaskMetadata] = None + data: Optional[Dict[str, Any]] = None + error: Optional[str] = None diff --git a/hyperbrowser/models/consts.py b/hyperbrowser/models/consts.py index e7c9083a..7fdd7f1a 100644 --- a/hyperbrowser/models/consts.py +++ b/hyperbrowser/models/consts.py @@ -29,6 +29,17 @@ BrowserUseVersion = Literal["0.1.40", "0.7.10", "latest"] HyperAgentVersion = Literal["0.8.0", "1.1.0"] +TaskLlm = Literal[ + "gpt-4o", + "gpt-4o-mini", + "claude-sonnet-4-5", + "claude-3-7-sonnet-20250219", + "claude-3-5-sonnet-20241022", + "claude-3-5-haiku-20241022", + "gemini-2.0-flash", + "gemini-2.5-flash", +] + BrowserUseLlm = Literal[ "gpt-4o", "gpt-4o-mini", diff --git a/hyperbrowser/models/profile.py b/hyperbrowser/models/profile.py index 2e3a7a81..2ab6e074 100644 --- a/hyperbrowser/models/profile.py +++ b/hyperbrowser/models/profile.py @@ -16,6 +16,18 @@ class CreateProfileParams(BaseModel): name: Optional[str] = Field(default=None, serialization_alias="name") +class UpdateProfileParams(BaseModel): + model_config = ConfigDict(populate_by_alias=True) + + name: Optional[str] = Field(default=None, serialization_alias="name") + + +class BatchDeleteProfilesParams(BaseModel): + model_config = ConfigDict(populate_by_alias=True) + + ids: List[str] = Field(serialization_alias="ids") + + class CreateProfileResponse(BaseModel): """ Response containing the created profile. diff --git a/hyperbrowser/models/sandbox.py b/hyperbrowser/models/sandbox.py index aa7faf0a..851ef007 100644 --- a/hyperbrowser/models/sandbox.py +++ b/hyperbrowser/models/sandbox.py @@ -1,5 +1,5 @@ from datetime import datetime, timezone -from typing import Callable, Dict, List, Literal, Optional, Union +from typing import Any, Callable, Dict, List, Literal, Optional, Union from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator @@ -31,6 +31,17 @@ SandboxFileWatchRoute = Literal["ws", "stream"] SandboxFileSystemEventType = Literal["chmod", "create", "remove", "rename", "write"] SandboxVolumeMountType = Literal["rw", "ro"] +SandboxImageSource = Literal["public", "team"] +FirecrackerImageBuildStatus = Literal[ + "awaiting_upload", + "upload_verified", + "dispatching", + "building", + "verifying", + "completed", + "failed", + "canceled", +] def _parse_optional_datetime(value): @@ -228,9 +239,9 @@ class SandboxListParams(SandboxBaseModel): class SandboxListResponse(SandboxBaseModel): sandboxes: List[Sandbox] - total_count: int = Field(alias="totalCount") - page: int - per_page: int = Field(alias="perPage") + total_count: Optional[int] = Field(default=None, alias="totalCount") + page: Optional[int] = None + per_page: Optional[int] = Field(default=None, alias="perPage") class SandboxImageSummary(SandboxBaseModel): @@ -242,12 +253,18 @@ class SandboxImageSummary(SandboxBaseModel): updated_at: datetime = Field(alias="updatedAt") +class SandboxImageListParams(SandboxBaseModel): + page: Optional[int] = None + limit: Optional[int] = None + source: Optional[Union[SandboxImageSource, List[SandboxImageSource]]] = None + search: Optional[str] = None + + class SandboxImageListResponse(SandboxBaseModel): images: List[SandboxImageSummary] - # TODO: add pagination metadata when /api/images supports it. - # total_count: Optional[int] = Field(default=None, alias="totalCount") - # page: Optional[int] = None - # per_page: Optional[int] = Field(default=None, alias="perPage") + total_count: Optional[int] = Field(default=None, alias="totalCount") + page: Optional[int] = None + per_page: Optional[int] = Field(default=None, alias="perPage") class SandboxSnapshotSummary(SandboxBaseModel): @@ -266,17 +283,104 @@ class SandboxSnapshotSummary(SandboxBaseModel): class SandboxSnapshotListParams(SandboxBaseModel): - status: Optional[SandboxSnapshotStatus] = Field(default=None, exclude=None) + status: Optional[Union[SandboxSnapshotStatus, List[SandboxSnapshotStatus]]] = Field( + default=None, exclude=None + ) + page: Optional[int] = Field(default=None, ge=1) limit: Optional[int] = Field(default=None, ge=1) image_name: Optional[str] = Field(default=None, serialization_alias="imageName") + search: Optional[str] = None class SandboxSnapshotListResponse(SandboxBaseModel): snapshots: List[SandboxSnapshotSummary] - # TODO: add pagination metadata when /api/snapshots supports it. - # total_count: Optional[int] = Field(default=None, alias="totalCount") - # page: Optional[int] = None - # per_page: Optional[int] = Field(default=None, alias="perPage") + total_count: Optional[int] = Field(default=None, alias="totalCount") + page: Optional[int] = None + per_page: Optional[int] = Field(default=None, alias="perPage") + + +class SandboxRuntimeBrowserAuthResponse(SandboxBaseModel): + runtime: SandboxRuntimeTarget + allowed_origin: str = Field(alias="allowedOrigin") + capabilities: List[str] + bootstrap_url: str = Field(alias="bootstrapUrl") + bootstrap_url_expires_at: Optional[datetime] = Field( + default=None, alias="bootstrapUrlExpiresAt" + ) + + +class FirecrackerImageInit(SandboxBaseModel): + commands: Optional[List[str]] = None + + +class CreateFirecrackerImageBuildParams(SandboxBaseModel): + image_name: str = Field(serialization_alias="imageName") + input_sha256: str = Field(serialization_alias="inputSha256") + input_size_bytes: int = Field(serialization_alias="inputSizeBytes") + input_format: str = Field(serialization_alias="inputFormat") + source_platform: str = Field(serialization_alias="sourcePlatform") + image_config_user: Optional[str] = Field( + default=None, serialization_alias="imageConfigUser" + ) + image_init: Optional[FirecrackerImageInit] = Field( + default=None, serialization_alias="imageInit" + ) + + +class CompleteFirecrackerImageBuildParams(SandboxBaseModel): + input_sha256: str = Field(serialization_alias="inputSha256") + input_size_bytes: int = Field(serialization_alias="inputSizeBytes") + input_format: str = Field(serialization_alias="inputFormat") + + +class FirecrackerImageBuild(SandboxBaseModel): + id: str + team_id: str = Field(alias="teamId") + user_id: Optional[str] = Field(default=None, alias="userId") + namespace: str + image_name: str = Field(alias="imageName") + image_id: str = Field(alias="imageId") + status: FirecrackerImageBuildStatus + input_bucket: Optional[str] = Field(default=None, alias="inputBucket") + input_key: Optional[str] = Field(default=None, alias="inputKey") + input_sha256: Optional[str] = Field(default=None, alias="inputSha256") + input_size_bytes: Optional[int] = Field(default=None, alias="inputSizeBytes") + output_bucket: Optional[str] = Field(default=None, alias="outputBucket") + output_key: Optional[str] = Field(default=None, alias="outputKey") + vm_id: Optional[str] = Field(default=None, alias="vmId") + error_code: Optional[str] = Field(default=None, alias="errorCode") + error_message: Optional[str] = Field(default=None, alias="errorMessage") + metadata: Optional[Dict[str, Any]] = None + completed_at: Optional[datetime] = Field(default=None, alias="completedAt") + created_at: datetime = Field(alias="createdAt") + updated_at: datetime = Field(alias="updatedAt") + + +class FirecrackerImageBuildUpload(SandboxBaseModel): + url: str + method: str + headers: Dict[str, str] + object_key: str = Field(alias="objectKey") + expires_in_seconds: int = Field(alias="expiresInSeconds") + max_upload_bytes: int = Field(alias="maxUploadBytes") + + +class CreateFirecrackerImageBuildResponse(SandboxBaseModel): + build: FirecrackerImageBuild + upload: FirecrackerImageBuildUpload + + +class FirecrackerImageBuildResponse(SandboxBaseModel): + build: FirecrackerImageBuild + + +class FirecrackerImageBuildListParams(SandboxBaseModel): + status: Optional[FirecrackerImageBuildStatus] = None + limit: Optional[int] = None + + +class FirecrackerImageBuildListResponse(SandboxBaseModel): + builds: List[FirecrackerImageBuild] class SandboxMemorySnapshotParams(SandboxBaseModel): diff --git a/hyperbrowser/models/session.py b/hyperbrowser/models/session.py index 2a3146c6..c55907a4 100644 --- a/hyperbrowser/models/session.py +++ b/hyperbrowser/models/session.py @@ -523,3 +523,116 @@ class SessionEventLogListResponse(BaseModel): total_count: int = Field(alias="totalCount") page: int = Field(alias="page") per_page: int = Field(alias="perPage") + + +class SessionLogsTokenResponse(BaseModel): + model_config = ConfigDict(populate_by_alias=True) + + token: str + token_expires_at: datetime = Field(alias="tokenExpiresAt") + + +class SessionInfoProfile(BaseModel): + model_config = ConfigDict(populate_by_alias=True) + + id: str + name: Optional[str] = None + + +class SessionInfoResponse(BaseModel): + model_config = ConfigDict(populate_by_alias=True) + + id: str + status: SessionStatus + start_time: Optional[int] = Field(default=None, alias="startTime") + launch_state: Optional[SessionLaunchState] = Field( + default=None, alias="launchState" + ) + duration: int + proxy_bytes_used: int = Field(alias="proxyBytesUsed") + region: str + settings: List[Any] + recording_url: Optional[str] = Field(default=None, alias="recordingUrl") + video_recording_url: Optional[str] = Field(default=None, alias="videoRecordingUrl") + video_recording_playlist_url: Optional[str] = Field( + default=None, alias="videoRecordingPlaylistUrl" + ) + is_recording_encrypted: Optional[bool] = Field( + default=None, alias="isRecordingEncrypted" + ) + live_url: Optional[str] = Field(default=None, alias="liveUrl") + live_domain: Optional[str] = Field(default=None, alias="liveDomain") + profile: Optional[SessionInfoProfile] = None + credits_used: Optional[float] = Field(default=None, alias="creditsUsed") + credit_breakdown: Optional[SessionCreditBreakdown] = Field( + default=None, alias="creditBreakdown" + ) + + +class SessionCreditUsageResponse(BaseModel): + model_config = ConfigDict(populate_by_alias=True) + + credit_usage: Optional[float] = Field(default=None, alias="creditUsage") + + +class SessionMetricsResponse(BaseModel): + model_config = ConfigDict(populate_by_alias=True) + + metrics: List[Dict[str, Any]] + displayed_metrics: List[str] = Field(alias="displayedMetrics") + + +SessionLogLevel = Literal["log", "debug", "info", "error", "warning"] +SessionLogOrder = Literal["asc", "desc"] +SessionNetworkMethod = Literal[ + "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD" +] + + +class SessionConsoleParams(BaseModel): + model_config = ConfigDict(populate_by_alias=True) + + log_levels: Optional[List[SessionLogLevel]] = Field( + default=None, serialization_alias="logLevels" + ) + search: Optional[str] = None + after_timestamp: Optional[int] = Field( + default=None, serialization_alias="afterTimestamp" + ) + before_timestamp: Optional[int] = Field( + default=None, serialization_alias="beforeTimestamp" + ) + order: Optional[SessionLogOrder] = None + limit: Optional[int] = None + + +class SessionNetworkParams(BaseModel): + model_config = ConfigDict(populate_by_alias=True) + + methods: Optional[List[SessionNetworkMethod]] = None + status_codes: Optional[List[str]] = Field( + default=None, serialization_alias="statusCodes" + ) + search: Optional[str] = None + after_timestamp: Optional[int] = Field( + default=None, serialization_alias="afterTimestamp" + ) + before_timestamp: Optional[int] = Field( + default=None, serialization_alias="beforeTimestamp" + ) + order: Optional[SessionLogOrder] = None + limit: Optional[int] = None + + +class SessionConsoleResponse(BaseModel): + model_config = ConfigDict(populate_by_alias=True) + + console_logs: List[Dict[str, Any]] = Field(alias="consoleLogs") + pages: List[Dict[str, Any]] + + +class SessionNetworkResponse(BaseModel): + model_config = ConfigDict(populate_by_alias=True) + + network_calls: List[Dict[str, Any]] = Field(alias="networkCalls") + pages: List[Dict[str, Any]] diff --git a/hyperbrowser/models/volume.py b/hyperbrowser/models/volume.py index 1287642a..c857d86f 100644 --- a/hyperbrowser/models/volume.py +++ b/hyperbrowser/models/volume.py @@ -13,6 +13,12 @@ class CreateVolumeParams(VolumeBaseModel): name: str +class VolumeListParams(VolumeBaseModel): + page: Optional[int] = None + limit: Optional[int] = None + search: Optional[str] = None + + class Volume(VolumeBaseModel): id: str name: str @@ -27,3 +33,6 @@ def parse_optional_int_fields(cls, value): class VolumeListResponse(VolumeBaseModel): volumes: List[Volume] + total_count: Optional[int] = Field(default=None, alias="totalCount") + page: Optional[int] = None + per_page: Optional[int] = Field(default=None, alias="perPage") From fa8ac44ee91969a372ed586222d0101597e979ce Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 14 May 2026 02:24:22 +0000 Subject: [PATCH 2/6] Keep sandbox image helpers on manager Co-authored-by: shri --- .../client/managers/async_manager/sandbox.py | 50 ------------------- .../client/managers/sync_manager/sandbox.py | 50 ------------------- 2 files changed, 100 deletions(-) diff --git a/hyperbrowser/client/managers/async_manager/sandbox.py b/hyperbrowser/client/managers/async_manager/sandbox.py index cb83a249..28d9d9de 100644 --- a/hyperbrowser/client/managers/async_manager/sandbox.py +++ b/hyperbrowser/client/managers/async_manager/sandbox.py @@ -157,56 +157,6 @@ async def connect(self) -> "SandboxHandle": await self.create_runtime_session(force_refresh=True) return self - async def get_runtime_browser_auth( - self, sandbox_id: str, allowed_origin: str - ) -> SandboxRuntimeBrowserAuthResponse: - response = await self._client.transport.client.request( - "POST", - self._client._build_url(f"/sandbox/{sandbox_id}/runtime/browser-auth"), - headers={"Origin": allowed_origin}, - ) - payload = await self._client.transport._handle_response(response) - return SandboxRuntimeBrowserAuthResponse(**payload.data) - - async def create_image_build( - self, params: CreateFirecrackerImageBuildParams - ) -> CreateFirecrackerImageBuildResponse: - payload = await self._request( - "POST", - "/images/builds", - data=params.model_dump(exclude_none=True, by_alias=True), - ) - return CreateFirecrackerImageBuildResponse(**payload) - - async def list_image_builds( - self, - params: FirecrackerImageBuildListParams = FirecrackerImageBuildListParams(), - ) -> FirecrackerImageBuildListResponse: - payload = await self._request( - "GET", - "/images/builds", - params=params.model_dump(exclude_none=True, by_alias=True), - ) - return FirecrackerImageBuildListResponse(**payload) - - async def get_image_build(self, build_id: str) -> FirecrackerImageBuildResponse: - payload = await self._request("GET", f"/images/builds/{build_id}") - return FirecrackerImageBuildResponse(**payload) - - async def complete_image_build( - self, build_id: str, params: CompleteFirecrackerImageBuildParams - ) -> FirecrackerImageBuildResponse: - payload = await self._request( - "POST", - f"/images/builds/{build_id}/complete", - data=params.model_dump(exclude_none=True, by_alias=True), - ) - return FirecrackerImageBuildResponse(**payload) - - async def cancel_image_build(self, build_id: str) -> FirecrackerImageBuildResponse: - payload = await self._request("POST", f"/images/builds/{build_id}/cancel") - return FirecrackerImageBuildResponse(**payload) - async def stop(self) -> BasicResponse: response = await self._service.stop(self.id) self._clear_runtime_session("closed") diff --git a/hyperbrowser/client/managers/sync_manager/sandbox.py b/hyperbrowser/client/managers/sync_manager/sandbox.py index 02b488d8..8965c15d 100644 --- a/hyperbrowser/client/managers/sync_manager/sandbox.py +++ b/hyperbrowser/client/managers/sync_manager/sandbox.py @@ -157,56 +157,6 @@ def connect(self) -> "SandboxHandle": self.create_runtime_session(force_refresh=True) return self - def get_runtime_browser_auth( - self, sandbox_id: str, allowed_origin: str - ) -> SandboxRuntimeBrowserAuthResponse: - response = self._client.transport.client.request( - "POST", - self._client._build_url(f"/sandbox/{sandbox_id}/runtime/browser-auth"), - headers={"Origin": allowed_origin}, - ) - payload = self._client.transport._handle_response(response) - return SandboxRuntimeBrowserAuthResponse(**payload.data) - - def create_image_build( - self, params: CreateFirecrackerImageBuildParams - ) -> CreateFirecrackerImageBuildResponse: - payload = self._request( - "POST", - "/images/builds", - data=params.model_dump(exclude_none=True, by_alias=True), - ) - return CreateFirecrackerImageBuildResponse(**payload) - - def list_image_builds( - self, - params: FirecrackerImageBuildListParams = FirecrackerImageBuildListParams(), - ) -> FirecrackerImageBuildListResponse: - payload = self._request( - "GET", - "/images/builds", - params=params.model_dump(exclude_none=True, by_alias=True), - ) - return FirecrackerImageBuildListResponse(**payload) - - def get_image_build(self, build_id: str) -> FirecrackerImageBuildResponse: - payload = self._request("GET", f"/images/builds/{build_id}") - return FirecrackerImageBuildResponse(**payload) - - def complete_image_build( - self, build_id: str, params: CompleteFirecrackerImageBuildParams - ) -> FirecrackerImageBuildResponse: - payload = self._request( - "POST", - f"/images/builds/{build_id}/complete", - data=params.model_dump(exclude_none=True, by_alias=True), - ) - return FirecrackerImageBuildResponse(**payload) - - def cancel_image_build(self, build_id: str) -> FirecrackerImageBuildResponse: - payload = self._request("POST", f"/images/builds/{build_id}/cancel") - return FirecrackerImageBuildResponse(**payload) - def stop(self) -> BasicResponse: response = self._service.stop(self.id) self._clear_runtime_session("closed") From f8b1c7e1819e99e8b4f3e0ada720ee57e088699a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 14 May 2026 20:26:35 +0000 Subject: [PATCH 3/6] Align task model types Co-authored-by: shri --- hyperbrowser/models/agents/task.py | 3 ++- hyperbrowser/models/consts.py | 12 +----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/hyperbrowser/models/agents/task.py b/hyperbrowser/models/agents/task.py index e89b5bd7..4d4a6a3b 100644 --- a/hyperbrowser/models/agents/task.py +++ b/hyperbrowser/models/agents/task.py @@ -1,3 +1,4 @@ +from datetime import datetime from typing import Any, Dict, List, Optional from pydantic import BaseModel, ConfigDict, Field @@ -21,7 +22,7 @@ class AgentTaskSummary(BaseModel): model_config = ConfigDict(populate_by_alias=True) id: str - created_at: str = Field(alias="createdAt") + created_at: datetime = Field(alias="createdAt") status: TaskStatus task: str diff --git a/hyperbrowser/models/consts.py b/hyperbrowser/models/consts.py index 7fdd7f1a..26d9ce2f 100644 --- a/hyperbrowser/models/consts.py +++ b/hyperbrowser/models/consts.py @@ -29,17 +29,6 @@ BrowserUseVersion = Literal["0.1.40", "0.7.10", "latest"] HyperAgentVersion = Literal["0.8.0", "1.1.0"] -TaskLlm = Literal[ - "gpt-4o", - "gpt-4o-mini", - "claude-sonnet-4-5", - "claude-3-7-sonnet-20250219", - "claude-3-5-sonnet-20241022", - "claude-3-5-haiku-20241022", - "gemini-2.0-flash", - "gemini-2.5-flash", -] - BrowserUseLlm = Literal[ "gpt-4o", "gpt-4o-mini", @@ -56,6 +45,7 @@ "gemini-2.0-flash", "gemini-2.5-flash", ] +TaskLlm = BrowserUseLlm HyperAgentLlm = Literal[ "gpt-5.5", "gpt-5.2", From 9071ccaca231b76abfab071c97864028c408eda8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 14 May 2026 21:27:18 +0000 Subject: [PATCH 4/6] Add generic task list methods Co-authored-by: shri --- .../client/managers/async_manager/agents/task.py | 11 +++++++++++ .../client/managers/sync_manager/agents/task.py | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/hyperbrowser/client/managers/async_manager/agents/task.py b/hyperbrowser/client/managers/async_manager/agents/task.py index 80d4a5b2..ad67fbf2 100644 --- a/hyperbrowser/client/managers/async_manager/agents/task.py +++ b/hyperbrowser/client/managers/async_manager/agents/task.py @@ -4,6 +4,8 @@ from .....models import ( POLLING_ATTEMPTS, BasicResponse, + AgentTaskListParams, + AgentTaskListResponse, StartTaskParams, StartTaskResponse, TaskResponse, @@ -40,6 +42,15 @@ async def stop(self, job_id: str) -> BasicResponse: ) return BasicResponse(**response.data) + async def list( + self, params: AgentTaskListParams = AgentTaskListParams() + ) -> AgentTaskListResponse: + response = await self._client.transport.get( + self._client._build_url("/task"), + params=params.model_dump(exclude_none=True, by_alias=True), + ) + return AgentTaskListResponse(**response.data) + async def start_and_wait(self, params: StartTaskParams) -> TaskResponse: job_start_resp = await self.start(params) job_id = job_start_resp.job_id diff --git a/hyperbrowser/client/managers/sync_manager/agents/task.py b/hyperbrowser/client/managers/sync_manager/agents/task.py index 6a3d7f24..a858bf9f 100644 --- a/hyperbrowser/client/managers/sync_manager/agents/task.py +++ b/hyperbrowser/client/managers/sync_manager/agents/task.py @@ -4,6 +4,8 @@ from .....models import ( POLLING_ATTEMPTS, BasicResponse, + AgentTaskListParams, + AgentTaskListResponse, StartTaskParams, StartTaskResponse, TaskResponse, @@ -40,6 +42,15 @@ def stop(self, job_id: str) -> BasicResponse: ) return BasicResponse(**response.data) + def list( + self, params: AgentTaskListParams = AgentTaskListParams() + ) -> AgentTaskListResponse: + response = self._client.transport.get( + self._client._build_url("/task"), + params=params.model_dump(exclude_none=True, by_alias=True), + ) + return AgentTaskListResponse(**response.data) + def start_and_wait(self, params: StartTaskParams) -> TaskResponse: job_start_resp = self.start(params) job_id = job_start_resp.job_id From 83b11f922920ca1cffc7142caa72398244480d94 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 14 May 2026 22:00:13 +0000 Subject: [PATCH 5/6] fix(session): accept int status codes in SessionNetworkParams status_codes was typed as List[str], forcing callers to use string literals for numeric HTTP codes. Use List[Union[str, int]] so values like [200, 404] are valid while string patterns remain supported. Co-authored-by: Shri Sukhani --- hyperbrowser/models/session.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hyperbrowser/models/session.py b/hyperbrowser/models/session.py index c55907a4..6f3ba428 100644 --- a/hyperbrowser/models/session.py +++ b/hyperbrowser/models/session.py @@ -610,7 +610,7 @@ class SessionNetworkParams(BaseModel): model_config = ConfigDict(populate_by_alias=True) methods: Optional[List[SessionNetworkMethod]] = None - status_codes: Optional[List[str]] = Field( + status_codes: Optional[List[Union[str, int]]] = Field( default=None, serialization_alias="statusCodes" ) search: Optional[str] = None From 222e3eb0e6405dea3aab4d5cef4185d16a9624b3 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 14 May 2026 22:39:04 +0000 Subject: [PATCH 6/6] Relax task list pagination fields Co-authored-by: shri --- hyperbrowser/models/agents/task.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hyperbrowser/models/agents/task.py b/hyperbrowser/models/agents/task.py index 4d4a6a3b..b99bad84 100644 --- a/hyperbrowser/models/agents/task.py +++ b/hyperbrowser/models/agents/task.py @@ -31,9 +31,9 @@ class AgentTaskListResponse(BaseModel): model_config = ConfigDict(populate_by_alias=True) tasks: List[AgentTaskSummary] - total_count: int = Field(alias="totalCount") - page: int - per_page: int = Field(alias="perPage") + total_count: Optional[int] = Field(default=None, alias="totalCount") + page: Optional[int] = None + per_page: Optional[int] = Field(default=None, alias="perPage") class StartTaskParams(BaseModel):