From eb86c27107781172bf740e0af3833eee86b7cb60 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 16 Oct 2025 20:31:16 +0000
Subject: [PATCH 1/3] feat: Phani/deploy with GitHub url
---
.stats.yml | 6 +-
README.md | 1 -
api.md | 6 +-
src/kernel/resources/deployments.py | 28 +-
src/kernel/resources/extensions.py | 304 ++++++++--------
src/kernel/types/__init__.py | 4 +-
src/kernel/types/deployment_create_params.py | 41 ++-
...d_params.py => extension_create_params.py} | 4 +-
...sponse.py => extension_create_response.py} | 4 +-
tests/api_resources/test_deployments.py | 56 +--
tests/api_resources/test_extensions.py | 324 +++++++++---------
11 files changed, 410 insertions(+), 368 deletions(-)
rename src/kernel/types/{extension_upload_params.py => extension_create_params.py} (81%)
rename src/kernel/types/{extension_upload_response.py => extension_create_response.py} (88%)
diff --git a/.stats.yml b/.stats.yml
index 6bb4af83..2bd40ccf 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 57
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-6c765f1c4ce1c4dd4ceb371f56bf047aa79af36031ba43cbd68fa16a5fdb9bb3.yml
-openapi_spec_hash: e9086f69281360f4e0895c9274a59531
-config_hash: deadfc4d2b0a947673bcf559b5db6e1b
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-6eaa6f5654abc94549962d7db1e8c7936af1f815bb3abe2f8249959394da1278.yml
+openapi_spec_hash: 31ece7cd801e74228b80a8112a762e56
+config_hash: 3fc2057ce765bc5f27785a694ed0f553
diff --git a/README.md b/README.md
index beec3f01..ae0066ec 100644
--- a/README.md
+++ b/README.md
@@ -218,7 +218,6 @@ from kernel import Kernel
client = Kernel()
client.deployments.create(
- entrypoint_rel_path="src/app.py",
file=Path("/path/to/file"),
)
```
diff --git a/api.md b/api.md
index 6059a78d..9f219359 100644
--- a/api.md
+++ b/api.md
@@ -202,13 +202,13 @@ Methods:
Types:
```python
-from kernel.types import ExtensionListResponse, ExtensionUploadResponse
+from kernel.types import ExtensionCreateResponse, ExtensionListResponse
```
Methods:
+- client.extensions.create(\*\*params) -> ExtensionCreateResponse
+- client.extensions.retrieve(id_or_name) -> BinaryAPIResponse
- client.extensions.list() -> ExtensionListResponse
- client.extensions.delete(id_or_name) -> None
-- client.extensions.download(id_or_name) -> BinaryAPIResponse
- client.extensions.download_from_chrome_store(\*\*params) -> BinaryAPIResponse
-- client.extensions.upload(\*\*params) -> ExtensionUploadResponse
diff --git a/src/kernel/resources/deployments.py b/src/kernel/resources/deployments.py
index 15812440..bdc200f1 100644
--- a/src/kernel/resources/deployments.py
+++ b/src/kernel/resources/deployments.py
@@ -52,11 +52,12 @@ def with_streaming_response(self) -> DeploymentsResourceWithStreamingResponse:
def create(
self,
*,
- entrypoint_rel_path: str,
- file: FileTypes,
+ entrypoint_rel_path: str | Omit = omit,
env_vars: Dict[str, str] | Omit = omit,
+ file: FileTypes | Omit = omit,
force: bool | Omit = omit,
region: Literal["aws.us-east-1a"] | Omit = omit,
+ source: deployment_create_params.Source | Omit = omit,
version: 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.
@@ -71,15 +72,17 @@ def create(
Args:
entrypoint_rel_path: Relative path to the entrypoint of the application
- file: ZIP file containing the application source directory
-
env_vars: Map of environment variables to set for the deployed application. Each key-value
pair represents an environment variable.
+ file: ZIP file containing the application source directory
+
force: Allow overwriting an existing app version
region: Region for deployment. Currently we only support "aws.us-east-1a"
+ source: Source from which to fetch application code.
+
version: Version of the application. Can be any string.
extra_headers: Send extra headers
@@ -93,10 +96,11 @@ def create(
body = deepcopy_minimal(
{
"entrypoint_rel_path": entrypoint_rel_path,
- "file": file,
"env_vars": env_vars,
+ "file": file,
"force": force,
"region": region,
+ "source": source,
"version": version,
}
)
@@ -271,11 +275,12 @@ def with_streaming_response(self) -> AsyncDeploymentsResourceWithStreamingRespon
async def create(
self,
*,
- entrypoint_rel_path: str,
- file: FileTypes,
+ entrypoint_rel_path: str | Omit = omit,
env_vars: Dict[str, str] | Omit = omit,
+ file: FileTypes | Omit = omit,
force: bool | Omit = omit,
region: Literal["aws.us-east-1a"] | Omit = omit,
+ source: deployment_create_params.Source | Omit = omit,
version: 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.
@@ -290,15 +295,17 @@ async def create(
Args:
entrypoint_rel_path: Relative path to the entrypoint of the application
- file: ZIP file containing the application source directory
-
env_vars: Map of environment variables to set for the deployed application. Each key-value
pair represents an environment variable.
+ file: ZIP file containing the application source directory
+
force: Allow overwriting an existing app version
region: Region for deployment. Currently we only support "aws.us-east-1a"
+ source: Source from which to fetch application code.
+
version: Version of the application. Can be any string.
extra_headers: Send extra headers
@@ -312,10 +319,11 @@ async def create(
body = deepcopy_minimal(
{
"entrypoint_rel_path": entrypoint_rel_path,
- "file": file,
"env_vars": env_vars,
+ "file": file,
"force": force,
"region": region,
+ "source": source,
"version": version,
}
)
diff --git a/src/kernel/resources/extensions.py b/src/kernel/resources/extensions.py
index 2f868716..45d08d91 100644
--- a/src/kernel/resources/extensions.py
+++ b/src/kernel/resources/extensions.py
@@ -7,7 +7,7 @@
import httpx
-from ..types import extension_upload_params, extension_download_from_chrome_store_params
+from ..types import extension_create_params, extension_download_from_chrome_store_params
from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, FileTypes, omit, not_given
from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform
from .._compat import cached_property
@@ -28,7 +28,7 @@
)
from .._base_client import make_request_options
from ..types.extension_list_response import ExtensionListResponse
-from ..types.extension_upload_response import ExtensionUploadResponse
+from ..types.extension_create_response import ExtensionCreateResponse
__all__ = ["ExtensionsResource", "AsyncExtensionsResource"]
@@ -53,26 +53,58 @@ def with_streaming_response(self) -> ExtensionsResourceWithStreamingResponse:
"""
return ExtensionsResourceWithStreamingResponse(self)
- def list(
+ def create(
self,
*,
+ file: FileTypes,
+ name: 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,
- ) -> ExtensionListResponse:
- """List extensions owned by the caller's organization."""
- return self._get(
+ ) -> ExtensionCreateResponse:
+ """Upload a zip file containing an unpacked browser extension.
+
+ Optionally provide a
+ unique name for later reference.
+
+ Args:
+ file: ZIP file containing the browser extension.
+
+ name: Optional unique name within the organization to reference this extension.
+
+ 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
+ """
+ body = deepcopy_minimal(
+ {
+ "file": file,
+ "name": name,
+ }
+ )
+ files = extract_files(cast(Mapping[str, object], body), paths=[["file"]])
+ # It should be noted that the actual Content-Type header that will be
+ # sent to the server will contain a `boundary` parameter, e.g.
+ # multipart/form-data; boundary=---abc--
+ extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
+ return self._post(
"/extensions",
+ body=maybe_transform(body, extension_create_params.ExtensionCreateParams),
+ files=files,
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=ExtensionListResponse,
+ cast_to=ExtensionCreateResponse,
)
- def delete(
+ def retrieve(
self,
id_or_name: str,
*,
@@ -82,9 +114,9 @@ def delete(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> None:
+ ) -> BinaryAPIResponse:
"""
- Delete an extension by its ID or by its name.
+ Download the extension as a ZIP archive by ID or name.
Args:
extra_headers: Send extra headers
@@ -97,16 +129,35 @@ def delete(
"""
if not id_or_name:
raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
- extra_headers = {"Accept": "*/*", **(extra_headers or {})}
- return self._delete(
+ extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})}
+ return self._get(
f"/extensions/{id_or_name}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=NoneType,
+ cast_to=BinaryAPIResponse,
+ )
+
+ def list(
+ self,
+ *,
+ # 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,
+ ) -> ExtensionListResponse:
+ """List extensions owned by the caller's organization."""
+ return self._get(
+ "/extensions",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ExtensionListResponse,
)
- def download(
+ def delete(
self,
id_or_name: str,
*,
@@ -116,9 +167,9 @@ def download(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> BinaryAPIResponse:
+ ) -> None:
"""
- Download the extension as a ZIP archive by ID or name.
+ Delete an extension by its ID or by its name.
Args:
extra_headers: Send extra headers
@@ -131,13 +182,13 @@ def download(
"""
if not id_or_name:
raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
- extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})}
- return self._get(
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._delete(
f"/extensions/{id_or_name}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=BinaryAPIResponse,
+ cast_to=NoneType,
)
def download_from_chrome_store(
@@ -188,7 +239,28 @@ def download_from_chrome_store(
cast_to=BinaryAPIResponse,
)
- def upload(
+
+class AsyncExtensionsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncExtensionsResourceWithRawResponse:
+ """
+ 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/onkernel/kernel-python-sdk#accessing-raw-response-data-eg-headers
+ """
+ return AsyncExtensionsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncExtensionsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/onkernel/kernel-python-sdk#with_streaming_response
+ """
+ return AsyncExtensionsResourceWithStreamingResponse(self)
+
+ async def create(
self,
*,
file: FileTypes,
@@ -199,7 +271,7 @@ def upload(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> ExtensionUploadResponse:
+ ) -> ExtensionCreateResponse:
"""Upload a zip file containing an unpacked browser extension.
Optionally provide a
@@ -229,36 +301,49 @@ def upload(
# sent to the server will contain a `boundary` parameter, e.g.
# multipart/form-data; boundary=---abc--
extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
- return self._post(
+ return await self._post(
"/extensions",
- body=maybe_transform(body, extension_upload_params.ExtensionUploadParams),
+ body=await async_maybe_transform(body, extension_create_params.ExtensionCreateParams),
files=files,
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=ExtensionUploadResponse,
+ cast_to=ExtensionCreateResponse,
)
-
-class AsyncExtensionsResource(AsyncAPIResource):
- @cached_property
- def with_raw_response(self) -> AsyncExtensionsResourceWithRawResponse:
+ async def retrieve(
+ self,
+ id_or_name: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AsyncBinaryAPIResponse:
"""
- This property can be used as a prefix for any HTTP method call to return
- the raw response object instead of the parsed content.
+ Download the extension as a ZIP archive by ID or name.
- For more information, see https://www.github.com/onkernel/kernel-python-sdk#accessing-raw-response-data-eg-headers
- """
- return AsyncExtensionsResourceWithRawResponse(self)
+ Args:
+ extra_headers: Send extra headers
- @cached_property
- def with_streaming_response(self) -> AsyncExtensionsResourceWithStreamingResponse:
- """
- An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+ extra_query: Add additional query parameters to the request
- For more information, see https://www.github.com/onkernel/kernel-python-sdk#with_streaming_response
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
"""
- return AsyncExtensionsResourceWithStreamingResponse(self)
+ if not id_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
+ extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})}
+ return await self._get(
+ f"/extensions/{id_or_name}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AsyncBinaryAPIResponse,
+ )
async def list(
self,
@@ -313,40 +398,6 @@ async def delete(
cast_to=NoneType,
)
- async def download(
- self,
- id_or_name: str,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> AsyncBinaryAPIResponse:
- """
- Download the extension as a ZIP archive by ID or name.
-
- 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_or_name:
- raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
- extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})}
- return await self._get(
- f"/extensions/{id_or_name}",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=AsyncBinaryAPIResponse,
- )
-
async def download_from_chrome_store(
self,
*,
@@ -395,145 +446,94 @@ async def download_from_chrome_store(
cast_to=AsyncBinaryAPIResponse,
)
- async def upload(
- self,
- *,
- file: FileTypes,
- name: 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,
- ) -> ExtensionUploadResponse:
- """Upload a zip file containing an unpacked browser extension.
-
- Optionally provide a
- unique name for later reference.
-
- Args:
- file: ZIP file containing the browser extension.
-
- name: Optional unique name within the organization to reference this extension.
-
- 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
- """
- body = deepcopy_minimal(
- {
- "file": file,
- "name": name,
- }
- )
- files = extract_files(cast(Mapping[str, object], body), paths=[["file"]])
- # It should be noted that the actual Content-Type header that will be
- # sent to the server will contain a `boundary` parameter, e.g.
- # multipart/form-data; boundary=---abc--
- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
- return await self._post(
- "/extensions",
- body=await async_maybe_transform(body, extension_upload_params.ExtensionUploadParams),
- files=files,
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=ExtensionUploadResponse,
- )
-
class ExtensionsResourceWithRawResponse:
def __init__(self, extensions: ExtensionsResource) -> None:
self._extensions = extensions
+ self.create = to_raw_response_wrapper(
+ extensions.create,
+ )
+ self.retrieve = to_custom_raw_response_wrapper(
+ extensions.retrieve,
+ BinaryAPIResponse,
+ )
self.list = to_raw_response_wrapper(
extensions.list,
)
self.delete = to_raw_response_wrapper(
extensions.delete,
)
- self.download = to_custom_raw_response_wrapper(
- extensions.download,
- BinaryAPIResponse,
- )
self.download_from_chrome_store = to_custom_raw_response_wrapper(
extensions.download_from_chrome_store,
BinaryAPIResponse,
)
- self.upload = to_raw_response_wrapper(
- extensions.upload,
- )
class AsyncExtensionsResourceWithRawResponse:
def __init__(self, extensions: AsyncExtensionsResource) -> None:
self._extensions = extensions
+ self.create = async_to_raw_response_wrapper(
+ extensions.create,
+ )
+ self.retrieve = async_to_custom_raw_response_wrapper(
+ extensions.retrieve,
+ AsyncBinaryAPIResponse,
+ )
self.list = async_to_raw_response_wrapper(
extensions.list,
)
self.delete = async_to_raw_response_wrapper(
extensions.delete,
)
- self.download = async_to_custom_raw_response_wrapper(
- extensions.download,
- AsyncBinaryAPIResponse,
- )
self.download_from_chrome_store = async_to_custom_raw_response_wrapper(
extensions.download_from_chrome_store,
AsyncBinaryAPIResponse,
)
- self.upload = async_to_raw_response_wrapper(
- extensions.upload,
- )
class ExtensionsResourceWithStreamingResponse:
def __init__(self, extensions: ExtensionsResource) -> None:
self._extensions = extensions
+ self.create = to_streamed_response_wrapper(
+ extensions.create,
+ )
+ self.retrieve = to_custom_streamed_response_wrapper(
+ extensions.retrieve,
+ StreamedBinaryAPIResponse,
+ )
self.list = to_streamed_response_wrapper(
extensions.list,
)
self.delete = to_streamed_response_wrapper(
extensions.delete,
)
- self.download = to_custom_streamed_response_wrapper(
- extensions.download,
- StreamedBinaryAPIResponse,
- )
self.download_from_chrome_store = to_custom_streamed_response_wrapper(
extensions.download_from_chrome_store,
StreamedBinaryAPIResponse,
)
- self.upload = to_streamed_response_wrapper(
- extensions.upload,
- )
class AsyncExtensionsResourceWithStreamingResponse:
def __init__(self, extensions: AsyncExtensionsResource) -> None:
self._extensions = extensions
+ self.create = async_to_streamed_response_wrapper(
+ extensions.create,
+ )
+ self.retrieve = async_to_custom_streamed_response_wrapper(
+ extensions.retrieve,
+ AsyncStreamedBinaryAPIResponse,
+ )
self.list = async_to_streamed_response_wrapper(
extensions.list,
)
self.delete = async_to_streamed_response_wrapper(
extensions.delete,
)
- self.download = async_to_custom_streamed_response_wrapper(
- extensions.download,
- AsyncStreamedBinaryAPIResponse,
- )
self.download_from_chrome_store = async_to_custom_streamed_response_wrapper(
extensions.download_from_chrome_store,
AsyncStreamedBinaryAPIResponse,
)
- self.upload = async_to_streamed_response_wrapper(
- extensions.upload,
- )
diff --git a/src/kernel/types/__init__.py b/src/kernel/types/__init__.py
index 6b49cf7f..2edc0bcd 100644
--- a/src/kernel/types/__init__.py
+++ b/src/kernel/types/__init__.py
@@ -27,8 +27,8 @@
from .invocation_list_params import InvocationListParams as InvocationListParams
from .invocation_state_event import InvocationStateEvent as InvocationStateEvent
from .browser_create_response import BrowserCreateResponse as BrowserCreateResponse
+from .extension_create_params import ExtensionCreateParams as ExtensionCreateParams
from .extension_list_response import ExtensionListResponse as ExtensionListResponse
-from .extension_upload_params import ExtensionUploadParams as ExtensionUploadParams
from .proxy_retrieve_response import ProxyRetrieveResponse as ProxyRetrieveResponse
from .deployment_create_params import DeploymentCreateParams as DeploymentCreateParams
from .deployment_follow_params import DeploymentFollowParams as DeploymentFollowParams
@@ -39,7 +39,7 @@
from .invocation_update_params import InvocationUpdateParams as InvocationUpdateParams
from .browser_persistence_param import BrowserPersistenceParam as BrowserPersistenceParam
from .browser_retrieve_response import BrowserRetrieveResponse as BrowserRetrieveResponse
-from .extension_upload_response import ExtensionUploadResponse as ExtensionUploadResponse
+from .extension_create_response import ExtensionCreateResponse as ExtensionCreateResponse
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/deployment_create_params.py b/src/kernel/types/deployment_create_params.py
index 6701c0a8..16eb5702 100644
--- a/src/kernel/types/deployment_create_params.py
+++ b/src/kernel/types/deployment_create_params.py
@@ -7,27 +7,58 @@
from .._types import FileTypes
-__all__ = ["DeploymentCreateParams"]
+__all__ = ["DeploymentCreateParams", "Source", "SourceAuth"]
class DeploymentCreateParams(TypedDict, total=False):
- entrypoint_rel_path: Required[str]
+ entrypoint_rel_path: str
"""Relative path to the entrypoint of the application"""
- file: Required[FileTypes]
- """ZIP file containing the application source directory"""
-
env_vars: Dict[str, str]
"""Map of environment variables to set for the deployed application.
Each key-value pair represents an environment variable.
"""
+ file: FileTypes
+ """ZIP file containing the application source directory"""
+
force: bool
"""Allow overwriting an existing app version"""
region: Literal["aws.us-east-1a"]
"""Region for deployment. Currently we only support "aws.us-east-1a" """
+ source: Source
+ """Source from which to fetch application code."""
+
version: str
"""Version of the application. Can be any string."""
+
+
+class SourceAuth(TypedDict, total=False):
+ token: Required[str]
+ """GitHub PAT or installation access token"""
+
+ method: Required[Literal["github_token"]]
+ """Auth method"""
+
+
+class Source(TypedDict, total=False):
+ entrypoint: Required[str]
+ """Relative path to the application entrypoint within the selected path."""
+
+ ref: Required[str]
+ """Git ref (branch, tag, or commit SHA) to fetch."""
+
+ type: Required[Literal["github"]]
+ """Source type identifier."""
+
+ url: Required[str]
+ """Base repository URL (without blob/tree suffixes)."""
+
+ auth: SourceAuth
+ """Authentication for private repositories."""
+
+ path: str
+ """Path within the repo to deploy (omit to use repo root)."""
diff --git a/src/kernel/types/extension_upload_params.py b/src/kernel/types/extension_create_params.py
similarity index 81%
rename from src/kernel/types/extension_upload_params.py
rename to src/kernel/types/extension_create_params.py
index d36dde31..6bb2b397 100644
--- a/src/kernel/types/extension_upload_params.py
+++ b/src/kernel/types/extension_create_params.py
@@ -6,10 +6,10 @@
from .._types import FileTypes
-__all__ = ["ExtensionUploadParams"]
+__all__ = ["ExtensionCreateParams"]
-class ExtensionUploadParams(TypedDict, total=False):
+class ExtensionCreateParams(TypedDict, total=False):
file: Required[FileTypes]
"""ZIP file containing the browser extension."""
diff --git a/src/kernel/types/extension_upload_response.py b/src/kernel/types/extension_create_response.py
similarity index 88%
rename from src/kernel/types/extension_upload_response.py
rename to src/kernel/types/extension_create_response.py
index 373e8861..c4fd6301 100644
--- a/src/kernel/types/extension_upload_response.py
+++ b/src/kernel/types/extension_create_response.py
@@ -5,10 +5,10 @@
from .._models import BaseModel
-__all__ = ["ExtensionUploadResponse"]
+__all__ = ["ExtensionCreateResponse"]
-class ExtensionUploadResponse(BaseModel):
+class ExtensionCreateResponse(BaseModel):
id: str
"""Unique identifier for the extension"""
diff --git a/tests/api_resources/test_deployments.py b/tests/api_resources/test_deployments.py
index fc5d2991..6c3354ef 100644
--- a/tests/api_resources/test_deployments.py
+++ b/tests/api_resources/test_deployments.py
@@ -25,10 +25,7 @@ class TestDeployments:
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_method_create(self, client: Kernel) -> None:
- deployment = client.deployments.create(
- entrypoint_rel_path="src/app.py",
- file=b"raw file contents",
- )
+ deployment = client.deployments.create()
assert_matches_type(DeploymentCreateResponse, deployment, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@@ -36,10 +33,21 @@ def test_method_create(self, client: Kernel) -> None:
def test_method_create_with_all_params(self, client: Kernel) -> None:
deployment = client.deployments.create(
entrypoint_rel_path="src/app.py",
+ env_vars={"FOO": "bar"},
file=b"raw file contents",
- env_vars={"foo": "string"},
force=False,
region="aws.us-east-1a",
+ source={
+ "entrypoint": "src/index.ts",
+ "ref": "main",
+ "type": "github",
+ "url": "https://github.com/org/repo",
+ "auth": {
+ "token": "ghs_***",
+ "method": "github_token",
+ },
+ "path": "apps/api",
+ },
version="1.0.0",
)
assert_matches_type(DeploymentCreateResponse, deployment, path=["response"])
@@ -47,10 +55,7 @@ def test_method_create_with_all_params(self, client: Kernel) -> None:
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_raw_response_create(self, client: Kernel) -> None:
- response = client.deployments.with_raw_response.create(
- entrypoint_rel_path="src/app.py",
- file=b"raw file contents",
- )
+ response = client.deployments.with_raw_response.create()
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -60,10 +65,7 @@ def test_raw_response_create(self, client: Kernel) -> None:
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_streaming_response_create(self, client: Kernel) -> None:
- with client.deployments.with_streaming_response.create(
- entrypoint_rel_path="src/app.py",
- file=b"raw file contents",
- ) as response:
+ with client.deployments.with_streaming_response.create() as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -211,10 +213,7 @@ class TestAsyncDeployments:
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_method_create(self, async_client: AsyncKernel) -> None:
- deployment = await async_client.deployments.create(
- entrypoint_rel_path="src/app.py",
- file=b"raw file contents",
- )
+ deployment = await async_client.deployments.create()
assert_matches_type(DeploymentCreateResponse, deployment, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@@ -222,10 +221,21 @@ async def test_method_create(self, async_client: AsyncKernel) -> None:
async def test_method_create_with_all_params(self, async_client: AsyncKernel) -> None:
deployment = await async_client.deployments.create(
entrypoint_rel_path="src/app.py",
+ env_vars={"FOO": "bar"},
file=b"raw file contents",
- env_vars={"foo": "string"},
force=False,
region="aws.us-east-1a",
+ source={
+ "entrypoint": "src/index.ts",
+ "ref": "main",
+ "type": "github",
+ "url": "https://github.com/org/repo",
+ "auth": {
+ "token": "ghs_***",
+ "method": "github_token",
+ },
+ "path": "apps/api",
+ },
version="1.0.0",
)
assert_matches_type(DeploymentCreateResponse, deployment, path=["response"])
@@ -233,10 +243,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncKernel) ->
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_raw_response_create(self, async_client: AsyncKernel) -> None:
- response = await async_client.deployments.with_raw_response.create(
- entrypoint_rel_path="src/app.py",
- file=b"raw file contents",
- )
+ response = await async_client.deployments.with_raw_response.create()
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -246,10 +253,7 @@ async def test_raw_response_create(self, async_client: AsyncKernel) -> None:
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_streaming_response_create(self, async_client: AsyncKernel) -> None:
- async with async_client.deployments.with_streaming_response.create(
- entrypoint_rel_path="src/app.py",
- file=b"raw file contents",
- ) as response:
+ async with async_client.deployments.with_streaming_response.create() as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
diff --git a/tests/api_resources/test_extensions.py b/tests/api_resources/test_extensions.py
index 5d61f327..ffdb02f6 100644
--- a/tests/api_resources/test_extensions.py
+++ b/tests/api_resources/test_extensions.py
@@ -13,7 +13,7 @@
from tests.utils import assert_matches_type
from kernel.types import (
ExtensionListResponse,
- ExtensionUploadResponse,
+ ExtensionCreateResponse,
)
from kernel._response import (
BinaryAPIResponse,
@@ -28,6 +28,99 @@
class TestExtensions:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_create(self, client: Kernel) -> None:
+ extension = client.extensions.create(
+ file=b"raw file contents",
+ )
+ assert_matches_type(ExtensionCreateResponse, extension, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_create_with_all_params(self, client: Kernel) -> None:
+ extension = client.extensions.create(
+ file=b"raw file contents",
+ name="name",
+ )
+ assert_matches_type(ExtensionCreateResponse, extension, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_create(self, client: Kernel) -> None:
+ response = client.extensions.with_raw_response.create(
+ file=b"raw file contents",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ extension = response.parse()
+ assert_matches_type(ExtensionCreateResponse, extension, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_create(self, client: Kernel) -> None:
+ with client.extensions.with_streaming_response.create(
+ file=b"raw file contents",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ extension = response.parse()
+ assert_matches_type(ExtensionCreateResponse, extension, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_method_retrieve(self, client: Kernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ extension = client.extensions.retrieve(
+ "id_or_name",
+ )
+ assert extension.is_closed
+ assert extension.json() == {"foo": "bar"}
+ assert cast(Any, extension.is_closed) is True
+ assert isinstance(extension, BinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_raw_response_retrieve(self, client: Kernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+
+ extension = client.extensions.with_raw_response.retrieve(
+ "id_or_name",
+ )
+
+ assert extension.is_closed is True
+ assert extension.http_request.headers.get("X-Stainless-Lang") == "python"
+ assert extension.json() == {"foo": "bar"}
+ assert isinstance(extension, BinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_streaming_response_retrieve(self, client: Kernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ with client.extensions.with_streaming_response.retrieve(
+ "id_or_name",
+ ) as extension:
+ assert not extension.is_closed
+ assert extension.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ assert extension.json() == {"foo": "bar"}
+ assert cast(Any, extension.is_closed) is True
+ assert isinstance(extension, StreamedBinaryAPIResponse)
+
+ assert cast(Any, extension.is_closed) is True
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_path_params_retrieve(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
+ client.extensions.with_raw_response.retrieve(
+ "",
+ )
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_method_list(self, client: Kernel) -> None:
@@ -98,56 +191,6 @@ def test_path_params_delete(self, client: Kernel) -> None:
"",
)
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_method_download(self, client: Kernel, respx_mock: MockRouter) -> None:
- respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- extension = client.extensions.download(
- "id_or_name",
- )
- assert extension.is_closed
- assert extension.json() == {"foo": "bar"}
- assert cast(Any, extension.is_closed) is True
- assert isinstance(extension, BinaryAPIResponse)
-
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_raw_response_download(self, client: Kernel, respx_mock: MockRouter) -> None:
- respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
-
- extension = client.extensions.with_raw_response.download(
- "id_or_name",
- )
-
- assert extension.is_closed is True
- assert extension.http_request.headers.get("X-Stainless-Lang") == "python"
- assert extension.json() == {"foo": "bar"}
- assert isinstance(extension, BinaryAPIResponse)
-
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_streaming_response_download(self, client: Kernel, respx_mock: MockRouter) -> None:
- respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- with client.extensions.with_streaming_response.download(
- "id_or_name",
- ) as extension:
- assert not extension.is_closed
- assert extension.http_request.headers.get("X-Stainless-Lang") == "python"
-
- assert extension.json() == {"foo": "bar"}
- assert cast(Any, extension.is_closed) is True
- assert isinstance(extension, StreamedBinaryAPIResponse)
-
- assert cast(Any, extension.is_closed) is True
-
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_path_params_download(self, client: Kernel) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
- client.extensions.with_raw_response.download(
- "",
- )
-
@parametrize
@pytest.mark.respx(base_url=base_url)
def test_method_download_from_chrome_store(self, client: Kernel, respx_mock: MockRouter) -> None:
@@ -203,54 +246,104 @@ def test_streaming_response_download_from_chrome_store(self, client: Kernel, res
assert cast(Any, extension.is_closed) is True
+
+class TestAsyncExtensions:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_method_upload(self, client: Kernel) -> None:
- extension = client.extensions.upload(
+ async def test_method_create(self, async_client: AsyncKernel) -> None:
+ extension = await async_client.extensions.create(
file=b"raw file contents",
)
- assert_matches_type(ExtensionUploadResponse, extension, path=["response"])
+ assert_matches_type(ExtensionCreateResponse, extension, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_method_upload_with_all_params(self, client: Kernel) -> None:
- extension = client.extensions.upload(
+ async def test_method_create_with_all_params(self, async_client: AsyncKernel) -> None:
+ extension = await async_client.extensions.create(
file=b"raw file contents",
name="name",
)
- assert_matches_type(ExtensionUploadResponse, extension, path=["response"])
+ assert_matches_type(ExtensionCreateResponse, extension, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_raw_response_upload(self, client: Kernel) -> None:
- response = client.extensions.with_raw_response.upload(
+ async def test_raw_response_create(self, async_client: AsyncKernel) -> None:
+ response = await async_client.extensions.with_raw_response.create(
file=b"raw file contents",
)
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- extension = response.parse()
- assert_matches_type(ExtensionUploadResponse, extension, path=["response"])
+ extension = await response.parse()
+ assert_matches_type(ExtensionCreateResponse, extension, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- def test_streaming_response_upload(self, client: Kernel) -> None:
- with client.extensions.with_streaming_response.upload(
+ async def test_streaming_response_create(self, async_client: AsyncKernel) -> None:
+ async with async_client.extensions.with_streaming_response.create(
file=b"raw file contents",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- extension = response.parse()
- assert_matches_type(ExtensionUploadResponse, extension, path=["response"])
+ extension = await response.parse()
+ assert_matches_type(ExtensionCreateResponse, extension, path=["response"])
assert cast(Any, response.is_closed) is True
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_method_retrieve(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ extension = await async_client.extensions.retrieve(
+ "id_or_name",
+ )
+ assert extension.is_closed
+ assert await extension.json() == {"foo": "bar"}
+ assert cast(Any, extension.is_closed) is True
+ assert isinstance(extension, AsyncBinaryAPIResponse)
-class TestAsyncExtensions:
- parametrize = pytest.mark.parametrize(
- "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
- )
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_raw_response_retrieve(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+
+ extension = await async_client.extensions.with_raw_response.retrieve(
+ "id_or_name",
+ )
+
+ assert extension.is_closed is True
+ assert extension.http_request.headers.get("X-Stainless-Lang") == "python"
+ assert await extension.json() == {"foo": "bar"}
+ assert isinstance(extension, AsyncBinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_streaming_response_retrieve(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ async with async_client.extensions.with_streaming_response.retrieve(
+ "id_or_name",
+ ) as extension:
+ assert not extension.is_closed
+ assert extension.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ assert await extension.json() == {"foo": "bar"}
+ assert cast(Any, extension.is_closed) is True
+ assert isinstance(extension, AsyncStreamedBinaryAPIResponse)
+
+ assert cast(Any, extension.is_closed) is True
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_path_params_retrieve(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
+ await async_client.extensions.with_raw_response.retrieve(
+ "",
+ )
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
@@ -322,56 +415,6 @@ async def test_path_params_delete(self, async_client: AsyncKernel) -> None:
"",
)
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_method_download(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
- respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- extension = await async_client.extensions.download(
- "id_or_name",
- )
- assert extension.is_closed
- assert await extension.json() == {"foo": "bar"}
- assert cast(Any, extension.is_closed) is True
- assert isinstance(extension, AsyncBinaryAPIResponse)
-
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_raw_response_download(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
- respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
-
- extension = await async_client.extensions.with_raw_response.download(
- "id_or_name",
- )
-
- assert extension.is_closed is True
- assert extension.http_request.headers.get("X-Stainless-Lang") == "python"
- assert await extension.json() == {"foo": "bar"}
- assert isinstance(extension, AsyncBinaryAPIResponse)
-
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_streaming_response_download(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
- respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- async with async_client.extensions.with_streaming_response.download(
- "id_or_name",
- ) as extension:
- assert not extension.is_closed
- assert extension.http_request.headers.get("X-Stainless-Lang") == "python"
-
- assert await extension.json() == {"foo": "bar"}
- assert cast(Any, extension.is_closed) is True
- assert isinstance(extension, AsyncStreamedBinaryAPIResponse)
-
- assert cast(Any, extension.is_closed) is True
-
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_path_params_download(self, async_client: AsyncKernel) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
- await async_client.extensions.with_raw_response.download(
- "",
- )
-
@parametrize
@pytest.mark.respx(base_url=base_url)
async def test_method_download_from_chrome_store(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
@@ -432,46 +475,3 @@ async def test_streaming_response_download_from_chrome_store(
assert isinstance(extension, AsyncStreamedBinaryAPIResponse)
assert cast(Any, extension.is_closed) is True
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- async def test_method_upload(self, async_client: AsyncKernel) -> None:
- extension = await async_client.extensions.upload(
- file=b"raw file contents",
- )
- assert_matches_type(ExtensionUploadResponse, extension, path=["response"])
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- async def test_method_upload_with_all_params(self, async_client: AsyncKernel) -> None:
- extension = await async_client.extensions.upload(
- file=b"raw file contents",
- name="name",
- )
- assert_matches_type(ExtensionUploadResponse, extension, path=["response"])
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- async def test_raw_response_upload(self, async_client: AsyncKernel) -> None:
- response = await async_client.extensions.with_raw_response.upload(
- file=b"raw file contents",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- extension = await response.parse()
- assert_matches_type(ExtensionUploadResponse, extension, path=["response"])
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- async def test_streaming_response_upload(self, async_client: AsyncKernel) -> None:
- async with async_client.extensions.with_streaming_response.upload(
- file=b"raw file contents",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- extension = await response.parse()
- assert_matches_type(ExtensionUploadResponse, extension, path=["response"])
-
- assert cast(Any, response.is_closed) is True
From b63f7adc7cf0da614a4fc3a9eed491f71f7ec742 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 17 Oct 2025 18:33:29 +0000
Subject: [PATCH 2/3] feat: click mouse, move mouse, screenshot
---
.stats.yml | 8 +-
api.md | 18 +-
src/kernel/resources/browsers/__init__.py | 14 +
src/kernel/resources/browsers/browsers.py | 32 +
src/kernel/resources/browsers/computer.py | 949 ++++++++++++++++++
src/kernel/resources/extensions.py | 304 +++---
src/kernel/types/__init__.py | 4 +-
src/kernel/types/browsers/__init__.py | 7 +
.../computer_capture_screenshot_params.py | 25 +
.../browsers/computer_click_mouse_params.py | 29 +
.../browsers/computer_drag_mouse_params.py | 36 +
.../browsers/computer_move_mouse_params.py | 20 +
.../browsers/computer_press_key_params.py | 28 +
.../types/browsers/computer_scroll_params.py | 26 +
.../browsers/computer_type_text_params.py | 15 +
...e_params.py => extension_upload_params.py} | 4 +-
...sponse.py => extension_upload_response.py} | 4 +-
tests/api_resources/browsers/test_computer.py | 892 ++++++++++++++++
tests/api_resources/test_extensions.py | 324 +++---
19 files changed, 2412 insertions(+), 327 deletions(-)
create mode 100644 src/kernel/resources/browsers/computer.py
create mode 100644 src/kernel/types/browsers/computer_capture_screenshot_params.py
create mode 100644 src/kernel/types/browsers/computer_click_mouse_params.py
create mode 100644 src/kernel/types/browsers/computer_drag_mouse_params.py
create mode 100644 src/kernel/types/browsers/computer_move_mouse_params.py
create mode 100644 src/kernel/types/browsers/computer_press_key_params.py
create mode 100644 src/kernel/types/browsers/computer_scroll_params.py
create mode 100644 src/kernel/types/browsers/computer_type_text_params.py
rename src/kernel/types/{extension_create_params.py => extension_upload_params.py} (81%)
rename src/kernel/types/{extension_create_response.py => extension_upload_response.py} (88%)
create mode 100644 tests/api_resources/browsers/test_computer.py
diff --git a/.stats.yml b/.stats.yml
index 2bd40ccf..b4dc6064 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 57
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-6eaa6f5654abc94549962d7db1e8c7936af1f815bb3abe2f8249959394da1278.yml
-openapi_spec_hash: 31ece7cd801e74228b80a8112a762e56
-config_hash: 3fc2057ce765bc5f27785a694ed0f553
+configured_endpoints: 64
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-e21f0324774a1762bc2bba0da3a8a6b0d0e74720d7a1c83dec813f9e027fcf58.yml
+openapi_spec_hash: f1b636abfd6cb8e7c2ba7ffb8e53b9ba
+config_hash: 09a2df23048cb16689c9a390d9e5bc47
diff --git a/api.md b/api.md
index 9f219359..858dbfd8 100644
--- a/api.md
+++ b/api.md
@@ -166,6 +166,18 @@ Methods:
- client.browsers.logs.stream(id, \*\*params) -> LogEvent
+## Computer
+
+Methods:
+
+- client.browsers.computer.capture_screenshot(id, \*\*params) -> BinaryAPIResponse
+- client.browsers.computer.click_mouse(id, \*\*params) -> None
+- client.browsers.computer.drag_mouse(id, \*\*params) -> None
+- client.browsers.computer.move_mouse(id, \*\*params) -> None
+- client.browsers.computer.press_key(id, \*\*params) -> None
+- client.browsers.computer.scroll(id, \*\*params) -> None
+- client.browsers.computer.type_text(id, \*\*params) -> None
+
# Profiles
Types:
@@ -202,13 +214,13 @@ Methods:
Types:
```python
-from kernel.types import ExtensionCreateResponse, ExtensionListResponse
+from kernel.types import ExtensionListResponse, ExtensionUploadResponse
```
Methods:
-- client.extensions.create(\*\*params) -> ExtensionCreateResponse
-- client.extensions.retrieve(id_or_name) -> BinaryAPIResponse
- client.extensions.list() -> ExtensionListResponse
- client.extensions.delete(id_or_name) -> None
+- client.extensions.download(id_or_name) -> BinaryAPIResponse
- client.extensions.download_from_chrome_store(\*\*params) -> BinaryAPIResponse
+- client.extensions.upload(\*\*params) -> ExtensionUploadResponse
diff --git a/src/kernel/resources/browsers/__init__.py b/src/kernel/resources/browsers/__init__.py
index 97c987e4..abcc8f78 100644
--- a/src/kernel/resources/browsers/__init__.py
+++ b/src/kernel/resources/browsers/__init__.py
@@ -40,6 +40,14 @@
BrowsersResourceWithStreamingResponse,
AsyncBrowsersResourceWithStreamingResponse,
)
+from .computer import (
+ ComputerResource,
+ AsyncComputerResource,
+ ComputerResourceWithRawResponse,
+ AsyncComputerResourceWithRawResponse,
+ ComputerResourceWithStreamingResponse,
+ AsyncComputerResourceWithStreamingResponse,
+)
__all__ = [
"ReplaysResource",
@@ -66,6 +74,12 @@
"AsyncLogsResourceWithRawResponse",
"LogsResourceWithStreamingResponse",
"AsyncLogsResourceWithStreamingResponse",
+ "ComputerResource",
+ "AsyncComputerResource",
+ "ComputerResourceWithRawResponse",
+ "AsyncComputerResourceWithRawResponse",
+ "ComputerResourceWithStreamingResponse",
+ "AsyncComputerResourceWithStreamingResponse",
"BrowsersResource",
"AsyncBrowsersResource",
"BrowsersResourceWithRawResponse",
diff --git a/src/kernel/resources/browsers/browsers.py b/src/kernel/resources/browsers/browsers.py
index 1d444214..c65a738a 100644
--- a/src/kernel/resources/browsers/browsers.py
+++ b/src/kernel/resources/browsers/browsers.py
@@ -41,6 +41,14 @@
)
from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
from ..._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform
+from .computer import (
+ ComputerResource,
+ AsyncComputerResource,
+ ComputerResourceWithRawResponse,
+ AsyncComputerResourceWithRawResponse,
+ ComputerResourceWithStreamingResponse,
+ AsyncComputerResourceWithStreamingResponse,
+)
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -75,6 +83,10 @@ def process(self) -> ProcessResource:
def logs(self) -> LogsResource:
return LogsResource(self._client)
+ @cached_property
+ def computer(self) -> ComputerResource:
+ return ComputerResource(self._client)
+
@cached_property
def with_raw_response(self) -> BrowsersResourceWithRawResponse:
"""
@@ -375,6 +387,10 @@ def process(self) -> AsyncProcessResource:
def logs(self) -> AsyncLogsResource:
return AsyncLogsResource(self._client)
+ @cached_property
+ def computer(self) -> AsyncComputerResource:
+ return AsyncComputerResource(self._client)
+
@cached_property
def with_raw_response(self) -> AsyncBrowsersResourceWithRawResponse:
"""
@@ -699,6 +715,10 @@ def process(self) -> ProcessResourceWithRawResponse:
def logs(self) -> LogsResourceWithRawResponse:
return LogsResourceWithRawResponse(self._browsers.logs)
+ @cached_property
+ def computer(self) -> ComputerResourceWithRawResponse:
+ return ComputerResourceWithRawResponse(self._browsers.computer)
+
class AsyncBrowsersResourceWithRawResponse:
def __init__(self, browsers: AsyncBrowsersResource) -> None:
@@ -739,6 +759,10 @@ def process(self) -> AsyncProcessResourceWithRawResponse:
def logs(self) -> AsyncLogsResourceWithRawResponse:
return AsyncLogsResourceWithRawResponse(self._browsers.logs)
+ @cached_property
+ def computer(self) -> AsyncComputerResourceWithRawResponse:
+ return AsyncComputerResourceWithRawResponse(self._browsers.computer)
+
class BrowsersResourceWithStreamingResponse:
def __init__(self, browsers: BrowsersResource) -> None:
@@ -779,6 +803,10 @@ def process(self) -> ProcessResourceWithStreamingResponse:
def logs(self) -> LogsResourceWithStreamingResponse:
return LogsResourceWithStreamingResponse(self._browsers.logs)
+ @cached_property
+ def computer(self) -> ComputerResourceWithStreamingResponse:
+ return ComputerResourceWithStreamingResponse(self._browsers.computer)
+
class AsyncBrowsersResourceWithStreamingResponse:
def __init__(self, browsers: AsyncBrowsersResource) -> None:
@@ -818,3 +846,7 @@ def process(self) -> AsyncProcessResourceWithStreamingResponse:
@cached_property
def logs(self) -> AsyncLogsResourceWithStreamingResponse:
return AsyncLogsResourceWithStreamingResponse(self._browsers.logs)
+
+ @cached_property
+ def computer(self) -> AsyncComputerResourceWithStreamingResponse:
+ return AsyncComputerResourceWithStreamingResponse(self._browsers.computer)
diff --git a/src/kernel/resources/browsers/computer.py b/src/kernel/resources/browsers/computer.py
new file mode 100644
index 00000000..68cee420
--- /dev/null
+++ b/src/kernel/resources/browsers/computer.py
@@ -0,0 +1,949 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable
+from typing_extensions import Literal
+
+import httpx
+
+from ..._types import Body, Omit, Query, Headers, NoneType, 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,
+ AsyncStreamedBinaryAPIResponse,
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ to_custom_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+ to_custom_streamed_response_wrapper,
+ async_to_custom_raw_response_wrapper,
+ async_to_custom_streamed_response_wrapper,
+)
+from ..._base_client import make_request_options
+from ...types.browsers import (
+ computer_scroll_params,
+ computer_press_key_params,
+ computer_type_text_params,
+ computer_drag_mouse_params,
+ computer_move_mouse_params,
+ computer_click_mouse_params,
+ computer_capture_screenshot_params,
+)
+
+__all__ = ["ComputerResource", "AsyncComputerResource"]
+
+
+class ComputerResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> ComputerResourceWithRawResponse:
+ """
+ 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/onkernel/kernel-python-sdk#accessing-raw-response-data-eg-headers
+ """
+ return ComputerResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> ComputerResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/onkernel/kernel-python-sdk#with_streaming_response
+ """
+ return ComputerResourceWithStreamingResponse(self)
+
+ def capture_screenshot(
+ self,
+ id: str,
+ *,
+ region: computer_capture_screenshot_params.Region | 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,
+ ) -> BinaryAPIResponse:
+ """
+ Capture a screenshot of the browser instance
+
+ 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": "image/png", **(extra_headers or {})}
+ return self._post(
+ f"/browsers/{id}/computer/screenshot",
+ body=maybe_transform(
+ {"region": region}, computer_capture_screenshot_params.ComputerCaptureScreenshotParams
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=BinaryAPIResponse,
+ )
+
+ def click_mouse(
+ self,
+ id: str,
+ *,
+ x: int,
+ y: int,
+ button: Literal["left", "right", "middle", "back", "forward"] | Omit = omit,
+ click_type: Literal["down", "up", "click"] | Omit = omit,
+ hold_keys: SequenceNotStr[str] | Omit = omit,
+ num_clicks: 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,
+ ) -> None:
+ """
+ Simulate a mouse click action on the browser instance
+
+ Args:
+ x: X coordinate of the click position
+
+ y: Y coordinate of the click position
+
+ button: Mouse button to interact with
+
+ click_type: Type of click action
+
+ hold_keys: Modifier keys to hold during the click
+
+ num_clicks: Number of times to repeat the click
+
+ 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._post(
+ f"/browsers/{id}/computer/click_mouse",
+ body=maybe_transform(
+ {
+ "x": x,
+ "y": y,
+ "button": button,
+ "click_type": click_type,
+ "hold_keys": hold_keys,
+ "num_clicks": num_clicks,
+ },
+ computer_click_mouse_params.ComputerClickMouseParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def drag_mouse(
+ self,
+ id: str,
+ *,
+ path: Iterable[Iterable[int]],
+ button: Literal["left", "middle", "right"] | Omit = omit,
+ delay: int | Omit = omit,
+ hold_keys: SequenceNotStr[str] | Omit = omit,
+ step_delay_ms: int | Omit = omit,
+ steps_per_segment: 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,
+ ) -> None:
+ """
+ Drag the mouse along a path
+
+ Args:
+ path: Ordered list of [x, y] coordinate pairs to move through while dragging. Must
+ contain at least 2 points.
+
+ button: Mouse button to drag with
+
+ delay: Delay in milliseconds between button down and starting to move along the path.
+
+ hold_keys: Modifier keys to hold during the drag
+
+ step_delay_ms: Delay in milliseconds between relative steps while dragging (not the initial
+ delay).
+
+ steps_per_segment: Number of relative move steps per segment in the path. Minimum 1.
+
+ 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._post(
+ f"/browsers/{id}/computer/drag_mouse",
+ body=maybe_transform(
+ {
+ "path": path,
+ "button": button,
+ "delay": delay,
+ "hold_keys": hold_keys,
+ "step_delay_ms": step_delay_ms,
+ "steps_per_segment": steps_per_segment,
+ },
+ computer_drag_mouse_params.ComputerDragMouseParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def move_mouse(
+ self,
+ id: str,
+ *,
+ x: int,
+ y: int,
+ hold_keys: SequenceNotStr[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,
+ ) -> None:
+ """
+ Move the mouse cursor to the specified coordinates on the browser instance
+
+ Args:
+ x: X coordinate to move the cursor to
+
+ y: Y coordinate to move the cursor to
+
+ hold_keys: Modifier keys to hold during the move
+
+ 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._post(
+ f"/browsers/{id}/computer/move_mouse",
+ body=maybe_transform(
+ {
+ "x": x,
+ "y": y,
+ "hold_keys": hold_keys,
+ },
+ computer_move_mouse_params.ComputerMoveMouseParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def press_key(
+ self,
+ id: str,
+ *,
+ keys: SequenceNotStr[str],
+ duration: int | Omit = omit,
+ hold_keys: SequenceNotStr[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,
+ ) -> None:
+ """
+ Press one or more keys on the host computer
+
+ Args:
+ keys: List of key symbols to press. Each item should be a key symbol supported by
+ xdotool (see X11 keysym definitions). Examples include "Return", "Shift",
+ "Ctrl", "Alt", "F5". Items in this list could also be combinations, e.g.
+ "Ctrl+t" or "Ctrl+Shift+Tab".
+
+ duration: Duration to hold the keys down in milliseconds. If omitted or 0, keys are
+ tapped.
+
+ hold_keys: Optional modifier keys to hold during the key press sequence.
+
+ 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._post(
+ f"/browsers/{id}/computer/press_key",
+ body=maybe_transform(
+ {
+ "keys": keys,
+ "duration": duration,
+ "hold_keys": hold_keys,
+ },
+ computer_press_key_params.ComputerPressKeyParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def scroll(
+ self,
+ id: str,
+ *,
+ x: int,
+ y: int,
+ delta_x: int | Omit = omit,
+ delta_y: int | Omit = omit,
+ hold_keys: SequenceNotStr[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,
+ ) -> None:
+ """
+ Scroll the mouse wheel at a position on the host computer
+
+ Args:
+ x: X coordinate at which to perform the scroll
+
+ y: Y coordinate at which to perform the scroll
+
+ delta_x: Horizontal scroll amount. Positive scrolls right, negative scrolls left.
+
+ delta_y: Vertical scroll amount. Positive scrolls down, negative scrolls up.
+
+ hold_keys: Modifier keys to hold during the scroll
+
+ 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._post(
+ f"/browsers/{id}/computer/scroll",
+ body=maybe_transform(
+ {
+ "x": x,
+ "y": y,
+ "delta_x": delta_x,
+ "delta_y": delta_y,
+ "hold_keys": hold_keys,
+ },
+ computer_scroll_params.ComputerScrollParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def type_text(
+ self,
+ id: str,
+ *,
+ text: str,
+ delay: 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,
+ ) -> None:
+ """
+ Type text on the browser instance
+
+ Args:
+ text: Text to type on the browser instance
+
+ delay: Delay in milliseconds between keystrokes
+
+ 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._post(
+ f"/browsers/{id}/computer/type",
+ body=maybe_transform(
+ {
+ "text": text,
+ "delay": delay,
+ },
+ computer_type_text_params.ComputerTypeTextParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class AsyncComputerResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncComputerResourceWithRawResponse:
+ """
+ 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/onkernel/kernel-python-sdk#accessing-raw-response-data-eg-headers
+ """
+ return AsyncComputerResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncComputerResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/onkernel/kernel-python-sdk#with_streaming_response
+ """
+ return AsyncComputerResourceWithStreamingResponse(self)
+
+ async def capture_screenshot(
+ self,
+ id: str,
+ *,
+ region: computer_capture_screenshot_params.Region | 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,
+ ) -> AsyncBinaryAPIResponse:
+ """
+ Capture a screenshot of the browser instance
+
+ 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": "image/png", **(extra_headers or {})}
+ return await self._post(
+ f"/browsers/{id}/computer/screenshot",
+ body=await async_maybe_transform(
+ {"region": region}, computer_capture_screenshot_params.ComputerCaptureScreenshotParams
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AsyncBinaryAPIResponse,
+ )
+
+ async def click_mouse(
+ self,
+ id: str,
+ *,
+ x: int,
+ y: int,
+ button: Literal["left", "right", "middle", "back", "forward"] | Omit = omit,
+ click_type: Literal["down", "up", "click"] | Omit = omit,
+ hold_keys: SequenceNotStr[str] | Omit = omit,
+ num_clicks: 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,
+ ) -> None:
+ """
+ Simulate a mouse click action on the browser instance
+
+ Args:
+ x: X coordinate of the click position
+
+ y: Y coordinate of the click position
+
+ button: Mouse button to interact with
+
+ click_type: Type of click action
+
+ hold_keys: Modifier keys to hold during the click
+
+ num_clicks: Number of times to repeat the click
+
+ 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._post(
+ f"/browsers/{id}/computer/click_mouse",
+ body=await async_maybe_transform(
+ {
+ "x": x,
+ "y": y,
+ "button": button,
+ "click_type": click_type,
+ "hold_keys": hold_keys,
+ "num_clicks": num_clicks,
+ },
+ computer_click_mouse_params.ComputerClickMouseParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ async def drag_mouse(
+ self,
+ id: str,
+ *,
+ path: Iterable[Iterable[int]],
+ button: Literal["left", "middle", "right"] | Omit = omit,
+ delay: int | Omit = omit,
+ hold_keys: SequenceNotStr[str] | Omit = omit,
+ step_delay_ms: int | Omit = omit,
+ steps_per_segment: 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,
+ ) -> None:
+ """
+ Drag the mouse along a path
+
+ Args:
+ path: Ordered list of [x, y] coordinate pairs to move through while dragging. Must
+ contain at least 2 points.
+
+ button: Mouse button to drag with
+
+ delay: Delay in milliseconds between button down and starting to move along the path.
+
+ hold_keys: Modifier keys to hold during the drag
+
+ step_delay_ms: Delay in milliseconds between relative steps while dragging (not the initial
+ delay).
+
+ steps_per_segment: Number of relative move steps per segment in the path. Minimum 1.
+
+ 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._post(
+ f"/browsers/{id}/computer/drag_mouse",
+ body=await async_maybe_transform(
+ {
+ "path": path,
+ "button": button,
+ "delay": delay,
+ "hold_keys": hold_keys,
+ "step_delay_ms": step_delay_ms,
+ "steps_per_segment": steps_per_segment,
+ },
+ computer_drag_mouse_params.ComputerDragMouseParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ async def move_mouse(
+ self,
+ id: str,
+ *,
+ x: int,
+ y: int,
+ hold_keys: SequenceNotStr[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,
+ ) -> None:
+ """
+ Move the mouse cursor to the specified coordinates on the browser instance
+
+ Args:
+ x: X coordinate to move the cursor to
+
+ y: Y coordinate to move the cursor to
+
+ hold_keys: Modifier keys to hold during the move
+
+ 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._post(
+ f"/browsers/{id}/computer/move_mouse",
+ body=await async_maybe_transform(
+ {
+ "x": x,
+ "y": y,
+ "hold_keys": hold_keys,
+ },
+ computer_move_mouse_params.ComputerMoveMouseParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ async def press_key(
+ self,
+ id: str,
+ *,
+ keys: SequenceNotStr[str],
+ duration: int | Omit = omit,
+ hold_keys: SequenceNotStr[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,
+ ) -> None:
+ """
+ Press one or more keys on the host computer
+
+ Args:
+ keys: List of key symbols to press. Each item should be a key symbol supported by
+ xdotool (see X11 keysym definitions). Examples include "Return", "Shift",
+ "Ctrl", "Alt", "F5". Items in this list could also be combinations, e.g.
+ "Ctrl+t" or "Ctrl+Shift+Tab".
+
+ duration: Duration to hold the keys down in milliseconds. If omitted or 0, keys are
+ tapped.
+
+ hold_keys: Optional modifier keys to hold during the key press sequence.
+
+ 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._post(
+ f"/browsers/{id}/computer/press_key",
+ body=await async_maybe_transform(
+ {
+ "keys": keys,
+ "duration": duration,
+ "hold_keys": hold_keys,
+ },
+ computer_press_key_params.ComputerPressKeyParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ async def scroll(
+ self,
+ id: str,
+ *,
+ x: int,
+ y: int,
+ delta_x: int | Omit = omit,
+ delta_y: int | Omit = omit,
+ hold_keys: SequenceNotStr[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,
+ ) -> None:
+ """
+ Scroll the mouse wheel at a position on the host computer
+
+ Args:
+ x: X coordinate at which to perform the scroll
+
+ y: Y coordinate at which to perform the scroll
+
+ delta_x: Horizontal scroll amount. Positive scrolls right, negative scrolls left.
+
+ delta_y: Vertical scroll amount. Positive scrolls down, negative scrolls up.
+
+ hold_keys: Modifier keys to hold during the scroll
+
+ 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._post(
+ f"/browsers/{id}/computer/scroll",
+ body=await async_maybe_transform(
+ {
+ "x": x,
+ "y": y,
+ "delta_x": delta_x,
+ "delta_y": delta_y,
+ "hold_keys": hold_keys,
+ },
+ computer_scroll_params.ComputerScrollParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ async def type_text(
+ self,
+ id: str,
+ *,
+ text: str,
+ delay: 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,
+ ) -> None:
+ """
+ Type text on the browser instance
+
+ Args:
+ text: Text to type on the browser instance
+
+ delay: Delay in milliseconds between keystrokes
+
+ 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._post(
+ f"/browsers/{id}/computer/type",
+ body=await async_maybe_transform(
+ {
+ "text": text,
+ "delay": delay,
+ },
+ computer_type_text_params.ComputerTypeTextParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class ComputerResourceWithRawResponse:
+ def __init__(self, computer: ComputerResource) -> None:
+ self._computer = computer
+
+ self.capture_screenshot = to_custom_raw_response_wrapper(
+ computer.capture_screenshot,
+ BinaryAPIResponse,
+ )
+ self.click_mouse = to_raw_response_wrapper(
+ computer.click_mouse,
+ )
+ self.drag_mouse = to_raw_response_wrapper(
+ computer.drag_mouse,
+ )
+ self.move_mouse = to_raw_response_wrapper(
+ computer.move_mouse,
+ )
+ self.press_key = to_raw_response_wrapper(
+ computer.press_key,
+ )
+ self.scroll = to_raw_response_wrapper(
+ computer.scroll,
+ )
+ self.type_text = to_raw_response_wrapper(
+ computer.type_text,
+ )
+
+
+class AsyncComputerResourceWithRawResponse:
+ def __init__(self, computer: AsyncComputerResource) -> None:
+ self._computer = computer
+
+ self.capture_screenshot = async_to_custom_raw_response_wrapper(
+ computer.capture_screenshot,
+ AsyncBinaryAPIResponse,
+ )
+ self.click_mouse = async_to_raw_response_wrapper(
+ computer.click_mouse,
+ )
+ self.drag_mouse = async_to_raw_response_wrapper(
+ computer.drag_mouse,
+ )
+ self.move_mouse = async_to_raw_response_wrapper(
+ computer.move_mouse,
+ )
+ self.press_key = async_to_raw_response_wrapper(
+ computer.press_key,
+ )
+ self.scroll = async_to_raw_response_wrapper(
+ computer.scroll,
+ )
+ self.type_text = async_to_raw_response_wrapper(
+ computer.type_text,
+ )
+
+
+class ComputerResourceWithStreamingResponse:
+ def __init__(self, computer: ComputerResource) -> None:
+ self._computer = computer
+
+ self.capture_screenshot = to_custom_streamed_response_wrapper(
+ computer.capture_screenshot,
+ StreamedBinaryAPIResponse,
+ )
+ self.click_mouse = to_streamed_response_wrapper(
+ computer.click_mouse,
+ )
+ self.drag_mouse = to_streamed_response_wrapper(
+ computer.drag_mouse,
+ )
+ self.move_mouse = to_streamed_response_wrapper(
+ computer.move_mouse,
+ )
+ self.press_key = to_streamed_response_wrapper(
+ computer.press_key,
+ )
+ self.scroll = to_streamed_response_wrapper(
+ computer.scroll,
+ )
+ self.type_text = to_streamed_response_wrapper(
+ computer.type_text,
+ )
+
+
+class AsyncComputerResourceWithStreamingResponse:
+ def __init__(self, computer: AsyncComputerResource) -> None:
+ self._computer = computer
+
+ self.capture_screenshot = async_to_custom_streamed_response_wrapper(
+ computer.capture_screenshot,
+ AsyncStreamedBinaryAPIResponse,
+ )
+ self.click_mouse = async_to_streamed_response_wrapper(
+ computer.click_mouse,
+ )
+ self.drag_mouse = async_to_streamed_response_wrapper(
+ computer.drag_mouse,
+ )
+ self.move_mouse = async_to_streamed_response_wrapper(
+ computer.move_mouse,
+ )
+ self.press_key = async_to_streamed_response_wrapper(
+ computer.press_key,
+ )
+ self.scroll = async_to_streamed_response_wrapper(
+ computer.scroll,
+ )
+ self.type_text = async_to_streamed_response_wrapper(
+ computer.type_text,
+ )
diff --git a/src/kernel/resources/extensions.py b/src/kernel/resources/extensions.py
index 45d08d91..2f868716 100644
--- a/src/kernel/resources/extensions.py
+++ b/src/kernel/resources/extensions.py
@@ -7,7 +7,7 @@
import httpx
-from ..types import extension_create_params, extension_download_from_chrome_store_params
+from ..types import extension_upload_params, extension_download_from_chrome_store_params
from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, FileTypes, omit, not_given
from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform
from .._compat import cached_property
@@ -28,7 +28,7 @@
)
from .._base_client import make_request_options
from ..types.extension_list_response import ExtensionListResponse
-from ..types.extension_create_response import ExtensionCreateResponse
+from ..types.extension_upload_response import ExtensionUploadResponse
__all__ = ["ExtensionsResource", "AsyncExtensionsResource"]
@@ -53,58 +53,26 @@ def with_streaming_response(self) -> ExtensionsResourceWithStreamingResponse:
"""
return ExtensionsResourceWithStreamingResponse(self)
- def create(
+ def list(
self,
*,
- file: FileTypes,
- name: 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,
- ) -> ExtensionCreateResponse:
- """Upload a zip file containing an unpacked browser extension.
-
- Optionally provide a
- unique name for later reference.
-
- Args:
- file: ZIP file containing the browser extension.
-
- name: Optional unique name within the organization to reference this extension.
-
- 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
- """
- body = deepcopy_minimal(
- {
- "file": file,
- "name": name,
- }
- )
- files = extract_files(cast(Mapping[str, object], body), paths=[["file"]])
- # It should be noted that the actual Content-Type header that will be
- # sent to the server will contain a `boundary` parameter, e.g.
- # multipart/form-data; boundary=---abc--
- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
- return self._post(
+ ) -> ExtensionListResponse:
+ """List extensions owned by the caller's organization."""
+ return self._get(
"/extensions",
- body=maybe_transform(body, extension_create_params.ExtensionCreateParams),
- files=files,
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=ExtensionCreateResponse,
+ cast_to=ExtensionListResponse,
)
- def retrieve(
+ def delete(
self,
id_or_name: str,
*,
@@ -114,9 +82,9 @@ def retrieve(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> BinaryAPIResponse:
+ ) -> None:
"""
- Download the extension as a ZIP archive by ID or name.
+ Delete an extension by its ID or by its name.
Args:
extra_headers: Send extra headers
@@ -129,35 +97,16 @@ def retrieve(
"""
if not id_or_name:
raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
- extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})}
- return self._get(
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._delete(
f"/extensions/{id_or_name}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=BinaryAPIResponse,
- )
-
- def list(
- self,
- *,
- # 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,
- ) -> ExtensionListResponse:
- """List extensions owned by the caller's organization."""
- return self._get(
- "/extensions",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=ExtensionListResponse,
+ cast_to=NoneType,
)
- def delete(
+ def download(
self,
id_or_name: str,
*,
@@ -167,9 +116,9 @@ def delete(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> None:
+ ) -> BinaryAPIResponse:
"""
- Delete an extension by its ID or by its name.
+ Download the extension as a ZIP archive by ID or name.
Args:
extra_headers: Send extra headers
@@ -182,13 +131,13 @@ def delete(
"""
if not id_or_name:
raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
- extra_headers = {"Accept": "*/*", **(extra_headers or {})}
- return self._delete(
+ extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})}
+ return self._get(
f"/extensions/{id_or_name}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=NoneType,
+ cast_to=BinaryAPIResponse,
)
def download_from_chrome_store(
@@ -239,28 +188,7 @@ def download_from_chrome_store(
cast_to=BinaryAPIResponse,
)
-
-class AsyncExtensionsResource(AsyncAPIResource):
- @cached_property
- def with_raw_response(self) -> AsyncExtensionsResourceWithRawResponse:
- """
- 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/onkernel/kernel-python-sdk#accessing-raw-response-data-eg-headers
- """
- return AsyncExtensionsResourceWithRawResponse(self)
-
- @cached_property
- def with_streaming_response(self) -> AsyncExtensionsResourceWithStreamingResponse:
- """
- An alternative to `.with_raw_response` that doesn't eagerly read the response body.
-
- For more information, see https://www.github.com/onkernel/kernel-python-sdk#with_streaming_response
- """
- return AsyncExtensionsResourceWithStreamingResponse(self)
-
- async def create(
+ def upload(
self,
*,
file: FileTypes,
@@ -271,7 +199,7 @@ async def create(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> ExtensionCreateResponse:
+ ) -> ExtensionUploadResponse:
"""Upload a zip file containing an unpacked browser extension.
Optionally provide a
@@ -301,49 +229,36 @@ async def create(
# sent to the server will contain a `boundary` parameter, e.g.
# multipart/form-data; boundary=---abc--
extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
- return await self._post(
+ return self._post(
"/extensions",
- body=await async_maybe_transform(body, extension_create_params.ExtensionCreateParams),
+ body=maybe_transform(body, extension_upload_params.ExtensionUploadParams),
files=files,
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=ExtensionCreateResponse,
+ cast_to=ExtensionUploadResponse,
)
- async def retrieve(
- self,
- id_or_name: str,
- *,
- # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
- # The extra values given here take precedence over values defined on the client or passed to this method.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> AsyncBinaryAPIResponse:
- """
- Download the extension as a ZIP archive by ID or name.
- Args:
- extra_headers: Send extra headers
+class AsyncExtensionsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncExtensionsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
- extra_query: Add additional query parameters to the request
+ For more information, see https://www.github.com/onkernel/kernel-python-sdk#accessing-raw-response-data-eg-headers
+ """
+ return AsyncExtensionsResourceWithRawResponse(self)
- extra_body: Add additional JSON properties to the request
+ @cached_property
+ def with_streaming_response(self) -> AsyncExtensionsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
- timeout: Override the client-level default timeout for this request, in seconds
+ For more information, see https://www.github.com/onkernel/kernel-python-sdk#with_streaming_response
"""
- if not id_or_name:
- raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
- extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})}
- return await self._get(
- f"/extensions/{id_or_name}",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=AsyncBinaryAPIResponse,
- )
+ return AsyncExtensionsResourceWithStreamingResponse(self)
async def list(
self,
@@ -398,6 +313,40 @@ async def delete(
cast_to=NoneType,
)
+ async def download(
+ self,
+ id_or_name: str,
+ *,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AsyncBinaryAPIResponse:
+ """
+ Download the extension as a ZIP archive by ID or name.
+
+ 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_or_name:
+ raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}")
+ extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})}
+ return await self._get(
+ f"/extensions/{id_or_name}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=AsyncBinaryAPIResponse,
+ )
+
async def download_from_chrome_store(
self,
*,
@@ -446,94 +395,145 @@ async def download_from_chrome_store(
cast_to=AsyncBinaryAPIResponse,
)
+ async def upload(
+ self,
+ *,
+ file: FileTypes,
+ name: 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,
+ ) -> ExtensionUploadResponse:
+ """Upload a zip file containing an unpacked browser extension.
+
+ Optionally provide a
+ unique name for later reference.
+
+ Args:
+ file: ZIP file containing the browser extension.
+
+ name: Optional unique name within the organization to reference this extension.
+
+ 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
+ """
+ body = deepcopy_minimal(
+ {
+ "file": file,
+ "name": name,
+ }
+ )
+ files = extract_files(cast(Mapping[str, object], body), paths=[["file"]])
+ # It should be noted that the actual Content-Type header that will be
+ # sent to the server will contain a `boundary` parameter, e.g.
+ # multipart/form-data; boundary=---abc--
+ extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
+ return await self._post(
+ "/extensions",
+ body=await async_maybe_transform(body, extension_upload_params.ExtensionUploadParams),
+ files=files,
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ExtensionUploadResponse,
+ )
+
class ExtensionsResourceWithRawResponse:
def __init__(self, extensions: ExtensionsResource) -> None:
self._extensions = extensions
- self.create = to_raw_response_wrapper(
- extensions.create,
- )
- self.retrieve = to_custom_raw_response_wrapper(
- extensions.retrieve,
- BinaryAPIResponse,
- )
self.list = to_raw_response_wrapper(
extensions.list,
)
self.delete = to_raw_response_wrapper(
extensions.delete,
)
+ self.download = to_custom_raw_response_wrapper(
+ extensions.download,
+ BinaryAPIResponse,
+ )
self.download_from_chrome_store = to_custom_raw_response_wrapper(
extensions.download_from_chrome_store,
BinaryAPIResponse,
)
+ self.upload = to_raw_response_wrapper(
+ extensions.upload,
+ )
class AsyncExtensionsResourceWithRawResponse:
def __init__(self, extensions: AsyncExtensionsResource) -> None:
self._extensions = extensions
- self.create = async_to_raw_response_wrapper(
- extensions.create,
- )
- self.retrieve = async_to_custom_raw_response_wrapper(
- extensions.retrieve,
- AsyncBinaryAPIResponse,
- )
self.list = async_to_raw_response_wrapper(
extensions.list,
)
self.delete = async_to_raw_response_wrapper(
extensions.delete,
)
+ self.download = async_to_custom_raw_response_wrapper(
+ extensions.download,
+ AsyncBinaryAPIResponse,
+ )
self.download_from_chrome_store = async_to_custom_raw_response_wrapper(
extensions.download_from_chrome_store,
AsyncBinaryAPIResponse,
)
+ self.upload = async_to_raw_response_wrapper(
+ extensions.upload,
+ )
class ExtensionsResourceWithStreamingResponse:
def __init__(self, extensions: ExtensionsResource) -> None:
self._extensions = extensions
- self.create = to_streamed_response_wrapper(
- extensions.create,
- )
- self.retrieve = to_custom_streamed_response_wrapper(
- extensions.retrieve,
- StreamedBinaryAPIResponse,
- )
self.list = to_streamed_response_wrapper(
extensions.list,
)
self.delete = to_streamed_response_wrapper(
extensions.delete,
)
+ self.download = to_custom_streamed_response_wrapper(
+ extensions.download,
+ StreamedBinaryAPIResponse,
+ )
self.download_from_chrome_store = to_custom_streamed_response_wrapper(
extensions.download_from_chrome_store,
StreamedBinaryAPIResponse,
)
+ self.upload = to_streamed_response_wrapper(
+ extensions.upload,
+ )
class AsyncExtensionsResourceWithStreamingResponse:
def __init__(self, extensions: AsyncExtensionsResource) -> None:
self._extensions = extensions
- self.create = async_to_streamed_response_wrapper(
- extensions.create,
- )
- self.retrieve = async_to_custom_streamed_response_wrapper(
- extensions.retrieve,
- AsyncStreamedBinaryAPIResponse,
- )
self.list = async_to_streamed_response_wrapper(
extensions.list,
)
self.delete = async_to_streamed_response_wrapper(
extensions.delete,
)
+ self.download = async_to_custom_streamed_response_wrapper(
+ extensions.download,
+ AsyncStreamedBinaryAPIResponse,
+ )
self.download_from_chrome_store = async_to_custom_streamed_response_wrapper(
extensions.download_from_chrome_store,
AsyncStreamedBinaryAPIResponse,
)
+ self.upload = async_to_streamed_response_wrapper(
+ extensions.upload,
+ )
diff --git a/src/kernel/types/__init__.py b/src/kernel/types/__init__.py
index 2edc0bcd..6b49cf7f 100644
--- a/src/kernel/types/__init__.py
+++ b/src/kernel/types/__init__.py
@@ -27,8 +27,8 @@
from .invocation_list_params import InvocationListParams as InvocationListParams
from .invocation_state_event import InvocationStateEvent as InvocationStateEvent
from .browser_create_response import BrowserCreateResponse as BrowserCreateResponse
-from .extension_create_params import ExtensionCreateParams as ExtensionCreateParams
from .extension_list_response import ExtensionListResponse as ExtensionListResponse
+from .extension_upload_params import ExtensionUploadParams as ExtensionUploadParams
from .proxy_retrieve_response import ProxyRetrieveResponse as ProxyRetrieveResponse
from .deployment_create_params import DeploymentCreateParams as DeploymentCreateParams
from .deployment_follow_params import DeploymentFollowParams as DeploymentFollowParams
@@ -39,7 +39,7 @@
from .invocation_update_params import InvocationUpdateParams as InvocationUpdateParams
from .browser_persistence_param import BrowserPersistenceParam as BrowserPersistenceParam
from .browser_retrieve_response import BrowserRetrieveResponse as BrowserRetrieveResponse
-from .extension_create_response import ExtensionCreateResponse as ExtensionCreateResponse
+from .extension_upload_response import ExtensionUploadResponse as ExtensionUploadResponse
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/browsers/__init__.py b/src/kernel/types/browsers/__init__.py
index d0b6b383..9b0ed53a 100644
--- a/src/kernel/types/browsers/__init__.py
+++ b/src/kernel/types/browsers/__init__.py
@@ -22,11 +22,18 @@
from .process_exec_response import ProcessExecResponse as ProcessExecResponse
from .process_kill_response import ProcessKillResponse as ProcessKillResponse
from .replay_start_response import ReplayStartResponse as ReplayStartResponse
+from .computer_scroll_params import ComputerScrollParams as ComputerScrollParams
from .process_spawn_response import ProcessSpawnResponse as ProcessSpawnResponse
from .process_stdin_response import ProcessStdinResponse as ProcessStdinResponse
from .process_status_response import ProcessStatusResponse as ProcessStatusResponse
+from .computer_press_key_params import ComputerPressKeyParams as ComputerPressKeyParams
+from .computer_type_text_params import ComputerTypeTextParams as ComputerTypeTextParams
from .f_create_directory_params import FCreateDirectoryParams as FCreateDirectoryParams
from .f_delete_directory_params import FDeleteDirectoryParams as FDeleteDirectoryParams
from .f_download_dir_zip_params import FDownloadDirZipParams as FDownloadDirZipParams
+from .computer_drag_mouse_params import ComputerDragMouseParams as ComputerDragMouseParams
+from .computer_move_mouse_params import ComputerMoveMouseParams as ComputerMoveMouseParams
+from .computer_click_mouse_params import ComputerClickMouseParams as ComputerClickMouseParams
from .f_set_file_permissions_params import FSetFilePermissionsParams as FSetFilePermissionsParams
from .process_stdout_stream_response import ProcessStdoutStreamResponse as ProcessStdoutStreamResponse
+from .computer_capture_screenshot_params import ComputerCaptureScreenshotParams as ComputerCaptureScreenshotParams
diff --git a/src/kernel/types/browsers/computer_capture_screenshot_params.py b/src/kernel/types/browsers/computer_capture_screenshot_params.py
new file mode 100644
index 00000000..942cef30
--- /dev/null
+++ b/src/kernel/types/browsers/computer_capture_screenshot_params.py
@@ -0,0 +1,25 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["ComputerCaptureScreenshotParams", "Region"]
+
+
+class ComputerCaptureScreenshotParams(TypedDict, total=False):
+ region: Region
+
+
+class Region(TypedDict, total=False):
+ height: Required[int]
+ """Height of the region in pixels"""
+
+ width: Required[int]
+ """Width of the region in pixels"""
+
+ x: Required[int]
+ """X coordinate of the region's top-left corner"""
+
+ y: Required[int]
+ """Y coordinate of the region's top-left corner"""
diff --git a/src/kernel/types/browsers/computer_click_mouse_params.py b/src/kernel/types/browsers/computer_click_mouse_params.py
new file mode 100644
index 00000000..9bde2e6a
--- /dev/null
+++ b/src/kernel/types/browsers/computer_click_mouse_params.py
@@ -0,0 +1,29 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+from ..._types import SequenceNotStr
+
+__all__ = ["ComputerClickMouseParams"]
+
+
+class ComputerClickMouseParams(TypedDict, total=False):
+ x: Required[int]
+ """X coordinate of the click position"""
+
+ y: Required[int]
+ """Y coordinate of the click position"""
+
+ button: Literal["left", "right", "middle", "back", "forward"]
+ """Mouse button to interact with"""
+
+ click_type: Literal["down", "up", "click"]
+ """Type of click action"""
+
+ hold_keys: SequenceNotStr[str]
+ """Modifier keys to hold during the click"""
+
+ num_clicks: int
+ """Number of times to repeat the click"""
diff --git a/src/kernel/types/browsers/computer_drag_mouse_params.py b/src/kernel/types/browsers/computer_drag_mouse_params.py
new file mode 100644
index 00000000..fb03b4be
--- /dev/null
+++ b/src/kernel/types/browsers/computer_drag_mouse_params.py
@@ -0,0 +1,36 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable
+from typing_extensions import Literal, Required, TypedDict
+
+from ..._types import SequenceNotStr
+
+__all__ = ["ComputerDragMouseParams"]
+
+
+class ComputerDragMouseParams(TypedDict, total=False):
+ path: Required[Iterable[Iterable[int]]]
+ """Ordered list of [x, y] coordinate pairs to move through while dragging.
+
+ Must contain at least 2 points.
+ """
+
+ button: Literal["left", "middle", "right"]
+ """Mouse button to drag with"""
+
+ delay: int
+ """Delay in milliseconds between button down and starting to move along the path."""
+
+ hold_keys: SequenceNotStr[str]
+ """Modifier keys to hold during the drag"""
+
+ step_delay_ms: int
+ """
+ Delay in milliseconds between relative steps while dragging (not the initial
+ delay).
+ """
+
+ steps_per_segment: int
+ """Number of relative move steps per segment in the path. Minimum 1."""
diff --git a/src/kernel/types/browsers/computer_move_mouse_params.py b/src/kernel/types/browsers/computer_move_mouse_params.py
new file mode 100644
index 00000000..1769e074
--- /dev/null
+++ b/src/kernel/types/browsers/computer_move_mouse_params.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+from ..._types import SequenceNotStr
+
+__all__ = ["ComputerMoveMouseParams"]
+
+
+class ComputerMoveMouseParams(TypedDict, total=False):
+ x: Required[int]
+ """X coordinate to move the cursor to"""
+
+ y: Required[int]
+ """Y coordinate to move the cursor to"""
+
+ hold_keys: SequenceNotStr[str]
+ """Modifier keys to hold during the move"""
diff --git a/src/kernel/types/browsers/computer_press_key_params.py b/src/kernel/types/browsers/computer_press_key_params.py
new file mode 100644
index 00000000..ea2c9b45
--- /dev/null
+++ b/src/kernel/types/browsers/computer_press_key_params.py
@@ -0,0 +1,28 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+from ..._types import SequenceNotStr
+
+__all__ = ["ComputerPressKeyParams"]
+
+
+class ComputerPressKeyParams(TypedDict, total=False):
+ keys: Required[SequenceNotStr[str]]
+ """List of key symbols to press.
+
+ Each item should be a key symbol supported by xdotool (see X11 keysym
+ definitions). Examples include "Return", "Shift", "Ctrl", "Alt", "F5". Items in
+ this list could also be combinations, e.g. "Ctrl+t" or "Ctrl+Shift+Tab".
+ """
+
+ duration: int
+ """Duration to hold the keys down in milliseconds.
+
+ If omitted or 0, keys are tapped.
+ """
+
+ hold_keys: SequenceNotStr[str]
+ """Optional modifier keys to hold during the key press sequence."""
diff --git a/src/kernel/types/browsers/computer_scroll_params.py b/src/kernel/types/browsers/computer_scroll_params.py
new file mode 100644
index 00000000..110cb302
--- /dev/null
+++ b/src/kernel/types/browsers/computer_scroll_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 Required, TypedDict
+
+from ..._types import SequenceNotStr
+
+__all__ = ["ComputerScrollParams"]
+
+
+class ComputerScrollParams(TypedDict, total=False):
+ x: Required[int]
+ """X coordinate at which to perform the scroll"""
+
+ y: Required[int]
+ """Y coordinate at which to perform the scroll"""
+
+ delta_x: int
+ """Horizontal scroll amount. Positive scrolls right, negative scrolls left."""
+
+ delta_y: int
+ """Vertical scroll amount. Positive scrolls down, negative scrolls up."""
+
+ hold_keys: SequenceNotStr[str]
+ """Modifier keys to hold during the scroll"""
diff --git a/src/kernel/types/browsers/computer_type_text_params.py b/src/kernel/types/browsers/computer_type_text_params.py
new file mode 100644
index 00000000..3a2c5133
--- /dev/null
+++ b/src/kernel/types/browsers/computer_type_text_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 Required, TypedDict
+
+__all__ = ["ComputerTypeTextParams"]
+
+
+class ComputerTypeTextParams(TypedDict, total=False):
+ text: Required[str]
+ """Text to type on the browser instance"""
+
+ delay: int
+ """Delay in milliseconds between keystrokes"""
diff --git a/src/kernel/types/extension_create_params.py b/src/kernel/types/extension_upload_params.py
similarity index 81%
rename from src/kernel/types/extension_create_params.py
rename to src/kernel/types/extension_upload_params.py
index 6bb2b397..d36dde31 100644
--- a/src/kernel/types/extension_create_params.py
+++ b/src/kernel/types/extension_upload_params.py
@@ -6,10 +6,10 @@
from .._types import FileTypes
-__all__ = ["ExtensionCreateParams"]
+__all__ = ["ExtensionUploadParams"]
-class ExtensionCreateParams(TypedDict, total=False):
+class ExtensionUploadParams(TypedDict, total=False):
file: Required[FileTypes]
"""ZIP file containing the browser extension."""
diff --git a/src/kernel/types/extension_create_response.py b/src/kernel/types/extension_upload_response.py
similarity index 88%
rename from src/kernel/types/extension_create_response.py
rename to src/kernel/types/extension_upload_response.py
index c4fd6301..373e8861 100644
--- a/src/kernel/types/extension_create_response.py
+++ b/src/kernel/types/extension_upload_response.py
@@ -5,10 +5,10 @@
from .._models import BaseModel
-__all__ = ["ExtensionCreateResponse"]
+__all__ = ["ExtensionUploadResponse"]
-class ExtensionCreateResponse(BaseModel):
+class ExtensionUploadResponse(BaseModel):
id: str
"""Unique identifier for the extension"""
diff --git a/tests/api_resources/browsers/test_computer.py b/tests/api_resources/browsers/test_computer.py
new file mode 100644
index 00000000..9e245481
--- /dev/null
+++ b/tests/api_resources/browsers/test_computer.py
@@ -0,0 +1,892 @@
+# 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 httpx
+import pytest
+from respx import MockRouter
+
+from kernel import Kernel, AsyncKernel
+from kernel._response import (
+ BinaryAPIResponse,
+ AsyncBinaryAPIResponse,
+ StreamedBinaryAPIResponse,
+ AsyncStreamedBinaryAPIResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestComputer:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_method_capture_screenshot(self, client: Kernel, respx_mock: MockRouter) -> None:
+ respx_mock.post("/browsers/id/computer/screenshot").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ computer = client.browsers.computer.capture_screenshot(
+ id="id",
+ )
+ assert computer.is_closed
+ assert computer.json() == {"foo": "bar"}
+ assert cast(Any, computer.is_closed) is True
+ assert isinstance(computer, BinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_method_capture_screenshot_with_all_params(self, client: Kernel, respx_mock: MockRouter) -> None:
+ respx_mock.post("/browsers/id/computer/screenshot").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ computer = client.browsers.computer.capture_screenshot(
+ id="id",
+ region={
+ "height": 0,
+ "width": 0,
+ "x": 0,
+ "y": 0,
+ },
+ )
+ assert computer.is_closed
+ assert computer.json() == {"foo": "bar"}
+ assert cast(Any, computer.is_closed) is True
+ assert isinstance(computer, BinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_raw_response_capture_screenshot(self, client: Kernel, respx_mock: MockRouter) -> None:
+ respx_mock.post("/browsers/id/computer/screenshot").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+
+ computer = client.browsers.computer.with_raw_response.capture_screenshot(
+ id="id",
+ )
+
+ assert computer.is_closed is True
+ assert computer.http_request.headers.get("X-Stainless-Lang") == "python"
+ assert computer.json() == {"foo": "bar"}
+ assert isinstance(computer, BinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_streaming_response_capture_screenshot(self, client: Kernel, respx_mock: MockRouter) -> None:
+ respx_mock.post("/browsers/id/computer/screenshot").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ with client.browsers.computer.with_streaming_response.capture_screenshot(
+ id="id",
+ ) as computer:
+ assert not computer.is_closed
+ assert computer.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ assert computer.json() == {"foo": "bar"}
+ assert cast(Any, computer.is_closed) is True
+ assert isinstance(computer, StreamedBinaryAPIResponse)
+
+ assert cast(Any, computer.is_closed) is True
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_path_params_capture_screenshot(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.browsers.computer.with_raw_response.capture_screenshot(
+ id="",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_click_mouse(self, client: Kernel) -> None:
+ computer = client.browsers.computer.click_mouse(
+ id="id",
+ x=0,
+ y=0,
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_click_mouse_with_all_params(self, client: Kernel) -> None:
+ computer = client.browsers.computer.click_mouse(
+ id="id",
+ x=0,
+ y=0,
+ button="left",
+ click_type="down",
+ hold_keys=["string"],
+ num_clicks=0,
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_click_mouse(self, client: Kernel) -> None:
+ response = client.browsers.computer.with_raw_response.click_mouse(
+ id="id",
+ x=0,
+ y=0,
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ computer = response.parse()
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_click_mouse(self, client: Kernel) -> None:
+ with client.browsers.computer.with_streaming_response.click_mouse(
+ id="id",
+ x=0,
+ y=0,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ computer = response.parse()
+ assert computer is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_click_mouse(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.browsers.computer.with_raw_response.click_mouse(
+ id="",
+ x=0,
+ y=0,
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_drag_mouse(self, client: Kernel) -> None:
+ computer = client.browsers.computer.drag_mouse(
+ id="id",
+ path=[[0, 0], [0, 0]],
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_drag_mouse_with_all_params(self, client: Kernel) -> None:
+ computer = client.browsers.computer.drag_mouse(
+ id="id",
+ path=[[0, 0], [0, 0]],
+ button="left",
+ delay=0,
+ hold_keys=["string"],
+ step_delay_ms=0,
+ steps_per_segment=1,
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_drag_mouse(self, client: Kernel) -> None:
+ response = client.browsers.computer.with_raw_response.drag_mouse(
+ id="id",
+ path=[[0, 0], [0, 0]],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ computer = response.parse()
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_drag_mouse(self, client: Kernel) -> None:
+ with client.browsers.computer.with_streaming_response.drag_mouse(
+ id="id",
+ path=[[0, 0], [0, 0]],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ computer = response.parse()
+ assert computer is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_drag_mouse(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.browsers.computer.with_raw_response.drag_mouse(
+ id="",
+ path=[[0, 0], [0, 0]],
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_move_mouse(self, client: Kernel) -> None:
+ computer = client.browsers.computer.move_mouse(
+ id="id",
+ x=0,
+ y=0,
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_move_mouse_with_all_params(self, client: Kernel) -> None:
+ computer = client.browsers.computer.move_mouse(
+ id="id",
+ x=0,
+ y=0,
+ hold_keys=["string"],
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_move_mouse(self, client: Kernel) -> None:
+ response = client.browsers.computer.with_raw_response.move_mouse(
+ id="id",
+ x=0,
+ y=0,
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ computer = response.parse()
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_move_mouse(self, client: Kernel) -> None:
+ with client.browsers.computer.with_streaming_response.move_mouse(
+ id="id",
+ x=0,
+ y=0,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ computer = response.parse()
+ assert computer is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_move_mouse(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.browsers.computer.with_raw_response.move_mouse(
+ id="",
+ x=0,
+ y=0,
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_press_key(self, client: Kernel) -> None:
+ computer = client.browsers.computer.press_key(
+ id="id",
+ keys=["string"],
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_press_key_with_all_params(self, client: Kernel) -> None:
+ computer = client.browsers.computer.press_key(
+ id="id",
+ keys=["string"],
+ duration=0,
+ hold_keys=["string"],
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_press_key(self, client: Kernel) -> None:
+ response = client.browsers.computer.with_raw_response.press_key(
+ id="id",
+ keys=["string"],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ computer = response.parse()
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_press_key(self, client: Kernel) -> None:
+ with client.browsers.computer.with_streaming_response.press_key(
+ id="id",
+ keys=["string"],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ computer = response.parse()
+ assert computer is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_press_key(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.browsers.computer.with_raw_response.press_key(
+ id="",
+ keys=["string"],
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_scroll(self, client: Kernel) -> None:
+ computer = client.browsers.computer.scroll(
+ id="id",
+ x=0,
+ y=0,
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_scroll_with_all_params(self, client: Kernel) -> None:
+ computer = client.browsers.computer.scroll(
+ id="id",
+ x=0,
+ y=0,
+ delta_x=0,
+ delta_y=0,
+ hold_keys=["string"],
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_scroll(self, client: Kernel) -> None:
+ response = client.browsers.computer.with_raw_response.scroll(
+ id="id",
+ x=0,
+ y=0,
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ computer = response.parse()
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_scroll(self, client: Kernel) -> None:
+ with client.browsers.computer.with_streaming_response.scroll(
+ id="id",
+ x=0,
+ y=0,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ computer = response.parse()
+ assert computer is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_scroll(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.browsers.computer.with_raw_response.scroll(
+ id="",
+ x=0,
+ y=0,
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_type_text(self, client: Kernel) -> None:
+ computer = client.browsers.computer.type_text(
+ id="id",
+ text="text",
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_type_text_with_all_params(self, client: Kernel) -> None:
+ computer = client.browsers.computer.type_text(
+ id="id",
+ text="text",
+ delay=0,
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_type_text(self, client: Kernel) -> None:
+ response = client.browsers.computer.with_raw_response.type_text(
+ id="id",
+ text="text",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ computer = response.parse()
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_type_text(self, client: Kernel) -> None:
+ with client.browsers.computer.with_streaming_response.type_text(
+ id="id",
+ text="text",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ computer = response.parse()
+ assert computer is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_type_text(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.browsers.computer.with_raw_response.type_text(
+ id="",
+ text="text",
+ )
+
+
+class TestAsyncComputer:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_method_capture_screenshot(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
+ respx_mock.post("/browsers/id/computer/screenshot").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ computer = await async_client.browsers.computer.capture_screenshot(
+ id="id",
+ )
+ assert computer.is_closed
+ assert await computer.json() == {"foo": "bar"}
+ assert cast(Any, computer.is_closed) is True
+ assert isinstance(computer, AsyncBinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_method_capture_screenshot_with_all_params(
+ self, async_client: AsyncKernel, respx_mock: MockRouter
+ ) -> None:
+ respx_mock.post("/browsers/id/computer/screenshot").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ computer = await async_client.browsers.computer.capture_screenshot(
+ id="id",
+ region={
+ "height": 0,
+ "width": 0,
+ "x": 0,
+ "y": 0,
+ },
+ )
+ assert computer.is_closed
+ assert await computer.json() == {"foo": "bar"}
+ assert cast(Any, computer.is_closed) is True
+ assert isinstance(computer, AsyncBinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_raw_response_capture_screenshot(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
+ respx_mock.post("/browsers/id/computer/screenshot").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+
+ computer = await async_client.browsers.computer.with_raw_response.capture_screenshot(
+ id="id",
+ )
+
+ assert computer.is_closed is True
+ assert computer.http_request.headers.get("X-Stainless-Lang") == "python"
+ assert await computer.json() == {"foo": "bar"}
+ assert isinstance(computer, AsyncBinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_streaming_response_capture_screenshot(
+ self, async_client: AsyncKernel, respx_mock: MockRouter
+ ) -> None:
+ respx_mock.post("/browsers/id/computer/screenshot").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ async with async_client.browsers.computer.with_streaming_response.capture_screenshot(
+ id="id",
+ ) as computer:
+ assert not computer.is_closed
+ assert computer.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ assert await computer.json() == {"foo": "bar"}
+ assert cast(Any, computer.is_closed) is True
+ assert isinstance(computer, AsyncStreamedBinaryAPIResponse)
+
+ assert cast(Any, computer.is_closed) is True
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_path_params_capture_screenshot(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.browsers.computer.with_raw_response.capture_screenshot(
+ id="",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_click_mouse(self, async_client: AsyncKernel) -> None:
+ computer = await async_client.browsers.computer.click_mouse(
+ id="id",
+ x=0,
+ y=0,
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_click_mouse_with_all_params(self, async_client: AsyncKernel) -> None:
+ computer = await async_client.browsers.computer.click_mouse(
+ id="id",
+ x=0,
+ y=0,
+ button="left",
+ click_type="down",
+ hold_keys=["string"],
+ num_clicks=0,
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_click_mouse(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.computer.with_raw_response.click_mouse(
+ id="id",
+ x=0,
+ y=0,
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ computer = await response.parse()
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_click_mouse(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.computer.with_streaming_response.click_mouse(
+ id="id",
+ x=0,
+ y=0,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ computer = await response.parse()
+ assert computer is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_click_mouse(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.browsers.computer.with_raw_response.click_mouse(
+ id="",
+ x=0,
+ y=0,
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_drag_mouse(self, async_client: AsyncKernel) -> None:
+ computer = await async_client.browsers.computer.drag_mouse(
+ id="id",
+ path=[[0, 0], [0, 0]],
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_drag_mouse_with_all_params(self, async_client: AsyncKernel) -> None:
+ computer = await async_client.browsers.computer.drag_mouse(
+ id="id",
+ path=[[0, 0], [0, 0]],
+ button="left",
+ delay=0,
+ hold_keys=["string"],
+ step_delay_ms=0,
+ steps_per_segment=1,
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_drag_mouse(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.computer.with_raw_response.drag_mouse(
+ id="id",
+ path=[[0, 0], [0, 0]],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ computer = await response.parse()
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_drag_mouse(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.computer.with_streaming_response.drag_mouse(
+ id="id",
+ path=[[0, 0], [0, 0]],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ computer = await response.parse()
+ assert computer is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_drag_mouse(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.browsers.computer.with_raw_response.drag_mouse(
+ id="",
+ path=[[0, 0], [0, 0]],
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_move_mouse(self, async_client: AsyncKernel) -> None:
+ computer = await async_client.browsers.computer.move_mouse(
+ id="id",
+ x=0,
+ y=0,
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_move_mouse_with_all_params(self, async_client: AsyncKernel) -> None:
+ computer = await async_client.browsers.computer.move_mouse(
+ id="id",
+ x=0,
+ y=0,
+ hold_keys=["string"],
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_move_mouse(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.computer.with_raw_response.move_mouse(
+ id="id",
+ x=0,
+ y=0,
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ computer = await response.parse()
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_move_mouse(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.computer.with_streaming_response.move_mouse(
+ id="id",
+ x=0,
+ y=0,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ computer = await response.parse()
+ assert computer is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_move_mouse(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.browsers.computer.with_raw_response.move_mouse(
+ id="",
+ x=0,
+ y=0,
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_press_key(self, async_client: AsyncKernel) -> None:
+ computer = await async_client.browsers.computer.press_key(
+ id="id",
+ keys=["string"],
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_press_key_with_all_params(self, async_client: AsyncKernel) -> None:
+ computer = await async_client.browsers.computer.press_key(
+ id="id",
+ keys=["string"],
+ duration=0,
+ hold_keys=["string"],
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_press_key(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.computer.with_raw_response.press_key(
+ id="id",
+ keys=["string"],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ computer = await response.parse()
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_press_key(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.computer.with_streaming_response.press_key(
+ id="id",
+ keys=["string"],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ computer = await response.parse()
+ assert computer is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_press_key(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.browsers.computer.with_raw_response.press_key(
+ id="",
+ keys=["string"],
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_scroll(self, async_client: AsyncKernel) -> None:
+ computer = await async_client.browsers.computer.scroll(
+ id="id",
+ x=0,
+ y=0,
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_scroll_with_all_params(self, async_client: AsyncKernel) -> None:
+ computer = await async_client.browsers.computer.scroll(
+ id="id",
+ x=0,
+ y=0,
+ delta_x=0,
+ delta_y=0,
+ hold_keys=["string"],
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_scroll(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.computer.with_raw_response.scroll(
+ id="id",
+ x=0,
+ y=0,
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ computer = await response.parse()
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_scroll(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.computer.with_streaming_response.scroll(
+ id="id",
+ x=0,
+ y=0,
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ computer = await response.parse()
+ assert computer is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_scroll(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.browsers.computer.with_raw_response.scroll(
+ id="",
+ x=0,
+ y=0,
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_type_text(self, async_client: AsyncKernel) -> None:
+ computer = await async_client.browsers.computer.type_text(
+ id="id",
+ text="text",
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_type_text_with_all_params(self, async_client: AsyncKernel) -> None:
+ computer = await async_client.browsers.computer.type_text(
+ id="id",
+ text="text",
+ delay=0,
+ )
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_type_text(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.computer.with_raw_response.type_text(
+ id="id",
+ text="text",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ computer = await response.parse()
+ assert computer is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_type_text(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.computer.with_streaming_response.type_text(
+ id="id",
+ text="text",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ computer = await response.parse()
+ assert computer is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_type_text(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.browsers.computer.with_raw_response.type_text(
+ id="",
+ text="text",
+ )
diff --git a/tests/api_resources/test_extensions.py b/tests/api_resources/test_extensions.py
index ffdb02f6..5d61f327 100644
--- a/tests/api_resources/test_extensions.py
+++ b/tests/api_resources/test_extensions.py
@@ -13,7 +13,7 @@
from tests.utils import assert_matches_type
from kernel.types import (
ExtensionListResponse,
- ExtensionCreateResponse,
+ ExtensionUploadResponse,
)
from kernel._response import (
BinaryAPIResponse,
@@ -28,99 +28,6 @@
class TestExtensions:
parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- def test_method_create(self, client: Kernel) -> None:
- extension = client.extensions.create(
- file=b"raw file contents",
- )
- assert_matches_type(ExtensionCreateResponse, extension, path=["response"])
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- def test_method_create_with_all_params(self, client: Kernel) -> None:
- extension = client.extensions.create(
- file=b"raw file contents",
- name="name",
- )
- assert_matches_type(ExtensionCreateResponse, extension, path=["response"])
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- def test_raw_response_create(self, client: Kernel) -> None:
- response = client.extensions.with_raw_response.create(
- file=b"raw file contents",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- extension = response.parse()
- assert_matches_type(ExtensionCreateResponse, extension, path=["response"])
-
- @pytest.mark.skip(reason="Prism tests are disabled")
- @parametrize
- def test_streaming_response_create(self, client: Kernel) -> None:
- with client.extensions.with_streaming_response.create(
- file=b"raw file contents",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- extension = response.parse()
- assert_matches_type(ExtensionCreateResponse, extension, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_method_retrieve(self, client: Kernel, respx_mock: MockRouter) -> None:
- respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- extension = client.extensions.retrieve(
- "id_or_name",
- )
- assert extension.is_closed
- assert extension.json() == {"foo": "bar"}
- assert cast(Any, extension.is_closed) is True
- assert isinstance(extension, BinaryAPIResponse)
-
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_raw_response_retrieve(self, client: Kernel, respx_mock: MockRouter) -> None:
- respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
-
- extension = client.extensions.with_raw_response.retrieve(
- "id_or_name",
- )
-
- assert extension.is_closed is True
- assert extension.http_request.headers.get("X-Stainless-Lang") == "python"
- assert extension.json() == {"foo": "bar"}
- assert isinstance(extension, BinaryAPIResponse)
-
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_streaming_response_retrieve(self, client: Kernel, respx_mock: MockRouter) -> None:
- respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- with client.extensions.with_streaming_response.retrieve(
- "id_or_name",
- ) as extension:
- assert not extension.is_closed
- assert extension.http_request.headers.get("X-Stainless-Lang") == "python"
-
- assert extension.json() == {"foo": "bar"}
- assert cast(Any, extension.is_closed) is True
- assert isinstance(extension, StreamedBinaryAPIResponse)
-
- assert cast(Any, extension.is_closed) is True
-
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_path_params_retrieve(self, client: Kernel) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
- client.extensions.with_raw_response.retrieve(
- "",
- )
-
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_method_list(self, client: Kernel) -> None:
@@ -191,6 +98,56 @@ def test_path_params_delete(self, client: Kernel) -> None:
"",
)
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_method_download(self, client: Kernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ extension = client.extensions.download(
+ "id_or_name",
+ )
+ assert extension.is_closed
+ assert extension.json() == {"foo": "bar"}
+ assert cast(Any, extension.is_closed) is True
+ assert isinstance(extension, BinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_raw_response_download(self, client: Kernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+
+ extension = client.extensions.with_raw_response.download(
+ "id_or_name",
+ )
+
+ assert extension.is_closed is True
+ assert extension.http_request.headers.get("X-Stainless-Lang") == "python"
+ assert extension.json() == {"foo": "bar"}
+ assert isinstance(extension, BinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_streaming_response_download(self, client: Kernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ with client.extensions.with_streaming_response.download(
+ "id_or_name",
+ ) as extension:
+ assert not extension.is_closed
+ assert extension.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ assert extension.json() == {"foo": "bar"}
+ assert cast(Any, extension.is_closed) is True
+ assert isinstance(extension, StreamedBinaryAPIResponse)
+
+ assert cast(Any, extension.is_closed) is True
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_path_params_download(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
+ client.extensions.with_raw_response.download(
+ "",
+ )
+
@parametrize
@pytest.mark.respx(base_url=base_url)
def test_method_download_from_chrome_store(self, client: Kernel, respx_mock: MockRouter) -> None:
@@ -246,104 +203,54 @@ def test_streaming_response_download_from_chrome_store(self, client: Kernel, res
assert cast(Any, extension.is_closed) is True
-
-class TestAsyncExtensions:
- parametrize = pytest.mark.parametrize(
- "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
- )
-
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_method_create(self, async_client: AsyncKernel) -> None:
- extension = await async_client.extensions.create(
+ def test_method_upload(self, client: Kernel) -> None:
+ extension = client.extensions.upload(
file=b"raw file contents",
)
- assert_matches_type(ExtensionCreateResponse, extension, path=["response"])
+ assert_matches_type(ExtensionUploadResponse, extension, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_method_create_with_all_params(self, async_client: AsyncKernel) -> None:
- extension = await async_client.extensions.create(
+ def test_method_upload_with_all_params(self, client: Kernel) -> None:
+ extension = client.extensions.upload(
file=b"raw file contents",
name="name",
)
- assert_matches_type(ExtensionCreateResponse, extension, path=["response"])
+ assert_matches_type(ExtensionUploadResponse, extension, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_raw_response_create(self, async_client: AsyncKernel) -> None:
- response = await async_client.extensions.with_raw_response.create(
+ def test_raw_response_upload(self, client: Kernel) -> None:
+ response = client.extensions.with_raw_response.upload(
file=b"raw file contents",
)
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- extension = await response.parse()
- assert_matches_type(ExtensionCreateResponse, extension, path=["response"])
+ extension = response.parse()
+ assert_matches_type(ExtensionUploadResponse, extension, path=["response"])
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
- async def test_streaming_response_create(self, async_client: AsyncKernel) -> None:
- async with async_client.extensions.with_streaming_response.create(
+ def test_streaming_response_upload(self, client: Kernel) -> None:
+ with client.extensions.with_streaming_response.upload(
file=b"raw file contents",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- extension = await response.parse()
- assert_matches_type(ExtensionCreateResponse, extension, path=["response"])
+ extension = response.parse()
+ assert_matches_type(ExtensionUploadResponse, extension, path=["response"])
assert cast(Any, response.is_closed) is True
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_method_retrieve(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
- respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- extension = await async_client.extensions.retrieve(
- "id_or_name",
- )
- assert extension.is_closed
- assert await extension.json() == {"foo": "bar"}
- assert cast(Any, extension.is_closed) is True
- assert isinstance(extension, AsyncBinaryAPIResponse)
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_raw_response_retrieve(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
- respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
-
- extension = await async_client.extensions.with_raw_response.retrieve(
- "id_or_name",
- )
-
- assert extension.is_closed is True
- assert extension.http_request.headers.get("X-Stainless-Lang") == "python"
- assert await extension.json() == {"foo": "bar"}
- assert isinstance(extension, AsyncBinaryAPIResponse)
-
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_streaming_response_retrieve(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
- respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- async with async_client.extensions.with_streaming_response.retrieve(
- "id_or_name",
- ) as extension:
- assert not extension.is_closed
- assert extension.http_request.headers.get("X-Stainless-Lang") == "python"
-
- assert await extension.json() == {"foo": "bar"}
- assert cast(Any, extension.is_closed) is True
- assert isinstance(extension, AsyncStreamedBinaryAPIResponse)
-
- assert cast(Any, extension.is_closed) is True
-
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_path_params_retrieve(self, async_client: AsyncKernel) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
- await async_client.extensions.with_raw_response.retrieve(
- "",
- )
+class TestAsyncExtensions:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
@@ -415,6 +322,56 @@ async def test_path_params_delete(self, async_client: AsyncKernel) -> None:
"",
)
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_method_download(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ extension = await async_client.extensions.download(
+ "id_or_name",
+ )
+ assert extension.is_closed
+ assert await extension.json() == {"foo": "bar"}
+ assert cast(Any, extension.is_closed) is True
+ assert isinstance(extension, AsyncBinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_raw_response_download(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+
+ extension = await async_client.extensions.with_raw_response.download(
+ "id_or_name",
+ )
+
+ assert extension.is_closed is True
+ assert extension.http_request.headers.get("X-Stainless-Lang") == "python"
+ assert await extension.json() == {"foo": "bar"}
+ assert isinstance(extension, AsyncBinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_streaming_response_download(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/extensions/id_or_name").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ async with async_client.extensions.with_streaming_response.download(
+ "id_or_name",
+ ) as extension:
+ assert not extension.is_closed
+ assert extension.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ assert await extension.json() == {"foo": "bar"}
+ assert cast(Any, extension.is_closed) is True
+ assert isinstance(extension, AsyncStreamedBinaryAPIResponse)
+
+ assert cast(Any, extension.is_closed) is True
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_path_params_download(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"):
+ await async_client.extensions.with_raw_response.download(
+ "",
+ )
+
@parametrize
@pytest.mark.respx(base_url=base_url)
async def test_method_download_from_chrome_store(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
@@ -475,3 +432,46 @@ async def test_streaming_response_download_from_chrome_store(
assert isinstance(extension, AsyncStreamedBinaryAPIResponse)
assert cast(Any, extension.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_upload(self, async_client: AsyncKernel) -> None:
+ extension = await async_client.extensions.upload(
+ file=b"raw file contents",
+ )
+ assert_matches_type(ExtensionUploadResponse, extension, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_upload_with_all_params(self, async_client: AsyncKernel) -> None:
+ extension = await async_client.extensions.upload(
+ file=b"raw file contents",
+ name="name",
+ )
+ assert_matches_type(ExtensionUploadResponse, extension, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_upload(self, async_client: AsyncKernel) -> None:
+ response = await async_client.extensions.with_raw_response.upload(
+ file=b"raw file contents",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ extension = await response.parse()
+ assert_matches_type(ExtensionUploadResponse, extension, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_upload(self, async_client: AsyncKernel) -> None:
+ async with async_client.extensions.with_streaming_response.upload(
+ file=b"raw file contents",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ extension = await response.parse()
+ assert_matches_type(ExtensionUploadResponse, extension, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
From 300c640a8339d84965f0719438b972b9aa55ec61 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 17 Oct 2025 18:33:46 +0000
Subject: [PATCH 3/3] release: 0.15.0
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 9 +++++++++
pyproject.toml | 2 +-
src/kernel/_version.py | 2 +-
4 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 3d26904f..8f3e0a49 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.14.2"
+ ".": "0.15.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 92e1ed7b..e9fc2228 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
# Changelog
+## 0.15.0 (2025-10-17)
+
+Full Changelog: [v0.14.2...v0.15.0](https://github.com/onkernel/kernel-python-sdk/compare/v0.14.2...v0.15.0)
+
+### Features
+
+* click mouse, move mouse, screenshot ([b63f7ad](https://github.com/onkernel/kernel-python-sdk/commit/b63f7adc7cf0da614a4fc3a9eed491f71f7ec742))
+* Phani/deploy with GitHub url ([eb86c27](https://github.com/onkernel/kernel-python-sdk/commit/eb86c27107781172bf740e0af3833eee86b7cb60))
+
## 0.14.2 (2025-10-16)
Full Changelog: [v0.14.1...v0.14.2](https://github.com/onkernel/kernel-python-sdk/compare/v0.14.1...v0.14.2)
diff --git a/pyproject.toml b/pyproject.toml
index c2e14c5d..0f8cb140 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "kernel"
-version = "0.14.2"
+version = "0.15.0"
description = "The official Python library for the kernel API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/kernel/_version.py b/src/kernel/_version.py
index dfcce591..e65ca7f2 100644
--- a/src/kernel/_version.py
+++ b/src/kernel/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "kernel"
-__version__ = "0.14.2" # x-release-please-version
+__version__ = "0.15.0" # x-release-please-version