From a1054a6b72e79e0fffc88b1b46661683a8a953b8 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 26 May 2026 18:33:59 +0000
Subject: [PATCH 1/4] feat: Support telemetry enabled request config and fix
SDK metadata
---
.stats.yml | 6 ++--
api.md | 1 -
src/kernel/resources/browsers/browsers.py | 9 +++---
src/kernel/types/browser_create_params.py | 29 +++++++++++++++++--
src/kernel/types/browser_update_params.py | 29 +++++++++++++++++--
src/kernel/types/browsers/__init__.py | 3 --
.../browser_telemetry_request_config_param.py | 28 ------------------
7 files changed, 59 insertions(+), 46 deletions(-)
delete mode 100644 src/kernel/types/browsers/browser_telemetry_request_config_param.py
diff --git a/.stats.yml b/.stats.yml
index 47ab7c1..7fdb7bf 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 112
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel/kernel-e9c99662d29710f105847d461f8919e06f6aa2e43b0e1a6285d0b137643a7907.yml
-openapi_spec_hash: 4415cb4790c7a5ec892f4e3521217cb4
-config_hash: 27b38657d9a3b33328be930eeb319628
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel/kernel-0a186c486b56f555cab374ea5f2adbef2d718b5c9190a48c862f0fdf1232324f.yml
+openapi_spec_hash: fad386b8e8712e6639ed9689e9dfc070
+config_hash: 5dde8b5de321a7bb96f695a69eb21c23
diff --git a/api.md b/api.md
index 106e3cd..4785c2d 100644
--- a/api.md
+++ b/api.md
@@ -137,7 +137,6 @@ from kernel.types.browsers import (
BrowserTelemetryCategoryConfig,
BrowserTelemetryConfig,
BrowserTelemetryEvent,
- BrowserTelemetryRequestConfig,
TelemetryStreamResponse,
)
```
diff --git a/src/kernel/resources/browsers/browsers.py b/src/kernel/resources/browsers/browsers.py
index ad8087f..c020c28 100644
--- a/src/kernel/resources/browsers/browsers.py
+++ b/src/kernel/resources/browsers/browsers.py
@@ -99,7 +99,6 @@
from ...types.shared_params.browser_profile import BrowserProfile
from ...types.shared_params.browser_viewport import BrowserViewport
from ...types.shared_params.browser_extension import BrowserExtension
-from ...types.browsers.browser_telemetry_request_config_param import BrowserTelemetryRequestConfigParam
__all__ = ["BrowsersResource", "AsyncBrowsersResource"]
@@ -173,7 +172,7 @@ def create(
proxy_id: str | Omit = omit,
start_url: str | Omit = omit,
stealth: bool | Omit = omit,
- telemetry: Optional[BrowserTelemetryRequestConfigParam] | Omit = omit,
+ telemetry: Optional[browser_create_params.Telemetry] | 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.
@@ -327,7 +326,7 @@ def update(
disable_default_proxy: bool | Omit = omit,
profile: BrowserProfile | Omit = omit,
proxy_id: Optional[str] | Omit = omit,
- telemetry: Optional[BrowserTelemetryRequestConfigParam] | Omit = omit,
+ telemetry: Optional[browser_update_params.Telemetry] | Omit = omit,
viewport: browser_update_params.Viewport | 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.
@@ -720,7 +719,7 @@ async def create(
proxy_id: str | Omit = omit,
start_url: str | Omit = omit,
stealth: bool | Omit = omit,
- telemetry: Optional[BrowserTelemetryRequestConfigParam] | Omit = omit,
+ telemetry: Optional[browser_create_params.Telemetry] | 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.
@@ -874,7 +873,7 @@ async def update(
disable_default_proxy: bool | Omit = omit,
profile: BrowserProfile | Omit = omit,
proxy_id: Optional[str] | Omit = omit,
- telemetry: Optional[BrowserTelemetryRequestConfigParam] | Omit = omit,
+ telemetry: Optional[browser_update_params.Telemetry] | Omit = omit,
viewport: browser_update_params.Viewport | 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.
diff --git a/src/kernel/types/browser_create_params.py b/src/kernel/types/browser_create_params.py
index 82406a5..c94071c 100644
--- a/src/kernel/types/browser_create_params.py
+++ b/src/kernel/types/browser_create_params.py
@@ -8,9 +8,9 @@
from .shared_params.browser_profile import BrowserProfile
from .shared_params.browser_viewport import BrowserViewport
from .shared_params.browser_extension import BrowserExtension
-from .browsers.browser_telemetry_request_config_param import BrowserTelemetryRequestConfigParam
+from .browsers.browser_telemetry_categories_config_param import BrowserTelemetryCategoriesConfigParam
-__all__ = ["BrowserCreateParams"]
+__all__ = ["BrowserCreateParams", "Telemetry"]
class BrowserCreateParams(TypedDict, total=False):
@@ -75,7 +75,7 @@ class BrowserCreateParams(TypedDict, total=False):
mechanisms.
"""
- telemetry: Optional[BrowserTelemetryRequestConfigParam]
+ telemetry: Optional[Telemetry]
"""Telemetry configuration for the browser session.
Set enabled to true to start capture using VM defaults, or provide browser
@@ -108,3 +108,26 @@ class BrowserCreateParams(TypedDict, total=False):
based on the resolution (higher resolutions use lower refresh rates to keep
bandwidth reasonable).
"""
+
+
+class Telemetry(TypedDict, total=False):
+ """Telemetry configuration for the browser session.
+
+ Set enabled to true to start capture using VM defaults, or provide browser category settings. If omitted, null, set to an empty object ({}), set to enabled: false without browser category settings, or all four categories are explicitly disabled, capture is not started.
+ """
+
+ browser: BrowserTelemetryCategoriesConfigParam
+ """Per-category enable/disable flags.
+
+ If enabled is true and browser is omitted or empty, the VM default category set
+ is used. Explicitly disabling all four categories stops capture on update and
+ starts no capture on create.
+ """
+
+ enabled: bool
+ """Request shortcut for browser telemetry capture.
+
+ True enables capture using VM defaults unless browser category settings are
+ provided. False stops capture on update and starts no capture on create.
+ enabled=false cannot be combined with browser category settings.
+ """
diff --git a/src/kernel/types/browser_update_params.py b/src/kernel/types/browser_update_params.py
index c883089..837c178 100644
--- a/src/kernel/types/browser_update_params.py
+++ b/src/kernel/types/browser_update_params.py
@@ -7,9 +7,9 @@
from .shared_params.browser_profile import BrowserProfile
from .shared_params.browser_viewport import BrowserViewport
-from .browsers.browser_telemetry_request_config_param import BrowserTelemetryRequestConfigParam
+from .browsers.browser_telemetry_categories_config_param import BrowserTelemetryCategoriesConfigParam
-__all__ = ["BrowserUpdateParams", "Viewport"]
+__all__ = ["BrowserUpdateParams", "Telemetry", "Viewport"]
class BrowserUpdateParams(TypedDict, total=False):
@@ -31,7 +31,7 @@ class BrowserUpdateParams(TypedDict, total=False):
Omit to leave unchanged, set to empty string to remove proxy.
"""
- telemetry: Optional[BrowserTelemetryRequestConfigParam]
+ telemetry: Optional[Telemetry]
"""Telemetry configuration.
Omit, set to null, or set to an empty object ({}) to leave the existing
@@ -45,6 +45,29 @@ class BrowserUpdateParams(TypedDict, total=False):
"""Viewport configuration to apply to the browser session."""
+class Telemetry(TypedDict, total=False):
+ """Telemetry configuration.
+
+ Omit, set to null, or set to an empty object ({}) to leave the existing configuration unchanged. Set enabled to true to enable capture using VM defaults. Set enabled to false to stop capture. Provide browser category settings for per-category updates. Explicitly disabling all four categories also stops capture.
+ """
+
+ browser: BrowserTelemetryCategoriesConfigParam
+ """Per-category enable/disable flags.
+
+ If enabled is true and browser is omitted or empty, the VM default category set
+ is used. Explicitly disabling all four categories stops capture on update and
+ starts no capture on create.
+ """
+
+ enabled: bool
+ """Request shortcut for browser telemetry capture.
+
+ True enables capture using VM defaults unless browser category settings are
+ provided. False stops capture on update and starts no capture on create.
+ enabled=false cannot be combined with browser category settings.
+ """
+
+
class Viewport(BrowserViewport, total=False):
"""Viewport configuration to apply to the browser session."""
diff --git a/src/kernel/types/browsers/__init__.py b/src/kernel/types/browsers/__init__.py
index 619a59c..34f4fd6 100644
--- a/src/kernel/types/browsers/__init__.py
+++ b/src/kernel/types/browsers/__init__.py
@@ -82,9 +82,6 @@
from .browser_monitor_reconnect_failed_event import (
BrowserMonitorReconnectFailedEvent as BrowserMonitorReconnectFailedEvent,
)
-from .browser_telemetry_request_config_param import (
- BrowserTelemetryRequestConfigParam as BrowserTelemetryRequestConfigParam,
-)
from .browser_telemetry_category_config_param import (
BrowserTelemetryCategoryConfigParam as BrowserTelemetryCategoryConfigParam,
)
diff --git a/src/kernel/types/browsers/browser_telemetry_request_config_param.py b/src/kernel/types/browsers/browser_telemetry_request_config_param.py
deleted file mode 100644
index d901a74..0000000
--- a/src/kernel/types/browsers/browser_telemetry_request_config_param.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-from typing_extensions import TypedDict
-
-from .browser_telemetry_categories_config_param import BrowserTelemetryCategoriesConfigParam
-
-__all__ = ["BrowserTelemetryRequestConfigParam"]
-
-
-class BrowserTelemetryRequestConfigParam(TypedDict, total=False):
- """Telemetry request configuration for a browser session."""
-
- browser: BrowserTelemetryCategoriesConfigParam
- """Per-category enable/disable flags.
-
- If enabled is true and browser is omitted or empty, the VM default category set
- is used. Explicitly disabling all four categories stops capture on update and
- starts no capture on create.
- """
-
- enabled: bool
- """Request shortcut for browser telemetry capture.
-
- True enables capture using VM defaults. False stops capture on update and starts
- no capture on create. Cannot be combined with browser category settings.
- """
From 41b3aa243c39af3614154af0059b0299c4e41664 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 27 May 2026 15:07:50 +0000
Subject: [PATCH 2/4] feat: [codex] Expose API keys in SDK config
---
.stats.yml | 6 +-
api.md | 16 +
src/kernel/_client.py | 44 ++
src/kernel/resources/__init__.py | 14 +
src/kernel/resources/api_keys.py | 557 ++++++++++++++++++++++
src/kernel/types/__init__.py | 5 +
src/kernel/types/api_key.py | 47 ++
src/kernel/types/api_key_create_params.py | 19 +
src/kernel/types/api_key_list_params.py | 15 +
src/kernel/types/api_key_update_params.py | 12 +
src/kernel/types/created_api_key.py | 12 +
tests/api_resources/test_api_keys.py | 447 +++++++++++++++++
12 files changed, 1191 insertions(+), 3 deletions(-)
create mode 100644 src/kernel/resources/api_keys.py
create mode 100644 src/kernel/types/api_key.py
create mode 100644 src/kernel/types/api_key_create_params.py
create mode 100644 src/kernel/types/api_key_list_params.py
create mode 100644 src/kernel/types/api_key_update_params.py
create mode 100644 src/kernel/types/created_api_key.py
create mode 100644 tests/api_resources/test_api_keys.py
diff --git a/.stats.yml b/.stats.yml
index 7fdb7bf..e86e334 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 112
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel/kernel-0a186c486b56f555cab374ea5f2adbef2d718b5c9190a48c862f0fdf1232324f.yml
+configured_endpoints: 117
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel/kernel-54d9016df9730e65881df9f694df4f33620b0c032f177849d36ccc426cd42fa8.yml
openapi_spec_hash: fad386b8e8712e6639ed9689e9dfc070
-config_hash: 5dde8b5de321a7bb96f695a69eb21c23
+config_hash: 58396d6c1fafaf72b26596b9117edf48
diff --git a/api.md b/api.md
index 4785c2d..1dc6fec 100644
--- a/api.md
+++ b/api.md
@@ -414,6 +414,22 @@ Methods:
- client.projects.limits.retrieve(id) -> ProjectLimits
- client.projects.limits.update(id, \*\*params) -> ProjectLimits
+# APIKeys
+
+Types:
+
+```python
+from kernel.types import APIKey, CreateAPIKeyRequest, CreatedAPIKey, UpdateAPIKeyRequest
+```
+
+Methods:
+
+- client.api_keys.create(\*\*params) -> CreatedAPIKey
+- client.api_keys.retrieve(id) -> APIKey
+- client.api_keys.update(id, \*\*params) -> APIKey
+- client.api_keys.list(\*\*params) -> SyncOffsetPagination[APIKey]
+- client.api_keys.delete(id) -> None
+
# CredentialProviders
Types:
diff --git a/src/kernel/_client.py b/src/kernel/_client.py
index 6c3e3aa..537d159 100644
--- a/src/kernel/_client.py
+++ b/src/kernel/_client.py
@@ -50,6 +50,7 @@
apps,
auth,
proxies,
+ api_keys,
browsers,
profiles,
projects,
@@ -62,6 +63,7 @@
)
from .resources.apps import AppsResource, AsyncAppsResource
from .resources.proxies import ProxiesResource, AsyncProxiesResource
+ from .resources.api_keys import APIKeysResource, AsyncAPIKeysResource
from .resources.profiles import ProfilesResource, AsyncProfilesResource
from .resources.auth.auth import AuthResource, AsyncAuthResource
from .resources.extensions import ExtensionsResource, AsyncExtensionsResource
@@ -260,6 +262,13 @@ def projects(self) -> ProjectsResource:
return ProjectsResource(self)
+ @cached_property
+ def api_keys(self) -> APIKeysResource:
+ """Create and manage API keys for organization and project-scoped access."""
+ from .resources.api_keys import APIKeysResource
+
+ return APIKeysResource(self)
+
@cached_property
def credential_providers(self) -> CredentialProvidersResource:
"""Configure external credential providers like 1Password."""
@@ -584,6 +593,13 @@ def projects(self) -> AsyncProjectsResource:
return AsyncProjectsResource(self)
+ @cached_property
+ def api_keys(self) -> AsyncAPIKeysResource:
+ """Create and manage API keys for organization and project-scoped access."""
+ from .resources.api_keys import AsyncAPIKeysResource
+
+ return AsyncAPIKeysResource(self)
+
@cached_property
def credential_providers(self) -> AsyncCredentialProvidersResource:
"""Configure external credential providers like 1Password."""
@@ -821,6 +837,13 @@ def projects(self) -> projects.ProjectsResourceWithRawResponse:
return ProjectsResourceWithRawResponse(self._client.projects)
+ @cached_property
+ def api_keys(self) -> api_keys.APIKeysResourceWithRawResponse:
+ """Create and manage API keys for organization and project-scoped access."""
+ from .resources.api_keys import APIKeysResourceWithRawResponse
+
+ return APIKeysResourceWithRawResponse(self._client.api_keys)
+
@cached_property
def credential_providers(self) -> credential_providers.CredentialProvidersResourceWithRawResponse:
"""Configure external credential providers like 1Password."""
@@ -911,6 +934,13 @@ def projects(self) -> projects.AsyncProjectsResourceWithRawResponse:
return AsyncProjectsResourceWithRawResponse(self._client.projects)
+ @cached_property
+ def api_keys(self) -> api_keys.AsyncAPIKeysResourceWithRawResponse:
+ """Create and manage API keys for organization and project-scoped access."""
+ from .resources.api_keys import AsyncAPIKeysResourceWithRawResponse
+
+ return AsyncAPIKeysResourceWithRawResponse(self._client.api_keys)
+
@cached_property
def credential_providers(self) -> credential_providers.AsyncCredentialProvidersResourceWithRawResponse:
"""Configure external credential providers like 1Password."""
@@ -1001,6 +1031,13 @@ def projects(self) -> projects.ProjectsResourceWithStreamingResponse:
return ProjectsResourceWithStreamingResponse(self._client.projects)
+ @cached_property
+ def api_keys(self) -> api_keys.APIKeysResourceWithStreamingResponse:
+ """Create and manage API keys for organization and project-scoped access."""
+ from .resources.api_keys import APIKeysResourceWithStreamingResponse
+
+ return APIKeysResourceWithStreamingResponse(self._client.api_keys)
+
@cached_property
def credential_providers(self) -> credential_providers.CredentialProvidersResourceWithStreamingResponse:
"""Configure external credential providers like 1Password."""
@@ -1091,6 +1128,13 @@ def projects(self) -> projects.AsyncProjectsResourceWithStreamingResponse:
return AsyncProjectsResourceWithStreamingResponse(self._client.projects)
+ @cached_property
+ def api_keys(self) -> api_keys.AsyncAPIKeysResourceWithStreamingResponse:
+ """Create and manage API keys for organization and project-scoped access."""
+ from .resources.api_keys import AsyncAPIKeysResourceWithStreamingResponse
+
+ return AsyncAPIKeysResourceWithStreamingResponse(self._client.api_keys)
+
@cached_property
def credential_providers(self) -> credential_providers.AsyncCredentialProvidersResourceWithStreamingResponse:
"""Configure external credential providers like 1Password."""
diff --git a/src/kernel/resources/__init__.py b/src/kernel/resources/__init__.py
index f078a03..64b9833 100644
--- a/src/kernel/resources/__init__.py
+++ b/src/kernel/resources/__init__.py
@@ -24,6 +24,14 @@
ProxiesResourceWithStreamingResponse,
AsyncProxiesResourceWithStreamingResponse,
)
+from .api_keys import (
+ APIKeysResource,
+ AsyncAPIKeysResource,
+ APIKeysResourceWithRawResponse,
+ AsyncAPIKeysResourceWithRawResponse,
+ APIKeysResourceWithStreamingResponse,
+ AsyncAPIKeysResourceWithStreamingResponse,
+)
from .browsers import (
BrowsersResource,
AsyncBrowsersResource,
@@ -164,6 +172,12 @@
"AsyncProjectsResourceWithRawResponse",
"ProjectsResourceWithStreamingResponse",
"AsyncProjectsResourceWithStreamingResponse",
+ "APIKeysResource",
+ "AsyncAPIKeysResource",
+ "APIKeysResourceWithRawResponse",
+ "AsyncAPIKeysResourceWithRawResponse",
+ "APIKeysResourceWithStreamingResponse",
+ "AsyncAPIKeysResourceWithStreamingResponse",
"CredentialProvidersResource",
"AsyncCredentialProvidersResource",
"CredentialProvidersResourceWithRawResponse",
diff --git a/src/kernel/resources/api_keys.py b/src/kernel/resources/api_keys.py
new file mode 100644
index 0000000..aae9679
--- /dev/null
+++ b/src/kernel/resources/api_keys.py
@@ -0,0 +1,557 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+
+import httpx
+
+from ..types import api_key_list_params, api_key_create_params, api_key_update_params
+from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
+from .._utils import path_template, 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 ..pagination import SyncOffsetPagination, AsyncOffsetPagination
+from .._base_client import AsyncPaginator, make_request_options
+from ..types.api_key import APIKey
+from ..types.created_api_key import CreatedAPIKey
+
+__all__ = ["APIKeysResource", "AsyncAPIKeysResource"]
+
+
+class APIKeysResource(SyncAPIResource):
+ """Create and manage API keys for organization and project-scoped access."""
+
+ @cached_property
+ def with_raw_response(self) -> APIKeysResourceWithRawResponse:
+ """
+ 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/kernel/kernel-python-sdk#accessing-raw-response-data-eg-headers
+ """
+ return APIKeysResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> APIKeysResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/kernel/kernel-python-sdk#with_streaming_response
+ """
+ return APIKeysResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ name: str,
+ days_to_expire: Optional[int] | Omit = omit,
+ project_id: Optional[str] | 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,
+ ) -> CreatedAPIKey:
+ """
+ Create a new API key within the authenticated organization.
+
+ Args:
+ name: API key name (1-255 characters)
+
+ days_to_expire: Number of days until expiry, up to 3650. Use null for never.
+
+ project_id: Unique project identifier
+
+ 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(
+ "/org/api_keys",
+ body=maybe_transform(
+ {
+ "name": name,
+ "days_to_expire": days_to_expire,
+ "project_id": project_id,
+ },
+ api_key_create_params.APIKeyCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=CreatedAPIKey,
+ )
+
+ def retrieve(
+ self,
+ id: 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,
+ ) -> APIKey:
+ """Retrieve an API key by ID for the authenticated organization.
+
+ API keys are
+ masked.
+
+ 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:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._get(
+ path_template("/org/api_keys/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=APIKey,
+ )
+
+ def update(
+ self,
+ id: str,
+ *,
+ 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,
+ ) -> APIKey:
+ """
+ Update an API key's name.
+
+ Args:
+ name: New API key name
+
+ 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._patch(
+ path_template("/org/api_keys/{id}", id=id),
+ body=maybe_transform({"name": name}, api_key_update_params.APIKeyUpdateParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=APIKey,
+ )
+
+ def list(
+ self,
+ *,
+ 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,
+ ) -> SyncOffsetPagination[APIKey]:
+ """List API keys for the authenticated organization.
+
+ API keys are masked.
+
+ Args:
+ limit: Maximum number of results to return
+
+ offset: Number of results to skip
+
+ 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(
+ "/org/api_keys",
+ page=SyncOffsetPagination[APIKey],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "limit": limit,
+ "offset": offset,
+ },
+ api_key_list_params.APIKeyListParams,
+ ),
+ ),
+ model=APIKey,
+ )
+
+ def delete(
+ self,
+ id: 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:
+ """
+ Delete an API key.
+
+ 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:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._delete(
+ path_template("/org/api_keys/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class AsyncAPIKeysResource(AsyncAPIResource):
+ """Create and manage API keys for organization and project-scoped access."""
+
+ @cached_property
+ def with_raw_response(self) -> AsyncAPIKeysResourceWithRawResponse:
+ """
+ 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/kernel/kernel-python-sdk#accessing-raw-response-data-eg-headers
+ """
+ return AsyncAPIKeysResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncAPIKeysResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/kernel/kernel-python-sdk#with_streaming_response
+ """
+ return AsyncAPIKeysResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ name: str,
+ days_to_expire: Optional[int] | Omit = omit,
+ project_id: Optional[str] | 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,
+ ) -> CreatedAPIKey:
+ """
+ Create a new API key within the authenticated organization.
+
+ Args:
+ name: API key name (1-255 characters)
+
+ days_to_expire: Number of days until expiry, up to 3650. Use null for never.
+
+ project_id: Unique project identifier
+
+ 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(
+ "/org/api_keys",
+ body=await async_maybe_transform(
+ {
+ "name": name,
+ "days_to_expire": days_to_expire,
+ "project_id": project_id,
+ },
+ api_key_create_params.APIKeyCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=CreatedAPIKey,
+ )
+
+ async def retrieve(
+ self,
+ id: 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,
+ ) -> APIKey:
+ """Retrieve an API key by ID for the authenticated organization.
+
+ API keys are
+ masked.
+
+ 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:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._get(
+ path_template("/org/api_keys/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=APIKey,
+ )
+
+ async def update(
+ self,
+ id: str,
+ *,
+ 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,
+ ) -> APIKey:
+ """
+ Update an API key's name.
+
+ Args:
+ name: New API key name
+
+ 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._patch(
+ path_template("/org/api_keys/{id}", id=id),
+ body=await async_maybe_transform({"name": name}, api_key_update_params.APIKeyUpdateParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=APIKey,
+ )
+
+ def list(
+ self,
+ *,
+ 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,
+ ) -> AsyncPaginator[APIKey, AsyncOffsetPagination[APIKey]]:
+ """List API keys for the authenticated organization.
+
+ API keys are masked.
+
+ Args:
+ limit: Maximum number of results to return
+
+ offset: Number of results to skip
+
+ 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(
+ "/org/api_keys",
+ page=AsyncOffsetPagination[APIKey],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "limit": limit,
+ "offset": offset,
+ },
+ api_key_list_params.APIKeyListParams,
+ ),
+ ),
+ model=APIKey,
+ )
+
+ async def delete(
+ self,
+ id: 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:
+ """
+ Delete an API key.
+
+ 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:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._delete(
+ path_template("/org/api_keys/{id}", id=id),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class APIKeysResourceWithRawResponse:
+ def __init__(self, api_keys: APIKeysResource) -> None:
+ self._api_keys = api_keys
+
+ self.create = to_raw_response_wrapper(
+ api_keys.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ api_keys.retrieve,
+ )
+ self.update = to_raw_response_wrapper(
+ api_keys.update,
+ )
+ self.list = to_raw_response_wrapper(
+ api_keys.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ api_keys.delete,
+ )
+
+
+class AsyncAPIKeysResourceWithRawResponse:
+ def __init__(self, api_keys: AsyncAPIKeysResource) -> None:
+ self._api_keys = api_keys
+
+ self.create = async_to_raw_response_wrapper(
+ api_keys.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ api_keys.retrieve,
+ )
+ self.update = async_to_raw_response_wrapper(
+ api_keys.update,
+ )
+ self.list = async_to_raw_response_wrapper(
+ api_keys.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ api_keys.delete,
+ )
+
+
+class APIKeysResourceWithStreamingResponse:
+ def __init__(self, api_keys: APIKeysResource) -> None:
+ self._api_keys = api_keys
+
+ self.create = to_streamed_response_wrapper(
+ api_keys.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ api_keys.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ api_keys.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ api_keys.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ api_keys.delete,
+ )
+
+
+class AsyncAPIKeysResourceWithStreamingResponse:
+ def __init__(self, api_keys: AsyncAPIKeysResource) -> None:
+ self._api_keys = api_keys
+
+ self.create = async_to_streamed_response_wrapper(
+ api_keys.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ api_keys.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ api_keys.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ api_keys.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ api_keys.delete,
+ )
diff --git a/src/kernel/types/__init__.py b/src/kernel/types/__init__.py
index 49346e2..fb6fd58 100644
--- a/src/kernel/types/__init__.py
+++ b/src/kernel/types/__init__.py
@@ -15,15 +15,18 @@
BrowserViewport as BrowserViewport,
BrowserExtension as BrowserExtension,
)
+from .api_key import APIKey as APIKey
from .profile import Profile as Profile
from .project import Project as Project
from .credential import Credential as Credential
from .browser_pool import BrowserPool as BrowserPool
from .browser_usage import BrowserUsage as BrowserUsage
from .app_list_params import AppListParams as AppListParams
+from .created_api_key import CreatedAPIKey as CreatedAPIKey
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 .api_key_list_params import APIKeyListParams as APIKeyListParams
from .browser_curl_params import BrowserCurlParams as BrowserCurlParams
from .browser_list_params import BrowserListParams as BrowserListParams
from .credential_provider import CredentialProvider as CredentialProvider
@@ -32,6 +35,8 @@
from .proxy_create_params import ProxyCreateParams as ProxyCreateParams
from .proxy_list_response import ProxyListResponse as ProxyListResponse
from .proxy_check_response import ProxyCheckResponse as ProxyCheckResponse
+from .api_key_create_params import APIKeyCreateParams as APIKeyCreateParams
+from .api_key_update_params import APIKeyUpdateParams as APIKeyUpdateParams
from .browser_create_params import BrowserCreateParams as BrowserCreateParams
from .browser_curl_response import BrowserCurlResponse as BrowserCurlResponse
from .browser_list_response import BrowserListResponse as BrowserListResponse
diff --git a/src/kernel/types/api_key.py b/src/kernel/types/api_key.py
new file mode 100644
index 0000000..6df577f
--- /dev/null
+++ b/src/kernel/types/api_key.py
@@ -0,0 +1,47 @@
+# 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
+
+__all__ = ["APIKey", "CreatedBy"]
+
+
+class CreatedBy(BaseModel):
+ id: str
+ """Kernel user ID of the creator."""
+
+ email: str
+ """Email address of the creator."""
+
+ name: Optional[str] = None
+ """Display name of the creator, if available."""
+
+
+class APIKey(BaseModel):
+ id: str
+ """Unique API key identifier"""
+
+ created_at: datetime
+ """When the API key was created"""
+
+ created_by: CreatedBy
+
+ expires_at: Optional[datetime] = None
+ """When the API key expires"""
+
+ masked_key: str
+ """Masked version of the API key"""
+
+ name: str
+ """API key name"""
+
+ project_id: Optional[str] = None
+ """Project identifier for project-scoped API keys. Null means org-wide."""
+
+ project_name: Optional[str] = None
+ """Project name for project-scoped API keys.
+
+ Null means the key is org-wide or the project name is unavailable.
+ """
diff --git a/src/kernel/types/api_key_create_params.py b/src/kernel/types/api_key_create_params.py
new file mode 100644
index 0000000..7705108
--- /dev/null
+++ b/src/kernel/types/api_key_create_params.py
@@ -0,0 +1,19 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Required, TypedDict
+
+__all__ = ["APIKeyCreateParams"]
+
+
+class APIKeyCreateParams(TypedDict, total=False):
+ name: Required[str]
+ """API key name (1-255 characters)"""
+
+ days_to_expire: Optional[int]
+ """Number of days until expiry, up to 3650. Use null for never."""
+
+ project_id: Optional[str]
+ """Unique project identifier"""
diff --git a/src/kernel/types/api_key_list_params.py b/src/kernel/types/api_key_list_params.py
new file mode 100644
index 0000000..e7d807f
--- /dev/null
+++ b/src/kernel/types/api_key_list_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__ = ["APIKeyListParams"]
+
+
+class APIKeyListParams(TypedDict, total=False):
+ limit: int
+ """Maximum number of results to return"""
+
+ offset: int
+ """Number of results to skip"""
diff --git a/src/kernel/types/api_key_update_params.py b/src/kernel/types/api_key_update_params.py
new file mode 100644
index 0000000..2e91760
--- /dev/null
+++ b/src/kernel/types/api_key_update_params.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["APIKeyUpdateParams"]
+
+
+class APIKeyUpdateParams(TypedDict, total=False):
+ name: Required[str]
+ """New API key name"""
diff --git a/src/kernel/types/created_api_key.py b/src/kernel/types/created_api_key.py
new file mode 100644
index 0000000..674943d
--- /dev/null
+++ b/src/kernel/types/created_api_key.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .api_key import APIKey
+
+__all__ = ["CreatedAPIKey"]
+
+
+class CreatedAPIKey(APIKey):
+ """API key returned immediately after creation. Includes the plaintext key once."""
+
+ key: str
+ """Plaintext API key. Only returned once when the key is created."""
diff --git a/tests/api_resources/test_api_keys.py b/tests/api_resources/test_api_keys.py
new file mode 100644
index 0000000..da854b2
--- /dev/null
+++ b/tests/api_resources/test_api_keys.py
@@ -0,0 +1,447 @@
+# 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 APIKey, CreatedAPIKey
+from kernel.pagination import SyncOffsetPagination, AsyncOffsetPagination
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestAPIKeys:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_create(self, client: Kernel) -> None:
+ api_key = client.api_keys.create(
+ name="staging",
+ )
+ assert_matches_type(CreatedAPIKey, api_key, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_create_with_all_params(self, client: Kernel) -> None:
+ api_key = client.api_keys.create(
+ name="staging",
+ days_to_expire=30,
+ project_id="proj_abc123",
+ )
+ assert_matches_type(CreatedAPIKey, api_key, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_create(self, client: Kernel) -> None:
+ response = client.api_keys.with_raw_response.create(
+ name="staging",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = response.parse()
+ assert_matches_type(CreatedAPIKey, api_key, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_create(self, client: Kernel) -> None:
+ with client.api_keys.with_streaming_response.create(
+ name="staging",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = response.parse()
+ assert_matches_type(CreatedAPIKey, api_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_retrieve(self, client: Kernel) -> None:
+ api_key = client.api_keys.retrieve(
+ "id",
+ )
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_retrieve(self, client: Kernel) -> None:
+ response = client.api_keys.with_raw_response.retrieve(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = response.parse()
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Kernel) -> None:
+ with client.api_keys.with_streaming_response.retrieve(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = response.parse()
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server 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` but received ''"):
+ client.api_keys.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_update(self, client: Kernel) -> None:
+ api_key = client.api_keys.update(
+ id="id",
+ name="new-api-name",
+ )
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_update(self, client: Kernel) -> None:
+ response = client.api_keys.with_raw_response.update(
+ id="id",
+ name="new-api-name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = response.parse()
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_update(self, client: Kernel) -> None:
+ with client.api_keys.with_streaming_response.update(
+ id="id",
+ name="new-api-name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = response.parse()
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server 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` but received ''"):
+ client.api_keys.with_raw_response.update(
+ id="",
+ name="new-api-name",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list(self, client: Kernel) -> None:
+ api_key = client.api_keys.list()
+ assert_matches_type(SyncOffsetPagination[APIKey], api_key, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_list_with_all_params(self, client: Kernel) -> None:
+ api_key = client.api_keys.list(
+ limit=100,
+ offset=0,
+ )
+ assert_matches_type(SyncOffsetPagination[APIKey], api_key, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_list(self, client: Kernel) -> None:
+ response = client.api_keys.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = response.parse()
+ assert_matches_type(SyncOffsetPagination[APIKey], api_key, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_list(self, client: Kernel) -> None:
+ with client.api_keys.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = response.parse()
+ assert_matches_type(SyncOffsetPagination[APIKey], api_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_delete(self, client: Kernel) -> None:
+ api_key = client.api_keys.delete(
+ "id",
+ )
+ assert api_key is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_delete(self, client: Kernel) -> None:
+ response = client.api_keys.with_raw_response.delete(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = response.parse()
+ assert api_key is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_delete(self, client: Kernel) -> None:
+ with client.api_keys.with_streaming_response.delete(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = response.parse()
+ assert api_key is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server 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` but received ''"):
+ client.api_keys.with_raw_response.delete(
+ "",
+ )
+
+
+class TestAsyncAPIKeys:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_create(self, async_client: AsyncKernel) -> None:
+ api_key = await async_client.api_keys.create(
+ name="staging",
+ )
+ assert_matches_type(CreatedAPIKey, api_key, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncKernel) -> None:
+ api_key = await async_client.api_keys.create(
+ name="staging",
+ days_to_expire=30,
+ project_id="proj_abc123",
+ )
+ assert_matches_type(CreatedAPIKey, api_key, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncKernel) -> None:
+ response = await async_client.api_keys.with_raw_response.create(
+ name="staging",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = await response.parse()
+ assert_matches_type(CreatedAPIKey, api_key, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncKernel) -> None:
+ async with async_client.api_keys.with_streaming_response.create(
+ name="staging",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = await response.parse()
+ assert_matches_type(CreatedAPIKey, api_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncKernel) -> None:
+ api_key = await async_client.api_keys.retrieve(
+ "id",
+ )
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncKernel) -> None:
+ response = await async_client.api_keys.with_raw_response.retrieve(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = await response.parse()
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncKernel) -> None:
+ async with async_client.api_keys.with_streaming_response.retrieve(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = await response.parse()
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server 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` but received ''"):
+ await async_client.api_keys.with_raw_response.retrieve(
+ "",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_update(self, async_client: AsyncKernel) -> None:
+ api_key = await async_client.api_keys.update(
+ id="id",
+ name="new-api-name",
+ )
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncKernel) -> None:
+ response = await async_client.api_keys.with_raw_response.update(
+ id="id",
+ name="new-api-name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = await response.parse()
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncKernel) -> None:
+ async with async_client.api_keys.with_streaming_response.update(
+ id="id",
+ name="new-api-name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = await response.parse()
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server 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` but received ''"):
+ await async_client.api_keys.with_raw_response.update(
+ id="",
+ name="new-api-name",
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list(self, async_client: AsyncKernel) -> None:
+ api_key = await async_client.api_keys.list()
+ assert_matches_type(AsyncOffsetPagination[APIKey], api_key, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncKernel) -> None:
+ api_key = await async_client.api_keys.list(
+ limit=100,
+ offset=0,
+ )
+ assert_matches_type(AsyncOffsetPagination[APIKey], api_key, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncKernel) -> None:
+ response = await async_client.api_keys.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = await response.parse()
+ assert_matches_type(AsyncOffsetPagination[APIKey], api_key, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncKernel) -> None:
+ async with async_client.api_keys.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = await response.parse()
+ assert_matches_type(AsyncOffsetPagination[APIKey], api_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncKernel) -> None:
+ api_key = await async_client.api_keys.delete(
+ "id",
+ )
+ assert api_key is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncKernel) -> None:
+ response = await async_client.api_keys.with_raw_response.delete(
+ "id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = await response.parse()
+ assert api_key is None
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncKernel) -> None:
+ async with async_client.api_keys.with_streaming_response.delete(
+ "id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = await response.parse()
+ assert api_key is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Mock server 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` but received ''"):
+ await async_client.api_keys.with_raw_response.delete(
+ "",
+ )
From 3ced474265441099b9fd26f26050ec799e804fac Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 27 May 2026 17:08:51 +0000
Subject: [PATCH 3/4] feat: Fix API key request model SDK metadata
---
.stats.yml | 4 ++--
api.md | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index e86e334..3cfc09d 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 117
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel/kernel-54d9016df9730e65881df9f694df4f33620b0c032f177849d36ccc426cd42fa8.yml
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel/kernel-3b34d85c005a4058ac1faaea092615af577d12cee6e420f102de57339251672d.yml
openapi_spec_hash: fad386b8e8712e6639ed9689e9dfc070
-config_hash: 58396d6c1fafaf72b26596b9117edf48
+config_hash: 0f222358f24700d1811c5d27078a3849
diff --git a/api.md b/api.md
index 1dc6fec..c9e1b29 100644
--- a/api.md
+++ b/api.md
@@ -419,7 +419,7 @@ Methods:
Types:
```python
-from kernel.types import APIKey, CreateAPIKeyRequest, CreatedAPIKey, UpdateAPIKeyRequest
+from kernel.types import APIKey, CreatedAPIKey
```
Methods:
From 57ae50c41ef33593fd2428b68c4f8931c04695a8 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 27 May 2026 17:09:23 +0000
Subject: [PATCH 4/4] release: 0.58.0
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 10 ++++++++++
pyproject.toml | 2 +-
src/kernel/_version.py | 2 +-
4 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 2afb750..2fbefb9 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.57.0"
+ ".": "0.58.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4df91e6..ba1969b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,15 @@
# Changelog
+## 0.58.0 (2026-05-27)
+
+Full Changelog: [v0.57.0...v0.58.0](https://github.com/kernel/kernel-python-sdk/compare/v0.57.0...v0.58.0)
+
+### Features
+
+* [codex] Expose API keys in SDK config ([41b3aa2](https://github.com/kernel/kernel-python-sdk/commit/41b3aa243c39af3614154af0059b0299c4e41664))
+* Fix API key request model SDK metadata ([3ced474](https://github.com/kernel/kernel-python-sdk/commit/3ced474265441099b9fd26f26050ec799e804fac))
+* Support telemetry enabled request config and fix SDK metadata ([a1054a6](https://github.com/kernel/kernel-python-sdk/commit/a1054a6b72e79e0fffc88b1b46661683a8a953b8))
+
## 0.57.0 (2026-05-26)
Full Changelog: [v0.57.0...v0.57.0](https://github.com/kernel/kernel-python-sdk/compare/v0.57.0...v0.57.0)
diff --git a/pyproject.toml b/pyproject.toml
index 6956b28..3ff0ba8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "kernel"
-version = "0.57.0"
+version = "0.58.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 fb252c5..9422de8 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.57.0" # x-release-please-version
+__version__ = "0.58.0" # x-release-please-version