diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 141e7cd..ff66120 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.47.0" + ".": "0.48.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 7f48513..d184bbd 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 104 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-ac10847d991ef8ed89124b5550922cb5726af2b4a4c3396ee6ff82938302fc25.yml -openapi_spec_hash: 0d902563108fe2461708c05336eab40a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-49a1a92e00d1eb87e91e8527275cb0705fce2edea30e70fea745f134dd451fbd.yml +openapi_spec_hash: 3aa6ab6939790f538332054162fbdedc config_hash: 16e4457a0bb26e98a335a1c2a572290a diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e99ff9..2cb4e4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 0.48.0 (2026-04-08) + +Full Changelog: [v0.47.0...v0.48.0](https://github.com/kernel/kernel-python-sdk/compare/v0.47.0...v0.48.0) + +### Features + +* [kernel-1116] add base_url field to browser session response ([335d9e0](https://github.com/kernel/kernel-python-sdk/commit/335d9e04998dd9e581f47d8869dd7f07a8f2db74)) + + +### Bug Fixes + +* **client:** preserve hardcoded query params when merging with user params ([6dfd882](https://github.com/kernel/kernel-python-sdk/commit/6dfd8826b84ed191d72ac114df986c398fa83c5c)) + ## 0.47.0 (2026-04-07) Full Changelog: [v0.46.0...v0.47.0](https://github.com/kernel/kernel-python-sdk/compare/v0.46.0...v0.47.0) diff --git a/pyproject.toml b/pyproject.toml index c9fe0cd..b26c2b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "kernel" -version = "0.47.0" +version = "0.48.0" description = "The official Python library for the kernel API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/kernel/_base_client.py b/src/kernel/_base_client.py index a3d47ea..2599dc4 100644 --- a/src/kernel/_base_client.py +++ b/src/kernel/_base_client.py @@ -540,6 +540,10 @@ def _build_request( files = cast(HttpxRequestFiles, ForceMultipartDict()) prepared_url = self._prepare_url(options.url) + # preserve hard-coded query params from the url + if params and prepared_url.query: + params = {**dict(prepared_url.params.items()), **params} + prepared_url = prepared_url.copy_with(raw_path=prepared_url.raw_path.split(b"?", 1)[0]) if "_" in prepared_url.host: # work around https://github.com/encode/httpx/discussions/2880 kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")} diff --git a/src/kernel/_version.py b/src/kernel/_version.py index 254e7e2..a9881de 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.47.0" # x-release-please-version +__version__ = "0.48.0" # x-release-please-version diff --git a/src/kernel/types/browser_create_response.py b/src/kernel/types/browser_create_response.py index d59a3d0..9356bb0 100644 --- a/src/kernel/types/browser_create_response.py +++ b/src/kernel/types/browser_create_response.py @@ -35,6 +35,9 @@ class BrowserCreateResponse(BaseModel): webdriver_ws_url: str """Websocket URL for WebDriver BiDi connections to the browser session""" + base_url: Optional[str] = None + """Metro-API HTTP base URL for this browser session.""" + browser_live_view_url: Optional[str] = None """Remote URL for live viewing the browser session. diff --git a/src/kernel/types/browser_list_response.py b/src/kernel/types/browser_list_response.py index 708caa9..f3a88f2 100644 --- a/src/kernel/types/browser_list_response.py +++ b/src/kernel/types/browser_list_response.py @@ -35,6 +35,9 @@ class BrowserListResponse(BaseModel): webdriver_ws_url: str """Websocket URL for WebDriver BiDi connections to the browser session""" + base_url: Optional[str] = None + """Metro-API HTTP base URL for this browser session.""" + browser_live_view_url: Optional[str] = None """Remote URL for live viewing the browser session. diff --git a/src/kernel/types/browser_pool_acquire_response.py b/src/kernel/types/browser_pool_acquire_response.py index 5ab52b5..064c405 100644 --- a/src/kernel/types/browser_pool_acquire_response.py +++ b/src/kernel/types/browser_pool_acquire_response.py @@ -35,6 +35,9 @@ class BrowserPoolAcquireResponse(BaseModel): webdriver_ws_url: str """Websocket URL for WebDriver BiDi connections to the browser session""" + base_url: Optional[str] = None + """Metro-API HTTP base URL for this browser session.""" + browser_live_view_url: Optional[str] = None """Remote URL for live viewing the browser session. diff --git a/src/kernel/types/browser_retrieve_response.py b/src/kernel/types/browser_retrieve_response.py index 221eab5..5b5a891 100644 --- a/src/kernel/types/browser_retrieve_response.py +++ b/src/kernel/types/browser_retrieve_response.py @@ -35,6 +35,9 @@ class BrowserRetrieveResponse(BaseModel): webdriver_ws_url: str """Websocket URL for WebDriver BiDi connections to the browser session""" + base_url: Optional[str] = None + """Metro-API HTTP base URL for this browser session.""" + browser_live_view_url: Optional[str] = None """Remote URL for live viewing the browser session. diff --git a/src/kernel/types/browser_update_response.py b/src/kernel/types/browser_update_response.py index c8a85c3..188895a 100644 --- a/src/kernel/types/browser_update_response.py +++ b/src/kernel/types/browser_update_response.py @@ -35,6 +35,9 @@ class BrowserUpdateResponse(BaseModel): webdriver_ws_url: str """Websocket URL for WebDriver BiDi connections to the browser session""" + base_url: Optional[str] = None + """Metro-API HTTP base URL for this browser session.""" + browser_live_view_url: Optional[str] = None """Remote URL for live viewing the browser session. diff --git a/src/kernel/types/invocation_list_browsers_response.py b/src/kernel/types/invocation_list_browsers_response.py index a0fed9a..23eda77 100644 --- a/src/kernel/types/invocation_list_browsers_response.py +++ b/src/kernel/types/invocation_list_browsers_response.py @@ -35,6 +35,9 @@ class Browser(BaseModel): webdriver_ws_url: str """Websocket URL for WebDriver BiDi connections to the browser session""" + base_url: Optional[str] = None + """Metro-API HTTP base URL for this browser session.""" + browser_live_view_url: Optional[str] = None """Remote URL for live viewing the browser session. diff --git a/tests/test_client.py b/tests/test_client.py index d2eca05..c3a1186 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -427,6 +427,30 @@ def test_default_query_option(self) -> None: client.close() + def test_hardcoded_query_params_in_url(self, client: Kernel) -> None: + request = client._build_request(FinalRequestOptions(method="get", url="/foo?beta=true")) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/foo?beta=true", + params={"limit": "10", "page": "abc"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true", "limit": "10", "page": "abc"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/files/a%2Fb?beta=true", + params={"limit": "10"}, + ) + ) + assert request.url.raw_path == b"/files/a%2Fb?beta=true&limit=10" + def test_request_extra_json(self, client: Kernel) -> None: request = client._build_request( FinalRequestOptions( @@ -1330,6 +1354,30 @@ async def test_default_query_option(self) -> None: await client.close() + async def test_hardcoded_query_params_in_url(self, async_client: AsyncKernel) -> None: + request = async_client._build_request(FinalRequestOptions(method="get", url="/foo?beta=true")) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true"} + + request = async_client._build_request( + FinalRequestOptions( + method="get", + url="/foo?beta=true", + params={"limit": "10", "page": "abc"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true", "limit": "10", "page": "abc"} + + request = async_client._build_request( + FinalRequestOptions( + method="get", + url="/files/a%2Fb?beta=true", + params={"limit": "10"}, + ) + ) + assert request.url.raw_path == b"/files/a%2Fb?beta=true&limit=10" + def test_request_extra_json(self, client: Kernel) -> None: request = client._build_request( FinalRequestOptions(