diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 0c2ecec6..86b0e83d 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.20.0"
+ ".": "0.21.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 11b820d4..25e77f1e 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 66
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-2af1b468584cb44aa9babbbfb82bff4055614fbb5c815084a6b7dacc1cf1a822.yml
-openapi_spec_hash: 891affa2849341ea01d62011125f7edc
-config_hash: 9421eb86b7f3f4b274f123279da3858e
+configured_endpoints: 74
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-3db06d1628149b5ea8303f1c72250664dfd7cb4a14ceb6102f1ae6e85c92c038.yml
+openapi_spec_hash: e5b3da2da328eb26d2a70e2521744c62
+config_hash: a4124701ae0a474e580d7416adbcfb00
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c0c65e07..a726401d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,25 @@
# Changelog
+## 0.21.0 (2025-12-02)
+
+Full Changelog: [v0.20.0...v0.21.0](https://github.com/onkernel/kernel-python-sdk/compare/v0.20.0...v0.21.0)
+
+### Features
+
+* Browser pools sdk release ([c2d7408](https://github.com/onkernel/kernel-python-sdk/commit/c2d7408a770ddd34902986015da0bfde3477f586))
+* Mason/agent auth api ([b872928](https://github.com/onkernel/kernel-python-sdk/commit/b8729284ba804461749dad0ac4613aea3c6b3ce6))
+
+
+### Bug Fixes
+
+* ensure streams are always closed ([c82f496](https://github.com/onkernel/kernel-python-sdk/commit/c82f496ba0112b195f65da9498fb22ab65d501fb))
+
+
+### Chores
+
+* add Python 3.14 classifier and testing ([9a1e7bf](https://github.com/onkernel/kernel-python-sdk/commit/9a1e7bf9c92d64da4a151dd44cc60057c75c63bf))
+* **deps:** mypy 1.18.1 has a regression, pin to 1.17 ([038b10d](https://github.com/onkernel/kernel-python-sdk/commit/038b10d52935961c37ebb0806e9b0754b4d53a20))
+
## 0.20.0 (2025-11-19)
Full Changelog: [v0.19.2...v0.20.0](https://github.com/onkernel/kernel-python-sdk/compare/v0.19.2...v0.20.0)
diff --git a/api.md b/api.md
index afa0ddd4..fe6b45c0 100644
--- a/api.md
+++ b/api.md
@@ -1,7 +1,17 @@
# Shared Types
```python
-from kernel.types import AppAction, ErrorDetail, ErrorEvent, ErrorModel, HeartbeatEvent, LogEvent
+from kernel.types import (
+ AppAction,
+ BrowserExtension,
+ BrowserProfile,
+ BrowserViewport,
+ ErrorDetail,
+ ErrorEvent,
+ ErrorModel,
+ HeartbeatEvent,
+ LogEvent,
+)
```
# Deployments
@@ -243,3 +253,30 @@ Methods:
- client.extensions.download(id_or_name) -> BinaryAPIResponse
- client.extensions.download_from_chrome_store(\*\*params) -> BinaryAPIResponse
- client.extensions.upload(\*\*params) -> ExtensionUploadResponse
+
+# BrowserPools
+
+Types:
+
+```python
+from kernel.types import (
+ BrowserPool,
+ BrowserPoolAcquireRequest,
+ BrowserPoolReleaseRequest,
+ BrowserPoolRequest,
+ BrowserPoolUpdateRequest,
+ BrowserPoolListResponse,
+ BrowserPoolAcquireResponse,
+)
+```
+
+Methods:
+
+- client.browser_pools.create(\*\*params) -> BrowserPool
+- client.browser_pools.retrieve(id_or_name) -> BrowserPool
+- client.browser_pools.update(id_or_name, \*\*params) -> BrowserPool
+- client.browser_pools.list() -> BrowserPoolListResponse
+- client.browser_pools.delete(id_or_name, \*\*params) -> None
+- client.browser_pools.acquire(id_or_name, \*\*params) -> BrowserPoolAcquireResponse
+- client.browser_pools.flush(id_or_name) -> None
+- client.browser_pools.release(id_or_name, \*\*params) -> None
diff --git a/pyproject.toml b/pyproject.toml
index b5d3ecb5..f4bbe655 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "kernel"
-version = "0.20.0"
+version = "0.21.0"
description = "The official Python library for the kernel API"
dynamic = ["readme"]
license = "Apache-2.0"
@@ -24,6 +24,7 @@ classifiers = [
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
"Operating System :: OS Independent",
"Operating System :: POSIX",
"Operating System :: MacOS",
@@ -45,7 +46,7 @@ managed = true
# version pins are in requirements-dev.lock
dev-dependencies = [
"pyright==1.1.399",
- "mypy",
+ "mypy==1.17",
"respx",
"pytest",
"pytest-asyncio",
diff --git a/requirements-dev.lock b/requirements-dev.lock
index fc032738..b435fc70 100644
--- a/requirements-dev.lock
+++ b/requirements-dev.lock
@@ -72,7 +72,7 @@ mdurl==0.1.2
multidict==6.4.4
# via aiohttp
# via yarl
-mypy==1.14.1
+mypy==1.17.0
mypy-extensions==1.0.0
# via mypy
nodeenv==1.8.0
@@ -81,6 +81,8 @@ nox==2023.4.22
packaging==23.2
# via nox
# via pytest
+pathspec==0.12.1
+ # via mypy
platformdirs==3.11.0
# via virtualenv
pluggy==1.5.0
diff --git a/requirements.lock b/requirements.lock
index ed64e3db..646e8cda 100644
--- a/requirements.lock
+++ b/requirements.lock
@@ -55,21 +55,21 @@ multidict==6.4.4
propcache==0.3.1
# via aiohttp
# via yarl
-pydantic==2.11.9
+pydantic==2.12.5
# via kernel
-pydantic-core==2.33.2
+pydantic-core==2.41.5
# via pydantic
sniffio==1.3.0
# via anyio
# via kernel
-typing-extensions==4.12.2
+typing-extensions==4.15.0
# via anyio
# via kernel
# via multidict
# via pydantic
# via pydantic-core
# via typing-inspection
-typing-inspection==0.4.1
+typing-inspection==0.4.2
# via pydantic
yarl==1.20.0
# via aiohttp
diff --git a/src/kernel/_client.py b/src/kernel/_client.py
index ea9e51b9..37ba4890 100644
--- a/src/kernel/_client.py
+++ b/src/kernel/_client.py
@@ -21,7 +21,7 @@
)
from ._utils import is_given, get_async_library
from ._version import __version__
-from .resources import apps, proxies, profiles, extensions, deployments, invocations
+from .resources import apps, proxies, profiles, extensions, deployments, invocations, browser_pools
from ._streaming import Stream as Stream, AsyncStream as AsyncStream
from ._exceptions import KernelError, APIStatusError
from ._base_client import (
@@ -57,6 +57,7 @@ class Kernel(SyncAPIClient):
profiles: profiles.ProfilesResource
proxies: proxies.ProxiesResource
extensions: extensions.ExtensionsResource
+ browser_pools: browser_pools.BrowserPoolsResource
with_raw_response: KernelWithRawResponse
with_streaming_response: KernelWithStreamedResponse
@@ -145,6 +146,7 @@ def __init__(
self.profiles = profiles.ProfilesResource(self)
self.proxies = proxies.ProxiesResource(self)
self.extensions = extensions.ExtensionsResource(self)
+ self.browser_pools = browser_pools.BrowserPoolsResource(self)
self.with_raw_response = KernelWithRawResponse(self)
self.with_streaming_response = KernelWithStreamedResponse(self)
@@ -263,6 +265,7 @@ class AsyncKernel(AsyncAPIClient):
profiles: profiles.AsyncProfilesResource
proxies: proxies.AsyncProxiesResource
extensions: extensions.AsyncExtensionsResource
+ browser_pools: browser_pools.AsyncBrowserPoolsResource
with_raw_response: AsyncKernelWithRawResponse
with_streaming_response: AsyncKernelWithStreamedResponse
@@ -351,6 +354,7 @@ def __init__(
self.profiles = profiles.AsyncProfilesResource(self)
self.proxies = proxies.AsyncProxiesResource(self)
self.extensions = extensions.AsyncExtensionsResource(self)
+ self.browser_pools = browser_pools.AsyncBrowserPoolsResource(self)
self.with_raw_response = AsyncKernelWithRawResponse(self)
self.with_streaming_response = AsyncKernelWithStreamedResponse(self)
@@ -470,6 +474,7 @@ def __init__(self, client: Kernel) -> None:
self.profiles = profiles.ProfilesResourceWithRawResponse(client.profiles)
self.proxies = proxies.ProxiesResourceWithRawResponse(client.proxies)
self.extensions = extensions.ExtensionsResourceWithRawResponse(client.extensions)
+ self.browser_pools = browser_pools.BrowserPoolsResourceWithRawResponse(client.browser_pools)
class AsyncKernelWithRawResponse:
@@ -481,6 +486,7 @@ def __init__(self, client: AsyncKernel) -> None:
self.profiles = profiles.AsyncProfilesResourceWithRawResponse(client.profiles)
self.proxies = proxies.AsyncProxiesResourceWithRawResponse(client.proxies)
self.extensions = extensions.AsyncExtensionsResourceWithRawResponse(client.extensions)
+ self.browser_pools = browser_pools.AsyncBrowserPoolsResourceWithRawResponse(client.browser_pools)
class KernelWithStreamedResponse:
@@ -492,6 +498,7 @@ def __init__(self, client: Kernel) -> None:
self.profiles = profiles.ProfilesResourceWithStreamingResponse(client.profiles)
self.proxies = proxies.ProxiesResourceWithStreamingResponse(client.proxies)
self.extensions = extensions.ExtensionsResourceWithStreamingResponse(client.extensions)
+ self.browser_pools = browser_pools.BrowserPoolsResourceWithStreamingResponse(client.browser_pools)
class AsyncKernelWithStreamedResponse:
@@ -503,6 +510,7 @@ def __init__(self, client: AsyncKernel) -> None:
self.profiles = profiles.AsyncProfilesResourceWithStreamingResponse(client.profiles)
self.proxies = proxies.AsyncProxiesResourceWithStreamingResponse(client.proxies)
self.extensions = extensions.AsyncExtensionsResourceWithStreamingResponse(client.extensions)
+ self.browser_pools = browser_pools.AsyncBrowserPoolsResourceWithStreamingResponse(client.browser_pools)
Client = Kernel
diff --git a/src/kernel/_streaming.py b/src/kernel/_streaming.py
index e6d03062..369a3f6d 100644
--- a/src/kernel/_streaming.py
+++ b/src/kernel/_streaming.py
@@ -54,11 +54,12 @@ def __stream__(self) -> Iterator[_T]:
process_data = self._client._process_response_data
iterator = self._iter_events()
- for sse in iterator:
- yield process_data(data=sse.json(), cast_to=cast_to, response=response)
-
- # As we might not fully consume the response stream, we need to close it explicitly
- response.close()
+ try:
+ for sse in iterator:
+ yield process_data(data=sse.json(), cast_to=cast_to, response=response)
+ finally:
+ # Ensure the response is closed even if the consumer doesn't read all data
+ response.close()
def __enter__(self) -> Self:
return self
@@ -117,11 +118,12 @@ async def __stream__(self) -> AsyncIterator[_T]:
process_data = self._client._process_response_data
iterator = self._iter_events()
- async for sse in iterator:
- yield process_data(data=sse.json(), cast_to=cast_to, response=response)
-
- # As we might not fully consume the response stream, we need to close it explicitly
- await response.aclose()
+ try:
+ async for sse in iterator:
+ yield process_data(data=sse.json(), cast_to=cast_to, response=response)
+ finally:
+ # Ensure the response is closed even if the consumer doesn't read all data
+ await response.aclose()
async def __aenter__(self) -> Self:
return self
diff --git a/src/kernel/_version.py b/src/kernel/_version.py
index 1a28cba2..1bd01d63 100644
--- a/src/kernel/_version.py
+++ b/src/kernel/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "kernel"
-__version__ = "0.20.0" # x-release-please-version
+__version__ = "0.21.0" # x-release-please-version
diff --git a/src/kernel/resources/__init__.py b/src/kernel/resources/__init__.py
index 1b68d89f..cf08046e 100644
--- a/src/kernel/resources/__init__.py
+++ b/src/kernel/resources/__init__.py
@@ -56,6 +56,14 @@
InvocationsResourceWithStreamingResponse,
AsyncInvocationsResourceWithStreamingResponse,
)
+from .browser_pools import (
+ BrowserPoolsResource,
+ AsyncBrowserPoolsResource,
+ BrowserPoolsResourceWithRawResponse,
+ AsyncBrowserPoolsResourceWithRawResponse,
+ BrowserPoolsResourceWithStreamingResponse,
+ AsyncBrowserPoolsResourceWithStreamingResponse,
+)
__all__ = [
"DeploymentsResource",
@@ -100,4 +108,10 @@
"AsyncExtensionsResourceWithRawResponse",
"ExtensionsResourceWithStreamingResponse",
"AsyncExtensionsResourceWithStreamingResponse",
+ "BrowserPoolsResource",
+ "AsyncBrowserPoolsResource",
+ "BrowserPoolsResourceWithRawResponse",
+ "AsyncBrowserPoolsResourceWithRawResponse",
+ "BrowserPoolsResourceWithStreamingResponse",
+ "AsyncBrowserPoolsResourceWithStreamingResponse",
]
diff --git a/src/kernel/resources/browser_pools.py b/src/kernel/resources/browser_pools.py
new file mode 100644
index 00000000..d085d515
--- /dev/null
+++ b/src/kernel/resources/browser_pools.py
@@ -0,0 +1,1022 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable
+
+import httpx
+
+from ..types import (
+ browser_pool_create_params,
+ browser_pool_delete_params,
+ browser_pool_update_params,
+ browser_pool_acquire_params,
+ browser_pool_release_params,
+)
+from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
+from .._utils import maybe_transform, async_maybe_transform
+from .._compat import cached_property
+from .._resource import SyncAPIResource, AsyncAPIResource
+from .._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from .._base_client import make_request_options
+from ..types.browser_pool import BrowserPool
+from ..types.browser_pool_list_response import BrowserPoolListResponse
+from ..types.browser_pool_acquire_response import BrowserPoolAcquireResponse
+from ..types.shared_params.browser_profile import BrowserProfile
+from ..types.shared_params.browser_viewport import BrowserViewport
+from ..types.shared_params.browser_extension import BrowserExtension
+
+__all__ = ["BrowserPoolsResource", "AsyncBrowserPoolsResource"]
+
+
+class BrowserPoolsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> BrowserPoolsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/onkernel/kernel-python-sdk#accessing-raw-response-data-eg-headers
+ """
+ return BrowserPoolsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> BrowserPoolsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/onkernel/kernel-python-sdk#with_streaming_response
+ """
+ return BrowserPoolsResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ size: int,
+ extensions: Iterable[BrowserExtension] | Omit = omit,
+ fill_rate_per_minute: int | Omit = omit,
+ headless: bool | Omit = omit,
+ kiosk_mode: bool | Omit = omit,
+ name: str | Omit = omit,
+ profile: BrowserProfile | Omit = omit,
+ proxy_id: str | Omit = omit,
+ stealth: bool | Omit = omit,
+ timeout_seconds: int | Omit = omit,
+ viewport: BrowserViewport | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> BrowserPool:
+ """
+ Create a new browser pool with the specified configuration and size.
+
+ Args:
+ size: Number of browsers to create in the pool
+
+ extensions: List of browser extensions to load into the session. Provide each by id or name.
+
+ fill_rate_per_minute: Percentage of the pool to fill per minute. Defaults to 10%.
+
+ headless: If true, launches the browser using a headless image. Defaults to false.
+
+ kiosk_mode: If true, launches the browser in kiosk mode to hide address bar and tabs in live
+ view.
+
+ name: Optional name for the browser pool. Must be unique within the organization.
+
+ profile: Profile selection for the browser session. Provide either id or name. If
+ specified, the matching profile will be loaded into the browser session.
+ Profiles must be created beforehand.
+
+ proxy_id: Optional proxy to associate to the browser session. Must reference a proxy
+ belonging to the caller's org.
+
+ stealth: If true, launches the browser in stealth mode to reduce detection by anti-bot
+ mechanisms.
+
+ timeout_seconds: Default idle timeout in seconds for browsers acquired from this pool before they
+ are destroyed. Defaults to 600 seconds if not specified
+
+ viewport: Initial browser window size in pixels with optional refresh rate. If omitted,
+ image defaults apply (commonly 1024x768@60). Only specific viewport
+ configurations are supported. The server will reject unsupported combinations.
+ Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
+ 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
+ be automatically determined from the width and height if they match a supported
+ configuration exactly. Note: Higher resolutions may affect the responsiveness of
+ live view browser
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/browser_pools",
+ body=maybe_transform(
+ {
+ "size": size,
+ "extensions": extensions,
+ "fill_rate_per_minute": fill_rate_per_minute,
+ "headless": headless,
+ "kiosk_mode": kiosk_mode,
+ "name": name,
+ "profile": profile,
+ "proxy_id": proxy_id,
+ "stealth": stealth,
+ "timeout_seconds": timeout_seconds,
+ "viewport": viewport,
+ },
+ browser_pool_create_params.BrowserPoolCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=BrowserPool,
+ )
+
+ def retrieve(
+ self,
+ id_or_name: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> BrowserPool:
+ """
+ Retrieve details for a single browser pool by its ID or name.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
+ return self._get(
+ f"/browser_pools/{id_or_name}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=BrowserPool,
+ )
+
+ def update(
+ self,
+ id_or_name: str,
+ *,
+ size: int,
+ discard_all_idle: bool | Omit = omit,
+ extensions: Iterable[BrowserExtension] | Omit = omit,
+ fill_rate_per_minute: int | Omit = omit,
+ headless: bool | Omit = omit,
+ kiosk_mode: bool | Omit = omit,
+ name: str | Omit = omit,
+ profile: BrowserProfile | Omit = omit,
+ proxy_id: str | Omit = omit,
+ stealth: bool | Omit = omit,
+ timeout_seconds: int | Omit = omit,
+ viewport: BrowserViewport | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> BrowserPool:
+ """
+ Updates the configuration used to create browsers in the pool.
+
+ Args:
+ size: Number of browsers to create in the pool
+
+ discard_all_idle: Whether to discard all idle browsers and rebuild the pool immediately. Defaults
+ to true.
+
+ extensions: List of browser extensions to load into the session. Provide each by id or name.
+
+ fill_rate_per_minute: Percentage of the pool to fill per minute. Defaults to 10%.
+
+ headless: If true, launches the browser using a headless image. Defaults to false.
+
+ kiosk_mode: If true, launches the browser in kiosk mode to hide address bar and tabs in live
+ view.
+
+ name: Optional name for the browser pool. Must be unique within the organization.
+
+ profile: Profile selection for the browser session. Provide either id or name. If
+ specified, the matching profile will be loaded into the browser session.
+ Profiles must be created beforehand.
+
+ proxy_id: Optional proxy to associate to the browser session. Must reference a proxy
+ belonging to the caller's org.
+
+ stealth: If true, launches the browser in stealth mode to reduce detection by anti-bot
+ mechanisms.
+
+ timeout_seconds: Default idle timeout in seconds for browsers acquired from this pool before they
+ are destroyed. Defaults to 600 seconds if not specified
+
+ viewport: Initial browser window size in pixels with optional refresh rate. If omitted,
+ image defaults apply (commonly 1024x768@60). Only specific viewport
+ configurations are supported. The server will reject unsupported combinations.
+ Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
+ 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
+ be automatically determined from the width and height if they match a supported
+ configuration exactly. Note: Higher resolutions may affect the responsiveness of
+ live view browser
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
+ return self._patch(
+ f"/browser_pools/{id_or_name}",
+ body=maybe_transform(
+ {
+ "size": size,
+ "discard_all_idle": discard_all_idle,
+ "extensions": extensions,
+ "fill_rate_per_minute": fill_rate_per_minute,
+ "headless": headless,
+ "kiosk_mode": kiosk_mode,
+ "name": name,
+ "profile": profile,
+ "proxy_id": proxy_id,
+ "stealth": stealth,
+ "timeout_seconds": timeout_seconds,
+ "viewport": viewport,
+ },
+ browser_pool_update_params.BrowserPoolUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=BrowserPool,
+ )
+
+ def list(
+ self,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> BrowserPoolListResponse:
+ """List browser pools owned by the caller's organization."""
+ return self._get(
+ "/browser_pools",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=BrowserPoolListResponse,
+ )
+
+ def delete(
+ self,
+ id_or_name: str,
+ *,
+ force: bool | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """Delete a browser pool and all browsers in it.
+
+ By default, deletion is blocked if
+ browsers are currently leased. Use force=true to terminate leased browsers.
+
+ Args:
+ force: If true, force delete even if browsers are currently leased. Leased browsers
+ will be terminated.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._delete(
+ f"/browser_pools/{id_or_name}",
+ body=maybe_transform({"force": force}, browser_pool_delete_params.BrowserPoolDeleteParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def acquire(
+ self,
+ id_or_name: str,
+ *,
+ acquire_timeout_seconds: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> BrowserPoolAcquireResponse:
+ """Long-polling endpoint to acquire a browser from the pool.
+
+ Returns immediately
+ when a browser is available, or returns 204 No Content when the poll times out.
+ The client should retry the request to continue waiting for a browser. The
+ acquired browser will use the pool's timeout_seconds for its idle timeout.
+
+ Args:
+ acquire_timeout_seconds: Maximum number of seconds to wait for a browser to be available. Defaults to the
+ calculated time it would take to fill the pool at the currently configured fill
+ rate.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
+ return self._post(
+ f"/browser_pools/{id_or_name}/acquire",
+ body=maybe_transform(
+ {"acquire_timeout_seconds": acquire_timeout_seconds},
+ browser_pool_acquire_params.BrowserPoolAcquireParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=BrowserPoolAcquireResponse,
+ )
+
+ def flush(
+ self,
+ id_or_name: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Destroys all idle browsers in the pool; leased browsers are not affected.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._post(
+ f"/browser_pools/{id_or_name}/flush",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def release(
+ self,
+ id_or_name: str,
+ *,
+ session_id: str,
+ reuse: bool | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Release a browser back to the pool, optionally recreating the browser instance.
+
+ Args:
+ session_id: Browser session ID to release back to the pool
+
+ reuse: Whether to reuse the browser instance or destroy it and create a new one.
+ Defaults to true.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._post(
+ f"/browser_pools/{id_or_name}/release",
+ body=maybe_transform(
+ {
+ "session_id": session_id,
+ "reuse": reuse,
+ },
+ browser_pool_release_params.BrowserPoolReleaseParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class AsyncBrowserPoolsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncBrowserPoolsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/onkernel/kernel-python-sdk#accessing-raw-response-data-eg-headers
+ """
+ return AsyncBrowserPoolsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncBrowserPoolsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/onkernel/kernel-python-sdk#with_streaming_response
+ """
+ return AsyncBrowserPoolsResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ size: int,
+ extensions: Iterable[BrowserExtension] | Omit = omit,
+ fill_rate_per_minute: int | Omit = omit,
+ headless: bool | Omit = omit,
+ kiosk_mode: bool | Omit = omit,
+ name: str | Omit = omit,
+ profile: BrowserProfile | Omit = omit,
+ proxy_id: str | Omit = omit,
+ stealth: bool | Omit = omit,
+ timeout_seconds: int | Omit = omit,
+ viewport: BrowserViewport | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> BrowserPool:
+ """
+ Create a new browser pool with the specified configuration and size.
+
+ Args:
+ size: Number of browsers to create in the pool
+
+ extensions: List of browser extensions to load into the session. Provide each by id or name.
+
+ fill_rate_per_minute: Percentage of the pool to fill per minute. Defaults to 10%.
+
+ headless: If true, launches the browser using a headless image. Defaults to false.
+
+ kiosk_mode: If true, launches the browser in kiosk mode to hide address bar and tabs in live
+ view.
+
+ name: Optional name for the browser pool. Must be unique within the organization.
+
+ profile: Profile selection for the browser session. Provide either id or name. If
+ specified, the matching profile will be loaded into the browser session.
+ Profiles must be created beforehand.
+
+ proxy_id: Optional proxy to associate to the browser session. Must reference a proxy
+ belonging to the caller's org.
+
+ stealth: If true, launches the browser in stealth mode to reduce detection by anti-bot
+ mechanisms.
+
+ timeout_seconds: Default idle timeout in seconds for browsers acquired from this pool before they
+ are destroyed. Defaults to 600 seconds if not specified
+
+ viewport: Initial browser window size in pixels with optional refresh rate. If omitted,
+ image defaults apply (commonly 1024x768@60). Only specific viewport
+ configurations are supported. The server will reject unsupported combinations.
+ Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
+ 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
+ be automatically determined from the width and height if they match a supported
+ configuration exactly. Note: Higher resolutions may affect the responsiveness of
+ live view browser
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/browser_pools",
+ body=await async_maybe_transform(
+ {
+ "size": size,
+ "extensions": extensions,
+ "fill_rate_per_minute": fill_rate_per_minute,
+ "headless": headless,
+ "kiosk_mode": kiosk_mode,
+ "name": name,
+ "profile": profile,
+ "proxy_id": proxy_id,
+ "stealth": stealth,
+ "timeout_seconds": timeout_seconds,
+ "viewport": viewport,
+ },
+ browser_pool_create_params.BrowserPoolCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=BrowserPool,
+ )
+
+ async def retrieve(
+ self,
+ id_or_name: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> BrowserPool:
+ """
+ Retrieve details for a single browser pool by its ID or name.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
+ return await self._get(
+ f"/browser_pools/{id_or_name}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=BrowserPool,
+ )
+
+ async def update(
+ self,
+ id_or_name: str,
+ *,
+ size: int,
+ discard_all_idle: bool | Omit = omit,
+ extensions: Iterable[BrowserExtension] | Omit = omit,
+ fill_rate_per_minute: int | Omit = omit,
+ headless: bool | Omit = omit,
+ kiosk_mode: bool | Omit = omit,
+ name: str | Omit = omit,
+ profile: BrowserProfile | Omit = omit,
+ proxy_id: str | Omit = omit,
+ stealth: bool | Omit = omit,
+ timeout_seconds: int | Omit = omit,
+ viewport: BrowserViewport | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> BrowserPool:
+ """
+ Updates the configuration used to create browsers in the pool.
+
+ Args:
+ size: Number of browsers to create in the pool
+
+ discard_all_idle: Whether to discard all idle browsers and rebuild the pool immediately. Defaults
+ to true.
+
+ extensions: List of browser extensions to load into the session. Provide each by id or name.
+
+ fill_rate_per_minute: Percentage of the pool to fill per minute. Defaults to 10%.
+
+ headless: If true, launches the browser using a headless image. Defaults to false.
+
+ kiosk_mode: If true, launches the browser in kiosk mode to hide address bar and tabs in live
+ view.
+
+ name: Optional name for the browser pool. Must be unique within the organization.
+
+ profile: Profile selection for the browser session. Provide either id or name. If
+ specified, the matching profile will be loaded into the browser session.
+ Profiles must be created beforehand.
+
+ proxy_id: Optional proxy to associate to the browser session. Must reference a proxy
+ belonging to the caller's org.
+
+ stealth: If true, launches the browser in stealth mode to reduce detection by anti-bot
+ mechanisms.
+
+ timeout_seconds: Default idle timeout in seconds for browsers acquired from this pool before they
+ are destroyed. Defaults to 600 seconds if not specified
+
+ viewport: Initial browser window size in pixels with optional refresh rate. If omitted,
+ image defaults apply (commonly 1024x768@60). Only specific viewport
+ configurations are supported. The server will reject unsupported combinations.
+ Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
+ 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
+ be automatically determined from the width and height if they match a supported
+ configuration exactly. Note: Higher resolutions may affect the responsiveness of
+ live view browser
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
+ return await self._patch(
+ f"/browser_pools/{id_or_name}",
+ body=await async_maybe_transform(
+ {
+ "size": size,
+ "discard_all_idle": discard_all_idle,
+ "extensions": extensions,
+ "fill_rate_per_minute": fill_rate_per_minute,
+ "headless": headless,
+ "kiosk_mode": kiosk_mode,
+ "name": name,
+ "profile": profile,
+ "proxy_id": proxy_id,
+ "stealth": stealth,
+ "timeout_seconds": timeout_seconds,
+ "viewport": viewport,
+ },
+ browser_pool_update_params.BrowserPoolUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=BrowserPool,
+ )
+
+ async def list(
+ self,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> BrowserPoolListResponse:
+ """List browser pools owned by the caller's organization."""
+ return await self._get(
+ "/browser_pools",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=BrowserPoolListResponse,
+ )
+
+ async def delete(
+ self,
+ id_or_name: str,
+ *,
+ force: bool | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """Delete a browser pool and all browsers in it.
+
+ By default, deletion is blocked if
+ browsers are currently leased. Use force=true to terminate leased browsers.
+
+ Args:
+ force: If true, force delete even if browsers are currently leased. Leased browsers
+ will be terminated.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._delete(
+ f"/browser_pools/{id_or_name}",
+ body=await async_maybe_transform({"force": force}, browser_pool_delete_params.BrowserPoolDeleteParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ async def acquire(
+ self,
+ id_or_name: str,
+ *,
+ acquire_timeout_seconds: int | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> BrowserPoolAcquireResponse:
+ """Long-polling endpoint to acquire a browser from the pool.
+
+ Returns immediately
+ when a browser is available, or returns 204 No Content when the poll times out.
+ The client should retry the request to continue waiting for a browser. The
+ acquired browser will use the pool's timeout_seconds for its idle timeout.
+
+ Args:
+ acquire_timeout_seconds: Maximum number of seconds to wait for a browser to be available. Defaults to the
+ calculated time it would take to fill the pool at the currently configured fill
+ rate.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
+ return await self._post(
+ f"/browser_pools/{id_or_name}/acquire",
+ body=await async_maybe_transform(
+ {"acquire_timeout_seconds": acquire_timeout_seconds},
+ browser_pool_acquire_params.BrowserPoolAcquireParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=BrowserPoolAcquireResponse,
+ )
+
+ async def flush(
+ self,
+ id_or_name: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Destroys all idle browsers in the pool; leased browsers are not affected.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._post(
+ f"/browser_pools/{id_or_name}/flush",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ async def release(
+ self,
+ id_or_name: str,
+ *,
+ session_id: str,
+ reuse: bool | Omit = omit,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> None:
+ """
+ Release a browser back to the pool, optionally recreating the browser instance.
+
+ Args:
+ session_id: Browser session ID to release back to the pool
+
+ reuse: Whether to reuse the browser instance or destroy it and create a new one.
+ Defaults to true.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._post(
+ f"/browser_pools/{id_or_name}/release",
+ body=await async_maybe_transform(
+ {
+ "session_id": session_id,
+ "reuse": reuse,
+ },
+ browser_pool_release_params.BrowserPoolReleaseParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class BrowserPoolsResourceWithRawResponse:
+ def __init__(self, browser_pools: BrowserPoolsResource) -> None:
+ self._browser_pools = browser_pools
+
+ self.create = to_raw_response_wrapper(
+ browser_pools.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ browser_pools.retrieve,
+ )
+ self.update = to_raw_response_wrapper(
+ browser_pools.update,
+ )
+ self.list = to_raw_response_wrapper(
+ browser_pools.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ browser_pools.delete,
+ )
+ self.acquire = to_raw_response_wrapper(
+ browser_pools.acquire,
+ )
+ self.flush = to_raw_response_wrapper(
+ browser_pools.flush,
+ )
+ self.release = to_raw_response_wrapper(
+ browser_pools.release,
+ )
+
+
+class AsyncBrowserPoolsResourceWithRawResponse:
+ def __init__(self, browser_pools: AsyncBrowserPoolsResource) -> None:
+ self._browser_pools = browser_pools
+
+ self.create = async_to_raw_response_wrapper(
+ browser_pools.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ browser_pools.retrieve,
+ )
+ self.update = async_to_raw_response_wrapper(
+ browser_pools.update,
+ )
+ self.list = async_to_raw_response_wrapper(
+ browser_pools.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ browser_pools.delete,
+ )
+ self.acquire = async_to_raw_response_wrapper(
+ browser_pools.acquire,
+ )
+ self.flush = async_to_raw_response_wrapper(
+ browser_pools.flush,
+ )
+ self.release = async_to_raw_response_wrapper(
+ browser_pools.release,
+ )
+
+
+class BrowserPoolsResourceWithStreamingResponse:
+ def __init__(self, browser_pools: BrowserPoolsResource) -> None:
+ self._browser_pools = browser_pools
+
+ self.create = to_streamed_response_wrapper(
+ browser_pools.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ browser_pools.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ browser_pools.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ browser_pools.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ browser_pools.delete,
+ )
+ self.acquire = to_streamed_response_wrapper(
+ browser_pools.acquire,
+ )
+ self.flush = to_streamed_response_wrapper(
+ browser_pools.flush,
+ )
+ self.release = to_streamed_response_wrapper(
+ browser_pools.release,
+ )
+
+
+class AsyncBrowserPoolsResourceWithStreamingResponse:
+ def __init__(self, browser_pools: AsyncBrowserPoolsResource) -> None:
+ self._browser_pools = browser_pools
+
+ self.create = async_to_streamed_response_wrapper(
+ browser_pools.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ browser_pools.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ browser_pools.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ browser_pools.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ browser_pools.delete,
+ )
+ self.acquire = async_to_streamed_response_wrapper(
+ browser_pools.acquire,
+ )
+ self.flush = async_to_streamed_response_wrapper(
+ browser_pools.flush,
+ )
+ self.release = async_to_streamed_response_wrapper(
+ browser_pools.release,
+ )
diff --git a/src/kernel/resources/browsers/browsers.py b/src/kernel/resources/browsers/browsers.py
index 84a4fe48..f41b46ad 100644
--- a/src/kernel/resources/browsers/browsers.py
+++ b/src/kernel/resources/browsers/browsers.py
@@ -76,6 +76,9 @@
from ...types.browser_create_response import BrowserCreateResponse
from ...types.browser_persistence_param import BrowserPersistenceParam
from ...types.browser_retrieve_response import BrowserRetrieveResponse
+from ...types.shared_params.browser_profile import BrowserProfile
+from ...types.shared_params.browser_viewport import BrowserViewport
+from ...types.shared_params.browser_extension import BrowserExtension
__all__ = ["BrowsersResource", "AsyncBrowsersResource"]
@@ -127,16 +130,16 @@ def with_streaming_response(self) -> BrowsersResourceWithStreamingResponse:
def create(
self,
*,
- extensions: Iterable[browser_create_params.Extension] | Omit = omit,
+ extensions: Iterable[BrowserExtension] | Omit = omit,
headless: bool | Omit = omit,
invocation_id: str | Omit = omit,
kiosk_mode: bool | Omit = omit,
persistence: BrowserPersistenceParam | Omit = omit,
- profile: browser_create_params.Profile | Omit = omit,
+ profile: BrowserProfile | Omit = omit,
proxy_id: str | Omit = omit,
stealth: bool | Omit = omit,
timeout_seconds: int | Omit = omit,
- viewport: browser_create_params.Viewport | Omit = omit,
+ viewport: BrowserViewport | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -470,16 +473,16 @@ def with_streaming_response(self) -> AsyncBrowsersResourceWithStreamingResponse:
async def create(
self,
*,
- extensions: Iterable[browser_create_params.Extension] | Omit = omit,
+ extensions: Iterable[BrowserExtension] | Omit = omit,
headless: bool | Omit = omit,
invocation_id: str | Omit = omit,
kiosk_mode: bool | Omit = omit,
persistence: BrowserPersistenceParam | Omit = omit,
- profile: browser_create_params.Profile | Omit = omit,
+ profile: BrowserProfile | Omit = omit,
proxy_id: str | Omit = omit,
stealth: bool | Omit = omit,
timeout_seconds: int | Omit = omit,
- viewport: browser_create_params.Viewport | Omit = omit,
+ viewport: BrowserViewport | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
diff --git a/src/kernel/types/__init__.py b/src/kernel/types/__init__.py
index 208a8bda..45b0c4ba 100644
--- a/src/kernel/types/__init__.py
+++ b/src/kernel/types/__init__.py
@@ -8,15 +8,20 @@
ErrorEvent as ErrorEvent,
ErrorModel as ErrorModel,
ErrorDetail as ErrorDetail,
+ BrowserProfile as BrowserProfile,
HeartbeatEvent as HeartbeatEvent,
+ BrowserViewport as BrowserViewport,
+ BrowserExtension as BrowserExtension,
)
from .profile import Profile as Profile
+from .browser_pool import BrowserPool as BrowserPool
from .app_list_params import AppListParams as AppListParams
from .app_list_response import AppListResponse as AppListResponse
from .browser_list_params import BrowserListParams as BrowserListParams
from .browser_persistence import BrowserPersistence as BrowserPersistence
from .proxy_create_params import ProxyCreateParams as ProxyCreateParams
from .proxy_list_response import ProxyListResponse as ProxyListResponse
+from .browser_pool_request import BrowserPoolRequest as BrowserPoolRequest
from .browser_create_params import BrowserCreateParams as BrowserCreateParams
from .browser_delete_params import BrowserDeleteParams as BrowserDeleteParams
from .browser_list_response import BrowserListResponse as BrowserListResponse
@@ -41,13 +46,20 @@
from .browser_persistence_param import BrowserPersistenceParam as BrowserPersistenceParam
from .browser_retrieve_response import BrowserRetrieveResponse as BrowserRetrieveResponse
from .extension_upload_response import ExtensionUploadResponse as ExtensionUploadResponse
+from .browser_pool_create_params import BrowserPoolCreateParams as BrowserPoolCreateParams
+from .browser_pool_delete_params import BrowserPoolDeleteParams as BrowserPoolDeleteParams
+from .browser_pool_list_response import BrowserPoolListResponse as BrowserPoolListResponse
+from .browser_pool_update_params import BrowserPoolUpdateParams as BrowserPoolUpdateParams
from .deployment_create_response import DeploymentCreateResponse as DeploymentCreateResponse
from .deployment_follow_response import DeploymentFollowResponse as DeploymentFollowResponse
from .invocation_create_response import InvocationCreateResponse as InvocationCreateResponse
from .invocation_follow_response import InvocationFollowResponse as InvocationFollowResponse
from .invocation_update_response import InvocationUpdateResponse as InvocationUpdateResponse
+from .browser_pool_acquire_params import BrowserPoolAcquireParams as BrowserPoolAcquireParams
+from .browser_pool_release_params import BrowserPoolReleaseParams as BrowserPoolReleaseParams
from .deployment_retrieve_response import DeploymentRetrieveResponse as DeploymentRetrieveResponse
from .invocation_retrieve_response import InvocationRetrieveResponse as InvocationRetrieveResponse
+from .browser_pool_acquire_response import BrowserPoolAcquireResponse as BrowserPoolAcquireResponse
from .browser_load_extensions_params import BrowserLoadExtensionsParams as BrowserLoadExtensionsParams
from .extension_download_from_chrome_store_params import (
ExtensionDownloadFromChromeStoreParams as ExtensionDownloadFromChromeStoreParams,
diff --git a/src/kernel/types/browser_create_params.py b/src/kernel/types/browser_create_params.py
index 1e54ce75..d1ff7907 100644
--- a/src/kernel/types/browser_create_params.py
+++ b/src/kernel/types/browser_create_params.py
@@ -3,15 +3,18 @@
from __future__ import annotations
from typing import Iterable
-from typing_extensions import Required, TypedDict
+from typing_extensions import TypedDict
from .browser_persistence_param import BrowserPersistenceParam
+from .shared_params.browser_profile import BrowserProfile
+from .shared_params.browser_viewport import BrowserViewport
+from .shared_params.browser_extension import BrowserExtension
-__all__ = ["BrowserCreateParams", "Extension", "Profile", "Viewport"]
+__all__ = ["BrowserCreateParams"]
class BrowserCreateParams(TypedDict, total=False):
- extensions: Iterable[Extension]
+ extensions: Iterable[BrowserExtension]
"""List of browser extensions to load into the session.
Provide each by id or name.
@@ -35,7 +38,7 @@ class BrowserCreateParams(TypedDict, total=False):
persistence: BrowserPersistenceParam
"""Optional persistence configuration for the browser session."""
- profile: Profile
+ profile: BrowserProfile
"""Profile selection for the browser session.
Provide either id or name. If specified, the matching profile will be loaded
@@ -64,7 +67,7 @@ class BrowserCreateParams(TypedDict, total=False):
specified value.
"""
- viewport: Viewport
+ viewport: BrowserViewport
"""Initial browser window size in pixels with optional refresh rate.
If omitted, image defaults apply (commonly 1024x768@60). Only specific viewport
@@ -75,45 +78,3 @@ class BrowserCreateParams(TypedDict, total=False):
configuration exactly. Note: Higher resolutions may affect the responsiveness of
live view browser
"""
-
-
-class Extension(TypedDict, total=False):
- id: str
- """Extension ID to load for this browser session"""
-
- name: str
- """Extension name to load for this browser session (instead of id).
-
- Must be 1-255 characters, using letters, numbers, dots, underscores, or hyphens.
- """
-
-
-class Profile(TypedDict, total=False):
- id: str
- """Profile ID to load for this browser session"""
-
- name: str
- """Profile name to load for this browser session (instead of id).
-
- Must be 1-255 characters, using letters, numbers, dots, underscores, or hyphens.
- """
-
- save_changes: bool
- """
- If true, save changes made during the session back to the profile when the
- session ends.
- """
-
-
-class Viewport(TypedDict, total=False):
- height: Required[int]
- """Browser window height in pixels."""
-
- width: Required[int]
- """Browser window width in pixels."""
-
- refresh_rate: int
- """Display refresh rate in Hz.
-
- If omitted, automatically determined from width and height.
- """
diff --git a/src/kernel/types/browser_create_response.py b/src/kernel/types/browser_create_response.py
index 21041eaa..0a5f33d4 100644
--- a/src/kernel/types/browser_create_response.py
+++ b/src/kernel/types/browser_create_response.py
@@ -6,22 +6,9 @@
from .profile import Profile
from .._models import BaseModel
from .browser_persistence import BrowserPersistence
+from .shared.browser_viewport import BrowserViewport
-__all__ = ["BrowserCreateResponse", "Viewport"]
-
-
-class Viewport(BaseModel):
- height: int
- """Browser window height in pixels."""
-
- width: int
- """Browser window width in pixels."""
-
- refresh_rate: Optional[int] = None
- """Display refresh rate in Hz.
-
- If omitted, automatically determined from width and height.
- """
+__all__ = ["BrowserCreateResponse"]
class BrowserCreateResponse(BaseModel):
@@ -64,7 +51,7 @@ class BrowserCreateResponse(BaseModel):
proxy_id: Optional[str] = None
"""ID of the proxy associated with this browser session, if any."""
- viewport: Optional[Viewport] = None
+ viewport: Optional[BrowserViewport] = None
"""Initial browser window size in pixels with optional refresh rate.
If omitted, image defaults apply (commonly 1024x768@60). Only specific viewport
diff --git a/src/kernel/types/browser_list_response.py b/src/kernel/types/browser_list_response.py
index 74978690..d6397291 100644
--- a/src/kernel/types/browser_list_response.py
+++ b/src/kernel/types/browser_list_response.py
@@ -6,22 +6,9 @@
from .profile import Profile
from .._models import BaseModel
from .browser_persistence import BrowserPersistence
+from .shared.browser_viewport import BrowserViewport
-__all__ = ["BrowserListResponse", "Viewport"]
-
-
-class Viewport(BaseModel):
- height: int
- """Browser window height in pixels."""
-
- width: int
- """Browser window width in pixels."""
-
- refresh_rate: Optional[int] = None
- """Display refresh rate in Hz.
-
- If omitted, automatically determined from width and height.
- """
+__all__ = ["BrowserListResponse"]
class BrowserListResponse(BaseModel):
@@ -64,7 +51,7 @@ class BrowserListResponse(BaseModel):
proxy_id: Optional[str] = None
"""ID of the proxy associated with this browser session, if any."""
- viewport: Optional[Viewport] = None
+ viewport: Optional[BrowserViewport] = None
"""Initial browser window size in pixels with optional refresh rate.
If omitted, image defaults apply (commonly 1024x768@60). Only specific viewport
diff --git a/src/kernel/types/browser_pool.py b/src/kernel/types/browser_pool.py
new file mode 100644
index 00000000..5fd30dca
--- /dev/null
+++ b/src/kernel/types/browser_pool.py
@@ -0,0 +1,29 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+
+from .._models import BaseModel
+from .browser_pool_request import BrowserPoolRequest
+
+__all__ = ["BrowserPool"]
+
+
+class BrowserPool(BaseModel):
+ id: str
+ """Unique identifier for the browser pool"""
+
+ acquired_count: int
+ """Number of browsers currently acquired from the pool"""
+
+ available_count: int
+ """Number of browsers currently available in the pool"""
+
+ browser_pool_config: BrowserPoolRequest
+ """Configuration used to create all browsers in this pool"""
+
+ created_at: datetime
+ """Timestamp when the browser pool was created"""
+
+ name: Optional[str] = None
+ """Browser pool name, if set"""
diff --git a/src/kernel/types/browser_pool_acquire_params.py b/src/kernel/types/browser_pool_acquire_params.py
new file mode 100644
index 00000000..d0df921a
--- /dev/null
+++ b/src/kernel/types/browser_pool_acquire_params.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["BrowserPoolAcquireParams"]
+
+
+class BrowserPoolAcquireParams(TypedDict, total=False):
+ acquire_timeout_seconds: int
+ """Maximum number of seconds to wait for a browser to be available.
+
+ Defaults to the calculated time it would take to fill the pool at the currently
+ configured fill rate.
+ """
diff --git a/src/kernel/types/browser_pool_acquire_response.py b/src/kernel/types/browser_pool_acquire_response.py
new file mode 100644
index 00000000..76ad037b
--- /dev/null
+++ b/src/kernel/types/browser_pool_acquire_response.py
@@ -0,0 +1,64 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+
+from .profile import Profile
+from .._models import BaseModel
+from .browser_persistence import BrowserPersistence
+from .shared.browser_viewport import BrowserViewport
+
+__all__ = ["BrowserPoolAcquireResponse"]
+
+
+class BrowserPoolAcquireResponse(BaseModel):
+ cdp_ws_url: str
+ """Websocket URL for Chrome DevTools Protocol connections to the browser session"""
+
+ created_at: datetime
+ """When the browser session was created."""
+
+ headless: bool
+ """Whether the browser session is running in headless mode."""
+
+ session_id: str
+ """Unique identifier for the browser session"""
+
+ stealth: bool
+ """Whether the browser session is running in stealth mode."""
+
+ timeout_seconds: int
+ """The number of seconds of inactivity before the browser session is terminated."""
+
+ browser_live_view_url: Optional[str] = None
+ """Remote URL for live viewing the browser session.
+
+ Only available for non-headless browsers.
+ """
+
+ deleted_at: Optional[datetime] = None
+ """When the browser session was soft-deleted. Only present for deleted sessions."""
+
+ kiosk_mode: Optional[bool] = None
+ """Whether the browser session is running in kiosk mode."""
+
+ persistence: Optional[BrowserPersistence] = None
+ """Optional persistence configuration for the browser session."""
+
+ profile: Optional[Profile] = None
+ """Browser profile metadata."""
+
+ proxy_id: Optional[str] = None
+ """ID of the proxy associated with this browser session, if any."""
+
+ viewport: Optional[BrowserViewport] = None
+ """Initial browser window size in pixels with optional refresh rate.
+
+ If omitted, image defaults apply (commonly 1024x768@60). Only specific viewport
+ configurations are supported. The server will reject unsupported combinations.
+ Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
+ 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
+ be automatically determined from the width and height if they match a supported
+ configuration exactly. Note: Higher resolutions may affect the responsiveness of
+ live view browser
+ """
diff --git a/src/kernel/types/browser_pool_create_params.py b/src/kernel/types/browser_pool_create_params.py
new file mode 100644
index 00000000..c7f87c64
--- /dev/null
+++ b/src/kernel/types/browser_pool_create_params.py
@@ -0,0 +1,75 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable
+from typing_extensions import Required, TypedDict
+
+from .shared_params.browser_profile import BrowserProfile
+from .shared_params.browser_viewport import BrowserViewport
+from .shared_params.browser_extension import BrowserExtension
+
+__all__ = ["BrowserPoolCreateParams"]
+
+
+class BrowserPoolCreateParams(TypedDict, total=False):
+ size: Required[int]
+ """Number of browsers to create in the pool"""
+
+ extensions: Iterable[BrowserExtension]
+ """List of browser extensions to load into the session.
+
+ Provide each by id or name.
+ """
+
+ fill_rate_per_minute: int
+ """Percentage of the pool to fill per minute. Defaults to 10%."""
+
+ headless: bool
+ """If true, launches the browser using a headless image. Defaults to false."""
+
+ kiosk_mode: bool
+ """
+ If true, launches the browser in kiosk mode to hide address bar and tabs in live
+ view.
+ """
+
+ name: str
+ """Optional name for the browser pool. Must be unique within the organization."""
+
+ profile: BrowserProfile
+ """Profile selection for the browser session.
+
+ Provide either id or name. If specified, the matching profile will be loaded
+ into the browser session. Profiles must be created beforehand.
+ """
+
+ proxy_id: str
+ """Optional proxy to associate to the browser session.
+
+ Must reference a proxy belonging to the caller's org.
+ """
+
+ stealth: bool
+ """
+ If true, launches the browser in stealth mode to reduce detection by anti-bot
+ mechanisms.
+ """
+
+ timeout_seconds: int
+ """
+ Default idle timeout in seconds for browsers acquired from this pool before they
+ are destroyed. Defaults to 600 seconds if not specified
+ """
+
+ viewport: BrowserViewport
+ """Initial browser window size in pixels with optional refresh rate.
+
+ If omitted, image defaults apply (commonly 1024x768@60). Only specific viewport
+ configurations are supported. The server will reject unsupported combinations.
+ Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
+ 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
+ be automatically determined from the width and height if they match a supported
+ configuration exactly. Note: Higher resolutions may affect the responsiveness of
+ live view browser
+ """
diff --git a/src/kernel/types/browser_pool_delete_params.py b/src/kernel/types/browser_pool_delete_params.py
new file mode 100644
index 00000000..0a63c0f1
--- /dev/null
+++ b/src/kernel/types/browser_pool_delete_params.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["BrowserPoolDeleteParams"]
+
+
+class BrowserPoolDeleteParams(TypedDict, total=False):
+ force: bool
+ """If true, force delete even if browsers are currently leased.
+
+ Leased browsers will be terminated.
+ """
diff --git a/src/kernel/types/browser_pool_list_response.py b/src/kernel/types/browser_pool_list_response.py
new file mode 100644
index 00000000..a11c4de2
--- /dev/null
+++ b/src/kernel/types/browser_pool_list_response.py
@@ -0,0 +1,10 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+from typing_extensions import TypeAlias
+
+from .browser_pool import BrowserPool
+
+__all__ = ["BrowserPoolListResponse"]
+
+BrowserPoolListResponse: TypeAlias = List[BrowserPool]
diff --git a/src/kernel/types/browser_pool_release_params.py b/src/kernel/types/browser_pool_release_params.py
new file mode 100644
index 00000000..104b0b0c
--- /dev/null
+++ b/src/kernel/types/browser_pool_release_params.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["BrowserPoolReleaseParams"]
+
+
+class BrowserPoolReleaseParams(TypedDict, total=False):
+ session_id: Required[str]
+ """Browser session ID to release back to the pool"""
+
+ reuse: bool
+ """Whether to reuse the browser instance or destroy it and create a new one.
+
+ Defaults to true.
+ """
diff --git a/src/kernel/types/browser_pool_request.py b/src/kernel/types/browser_pool_request.py
new file mode 100644
index 00000000..c25b3a55
--- /dev/null
+++ b/src/kernel/types/browser_pool_request.py
@@ -0,0 +1,73 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+
+from .._models import BaseModel
+from .shared.browser_profile import BrowserProfile
+from .shared.browser_viewport import BrowserViewport
+from .shared.browser_extension import BrowserExtension
+
+__all__ = ["BrowserPoolRequest"]
+
+
+class BrowserPoolRequest(BaseModel):
+ size: int
+ """Number of browsers to create in the pool"""
+
+ extensions: Optional[List[BrowserExtension]] = None
+ """List of browser extensions to load into the session.
+
+ Provide each by id or name.
+ """
+
+ fill_rate_per_minute: Optional[int] = None
+ """Percentage of the pool to fill per minute. Defaults to 10%."""
+
+ headless: Optional[bool] = None
+ """If true, launches the browser using a headless image. Defaults to false."""
+
+ kiosk_mode: Optional[bool] = None
+ """
+ If true, launches the browser in kiosk mode to hide address bar and tabs in live
+ view.
+ """
+
+ name: Optional[str] = None
+ """Optional name for the browser pool. Must be unique within the organization."""
+
+ profile: Optional[BrowserProfile] = None
+ """Profile selection for the browser session.
+
+ Provide either id or name. If specified, the matching profile will be loaded
+ into the browser session. Profiles must be created beforehand.
+ """
+
+ proxy_id: Optional[str] = None
+ """Optional proxy to associate to the browser session.
+
+ Must reference a proxy belonging to the caller's org.
+ """
+
+ stealth: Optional[bool] = None
+ """
+ If true, launches the browser in stealth mode to reduce detection by anti-bot
+ mechanisms.
+ """
+
+ timeout_seconds: Optional[int] = None
+ """
+ Default idle timeout in seconds for browsers acquired from this pool before they
+ are destroyed. Defaults to 600 seconds if not specified
+ """
+
+ viewport: Optional[BrowserViewport] = None
+ """Initial browser window size in pixels with optional refresh rate.
+
+ If omitted, image defaults apply (commonly 1024x768@60). Only specific viewport
+ configurations are supported. The server will reject unsupported combinations.
+ Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
+ 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
+ be automatically determined from the width and height if they match a supported
+ configuration exactly. Note: Higher resolutions may affect the responsiveness of
+ live view browser
+ """
diff --git a/src/kernel/types/browser_pool_update_params.py b/src/kernel/types/browser_pool_update_params.py
new file mode 100644
index 00000000..ed9a7e84
--- /dev/null
+++ b/src/kernel/types/browser_pool_update_params.py
@@ -0,0 +1,81 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable
+from typing_extensions import Required, TypedDict
+
+from .shared_params.browser_profile import BrowserProfile
+from .shared_params.browser_viewport import BrowserViewport
+from .shared_params.browser_extension import BrowserExtension
+
+__all__ = ["BrowserPoolUpdateParams"]
+
+
+class BrowserPoolUpdateParams(TypedDict, total=False):
+ size: Required[int]
+ """Number of browsers to create in the pool"""
+
+ discard_all_idle: bool
+ """Whether to discard all idle browsers and rebuild the pool immediately.
+
+ Defaults to true.
+ """
+
+ extensions: Iterable[BrowserExtension]
+ """List of browser extensions to load into the session.
+
+ Provide each by id or name.
+ """
+
+ fill_rate_per_minute: int
+ """Percentage of the pool to fill per minute. Defaults to 10%."""
+
+ headless: bool
+ """If true, launches the browser using a headless image. Defaults to false."""
+
+ kiosk_mode: bool
+ """
+ If true, launches the browser in kiosk mode to hide address bar and tabs in live
+ view.
+ """
+
+ name: str
+ """Optional name for the browser pool. Must be unique within the organization."""
+
+ profile: BrowserProfile
+ """Profile selection for the browser session.
+
+ Provide either id or name. If specified, the matching profile will be loaded
+ into the browser session. Profiles must be created beforehand.
+ """
+
+ proxy_id: str
+ """Optional proxy to associate to the browser session.
+
+ Must reference a proxy belonging to the caller's org.
+ """
+
+ stealth: bool
+ """
+ If true, launches the browser in stealth mode to reduce detection by anti-bot
+ mechanisms.
+ """
+
+ timeout_seconds: int
+ """
+ Default idle timeout in seconds for browsers acquired from this pool before they
+ are destroyed. Defaults to 600 seconds if not specified
+ """
+
+ viewport: BrowserViewport
+ """Initial browser window size in pixels with optional refresh rate.
+
+ If omitted, image defaults apply (commonly 1024x768@60). Only specific viewport
+ configurations are supported. The server will reject unsupported combinations.
+ Supported resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25,
+ 1440x900@25, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
+ be automatically determined from the width and height if they match a supported
+ configuration exactly. Note: Higher resolutions may affect the responsiveness of
+ live view browser
+ """
diff --git a/src/kernel/types/browser_retrieve_response.py b/src/kernel/types/browser_retrieve_response.py
index 527386da..111149b3 100644
--- a/src/kernel/types/browser_retrieve_response.py
+++ b/src/kernel/types/browser_retrieve_response.py
@@ -6,22 +6,9 @@
from .profile import Profile
from .._models import BaseModel
from .browser_persistence import BrowserPersistence
+from .shared.browser_viewport import BrowserViewport
-__all__ = ["BrowserRetrieveResponse", "Viewport"]
-
-
-class Viewport(BaseModel):
- height: int
- """Browser window height in pixels."""
-
- width: int
- """Browser window width in pixels."""
-
- refresh_rate: Optional[int] = None
- """Display refresh rate in Hz.
-
- If omitted, automatically determined from width and height.
- """
+__all__ = ["BrowserRetrieveResponse"]
class BrowserRetrieveResponse(BaseModel):
@@ -64,7 +51,7 @@ class BrowserRetrieveResponse(BaseModel):
proxy_id: Optional[str] = None
"""ID of the proxy associated with this browser session, if any."""
- viewport: Optional[Viewport] = None
+ viewport: Optional[BrowserViewport] = None
"""Initial browser window size in pixels with optional refresh rate.
If omitted, image defaults apply (commonly 1024x768@60). Only specific viewport
diff --git a/src/kernel/types/shared/__init__.py b/src/kernel/types/shared/__init__.py
index ea360f12..6b649199 100644
--- a/src/kernel/types/shared/__init__.py
+++ b/src/kernel/types/shared/__init__.py
@@ -5,4 +5,7 @@
from .error_event import ErrorEvent as ErrorEvent
from .error_model import ErrorModel as ErrorModel
from .error_detail import ErrorDetail as ErrorDetail
+from .browser_profile import BrowserProfile as BrowserProfile
from .heartbeat_event import HeartbeatEvent as HeartbeatEvent
+from .browser_viewport import BrowserViewport as BrowserViewport
+from .browser_extension import BrowserExtension as BrowserExtension
diff --git a/src/kernel/types/shared/browser_extension.py b/src/kernel/types/shared/browser_extension.py
new file mode 100644
index 00000000..7bc1a5ff
--- /dev/null
+++ b/src/kernel/types/shared/browser_extension.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from ..._models import BaseModel
+
+__all__ = ["BrowserExtension"]
+
+
+class BrowserExtension(BaseModel):
+ id: Optional[str] = None
+ """Extension ID to load for this browser session"""
+
+ name: Optional[str] = None
+ """Extension name to load for this browser session (instead of id).
+
+ Must be 1-255 characters, using letters, numbers, dots, underscores, or hyphens.
+ """
diff --git a/src/kernel/types/shared/browser_profile.py b/src/kernel/types/shared/browser_profile.py
new file mode 100644
index 00000000..5f790ccb
--- /dev/null
+++ b/src/kernel/types/shared/browser_profile.py
@@ -0,0 +1,24 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from ..._models import BaseModel
+
+__all__ = ["BrowserProfile"]
+
+
+class BrowserProfile(BaseModel):
+ id: Optional[str] = None
+ """Profile ID to load for this browser session"""
+
+ name: Optional[str] = None
+ """Profile name to load for this browser session (instead of id).
+
+ Must be 1-255 characters, using letters, numbers, dots, underscores, or hyphens.
+ """
+
+ save_changes: Optional[bool] = None
+ """
+ If true, save changes made during the session back to the profile when the
+ session ends.
+ """
diff --git a/src/kernel/types/shared/browser_viewport.py b/src/kernel/types/shared/browser_viewport.py
new file mode 100644
index 00000000..abffcc29
--- /dev/null
+++ b/src/kernel/types/shared/browser_viewport.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from ..._models import BaseModel
+
+__all__ = ["BrowserViewport"]
+
+
+class BrowserViewport(BaseModel):
+ height: int
+ """Browser window height in pixels."""
+
+ width: int
+ """Browser window width in pixels."""
+
+ refresh_rate: Optional[int] = None
+ """Display refresh rate in Hz.
+
+ If omitted, automatically determined from width and height.
+ """
diff --git a/src/kernel/types/shared_params/__init__.py b/src/kernel/types/shared_params/__init__.py
new file mode 100644
index 00000000..de63c649
--- /dev/null
+++ b/src/kernel/types/shared_params/__init__.py
@@ -0,0 +1,5 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .browser_profile import BrowserProfile as BrowserProfile
+from .browser_viewport import BrowserViewport as BrowserViewport
+from .browser_extension import BrowserExtension as BrowserExtension
diff --git a/src/kernel/types/shared_params/browser_extension.py b/src/kernel/types/shared_params/browser_extension.py
new file mode 100644
index 00000000..d81ac708
--- /dev/null
+++ b/src/kernel/types/shared_params/browser_extension.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["BrowserExtension"]
+
+
+class BrowserExtension(TypedDict, total=False):
+ id: str
+ """Extension ID to load for this browser session"""
+
+ name: str
+ """Extension name to load for this browser session (instead of id).
+
+ Must be 1-255 characters, using letters, numbers, dots, underscores, or hyphens.
+ """
diff --git a/src/kernel/types/shared_params/browser_profile.py b/src/kernel/types/shared_params/browser_profile.py
new file mode 100644
index 00000000..e1027d22
--- /dev/null
+++ b/src/kernel/types/shared_params/browser_profile.py
@@ -0,0 +1,24 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["BrowserProfile"]
+
+
+class BrowserProfile(TypedDict, total=False):
+ id: str
+ """Profile ID to load for this browser session"""
+
+ name: str
+ """Profile name to load for this browser session (instead of id).
+
+ Must be 1-255 characters, using letters, numbers, dots, underscores, or hyphens.
+ """
+
+ save_changes: bool
+ """
+ If true, save changes made during the session back to the profile when the
+ session ends.
+ """
diff --git a/src/kernel/types/shared_params/browser_viewport.py b/src/kernel/types/shared_params/browser_viewport.py
new file mode 100644
index 00000000..b7cb2f0f
--- /dev/null
+++ b/src/kernel/types/shared_params/browser_viewport.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["BrowserViewport"]
+
+
+class BrowserViewport(TypedDict, total=False):
+ height: Required[int]
+ """Browser window height in pixels."""
+
+ width: Required[int]
+ """Browser window width in pixels."""
+
+ refresh_rate: int
+ """Display refresh rate in Hz.
+
+ If omitted, automatically determined from width and height.
+ """
diff --git a/tests/api_resources/test_browser_pools.py b/tests/api_resources/test_browser_pools.py
new file mode 100644
index 00000000..6a8f164e
--- /dev/null
+++ b/tests/api_resources/test_browser_pools.py
@@ -0,0 +1,856 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from kernel import Kernel, AsyncKernel
+from tests.utils import assert_matches_type
+from kernel.types import (
+ BrowserPool,
+ BrowserPoolListResponse,
+ BrowserPoolAcquireResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestBrowserPools:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_create(self, client: Kernel) -> None:
+ browser_pool = client.browser_pools.create(
+ size=10,
+ )
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_create_with_all_params(self, client: Kernel) -> None:
+ browser_pool = client.browser_pools.create(
+ size=10,
+ extensions=[
+ {
+ "id": "id",
+ "name": "name",
+ }
+ ],
+ fill_rate_per_minute=0,
+ headless=False,
+ kiosk_mode=True,
+ name="my-pool",
+ profile={
+ "id": "id",
+ "name": "name",
+ "save_changes": True,
+ },
+ proxy_id="proxy_id",
+ stealth=True,
+ timeout_seconds=60,
+ viewport={
+ "height": 800,
+ "width": 1280,
+ "refresh_rate": 60,
+ },
+ )
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_create(self, client: Kernel) -> None:
+ response = client.browser_pools.with_raw_response.create(
+ size=10,
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ browser_pool = response.parse()
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_create(self, client: Kernel) -> None:
+ with client.browser_pools.with_streaming_response.create(
+ size=10,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ browser_pool = response.parse()
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_retrieve(self, client: Kernel) -> None:
+ browser_pool = client.browser_pools.retrieve(
+ "id_or_name",
+ )
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_retrieve(self, client: Kernel) -> None:
+ response = client.browser_pools.with_raw_response.retrieve(
+ "id_or_name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ browser_pool = response.parse()
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Kernel) -> None:
+ with client.browser_pools.with_streaming_response.retrieve(
+ "id_or_name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ browser_pool = response.parse()
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_retrieve(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
+ client.browser_pools.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_update(self, client: Kernel) -> None:
+ browser_pool = client.browser_pools.update(
+ id_or_name="id_or_name",
+ size=10,
+ )
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_update_with_all_params(self, client: Kernel) -> None:
+ browser_pool = client.browser_pools.update(
+ id_or_name="id_or_name",
+ size=10,
+ discard_all_idle=False,
+ extensions=[
+ {
+ "id": "id",
+ "name": "name",
+ }
+ ],
+ fill_rate_per_minute=0,
+ headless=False,
+ kiosk_mode=True,
+ name="my-pool",
+ profile={
+ "id": "id",
+ "name": "name",
+ "save_changes": True,
+ },
+ proxy_id="proxy_id",
+ stealth=True,
+ timeout_seconds=60,
+ viewport={
+ "height": 800,
+ "width": 1280,
+ "refresh_rate": 60,
+ },
+ )
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_update(self, client: Kernel) -> None:
+ response = client.browser_pools.with_raw_response.update(
+ id_or_name="id_or_name",
+ size=10,
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ browser_pool = response.parse()
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_update(self, client: Kernel) -> None:
+ with client.browser_pools.with_streaming_response.update(
+ id_or_name="id_or_name",
+ size=10,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ browser_pool = response.parse()
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_update(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
+ client.browser_pools.with_raw_response.update(
+ id_or_name="",
+ size=10,
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_list(self, client: Kernel) -> None:
+ browser_pool = client.browser_pools.list()
+ assert_matches_type(BrowserPoolListResponse, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_list(self, client: Kernel) -> None:
+ response = client.browser_pools.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ browser_pool = response.parse()
+ assert_matches_type(BrowserPoolListResponse, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_list(self, client: Kernel) -> None:
+ with client.browser_pools.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ browser_pool = response.parse()
+ assert_matches_type(BrowserPoolListResponse, browser_pool, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_delete(self, client: Kernel) -> None:
+ browser_pool = client.browser_pools.delete(
+ id_or_name="id_or_name",
+ )
+ assert browser_pool is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_delete_with_all_params(self, client: Kernel) -> None:
+ browser_pool = client.browser_pools.delete(
+ id_or_name="id_or_name",
+ force=True,
+ )
+ assert browser_pool is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_delete(self, client: Kernel) -> None:
+ response = client.browser_pools.with_raw_response.delete(
+ id_or_name="id_or_name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ browser_pool = response.parse()
+ assert browser_pool is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_delete(self, client: Kernel) -> None:
+ with client.browser_pools.with_streaming_response.delete(
+ id_or_name="id_or_name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ browser_pool = response.parse()
+ assert browser_pool is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_delete(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
+ client.browser_pools.with_raw_response.delete(
+ id_or_name="",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_acquire(self, client: Kernel) -> None:
+ browser_pool = client.browser_pools.acquire(
+ id_or_name="id_or_name",
+ )
+ assert_matches_type(BrowserPoolAcquireResponse, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_acquire_with_all_params(self, client: Kernel) -> None:
+ browser_pool = client.browser_pools.acquire(
+ id_or_name="id_or_name",
+ acquire_timeout_seconds=0,
+ )
+ assert_matches_type(BrowserPoolAcquireResponse, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_acquire(self, client: Kernel) -> None:
+ response = client.browser_pools.with_raw_response.acquire(
+ id_or_name="id_or_name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ browser_pool = response.parse()
+ assert_matches_type(BrowserPoolAcquireResponse, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_acquire(self, client: Kernel) -> None:
+ with client.browser_pools.with_streaming_response.acquire(
+ id_or_name="id_or_name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ browser_pool = response.parse()
+ assert_matches_type(BrowserPoolAcquireResponse, browser_pool, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_acquire(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
+ client.browser_pools.with_raw_response.acquire(
+ id_or_name="",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_flush(self, client: Kernel) -> None:
+ browser_pool = client.browser_pools.flush(
+ "id_or_name",
+ )
+ assert browser_pool is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_flush(self, client: Kernel) -> None:
+ response = client.browser_pools.with_raw_response.flush(
+ "id_or_name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ browser_pool = response.parse()
+ assert browser_pool is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_flush(self, client: Kernel) -> None:
+ with client.browser_pools.with_streaming_response.flush(
+ "id_or_name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ browser_pool = response.parse()
+ assert browser_pool is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_flush(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
+ client.browser_pools.with_raw_response.flush(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_release(self, client: Kernel) -> None:
+ browser_pool = client.browser_pools.release(
+ id_or_name="id_or_name",
+ session_id="ts8iy3sg25ibheguyni2lg9t",
+ )
+ assert browser_pool is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_release_with_all_params(self, client: Kernel) -> None:
+ browser_pool = client.browser_pools.release(
+ id_or_name="id_or_name",
+ session_id="ts8iy3sg25ibheguyni2lg9t",
+ reuse=False,
+ )
+ assert browser_pool is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_release(self, client: Kernel) -> None:
+ response = client.browser_pools.with_raw_response.release(
+ id_or_name="id_or_name",
+ session_id="ts8iy3sg25ibheguyni2lg9t",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ browser_pool = response.parse()
+ assert browser_pool is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_release(self, client: Kernel) -> None:
+ with client.browser_pools.with_streaming_response.release(
+ id_or_name="id_or_name",
+ session_id="ts8iy3sg25ibheguyni2lg9t",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ browser_pool = response.parse()
+ assert browser_pool is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_release(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
+ client.browser_pools.with_raw_response.release(
+ id_or_name="",
+ session_id="ts8iy3sg25ibheguyni2lg9t",
+ )
+
+
+class TestAsyncBrowserPools:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_create(self, async_client: AsyncKernel) -> None:
+ browser_pool = await async_client.browser_pools.create(
+ size=10,
+ )
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncKernel) -> None:
+ browser_pool = await async_client.browser_pools.create(
+ size=10,
+ extensions=[
+ {
+ "id": "id",
+ "name": "name",
+ }
+ ],
+ fill_rate_per_minute=0,
+ headless=False,
+ kiosk_mode=True,
+ name="my-pool",
+ profile={
+ "id": "id",
+ "name": "name",
+ "save_changes": True,
+ },
+ proxy_id="proxy_id",
+ stealth=True,
+ timeout_seconds=60,
+ viewport={
+ "height": 800,
+ "width": 1280,
+ "refresh_rate": 60,
+ },
+ )
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browser_pools.with_raw_response.create(
+ size=10,
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ browser_pool = await response.parse()
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncKernel) -> None:
+ async with async_client.browser_pools.with_streaming_response.create(
+ size=10,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ browser_pool = await response.parse()
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncKernel) -> None:
+ browser_pool = await async_client.browser_pools.retrieve(
+ "id_or_name",
+ )
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browser_pools.with_raw_response.retrieve(
+ "id_or_name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ browser_pool = await response.parse()
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncKernel) -> None:
+ async with async_client.browser_pools.with_streaming_response.retrieve(
+ "id_or_name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ browser_pool = await response.parse()
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
+ await async_client.browser_pools.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_update(self, async_client: AsyncKernel) -> None:
+ browser_pool = await async_client.browser_pools.update(
+ id_or_name="id_or_name",
+ size=10,
+ )
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncKernel) -> None:
+ browser_pool = await async_client.browser_pools.update(
+ id_or_name="id_or_name",
+ size=10,
+ discard_all_idle=False,
+ extensions=[
+ {
+ "id": "id",
+ "name": "name",
+ }
+ ],
+ fill_rate_per_minute=0,
+ headless=False,
+ kiosk_mode=True,
+ name="my-pool",
+ profile={
+ "id": "id",
+ "name": "name",
+ "save_changes": True,
+ },
+ proxy_id="proxy_id",
+ stealth=True,
+ timeout_seconds=60,
+ viewport={
+ "height": 800,
+ "width": 1280,
+ "refresh_rate": 60,
+ },
+ )
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browser_pools.with_raw_response.update(
+ id_or_name="id_or_name",
+ size=10,
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ browser_pool = await response.parse()
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncKernel) -> None:
+ async with async_client.browser_pools.with_streaming_response.update(
+ id_or_name="id_or_name",
+ size=10,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ browser_pool = await response.parse()
+ assert_matches_type(BrowserPool, browser_pool, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
+ await async_client.browser_pools.with_raw_response.update(
+ id_or_name="",
+ size=10,
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_list(self, async_client: AsyncKernel) -> None:
+ browser_pool = await async_client.browser_pools.list()
+ assert_matches_type(BrowserPoolListResponse, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browser_pools.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ browser_pool = await response.parse()
+ assert_matches_type(BrowserPoolListResponse, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncKernel) -> None:
+ async with async_client.browser_pools.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ browser_pool = await response.parse()
+ assert_matches_type(BrowserPoolListResponse, browser_pool, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncKernel) -> None:
+ browser_pool = await async_client.browser_pools.delete(
+ id_or_name="id_or_name",
+ )
+ assert browser_pool is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_delete_with_all_params(self, async_client: AsyncKernel) -> None:
+ browser_pool = await async_client.browser_pools.delete(
+ id_or_name="id_or_name",
+ force=True,
+ )
+ assert browser_pool is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browser_pools.with_raw_response.delete(
+ id_or_name="id_or_name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ browser_pool = await response.parse()
+ assert browser_pool is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncKernel) -> None:
+ async with async_client.browser_pools.with_streaming_response.delete(
+ id_or_name="id_or_name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ browser_pool = await response.parse()
+ assert browser_pool is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
+ await async_client.browser_pools.with_raw_response.delete(
+ id_or_name="",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_acquire(self, async_client: AsyncKernel) -> None:
+ browser_pool = await async_client.browser_pools.acquire(
+ id_or_name="id_or_name",
+ )
+ assert_matches_type(BrowserPoolAcquireResponse, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_acquire_with_all_params(self, async_client: AsyncKernel) -> None:
+ browser_pool = await async_client.browser_pools.acquire(
+ id_or_name="id_or_name",
+ acquire_timeout_seconds=0,
+ )
+ assert_matches_type(BrowserPoolAcquireResponse, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_acquire(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browser_pools.with_raw_response.acquire(
+ id_or_name="id_or_name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ browser_pool = await response.parse()
+ assert_matches_type(BrowserPoolAcquireResponse, browser_pool, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_acquire(self, async_client: AsyncKernel) -> None:
+ async with async_client.browser_pools.with_streaming_response.acquire(
+ id_or_name="id_or_name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ browser_pool = await response.parse()
+ assert_matches_type(BrowserPoolAcquireResponse, browser_pool, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_acquire(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
+ await async_client.browser_pools.with_raw_response.acquire(
+ id_or_name="",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_flush(self, async_client: AsyncKernel) -> None:
+ browser_pool = await async_client.browser_pools.flush(
+ "id_or_name",
+ )
+ assert browser_pool is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_flush(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browser_pools.with_raw_response.flush(
+ "id_or_name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ browser_pool = await response.parse()
+ assert browser_pool is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_flush(self, async_client: AsyncKernel) -> None:
+ async with async_client.browser_pools.with_streaming_response.flush(
+ "id_or_name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ browser_pool = await response.parse()
+ assert browser_pool is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_flush(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
+ await async_client.browser_pools.with_raw_response.flush(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_release(self, async_client: AsyncKernel) -> None:
+ browser_pool = await async_client.browser_pools.release(
+ id_or_name="id_or_name",
+ session_id="ts8iy3sg25ibheguyni2lg9t",
+ )
+ assert browser_pool is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_release_with_all_params(self, async_client: AsyncKernel) -> None:
+ browser_pool = await async_client.browser_pools.release(
+ id_or_name="id_or_name",
+ session_id="ts8iy3sg25ibheguyni2lg9t",
+ reuse=False,
+ )
+ assert browser_pool is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_release(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browser_pools.with_raw_response.release(
+ id_or_name="id_or_name",
+ session_id="ts8iy3sg25ibheguyni2lg9t",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ browser_pool = await response.parse()
+ assert browser_pool is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_release(self, async_client: AsyncKernel) -> None:
+ async with async_client.browser_pools.with_streaming_response.release(
+ id_or_name="id_or_name",
+ session_id="ts8iy3sg25ibheguyni2lg9t",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ browser_pool = await response.parse()
+ assert browser_pool is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_release(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
+ await async_client.browser_pools.with_raw_response.release(
+ id_or_name="",
+ session_id="ts8iy3sg25ibheguyni2lg9t",
+ )