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