Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.49.0"
".": "0.50.0"
}
8 changes: 4 additions & 4 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 111
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-49a1a92e00d1eb87e91e8527275cb0705fce2edea30e70fea745f134dd451fbd.yml
openapi_spec_hash: 0ffef6a95f9d9b1096180fc5e4c5b39c
config_hash: 9818dd634f87b677410eefd013d7a179
configured_endpoints: 112
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-930823e8b25b4644b74098ad5479840f64a329321aa236460f8a9562ae9051bf.yml
openapi_spec_hash: 9f868e67df8fd2fec8d8fc3eb5ba0b26
config_hash: 08d55086449943a8fec212b870061a3f
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## 0.50.0 (2026-04-13)

Full Changelog: [v0.49.0...v0.50.0](https://github.com/kernel/kernel-python-sdk/compare/v0.49.0...v0.50.0)

### Features

* add POST /browsers/{id}/curl and /curl/raw endpoints ([e91bc38](https://github.com/kernel/kernel-python-sdk/commit/e91bc387e5ef74c1b02f62e19e9ae31867296af4))


### Bug Fixes

* ensure file data are only sent as 1 parameter ([e566aa5](https://github.com/kernel/kernel-python-sdk/commit/e566aa50a9022d5b284c6334b3704df2a96643cc))

## 0.49.0 (2026-04-10)

Full Changelog: [v0.48.0...v0.49.0](https://github.com/kernel/kernel-python-sdk/compare/v0.48.0...v0.49.0)
Expand Down
2 changes: 2 additions & 0 deletions api.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ from kernel.types import (
BrowserRetrieveResponse,
BrowserUpdateResponse,
BrowserListResponse,
BrowserCurlResponse,
)
```

Expand All @@ -98,6 +99,7 @@ Methods:
- <code title="patch /browsers/{id}">client.browsers.<a href="./src/kernel/resources/browsers/browsers.py">update</a>(id, \*\*<a href="src/kernel/types/browser_update_params.py">params</a>) -> <a href="./src/kernel/types/browser_update_response.py">BrowserUpdateResponse</a></code>
- <code title="get /browsers">client.browsers.<a href="./src/kernel/resources/browsers/browsers.py">list</a>(\*\*<a href="src/kernel/types/browser_list_params.py">params</a>) -> <a href="./src/kernel/types/browser_list_response.py">SyncOffsetPagination[BrowserListResponse]</a></code>
- <code title="delete /browsers">client.browsers.<a href="./src/kernel/resources/browsers/browsers.py">delete</a>(\*\*<a href="src/kernel/types/browser_delete_params.py">params</a>) -> None</code>
- <code title="post /browsers/{id}/curl">client.browsers.<a href="./src/kernel/resources/browsers/browsers.py">curl</a>(id, \*\*<a href="src/kernel/types/browser_curl_params.py">params</a>) -> <a href="./src/kernel/types/browser_curl_response.py">BrowserCurlResponse</a></code>
- <code title="delete /browsers/{id}">client.browsers.<a href="./src/kernel/resources/browsers/browsers.py">delete_by_id</a>(id) -> None</code>
- <code title="post /browsers/{id}/extensions">client.browsers.<a href="./src/kernel/resources/browsers/browsers.py">load_extensions</a>(id, \*\*<a href="src/kernel/types/browser_load_extensions_params.py">params</a>) -> None</code>

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "kernel"
version = "0.49.0"
version = "0.50.0"
description = "The official Python library for the kernel API"
dynamic = ["readme"]
license = "Apache-2.0"
Expand Down
5 changes: 3 additions & 2 deletions src/kernel/_utils/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ def _extract_items(
index += 1
if is_dict(obj):
try:
# We are at the last entry in the path so we must remove the field
if (len(path)) == index:
# Remove the field if there are no more dict keys in the path,
# only "<array>" traversal markers or end.
if all(p == "<array>" for p in path[index:]):
item = obj.pop(key)
else:
item = obj[key]
Expand Down
2 changes: 1 addition & 1 deletion src/kernel/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

__title__ = "kernel"
__version__ = "0.49.0" # x-release-please-version
__version__ = "0.50.0" # x-release-please-version
144 changes: 143 additions & 1 deletion src/kernel/resources/browsers/browsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from __future__ import annotations

import typing_extensions
from typing import Mapping, Iterable, Optional, cast
from typing import Dict, Mapping, Iterable, Optional, cast
from typing_extensions import Literal

import httpx
Expand All @@ -25,6 +25,7 @@
AsyncFsResourceWithStreamingResponse,
)
from ...types import (
browser_curl_params,
browser_list_params,
browser_create_params,
browser_delete_params,
Expand Down Expand Up @@ -76,6 +77,7 @@
)
from ...pagination import SyncOffsetPagination, AsyncOffsetPagination
from ..._base_client import AsyncPaginator, make_request_options
from ...types.browser_curl_response import BrowserCurlResponse
from ...types.browser_list_response import BrowserListResponse
from ...types.browser_create_response import BrowserCreateResponse
from ...types.browser_update_response import BrowserUpdateResponse
Expand Down Expand Up @@ -443,6 +445,70 @@ def delete(
cast_to=NoneType,
)

def curl(
self,
id: str,
*,
url: str,
body: str | Omit = omit,
headers: Dict[str, str] | Omit = omit,
method: Literal["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"] | Omit = omit,
response_encoding: Literal["utf8", "base64"] | Omit = omit,
timeout_ms: 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,
) -> BrowserCurlResponse:
"""
Sends an HTTP request through Chrome's HTTP request stack, inheriting the
browser's TLS fingerprint, cookies, proxy configuration, and headers. Returns a
structured JSON response with status, headers, body, and timing.

Args:
url: Target URL (must be http or https).

body: Request body (for POST/PUT/PATCH).

headers: Custom headers merged with browser defaults.

method: HTTP method.

response_encoding: Encoding for the response body. Use base64 for binary content.

timeout_ms: Request timeout in milliseconds.

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:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return self._post(
path_template("/browsers/{id}/curl", id=id),
body=maybe_transform(
{
"url": url,
"body": body,
"headers": headers,
"method": method,
"response_encoding": response_encoding,
"timeout_ms": timeout_ms,
},
browser_curl_params.BrowserCurlParams,
),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
cast_to=BrowserCurlResponse,
)

def delete_by_id(
self,
id: str,
Expand Down Expand Up @@ -881,6 +947,70 @@ async def delete(
cast_to=NoneType,
)

async def curl(
self,
id: str,
*,
url: str,
body: str | Omit = omit,
headers: Dict[str, str] | Omit = omit,
method: Literal["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"] | Omit = omit,
response_encoding: Literal["utf8", "base64"] | Omit = omit,
timeout_ms: 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,
) -> BrowserCurlResponse:
"""
Sends an HTTP request through Chrome's HTTP request stack, inheriting the
browser's TLS fingerprint, cookies, proxy configuration, and headers. Returns a
structured JSON response with status, headers, body, and timing.

Args:
url: Target URL (must be http or https).

body: Request body (for POST/PUT/PATCH).

headers: Custom headers merged with browser defaults.

method: HTTP method.

response_encoding: Encoding for the response body. Use base64 for binary content.

timeout_ms: Request timeout in milliseconds.

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:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return await self._post(
path_template("/browsers/{id}/curl", id=id),
body=await async_maybe_transform(
{
"url": url,
"body": body,
"headers": headers,
"method": method,
"response_encoding": response_encoding,
"timeout_ms": timeout_ms,
},
browser_curl_params.BrowserCurlParams,
),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
cast_to=BrowserCurlResponse,
)

async def delete_by_id(
self,
id: str,
Expand Down Expand Up @@ -983,6 +1113,9 @@ def __init__(self, browsers: BrowsersResource) -> None:
browsers.delete, # pyright: ignore[reportDeprecated],
)
)
self.curl = to_raw_response_wrapper(
browsers.curl,
)
self.delete_by_id = to_raw_response_wrapper(
browsers.delete_by_id,
)
Expand Down Expand Up @@ -1041,6 +1174,9 @@ def __init__(self, browsers: AsyncBrowsersResource) -> None:
browsers.delete, # pyright: ignore[reportDeprecated],
)
)
self.curl = async_to_raw_response_wrapper(
browsers.curl,
)
self.delete_by_id = async_to_raw_response_wrapper(
browsers.delete_by_id,
)
Expand Down Expand Up @@ -1099,6 +1235,9 @@ def __init__(self, browsers: BrowsersResource) -> None:
browsers.delete, # pyright: ignore[reportDeprecated],
)
)
self.curl = to_streamed_response_wrapper(
browsers.curl,
)
self.delete_by_id = to_streamed_response_wrapper(
browsers.delete_by_id,
)
Expand Down Expand Up @@ -1157,6 +1296,9 @@ def __init__(self, browsers: AsyncBrowsersResource) -> None:
browsers.delete, # pyright: ignore[reportDeprecated],
)
)
self.curl = async_to_streamed_response_wrapper(
browsers.curl,
)
self.delete_by_id = async_to_streamed_response_wrapper(
browsers.delete_by_id,
)
Expand Down
2 changes: 2 additions & 0 deletions src/kernel/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from .browser_pool_ref import BrowserPoolRef as BrowserPoolRef
from .app_list_response import AppListResponse as AppListResponse
from .proxy_check_params import ProxyCheckParams as ProxyCheckParams
from .browser_curl_params import BrowserCurlParams as BrowserCurlParams
from .browser_list_params import BrowserListParams as BrowserListParams
from .browser_persistence import BrowserPersistence as BrowserPersistence
from .credential_provider import CredentialProvider as CredentialProvider
Expand All @@ -31,6 +32,7 @@
from .proxy_list_response import ProxyListResponse as ProxyListResponse
from .proxy_check_response import ProxyCheckResponse as ProxyCheckResponse
from .browser_create_params import BrowserCreateParams as BrowserCreateParams
from .browser_curl_response import BrowserCurlResponse as BrowserCurlResponse
from .browser_delete_params import BrowserDeleteParams as BrowserDeleteParams
from .browser_list_response import BrowserListResponse as BrowserListResponse
from .browser_update_params import BrowserUpdateParams as BrowserUpdateParams
Expand Down
28 changes: 28 additions & 0 deletions src/kernel/types/browser_curl_params.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

from __future__ import annotations

from typing import Dict
from typing_extensions import Literal, Required, TypedDict

__all__ = ["BrowserCurlParams"]


class BrowserCurlParams(TypedDict, total=False):
url: Required[str]
"""Target URL (must be http or https)."""

body: str
"""Request body (for POST/PUT/PATCH)."""

headers: Dict[str, str]
"""Custom headers merged with browser defaults."""

method: Literal["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]
"""HTTP method."""

response_encoding: Literal["utf8", "base64"]
"""Encoding for the response body. Use base64 for binary content."""

timeout_ms: int
"""Request timeout in milliseconds."""
23 changes: 23 additions & 0 deletions src/kernel/types/browser_curl_response.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

from typing import Dict, List

from .._models import BaseModel

__all__ = ["BrowserCurlResponse"]


class BrowserCurlResponse(BaseModel):
"""Structured response from the browser curl request."""

body: str
"""Response body (UTF-8 string or base64 depending on request)."""

duration_ms: int
"""Total request duration in milliseconds."""

headers: Dict[str, List[str]]
"""Response headers (multi-value)."""

status: int
"""HTTP status code from target."""
Loading