From 47ba0547e248280eca96a0acf762f357b3393afe Mon Sep 17 00:00:00 2001 From: sjmiller609 <7516283+sjmiller609@users.noreply.github.com> Date: Fri, 7 Aug 2026 18:42:47 +0000 Subject: [PATCH 1/4] Build SDK Stainless-Generated-From: 752706f5daca4d21f33a833f0267a2666d93f153 --- .stats.yml | 2 +- api.md | 26 +- src/kernel/_client.py | 2 +- src/kernel/resources/audit_logs/__init__.py | 33 + .../resources/{ => audit_logs}/audit_logs.py | 58 +- .../audit_logs/export_destinations.py | 691 ++++++++++++++++++ src/kernel/types/audit_logs/__init__.py | 11 + .../audit_log_export_destination.py | 79 ++ ...udit_log_export_destination_test_result.py | 22 + .../export_destination_create_params.py | 23 + .../export_destination_list_params.py | 15 + .../export_destination_update_params.py | 26 + tests/api_resources/audit_logs/__init__.py | 1 + .../audit_logs/test_export_destinations.py | 592 +++++++++++++++ 14 files changed, 1567 insertions(+), 14 deletions(-) create mode 100644 src/kernel/resources/audit_logs/__init__.py rename src/kernel/resources/{ => audit_logs}/audit_logs.py (89%) create mode 100644 src/kernel/resources/audit_logs/export_destinations.py create mode 100644 src/kernel/types/audit_logs/__init__.py create mode 100644 src/kernel/types/audit_logs/audit_log_export_destination.py create mode 100644 src/kernel/types/audit_logs/audit_log_export_destination_test_result.py create mode 100644 src/kernel/types/audit_logs/export_destination_create_params.py create mode 100644 src/kernel/types/audit_logs/export_destination_list_params.py create mode 100644 src/kernel/types/audit_logs/export_destination_update_params.py create mode 100644 tests/api_resources/audit_logs/__init__.py create mode 100644 tests/api_resources/audit_logs/test_export_destinations.py diff --git a/.stats.yml b/.stats.yml index 71c320fc..4bb27f8b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 127 +configured_endpoints: 133 diff --git a/api.md b/api.md index 2685a9d5..bf8b28ae 100644 --- a/api.md +++ b/api.md @@ -458,8 +458,30 @@ from kernel.types import AuditLogEntry Methods: -- client.audit_logs.list(\*\*params) -> SyncPageTokenPagination[AuditLogEntry] -- client.audit_logs.export_chunk(\*\*params) -> BinaryAPIResponse +- client.audit_logs.list(\*\*params) -> SyncPageTokenPagination[AuditLogEntry] +- client.audit_logs.export_chunk(\*\*params) -> BinaryAPIResponse + +## ExportDestinations + +Types: + +```python +from kernel.types.audit_logs import ( + AuditLogExportDestination, + AuditLogExportDestinationTestResult, + CreateAuditLogExportDestinationRequest, + UpdateAuditLogExportDestinationRequest, +) +``` + +Methods: + +- client.audit_logs.export_destinations.create(\*\*params) -> AuditLogExportDestination +- client.audit_logs.export_destinations.retrieve(id) -> AuditLogExportDestination +- client.audit_logs.export_destinations.update(id, \*\*params) -> AuditLogExportDestination +- client.audit_logs.export_destinations.list(\*\*params) -> SyncOffsetPagination[AuditLogExportDestination] +- client.audit_logs.export_destinations.delete(id) -> None +- client.audit_logs.export_destinations.test(id) -> AuditLogExportDestinationTestResult # APIKeys diff --git a/src/kernel/_client.py b/src/kernel/_client.py index 64c7136b..62bd2014 100644 --- a/src/kernel/_client.py +++ b/src/kernel/_client.py @@ -68,7 +68,6 @@ from .resources.api_keys import APIKeysResource, AsyncAPIKeysResource from .resources.profiles import ProfilesResource, AsyncProfilesResource from .resources.auth.auth import AuthResource, AsyncAuthResource - from .resources.audit_logs import AuditLogsResource, AsyncAuditLogsResource from .resources.extensions import ExtensionsResource, AsyncExtensionsResource from .resources.credentials import CredentialsResource, AsyncCredentialsResource from .resources.deployments import DeploymentsResource, AsyncDeploymentsResource @@ -77,6 +76,7 @@ from .resources.browsers.browsers import BrowsersResource, AsyncBrowsersResource from .resources.projects.projects import ProjectsResource, AsyncProjectsResource from .resources.credential_providers import CredentialProvidersResource, AsyncCredentialProvidersResource + from .resources.audit_logs.audit_logs import AuditLogsResource, AsyncAuditLogsResource from .resources.organization.organization import OrganizationResource, AsyncOrganizationResource __all__ = [ diff --git a/src/kernel/resources/audit_logs/__init__.py b/src/kernel/resources/audit_logs/__init__.py new file mode 100644 index 00000000..5127038d --- /dev/null +++ b/src/kernel/resources/audit_logs/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .audit_logs import ( + AuditLogsResource, + AsyncAuditLogsResource, + AuditLogsResourceWithRawResponse, + AsyncAuditLogsResourceWithRawResponse, + AuditLogsResourceWithStreamingResponse, + AsyncAuditLogsResourceWithStreamingResponse, +) +from .export_destinations import ( + ExportDestinationsResource, + AsyncExportDestinationsResource, + ExportDestinationsResourceWithRawResponse, + AsyncExportDestinationsResourceWithRawResponse, + ExportDestinationsResourceWithStreamingResponse, + AsyncExportDestinationsResourceWithStreamingResponse, +) + +__all__ = [ + "ExportDestinationsResource", + "AsyncExportDestinationsResource", + "ExportDestinationsResourceWithRawResponse", + "AsyncExportDestinationsResourceWithRawResponse", + "ExportDestinationsResourceWithStreamingResponse", + "AsyncExportDestinationsResourceWithStreamingResponse", + "AuditLogsResource", + "AsyncAuditLogsResource", + "AuditLogsResourceWithRawResponse", + "AsyncAuditLogsResourceWithRawResponse", + "AuditLogsResourceWithStreamingResponse", + "AsyncAuditLogsResourceWithStreamingResponse", +] diff --git a/src/kernel/resources/audit_logs.py b/src/kernel/resources/audit_logs/audit_logs.py similarity index 89% rename from src/kernel/resources/audit_logs.py rename to src/kernel/resources/audit_logs/audit_logs.py index aac5d71c..d6aa80a6 100644 --- a/src/kernel/resources/audit_logs.py +++ b/src/kernel/resources/audit_logs/audit_logs.py @@ -8,12 +8,12 @@ import httpx -from ..types import audit_log_list_params, audit_log_export_chunk_params -from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( +from ...types import audit_log_list_params, audit_log_export_chunk_params +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( BinaryAPIResponse, AsyncBinaryAPIResponse, StreamedBinaryAPIResponse, @@ -27,10 +27,18 @@ async_to_custom_raw_response_wrapper, async_to_custom_streamed_response_wrapper, ) -from ..pagination import SyncPageTokenPagination, AsyncPageTokenPagination -from .._base_client import AsyncPaginator, make_request_options -from ..types.audit_log_entry import AuditLogEntry -from ..lib.audit_log_download import ( +from ...pagination import SyncPageTokenPagination, AsyncPageTokenPagination +from ..._base_client import AsyncPaginator, make_request_options +from .export_destinations import ( + ExportDestinationsResource, + AsyncExportDestinationsResource, + ExportDestinationsResourceWithRawResponse, + AsyncExportDestinationsResourceWithRawResponse, + ExportDestinationsResourceWithStreamingResponse, + AsyncExportDestinationsResourceWithStreamingResponse, +) +from ...types.audit_log_entry import AuditLogEntry +from ...lib.audit_log_download import ( ProgressCallback, AsyncProgressCallback, AuditLogDownloadResult, @@ -44,6 +52,11 @@ class AuditLogsResource(SyncAPIResource): """Read audit log records for the authenticated organization.""" + @cached_property + def export_destinations(self) -> ExportDestinationsResource: + """Read audit log records for the authenticated organization.""" + return ExportDestinationsResource(self._client) + @cached_property def with_raw_response(self) -> AuditLogsResourceWithRawResponse: """ @@ -287,6 +300,11 @@ def fetch_chunk(cursor: str | None) -> ContextManager[StreamedBinaryAPIResponse] class AsyncAuditLogsResource(AsyncAPIResource): """Read audit log records for the authenticated organization.""" + @cached_property + def export_destinations(self) -> AsyncExportDestinationsResource: + """Read audit log records for the authenticated organization.""" + return AsyncExportDestinationsResource(self._client) + @cached_property def with_raw_response(self) -> AsyncAuditLogsResourceWithRawResponse: """ @@ -539,6 +557,11 @@ def __init__(self, audit_logs: AuditLogsResource) -> None: BinaryAPIResponse, ) + @cached_property + def export_destinations(self) -> ExportDestinationsResourceWithRawResponse: + """Read audit log records for the authenticated organization.""" + return ExportDestinationsResourceWithRawResponse(self._audit_logs.export_destinations) + class AsyncAuditLogsResourceWithRawResponse: def __init__(self, audit_logs: AsyncAuditLogsResource) -> None: @@ -552,6 +575,11 @@ def __init__(self, audit_logs: AsyncAuditLogsResource) -> None: AsyncBinaryAPIResponse, ) + @cached_property + def export_destinations(self) -> AsyncExportDestinationsResourceWithRawResponse: + """Read audit log records for the authenticated organization.""" + return AsyncExportDestinationsResourceWithRawResponse(self._audit_logs.export_destinations) + class AuditLogsResourceWithStreamingResponse: def __init__(self, audit_logs: AuditLogsResource) -> None: @@ -565,6 +593,11 @@ def __init__(self, audit_logs: AuditLogsResource) -> None: StreamedBinaryAPIResponse, ) + @cached_property + def export_destinations(self) -> ExportDestinationsResourceWithStreamingResponse: + """Read audit log records for the authenticated organization.""" + return ExportDestinationsResourceWithStreamingResponse(self._audit_logs.export_destinations) + class AsyncAuditLogsResourceWithStreamingResponse: def __init__(self, audit_logs: AsyncAuditLogsResource) -> None: @@ -577,3 +610,8 @@ def __init__(self, audit_logs: AsyncAuditLogsResource) -> None: audit_logs.export_chunk, AsyncStreamedBinaryAPIResponse, ) + + @cached_property + def export_destinations(self) -> AsyncExportDestinationsResourceWithStreamingResponse: + """Read audit log records for the authenticated organization.""" + return AsyncExportDestinationsResourceWithStreamingResponse(self._audit_logs.export_destinations) diff --git a/src/kernel/resources/audit_logs/export_destinations.py b/src/kernel/resources/audit_logs/export_destinations.py new file mode 100644 index 00000000..c5d79624 --- /dev/null +++ b/src/kernel/resources/audit_logs/export_destinations.py @@ -0,0 +1,691 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +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.audit_logs import ( + export_destination_list_params, + export_destination_create_params, + export_destination_update_params, +) +from ...types.audit_logs.audit_log_export_destination import AuditLogExportDestination +from ...types.audit_logs.audit_log_export_destination_test_result import AuditLogExportDestinationTestResult + +__all__ = ["ExportDestinationsResource", "AsyncExportDestinationsResource"] + + +class ExportDestinationsResource(SyncAPIResource): + """Read audit log records for the authenticated organization.""" + + @cached_property + def with_raw_response(self) -> ExportDestinationsResourceWithRawResponse: + """ + 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 ExportDestinationsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ExportDestinationsResourceWithStreamingResponse: + """ + 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 ExportDestinationsResourceWithStreamingResponse(self) + + def create( + self, + *, + bucket: str, + format: Literal["jsonl.gz"], + prefix: str, + region: str, + role_arn: str, + type: Literal["s3"], + kms_key_id: 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, + ) -> AuditLogExportDestination: + """Create a paused destination. + + Activate it with a status update once the + destination test passes. Requires an active Enterprise plan. + + 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 + """ + return self._post( + "/audit-logs/export/destinations", + body=maybe_transform( + { + "bucket": bucket, + "format": format, + "prefix": prefix, + "region": region, + "role_arn": role_arn, + "type": type, + "kms_key_id": kms_key_id, + }, + export_destination_create_params.ExportDestinationCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AuditLogExportDestination, + ) + + 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, + ) -> AuditLogExportDestination: + """ + Retrieve details for a single audit log export destination by its ID. + + 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("/audit-logs/export/destinations/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AuditLogExportDestination, + ) + + def update( + self, + id: str, + *, + bucket: str | Omit = omit, + kms_key_id: str | Omit = omit, + prefix: str | Omit = omit, + region: str | Omit = omit, + role_arn: str | Omit = omit, + status: Literal["active", "paused"] | 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, + ) -> AuditLogExportDestination: + """Apply a partial update to a destination. + + Requires an active Enterprise plan. + Returns 409 when the destination was changed concurrently, because the merged + configuration this request validated is no longer the one that would be stored; + retry against fresh state. Pausing prevents new delivery attempts, but an S3 + upload already in progress may complete after the response. + + Args: + kms_key_id: KMS key ID, alias, or ARN. Set to an empty string to remove the configured KMS + key; omit or send null to leave unchanged. + + 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("/audit-logs/export/destinations/{id}", id=id), + body=maybe_transform( + { + "bucket": bucket, + "kms_key_id": kms_key_id, + "prefix": prefix, + "region": region, + "role_arn": role_arn, + "status": status, + }, + export_destination_update_params.ExportDestinationUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AuditLogExportDestination, + ) + + 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[AuditLogExportDestination]: + """ + List audit log export destinations for the organization with pagination support. + + Args: + limit: Limit the number of destinations to return. + + offset: Offset the number of destinations to return. + + 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( + "/audit-logs/export/destinations", + page=SyncOffsetPagination[AuditLogExportDestination], + 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, + }, + export_destination_list_params.ExportDestinationListParams, + ), + ), + model=AuditLogExportDestination, + ) + + 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: + """Soft delete the destination and prevent new delivery attempts. + + An S3 upload + already in progress may complete after the response. + + 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("/audit-logs/export/destinations/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def test( + 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, + ) -> AuditLogExportDestinationTestResult: + """ + Verify the destination is writable by assuming the configured role and uploading + a temporary probe object with the same request metadata as a real delivery. + Requires an active Enterprise plan. + + 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._post( + path_template("/audit-logs/export/destinations/{id}/test", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AuditLogExportDestinationTestResult, + ) + + +class AsyncExportDestinationsResource(AsyncAPIResource): + """Read audit log records for the authenticated organization.""" + + @cached_property + def with_raw_response(self) -> AsyncExportDestinationsResourceWithRawResponse: + """ + 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 AsyncExportDestinationsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncExportDestinationsResourceWithStreamingResponse: + """ + 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 AsyncExportDestinationsResourceWithStreamingResponse(self) + + async def create( + self, + *, + bucket: str, + format: Literal["jsonl.gz"], + prefix: str, + region: str, + role_arn: str, + type: Literal["s3"], + kms_key_id: 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, + ) -> AuditLogExportDestination: + """Create a paused destination. + + Activate it with a status update once the + destination test passes. Requires an active Enterprise plan. + + 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 + """ + return await self._post( + "/audit-logs/export/destinations", + body=await async_maybe_transform( + { + "bucket": bucket, + "format": format, + "prefix": prefix, + "region": region, + "role_arn": role_arn, + "type": type, + "kms_key_id": kms_key_id, + }, + export_destination_create_params.ExportDestinationCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AuditLogExportDestination, + ) + + 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, + ) -> AuditLogExportDestination: + """ + Retrieve details for a single audit log export destination by its ID. + + 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("/audit-logs/export/destinations/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AuditLogExportDestination, + ) + + async def update( + self, + id: str, + *, + bucket: str | Omit = omit, + kms_key_id: str | Omit = omit, + prefix: str | Omit = omit, + region: str | Omit = omit, + role_arn: str | Omit = omit, + status: Literal["active", "paused"] | 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, + ) -> AuditLogExportDestination: + """Apply a partial update to a destination. + + Requires an active Enterprise plan. + Returns 409 when the destination was changed concurrently, because the merged + configuration this request validated is no longer the one that would be stored; + retry against fresh state. Pausing prevents new delivery attempts, but an S3 + upload already in progress may complete after the response. + + Args: + kms_key_id: KMS key ID, alias, or ARN. Set to an empty string to remove the configured KMS + key; omit or send null to leave unchanged. + + 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("/audit-logs/export/destinations/{id}", id=id), + body=await async_maybe_transform( + { + "bucket": bucket, + "kms_key_id": kms_key_id, + "prefix": prefix, + "region": region, + "role_arn": role_arn, + "status": status, + }, + export_destination_update_params.ExportDestinationUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AuditLogExportDestination, + ) + + 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[AuditLogExportDestination, AsyncOffsetPagination[AuditLogExportDestination]]: + """ + List audit log export destinations for the organization with pagination support. + + Args: + limit: Limit the number of destinations to return. + + offset: Offset the number of destinations to return. + + 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( + "/audit-logs/export/destinations", + page=AsyncOffsetPagination[AuditLogExportDestination], + 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, + }, + export_destination_list_params.ExportDestinationListParams, + ), + ), + model=AuditLogExportDestination, + ) + + 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: + """Soft delete the destination and prevent new delivery attempts. + + An S3 upload + already in progress may complete after the response. + + 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("/audit-logs/export/destinations/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def test( + 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, + ) -> AuditLogExportDestinationTestResult: + """ + Verify the destination is writable by assuming the configured role and uploading + a temporary probe object with the same request metadata as a real delivery. + Requires an active Enterprise plan. + + 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._post( + path_template("/audit-logs/export/destinations/{id}/test", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AuditLogExportDestinationTestResult, + ) + + +class ExportDestinationsResourceWithRawResponse: + def __init__(self, export_destinations: ExportDestinationsResource) -> None: + self._export_destinations = export_destinations + + self.create = to_raw_response_wrapper( + export_destinations.create, + ) + self.retrieve = to_raw_response_wrapper( + export_destinations.retrieve, + ) + self.update = to_raw_response_wrapper( + export_destinations.update, + ) + self.list = to_raw_response_wrapper( + export_destinations.list, + ) + self.delete = to_raw_response_wrapper( + export_destinations.delete, + ) + self.test = to_raw_response_wrapper( + export_destinations.test, + ) + + +class AsyncExportDestinationsResourceWithRawResponse: + def __init__(self, export_destinations: AsyncExportDestinationsResource) -> None: + self._export_destinations = export_destinations + + self.create = async_to_raw_response_wrapper( + export_destinations.create, + ) + self.retrieve = async_to_raw_response_wrapper( + export_destinations.retrieve, + ) + self.update = async_to_raw_response_wrapper( + export_destinations.update, + ) + self.list = async_to_raw_response_wrapper( + export_destinations.list, + ) + self.delete = async_to_raw_response_wrapper( + export_destinations.delete, + ) + self.test = async_to_raw_response_wrapper( + export_destinations.test, + ) + + +class ExportDestinationsResourceWithStreamingResponse: + def __init__(self, export_destinations: ExportDestinationsResource) -> None: + self._export_destinations = export_destinations + + self.create = to_streamed_response_wrapper( + export_destinations.create, + ) + self.retrieve = to_streamed_response_wrapper( + export_destinations.retrieve, + ) + self.update = to_streamed_response_wrapper( + export_destinations.update, + ) + self.list = to_streamed_response_wrapper( + export_destinations.list, + ) + self.delete = to_streamed_response_wrapper( + export_destinations.delete, + ) + self.test = to_streamed_response_wrapper( + export_destinations.test, + ) + + +class AsyncExportDestinationsResourceWithStreamingResponse: + def __init__(self, export_destinations: AsyncExportDestinationsResource) -> None: + self._export_destinations = export_destinations + + self.create = async_to_streamed_response_wrapper( + export_destinations.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + export_destinations.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + export_destinations.update, + ) + self.list = async_to_streamed_response_wrapper( + export_destinations.list, + ) + self.delete = async_to_streamed_response_wrapper( + export_destinations.delete, + ) + self.test = async_to_streamed_response_wrapper( + export_destinations.test, + ) diff --git a/src/kernel/types/audit_logs/__init__.py b/src/kernel/types/audit_logs/__init__.py new file mode 100644 index 00000000..0a6f41ef --- /dev/null +++ b/src/kernel/types/audit_logs/__init__.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .audit_log_export_destination import AuditLogExportDestination as AuditLogExportDestination +from .export_destination_list_params import ExportDestinationListParams as ExportDestinationListParams +from .export_destination_create_params import ExportDestinationCreateParams as ExportDestinationCreateParams +from .export_destination_update_params import ExportDestinationUpdateParams as ExportDestinationUpdateParams +from .audit_log_export_destination_test_result import ( + AuditLogExportDestinationTestResult as AuditLogExportDestinationTestResult, +) diff --git a/src/kernel/types/audit_logs/audit_log_export_destination.py b/src/kernel/types/audit_logs/audit_log_export_destination.py new file mode 100644 index 00000000..0897cdf7 --- /dev/null +++ b/src/kernel/types/audit_logs/audit_log_export_destination.py @@ -0,0 +1,79 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AuditLogExportDestination"] + + +class AuditLogExportDestination(BaseModel): + """An organization-scoped audit log export destination. + + Delivery is at-least-once for rows visible when their window is committed: a delivery that is retried rewrites the same object, and the same `event_id` can appear in more than one object, so consumers must deduplicate on `event_id`. Each event-time window is held for ten minutes before it commits; a row that becomes visible after its window is committed may not be delivered. + + Objects are written as `/destination_id=/org_id=/date=/hour=/-.jsonl.gz`, where `date` and `hour` are the UTC calendar hour that fully contains every row in the object, so the layout is safe to register as a Hive-partitioned table. The object name is derived from the rows it holds, so a retried delivery rewrites its own object. + """ + + id: str + + bucket: str + + consecutive_failures: int + + created_at: datetime + + external_id: str + + format: Literal["jsonl.gz"] + + kernel_role_arn: str + """The Kernel role that assumes `role_arn` in your account to deliver logs. + + Allow this role as the principal in your role's trust policy, and require + `external_id` as the `sts:ExternalId` condition. + + Recreating a destination issues a new `external_id`, which the trust policy has + to be updated to match. + """ + + prefix: str + + region: str + + role_arn: str + + status: Literal["active", "paused"] + """Pausing prevents new delivery attempts. + + An S3 upload already in progress may complete after the pause response; its rows + can appear again after the destination is resumed. + """ + + type: Literal["s3"] + + updated_at: datetime + + kms_key_id: Optional[str] = None + + last_error: Optional[str] = None + """Sanitized description of the most recent delivery failure.""" + + last_error_at: Optional[datetime] = None + + last_exported_cursor: Optional[str] = None + """Opaque, versioned checkpoint for forward-only continuous export. + + This value is not compatible with audit-log list page tokens. + + Delivery starts at the moment the destination is activated, so events recorded + before that are not delivered. Pausing stops delivery and resuming starts again + from the time of the resume: events recorded while a destination was paused are + never exported, and pausing is not a way to defer delivery. + """ + + last_success_at: Optional[datetime] = None + + next_attempt_at: Optional[datetime] = None diff --git a/src/kernel/types/audit_logs/audit_log_export_destination_test_result.py b/src/kernel/types/audit_logs/audit_log_export_destination_test_result.py new file mode 100644 index 00000000..ca1eda6f --- /dev/null +++ b/src/kernel/types/audit_logs/audit_log_export_destination_test_result.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AuditLogExportDestinationTestResult", "Error"] + + +class Error(BaseModel): + code: Literal["assume_role_failed", "put_object_failed"] + + message: str + + +class AuditLogExportDestinationTestResult(BaseModel): + stage: Literal["assume_role", "put_object", "complete"] + + success: bool + + error: Optional[Error] = None diff --git a/src/kernel/types/audit_logs/export_destination_create_params.py b/src/kernel/types/audit_logs/export_destination_create_params.py new file mode 100644 index 00000000..b01cdee0 --- /dev/null +++ b/src/kernel/types/audit_logs/export_destination_create_params.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ExportDestinationCreateParams"] + + +class ExportDestinationCreateParams(TypedDict, total=False): + bucket: Required[str] + + format: Required[Literal["jsonl.gz"]] + + prefix: Required[str] + + region: Required[str] + + role_arn: Required[str] + + type: Required[Literal["s3"]] + + kms_key_id: str diff --git a/src/kernel/types/audit_logs/export_destination_list_params.py b/src/kernel/types/audit_logs/export_destination_list_params.py new file mode 100644 index 00000000..2026a4b1 --- /dev/null +++ b/src/kernel/types/audit_logs/export_destination_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__ = ["ExportDestinationListParams"] + + +class ExportDestinationListParams(TypedDict, total=False): + limit: int + """Limit the number of destinations to return.""" + + offset: int + """Offset the number of destinations to return.""" diff --git a/src/kernel/types/audit_logs/export_destination_update_params.py b/src/kernel/types/audit_logs/export_destination_update_params.py new file mode 100644 index 00000000..e21538f0 --- /dev/null +++ b/src/kernel/types/audit_logs/export_destination_update_params.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["ExportDestinationUpdateParams"] + + +class ExportDestinationUpdateParams(TypedDict, total=False): + bucket: str + + kms_key_id: str + """KMS key ID, alias, or ARN. + + Set to an empty string to remove the configured KMS key; omit or send null to + leave unchanged. + """ + + prefix: str + + region: str + + role_arn: str + + status: Literal["active", "paused"] diff --git a/tests/api_resources/audit_logs/__init__.py b/tests/api_resources/audit_logs/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/audit_logs/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/audit_logs/test_export_destinations.py b/tests/api_resources/audit_logs/test_export_destinations.py new file mode 100644 index 00000000..34b4fde8 --- /dev/null +++ b/tests/api_resources/audit_logs/test_export_destinations.py @@ -0,0 +1,592 @@ +# 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.pagination import SyncOffsetPagination, AsyncOffsetPagination +from kernel.types.audit_logs import ( + AuditLogExportDestination, + AuditLogExportDestinationTestResult, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestExportDestinations: + 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: + export_destination = client.audit_logs.export_destinations.create( + bucket="xxx", + format="jsonl.gz", + prefix="prefix", + region="x", + role_arn="x", + type="s3", + ) + assert_matches_type(AuditLogExportDestination, export_destination, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Kernel) -> None: + export_destination = client.audit_logs.export_destinations.create( + bucket="xxx", + format="jsonl.gz", + prefix="prefix", + region="x", + role_arn="x", + type="s3", + kms_key_id="kms_key_id", + ) + assert_matches_type(AuditLogExportDestination, export_destination, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_create(self, client: Kernel) -> None: + response = client.audit_logs.export_destinations.with_raw_response.create( + bucket="xxx", + format="jsonl.gz", + prefix="prefix", + region="x", + role_arn="x", + type="s3", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export_destination = response.parse() + assert_matches_type(AuditLogExportDestination, export_destination, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Kernel) -> None: + with client.audit_logs.export_destinations.with_streaming_response.create( + bucket="xxx", + format="jsonl.gz", + prefix="prefix", + region="x", + role_arn="x", + type="s3", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export_destination = response.parse() + assert_matches_type(AuditLogExportDestination, export_destination, 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: + export_destination = client.audit_logs.export_destinations.retrieve( + "id", + ) + assert_matches_type(AuditLogExportDestination, export_destination, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Kernel) -> None: + response = client.audit_logs.export_destinations.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export_destination = response.parse() + assert_matches_type(AuditLogExportDestination, export_destination, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Kernel) -> None: + with client.audit_logs.export_destinations.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export_destination = response.parse() + assert_matches_type(AuditLogExportDestination, export_destination, 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.audit_logs.export_destinations.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_update(self, client: Kernel) -> None: + export_destination = client.audit_logs.export_destinations.update( + id="id", + ) + assert_matches_type(AuditLogExportDestination, export_destination, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Kernel) -> None: + export_destination = client.audit_logs.export_destinations.update( + id="id", + bucket="xxx", + kms_key_id="kms_key_id", + prefix="prefix", + region="x", + role_arn="x", + status="active", + ) + assert_matches_type(AuditLogExportDestination, export_destination, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_update(self, client: Kernel) -> None: + response = client.audit_logs.export_destinations.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export_destination = response.parse() + assert_matches_type(AuditLogExportDestination, export_destination, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Kernel) -> None: + with client.audit_logs.export_destinations.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export_destination = response.parse() + assert_matches_type(AuditLogExportDestination, export_destination, 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.audit_logs.export_destinations.with_raw_response.update( + id="", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_list(self, client: Kernel) -> None: + export_destination = client.audit_logs.export_destinations.list() + assert_matches_type(SyncOffsetPagination[AuditLogExportDestination], export_destination, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Kernel) -> None: + export_destination = client.audit_logs.export_destinations.list( + limit=1, + offset=0, + ) + assert_matches_type(SyncOffsetPagination[AuditLogExportDestination], export_destination, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_list(self, client: Kernel) -> None: + response = client.audit_logs.export_destinations.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export_destination = response.parse() + assert_matches_type(SyncOffsetPagination[AuditLogExportDestination], export_destination, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Kernel) -> None: + with client.audit_logs.export_destinations.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export_destination = response.parse() + assert_matches_type(SyncOffsetPagination[AuditLogExportDestination], export_destination, 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: + export_destination = client.audit_logs.export_destinations.delete( + "id", + ) + assert export_destination is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Kernel) -> None: + response = client.audit_logs.export_destinations.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export_destination = response.parse() + assert export_destination is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Kernel) -> None: + with client.audit_logs.export_destinations.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export_destination = response.parse() + assert export_destination 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.audit_logs.export_destinations.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_test(self, client: Kernel) -> None: + export_destination = client.audit_logs.export_destinations.test( + "id", + ) + assert_matches_type(AuditLogExportDestinationTestResult, export_destination, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_test(self, client: Kernel) -> None: + response = client.audit_logs.export_destinations.with_raw_response.test( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export_destination = response.parse() + assert_matches_type(AuditLogExportDestinationTestResult, export_destination, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_test(self, client: Kernel) -> None: + with client.audit_logs.export_destinations.with_streaming_response.test( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export_destination = response.parse() + assert_matches_type(AuditLogExportDestinationTestResult, export_destination, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_test(self, client: Kernel) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.audit_logs.export_destinations.with_raw_response.test( + "", + ) + + +class TestAsyncExportDestinations: + 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: + export_destination = await async_client.audit_logs.export_destinations.create( + bucket="xxx", + format="jsonl.gz", + prefix="prefix", + region="x", + role_arn="x", + type="s3", + ) + assert_matches_type(AuditLogExportDestination, export_destination, 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: + export_destination = await async_client.audit_logs.export_destinations.create( + bucket="xxx", + format="jsonl.gz", + prefix="prefix", + region="x", + role_arn="x", + type="s3", + kms_key_id="kms_key_id", + ) + assert_matches_type(AuditLogExportDestination, export_destination, 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.audit_logs.export_destinations.with_raw_response.create( + bucket="xxx", + format="jsonl.gz", + prefix="prefix", + region="x", + role_arn="x", + type="s3", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export_destination = await response.parse() + assert_matches_type(AuditLogExportDestination, export_destination, 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.audit_logs.export_destinations.with_streaming_response.create( + bucket="xxx", + format="jsonl.gz", + prefix="prefix", + region="x", + role_arn="x", + type="s3", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export_destination = await response.parse() + assert_matches_type(AuditLogExportDestination, export_destination, 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: + export_destination = await async_client.audit_logs.export_destinations.retrieve( + "id", + ) + assert_matches_type(AuditLogExportDestination, export_destination, 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.audit_logs.export_destinations.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export_destination = await response.parse() + assert_matches_type(AuditLogExportDestination, export_destination, 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.audit_logs.export_destinations.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export_destination = await response.parse() + assert_matches_type(AuditLogExportDestination, export_destination, 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.audit_logs.export_destinations.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncKernel) -> None: + export_destination = await async_client.audit_logs.export_destinations.update( + id="id", + ) + assert_matches_type(AuditLogExportDestination, export_destination, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncKernel) -> None: + export_destination = await async_client.audit_logs.export_destinations.update( + id="id", + bucket="xxx", + kms_key_id="kms_key_id", + prefix="prefix", + region="x", + role_arn="x", + status="active", + ) + assert_matches_type(AuditLogExportDestination, export_destination, 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.audit_logs.export_destinations.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export_destination = await response.parse() + assert_matches_type(AuditLogExportDestination, export_destination, 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.audit_logs.export_destinations.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export_destination = await response.parse() + assert_matches_type(AuditLogExportDestination, export_destination, 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.audit_logs.export_destinations.with_raw_response.update( + id="", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncKernel) -> None: + export_destination = await async_client.audit_logs.export_destinations.list() + assert_matches_type(AsyncOffsetPagination[AuditLogExportDestination], export_destination, 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: + export_destination = await async_client.audit_logs.export_destinations.list( + limit=1, + offset=0, + ) + assert_matches_type(AsyncOffsetPagination[AuditLogExportDestination], export_destination, 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.audit_logs.export_destinations.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export_destination = await response.parse() + assert_matches_type(AsyncOffsetPagination[AuditLogExportDestination], export_destination, 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.audit_logs.export_destinations.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export_destination = await response.parse() + assert_matches_type(AsyncOffsetPagination[AuditLogExportDestination], export_destination, 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: + export_destination = await async_client.audit_logs.export_destinations.delete( + "id", + ) + assert export_destination 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.audit_logs.export_destinations.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export_destination = await response.parse() + assert export_destination 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.audit_logs.export_destinations.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export_destination = await response.parse() + assert export_destination 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.audit_logs.export_destinations.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_test(self, async_client: AsyncKernel) -> None: + export_destination = await async_client.audit_logs.export_destinations.test( + "id", + ) + assert_matches_type(AuditLogExportDestinationTestResult, export_destination, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_test(self, async_client: AsyncKernel) -> None: + response = await async_client.audit_logs.export_destinations.with_raw_response.test( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export_destination = await response.parse() + assert_matches_type(AuditLogExportDestinationTestResult, export_destination, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_test(self, async_client: AsyncKernel) -> None: + async with async_client.audit_logs.export_destinations.with_streaming_response.test( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export_destination = await response.parse() + assert_matches_type(AuditLogExportDestinationTestResult, export_destination, 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_test(self, async_client: AsyncKernel) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.audit_logs.export_destinations.with_raw_response.test( + "", + ) From 5ae6c3e0b6a54a884b9fc6d3d4658d970a7aa459 Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Sat, 8 Aug 2026 19:56:33 +0000 Subject: [PATCH 2/4] feat: Forward egress deploy owner attribution Stainless-Generated-From: 00e953c7c4e06216815d4e9f8e53ab7596262ca7 --- api.md | 4 + src/kernel/resources/auth/connections.py | 91 +++++---- src/kernel/resources/browsers/browsers.py | 65 +++++- src/kernel/types/__init__.py | 4 + src/kernel/types/auth/__init__.py | 2 + .../types/auth/connection_create_params.py | 33 ++-- .../types/auth/connection_login_params.py | 31 ++- .../types/auth/connection_update_params.py | 33 ++-- src/kernel/types/auth/managed_auth.py | 24 ++- .../types/auth/managed_auth_browser_config.py | 118 +++++++++++ .../auth/managed_auth_browser_config_param.py | 120 +++++++++++ src/kernel/types/browser_create_params.py | 25 ++- src/kernel/types/browser_create_response.py | 9 +- src/kernel/types/browser_list_response.py | 9 +- .../types/browser_pool_acquire_response.py | 9 +- src/kernel/types/browser_proxy.py | 30 +++ src/kernel/types/browser_proxy_config.py | 34 ++++ .../types/browser_proxy_config_param.py | 35 ++++ src/kernel/types/browser_proxy_mode.py | 7 + src/kernel/types/browser_retrieve_response.py | 9 +- src/kernel/types/browser_update_params.py | 17 +- src/kernel/types/browser_update_response.py | 9 +- .../invocation_list_browsers_response.py | 9 +- tests/api_resources/auth/test_connections.py | 186 ++++++++++++++++++ tests/api_resources/test_browsers.py | 20 ++ 25 files changed, 808 insertions(+), 125 deletions(-) create mode 100644 src/kernel/types/auth/managed_auth_browser_config.py create mode 100644 src/kernel/types/auth/managed_auth_browser_config_param.py create mode 100644 src/kernel/types/browser_proxy.py create mode 100644 src/kernel/types/browser_proxy_config.py create mode 100644 src/kernel/types/browser_proxy_config_param.py create mode 100644 src/kernel/types/browser_proxy_mode.py diff --git a/api.md b/api.md index 7de716a0..65ac4c06 100644 --- a/api.md +++ b/api.md @@ -81,6 +81,9 @@ Types: ```python from kernel.types import ( BrowserPoolRef, + BrowserProxy, + BrowserProxyConfig, + BrowserProxyMode, BrowserUsage, Profile, Tags, @@ -314,6 +317,7 @@ Types: from kernel.types.auth import ( LoginResponse, ManagedAuth, + ManagedAuthBrowserConfig, ManagedAuthCreateRequest, ManagedAuthTimelineEvent, ManagedAuthUpdateRequest, diff --git a/src/kernel/resources/auth/connections.py b/src/kernel/resources/auth/connections.py index f5f4529a..a84d42af 100644 --- a/src/kernel/resources/auth/connections.py +++ b/src/kernel/resources/auth/connections.py @@ -33,6 +33,7 @@ from ...types.auth.submit_fields_response import SubmitFieldsResponse from ...types.auth.connection_follow_response import ConnectionFollowResponse from ...types.auth.managed_auth_timeline_event import ManagedAuthTimelineEvent +from ...types.auth.managed_auth_browser_config_param import ManagedAuthBrowserConfigParam __all__ = ["ConnectionsResource", "AsyncConnectionsResource"] @@ -66,6 +67,7 @@ def create( profile_name: str, allowed_domains: SequenceNotStr[str] | Omit = omit, auto_reauth: bool | Omit = omit, + browser: ManagedAuthBrowserConfigParam | Omit = omit, browser_telemetry: Optional[connection_create_params.BrowserTelemetry] | Omit = omit, credential: connection_create_params.Credential | Omit = omit, health_check_interval: int | Omit = omit, @@ -120,9 +122,11 @@ def create( false, expired sessions are marked as `NEEDS_AUTH` instead of attempting re-auth. Defaults to true. - browser_telemetry: Browser telemetry configuration used by this connection's browser sessions by - default. Uses the exact create-browser configuration. Can be overridden - per-login. + browser: Default browser configuration for login, reauthentication, and health-check + sessions. + + browser_telemetry: Deprecated. Use browser.telemetry. Retained during migration for existing + clients. credential: Reference to credentials for the auth connection. Use one of: @@ -144,10 +148,7 @@ def create( login_url: Optional login page URL to skip discovery - proxy: Proxy selection. Provide either id or name. The proxy must be in the same - project as the resource referencing it. When selecting by name, the name must - match exactly one active proxy in the project. Ambiguous names return a 400; use - id for stable references. + proxy: Deprecated. Use browser.proxy. Retained during migration for existing clients. record_session: Whether to record browser sessions for this connection by default. Useful for debugging. Can be overridden per-login. Defaults to false. @@ -171,6 +172,7 @@ def create( "profile_name": profile_name, "allowed_domains": allowed_domains, "auto_reauth": auto_reauth, + "browser": browser, "browser_telemetry": browser_telemetry, "credential": credential, "health_check_interval": health_check_interval, @@ -229,6 +231,7 @@ def update( *, allowed_domains: SequenceNotStr[str] | Omit = omit, auto_reauth: bool | Omit = omit, + browser: ManagedAuthBrowserConfigParam | Omit = omit, browser_telemetry: Optional[connection_update_params.BrowserTelemetry] | Omit = omit, credential: connection_update_params.Credential | Omit = omit, health_check_interval: int | Omit = omit, @@ -260,9 +263,11 @@ def update( when `health_checks` is false. When false, expired sessions detected by a health check are marked as `NEEDS_AUTH` instead of attempting re-auth. - browser_telemetry: Browser telemetry configuration used by future browser sessions for this - connection. Uses the exact create-browser configuration. Set enabled to false to - disable telemetry. + browser: Browser configuration updates for future login, reauthentication, and + health-check sessions. Omitted properties remain unchanged. + + browser_telemetry: Deprecated. Use browser.telemetry. Retained during migration for existing + clients. credential: Reference to credentials for the auth connection. Use one of: @@ -280,10 +285,7 @@ def update( login_url: Login page URL. Set to empty string to clear. - proxy: Proxy selection. Provide either id or name. The proxy must be in the same - project as the resource referencing it. When selecting by name, the name must - match exactly one active proxy in the project. Ambiguous names return a 400; use - id for stable references. + proxy: Deprecated. Use browser.proxy. Retained during migration for existing clients. record_session: Whether to record browser sessions for this connection by default @@ -305,6 +307,7 @@ def update( { "allowed_domains": allowed_domains, "auto_reauth": auto_reauth, + "browser": browser, "browser_telemetry": browser_telemetry, "credential": credential, "health_check_interval": health_check_interval, @@ -464,6 +467,7 @@ def login( self, id: str, *, + browser: ManagedAuthBrowserConfigParam | Omit = omit, browser_telemetry: Optional[connection_login_params.BrowserTelemetry] | Omit = omit, proxy: connection_login_params.Proxy | Omit = omit, record_session: bool | Omit = omit, @@ -481,14 +485,13 @@ def login( credentials are stored. Args: - browser_telemetry: Override the connection's default browser telemetry configuration for this - login. When omitted, the connection's browser_telemetry default is used. Uses - the exact create-browser configuration. + browser: Browser configuration override for this login. Omitted properties inherit the + connection defaults. + + browser_telemetry: Deprecated. Use browser.telemetry. Retained during migration for existing + clients. - proxy: Proxy selection. Provide either id or name. The proxy must be in the same - project as the resource referencing it. When selecting by name, the name must - match exactly one active proxy in the project. Ambiguous names return a 400; use - id for stable references. + proxy: Deprecated. Use browser.proxy. Retained during migration for existing clients. record_session: Override the connection's default for recording this login's browser session. When omitted, the connection's record_session default is used. @@ -507,6 +510,7 @@ def login( path_template("/auth/connections/{id}/login", id=id), body=maybe_transform( { + "browser": browser, "browser_telemetry": browser_telemetry, "proxy": proxy, "record_session": record_session, @@ -675,6 +679,7 @@ async def create( profile_name: str, allowed_domains: SequenceNotStr[str] | Omit = omit, auto_reauth: bool | Omit = omit, + browser: ManagedAuthBrowserConfigParam | Omit = omit, browser_telemetry: Optional[connection_create_params.BrowserTelemetry] | Omit = omit, credential: connection_create_params.Credential | Omit = omit, health_check_interval: int | Omit = omit, @@ -729,9 +734,11 @@ async def create( false, expired sessions are marked as `NEEDS_AUTH` instead of attempting re-auth. Defaults to true. - browser_telemetry: Browser telemetry configuration used by this connection's browser sessions by - default. Uses the exact create-browser configuration. Can be overridden - per-login. + browser: Default browser configuration for login, reauthentication, and health-check + sessions. + + browser_telemetry: Deprecated. Use browser.telemetry. Retained during migration for existing + clients. credential: Reference to credentials for the auth connection. Use one of: @@ -753,10 +760,7 @@ async def create( login_url: Optional login page URL to skip discovery - proxy: Proxy selection. Provide either id or name. The proxy must be in the same - project as the resource referencing it. When selecting by name, the name must - match exactly one active proxy in the project. Ambiguous names return a 400; use - id for stable references. + proxy: Deprecated. Use browser.proxy. Retained during migration for existing clients. record_session: Whether to record browser sessions for this connection by default. Useful for debugging. Can be overridden per-login. Defaults to false. @@ -780,6 +784,7 @@ async def create( "profile_name": profile_name, "allowed_domains": allowed_domains, "auto_reauth": auto_reauth, + "browser": browser, "browser_telemetry": browser_telemetry, "credential": credential, "health_check_interval": health_check_interval, @@ -838,6 +843,7 @@ async def update( *, allowed_domains: SequenceNotStr[str] | Omit = omit, auto_reauth: bool | Omit = omit, + browser: ManagedAuthBrowserConfigParam | Omit = omit, browser_telemetry: Optional[connection_update_params.BrowserTelemetry] | Omit = omit, credential: connection_update_params.Credential | Omit = omit, health_check_interval: int | Omit = omit, @@ -869,9 +875,11 @@ async def update( when `health_checks` is false. When false, expired sessions detected by a health check are marked as `NEEDS_AUTH` instead of attempting re-auth. - browser_telemetry: Browser telemetry configuration used by future browser sessions for this - connection. Uses the exact create-browser configuration. Set enabled to false to - disable telemetry. + browser: Browser configuration updates for future login, reauthentication, and + health-check sessions. Omitted properties remain unchanged. + + browser_telemetry: Deprecated. Use browser.telemetry. Retained during migration for existing + clients. credential: Reference to credentials for the auth connection. Use one of: @@ -889,10 +897,7 @@ async def update( login_url: Login page URL. Set to empty string to clear. - proxy: Proxy selection. Provide either id or name. The proxy must be in the same - project as the resource referencing it. When selecting by name, the name must - match exactly one active proxy in the project. Ambiguous names return a 400; use - id for stable references. + proxy: Deprecated. Use browser.proxy. Retained during migration for existing clients. record_session: Whether to record browser sessions for this connection by default @@ -914,6 +919,7 @@ async def update( { "allowed_domains": allowed_domains, "auto_reauth": auto_reauth, + "browser": browser, "browser_telemetry": browser_telemetry, "credential": credential, "health_check_interval": health_check_interval, @@ -1073,6 +1079,7 @@ async def login( self, id: str, *, + browser: ManagedAuthBrowserConfigParam | Omit = omit, browser_telemetry: Optional[connection_login_params.BrowserTelemetry] | Omit = omit, proxy: connection_login_params.Proxy | Omit = omit, record_session: bool | Omit = omit, @@ -1090,14 +1097,13 @@ async def login( credentials are stored. Args: - browser_telemetry: Override the connection's default browser telemetry configuration for this - login. When omitted, the connection's browser_telemetry default is used. Uses - the exact create-browser configuration. + browser: Browser configuration override for this login. Omitted properties inherit the + connection defaults. + + browser_telemetry: Deprecated. Use browser.telemetry. Retained during migration for existing + clients. - proxy: Proxy selection. Provide either id or name. The proxy must be in the same - project as the resource referencing it. When selecting by name, the name must - match exactly one active proxy in the project. Ambiguous names return a 400; use - id for stable references. + proxy: Deprecated. Use browser.proxy. Retained during migration for existing clients. record_session: Override the connection's default for recording this login's browser session. When omitted, the connection's record_session default is used. @@ -1116,6 +1122,7 @@ async def login( path_template("/auth/connections/{id}/login", id=id), body=await async_maybe_transform( { + "browser": browser, "browser_telemetry": browser_telemetry, "proxy": proxy, "record_session": record_session, diff --git a/src/kernel/resources/browsers/browsers.py b/src/kernel/resources/browsers/browsers.py index 553a98dd..19f48988 100644 --- a/src/kernel/resources/browsers/browsers.py +++ b/src/kernel/resources/browsers/browsers.py @@ -97,6 +97,7 @@ from ...types.browser_create_response import BrowserCreateResponse from ...types.browser_update_response import BrowserUpdateResponse from ...types.browser_retrieve_response import BrowserRetrieveResponse +from ...types.browser_proxy_config_param import BrowserProxyConfigParam from ...types.shared_params.browser_profile import BrowserProfile from ...types.shared_params.browser_viewport import BrowserViewport from ...types.shared_params.browser_extension import BrowserExtension @@ -172,6 +173,7 @@ def create( kiosk_mode: bool | Omit = omit, name: str | Omit = omit, profile: BrowserProfile | Omit = omit, + proxy: BrowserProxyConfigParam | Omit = omit, proxy_id: str | Omit = omit, start_url: str | Omit = omit, stealth: bool | Omit = omit, @@ -216,15 +218,26 @@ def create( specified, the matching profile will be loaded into the browser session. Profiles must be created beforehand. + proxy: Proxy configuration for the browser session. Cannot be combined with proxy_id. + Omit to use the browser default: stealth browsers use Kernel's default stealth + proxy, while non-stealth browsers use direct egress. Set mode to direct to force + direct egress regardless of stealth. Set mode to default to explicitly use the + browser default: Kernel's default stealth proxy when stealth=true, or direct + egress when stealth=false. Select id or name to use that proxy regardless of + stealth. Proxy selection does not change stealth or CAPTCHA solver behavior. + proxy_id: Optional proxy to associate to the browser session. Must reference a proxy in - the same project as the browser session. + the same project as the browser session. Deprecated in favor of proxy. start_url: Optional URL to open when the browser session is created. Navigation is best-effort, so navigation failures do not prevent the session from being created. - stealth: If true, launches the browser in stealth mode to reduce detection by anti-bot - mechanisms. + stealth: If true, launches the browser in stealth mode and enables the CAPTCHA solver. + Defaults to false. When proxy is omitted, stealth browsers use Kernel's default + stealth proxy and non-stealth browsers use direct egress. An explicit proxy + configuration changes only egress; it does not enable or disable stealth or the + CAPTCHA solver. tags: Optional user-defined key-value tags for the browser session, used to find and group sessions later. Can be changed later via PATCH /browsers/{id_or_name}. Up @@ -275,6 +288,7 @@ def create( "kiosk_mode": kiosk_mode, "name": name, "profile": profile, + "proxy": proxy, "proxy_id": proxy_id, "start_url": start_url, "stealth": stealth, @@ -340,6 +354,7 @@ def update( disable_default_proxy: bool | Omit = omit, name: Optional[str] | Omit = omit, profile: BrowserProfile | Omit = omit, + proxy: BrowserProxyConfigParam | Omit = omit, proxy_id: Optional[str] | Omit = omit, tags: Optional[TagsParam] | Omit = omit, telemetry: Optional[browser_update_params.Telemetry] | Omit = omit, @@ -356,7 +371,7 @@ def update( Args: disable_default_proxy: If true, stealth browsers connect directly instead of using the default stealth - proxy. + proxy. Deprecated in favor of proxy.mode. name: Human-readable name for the browser session. Omit to leave unchanged, set to an empty string to clear the name. When set, must be unique among active sessions @@ -365,8 +380,15 @@ def update( profile: Profile to load into the browser session. Only allowed if the session does not already have a profile loaded. + proxy: Proxy configuration to apply. Omit to leave the current configuration unchanged. + Cannot be combined with proxy_id or disable_default_proxy. Set mode to direct to + switch to direct egress regardless of stealth. Set mode to default to restore + the browser default after using a selected proxy: Kernel's default stealth proxy + for a stealth browser, or direct egress for a non-stealth browser. Updating + proxy does not change stealth or CAPTCHA solver behavior. + proxy_id: ID of the proxy to use. Omit to leave unchanged, set to empty string to remove - proxy. + proxy. Deprecated in favor of proxy. tags: User-defined key-value tags for the browser session. Omit to leave unchanged. Provide a map to replace the entire tag set (full replace, not a merge). Set to @@ -397,6 +419,7 @@ def update( "disable_default_proxy": disable_default_proxy, "name": name, "profile": profile, + "proxy": proxy, "proxy_id": proxy_id, "tags": tags, "telemetry": telemetry, @@ -750,6 +773,7 @@ async def create( kiosk_mode: bool | Omit = omit, name: str | Omit = omit, profile: BrowserProfile | Omit = omit, + proxy: BrowserProxyConfigParam | Omit = omit, proxy_id: str | Omit = omit, start_url: str | Omit = omit, stealth: bool | Omit = omit, @@ -794,15 +818,26 @@ async def create( specified, the matching profile will be loaded into the browser session. Profiles must be created beforehand. + proxy: Proxy configuration for the browser session. Cannot be combined with proxy_id. + Omit to use the browser default: stealth browsers use Kernel's default stealth + proxy, while non-stealth browsers use direct egress. Set mode to direct to force + direct egress regardless of stealth. Set mode to default to explicitly use the + browser default: Kernel's default stealth proxy when stealth=true, or direct + egress when stealth=false. Select id or name to use that proxy regardless of + stealth. Proxy selection does not change stealth or CAPTCHA solver behavior. + proxy_id: Optional proxy to associate to the browser session. Must reference a proxy in - the same project as the browser session. + the same project as the browser session. Deprecated in favor of proxy. start_url: Optional URL to open when the browser session is created. Navigation is best-effort, so navigation failures do not prevent the session from being created. - stealth: If true, launches the browser in stealth mode to reduce detection by anti-bot - mechanisms. + stealth: If true, launches the browser in stealth mode and enables the CAPTCHA solver. + Defaults to false. When proxy is omitted, stealth browsers use Kernel's default + stealth proxy and non-stealth browsers use direct egress. An explicit proxy + configuration changes only egress; it does not enable or disable stealth or the + CAPTCHA solver. tags: Optional user-defined key-value tags for the browser session, used to find and group sessions later. Can be changed later via PATCH /browsers/{id_or_name}. Up @@ -853,6 +888,7 @@ async def create( "kiosk_mode": kiosk_mode, "name": name, "profile": profile, + "proxy": proxy, "proxy_id": proxy_id, "start_url": start_url, "stealth": stealth, @@ -918,6 +954,7 @@ async def update( disable_default_proxy: bool | Omit = omit, name: Optional[str] | Omit = omit, profile: BrowserProfile | Omit = omit, + proxy: BrowserProxyConfigParam | Omit = omit, proxy_id: Optional[str] | Omit = omit, tags: Optional[TagsParam] | Omit = omit, telemetry: Optional[browser_update_params.Telemetry] | Omit = omit, @@ -934,7 +971,7 @@ async def update( Args: disable_default_proxy: If true, stealth browsers connect directly instead of using the default stealth - proxy. + proxy. Deprecated in favor of proxy.mode. name: Human-readable name for the browser session. Omit to leave unchanged, set to an empty string to clear the name. When set, must be unique among active sessions @@ -943,8 +980,15 @@ async def update( profile: Profile to load into the browser session. Only allowed if the session does not already have a profile loaded. + proxy: Proxy configuration to apply. Omit to leave the current configuration unchanged. + Cannot be combined with proxy_id or disable_default_proxy. Set mode to direct to + switch to direct egress regardless of stealth. Set mode to default to restore + the browser default after using a selected proxy: Kernel's default stealth proxy + for a stealth browser, or direct egress for a non-stealth browser. Updating + proxy does not change stealth or CAPTCHA solver behavior. + proxy_id: ID of the proxy to use. Omit to leave unchanged, set to empty string to remove - proxy. + proxy. Deprecated in favor of proxy. tags: User-defined key-value tags for the browser session. Omit to leave unchanged. Provide a map to replace the entire tag set (full replace, not a merge). Set to @@ -975,6 +1019,7 @@ async def update( "disable_default_proxy": disable_default_proxy, "name": name, "profile": profile, + "proxy": proxy, "proxy_id": proxy_id, "tags": tags, "telemetry": telemetry, diff --git a/src/kernel/types/__init__.py b/src/kernel/types/__init__.py index ab58edbc..e86ac9c4 100644 --- a/src/kernel/types/__init__.py +++ b/src/kernel/types/__init__.py @@ -22,6 +22,7 @@ from .credential import Credential as Credential from .tags_param import TagsParam as TagsParam from .browser_pool import BrowserPool as BrowserPool +from .browser_proxy import BrowserProxy as BrowserProxy from .browser_usage import BrowserUsage as BrowserUsage from .app_list_params import AppListParams as AppListParams from .audit_log_entry import AuditLogEntry as AuditLogEntry @@ -29,6 +30,7 @@ from .browser_pool_ref import BrowserPoolRef as BrowserPoolRef from .app_list_response import AppListResponse as AppListResponse from .proxy_list_params import ProxyListParams as ProxyListParams +from .browser_proxy_mode import BrowserProxyMode as BrowserProxyMode 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 @@ -39,6 +41,7 @@ from .proxy_create_params import ProxyCreateParams as ProxyCreateParams from .proxy_list_response import ProxyListResponse as ProxyListResponse from .proxy_update_params import ProxyUpdateParams as ProxyUpdateParams +from .browser_proxy_config import BrowserProxyConfig as BrowserProxyConfig from .proxy_check_response import ProxyCheckResponse as ProxyCheckResponse from .api_key_create_params import APIKeyCreateParams as APIKeyCreateParams from .api_key_rotate_params import APIKeyRotateParams as APIKeyRotateParams @@ -85,6 +88,7 @@ from .browser_pool_create_params import BrowserPoolCreateParams as BrowserPoolCreateParams from .browser_pool_delete_params import BrowserPoolDeleteParams as BrowserPoolDeleteParams from .browser_pool_update_params import BrowserPoolUpdateParams as BrowserPoolUpdateParams +from .browser_proxy_config_param import BrowserProxyConfigParam as BrowserProxyConfigParam from .deployment_create_response import DeploymentCreateResponse as DeploymentCreateResponse from .deployment_follow_response import DeploymentFollowResponse as DeploymentFollowResponse from .invocation_create_response import InvocationCreateResponse as InvocationCreateResponse diff --git a/src/kernel/types/auth/__init__.py b/src/kernel/types/auth/__init__.py index 2d0cf1b0..ced593b0 100644 --- a/src/kernel/types/auth/__init__.py +++ b/src/kernel/types/auth/__init__.py @@ -13,4 +13,6 @@ from .connection_update_params import ConnectionUpdateParams as ConnectionUpdateParams from .connection_follow_response import ConnectionFollowResponse as ConnectionFollowResponse from .connection_timeline_params import ConnectionTimelineParams as ConnectionTimelineParams +from .managed_auth_browser_config import ManagedAuthBrowserConfig as ManagedAuthBrowserConfig from .managed_auth_timeline_event import ManagedAuthTimelineEvent as ManagedAuthTimelineEvent +from .managed_auth_browser_config_param import ManagedAuthBrowserConfigParam as ManagedAuthBrowserConfigParam diff --git a/src/kernel/types/auth/connection_create_params.py b/src/kernel/types/auth/connection_create_params.py index 407857be..23d53cb2 100644 --- a/src/kernel/types/auth/connection_create_params.py +++ b/src/kernel/types/auth/connection_create_params.py @@ -6,6 +6,7 @@ from typing_extensions import Required, TypedDict from ..._types import SequenceNotStr +from .managed_auth_browser_config_param import ManagedAuthBrowserConfigParam from ..browsers.browser_telemetry_categories_config_param import BrowserTelemetryCategoriesConfigParam __all__ = [ @@ -62,11 +63,16 @@ class ConnectionCreateParams(TypedDict, total=False): re-auth. Defaults to true. """ - browser_telemetry: Optional[BrowserTelemetry] + browser: ManagedAuthBrowserConfigParam + """ + Default browser configuration for login, reauthentication, and health-check + sessions. """ - Browser telemetry configuration used by this connection's browser sessions by - default. Uses the exact create-browser configuration. Can be overridden - per-login. + + browser_telemetry: Optional[BrowserTelemetry] + """Deprecated. + + Use browser.telemetry. Retained during migration for existing clients. """ credential: Credential @@ -98,13 +104,7 @@ class ConnectionCreateParams(TypedDict, total=False): """Optional login page URL to skip discovery""" proxy: Proxy - """Proxy selection. - - Provide either id or name. The proxy must be in the same project as the resource - referencing it. When selecting by name, the name must match exactly one active - proxy in the project. Ambiguous names return a 400; use id for stable - references. - """ + """Deprecated. Use browser.proxy. Retained during migration for existing clients.""" record_session: bool """Whether to record browser sessions for this connection by default. @@ -164,8 +164,9 @@ class BrowserTelemetryExport(TypedDict, total=False): class BrowserTelemetry(TypedDict, total=False): - """ - Browser telemetry configuration used by this connection's browser sessions by default. Uses the exact create-browser configuration. Can be overridden per-login. + """Deprecated. + + Use browser.telemetry. Retained during migration for existing clients. """ browser: BrowserTelemetryCategoriesConfigParam @@ -225,11 +226,7 @@ class Credential(TypedDict, total=False): class Proxy(TypedDict, total=False): - """Proxy selection. - - Provide either id or name. The proxy must be in the same project as the resource referencing it. - When selecting by name, the name must match exactly one active proxy in the project. Ambiguous names return a 400; use id for stable references. - """ + """Deprecated. Use browser.proxy. Retained during migration for existing clients.""" id: str """Proxy ID""" diff --git a/src/kernel/types/auth/connection_login_params.py b/src/kernel/types/auth/connection_login_params.py index 117d3c7d..c26b020f 100644 --- a/src/kernel/types/auth/connection_login_params.py +++ b/src/kernel/types/auth/connection_login_params.py @@ -5,6 +5,7 @@ from typing import Optional from typing_extensions import TypedDict +from .managed_auth_browser_config_param import ManagedAuthBrowserConfigParam from ..browsers.browser_telemetry_categories_config_param import BrowserTelemetryCategoriesConfigParam __all__ = [ @@ -18,23 +19,21 @@ class ConnectionLoginParams(TypedDict, total=False): - browser_telemetry: Optional[BrowserTelemetry] - """Override the connection's default browser telemetry configuration for this - login. + browser: ManagedAuthBrowserConfigParam + """Browser configuration override for this login. - When omitted, the connection's browser_telemetry default is used. Uses the exact - create-browser configuration. + Omitted properties inherit the connection defaults. """ - proxy: Proxy - """Proxy selection. + browser_telemetry: Optional[BrowserTelemetry] + """Deprecated. - Provide either id or name. The proxy must be in the same project as the resource - referencing it. When selecting by name, the name must match exactly one active - proxy in the project. Ambiguous names return a 400; use id for stable - references. + Use browser.telemetry. Retained during migration for existing clients. """ + proxy: Proxy + """Deprecated. Use browser.proxy. Retained during migration for existing clients.""" + record_session: bool """Override the connection's default for recording this login's browser session. @@ -87,9 +86,9 @@ class BrowserTelemetryExport(TypedDict, total=False): class BrowserTelemetry(TypedDict, total=False): - """Override the connection's default browser telemetry configuration for this login. + """Deprecated. - When omitted, the connection's browser_telemetry default is used. Uses the exact create-browser configuration. + Use browser.telemetry. Retained during migration for existing clients. """ browser: BrowserTelemetryCategoriesConfigParam @@ -127,11 +126,7 @@ class BrowserTelemetry(TypedDict, total=False): class Proxy(TypedDict, total=False): - """Proxy selection. - - Provide either id or name. The proxy must be in the same project as the resource referencing it. - When selecting by name, the name must match exactly one active proxy in the project. Ambiguous names return a 400; use id for stable references. - """ + """Deprecated. Use browser.proxy. Retained during migration for existing clients.""" id: str """Proxy ID""" diff --git a/src/kernel/types/auth/connection_update_params.py b/src/kernel/types/auth/connection_update_params.py index f92a40c7..ad3fbe44 100644 --- a/src/kernel/types/auth/connection_update_params.py +++ b/src/kernel/types/auth/connection_update_params.py @@ -6,6 +6,7 @@ from typing_extensions import TypedDict from ..._types import SequenceNotStr +from .managed_auth_browser_config_param import ManagedAuthBrowserConfigParam from ..browsers.browser_telemetry_categories_config_param import BrowserTelemetryCategoriesConfigParam __all__ = [ @@ -35,11 +36,16 @@ class ConnectionUpdateParams(TypedDict, total=False): re-auth. """ - browser_telemetry: Optional[BrowserTelemetry] + browser: ManagedAuthBrowserConfigParam + """ + Browser configuration updates for future login, reauthentication, and + health-check sessions. Omitted properties remain unchanged. """ - Browser telemetry configuration used by future browser sessions for this - connection. Uses the exact create-browser configuration. Set enabled to false to - disable telemetry. + + browser_telemetry: Optional[BrowserTelemetry] + """Deprecated. + + Use browser.telemetry. Retained during migration for existing clients. """ credential: Credential @@ -65,13 +71,7 @@ class ConnectionUpdateParams(TypedDict, total=False): """Login page URL. Set to empty string to clear.""" proxy: Proxy - """Proxy selection. - - Provide either id or name. The proxy must be in the same project as the resource - referencing it. When selecting by name, the name must match exactly one active - proxy in the project. Ambiguous names return a 400; use id for stable - references. - """ + """Deprecated. Use browser.proxy. Retained during migration for existing clients.""" record_session: bool """Whether to record browser sessions for this connection by default""" @@ -125,8 +125,9 @@ class BrowserTelemetryExport(TypedDict, total=False): class BrowserTelemetry(TypedDict, total=False): - """ - Browser telemetry configuration used by future browser sessions for this connection. Uses the exact create-browser configuration. Set enabled to false to disable telemetry. + """Deprecated. + + Use browser.telemetry. Retained during migration for existing clients. """ browser: BrowserTelemetryCategoriesConfigParam @@ -186,11 +187,7 @@ class Credential(TypedDict, total=False): class Proxy(TypedDict, total=False): - """Proxy selection. - - Provide either id or name. The proxy must be in the same project as the resource referencing it. - When selecting by name, the name must match exactly one active proxy in the project. Ambiguous names return a 400; use id for stable references. - """ + """Deprecated. Use browser.proxy. Retained during migration for existing clients.""" id: str """Proxy ID""" diff --git a/src/kernel/types/auth/managed_auth.py b/src/kernel/types/auth/managed_auth.py index e3bf7b9c..f1bb2198 100644 --- a/src/kernel/types/auth/managed_auth.py +++ b/src/kernel/types/auth/managed_auth.py @@ -5,6 +5,7 @@ from typing_extensions import Literal from ..._models import BaseModel +from .managed_auth_browser_config import ManagedAuthBrowserConfig from ..browsers.browser_telemetry_categories_config import BrowserTelemetryCategoriesConfig __all__ = [ @@ -68,8 +69,9 @@ class BrowserTelemetryExport(BaseModel): class BrowserTelemetry(BaseModel): - """ - Browser telemetry configuration used by this connection's browser sessions by default. The exact create-browser configuration is preserved and can be overridden per-login. + """Deprecated. + + Use browser.telemetry. Retained during migration for existing clients. """ browser: Optional[BrowserTelemetryCategoriesConfig] = None @@ -324,6 +326,12 @@ class ManagedAuth(BaseModel): re-auth. """ + browser: Optional[ManagedAuthBrowserConfig] = None + """ + Default browser configuration for login, reauthentication, and health-check + sessions. + """ + browser_session_id: Optional[str] = None """ ID of the underlying browser session driving the current flow (present when flow @@ -332,10 +340,9 @@ class ManagedAuth(BaseModel): """ browser_telemetry: Optional[BrowserTelemetry] = None - """ - Browser telemetry configuration used by this connection's browser sessions by - default. The exact create-browser configuration is preserved and can be - overridden per-login. + """Deprecated. + + Use browser.telemetry. Retained during migration for existing clients. """ can_reauth: Optional[bool] = None @@ -513,7 +520,10 @@ class ManagedAuth(BaseModel): """URL where the browser landed after successful login""" proxy_id: Optional[str] = None - """ID of the proxy associated with this connection, if any.""" + """Deprecated. + + Read browser.proxy instead. Retained during migration for existing clients. + """ sign_in_options: Optional[List[SignInOption]] = None """ diff --git a/src/kernel/types/auth/managed_auth_browser_config.py b/src/kernel/types/auth/managed_auth_browser_config.py new file mode 100644 index 00000000..a4d63de7 --- /dev/null +++ b/src/kernel/types/auth/managed_auth_browser_config.py @@ -0,0 +1,118 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from ..browser_proxy_config import BrowserProxyConfig +from ..browsers.browser_telemetry_categories_config import BrowserTelemetryCategoriesConfig + +__all__ = [ + "ManagedAuthBrowserConfig", + "Telemetry", + "TelemetryExport", + "TelemetryExportOtlp", + "TelemetryExportOtlpDestination", +] + + +class TelemetryExportOtlpDestination(BaseModel): + """OTLP destination to export this session's captured telemetry to. + + Provide either id or name. Requires telemetry capture to be enabled. + """ + + id: Optional[str] = None + """OTLP destination ID""" + + name: Optional[str] = None + """OTLP destination name""" + + +class TelemetryExportOtlp(BaseModel): + """ + Export captured telemetry over OTLP to one of the org's configured destinations. + """ + + destination: Optional[TelemetryExportOtlpDestination] = None + """OTLP destination to export this session's captured telemetry to. + + Provide either id or name. Requires telemetry capture to be enabled. + """ + + enabled: Optional[bool] = None + """Whether to export captured telemetry over OTLP. + + Setting destination implies enabled=true, so this only needs to be set + explicitly to disable export (enabled=false with a destination is rejected). + """ + + +class TelemetryExport(BaseModel): + """Where to export this session's captured telemetry. + + Omit to capture without exporting. + """ + + otlp: Optional[TelemetryExportOtlp] = None + """ + Export captured telemetry over OTLP to one of the org's configured destinations. + """ + + +class Telemetry(BaseModel): + """Browser telemetry configuration using the same semantics as browser create.""" + + browser: Optional[BrowserTelemetryCategoriesConfig] = None + """Per-category capture flags. + + The operational categories (control, connection, system, captcha) are captured + whenever telemetry is enabled; set one to enabled=false to opt out. The CDP + categories (console, network, page, interaction) and screenshot are off by + default; set enabled=true to opt in. On create, provided categories layer onto + the default set. On update, provided categories merge onto the session's current + config; when no telemetry is active this falls back to the default set (matching + create). If browser is omitted or empty, the default set is used. A browser + config that disables every category stops capture on update and starts no + capture on create. + """ + + enabled: Optional[bool] = None + """Request shortcut for browser telemetry capture. + + True enables capture; with no browser category settings it captures the default + set (control, connection, system, captcha), and any browser category settings + are layered onto that default set. On update, enabled=true resolves the config + fresh from the default set plus any provided categories, replacing the session's + current selection rather than merging onto it; omit enabled to merge categories + onto the current selection instead. False stops capture on update and starts no + capture on create. enabled=false cannot be combined with browser category + settings. + """ + + export: Optional[TelemetryExport] = None + """Where to export this session's captured telemetry. + + Omit to capture without exporting. + """ + + +class ManagedAuthBrowserConfig(BaseModel): + """ + Browser configuration applied to browser sessions created for a managed auth connection. Managed auth controls the profile, headless mode, timeout, start URL, kiosk mode, and viewport. + """ + + proxy: Optional[BrowserProxyConfig] = None + """Proxy configuration for managed auth browser sessions. + + Omit on create to derive the default from stealth, or on update and login to + preserve or inherit the connection default. + """ + + stealth: Optional[bool] = None + """Whether managed auth browser sessions use stealth mode. + + Defaults to true when omitted. + """ + + telemetry: Optional[Telemetry] = None + """Browser telemetry configuration using the same semantics as browser create.""" diff --git a/src/kernel/types/auth/managed_auth_browser_config_param.py b/src/kernel/types/auth/managed_auth_browser_config_param.py new file mode 100644 index 00000000..7f8650e0 --- /dev/null +++ b/src/kernel/types/auth/managed_auth_browser_config_param.py @@ -0,0 +1,120 @@ +# 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 TypedDict + +from ..browser_proxy_config_param import BrowserProxyConfigParam +from ..browsers.browser_telemetry_categories_config_param import BrowserTelemetryCategoriesConfigParam + +__all__ = [ + "ManagedAuthBrowserConfigParam", + "Telemetry", + "TelemetryExport", + "TelemetryExportOtlp", + "TelemetryExportOtlpDestination", +] + + +class TelemetryExportOtlpDestination(TypedDict, total=False): + """OTLP destination to export this session's captured telemetry to. + + Provide either id or name. Requires telemetry capture to be enabled. + """ + + id: str + """OTLP destination ID""" + + name: str + """OTLP destination name""" + + +class TelemetryExportOtlp(TypedDict, total=False): + """ + Export captured telemetry over OTLP to one of the org's configured destinations. + """ + + destination: TelemetryExportOtlpDestination + """OTLP destination to export this session's captured telemetry to. + + Provide either id or name. Requires telemetry capture to be enabled. + """ + + enabled: bool + """Whether to export captured telemetry over OTLP. + + Setting destination implies enabled=true, so this only needs to be set + explicitly to disable export (enabled=false with a destination is rejected). + """ + + +class TelemetryExport(TypedDict, total=False): + """Where to export this session's captured telemetry. + + Omit to capture without exporting. + """ + + otlp: TelemetryExportOtlp + """ + Export captured telemetry over OTLP to one of the org's configured destinations. + """ + + +class Telemetry(TypedDict, total=False): + """Browser telemetry configuration using the same semantics as browser create.""" + + browser: BrowserTelemetryCategoriesConfigParam + """Per-category capture flags. + + The operational categories (control, connection, system, captcha) are captured + whenever telemetry is enabled; set one to enabled=false to opt out. The CDP + categories (console, network, page, interaction) and screenshot are off by + default; set enabled=true to opt in. On create, provided categories layer onto + the default set. On update, provided categories merge onto the session's current + config; when no telemetry is active this falls back to the default set (matching + create). If browser is omitted or empty, the default set is used. A browser + config that disables every category stops capture on update and starts no + capture on create. + """ + + enabled: bool + """Request shortcut for browser telemetry capture. + + True enables capture; with no browser category settings it captures the default + set (control, connection, system, captcha), and any browser category settings + are layered onto that default set. On update, enabled=true resolves the config + fresh from the default set plus any provided categories, replacing the session's + current selection rather than merging onto it; omit enabled to merge categories + onto the current selection instead. False stops capture on update and starts no + capture on create. enabled=false cannot be combined with browser category + settings. + """ + + export: TelemetryExport + """Where to export this session's captured telemetry. + + Omit to capture without exporting. + """ + + +class ManagedAuthBrowserConfigParam(TypedDict, total=False): + """ + Browser configuration applied to browser sessions created for a managed auth connection. Managed auth controls the profile, headless mode, timeout, start URL, kiosk mode, and viewport. + """ + + proxy: BrowserProxyConfigParam + """Proxy configuration for managed auth browser sessions. + + Omit on create to derive the default from stealth, or on update and login to + preserve or inherit the connection default. + """ + + stealth: bool + """Whether managed auth browser sessions use stealth mode. + + Defaults to true when omitted. + """ + + telemetry: Optional[Telemetry] + """Browser telemetry configuration using the same semantics as browser create.""" diff --git a/src/kernel/types/browser_create_params.py b/src/kernel/types/browser_create_params.py index f09a29cf..262383c5 100644 --- a/src/kernel/types/browser_create_params.py +++ b/src/kernel/types/browser_create_params.py @@ -6,6 +6,7 @@ from typing_extensions import TypedDict from .tags_param import TagsParam +from .browser_proxy_config_param import BrowserProxyConfigParam from .shared_params.browser_profile import BrowserProfile from .shared_params.browser_viewport import BrowserViewport from .shared_params.browser_extension import BrowserExtension @@ -70,10 +71,23 @@ class BrowserCreateParams(TypedDict, total=False): into the browser session. Profiles must be created beforehand. """ + proxy: BrowserProxyConfigParam + """Proxy configuration for the browser session. + + Cannot be combined with proxy_id. Omit to use the browser default: stealth + browsers use Kernel's default stealth proxy, while non-stealth browsers use + direct egress. Set mode to direct to force direct egress regardless of stealth. + Set mode to default to explicitly use the browser default: Kernel's default + stealth proxy when stealth=true, or direct egress when stealth=false. Select id + or name to use that proxy regardless of stealth. Proxy selection does not change + stealth or CAPTCHA solver behavior. + """ + proxy_id: str """Optional proxy to associate to the browser session. - Must reference a proxy in the same project as the browser session. + Must reference a proxy in the same project as the browser session. Deprecated in + favor of proxy. """ start_url: str @@ -84,9 +98,12 @@ class BrowserCreateParams(TypedDict, total=False): """ stealth: bool - """ - If true, launches the browser in stealth mode to reduce detection by anti-bot - mechanisms. + """If true, launches the browser in stealth mode and enables the CAPTCHA solver. + + Defaults to false. When proxy is omitted, stealth browsers use Kernel's default + stealth proxy and non-stealth browsers use direct egress. An explicit proxy + configuration changes only egress; it does not enable or disable stealth or the + CAPTCHA solver. """ tags: TagsParam diff --git a/src/kernel/types/browser_create_response.py b/src/kernel/types/browser_create_response.py index 0dbf867c..8618e7e5 100644 --- a/src/kernel/types/browser_create_response.py +++ b/src/kernel/types/browser_create_response.py @@ -6,6 +6,7 @@ from .tags import Tags from .profile import Profile from .._models import BaseModel +from .browser_proxy import BrowserProxy from .browser_usage import BrowserUsage from .browser_pool_ref import BrowserPoolRef from .shared.browser_viewport import BrowserViewport @@ -79,8 +80,14 @@ class BrowserCreateResponse(BaseModel): when the session ends. Omitted when no profile is attached. """ + proxy: Optional[BrowserProxy] = None + """Resolved proxy configuration for this browser session.""" + proxy_id: Optional[str] = None - """ID of the proxy associated with this browser session, if any.""" + """ID of the proxy associated with this browser session, if any. + + Deprecated in favor of proxy. + """ start_url: Optional[str] = None """URL the session was asked to navigate to on creation, if any. diff --git a/src/kernel/types/browser_list_response.py b/src/kernel/types/browser_list_response.py index f3215ed5..8f4917a8 100644 --- a/src/kernel/types/browser_list_response.py +++ b/src/kernel/types/browser_list_response.py @@ -6,6 +6,7 @@ from .tags import Tags from .profile import Profile from .._models import BaseModel +from .browser_proxy import BrowserProxy from .browser_usage import BrowserUsage from .browser_pool_ref import BrowserPoolRef from .shared.browser_viewport import BrowserViewport @@ -79,8 +80,14 @@ class BrowserListResponse(BaseModel): when the session ends. Omitted when no profile is attached. """ + proxy: Optional[BrowserProxy] = None + """Resolved proxy configuration for this browser session.""" + proxy_id: Optional[str] = None - """ID of the proxy associated with this browser session, if any.""" + """ID of the proxy associated with this browser session, if any. + + Deprecated in favor of proxy. + """ start_url: Optional[str] = None """URL the session was asked to navigate to on creation, if any. diff --git a/src/kernel/types/browser_pool_acquire_response.py b/src/kernel/types/browser_pool_acquire_response.py index 1dabc492..7d849958 100644 --- a/src/kernel/types/browser_pool_acquire_response.py +++ b/src/kernel/types/browser_pool_acquire_response.py @@ -6,6 +6,7 @@ from .tags import Tags from .profile import Profile from .._models import BaseModel +from .browser_proxy import BrowserProxy from .browser_usage import BrowserUsage from .browser_pool_ref import BrowserPoolRef from .shared.browser_viewport import BrowserViewport @@ -79,8 +80,14 @@ class BrowserPoolAcquireResponse(BaseModel): when the session ends. Omitted when no profile is attached. """ + proxy: Optional[BrowserProxy] = None + """Resolved proxy configuration for this browser session.""" + proxy_id: Optional[str] = None - """ID of the proxy associated with this browser session, if any.""" + """ID of the proxy associated with this browser session, if any. + + Deprecated in favor of proxy. + """ start_url: Optional[str] = None """URL the session was asked to navigate to on creation, if any. diff --git a/src/kernel/types/browser_proxy.py b/src/kernel/types/browser_proxy.py new file mode 100644 index 00000000..20078429 --- /dev/null +++ b/src/kernel/types/browser_proxy.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel +from .browser_proxy_mode import BrowserProxyMode + +__all__ = ["BrowserProxy"] + + +class BrowserProxy(BaseModel): + """Resolved proxy configuration for a browser session. + + Selected proxies are returned by stable ID. + """ + + id: Optional[str] = None + """Selected proxy ID.""" + + mode: Optional[BrowserProxyMode] = None + """Proxy egress mode. + + direct forces no proxy regardless of stealth. default uses the browser's + stealth-derived default: Kernel's default stealth proxy when stealth=true, or + direct egress when stealth=false. default is primarily useful on browser update + to restore the browser default after selected-proxy egress. + """ + + name: Optional[str] = None + """Selected proxy name.""" diff --git a/src/kernel/types/browser_proxy_config.py b/src/kernel/types/browser_proxy_config.py new file mode 100644 index 00000000..6db5d5ec --- /dev/null +++ b/src/kernel/types/browser_proxy_config.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel +from .browser_proxy_mode import BrowserProxyMode + +__all__ = ["BrowserProxyConfig"] + + +class BrowserProxyConfig(BaseModel): + """Browser proxy configuration. + + Provide exactly one of mode, id, or name; an empty object is invalid. + Set mode to direct for no proxy regardless of stealth. Set mode to default to use the browser's stealth-derived default: Kernel's default stealth proxy when stealth=true, or direct egress when stealth=false. + Select id or name to use that proxy regardless of stealth. The selected proxy must be in the same project as the browser. Names must match exactly one active proxy; use id for stable references. + Proxy configuration changes only egress and does not change stealth or CAPTCHA solver behavior. A stealth browser using mode=direct still runs in stealth mode with the CAPTCHA solver enabled. + When proxy is omitted on browser creation, stealth browsers use Kernel's default stealth proxy and non-stealth browsers use direct egress. When omitted on update, the current configuration is unchanged. + """ + + id: Optional[str] = None + """Proxy ID.""" + + mode: Optional[BrowserProxyMode] = None + """Proxy egress mode. + + direct forces no proxy regardless of stealth. default uses the browser's + stealth-derived default: Kernel's default stealth proxy when stealth=true, or + direct egress when stealth=false. default is primarily useful on browser update + to restore the browser default after selected-proxy egress. + """ + + name: Optional[str] = None + """Proxy name. Must match exactly one active proxy in the project.""" diff --git a/src/kernel/types/browser_proxy_config_param.py b/src/kernel/types/browser_proxy_config_param.py new file mode 100644 index 00000000..0fc83157 --- /dev/null +++ b/src/kernel/types/browser_proxy_config_param.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from .browser_proxy_mode import BrowserProxyMode + +__all__ = ["BrowserProxyConfigParam"] + + +class BrowserProxyConfigParam(TypedDict, total=False): + """Browser proxy configuration. + + Provide exactly one of mode, id, or name; an empty object is invalid. + Set mode to direct for no proxy regardless of stealth. Set mode to default to use the browser's stealth-derived default: Kernel's default stealth proxy when stealth=true, or direct egress when stealth=false. + Select id or name to use that proxy regardless of stealth. The selected proxy must be in the same project as the browser. Names must match exactly one active proxy; use id for stable references. + Proxy configuration changes only egress and does not change stealth or CAPTCHA solver behavior. A stealth browser using mode=direct still runs in stealth mode with the CAPTCHA solver enabled. + When proxy is omitted on browser creation, stealth browsers use Kernel's default stealth proxy and non-stealth browsers use direct egress. When omitted on update, the current configuration is unchanged. + """ + + id: str + """Proxy ID.""" + + mode: BrowserProxyMode + """Proxy egress mode. + + direct forces no proxy regardless of stealth. default uses the browser's + stealth-derived default: Kernel's default stealth proxy when stealth=true, or + direct egress when stealth=false. default is primarily useful on browser update + to restore the browser default after selected-proxy egress. + """ + + name: str + """Proxy name. Must match exactly one active proxy in the project.""" diff --git a/src/kernel/types/browser_proxy_mode.py b/src/kernel/types/browser_proxy_mode.py new file mode 100644 index 00000000..c8b51c1b --- /dev/null +++ b/src/kernel/types/browser_proxy_mode.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["BrowserProxyMode"] + +BrowserProxyMode: TypeAlias = Literal["direct", "default"] diff --git a/src/kernel/types/browser_retrieve_response.py b/src/kernel/types/browser_retrieve_response.py index 5563ab98..3afc6082 100644 --- a/src/kernel/types/browser_retrieve_response.py +++ b/src/kernel/types/browser_retrieve_response.py @@ -6,6 +6,7 @@ from .tags import Tags from .profile import Profile from .._models import BaseModel +from .browser_proxy import BrowserProxy from .browser_usage import BrowserUsage from .browser_pool_ref import BrowserPoolRef from .shared.browser_viewport import BrowserViewport @@ -79,8 +80,14 @@ class BrowserRetrieveResponse(BaseModel): when the session ends. Omitted when no profile is attached. """ + proxy: Optional[BrowserProxy] = None + """Resolved proxy configuration for this browser session.""" + proxy_id: Optional[str] = None - """ID of the proxy associated with this browser session, if any.""" + """ID of the proxy associated with this browser session, if any. + + Deprecated in favor of proxy. + """ start_url: Optional[str] = None """URL the session was asked to navigate to on creation, if any. diff --git a/src/kernel/types/browser_update_params.py b/src/kernel/types/browser_update_params.py index 01b19c8a..a9ea675c 100644 --- a/src/kernel/types/browser_update_params.py +++ b/src/kernel/types/browser_update_params.py @@ -6,6 +6,7 @@ from typing_extensions import TypedDict from .tags_param import TagsParam +from .browser_proxy_config_param import BrowserProxyConfigParam from .shared_params.browser_profile import BrowserProfile from .shared_params.browser_viewport import BrowserViewport from .browsers.browser_telemetry_categories_config_param import BrowserTelemetryCategoriesConfigParam @@ -24,7 +25,7 @@ class BrowserUpdateParams(TypedDict, total=False): disable_default_proxy: bool """ If true, stealth browsers connect directly instead of using the default stealth - proxy. + proxy. Deprecated in favor of proxy.mode. """ name: Optional[str] @@ -40,10 +41,22 @@ class BrowserUpdateParams(TypedDict, total=False): Only allowed if the session does not already have a profile loaded. """ + proxy: BrowserProxyConfigParam + """Proxy configuration to apply. + + Omit to leave the current configuration unchanged. Cannot be combined with + proxy_id or disable_default_proxy. Set mode to direct to switch to direct egress + regardless of stealth. Set mode to default to restore the browser default after + using a selected proxy: Kernel's default stealth proxy for a stealth browser, or + direct egress for a non-stealth browser. Updating proxy does not change stealth + or CAPTCHA solver behavior. + """ + proxy_id: Optional[str] """ID of the proxy to use. - Omit to leave unchanged, set to empty string to remove proxy. + Omit to leave unchanged, set to empty string to remove proxy. Deprecated in + favor of proxy. """ tags: Optional[TagsParam] diff --git a/src/kernel/types/browser_update_response.py b/src/kernel/types/browser_update_response.py index e4602bd5..d60fe900 100644 --- a/src/kernel/types/browser_update_response.py +++ b/src/kernel/types/browser_update_response.py @@ -6,6 +6,7 @@ from .tags import Tags from .profile import Profile from .._models import BaseModel +from .browser_proxy import BrowserProxy from .browser_usage import BrowserUsage from .browser_pool_ref import BrowserPoolRef from .shared.browser_viewport import BrowserViewport @@ -79,8 +80,14 @@ class BrowserUpdateResponse(BaseModel): when the session ends. Omitted when no profile is attached. """ + proxy: Optional[BrowserProxy] = None + """Resolved proxy configuration for this browser session.""" + proxy_id: Optional[str] = None - """ID of the proxy associated with this browser session, if any.""" + """ID of the proxy associated with this browser session, if any. + + Deprecated in favor of proxy. + """ start_url: Optional[str] = None """URL the session was asked to navigate to on creation, if any. diff --git a/src/kernel/types/invocation_list_browsers_response.py b/src/kernel/types/invocation_list_browsers_response.py index 9abb0fa6..5cfe7e89 100644 --- a/src/kernel/types/invocation_list_browsers_response.py +++ b/src/kernel/types/invocation_list_browsers_response.py @@ -6,6 +6,7 @@ from .tags import Tags from .profile import Profile from .._models import BaseModel +from .browser_proxy import BrowserProxy from .browser_usage import BrowserUsage from .browser_pool_ref import BrowserPoolRef from .shared.browser_viewport import BrowserViewport @@ -79,8 +80,14 @@ class Browser(BaseModel): when the session ends. Omitted when no profile is attached. """ + proxy: Optional[BrowserProxy] = None + """Resolved proxy configuration for this browser session.""" + proxy_id: Optional[str] = None - """ID of the proxy associated with this browser session, if any.""" + """ID of the proxy associated with this browser session, if any. + + Deprecated in favor of proxy. + """ start_url: Optional[str] = None """URL the session was asked to navigate to on creation, if any. diff --git a/tests/api_resources/auth/test_connections.py b/tests/api_resources/auth/test_connections.py index 321e84e4..f026a2cd 100644 --- a/tests/api_resources/auth/test_connections.py +++ b/tests/api_resources/auth/test_connections.py @@ -40,6 +40,37 @@ def test_method_create_with_all_params(self, client: Kernel) -> None: profile_name="user-123", allowed_domains=["login.netflix.com", "auth.netflix.com"], auto_reauth=True, + browser={ + "proxy": { + "id": "x", + "mode": "direct", + "name": "x", + }, + "stealth": False, + "telemetry": { + "browser": { + "captcha": {"enabled": True}, + "connection": {"enabled": True}, + "console": {"enabled": True}, + "control": {"enabled": True}, + "interaction": {"enabled": True}, + "network": {"enabled": True}, + "page": {"enabled": True}, + "screenshot": {"enabled": True}, + "system": {"enabled": True}, + }, + "enabled": True, + "export": { + "otlp": { + "destination": { + "id": "id", + "name": "name", + }, + "enabled": True, + } + }, + }, + }, browser_telemetry={ "browser": { "captcha": {"enabled": True}, @@ -166,6 +197,37 @@ def test_method_update_with_all_params(self, client: Kernel) -> None: id="id", allowed_domains=["login.netflix.com", "auth.netflix.com"], auto_reauth=True, + browser={ + "proxy": { + "id": "x", + "mode": "direct", + "name": "x", + }, + "stealth": False, + "telemetry": { + "browser": { + "captcha": {"enabled": True}, + "connection": {"enabled": True}, + "console": {"enabled": True}, + "control": {"enabled": True}, + "interaction": {"enabled": True}, + "network": {"enabled": True}, + "page": {"enabled": True}, + "screenshot": {"enabled": True}, + "system": {"enabled": True}, + }, + "enabled": True, + "export": { + "otlp": { + "destination": { + "id": "id", + "name": "name", + }, + "enabled": True, + } + }, + }, + }, browser_telemetry={ "browser": { "captcha": {"enabled": True}, @@ -377,6 +439,37 @@ def test_method_login(self, client: Kernel) -> None: def test_method_login_with_all_params(self, client: Kernel) -> None: connection = client.auth.connections.login( id="id", + browser={ + "proxy": { + "id": "x", + "mode": "direct", + "name": "x", + }, + "stealth": False, + "telemetry": { + "browser": { + "captcha": {"enabled": True}, + "connection": {"enabled": True}, + "console": {"enabled": True}, + "control": {"enabled": True}, + "interaction": {"enabled": True}, + "network": {"enabled": True}, + "page": {"enabled": True}, + "screenshot": {"enabled": True}, + "system": {"enabled": True}, + }, + "enabled": True, + "export": { + "otlp": { + "destination": { + "id": "id", + "name": "name", + }, + "enabled": True, + } + }, + }, + }, browser_telemetry={ "browser": { "captcha": {"enabled": True}, @@ -581,6 +674,37 @@ async def test_method_create_with_all_params(self, async_client: AsyncKernel) -> profile_name="user-123", allowed_domains=["login.netflix.com", "auth.netflix.com"], auto_reauth=True, + browser={ + "proxy": { + "id": "x", + "mode": "direct", + "name": "x", + }, + "stealth": False, + "telemetry": { + "browser": { + "captcha": {"enabled": True}, + "connection": {"enabled": True}, + "console": {"enabled": True}, + "control": {"enabled": True}, + "interaction": {"enabled": True}, + "network": {"enabled": True}, + "page": {"enabled": True}, + "screenshot": {"enabled": True}, + "system": {"enabled": True}, + }, + "enabled": True, + "export": { + "otlp": { + "destination": { + "id": "id", + "name": "name", + }, + "enabled": True, + } + }, + }, + }, browser_telemetry={ "browser": { "captcha": {"enabled": True}, @@ -707,6 +831,37 @@ async def test_method_update_with_all_params(self, async_client: AsyncKernel) -> id="id", allowed_domains=["login.netflix.com", "auth.netflix.com"], auto_reauth=True, + browser={ + "proxy": { + "id": "x", + "mode": "direct", + "name": "x", + }, + "stealth": False, + "telemetry": { + "browser": { + "captcha": {"enabled": True}, + "connection": {"enabled": True}, + "console": {"enabled": True}, + "control": {"enabled": True}, + "interaction": {"enabled": True}, + "network": {"enabled": True}, + "page": {"enabled": True}, + "screenshot": {"enabled": True}, + "system": {"enabled": True}, + }, + "enabled": True, + "export": { + "otlp": { + "destination": { + "id": "id", + "name": "name", + }, + "enabled": True, + } + }, + }, + }, browser_telemetry={ "browser": { "captcha": {"enabled": True}, @@ -918,6 +1073,37 @@ async def test_method_login(self, async_client: AsyncKernel) -> None: async def test_method_login_with_all_params(self, async_client: AsyncKernel) -> None: connection = await async_client.auth.connections.login( id="id", + browser={ + "proxy": { + "id": "x", + "mode": "direct", + "name": "x", + }, + "stealth": False, + "telemetry": { + "browser": { + "captcha": {"enabled": True}, + "connection": {"enabled": True}, + "console": {"enabled": True}, + "control": {"enabled": True}, + "interaction": {"enabled": True}, + "network": {"enabled": True}, + "page": {"enabled": True}, + "screenshot": {"enabled": True}, + "system": {"enabled": True}, + }, + "enabled": True, + "export": { + "otlp": { + "destination": { + "id": "id", + "name": "name", + }, + "enabled": True, + } + }, + }, + }, browser_telemetry={ "browser": { "captcha": {"enabled": True}, diff --git a/tests/api_resources/test_browsers.py b/tests/api_resources/test_browsers.py index a8f93efc..8b0a6182 100644 --- a/tests/api_resources/test_browsers.py +++ b/tests/api_resources/test_browsers.py @@ -51,6 +51,11 @@ def test_method_create_with_all_params(self, client: Kernel) -> None: "name": "name", "save_changes": True, }, + proxy={ + "id": "x", + "mode": "direct", + "name": "x", + }, proxy_id="proxy_id", start_url="https://example.com", stealth=True, @@ -183,6 +188,11 @@ def test_method_update_with_all_params(self, client: Kernel) -> None: "name": "name", "save_changes": True, }, + proxy={ + "id": "x", + "mode": "direct", + "name": "x", + }, proxy_id="proxy_id", tags={ "team": "backend", @@ -496,6 +506,11 @@ async def test_method_create_with_all_params(self, async_client: AsyncKernel) -> "name": "name", "save_changes": True, }, + proxy={ + "id": "x", + "mode": "direct", + "name": "x", + }, proxy_id="proxy_id", start_url="https://example.com", stealth=True, @@ -628,6 +643,11 @@ async def test_method_update_with_all_params(self, async_client: AsyncKernel) -> "name": "name", "save_changes": True, }, + proxy={ + "id": "x", + "mode": "direct", + "name": "x", + }, proxy_id="proxy_id", tags={ "team": "backend", From bacb44f61f32e82367c264bab9120595f93f5cbf Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Sun, 9 Aug 2026 16:32:50 +0000 Subject: [PATCH 3/4] feat: Persist CUA-TS reauth blockers before failure Stainless-Generated-From: 207ec8076e1bcc7c24257506b0d58a18837505bb --- src/kernel/types/auth/managed_auth.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/kernel/types/auth/managed_auth.py b/src/kernel/types/auth/managed_auth.py index f1bb2198..3625cbbc 100644 --- a/src/kernel/types/auth/managed_auth.py +++ b/src/kernel/types/auth/managed_auth.py @@ -369,6 +369,7 @@ class ManagedAuth(BaseModel): "requires_totp_without_secret", "requires_sms_code", "requires_email_code", + "requires_customer_input", ] ] = None """ @@ -401,6 +402,8 @@ class ManagedAuth(BaseModel): automatically - `requires_email_code` — flow needs an email code that cannot be received automatically + - `requires_customer_input` — flow needs another field or choice that is + unavailable during unattended re-authentication """ choices: Optional[List[Choice]] = None From 2afe161bedb272fe458ae2f0adcd3df140cb151d Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 16:33:45 +0000 Subject: [PATCH 4/4] feat: Managed auth: free-plan connections (cap 3), credentials, 6h floor (packaging PR 5) Stainless-Generated-From: 27294af8c209bd0f75875350803cd3ec10f081a9 --- src/kernel/resources/auth/connections.py | 14 ++++++++------ src/kernel/types/auth/connection_create_params.py | 5 +++-- src/kernel/types/auth/managed_auth.py | 5 +++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/kernel/resources/auth/connections.py b/src/kernel/resources/auth/connections.py index a84d42af..56785ea2 100644 --- a/src/kernel/resources/auth/connections.py +++ b/src/kernel/resources/auth/connections.py @@ -137,9 +137,10 @@ def create( health_check_interval: Interval in seconds between automatic health checks. When set, the system periodically verifies the authentication status and triggers re-authentication - if needed. Maximum is 86400 (24 hours). Default is 3600 (1 hour). The minimum - depends on your plan: Enterprise: 300 (5 minutes), Startup: 1200 (20 minutes), - Hobbyist: 3600 (1 hour). + if needed. Maximum is 86400 (24 hours). Default is 3600 (1 hour) or your plan + minimum, whichever is larger. The minimum depends on your plan: Enterprise: 300 + (5 minutes), Startup: 1200 (20 minutes), Hobbyist: 3600 (1 hour), Free: 21600 (6 + hours). health_checks: Whether to enable periodic health checks. When false, the system will not automatically verify authentication status, and `auto_reauth` has no effect on @@ -749,9 +750,10 @@ async def create( health_check_interval: Interval in seconds between automatic health checks. When set, the system periodically verifies the authentication status and triggers re-authentication - if needed. Maximum is 86400 (24 hours). Default is 3600 (1 hour). The minimum - depends on your plan: Enterprise: 300 (5 minutes), Startup: 1200 (20 minutes), - Hobbyist: 3600 (1 hour). + if needed. Maximum is 86400 (24 hours). Default is 3600 (1 hour) or your plan + minimum, whichever is larger. The minimum depends on your plan: Enterprise: 300 + (5 minutes), Startup: 1200 (20 minutes), Hobbyist: 3600 (1 hour), Free: 21600 (6 + hours). health_checks: Whether to enable periodic health checks. When false, the system will not automatically verify authentication status, and `auto_reauth` has no effect on diff --git a/src/kernel/types/auth/connection_create_params.py b/src/kernel/types/auth/connection_create_params.py index 23d53cb2..2fdc808b 100644 --- a/src/kernel/types/auth/connection_create_params.py +++ b/src/kernel/types/auth/connection_create_params.py @@ -88,8 +88,9 @@ class ConnectionCreateParams(TypedDict, total=False): When set, the system periodically verifies the authentication status and triggers re-authentication if needed. Maximum is 86400 (24 hours). Default is - 3600 (1 hour). The minimum depends on your plan: Enterprise: 300 (5 minutes), - Startup: 1200 (20 minutes), Hobbyist: 3600 (1 hour). + 3600 (1 hour) or your plan minimum, whichever is larger. The minimum depends on + your plan: Enterprise: 300 (5 minutes), Startup: 1200 (20 minutes), Hobbyist: + 3600 (1 hour), Free: 21600 (6 hours). """ health_checks: bool diff --git a/src/kernel/types/auth/managed_auth.py b/src/kernel/types/auth/managed_auth.py index 3625cbbc..9db58514 100644 --- a/src/kernel/types/auth/managed_auth.py +++ b/src/kernel/types/auth/managed_auth.py @@ -469,8 +469,9 @@ class ManagedAuth(BaseModel): When set, the system periodically verifies the authentication status and triggers re-authentication if needed. Maximum is 86400 (24 hours). Default is - 3600 (1 hour). The minimum depends on your plan: Enterprise: 300 (5 minutes), - Startup: 1200 (20 minutes), Hobbyist: 3600 (1 hour). + 3600 (1 hour) or your plan minimum, whichever is larger. The minimum depends on + your plan: Enterprise: 300 (5 minutes), Startup: 1200 (20 minutes), Hobbyist: + 3600 (1 hour), Free: 21600 (6 hours). """ health_checks: Optional[bool] = None