diff --git a/.release-please-manifest.json b/.release-please-manifest.json index cadbcc6f..f3343e21 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.124.0" + ".": "0.125.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c786557b..fa87bc60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.125.0](https://github.com/kernel/kernel-python-sdk/compare/v0.124.0...v0.125.0) (2026-10-13) + + +### Features + +* chore(stlc): seal custom-code tracking files ([a77f648](https://github.com/kernel/kernel-python-sdk/commit/a77f648fb68c7b4e6f93826238e9ac21ab39a4dd)) + ## [0.124.0](https://github.com/kernel/kernel-python-sdk/compare/v0.123.0...v0.124.0) (2026-10-13) diff --git a/pyproject.toml b/pyproject.toml index d86abd33..a9a2b552 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "kernel" -version = "0.124.0" +version = "0.125.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 e084d175..7bf40e5e 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.124.0" # x-release-please-version +__version__ = "0.125.0" # x-release-please-version diff --git a/src/kernel/resources/api_keys.py b/src/kernel/resources/api_keys.py index 8cc1ed53..52e6e6f9 100644 --- a/src/kernel/resources/api_keys.py +++ b/src/kernel/resources/api_keys.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import Optional +from typing import Union, Optional +from datetime import datetime from typing_extensions import Literal import httpx @@ -59,6 +60,8 @@ def create( *, name: str, days_to_expire: Optional[int] | Omit = omit, + expires_at: Union[str, datetime, None] | Omit = omit, + policy: api_key_create_params.Policy | 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. @@ -67,13 +70,22 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> CreatedAPIKey: - """ - Create a new API key within the authenticated organization. + """Create a new API key. + + API keys may be scoped by project and may include a + policy. Args: name: Label for the API key (1-255 characters). API keys are not addressable by name. - days_to_expire: Number of days until expiry, up to 3650. Use null for never. + days_to_expire: Number of days until expiry, up to 3650. Use null for never. Cannot be combined + with expires_at. + + expires_at: Exact expiry time. Must be in the future and no later than the issuing + credential's own expiry. Cannot be combined with days_to_expire. + + policy: Issue a scoped key that may only perform the listed actions on the listed + resources. project_id: Unique project identifier @@ -91,6 +103,8 @@ def create( { "name": name, "days_to_expire": days_to_expire, + "expires_at": expires_at, + "policy": policy, "project_id": project_id, }, api_key_create_params.APIKeyCreateParams, @@ -377,6 +391,8 @@ async def create( *, name: str, days_to_expire: Optional[int] | Omit = omit, + expires_at: Union[str, datetime, None] | Omit = omit, + policy: api_key_create_params.Policy | 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. @@ -385,13 +401,22 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> CreatedAPIKey: - """ - Create a new API key within the authenticated organization. + """Create a new API key. + + API keys may be scoped by project and may include a + policy. Args: name: Label for the API key (1-255 characters). API keys are not addressable by name. - days_to_expire: Number of days until expiry, up to 3650. Use null for never. + days_to_expire: Number of days until expiry, up to 3650. Use null for never. Cannot be combined + with expires_at. + + expires_at: Exact expiry time. Must be in the future and no later than the issuing + credential's own expiry. Cannot be combined with days_to_expire. + + policy: Issue a scoped key that may only perform the listed actions on the listed + resources. project_id: Unique project identifier @@ -409,6 +434,8 @@ async def create( { "name": name, "days_to_expire": days_to_expire, + "expires_at": expires_at, + "policy": policy, "project_id": project_id, }, api_key_create_params.APIKeyCreateParams, diff --git a/src/kernel/types/api_key.py b/src/kernel/types/api_key.py index b0d6eef2..5dd0b02c 100644 --- a/src/kernel/types/api_key.py +++ b/src/kernel/types/api_key.py @@ -1,11 +1,11 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import List, Optional from datetime import datetime from .._models import BaseModel -__all__ = ["APIKey", "CreatedBy"] +__all__ = ["APIKey", "CreatedBy", "Policy", "PolicyStatement"] class CreatedBy(BaseModel): @@ -19,6 +19,27 @@ class CreatedBy(BaseModel): """Display name of the creator, if available.""" +class PolicyStatement(BaseModel): + action: str + """Permitted action, such as browsers:use or vaults:read.""" + + resource: str + """Resource the action applies to, in the format projects/{project_id}/{kind}/{id}. + + Wildcards are supported. + """ + + +class Policy(BaseModel): + """Permissions granted to a scoped API key. + + Absent for an ordinary key, which keeps unrestricted access within its organization or project. + """ + + statements: List[PolicyStatement] + """Allow statements. Every action and resource not listed is denied.""" + + class APIKey(BaseModel): id: str """Unique API key identifier""" @@ -55,3 +76,10 @@ class APIKey(BaseModel): Null means the key is org-wide or the project name is unavailable. """ + + policy: Optional[Policy] = None + """Permissions granted to a scoped API key. + + Absent for an ordinary key, which keeps unrestricted access within its + organization or project. + """ diff --git a/src/kernel/types/api_key_create_params.py b/src/kernel/types/api_key_create_params.py index 2909510e..86d0ccf3 100644 --- a/src/kernel/types/api_key_create_params.py +++ b/src/kernel/types/api_key_create_params.py @@ -2,10 +2,13 @@ from __future__ import annotations -from typing import Optional -from typing_extensions import Required, TypedDict +from typing import Union, Iterable, Optional +from datetime import datetime +from typing_extensions import Required, Annotated, TypedDict -__all__ = ["APIKeyCreateParams"] +from .._utils import PropertyInfo + +__all__ = ["APIKeyCreateParams", "Policy", "PolicyStatement"] class APIKeyCreateParams(TypedDict, total=False): @@ -13,7 +16,43 @@ class APIKeyCreateParams(TypedDict, total=False): """Label for the API key (1-255 characters). API keys are not addressable by name.""" days_to_expire: Optional[int] - """Number of days until expiry, up to 3650. Use null for never.""" + """Number of days until expiry, up to 3650. + + Use null for never. Cannot be combined with expires_at. + """ + + expires_at: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + """Exact expiry time. + + Must be in the future and no later than the issuing credential's own expiry. + Cannot be combined with days_to_expire. + """ + + policy: Policy + """ + Issue a scoped key that may only perform the listed actions on the listed + resources. + """ project_id: Optional[str] """Unique project identifier""" + + +class PolicyStatement(TypedDict, total=False): + action: Required[str] + """Permitted action, such as browsers:use or vaults:read.""" + + resource: Required[str] + """Resource the action applies to, in the format projects/{project_id}/{kind}/{id}. + + Wildcards are supported. + """ + + +class Policy(TypedDict, total=False): + """ + Issue a scoped key that may only perform the listed actions on the listed resources. + """ + + statements: Required[Iterable[PolicyStatement]] + """Allow statements. Every action and resource not listed is denied.""" diff --git a/tests/api_resources/test_api_keys.py b/tests/api_resources/test_api_keys.py index 03d1ed8e..67005442 100644 --- a/tests/api_resources/test_api_keys.py +++ b/tests/api_resources/test_api_keys.py @@ -13,6 +13,7 @@ APIKey, CreatedAPIKey, ) +from kernel._utils import parse_datetime from kernel.pagination import SyncOffsetPagination, AsyncOffsetPagination base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -35,6 +36,15 @@ def test_method_create_with_all_params(self, client: Kernel) -> None: api_key = client.api_keys.create( name="staging", days_to_expire=30, + expires_at=parse_datetime("2025-01-15T09:00:00Z"), + policy={ + "statements": [ + { + "action": "browsers:use", + "resource": "projects/proj_abc123/browsers/br_abc123", + } + ] + }, project_id="proj_abc123", ) assert_matches_type(CreatedAPIKey, api_key, path=["response"]) @@ -319,6 +329,15 @@ async def test_method_create_with_all_params(self, async_client: AsyncKernel) -> api_key = await async_client.api_keys.create( name="staging", days_to_expire=30, + expires_at=parse_datetime("2025-01-15T09:00:00Z"), + policy={ + "statements": [ + { + "action": "browsers:use", + "resource": "projects/proj_abc123/browsers/br_abc123", + } + ] + }, project_id="proj_abc123", ) assert_matches_type(CreatedAPIKey, api_key, path=["response"])