diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1692944e..795bdad6 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -36,13 +36,13 @@ jobs:
run: ./scripts/lint
build:
- if: github.repository == 'stainless-sdks/kernel-python' && (github.event_name == 'push' || github.event.pull_request.head.repo.fork)
+ if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
timeout-minutes: 10
name: build
permissions:
contents: read
id-token: write
- runs-on: depot-ubuntu-24.04
+ runs-on: ${{ github.repository == 'stainless-sdks/kernel-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@v4
@@ -61,12 +61,14 @@ jobs:
run: rye build
- name: Get GitHub OIDC Token
+ if: github.repository == 'stainless-sdks/kernel-python'
id: github-oidc
uses: actions/github-script@v6
with:
script: core.setOutput('github_token', await core.getIDToken());
- name: Upload tarball
+ if: github.repository == 'stainless-sdks/kernel-python'
env:
URL: https://pkg.stainless.com/s
AUTH: ${{ steps.github-oidc.outputs.github_token }}
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 05988747..091cfb12 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.9.1"
+ ".": "0.10.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index f6de080e..b791078d 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 31
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-b55c3e0424fa7733487139488b9fff00ad8949ff02ee3160ee36b9334e84b134.yml
-openapi_spec_hash: 17f36677e3dc0a3aeb419654c8d5cae3
-config_hash: f67e4b33b2fb30c1405ee2fff8096320
+configured_endpoints: 41
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-a7c1df5070fe59642d7a1f168aa902a468227752bfc930cbf38930f7c205dbb6.yml
+openapi_spec_hash: eab65e39aef4f0a0952b82adeecf6b5b
+config_hash: 5de78bc29ac060562575cb54bb26826c
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d78a949e..508c4207 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,25 @@
# Changelog
+## 0.10.0 (2025-08-27)
+
+Full Changelog: [v0.9.1...v0.10.0](https://github.com/onkernel/kernel-python-sdk/compare/v0.9.1...v0.10.0)
+
+### Features
+
+* **api:** new process, fs, and log endpoints ([48a39b4](https://github.com/onkernel/kernel-python-sdk/commit/48a39b4cc1ab32b4375ad4e33e5f9e4349502072))
+
+
+### Bug Fixes
+
+* avoid newer type syntax ([9ec7c40](https://github.com/onkernel/kernel-python-sdk/commit/9ec7c40b34b264709b904f36e309624bd1161413))
+
+
+### Chores
+
+* **internal:** change ci workflow machines ([3a2969d](https://github.com/onkernel/kernel-python-sdk/commit/3a2969d035b9e0bb4fa39dc27de2db6d4edad6dd))
+* **internal:** update pyright exclude list ([39439aa](https://github.com/onkernel/kernel-python-sdk/commit/39439aaad72c92aa9f4bb74ac055b929c93b6060))
+* update github action ([fff64d0](https://github.com/onkernel/kernel-python-sdk/commit/fff64d001d2c759967f08e7a1932e1fb7d84b126))
+
## 0.9.1 (2025-08-15)
Full Changelog: [v0.9.0...v0.9.1](https://github.com/onkernel/kernel-python-sdk/compare/v0.9.0...v0.9.1)
diff --git a/api.md b/api.md
index f03855dc..7e07a7b0 100644
--- a/api.md
+++ b/api.md
@@ -108,11 +108,14 @@ Methods:
- client.browsers.fs.create_directory(id, \*\*params) -> None
- client.browsers.fs.delete_directory(id, \*\*params) -> None
- client.browsers.fs.delete_file(id, \*\*params) -> None
+- client.browsers.fs.download_dir_zip(id, \*\*params) -> BinaryAPIResponse
- client.browsers.fs.file_info(id, \*\*params) -> FFileInfoResponse
- client.browsers.fs.list_files(id, \*\*params) -> FListFilesResponse
- client.browsers.fs.move(id, \*\*params) -> None
- client.browsers.fs.read_file(id, \*\*params) -> BinaryAPIResponse
- client.browsers.fs.set_file_permissions(id, \*\*params) -> None
+- client.browsers.fs.upload(id, \*\*params) -> None
+- client.browsers.fs.upload_zip(id, \*\*params) -> None
- client.browsers.fs.write_file(id, contents, \*\*params) -> None
### Watch
@@ -128,3 +131,33 @@ Methods:
- client.browsers.fs.watch.events(watch_id, \*, id) -> WatchEventsResponse
- client.browsers.fs.watch.start(id, \*\*params) -> WatchStartResponse
- client.browsers.fs.watch.stop(watch_id, \*, id) -> None
+
+## Process
+
+Types:
+
+```python
+from kernel.types.browsers import (
+ ProcessExecResponse,
+ ProcessKillResponse,
+ ProcessSpawnResponse,
+ ProcessStatusResponse,
+ ProcessStdinResponse,
+ ProcessStdoutStreamResponse,
+)
+```
+
+Methods:
+
+- client.browsers.process.exec(id, \*\*params) -> ProcessExecResponse
+- client.browsers.process.kill(process_id, \*, id, \*\*params) -> ProcessKillResponse
+- client.browsers.process.spawn(id, \*\*params) -> ProcessSpawnResponse
+- client.browsers.process.status(process_id, \*, id) -> ProcessStatusResponse
+- client.browsers.process.stdin(process_id, \*, id, \*\*params) -> ProcessStdinResponse
+- client.browsers.process.stdout_stream(process_id, \*, id) -> ProcessStdoutStreamResponse
+
+## Logs
+
+Methods:
+
+- client.browsers.logs.stream(id, \*\*params) -> LogEvent
diff --git a/pyproject.toml b/pyproject.toml
index ddb4fbb1..3cc0fb34 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "kernel"
-version = "0.9.1"
+version = "0.10.0"
description = "The official Python library for the kernel API"
dynamic = ["readme"]
license = "Apache-2.0"
@@ -148,6 +148,7 @@ exclude = [
"_dev",
".venv",
".nox",
+ ".git",
]
reportImplicitOverride = true
diff --git a/src/kernel/_models.py b/src/kernel/_models.py
index b8387ce9..92f7c10b 100644
--- a/src/kernel/_models.py
+++ b/src/kernel/_models.py
@@ -304,7 +304,7 @@ def model_dump(
exclude_none=exclude_none,
)
- return cast(dict[str, Any], json_safe(dumped)) if mode == "json" else dumped
+ return cast("dict[str, Any]", json_safe(dumped)) if mode == "json" else dumped
@override
def model_dump_json(
diff --git a/src/kernel/_version.py b/src/kernel/_version.py
index de748b73..db4afd4b 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.9.1" # x-release-please-version
+__version__ = "0.10.0" # x-release-please-version
diff --git a/src/kernel/resources/browsers/__init__.py b/src/kernel/resources/browsers/__init__.py
index 41452e9d..97c987e4 100644
--- a/src/kernel/resources/browsers/__init__.py
+++ b/src/kernel/resources/browsers/__init__.py
@@ -8,6 +8,22 @@
FsResourceWithStreamingResponse,
AsyncFsResourceWithStreamingResponse,
)
+from .logs import (
+ LogsResource,
+ AsyncLogsResource,
+ LogsResourceWithRawResponse,
+ AsyncLogsResourceWithRawResponse,
+ LogsResourceWithStreamingResponse,
+ AsyncLogsResourceWithStreamingResponse,
+)
+from .process import (
+ ProcessResource,
+ AsyncProcessResource,
+ ProcessResourceWithRawResponse,
+ AsyncProcessResourceWithRawResponse,
+ ProcessResourceWithStreamingResponse,
+ AsyncProcessResourceWithStreamingResponse,
+)
from .replays import (
ReplaysResource,
AsyncReplaysResource,
@@ -38,6 +54,18 @@
"AsyncFsResourceWithRawResponse",
"FsResourceWithStreamingResponse",
"AsyncFsResourceWithStreamingResponse",
+ "ProcessResource",
+ "AsyncProcessResource",
+ "ProcessResourceWithRawResponse",
+ "AsyncProcessResourceWithRawResponse",
+ "ProcessResourceWithStreamingResponse",
+ "AsyncProcessResourceWithStreamingResponse",
+ "LogsResource",
+ "AsyncLogsResource",
+ "LogsResourceWithRawResponse",
+ "AsyncLogsResourceWithRawResponse",
+ "LogsResourceWithStreamingResponse",
+ "AsyncLogsResourceWithStreamingResponse",
"BrowsersResource",
"AsyncBrowsersResource",
"BrowsersResourceWithRawResponse",
diff --git a/src/kernel/resources/browsers/browsers.py b/src/kernel/resources/browsers/browsers.py
index 559e0948..80afc60d 100644
--- a/src/kernel/resources/browsers/browsers.py
+++ b/src/kernel/resources/browsers/browsers.py
@@ -4,6 +4,14 @@
import httpx
+from .logs import (
+ LogsResource,
+ AsyncLogsResource,
+ LogsResourceWithRawResponse,
+ AsyncLogsResourceWithRawResponse,
+ LogsResourceWithStreamingResponse,
+ AsyncLogsResourceWithStreamingResponse,
+)
from .fs.fs import (
FsResource,
AsyncFsResource,
@@ -13,6 +21,14 @@
AsyncFsResourceWithStreamingResponse,
)
from ...types import browser_create_params, browser_delete_params
+from .process import (
+ ProcessResource,
+ AsyncProcessResource,
+ ProcessResourceWithRawResponse,
+ AsyncProcessResourceWithRawResponse,
+ ProcessResourceWithStreamingResponse,
+ AsyncProcessResourceWithStreamingResponse,
+)
from .replays import (
ReplaysResource,
AsyncReplaysResource,
@@ -49,6 +65,14 @@ def replays(self) -> ReplaysResource:
def fs(self) -> FsResource:
return FsResource(self._client)
+ @cached_property
+ def process(self) -> ProcessResource:
+ return ProcessResource(self._client)
+
+ @cached_property
+ def logs(self) -> LogsResource:
+ return LogsResource(self._client)
+
@cached_property
def with_raw_response(self) -> BrowsersResourceWithRawResponse:
"""
@@ -261,6 +285,14 @@ def replays(self) -> AsyncReplaysResource:
def fs(self) -> AsyncFsResource:
return AsyncFsResource(self._client)
+ @cached_property
+ def process(self) -> AsyncProcessResource:
+ return AsyncProcessResource(self._client)
+
+ @cached_property
+ def logs(self) -> AsyncLogsResource:
+ return AsyncLogsResource(self._client)
+
@cached_property
def with_raw_response(self) -> AsyncBrowsersResourceWithRawResponse:
"""
@@ -494,6 +526,14 @@ def replays(self) -> ReplaysResourceWithRawResponse:
def fs(self) -> FsResourceWithRawResponse:
return FsResourceWithRawResponse(self._browsers.fs)
+ @cached_property
+ def process(self) -> ProcessResourceWithRawResponse:
+ return ProcessResourceWithRawResponse(self._browsers.process)
+
+ @cached_property
+ def logs(self) -> LogsResourceWithRawResponse:
+ return LogsResourceWithRawResponse(self._browsers.logs)
+
class AsyncBrowsersResourceWithRawResponse:
def __init__(self, browsers: AsyncBrowsersResource) -> None:
@@ -523,6 +563,14 @@ def replays(self) -> AsyncReplaysResourceWithRawResponse:
def fs(self) -> AsyncFsResourceWithRawResponse:
return AsyncFsResourceWithRawResponse(self._browsers.fs)
+ @cached_property
+ def process(self) -> AsyncProcessResourceWithRawResponse:
+ return AsyncProcessResourceWithRawResponse(self._browsers.process)
+
+ @cached_property
+ def logs(self) -> AsyncLogsResourceWithRawResponse:
+ return AsyncLogsResourceWithRawResponse(self._browsers.logs)
+
class BrowsersResourceWithStreamingResponse:
def __init__(self, browsers: BrowsersResource) -> None:
@@ -552,6 +600,14 @@ def replays(self) -> ReplaysResourceWithStreamingResponse:
def fs(self) -> FsResourceWithStreamingResponse:
return FsResourceWithStreamingResponse(self._browsers.fs)
+ @cached_property
+ def process(self) -> ProcessResourceWithStreamingResponse:
+ return ProcessResourceWithStreamingResponse(self._browsers.process)
+
+ @cached_property
+ def logs(self) -> LogsResourceWithStreamingResponse:
+ return LogsResourceWithStreamingResponse(self._browsers.logs)
+
class AsyncBrowsersResourceWithStreamingResponse:
def __init__(self, browsers: AsyncBrowsersResource) -> None:
@@ -580,3 +636,11 @@ def replays(self) -> AsyncReplaysResourceWithStreamingResponse:
@cached_property
def fs(self) -> AsyncFsResourceWithStreamingResponse:
return AsyncFsResourceWithStreamingResponse(self._browsers.fs)
+
+ @cached_property
+ def process(self) -> AsyncProcessResourceWithStreamingResponse:
+ return AsyncProcessResourceWithStreamingResponse(self._browsers.process)
+
+ @cached_property
+ def logs(self) -> AsyncLogsResourceWithStreamingResponse:
+ return AsyncLogsResourceWithStreamingResponse(self._browsers.logs)
diff --git a/src/kernel/resources/browsers/fs/fs.py b/src/kernel/resources/browsers/fs/fs.py
index 3563c7cb..cb7b3010 100644
--- a/src/kernel/resources/browsers/fs/fs.py
+++ b/src/kernel/resources/browsers/fs/fs.py
@@ -2,6 +2,8 @@
from __future__ import annotations
+from typing import Mapping, Iterable, cast
+
import httpx
from .watch import (
@@ -13,8 +15,8 @@
AsyncWatchResourceWithStreamingResponse,
)
from ...._files import read_file_content, async_read_file_content
-from ...._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven, FileContent
-from ...._utils import maybe_transform, async_maybe_transform
+from ...._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven, FileTypes, FileContent
+from ...._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform
from ...._compat import cached_property
from ...._resource import SyncAPIResource, AsyncAPIResource
from ...._response import (
@@ -34,13 +36,16 @@
from ...._base_client import make_request_options
from ....types.browsers import (
f_move_params,
+ f_upload_params,
f_file_info_params,
f_read_file_params,
f_list_files_params,
+ f_upload_zip_params,
f_write_file_params,
f_delete_file_params,
f_create_directory_params,
f_delete_directory_params,
+ f_download_dir_zip_params,
f_set_file_permissions_params,
)
from ....types.browsers.f_file_info_response import FFileInfoResponse
@@ -196,6 +201,47 @@ def delete_file(
cast_to=NoneType,
)
+ def download_dir_zip(
+ self,
+ id: str,
+ *,
+ path: 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,
+ ) -> BinaryAPIResponse:
+ """
+ Returns a ZIP file containing the contents of the specified directory.
+
+ Args:
+ path: Absolute directory path to archive and download.
+
+ 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": "application/zip", **(extra_headers or {})}
+ return self._get(
+ f"/browsers/{id}/fs/download_dir_zip",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"path": path}, f_download_dir_zip_params.FDownloadDirZipParams),
+ ),
+ cast_to=BinaryAPIResponse,
+ )
+
def file_info(
self,
id: str,
@@ -419,6 +465,100 @@ def set_file_permissions(
cast_to=NoneType,
)
+ def upload(
+ self,
+ id: str,
+ *,
+ files: Iterable[f_upload_params.File],
+ # 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:
+ """
+ Allows uploading single or multiple files to the remote filesystem.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ body = deepcopy_minimal({"files": files})
+ extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", "", "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"
+ return self._post(
+ f"/browsers/{id}/fs/upload",
+ body=maybe_transform(body, f_upload_params.FUploadParams),
+ files=extracted_files,
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def upload_zip(
+ self,
+ id: str,
+ *,
+ dest_path: str,
+ zip_file: FileTypes,
+ # 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:
+ """
+ Upload a zip file and extract its contents to the specified destination path.
+
+ Args:
+ dest_path: Absolute destination directory to extract the archive to.
+
+ 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 {})}
+ body = deepcopy_minimal(
+ {
+ "dest_path": dest_path,
+ "zip_file": zip_file,
+ }
+ )
+ files = extract_files(cast(Mapping[str, object], body), paths=[["zip_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"
+ return self._post(
+ f"/browsers/{id}/fs/upload_zip",
+ body=maybe_transform(body, f_upload_zip_params.FUploadZipParams),
+ files=files,
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
def write_file(
self,
id: str,
@@ -620,6 +760,47 @@ async def delete_file(
cast_to=NoneType,
)
+ async def download_dir_zip(
+ self,
+ id: str,
+ *,
+ path: 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:
+ """
+ Returns a ZIP file containing the contents of the specified directory.
+
+ Args:
+ path: Absolute directory path to archive and download.
+
+ 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": "application/zip", **(extra_headers or {})}
+ return await self._get(
+ f"/browsers/{id}/fs/download_dir_zip",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"path": path}, f_download_dir_zip_params.FDownloadDirZipParams),
+ ),
+ cast_to=AsyncBinaryAPIResponse,
+ )
+
async def file_info(
self,
id: str,
@@ -843,6 +1024,100 @@ async def set_file_permissions(
cast_to=NoneType,
)
+ async def upload(
+ self,
+ id: str,
+ *,
+ files: Iterable[f_upload_params.File],
+ # 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:
+ """
+ Allows uploading single or multiple files to the remote filesystem.
+
+ Args:
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ body = deepcopy_minimal({"files": files})
+ extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", "", "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"
+ return await self._post(
+ f"/browsers/{id}/fs/upload",
+ body=await async_maybe_transform(body, f_upload_params.FUploadParams),
+ files=extracted_files,
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ async def upload_zip(
+ self,
+ id: str,
+ *,
+ dest_path: str,
+ zip_file: FileTypes,
+ # 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:
+ """
+ Upload a zip file and extract its contents to the specified destination path.
+
+ Args:
+ dest_path: Absolute destination directory to extract the archive to.
+
+ 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 {})}
+ body = deepcopy_minimal(
+ {
+ "dest_path": dest_path,
+ "zip_file": zip_file,
+ }
+ )
+ files = extract_files(cast(Mapping[str, object], body), paths=[["zip_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"
+ return await self._post(
+ f"/browsers/{id}/fs/upload_zip",
+ body=await async_maybe_transform(body, f_upload_zip_params.FUploadZipParams),
+ files=files,
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
async def write_file(
self,
id: str,
@@ -910,6 +1185,10 @@ def __init__(self, fs: FsResource) -> None:
self.delete_file = to_raw_response_wrapper(
fs.delete_file,
)
+ self.download_dir_zip = to_custom_raw_response_wrapper(
+ fs.download_dir_zip,
+ BinaryAPIResponse,
+ )
self.file_info = to_raw_response_wrapper(
fs.file_info,
)
@@ -926,6 +1205,12 @@ def __init__(self, fs: FsResource) -> None:
self.set_file_permissions = to_raw_response_wrapper(
fs.set_file_permissions,
)
+ self.upload = to_raw_response_wrapper(
+ fs.upload,
+ )
+ self.upload_zip = to_raw_response_wrapper(
+ fs.upload_zip,
+ )
self.write_file = to_raw_response_wrapper(
fs.write_file,
)
@@ -948,6 +1233,10 @@ def __init__(self, fs: AsyncFsResource) -> None:
self.delete_file = async_to_raw_response_wrapper(
fs.delete_file,
)
+ self.download_dir_zip = async_to_custom_raw_response_wrapper(
+ fs.download_dir_zip,
+ AsyncBinaryAPIResponse,
+ )
self.file_info = async_to_raw_response_wrapper(
fs.file_info,
)
@@ -964,6 +1253,12 @@ def __init__(self, fs: AsyncFsResource) -> None:
self.set_file_permissions = async_to_raw_response_wrapper(
fs.set_file_permissions,
)
+ self.upload = async_to_raw_response_wrapper(
+ fs.upload,
+ )
+ self.upload_zip = async_to_raw_response_wrapper(
+ fs.upload_zip,
+ )
self.write_file = async_to_raw_response_wrapper(
fs.write_file,
)
@@ -986,6 +1281,10 @@ def __init__(self, fs: FsResource) -> None:
self.delete_file = to_streamed_response_wrapper(
fs.delete_file,
)
+ self.download_dir_zip = to_custom_streamed_response_wrapper(
+ fs.download_dir_zip,
+ StreamedBinaryAPIResponse,
+ )
self.file_info = to_streamed_response_wrapper(
fs.file_info,
)
@@ -1002,6 +1301,12 @@ def __init__(self, fs: FsResource) -> None:
self.set_file_permissions = to_streamed_response_wrapper(
fs.set_file_permissions,
)
+ self.upload = to_streamed_response_wrapper(
+ fs.upload,
+ )
+ self.upload_zip = to_streamed_response_wrapper(
+ fs.upload_zip,
+ )
self.write_file = to_streamed_response_wrapper(
fs.write_file,
)
@@ -1024,6 +1329,10 @@ def __init__(self, fs: AsyncFsResource) -> None:
self.delete_file = async_to_streamed_response_wrapper(
fs.delete_file,
)
+ self.download_dir_zip = async_to_custom_streamed_response_wrapper(
+ fs.download_dir_zip,
+ AsyncStreamedBinaryAPIResponse,
+ )
self.file_info = async_to_streamed_response_wrapper(
fs.file_info,
)
@@ -1040,6 +1349,12 @@ def __init__(self, fs: AsyncFsResource) -> None:
self.set_file_permissions = async_to_streamed_response_wrapper(
fs.set_file_permissions,
)
+ self.upload = async_to_streamed_response_wrapper(
+ fs.upload,
+ )
+ self.upload_zip = async_to_streamed_response_wrapper(
+ fs.upload_zip,
+ )
self.write_file = async_to_streamed_response_wrapper(
fs.write_file,
)
diff --git a/src/kernel/resources/browsers/logs.py b/src/kernel/resources/browsers/logs.py
new file mode 100644
index 00000000..fbbe14a5
--- /dev/null
+++ b/src/kernel/resources/browsers/logs.py
@@ -0,0 +1,214 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal
+
+import httpx
+
+from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from ..._utils import maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..._streaming import Stream, AsyncStream
+from ..._base_client import make_request_options
+from ...types.browsers import log_stream_params
+from ...types.shared.log_event import LogEvent
+
+__all__ = ["LogsResource", "AsyncLogsResource"]
+
+
+class LogsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> LogsResourceWithRawResponse:
+ """
+ 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 LogsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> LogsResourceWithStreamingResponse:
+ """
+ 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 LogsResourceWithStreamingResponse(self)
+
+ def stream(
+ self,
+ id: str,
+ *,
+ source: Literal["path", "supervisor"],
+ follow: bool | NotGiven = NOT_GIVEN,
+ path: str | NotGiven = NOT_GIVEN,
+ supervisor_process: str | NotGiven = NOT_GIVEN,
+ # 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,
+ ) -> Stream[LogEvent]:
+ """
+ Stream log files on the browser instance via SSE
+
+ Args:
+ path: only required if source is path
+
+ supervisor_process: only required if source is supervisor
+
+ 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": "text/event-stream", **(extra_headers or {})}
+ return self._get(
+ f"/browsers/{id}/logs/stream",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "source": source,
+ "follow": follow,
+ "path": path,
+ "supervisor_process": supervisor_process,
+ },
+ log_stream_params.LogStreamParams,
+ ),
+ ),
+ cast_to=LogEvent,
+ stream=True,
+ stream_cls=Stream[LogEvent],
+ )
+
+
+class AsyncLogsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncLogsResourceWithRawResponse:
+ """
+ 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 AsyncLogsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncLogsResourceWithStreamingResponse:
+ """
+ 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 AsyncLogsResourceWithStreamingResponse(self)
+
+ async def stream(
+ self,
+ id: str,
+ *,
+ source: Literal["path", "supervisor"],
+ follow: bool | NotGiven = NOT_GIVEN,
+ path: str | NotGiven = NOT_GIVEN,
+ supervisor_process: str | NotGiven = NOT_GIVEN,
+ # 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,
+ ) -> AsyncStream[LogEvent]:
+ """
+ Stream log files on the browser instance via SSE
+
+ Args:
+ path: only required if source is path
+
+ supervisor_process: only required if source is supervisor
+
+ 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": "text/event-stream", **(extra_headers or {})}
+ return await self._get(
+ f"/browsers/{id}/logs/stream",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {
+ "source": source,
+ "follow": follow,
+ "path": path,
+ "supervisor_process": supervisor_process,
+ },
+ log_stream_params.LogStreamParams,
+ ),
+ ),
+ cast_to=LogEvent,
+ stream=True,
+ stream_cls=AsyncStream[LogEvent],
+ )
+
+
+class LogsResourceWithRawResponse:
+ def __init__(self, logs: LogsResource) -> None:
+ self._logs = logs
+
+ self.stream = to_raw_response_wrapper(
+ logs.stream,
+ )
+
+
+class AsyncLogsResourceWithRawResponse:
+ def __init__(self, logs: AsyncLogsResource) -> None:
+ self._logs = logs
+
+ self.stream = async_to_raw_response_wrapper(
+ logs.stream,
+ )
+
+
+class LogsResourceWithStreamingResponse:
+ def __init__(self, logs: LogsResource) -> None:
+ self._logs = logs
+
+ self.stream = to_streamed_response_wrapper(
+ logs.stream,
+ )
+
+
+class AsyncLogsResourceWithStreamingResponse:
+ def __init__(self, logs: AsyncLogsResource) -> None:
+ self._logs = logs
+
+ self.stream = async_to_streamed_response_wrapper(
+ logs.stream,
+ )
diff --git a/src/kernel/resources/browsers/process.py b/src/kernel/resources/browsers/process.py
new file mode 100644
index 00000000..d3f5eca5
--- /dev/null
+++ b/src/kernel/resources/browsers/process.py
@@ -0,0 +1,742 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, List, Optional
+from typing_extensions import Literal
+
+import httpx
+
+from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from ..._utils import maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..._streaming import Stream, AsyncStream
+from ..._base_client import make_request_options
+from ...types.browsers import process_exec_params, process_kill_params, process_spawn_params, process_stdin_params
+from ...types.browsers.process_exec_response import ProcessExecResponse
+from ...types.browsers.process_kill_response import ProcessKillResponse
+from ...types.browsers.process_spawn_response import ProcessSpawnResponse
+from ...types.browsers.process_stdin_response import ProcessStdinResponse
+from ...types.browsers.process_status_response import ProcessStatusResponse
+from ...types.browsers.process_stdout_stream_response import ProcessStdoutStreamResponse
+
+__all__ = ["ProcessResource", "AsyncProcessResource"]
+
+
+class ProcessResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> ProcessResourceWithRawResponse:
+ """
+ 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 ProcessResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> ProcessResourceWithStreamingResponse:
+ """
+ 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 ProcessResourceWithStreamingResponse(self)
+
+ def exec(
+ self,
+ id: str,
+ *,
+ command: str,
+ args: List[str] | NotGiven = NOT_GIVEN,
+ as_root: bool | NotGiven = NOT_GIVEN,
+ as_user: Optional[str] | NotGiven = NOT_GIVEN,
+ cwd: Optional[str] | NotGiven = NOT_GIVEN,
+ env: Dict[str, str] | NotGiven = NOT_GIVEN,
+ timeout_sec: Optional[int] | NotGiven = NOT_GIVEN,
+ # 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,
+ ) -> ProcessExecResponse:
+ """
+ Execute a command synchronously
+
+ Args:
+ command: Executable or shell command to run.
+
+ args: Command arguments.
+
+ as_root: Run the process with root privileges.
+
+ as_user: Run the process as this user.
+
+ cwd: Working directory (absolute path) to run the command in.
+
+ env: Environment variables to set for the process.
+
+ timeout_sec: Maximum execution time in seconds.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._post(
+ f"/browsers/{id}/process/exec",
+ body=maybe_transform(
+ {
+ "command": command,
+ "args": args,
+ "as_root": as_root,
+ "as_user": as_user,
+ "cwd": cwd,
+ "env": env,
+ "timeout_sec": timeout_sec,
+ },
+ process_exec_params.ProcessExecParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProcessExecResponse,
+ )
+
+ def kill(
+ self,
+ process_id: str,
+ *,
+ id: str,
+ signal: Literal["TERM", "KILL", "INT", "HUP"],
+ # 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,
+ ) -> ProcessKillResponse:
+ """
+ Send signal to process
+
+ Args:
+ signal: Signal to send.
+
+ 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}")
+ if not process_id:
+ raise ValueError(f"Expected a non-empty value for `process_id` but received {process_id!r}")
+ return self._post(
+ f"/browsers/{id}/process/{process_id}/kill",
+ body=maybe_transform({"signal": signal}, process_kill_params.ProcessKillParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProcessKillResponse,
+ )
+
+ def spawn(
+ self,
+ id: str,
+ *,
+ command: str,
+ args: List[str] | NotGiven = NOT_GIVEN,
+ as_root: bool | NotGiven = NOT_GIVEN,
+ as_user: Optional[str] | NotGiven = NOT_GIVEN,
+ cwd: Optional[str] | NotGiven = NOT_GIVEN,
+ env: Dict[str, str] | NotGiven = NOT_GIVEN,
+ timeout_sec: Optional[int] | NotGiven = NOT_GIVEN,
+ # 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,
+ ) -> ProcessSpawnResponse:
+ """
+ Execute a command asynchronously
+
+ Args:
+ command: Executable or shell command to run.
+
+ args: Command arguments.
+
+ as_root: Run the process with root privileges.
+
+ as_user: Run the process as this user.
+
+ cwd: Working directory (absolute path) to run the command in.
+
+ env: Environment variables to set for the process.
+
+ timeout_sec: Maximum execution time in seconds.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._post(
+ f"/browsers/{id}/process/spawn",
+ body=maybe_transform(
+ {
+ "command": command,
+ "args": args,
+ "as_root": as_root,
+ "as_user": as_user,
+ "cwd": cwd,
+ "env": env,
+ "timeout_sec": timeout_sec,
+ },
+ process_spawn_params.ProcessSpawnParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProcessSpawnResponse,
+ )
+
+ def status(
+ self,
+ process_id: str,
+ *,
+ id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> ProcessStatusResponse:
+ """
+ Get process status
+
+ 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}")
+ if not process_id:
+ raise ValueError(f"Expected a non-empty value for `process_id` but received {process_id!r}")
+ return self._get(
+ f"/browsers/{id}/process/{process_id}/status",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProcessStatusResponse,
+ )
+
+ def stdin(
+ self,
+ process_id: str,
+ *,
+ id: str,
+ data_b64: 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,
+ ) -> ProcessStdinResponse:
+ """
+ Write to process stdin
+
+ Args:
+ data_b64: Base64-encoded data to write.
+
+ 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}")
+ if not process_id:
+ raise ValueError(f"Expected a non-empty value for `process_id` but received {process_id!r}")
+ return self._post(
+ f"/browsers/{id}/process/{process_id}/stdin",
+ body=maybe_transform({"data_b64": data_b64}, process_stdin_params.ProcessStdinParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProcessStdinResponse,
+ )
+
+ def stdout_stream(
+ self,
+ process_id: str,
+ *,
+ id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> Stream[ProcessStdoutStreamResponse]:
+ """
+ Stream process stdout via SSE
+
+ 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}")
+ if not process_id:
+ raise ValueError(f"Expected a non-empty value for `process_id` but received {process_id!r}")
+ extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})}
+ return self._get(
+ f"/browsers/{id}/process/{process_id}/stdout/stream",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProcessStdoutStreamResponse,
+ stream=True,
+ stream_cls=Stream[ProcessStdoutStreamResponse],
+ )
+
+
+class AsyncProcessResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncProcessResourceWithRawResponse:
+ """
+ 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 AsyncProcessResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncProcessResourceWithStreamingResponse:
+ """
+ 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 AsyncProcessResourceWithStreamingResponse(self)
+
+ async def exec(
+ self,
+ id: str,
+ *,
+ command: str,
+ args: List[str] | NotGiven = NOT_GIVEN,
+ as_root: bool | NotGiven = NOT_GIVEN,
+ as_user: Optional[str] | NotGiven = NOT_GIVEN,
+ cwd: Optional[str] | NotGiven = NOT_GIVEN,
+ env: Dict[str, str] | NotGiven = NOT_GIVEN,
+ timeout_sec: Optional[int] | NotGiven = NOT_GIVEN,
+ # 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,
+ ) -> ProcessExecResponse:
+ """
+ Execute a command synchronously
+
+ Args:
+ command: Executable or shell command to run.
+
+ args: Command arguments.
+
+ as_root: Run the process with root privileges.
+
+ as_user: Run the process as this user.
+
+ cwd: Working directory (absolute path) to run the command in.
+
+ env: Environment variables to set for the process.
+
+ timeout_sec: Maximum execution time in seconds.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._post(
+ f"/browsers/{id}/process/exec",
+ body=await async_maybe_transform(
+ {
+ "command": command,
+ "args": args,
+ "as_root": as_root,
+ "as_user": as_user,
+ "cwd": cwd,
+ "env": env,
+ "timeout_sec": timeout_sec,
+ },
+ process_exec_params.ProcessExecParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProcessExecResponse,
+ )
+
+ async def kill(
+ self,
+ process_id: str,
+ *,
+ id: str,
+ signal: Literal["TERM", "KILL", "INT", "HUP"],
+ # 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,
+ ) -> ProcessKillResponse:
+ """
+ Send signal to process
+
+ Args:
+ signal: Signal to send.
+
+ 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}")
+ if not process_id:
+ raise ValueError(f"Expected a non-empty value for `process_id` but received {process_id!r}")
+ return await self._post(
+ f"/browsers/{id}/process/{process_id}/kill",
+ body=await async_maybe_transform({"signal": signal}, process_kill_params.ProcessKillParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProcessKillResponse,
+ )
+
+ async def spawn(
+ self,
+ id: str,
+ *,
+ command: str,
+ args: List[str] | NotGiven = NOT_GIVEN,
+ as_root: bool | NotGiven = NOT_GIVEN,
+ as_user: Optional[str] | NotGiven = NOT_GIVEN,
+ cwd: Optional[str] | NotGiven = NOT_GIVEN,
+ env: Dict[str, str] | NotGiven = NOT_GIVEN,
+ timeout_sec: Optional[int] | NotGiven = NOT_GIVEN,
+ # 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,
+ ) -> ProcessSpawnResponse:
+ """
+ Execute a command asynchronously
+
+ Args:
+ command: Executable or shell command to run.
+
+ args: Command arguments.
+
+ as_root: Run the process with root privileges.
+
+ as_user: Run the process as this user.
+
+ cwd: Working directory (absolute path) to run the command in.
+
+ env: Environment variables to set for the process.
+
+ timeout_sec: Maximum execution time in seconds.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._post(
+ f"/browsers/{id}/process/spawn",
+ body=await async_maybe_transform(
+ {
+ "command": command,
+ "args": args,
+ "as_root": as_root,
+ "as_user": as_user,
+ "cwd": cwd,
+ "env": env,
+ "timeout_sec": timeout_sec,
+ },
+ process_spawn_params.ProcessSpawnParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProcessSpawnResponse,
+ )
+
+ async def status(
+ self,
+ process_id: str,
+ *,
+ id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> ProcessStatusResponse:
+ """
+ Get process status
+
+ 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}")
+ if not process_id:
+ raise ValueError(f"Expected a non-empty value for `process_id` but received {process_id!r}")
+ return await self._get(
+ f"/browsers/{id}/process/{process_id}/status",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProcessStatusResponse,
+ )
+
+ async def stdin(
+ self,
+ process_id: str,
+ *,
+ id: str,
+ data_b64: 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,
+ ) -> ProcessStdinResponse:
+ """
+ Write to process stdin
+
+ Args:
+ data_b64: Base64-encoded data to write.
+
+ 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}")
+ if not process_id:
+ raise ValueError(f"Expected a non-empty value for `process_id` but received {process_id!r}")
+ return await self._post(
+ f"/browsers/{id}/process/{process_id}/stdin",
+ body=await async_maybe_transform({"data_b64": data_b64}, process_stdin_params.ProcessStdinParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProcessStdinResponse,
+ )
+
+ async def stdout_stream(
+ self,
+ process_id: str,
+ *,
+ id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> AsyncStream[ProcessStdoutStreamResponse]:
+ """
+ Stream process stdout via SSE
+
+ 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}")
+ if not process_id:
+ raise ValueError(f"Expected a non-empty value for `process_id` but received {process_id!r}")
+ extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})}
+ return await self._get(
+ f"/browsers/{id}/process/{process_id}/stdout/stream",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ProcessStdoutStreamResponse,
+ stream=True,
+ stream_cls=AsyncStream[ProcessStdoutStreamResponse],
+ )
+
+
+class ProcessResourceWithRawResponse:
+ def __init__(self, process: ProcessResource) -> None:
+ self._process = process
+
+ self.exec = to_raw_response_wrapper(
+ process.exec,
+ )
+ self.kill = to_raw_response_wrapper(
+ process.kill,
+ )
+ self.spawn = to_raw_response_wrapper(
+ process.spawn,
+ )
+ self.status = to_raw_response_wrapper(
+ process.status,
+ )
+ self.stdin = to_raw_response_wrapper(
+ process.stdin,
+ )
+ self.stdout_stream = to_raw_response_wrapper(
+ process.stdout_stream,
+ )
+
+
+class AsyncProcessResourceWithRawResponse:
+ def __init__(self, process: AsyncProcessResource) -> None:
+ self._process = process
+
+ self.exec = async_to_raw_response_wrapper(
+ process.exec,
+ )
+ self.kill = async_to_raw_response_wrapper(
+ process.kill,
+ )
+ self.spawn = async_to_raw_response_wrapper(
+ process.spawn,
+ )
+ self.status = async_to_raw_response_wrapper(
+ process.status,
+ )
+ self.stdin = async_to_raw_response_wrapper(
+ process.stdin,
+ )
+ self.stdout_stream = async_to_raw_response_wrapper(
+ process.stdout_stream,
+ )
+
+
+class ProcessResourceWithStreamingResponse:
+ def __init__(self, process: ProcessResource) -> None:
+ self._process = process
+
+ self.exec = to_streamed_response_wrapper(
+ process.exec,
+ )
+ self.kill = to_streamed_response_wrapper(
+ process.kill,
+ )
+ self.spawn = to_streamed_response_wrapper(
+ process.spawn,
+ )
+ self.status = to_streamed_response_wrapper(
+ process.status,
+ )
+ self.stdin = to_streamed_response_wrapper(
+ process.stdin,
+ )
+ self.stdout_stream = to_streamed_response_wrapper(
+ process.stdout_stream,
+ )
+
+
+class AsyncProcessResourceWithStreamingResponse:
+ def __init__(self, process: AsyncProcessResource) -> None:
+ self._process = process
+
+ self.exec = async_to_streamed_response_wrapper(
+ process.exec,
+ )
+ self.kill = async_to_streamed_response_wrapper(
+ process.kill,
+ )
+ self.spawn = async_to_streamed_response_wrapper(
+ process.spawn,
+ )
+ self.status = async_to_streamed_response_wrapper(
+ process.status,
+ )
+ self.stdin = async_to_streamed_response_wrapper(
+ process.stdin,
+ )
+ self.stdout_stream = async_to_streamed_response_wrapper(
+ process.stdout_stream,
+ )
diff --git a/src/kernel/types/browsers/__init__.py b/src/kernel/types/browsers/__init__.py
index c4e80a89..d0b6b383 100644
--- a/src/kernel/types/browsers/__init__.py
+++ b/src/kernel/types/browsers/__init__.py
@@ -3,16 +3,30 @@
from __future__ import annotations
from .f_move_params import FMoveParams as FMoveParams
+from .f_upload_params import FUploadParams as FUploadParams
+from .log_stream_params import LogStreamParams as LogStreamParams
from .f_file_info_params import FFileInfoParams as FFileInfoParams
from .f_read_file_params import FReadFileParams as FReadFileParams
from .f_list_files_params import FListFilesParams as FListFilesParams
+from .f_upload_zip_params import FUploadZipParams as FUploadZipParams
from .f_write_file_params import FWriteFileParams as FWriteFileParams
+from .process_exec_params import ProcessExecParams as ProcessExecParams
+from .process_kill_params import ProcessKillParams as ProcessKillParams
from .replay_start_params import ReplayStartParams as ReplayStartParams
from .f_delete_file_params import FDeleteFileParams as FDeleteFileParams
from .f_file_info_response import FFileInfoResponse as FFileInfoResponse
+from .process_spawn_params import ProcessSpawnParams as ProcessSpawnParams
+from .process_stdin_params import ProcessStdinParams as ProcessStdinParams
from .replay_list_response import ReplayListResponse as ReplayListResponse
from .f_list_files_response import FListFilesResponse as FListFilesResponse
+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 .process_spawn_response import ProcessSpawnResponse as ProcessSpawnResponse
+from .process_stdin_response import ProcessStdinResponse as ProcessStdinResponse
+from .process_status_response import ProcessStatusResponse as ProcessStatusResponse
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 .f_set_file_permissions_params import FSetFilePermissionsParams as FSetFilePermissionsParams
+from .process_stdout_stream_response import ProcessStdoutStreamResponse as ProcessStdoutStreamResponse
diff --git a/src/kernel/types/browsers/f_download_dir_zip_params.py b/src/kernel/types/browsers/f_download_dir_zip_params.py
new file mode 100644
index 00000000..88212c64
--- /dev/null
+++ b/src/kernel/types/browsers/f_download_dir_zip_params.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["FDownloadDirZipParams"]
+
+
+class FDownloadDirZipParams(TypedDict, total=False):
+ path: Required[str]
+ """Absolute directory path to archive and download."""
diff --git a/src/kernel/types/browsers/f_upload_params.py b/src/kernel/types/browsers/f_upload_params.py
new file mode 100644
index 00000000..8f6534bd
--- /dev/null
+++ b/src/kernel/types/browsers/f_upload_params.py
@@ -0,0 +1,21 @@
+# 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 Required, TypedDict
+
+from ..._types import FileTypes
+
+__all__ = ["FUploadParams", "File"]
+
+
+class FUploadParams(TypedDict, total=False):
+ files: Required[Iterable[File]]
+
+
+class File(TypedDict, total=False):
+ dest_path: Required[str]
+ """Absolute destination path to write the file."""
+
+ file: Required[FileTypes]
diff --git a/src/kernel/types/browsers/f_upload_zip_params.py b/src/kernel/types/browsers/f_upload_zip_params.py
new file mode 100644
index 00000000..4646e05f
--- /dev/null
+++ b/src/kernel/types/browsers/f_upload_zip_params.py
@@ -0,0 +1,16 @@
+# 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 FileTypes
+
+__all__ = ["FUploadZipParams"]
+
+
+class FUploadZipParams(TypedDict, total=False):
+ dest_path: Required[str]
+ """Absolute destination directory to extract the archive to."""
+
+ zip_file: Required[FileTypes]
diff --git a/src/kernel/types/browsers/log_stream_params.py b/src/kernel/types/browsers/log_stream_params.py
new file mode 100644
index 00000000..2eeb9b32
--- /dev/null
+++ b/src/kernel/types/browsers/log_stream_params.py
@@ -0,0 +1,19 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["LogStreamParams"]
+
+
+class LogStreamParams(TypedDict, total=False):
+ source: Required[Literal["path", "supervisor"]]
+
+ follow: bool
+
+ path: str
+ """only required if source is path"""
+
+ supervisor_process: str
+ """only required if source is supervisor"""
diff --git a/src/kernel/types/browsers/process_exec_params.py b/src/kernel/types/browsers/process_exec_params.py
new file mode 100644
index 00000000..3dd3ad59
--- /dev/null
+++ b/src/kernel/types/browsers/process_exec_params.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, List, Optional
+from typing_extensions import Required, TypedDict
+
+__all__ = ["ProcessExecParams"]
+
+
+class ProcessExecParams(TypedDict, total=False):
+ command: Required[str]
+ """Executable or shell command to run."""
+
+ args: List[str]
+ """Command arguments."""
+
+ as_root: bool
+ """Run the process with root privileges."""
+
+ as_user: Optional[str]
+ """Run the process as this user."""
+
+ cwd: Optional[str]
+ """Working directory (absolute path) to run the command in."""
+
+ env: Dict[str, str]
+ """Environment variables to set for the process."""
+
+ timeout_sec: Optional[int]
+ """Maximum execution time in seconds."""
diff --git a/src/kernel/types/browsers/process_exec_response.py b/src/kernel/types/browsers/process_exec_response.py
new file mode 100644
index 00000000..02588de0
--- /dev/null
+++ b/src/kernel/types/browsers/process_exec_response.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from ..._models import BaseModel
+
+__all__ = ["ProcessExecResponse"]
+
+
+class ProcessExecResponse(BaseModel):
+ duration_ms: Optional[int] = None
+ """Execution duration in milliseconds."""
+
+ exit_code: Optional[int] = None
+ """Process exit code."""
+
+ stderr_b64: Optional[str] = None
+ """Base64-encoded stderr buffer."""
+
+ stdout_b64: Optional[str] = None
+ """Base64-encoded stdout buffer."""
diff --git a/src/kernel/types/browsers/process_kill_params.py b/src/kernel/types/browsers/process_kill_params.py
new file mode 100644
index 00000000..84be2770
--- /dev/null
+++ b/src/kernel/types/browsers/process_kill_params.py
@@ -0,0 +1,14 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["ProcessKillParams"]
+
+
+class ProcessKillParams(TypedDict, total=False):
+ id: Required[str]
+
+ signal: Required[Literal["TERM", "KILL", "INT", "HUP"]]
+ """Signal to send."""
diff --git a/src/kernel/types/browsers/process_kill_response.py b/src/kernel/types/browsers/process_kill_response.py
new file mode 100644
index 00000000..ed128a78
--- /dev/null
+++ b/src/kernel/types/browsers/process_kill_response.py
@@ -0,0 +1,10 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from ..._models import BaseModel
+
+__all__ = ["ProcessKillResponse"]
+
+
+class ProcessKillResponse(BaseModel):
+ ok: bool
+ """Indicates success."""
diff --git a/src/kernel/types/browsers/process_spawn_params.py b/src/kernel/types/browsers/process_spawn_params.py
new file mode 100644
index 00000000..a468c473
--- /dev/null
+++ b/src/kernel/types/browsers/process_spawn_params.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, List, Optional
+from typing_extensions import Required, TypedDict
+
+__all__ = ["ProcessSpawnParams"]
+
+
+class ProcessSpawnParams(TypedDict, total=False):
+ command: Required[str]
+ """Executable or shell command to run."""
+
+ args: List[str]
+ """Command arguments."""
+
+ as_root: bool
+ """Run the process with root privileges."""
+
+ as_user: Optional[str]
+ """Run the process as this user."""
+
+ cwd: Optional[str]
+ """Working directory (absolute path) to run the command in."""
+
+ env: Dict[str, str]
+ """Environment variables to set for the process."""
+
+ timeout_sec: Optional[int]
+ """Maximum execution time in seconds."""
diff --git a/src/kernel/types/browsers/process_spawn_response.py b/src/kernel/types/browsers/process_spawn_response.py
new file mode 100644
index 00000000..23444da2
--- /dev/null
+++ b/src/kernel/types/browsers/process_spawn_response.py
@@ -0,0 +1,19 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+
+from ..._models import BaseModel
+
+__all__ = ["ProcessSpawnResponse"]
+
+
+class ProcessSpawnResponse(BaseModel):
+ pid: Optional[int] = None
+ """OS process ID."""
+
+ process_id: Optional[str] = None
+ """Server-assigned identifier for the process."""
+
+ started_at: Optional[datetime] = None
+ """Timestamp when the process started."""
diff --git a/src/kernel/types/browsers/process_status_response.py b/src/kernel/types/browsers/process_status_response.py
new file mode 100644
index 00000000..67626fe9
--- /dev/null
+++ b/src/kernel/types/browsers/process_status_response.py
@@ -0,0 +1,22 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["ProcessStatusResponse"]
+
+
+class ProcessStatusResponse(BaseModel):
+ cpu_pct: Optional[float] = None
+ """Estimated CPU usage percentage."""
+
+ exit_code: Optional[int] = None
+ """Exit code if the process has exited."""
+
+ mem_bytes: Optional[int] = None
+ """Estimated resident memory usage in bytes."""
+
+ state: Optional[Literal["running", "exited"]] = None
+ """Process state."""
diff --git a/src/kernel/types/browsers/process_stdin_params.py b/src/kernel/types/browsers/process_stdin_params.py
new file mode 100644
index 00000000..9ece9a57
--- /dev/null
+++ b/src/kernel/types/browsers/process_stdin_params.py
@@ -0,0 +1,14 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["ProcessStdinParams"]
+
+
+class ProcessStdinParams(TypedDict, total=False):
+ id: Required[str]
+
+ data_b64: Required[str]
+ """Base64-encoded data to write."""
diff --git a/src/kernel/types/browsers/process_stdin_response.py b/src/kernel/types/browsers/process_stdin_response.py
new file mode 100644
index 00000000..d137a962
--- /dev/null
+++ b/src/kernel/types/browsers/process_stdin_response.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from ..._models import BaseModel
+
+__all__ = ["ProcessStdinResponse"]
+
+
+class ProcessStdinResponse(BaseModel):
+ written_bytes: Optional[int] = None
+ """Number of bytes written."""
diff --git a/src/kernel/types/browsers/process_stdout_stream_response.py b/src/kernel/types/browsers/process_stdout_stream_response.py
new file mode 100644
index 00000000..0b1d0a8e
--- /dev/null
+++ b/src/kernel/types/browsers/process_stdout_stream_response.py
@@ -0,0 +1,22 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["ProcessStdoutStreamResponse"]
+
+
+class ProcessStdoutStreamResponse(BaseModel):
+ data_b64: Optional[str] = None
+ """Base64-encoded data from the process stream."""
+
+ event: Optional[Literal["exit"]] = None
+ """Lifecycle event type."""
+
+ exit_code: Optional[int] = None
+ """Exit code when the event is "exit"."""
+
+ stream: Optional[Literal["stdout", "stderr"]] = None
+ """Source stream of the data chunk."""
diff --git a/tests/api_resources/browsers/test_fs.py b/tests/api_resources/browsers/test_fs.py
index 24860c90..38e07b78 100644
--- a/tests/api_resources/browsers/test_fs.py
+++ b/tests/api_resources/browsers/test_fs.py
@@ -176,6 +176,60 @@ def test_path_params_delete_file(self, client: Kernel) -> None:
path="/J!",
)
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_method_download_dir_zip(self, client: Kernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/browsers/id/fs/download_dir_zip").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ f = client.browsers.fs.download_dir_zip(
+ id="id",
+ path="/J!",
+ )
+ assert f.is_closed
+ assert f.json() == {"foo": "bar"}
+ assert cast(Any, f.is_closed) is True
+ assert isinstance(f, BinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_raw_response_download_dir_zip(self, client: Kernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/browsers/id/fs/download_dir_zip").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+
+ f = client.browsers.fs.with_raw_response.download_dir_zip(
+ id="id",
+ path="/J!",
+ )
+
+ assert f.is_closed is True
+ assert f.http_request.headers.get("X-Stainless-Lang") == "python"
+ assert f.json() == {"foo": "bar"}
+ assert isinstance(f, BinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_streaming_response_download_dir_zip(self, client: Kernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/browsers/id/fs/download_dir_zip").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ with client.browsers.fs.with_streaming_response.download_dir_zip(
+ id="id",
+ path="/J!",
+ ) as f:
+ assert not f.is_closed
+ assert f.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ assert f.json() == {"foo": "bar"}
+ assert cast(Any, f.is_closed) is True
+ assert isinstance(f, StreamedBinaryAPIResponse)
+
+ assert cast(Any, f.is_closed) is True
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_path_params_download_dir_zip(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.browsers.fs.with_raw_response.download_dir_zip(
+ id="",
+ path="/J!",
+ )
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_method_file_info(self, client: Kernel) -> None:
@@ -434,6 +488,122 @@ def test_path_params_set_file_permissions(self, client: Kernel) -> None:
path="/J!",
)
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_upload(self, client: Kernel) -> None:
+ f = client.browsers.fs.upload(
+ id="id",
+ files=[
+ {
+ "dest_path": "/J!",
+ "file": b"raw file contents",
+ }
+ ],
+ )
+ assert f is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_upload(self, client: Kernel) -> None:
+ response = client.browsers.fs.with_raw_response.upload(
+ id="id",
+ files=[
+ {
+ "dest_path": "/J!",
+ "file": b"raw file contents",
+ }
+ ],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ f = response.parse()
+ assert f is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_upload(self, client: Kernel) -> None:
+ with client.browsers.fs.with_streaming_response.upload(
+ id="id",
+ files=[
+ {
+ "dest_path": "/J!",
+ "file": b"raw file contents",
+ }
+ ],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ f = response.parse()
+ assert f is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_upload(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.browsers.fs.with_raw_response.upload(
+ id="",
+ files=[
+ {
+ "dest_path": "/J!",
+ "file": b"raw file contents",
+ }
+ ],
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_upload_zip(self, client: Kernel) -> None:
+ f = client.browsers.fs.upload_zip(
+ id="id",
+ dest_path="/J!",
+ zip_file=b"raw file contents",
+ )
+ assert f is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_upload_zip(self, client: Kernel) -> None:
+ response = client.browsers.fs.with_raw_response.upload_zip(
+ id="id",
+ dest_path="/J!",
+ zip_file=b"raw file contents",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ f = response.parse()
+ assert f is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_upload_zip(self, client: Kernel) -> None:
+ with client.browsers.fs.with_streaming_response.upload_zip(
+ id="id",
+ dest_path="/J!",
+ zip_file=b"raw file contents",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ f = response.parse()
+ assert f is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_upload_zip(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.browsers.fs.with_raw_response.upload_zip(
+ id="",
+ dest_path="/J!",
+ zip_file=b"raw file contents",
+ )
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_method_write_file(self, client: Kernel) -> None:
@@ -649,6 +819,60 @@ async def test_path_params_delete_file(self, async_client: AsyncKernel) -> None:
path="/J!",
)
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_method_download_dir_zip(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/browsers/id/fs/download_dir_zip").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ f = await async_client.browsers.fs.download_dir_zip(
+ id="id",
+ path="/J!",
+ )
+ assert f.is_closed
+ assert await f.json() == {"foo": "bar"}
+ assert cast(Any, f.is_closed) is True
+ assert isinstance(f, AsyncBinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_raw_response_download_dir_zip(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/browsers/id/fs/download_dir_zip").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+
+ f = await async_client.browsers.fs.with_raw_response.download_dir_zip(
+ id="id",
+ path="/J!",
+ )
+
+ assert f.is_closed is True
+ assert f.http_request.headers.get("X-Stainless-Lang") == "python"
+ assert await f.json() == {"foo": "bar"}
+ assert isinstance(f, AsyncBinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_streaming_response_download_dir_zip(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/browsers/id/fs/download_dir_zip").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ async with async_client.browsers.fs.with_streaming_response.download_dir_zip(
+ id="id",
+ path="/J!",
+ ) as f:
+ assert not f.is_closed
+ assert f.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ assert await f.json() == {"foo": "bar"}
+ assert cast(Any, f.is_closed) is True
+ assert isinstance(f, AsyncStreamedBinaryAPIResponse)
+
+ assert cast(Any, f.is_closed) is True
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_path_params_download_dir_zip(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.browsers.fs.with_raw_response.download_dir_zip(
+ id="",
+ path="/J!",
+ )
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_method_file_info(self, async_client: AsyncKernel) -> None:
@@ -907,6 +1131,122 @@ async def test_path_params_set_file_permissions(self, async_client: AsyncKernel)
path="/J!",
)
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_upload(self, async_client: AsyncKernel) -> None:
+ f = await async_client.browsers.fs.upload(
+ id="id",
+ files=[
+ {
+ "dest_path": "/J!",
+ "file": b"raw file contents",
+ }
+ ],
+ )
+ assert f is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_upload(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.fs.with_raw_response.upload(
+ id="id",
+ files=[
+ {
+ "dest_path": "/J!",
+ "file": b"raw file contents",
+ }
+ ],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ f = await response.parse()
+ assert f is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_upload(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.fs.with_streaming_response.upload(
+ id="id",
+ files=[
+ {
+ "dest_path": "/J!",
+ "file": b"raw file contents",
+ }
+ ],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ f = await response.parse()
+ assert f is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_upload(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.browsers.fs.with_raw_response.upload(
+ id="",
+ files=[
+ {
+ "dest_path": "/J!",
+ "file": b"raw file contents",
+ }
+ ],
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_upload_zip(self, async_client: AsyncKernel) -> None:
+ f = await async_client.browsers.fs.upload_zip(
+ id="id",
+ dest_path="/J!",
+ zip_file=b"raw file contents",
+ )
+ assert f is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_upload_zip(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.fs.with_raw_response.upload_zip(
+ id="id",
+ dest_path="/J!",
+ zip_file=b"raw file contents",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ f = await response.parse()
+ assert f is None
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_upload_zip(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.fs.with_streaming_response.upload_zip(
+ id="id",
+ dest_path="/J!",
+ zip_file=b"raw file contents",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ f = await response.parse()
+ assert f is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_upload_zip(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.browsers.fs.with_raw_response.upload_zip(
+ id="",
+ dest_path="/J!",
+ zip_file=b"raw file contents",
+ )
+
@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_method_write_file(self, async_client: AsyncKernel) -> None:
diff --git a/tests/api_resources/browsers/test_logs.py b/tests/api_resources/browsers/test_logs.py
new file mode 100644
index 00000000..6aac62f6
--- /dev/null
+++ b/tests/api_resources/browsers/test_logs.py
@@ -0,0 +1,136 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from kernel import Kernel, AsyncKernel
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestLogs:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
+ @parametrize
+ def test_method_stream(self, client: Kernel) -> None:
+ log_stream = client.browsers.logs.stream(
+ id="id",
+ source="path",
+ )
+ log_stream.response.close()
+
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
+ @parametrize
+ def test_method_stream_with_all_params(self, client: Kernel) -> None:
+ log_stream = client.browsers.logs.stream(
+ id="id",
+ source="path",
+ follow=True,
+ path="path",
+ supervisor_process="supervisor_process",
+ )
+ log_stream.response.close()
+
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
+ @parametrize
+ def test_raw_response_stream(self, client: Kernel) -> None:
+ response = client.browsers.logs.with_raw_response.stream(
+ id="id",
+ source="path",
+ )
+
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ stream = response.parse()
+ stream.close()
+
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
+ @parametrize
+ def test_streaming_response_stream(self, client: Kernel) -> None:
+ with client.browsers.logs.with_streaming_response.stream(
+ id="id",
+ source="path",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ stream = response.parse()
+ stream.close()
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
+ @parametrize
+ def test_path_params_stream(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.browsers.logs.with_raw_response.stream(
+ id="",
+ source="path",
+ )
+
+
+class TestAsyncLogs:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
+ @parametrize
+ async def test_method_stream(self, async_client: AsyncKernel) -> None:
+ log_stream = await async_client.browsers.logs.stream(
+ id="id",
+ source="path",
+ )
+ await log_stream.response.aclose()
+
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
+ @parametrize
+ async def test_method_stream_with_all_params(self, async_client: AsyncKernel) -> None:
+ log_stream = await async_client.browsers.logs.stream(
+ id="id",
+ source="path",
+ follow=True,
+ path="path",
+ supervisor_process="supervisor_process",
+ )
+ await log_stream.response.aclose()
+
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
+ @parametrize
+ async def test_raw_response_stream(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.logs.with_raw_response.stream(
+ id="id",
+ source="path",
+ )
+
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ stream = await response.parse()
+ await stream.close()
+
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
+ @parametrize
+ async def test_streaming_response_stream(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.logs.with_streaming_response.stream(
+ id="id",
+ source="path",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ stream = await response.parse()
+ await stream.close()
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
+ @parametrize
+ async def test_path_params_stream(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.browsers.logs.with_raw_response.stream(
+ id="",
+ source="path",
+ )
diff --git a/tests/api_resources/browsers/test_process.py b/tests/api_resources/browsers/test_process.py
new file mode 100644
index 00000000..69977621
--- /dev/null
+++ b/tests/api_resources/browsers/test_process.py
@@ -0,0 +1,708 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from kernel import Kernel, AsyncKernel
+from tests.utils import assert_matches_type
+from kernel.types.browsers import (
+ ProcessExecResponse,
+ ProcessKillResponse,
+ ProcessSpawnResponse,
+ ProcessStdinResponse,
+ ProcessStatusResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestProcess:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_exec(self, client: Kernel) -> None:
+ process = client.browsers.process.exec(
+ id="id",
+ command="command",
+ )
+ assert_matches_type(ProcessExecResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_exec_with_all_params(self, client: Kernel) -> None:
+ process = client.browsers.process.exec(
+ id="id",
+ command="command",
+ args=["string"],
+ as_root=True,
+ as_user="as_user",
+ cwd="/J!",
+ env={"foo": "string"},
+ timeout_sec=0,
+ )
+ assert_matches_type(ProcessExecResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_exec(self, client: Kernel) -> None:
+ response = client.browsers.process.with_raw_response.exec(
+ id="id",
+ command="command",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ process = response.parse()
+ assert_matches_type(ProcessExecResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_exec(self, client: Kernel) -> None:
+ with client.browsers.process.with_streaming_response.exec(
+ id="id",
+ command="command",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ process = response.parse()
+ assert_matches_type(ProcessExecResponse, process, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_exec(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.browsers.process.with_raw_response.exec(
+ id="",
+ command="command",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_kill(self, client: Kernel) -> None:
+ process = client.browsers.process.kill(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ signal="TERM",
+ )
+ assert_matches_type(ProcessKillResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_kill(self, client: Kernel) -> None:
+ response = client.browsers.process.with_raw_response.kill(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ signal="TERM",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ process = response.parse()
+ assert_matches_type(ProcessKillResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_kill(self, client: Kernel) -> None:
+ with client.browsers.process.with_streaming_response.kill(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ signal="TERM",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ process = response.parse()
+ assert_matches_type(ProcessKillResponse, process, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_kill(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.browsers.process.with_raw_response.kill(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="",
+ signal="TERM",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `process_id` but received ''"):
+ client.browsers.process.with_raw_response.kill(
+ process_id="",
+ id="id",
+ signal="TERM",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_spawn(self, client: Kernel) -> None:
+ process = client.browsers.process.spawn(
+ id="id",
+ command="command",
+ )
+ assert_matches_type(ProcessSpawnResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_spawn_with_all_params(self, client: Kernel) -> None:
+ process = client.browsers.process.spawn(
+ id="id",
+ command="command",
+ args=["string"],
+ as_root=True,
+ as_user="as_user",
+ cwd="/J!",
+ env={"foo": "string"},
+ timeout_sec=0,
+ )
+ assert_matches_type(ProcessSpawnResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_spawn(self, client: Kernel) -> None:
+ response = client.browsers.process.with_raw_response.spawn(
+ id="id",
+ command="command",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ process = response.parse()
+ assert_matches_type(ProcessSpawnResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_spawn(self, client: Kernel) -> None:
+ with client.browsers.process.with_streaming_response.spawn(
+ id="id",
+ command="command",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ process = response.parse()
+ assert_matches_type(ProcessSpawnResponse, process, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_spawn(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.browsers.process.with_raw_response.spawn(
+ id="",
+ command="command",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_status(self, client: Kernel) -> None:
+ process = client.browsers.process.status(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ )
+ assert_matches_type(ProcessStatusResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_status(self, client: Kernel) -> None:
+ response = client.browsers.process.with_raw_response.status(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ process = response.parse()
+ assert_matches_type(ProcessStatusResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_status(self, client: Kernel) -> None:
+ with client.browsers.process.with_streaming_response.status(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ process = response.parse()
+ assert_matches_type(ProcessStatusResponse, process, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_status(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.browsers.process.with_raw_response.status(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `process_id` but received ''"):
+ client.browsers.process.with_raw_response.status(
+ process_id="",
+ id="id",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_method_stdin(self, client: Kernel) -> None:
+ process = client.browsers.process.stdin(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ data_b64="data_b64",
+ )
+ assert_matches_type(ProcessStdinResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_raw_response_stdin(self, client: Kernel) -> None:
+ response = client.browsers.process.with_raw_response.stdin(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ data_b64="data_b64",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ process = response.parse()
+ assert_matches_type(ProcessStdinResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_streaming_response_stdin(self, client: Kernel) -> None:
+ with client.browsers.process.with_streaming_response.stdin(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ data_b64="data_b64",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ process = response.parse()
+ assert_matches_type(ProcessStdinResponse, process, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ def test_path_params_stdin(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.browsers.process.with_raw_response.stdin(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="",
+ data_b64="data_b64",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `process_id` but received ''"):
+ client.browsers.process.with_raw_response.stdin(
+ process_id="",
+ id="id",
+ data_b64="data_b64",
+ )
+
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
+ @parametrize
+ def test_method_stdout_stream(self, client: Kernel) -> None:
+ process_stream = client.browsers.process.stdout_stream(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ )
+ process_stream.response.close()
+
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
+ @parametrize
+ def test_raw_response_stdout_stream(self, client: Kernel) -> None:
+ response = client.browsers.process.with_raw_response.stdout_stream(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ )
+
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ stream = response.parse()
+ stream.close()
+
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
+ @parametrize
+ def test_streaming_response_stdout_stream(self, client: Kernel) -> None:
+ with client.browsers.process.with_streaming_response.stdout_stream(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ stream = response.parse()
+ stream.close()
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
+ @parametrize
+ def test_path_params_stdout_stream(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.browsers.process.with_raw_response.stdout_stream(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `process_id` but received ''"):
+ client.browsers.process.with_raw_response.stdout_stream(
+ process_id="",
+ id="id",
+ )
+
+
+class TestAsyncProcess:
+ 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_exec(self, async_client: AsyncKernel) -> None:
+ process = await async_client.browsers.process.exec(
+ id="id",
+ command="command",
+ )
+ assert_matches_type(ProcessExecResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_exec_with_all_params(self, async_client: AsyncKernel) -> None:
+ process = await async_client.browsers.process.exec(
+ id="id",
+ command="command",
+ args=["string"],
+ as_root=True,
+ as_user="as_user",
+ cwd="/J!",
+ env={"foo": "string"},
+ timeout_sec=0,
+ )
+ assert_matches_type(ProcessExecResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_exec(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.process.with_raw_response.exec(
+ id="id",
+ command="command",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ process = await response.parse()
+ assert_matches_type(ProcessExecResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_exec(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.process.with_streaming_response.exec(
+ id="id",
+ command="command",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ process = await response.parse()
+ assert_matches_type(ProcessExecResponse, process, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_exec(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.browsers.process.with_raw_response.exec(
+ id="",
+ command="command",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_kill(self, async_client: AsyncKernel) -> None:
+ process = await async_client.browsers.process.kill(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ signal="TERM",
+ )
+ assert_matches_type(ProcessKillResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_kill(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.process.with_raw_response.kill(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ signal="TERM",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ process = await response.parse()
+ assert_matches_type(ProcessKillResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_kill(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.process.with_streaming_response.kill(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ signal="TERM",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ process = await response.parse()
+ assert_matches_type(ProcessKillResponse, process, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_kill(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.browsers.process.with_raw_response.kill(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="",
+ signal="TERM",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `process_id` but received ''"):
+ await async_client.browsers.process.with_raw_response.kill(
+ process_id="",
+ id="id",
+ signal="TERM",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_spawn(self, async_client: AsyncKernel) -> None:
+ process = await async_client.browsers.process.spawn(
+ id="id",
+ command="command",
+ )
+ assert_matches_type(ProcessSpawnResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_spawn_with_all_params(self, async_client: AsyncKernel) -> None:
+ process = await async_client.browsers.process.spawn(
+ id="id",
+ command="command",
+ args=["string"],
+ as_root=True,
+ as_user="as_user",
+ cwd="/J!",
+ env={"foo": "string"},
+ timeout_sec=0,
+ )
+ assert_matches_type(ProcessSpawnResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_spawn(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.process.with_raw_response.spawn(
+ id="id",
+ command="command",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ process = await response.parse()
+ assert_matches_type(ProcessSpawnResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_spawn(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.process.with_streaming_response.spawn(
+ id="id",
+ command="command",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ process = await response.parse()
+ assert_matches_type(ProcessSpawnResponse, process, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_spawn(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.browsers.process.with_raw_response.spawn(
+ id="",
+ command="command",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_status(self, async_client: AsyncKernel) -> None:
+ process = await async_client.browsers.process.status(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ )
+ assert_matches_type(ProcessStatusResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_status(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.process.with_raw_response.status(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ process = await response.parse()
+ assert_matches_type(ProcessStatusResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_status(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.process.with_streaming_response.status(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ process = await response.parse()
+ assert_matches_type(ProcessStatusResponse, process, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_status(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.browsers.process.with_raw_response.status(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `process_id` but received ''"):
+ await async_client.browsers.process.with_raw_response.status(
+ process_id="",
+ id="id",
+ )
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_method_stdin(self, async_client: AsyncKernel) -> None:
+ process = await async_client.browsers.process.stdin(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ data_b64="data_b64",
+ )
+ assert_matches_type(ProcessStdinResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_raw_response_stdin(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.process.with_raw_response.stdin(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ data_b64="data_b64",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ process = await response.parse()
+ assert_matches_type(ProcessStdinResponse, process, path=["response"])
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_streaming_response_stdin(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.process.with_streaming_response.stdin(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ data_b64="data_b64",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ process = await response.parse()
+ assert_matches_type(ProcessStdinResponse, process, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism tests are disabled")
+ @parametrize
+ async def test_path_params_stdin(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.browsers.process.with_raw_response.stdin(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="",
+ data_b64="data_b64",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `process_id` but received ''"):
+ await async_client.browsers.process.with_raw_response.stdin(
+ process_id="",
+ id="id",
+ data_b64="data_b64",
+ )
+
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
+ @parametrize
+ async def test_method_stdout_stream(self, async_client: AsyncKernel) -> None:
+ process_stream = await async_client.browsers.process.stdout_stream(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ )
+ await process_stream.response.aclose()
+
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
+ @parametrize
+ async def test_raw_response_stdout_stream(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.process.with_raw_response.stdout_stream(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ )
+
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ stream = await response.parse()
+ await stream.close()
+
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
+ @parametrize
+ async def test_streaming_response_stdout_stream(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.process.with_streaming_response.stdout_stream(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ stream = await response.parse()
+ await stream.close()
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses")
+ @parametrize
+ async def test_path_params_stdout_stream(self, async_client: AsyncKernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ await async_client.browsers.process.with_raw_response.stdout_stream(
+ process_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `process_id` but received ''"):
+ await async_client.browsers.process.with_raw_response.stdout_stream(
+ process_id="",
+ id="id",
+ )