diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c18333e..0c2ecec 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.19.2" + ".": "0.20.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 53123c9..11b820d 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-d611cf8b0301a07123eab0e92498bea5ad69c5292b28aca1016c362cca0a0564.yml -openapi_spec_hash: 6d30f4ad9d61a7da8a75d543cf3d3d75 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-2af1b468584cb44aa9babbbfb82bff4055614fbb5c815084a6b7dacc1cf1a822.yml +openapi_spec_hash: 891affa2849341ea01d62011125f7edc config_hash: 9421eb86b7f3f4b274f123279da3858e diff --git a/CHANGELOG.md b/CHANGELOG.md index 711c887..c0c65e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 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) + +### Features + +* Add pagination to list browsers method and allow it to include deleted browsers when `include_deleted = true` ([0bf4d45](https://github.com/onkernel/kernel-python-sdk/commit/0bf4d4546171f7bc2fad9d225b7fcf2be14a6a71)) + ## 0.19.2 (2025-11-17) Full Changelog: [v0.19.1...v0.19.2](https://github.com/onkernel/kernel-python-sdk/compare/v0.19.1...v0.19.2) diff --git a/api.md b/api.md index 8a015c3..afa0ddd 100644 --- a/api.md +++ b/api.md @@ -79,7 +79,7 @@ Methods: - client.browsers.create(\*\*params) -> BrowserCreateResponse - client.browsers.retrieve(id) -> BrowserRetrieveResponse -- client.browsers.list() -> BrowserListResponse +- client.browsers.list(\*\*params) -> SyncOffsetPagination[BrowserListResponse] - client.browsers.delete(\*\*params) -> None - client.browsers.delete_by_id(id) -> None - client.browsers.load_extensions(id, \*\*params) -> None diff --git a/pyproject.toml b/pyproject.toml index fc74c8a..b5d3ecb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "kernel" -version = "0.19.2" +version = "0.20.0" description = "The official Python library for the kernel API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/kernel/_version.py b/src/kernel/_version.py index 67d5101..1a28cba 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.19.2" # x-release-please-version +__version__ = "0.20.0" # x-release-please-version diff --git a/src/kernel/resources/browsers/browsers.py b/src/kernel/resources/browsers/browsers.py index 8503736..84a4fe4 100644 --- a/src/kernel/resources/browsers/browsers.py +++ b/src/kernel/resources/browsers/browsers.py @@ -22,7 +22,12 @@ FsResourceWithStreamingResponse, AsyncFsResourceWithStreamingResponse, ) -from ...types import browser_create_params, browser_delete_params, browser_load_extensions_params +from ...types import ( + browser_list_params, + browser_create_params, + browser_delete_params, + browser_load_extensions_params, +) from .process import ( ProcessResource, AsyncProcessResource, @@ -65,7 +70,8 @@ async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from ..._base_client import make_request_options +from ...pagination import SyncOffsetPagination, AsyncOffsetPagination +from ..._base_client import AsyncPaginator, make_request_options from ...types.browser_list_response import BrowserListResponse from ...types.browser_create_response import BrowserCreateResponse from ...types.browser_persistence_param import BrowserPersistenceParam @@ -247,20 +253,55 @@ def retrieve( def list( self, *, + include_deleted: bool | Omit = omit, + limit: int | Omit = omit, + offset: 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, - ) -> BrowserListResponse: - """List active browser sessions""" - return self._get( + ) -> SyncOffsetPagination[BrowserListResponse]: + """List all browser sessions with pagination support. + + Use include_deleted=true to + include soft-deleted sessions in the results. + + Args: + include_deleted: When true, includes soft-deleted browser sessions in the results alongside + active sessions. + + limit: Maximum number of results to return. Defaults to 20, maximum 100. + + offset: Number of results to skip. Defaults to 0. + + 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._get_api_list( "/browsers", + page=SyncOffsetPagination[BrowserListResponse], options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "include_deleted": include_deleted, + "limit": limit, + "offset": offset, + }, + browser_list_params.BrowserListParams, + ), ), - cast_to=BrowserListResponse, + model=BrowserListResponse, ) def delete( @@ -552,23 +593,58 @@ async def retrieve( cast_to=BrowserRetrieveResponse, ) - async def list( + def list( self, *, + include_deleted: bool | Omit = omit, + limit: int | Omit = omit, + offset: 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, - ) -> BrowserListResponse: - """List active browser sessions""" - return await self._get( + ) -> AsyncPaginator[BrowserListResponse, AsyncOffsetPagination[BrowserListResponse]]: + """List all browser sessions with pagination support. + + Use include_deleted=true to + include soft-deleted sessions in the results. + + Args: + include_deleted: When true, includes soft-deleted browser sessions in the results alongside + active sessions. + + limit: Maximum number of results to return. Defaults to 20, maximum 100. + + offset: Number of results to skip. Defaults to 0. + + 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._get_api_list( "/browsers", + page=AsyncOffsetPagination[BrowserListResponse], options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "include_deleted": include_deleted, + "limit": limit, + "offset": offset, + }, + browser_list_params.BrowserListParams, + ), ), - cast_to=BrowserListResponse, + model=BrowserListResponse, ) async def delete( diff --git a/src/kernel/types/__init__.py b/src/kernel/types/__init__.py index 6b49cf7..208a8bd 100644 --- a/src/kernel/types/__init__.py +++ b/src/kernel/types/__init__.py @@ -13,6 +13,7 @@ from .profile import Profile as Profile 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 diff --git a/src/kernel/types/browser_create_response.py b/src/kernel/types/browser_create_response.py index 26fef74..21041ea 100644 --- a/src/kernel/types/browser_create_response.py +++ b/src/kernel/types/browser_create_response.py @@ -49,6 +49,9 @@ class BrowserCreateResponse(BaseModel): 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.""" diff --git a/src/kernel/types/browser_list_params.py b/src/kernel/types/browser_list_params.py new file mode 100644 index 0000000..20837be --- /dev/null +++ b/src/kernel/types/browser_list_params.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 TypedDict + +__all__ = ["BrowserListParams"] + + +class BrowserListParams(TypedDict, total=False): + include_deleted: bool + """ + When true, includes soft-deleted browser sessions in the results alongside + active sessions. + """ + + limit: int + """Maximum number of results to return. Defaults to 20, maximum 100.""" + + offset: int + """Number of results to skip. Defaults to 0.""" diff --git a/src/kernel/types/browser_list_response.py b/src/kernel/types/browser_list_response.py index 72ab6f3..7497869 100644 --- a/src/kernel/types/browser_list_response.py +++ b/src/kernel/types/browser_list_response.py @@ -1,17 +1,16 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Optional +from typing import Optional from datetime import datetime -from typing_extensions import TypeAlias from .profile import Profile from .._models import BaseModel from .browser_persistence import BrowserPersistence -__all__ = ["BrowserListResponse", "BrowserListResponseItem", "BrowserListResponseItemViewport"] +__all__ = ["BrowserListResponse", "Viewport"] -class BrowserListResponseItemViewport(BaseModel): +class Viewport(BaseModel): height: int """Browser window height in pixels.""" @@ -25,7 +24,7 @@ class BrowserListResponseItemViewport(BaseModel): """ -class BrowserListResponseItem(BaseModel): +class BrowserListResponse(BaseModel): cdp_ws_url: str """Websocket URL for Chrome DevTools Protocol connections to the browser session""" @@ -50,6 +49,9 @@ class BrowserListResponseItem(BaseModel): 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.""" @@ -62,7 +64,7 @@ class BrowserListResponseItem(BaseModel): proxy_id: Optional[str] = None """ID of the proxy associated with this browser session, if any.""" - viewport: Optional[BrowserListResponseItemViewport] = None + viewport: Optional[Viewport] = None """Initial browser window size in pixels with optional refresh rate. If omitted, image defaults apply (commonly 1024x768@60). Only specific viewport @@ -73,6 +75,3 @@ class BrowserListResponseItem(BaseModel): configuration exactly. Note: Higher resolutions may affect the responsiveness of live view browser """ - - -BrowserListResponse: TypeAlias = List[BrowserListResponseItem] diff --git a/src/kernel/types/browser_retrieve_response.py b/src/kernel/types/browser_retrieve_response.py index 4d575f0..527386d 100644 --- a/src/kernel/types/browser_retrieve_response.py +++ b/src/kernel/types/browser_retrieve_response.py @@ -49,6 +49,9 @@ class BrowserRetrieveResponse(BaseModel): 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.""" diff --git a/tests/api_resources/test_browsers.py b/tests/api_resources/test_browsers.py index bd75630..c87fc3d 100644 --- a/tests/api_resources/test_browsers.py +++ b/tests/api_resources/test_browsers.py @@ -14,6 +14,7 @@ BrowserCreateResponse, BrowserRetrieveResponse, ) +from kernel.pagination import SyncOffsetPagination, AsyncOffsetPagination base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -125,7 +126,17 @@ def test_path_params_retrieve(self, client: Kernel) -> None: @parametrize def test_method_list(self, client: Kernel) -> None: browser = client.browsers.list() - assert_matches_type(BrowserListResponse, browser, path=["response"]) + assert_matches_type(SyncOffsetPagination[BrowserListResponse], browser, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Kernel) -> None: + browser = client.browsers.list( + include_deleted=True, + limit=1, + offset=0, + ) + assert_matches_type(SyncOffsetPagination[BrowserListResponse], browser, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize @@ -135,7 +146,7 @@ def test_raw_response_list(self, client: Kernel) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" browser = response.parse() - assert_matches_type(BrowserListResponse, browser, path=["response"]) + assert_matches_type(SyncOffsetPagination[BrowserListResponse], browser, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize @@ -145,7 +156,7 @@ def test_streaming_response_list(self, client: Kernel) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" browser = response.parse() - assert_matches_type(BrowserListResponse, browser, path=["response"]) + assert_matches_type(SyncOffsetPagination[BrowserListResponse], browser, path=["response"]) assert cast(Any, response.is_closed) is True @@ -401,7 +412,17 @@ async def test_path_params_retrieve(self, async_client: AsyncKernel) -> None: @parametrize async def test_method_list(self, async_client: AsyncKernel) -> None: browser = await async_client.browsers.list() - assert_matches_type(BrowserListResponse, browser, path=["response"]) + assert_matches_type(AsyncOffsetPagination[BrowserListResponse], browser, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncKernel) -> None: + browser = await async_client.browsers.list( + include_deleted=True, + limit=1, + offset=0, + ) + assert_matches_type(AsyncOffsetPagination[BrowserListResponse], browser, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize @@ -411,7 +432,7 @@ async def test_raw_response_list(self, async_client: AsyncKernel) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" browser = await response.parse() - assert_matches_type(BrowserListResponse, browser, path=["response"]) + assert_matches_type(AsyncOffsetPagination[BrowserListResponse], browser, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize @@ -421,7 +442,7 @@ async def test_streaming_response_list(self, async_client: AsyncKernel) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" browser = await response.parse() - assert_matches_type(BrowserListResponse, browser, path=["response"]) + assert_matches_type(AsyncOffsetPagination[BrowserListResponse], browser, path=["response"]) assert cast(Any, response.is_closed) is True