From 07b55e4a4b65b403b3b6f69b95b221e2020cf30b Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 6 Aug 2025 05:32:21 +0000
Subject: [PATCH 1/3] chore(internal): fix ruff target version
---
pyproject.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pyproject.toml b/pyproject.toml
index 5927c84..a4c4b29 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -159,7 +159,7 @@ reportPrivateUsage = false
[tool.ruff]
line-length = 120
output-format = "grouped"
-target-version = "py37"
+target-version = "py38"
[tool.ruff.format]
docstring-code-format = true
From 14667cdfd06540585ffac570b8963b322cf9ef23 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 7 Aug 2025 21:27:53 +0000
Subject: [PATCH 2/3] feat(api): browser instance file i/o
---
.stats.yml | 8 +-
api.md | 34 +
src/kernel/resources/browsers/__init__.py | 14 +
src/kernel/resources/browsers/browsers.py | 32 +
src/kernel/resources/browsers/fs/__init__.py | 33 +
src/kernel/resources/browsers/fs/fs.py | 1049 +++++++++++++++++
src/kernel/resources/browsers/fs/watch.py | 369 ++++++
src/kernel/types/browsers/__init__.py | 11 +
.../browsers/f_create_directory_params.py | 15 +
.../browsers/f_delete_directory_params.py | 12 +
.../types/browsers/f_delete_file_params.py | 12 +
.../types/browsers/f_file_info_params.py | 12 +
.../types/browsers/f_file_info_response.py | 27 +
.../types/browsers/f_list_files_params.py | 12 +
.../types/browsers/f_list_files_response.py | 32 +
src/kernel/types/browsers/f_move_params.py | 15 +
.../types/browsers/f_read_file_params.py | 12 +
.../browsers/f_set_file_permissions_params.py | 21 +
.../types/browsers/f_write_file_params.py | 15 +
src/kernel/types/browsers/fs/__init__.py | 7 +
.../browsers/fs/watch_events_response.py | 22 +
.../types/browsers/fs/watch_start_params.py | 15 +
.../types/browsers/fs/watch_start_response.py | 12 +
tests/api_resources/browsers/fs/__init__.py | 1 +
tests/api_resources/browsers/fs/test_watch.py | 358 ++++++
tests/api_resources/browsers/test_fs.py | 977 +++++++++++++++
26 files changed, 3123 insertions(+), 4 deletions(-)
create mode 100644 src/kernel/resources/browsers/fs/__init__.py
create mode 100644 src/kernel/resources/browsers/fs/fs.py
create mode 100644 src/kernel/resources/browsers/fs/watch.py
create mode 100644 src/kernel/types/browsers/f_create_directory_params.py
create mode 100644 src/kernel/types/browsers/f_delete_directory_params.py
create mode 100644 src/kernel/types/browsers/f_delete_file_params.py
create mode 100644 src/kernel/types/browsers/f_file_info_params.py
create mode 100644 src/kernel/types/browsers/f_file_info_response.py
create mode 100644 src/kernel/types/browsers/f_list_files_params.py
create mode 100644 src/kernel/types/browsers/f_list_files_response.py
create mode 100644 src/kernel/types/browsers/f_move_params.py
create mode 100644 src/kernel/types/browsers/f_read_file_params.py
create mode 100644 src/kernel/types/browsers/f_set_file_permissions_params.py
create mode 100644 src/kernel/types/browsers/f_write_file_params.py
create mode 100644 src/kernel/types/browsers/fs/__init__.py
create mode 100644 src/kernel/types/browsers/fs/watch_events_response.py
create mode 100644 src/kernel/types/browsers/fs/watch_start_params.py
create mode 100644 src/kernel/types/browsers/fs/watch_start_response.py
create mode 100644 tests/api_resources/browsers/fs/__init__.py
create mode 100644 tests/api_resources/browsers/fs/test_watch.py
create mode 100644 tests/api_resources/browsers/test_fs.py
diff --git a/.stats.yml b/.stats.yml
index fbe1e82..062204b 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 19
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-9f2d347a4bcb03aed092ba4495aac090c3d988e9a99af091ee35c09994adad8b.yml
-openapi_spec_hash: 73b92bd5503ab6c64dc26da31cca36e2
-config_hash: 65328ff206b8c0168c915914506d9dba
+configured_endpoints: 31
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-e907afeabfeea49dedd783112ac3fd29267bc86f3d594f89ba9a2abf2bcbc9d8.yml
+openapi_spec_hash: 060ca6288c1a09b6d1bdf207a0011165
+config_hash: f67e4b33b2fb30c1405ee2fff8096320
diff --git a/api.md b/api.md
index 434ace2..f03855d 100644
--- a/api.md
+++ b/api.md
@@ -94,3 +94,37 @@ Methods:
- client.browsers.replays.download(replay_id, \*, id) -> BinaryAPIResponse
- client.browsers.replays.start(id, \*\*params) -> ReplayStartResponse
- client.browsers.replays.stop(replay_id, \*, id) -> None
+
+## Fs
+
+Types:
+
+```python
+from kernel.types.browsers import FFileInfoResponse, FListFilesResponse
+```
+
+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.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.write_file(id, contents, \*\*params) -> None
+
+### Watch
+
+Types:
+
+```python
+from kernel.types.browsers.fs import WatchEventsResponse, WatchStartResponse
+```
+
+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
diff --git a/src/kernel/resources/browsers/__init__.py b/src/kernel/resources/browsers/__init__.py
index 236b5f7..41452e9 100644
--- a/src/kernel/resources/browsers/__init__.py
+++ b/src/kernel/resources/browsers/__init__.py
@@ -1,5 +1,13 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+from .fs import (
+ FsResource,
+ AsyncFsResource,
+ FsResourceWithRawResponse,
+ AsyncFsResourceWithRawResponse,
+ FsResourceWithStreamingResponse,
+ AsyncFsResourceWithStreamingResponse,
+)
from .replays import (
ReplaysResource,
AsyncReplaysResource,
@@ -24,6 +32,12 @@
"AsyncReplaysResourceWithRawResponse",
"ReplaysResourceWithStreamingResponse",
"AsyncReplaysResourceWithStreamingResponse",
+ "FsResource",
+ "AsyncFsResource",
+ "FsResourceWithRawResponse",
+ "AsyncFsResourceWithRawResponse",
+ "FsResourceWithStreamingResponse",
+ "AsyncFsResourceWithStreamingResponse",
"BrowsersResource",
"AsyncBrowsersResource",
"BrowsersResourceWithRawResponse",
diff --git a/src/kernel/resources/browsers/browsers.py b/src/kernel/resources/browsers/browsers.py
index b44573e..6d29c9e 100644
--- a/src/kernel/resources/browsers/browsers.py
+++ b/src/kernel/resources/browsers/browsers.py
@@ -4,6 +4,14 @@
import httpx
+from .fs.fs import (
+ FsResource,
+ AsyncFsResource,
+ FsResourceWithRawResponse,
+ AsyncFsResourceWithRawResponse,
+ FsResourceWithStreamingResponse,
+ AsyncFsResourceWithStreamingResponse,
+)
from ...types import browser_create_params, browser_delete_params
from .replays import (
ReplaysResource,
@@ -37,6 +45,10 @@ class BrowsersResource(SyncAPIResource):
def replays(self) -> ReplaysResource:
return ReplaysResource(self._client)
+ @cached_property
+ def fs(self) -> FsResource:
+ return FsResource(self._client)
+
@cached_property
def with_raw_response(self) -> BrowsersResourceWithRawResponse:
"""
@@ -239,6 +251,10 @@ class AsyncBrowsersResource(AsyncAPIResource):
def replays(self) -> AsyncReplaysResource:
return AsyncReplaysResource(self._client)
+ @cached_property
+ def fs(self) -> AsyncFsResource:
+ return AsyncFsResource(self._client)
+
@cached_property
def with_raw_response(self) -> AsyncBrowsersResourceWithRawResponse:
"""
@@ -462,6 +478,10 @@ def __init__(self, browsers: BrowsersResource) -> None:
def replays(self) -> ReplaysResourceWithRawResponse:
return ReplaysResourceWithRawResponse(self._browsers.replays)
+ @cached_property
+ def fs(self) -> FsResourceWithRawResponse:
+ return FsResourceWithRawResponse(self._browsers.fs)
+
class AsyncBrowsersResourceWithRawResponse:
def __init__(self, browsers: AsyncBrowsersResource) -> None:
@@ -487,6 +507,10 @@ def __init__(self, browsers: AsyncBrowsersResource) -> None:
def replays(self) -> AsyncReplaysResourceWithRawResponse:
return AsyncReplaysResourceWithRawResponse(self._browsers.replays)
+ @cached_property
+ def fs(self) -> AsyncFsResourceWithRawResponse:
+ return AsyncFsResourceWithRawResponse(self._browsers.fs)
+
class BrowsersResourceWithStreamingResponse:
def __init__(self, browsers: BrowsersResource) -> None:
@@ -512,6 +536,10 @@ def __init__(self, browsers: BrowsersResource) -> None:
def replays(self) -> ReplaysResourceWithStreamingResponse:
return ReplaysResourceWithStreamingResponse(self._browsers.replays)
+ @cached_property
+ def fs(self) -> FsResourceWithStreamingResponse:
+ return FsResourceWithStreamingResponse(self._browsers.fs)
+
class AsyncBrowsersResourceWithStreamingResponse:
def __init__(self, browsers: AsyncBrowsersResource) -> None:
@@ -536,3 +564,7 @@ def __init__(self, browsers: AsyncBrowsersResource) -> None:
@cached_property
def replays(self) -> AsyncReplaysResourceWithStreamingResponse:
return AsyncReplaysResourceWithStreamingResponse(self._browsers.replays)
+
+ @cached_property
+ def fs(self) -> AsyncFsResourceWithStreamingResponse:
+ return AsyncFsResourceWithStreamingResponse(self._browsers.fs)
diff --git a/src/kernel/resources/browsers/fs/__init__.py b/src/kernel/resources/browsers/fs/__init__.py
new file mode 100644
index 0000000..8195b3f
--- /dev/null
+++ b/src/kernel/resources/browsers/fs/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .fs import (
+ FsResource,
+ AsyncFsResource,
+ FsResourceWithRawResponse,
+ AsyncFsResourceWithRawResponse,
+ FsResourceWithStreamingResponse,
+ AsyncFsResourceWithStreamingResponse,
+)
+from .watch import (
+ WatchResource,
+ AsyncWatchResource,
+ WatchResourceWithRawResponse,
+ AsyncWatchResourceWithRawResponse,
+ WatchResourceWithStreamingResponse,
+ AsyncWatchResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "WatchResource",
+ "AsyncWatchResource",
+ "WatchResourceWithRawResponse",
+ "AsyncWatchResourceWithRawResponse",
+ "WatchResourceWithStreamingResponse",
+ "AsyncWatchResourceWithStreamingResponse",
+ "FsResource",
+ "AsyncFsResource",
+ "FsResourceWithRawResponse",
+ "AsyncFsResourceWithRawResponse",
+ "FsResourceWithStreamingResponse",
+ "AsyncFsResourceWithStreamingResponse",
+]
diff --git a/src/kernel/resources/browsers/fs/fs.py b/src/kernel/resources/browsers/fs/fs.py
new file mode 100644
index 0000000..3563c7c
--- /dev/null
+++ b/src/kernel/resources/browsers/fs/fs.py
@@ -0,0 +1,1049 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from .watch import (
+ WatchResource,
+ AsyncWatchResource,
+ WatchResourceWithRawResponse,
+ AsyncWatchResourceWithRawResponse,
+ WatchResourceWithStreamingResponse,
+ 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 ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
+ BinaryAPIResponse,
+ AsyncBinaryAPIResponse,
+ StreamedBinaryAPIResponse,
+ AsyncStreamedBinaryAPIResponse,
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ to_custom_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+ to_custom_streamed_response_wrapper,
+ async_to_custom_raw_response_wrapper,
+ async_to_custom_streamed_response_wrapper,
+)
+from ...._base_client import make_request_options
+from ....types.browsers import (
+ f_move_params,
+ f_file_info_params,
+ f_read_file_params,
+ f_list_files_params,
+ f_write_file_params,
+ f_delete_file_params,
+ f_create_directory_params,
+ f_delete_directory_params,
+ f_set_file_permissions_params,
+)
+from ....types.browsers.f_file_info_response import FFileInfoResponse
+from ....types.browsers.f_list_files_response import FListFilesResponse
+
+__all__ = ["FsResource", "AsyncFsResource"]
+
+
+class FsResource(SyncAPIResource):
+ @cached_property
+ def watch(self) -> WatchResource:
+ return WatchResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> FsResourceWithRawResponse:
+ """
+ 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 FsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> FsResourceWithStreamingResponse:
+ """
+ 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 FsResourceWithStreamingResponse(self)
+
+ def create_directory(
+ self,
+ id: str,
+ *,
+ path: str,
+ mode: 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,
+ ) -> None:
+ """
+ Create a new directory
+
+ Args:
+ path: Absolute directory path to create.
+
+ mode: Optional directory mode (octal string, e.g. 755). Defaults to 755.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._put(
+ f"/browsers/{id}/fs/create_directory",
+ body=maybe_transform(
+ {
+ "path": path,
+ "mode": mode,
+ },
+ f_create_directory_params.FCreateDirectoryParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def delete_directory(
+ 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,
+ ) -> None:
+ """
+ Delete a directory
+
+ Args:
+ path: Absolute path to delete.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._put(
+ f"/browsers/{id}/fs/delete_directory",
+ body=maybe_transform({"path": path}, f_delete_directory_params.FDeleteDirectoryParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def delete_file(
+ 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,
+ ) -> None:
+ """
+ Delete a file
+
+ Args:
+ path: Absolute path to delete.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._put(
+ f"/browsers/{id}/fs/delete_file",
+ body=maybe_transform({"path": path}, f_delete_file_params.FDeleteFileParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def file_info(
+ 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,
+ ) -> FFileInfoResponse:
+ """
+ Get information about a file or directory
+
+ Args:
+ path: Absolute path of the file or directory.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._get(
+ f"/browsers/{id}/fs/file_info",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"path": path}, f_file_info_params.FFileInfoParams),
+ ),
+ cast_to=FFileInfoResponse,
+ )
+
+ def list_files(
+ 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,
+ ) -> FListFilesResponse:
+ """
+ List files in a directory
+
+ Args:
+ path: Absolute directory path.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return self._get(
+ f"/browsers/{id}/fs/list_files",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"path": path}, f_list_files_params.FListFilesParams),
+ ),
+ cast_to=FListFilesResponse,
+ )
+
+ def move(
+ self,
+ id: str,
+ *,
+ dest_path: str,
+ src_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,
+ ) -> None:
+ """
+ Move or rename a file or directory
+
+ Args:
+ dest_path: Absolute destination path.
+
+ src_path: Absolute source path.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._put(
+ f"/browsers/{id}/fs/move",
+ body=maybe_transform(
+ {
+ "dest_path": dest_path,
+ "src_path": src_path,
+ },
+ f_move_params.FMoveParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ def read_file(
+ 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:
+ """
+ Read file contents
+
+ Args:
+ path: Absolute file path to read.
+
+ 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/octet-stream", **(extra_headers or {})}
+ return self._get(
+ f"/browsers/{id}/fs/read_file",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"path": path}, f_read_file_params.FReadFileParams),
+ ),
+ cast_to=BinaryAPIResponse,
+ )
+
+ def set_file_permissions(
+ self,
+ id: str,
+ *,
+ mode: str,
+ path: str,
+ group: str | NotGiven = NOT_GIVEN,
+ owner: 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,
+ ) -> None:
+ """
+ Set file or directory permissions/ownership
+
+ Args:
+ mode: File mode bits (octal string, e.g. 644).
+
+ path: Absolute path whose permissions are to be changed.
+
+ group: New group name or GID.
+
+ owner: New owner username or UID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._put(
+ f"/browsers/{id}/fs/set_file_permissions",
+ body=maybe_transform(
+ {
+ "mode": mode,
+ "path": path,
+ "group": group,
+ "owner": owner,
+ },
+ f_set_file_permissions_params.FSetFilePermissionsParams,
+ ),
+ 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,
+ contents: FileContent,
+ *,
+ path: str,
+ mode: 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,
+ ) -> None:
+ """
+ Write or create a file
+
+ Args:
+ path: Destination absolute file path.
+
+ mode: Optional file mode (octal string, e.g. 644). Defaults to 644.
+
+ 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 {})}
+ extra_headers["Content-Type"] = "application/octet-stream"
+ return self._put(
+ f"/browsers/{id}/fs/write_file",
+ body=read_file_content(contents),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "path": path,
+ "mode": mode,
+ },
+ f_write_file_params.FWriteFileParams,
+ ),
+ ),
+ cast_to=NoneType,
+ )
+
+
+class AsyncFsResource(AsyncAPIResource):
+ @cached_property
+ def watch(self) -> AsyncWatchResource:
+ return AsyncWatchResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncFsResourceWithRawResponse:
+ """
+ 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 AsyncFsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncFsResourceWithStreamingResponse:
+ """
+ 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 AsyncFsResourceWithStreamingResponse(self)
+
+ async def create_directory(
+ self,
+ id: str,
+ *,
+ path: str,
+ mode: 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,
+ ) -> None:
+ """
+ Create a new directory
+
+ Args:
+ path: Absolute directory path to create.
+
+ mode: Optional directory mode (octal string, e.g. 755). Defaults to 755.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._put(
+ f"/browsers/{id}/fs/create_directory",
+ body=await async_maybe_transform(
+ {
+ "path": path,
+ "mode": mode,
+ },
+ f_create_directory_params.FCreateDirectoryParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ async def delete_directory(
+ 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,
+ ) -> None:
+ """
+ Delete a directory
+
+ Args:
+ path: Absolute path to delete.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._put(
+ f"/browsers/{id}/fs/delete_directory",
+ body=await async_maybe_transform({"path": path}, f_delete_directory_params.FDeleteDirectoryParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ async def delete_file(
+ 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,
+ ) -> None:
+ """
+ Delete a file
+
+ Args:
+ path: Absolute path to delete.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._put(
+ f"/browsers/{id}/fs/delete_file",
+ body=await async_maybe_transform({"path": path}, f_delete_file_params.FDeleteFileParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ async def file_info(
+ 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,
+ ) -> FFileInfoResponse:
+ """
+ Get information about a file or directory
+
+ Args:
+ path: Absolute path of the file or directory.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._get(
+ f"/browsers/{id}/fs/file_info",
+ 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_file_info_params.FFileInfoParams),
+ ),
+ cast_to=FFileInfoResponse,
+ )
+
+ async def list_files(
+ 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,
+ ) -> FListFilesResponse:
+ """
+ List files in a directory
+
+ Args:
+ path: Absolute directory path.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ return await self._get(
+ f"/browsers/{id}/fs/list_files",
+ 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_list_files_params.FListFilesParams),
+ ),
+ cast_to=FListFilesResponse,
+ )
+
+ async def move(
+ self,
+ id: str,
+ *,
+ dest_path: str,
+ src_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,
+ ) -> None:
+ """
+ Move or rename a file or directory
+
+ Args:
+ dest_path: Absolute destination path.
+
+ src_path: Absolute source path.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._put(
+ f"/browsers/{id}/fs/move",
+ body=await async_maybe_transform(
+ {
+ "dest_path": dest_path,
+ "src_path": src_path,
+ },
+ f_move_params.FMoveParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+ async def read_file(
+ 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:
+ """
+ Read file contents
+
+ Args:
+ path: Absolute file path to read.
+
+ 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/octet-stream", **(extra_headers or {})}
+ return await self._get(
+ f"/browsers/{id}/fs/read_file",
+ 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_read_file_params.FReadFileParams),
+ ),
+ cast_to=AsyncBinaryAPIResponse,
+ )
+
+ async def set_file_permissions(
+ self,
+ id: str,
+ *,
+ mode: str,
+ path: str,
+ group: str | NotGiven = NOT_GIVEN,
+ owner: 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,
+ ) -> None:
+ """
+ Set file or directory permissions/ownership
+
+ Args:
+ mode: File mode bits (octal string, e.g. 644).
+
+ path: Absolute path whose permissions are to be changed.
+
+ group: New group name or GID.
+
+ owner: New owner username or UID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not id:
+ raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._put(
+ f"/browsers/{id}/fs/set_file_permissions",
+ body=await async_maybe_transform(
+ {
+ "mode": mode,
+ "path": path,
+ "group": group,
+ "owner": owner,
+ },
+ f_set_file_permissions_params.FSetFilePermissionsParams,
+ ),
+ 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,
+ contents: FileContent,
+ *,
+ path: str,
+ mode: 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,
+ ) -> None:
+ """
+ Write or create a file
+
+ Args:
+ path: Destination absolute file path.
+
+ mode: Optional file mode (octal string, e.g. 644). Defaults to 644.
+
+ 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 {})}
+ extra_headers["Content-Type"] = "application/octet-stream"
+ return await self._put(
+ f"/browsers/{id}/fs/write_file",
+ body=await async_read_file_content(contents),
+ 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,
+ "mode": mode,
+ },
+ f_write_file_params.FWriteFileParams,
+ ),
+ ),
+ cast_to=NoneType,
+ )
+
+
+class FsResourceWithRawResponse:
+ def __init__(self, fs: FsResource) -> None:
+ self._fs = fs
+
+ self.create_directory = to_raw_response_wrapper(
+ fs.create_directory,
+ )
+ self.delete_directory = to_raw_response_wrapper(
+ fs.delete_directory,
+ )
+ self.delete_file = to_raw_response_wrapper(
+ fs.delete_file,
+ )
+ self.file_info = to_raw_response_wrapper(
+ fs.file_info,
+ )
+ self.list_files = to_raw_response_wrapper(
+ fs.list_files,
+ )
+ self.move = to_raw_response_wrapper(
+ fs.move,
+ )
+ self.read_file = to_custom_raw_response_wrapper(
+ fs.read_file,
+ BinaryAPIResponse,
+ )
+ self.set_file_permissions = to_raw_response_wrapper(
+ fs.set_file_permissions,
+ )
+ self.write_file = to_raw_response_wrapper(
+ fs.write_file,
+ )
+
+ @cached_property
+ def watch(self) -> WatchResourceWithRawResponse:
+ return WatchResourceWithRawResponse(self._fs.watch)
+
+
+class AsyncFsResourceWithRawResponse:
+ def __init__(self, fs: AsyncFsResource) -> None:
+ self._fs = fs
+
+ self.create_directory = async_to_raw_response_wrapper(
+ fs.create_directory,
+ )
+ self.delete_directory = async_to_raw_response_wrapper(
+ fs.delete_directory,
+ )
+ self.delete_file = async_to_raw_response_wrapper(
+ fs.delete_file,
+ )
+ self.file_info = async_to_raw_response_wrapper(
+ fs.file_info,
+ )
+ self.list_files = async_to_raw_response_wrapper(
+ fs.list_files,
+ )
+ self.move = async_to_raw_response_wrapper(
+ fs.move,
+ )
+ self.read_file = async_to_custom_raw_response_wrapper(
+ fs.read_file,
+ AsyncBinaryAPIResponse,
+ )
+ self.set_file_permissions = async_to_raw_response_wrapper(
+ fs.set_file_permissions,
+ )
+ self.write_file = async_to_raw_response_wrapper(
+ fs.write_file,
+ )
+
+ @cached_property
+ def watch(self) -> AsyncWatchResourceWithRawResponse:
+ return AsyncWatchResourceWithRawResponse(self._fs.watch)
+
+
+class FsResourceWithStreamingResponse:
+ def __init__(self, fs: FsResource) -> None:
+ self._fs = fs
+
+ self.create_directory = to_streamed_response_wrapper(
+ fs.create_directory,
+ )
+ self.delete_directory = to_streamed_response_wrapper(
+ fs.delete_directory,
+ )
+ self.delete_file = to_streamed_response_wrapper(
+ fs.delete_file,
+ )
+ self.file_info = to_streamed_response_wrapper(
+ fs.file_info,
+ )
+ self.list_files = to_streamed_response_wrapper(
+ fs.list_files,
+ )
+ self.move = to_streamed_response_wrapper(
+ fs.move,
+ )
+ self.read_file = to_custom_streamed_response_wrapper(
+ fs.read_file,
+ StreamedBinaryAPIResponse,
+ )
+ self.set_file_permissions = to_streamed_response_wrapper(
+ fs.set_file_permissions,
+ )
+ self.write_file = to_streamed_response_wrapper(
+ fs.write_file,
+ )
+
+ @cached_property
+ def watch(self) -> WatchResourceWithStreamingResponse:
+ return WatchResourceWithStreamingResponse(self._fs.watch)
+
+
+class AsyncFsResourceWithStreamingResponse:
+ def __init__(self, fs: AsyncFsResource) -> None:
+ self._fs = fs
+
+ self.create_directory = async_to_streamed_response_wrapper(
+ fs.create_directory,
+ )
+ self.delete_directory = async_to_streamed_response_wrapper(
+ fs.delete_directory,
+ )
+ self.delete_file = async_to_streamed_response_wrapper(
+ fs.delete_file,
+ )
+ self.file_info = async_to_streamed_response_wrapper(
+ fs.file_info,
+ )
+ self.list_files = async_to_streamed_response_wrapper(
+ fs.list_files,
+ )
+ self.move = async_to_streamed_response_wrapper(
+ fs.move,
+ )
+ self.read_file = async_to_custom_streamed_response_wrapper(
+ fs.read_file,
+ AsyncStreamedBinaryAPIResponse,
+ )
+ self.set_file_permissions = async_to_streamed_response_wrapper(
+ fs.set_file_permissions,
+ )
+ self.write_file = async_to_streamed_response_wrapper(
+ fs.write_file,
+ )
+
+ @cached_property
+ def watch(self) -> AsyncWatchResourceWithStreamingResponse:
+ return AsyncWatchResourceWithStreamingResponse(self._fs.watch)
diff --git a/src/kernel/resources/browsers/fs/watch.py b/src/kernel/resources/browsers/fs/watch.py
new file mode 100644
index 0000000..a35e0de
--- /dev/null
+++ b/src/kernel/resources/browsers/fs/watch.py
@@ -0,0 +1,369 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ...._types import NOT_GIVEN, Body, Query, Headers, NoneType, 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.fs import watch_start_params
+from ....types.browsers.fs.watch_start_response import WatchStartResponse
+from ....types.browsers.fs.watch_events_response import WatchEventsResponse
+
+__all__ = ["WatchResource", "AsyncWatchResource"]
+
+
+class WatchResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> WatchResourceWithRawResponse:
+ """
+ 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 WatchResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> WatchResourceWithStreamingResponse:
+ """
+ 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 WatchResourceWithStreamingResponse(self)
+
+ def events(
+ self,
+ watch_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[WatchEventsResponse]:
+ """
+ Stream filesystem events for a watch
+
+ 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 watch_id:
+ raise ValueError(f"Expected a non-empty value for `watch_id` but received {watch_id!r}")
+ extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})}
+ return self._get(
+ f"/browsers/{id}/fs/watch/{watch_id}/events",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WatchEventsResponse,
+ stream=True,
+ stream_cls=Stream[WatchEventsResponse],
+ )
+
+ def start(
+ self,
+ id: str,
+ *,
+ path: str,
+ recursive: bool | 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,
+ ) -> WatchStartResponse:
+ """
+ Watch a directory for changes
+
+ Args:
+ path: Directory to watch.
+
+ recursive: Whether to watch recursively.
+
+ 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}/fs/watch",
+ body=maybe_transform(
+ {
+ "path": path,
+ "recursive": recursive,
+ },
+ watch_start_params.WatchStartParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WatchStartResponse,
+ )
+
+ def stop(
+ self,
+ watch_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,
+ ) -> None:
+ """
+ Stop watching a directory
+
+ 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 watch_id:
+ raise ValueError(f"Expected a non-empty value for `watch_id` but received {watch_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return self._delete(
+ f"/browsers/{id}/fs/watch/{watch_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class AsyncWatchResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncWatchResourceWithRawResponse:
+ """
+ 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 AsyncWatchResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncWatchResourceWithStreamingResponse:
+ """
+ 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 AsyncWatchResourceWithStreamingResponse(self)
+
+ async def events(
+ self,
+ watch_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[WatchEventsResponse]:
+ """
+ Stream filesystem events for a watch
+
+ 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 watch_id:
+ raise ValueError(f"Expected a non-empty value for `watch_id` but received {watch_id!r}")
+ extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})}
+ return await self._get(
+ f"/browsers/{id}/fs/watch/{watch_id}/events",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WatchEventsResponse,
+ stream=True,
+ stream_cls=AsyncStream[WatchEventsResponse],
+ )
+
+ async def start(
+ self,
+ id: str,
+ *,
+ path: str,
+ recursive: bool | 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,
+ ) -> WatchStartResponse:
+ """
+ Watch a directory for changes
+
+ Args:
+ path: Directory to watch.
+
+ recursive: Whether to watch recursively.
+
+ 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}/fs/watch",
+ body=await async_maybe_transform(
+ {
+ "path": path,
+ "recursive": recursive,
+ },
+ watch_start_params.WatchStartParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=WatchStartResponse,
+ )
+
+ async def stop(
+ self,
+ watch_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,
+ ) -> None:
+ """
+ Stop watching a directory
+
+ 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 watch_id:
+ raise ValueError(f"Expected a non-empty value for `watch_id` but received {watch_id!r}")
+ extra_headers = {"Accept": "*/*", **(extra_headers or {})}
+ return await self._delete(
+ f"/browsers/{id}/fs/watch/{watch_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NoneType,
+ )
+
+
+class WatchResourceWithRawResponse:
+ def __init__(self, watch: WatchResource) -> None:
+ self._watch = watch
+
+ self.events = to_raw_response_wrapper(
+ watch.events,
+ )
+ self.start = to_raw_response_wrapper(
+ watch.start,
+ )
+ self.stop = to_raw_response_wrapper(
+ watch.stop,
+ )
+
+
+class AsyncWatchResourceWithRawResponse:
+ def __init__(self, watch: AsyncWatchResource) -> None:
+ self._watch = watch
+
+ self.events = async_to_raw_response_wrapper(
+ watch.events,
+ )
+ self.start = async_to_raw_response_wrapper(
+ watch.start,
+ )
+ self.stop = async_to_raw_response_wrapper(
+ watch.stop,
+ )
+
+
+class WatchResourceWithStreamingResponse:
+ def __init__(self, watch: WatchResource) -> None:
+ self._watch = watch
+
+ self.events = to_streamed_response_wrapper(
+ watch.events,
+ )
+ self.start = to_streamed_response_wrapper(
+ watch.start,
+ )
+ self.stop = to_streamed_response_wrapper(
+ watch.stop,
+ )
+
+
+class AsyncWatchResourceWithStreamingResponse:
+ def __init__(self, watch: AsyncWatchResource) -> None:
+ self._watch = watch
+
+ self.events = async_to_streamed_response_wrapper(
+ watch.events,
+ )
+ self.start = async_to_streamed_response_wrapper(
+ watch.start,
+ )
+ self.stop = async_to_streamed_response_wrapper(
+ watch.stop,
+ )
diff --git a/src/kernel/types/browsers/__init__.py b/src/kernel/types/browsers/__init__.py
index 61b18bc..c4e80a8 100644
--- a/src/kernel/types/browsers/__init__.py
+++ b/src/kernel/types/browsers/__init__.py
@@ -2,6 +2,17 @@
from __future__ import annotations
+from .f_move_params import FMoveParams as FMoveParams
+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_write_file_params import FWriteFileParams as FWriteFileParams
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 .replay_list_response import ReplayListResponse as ReplayListResponse
+from .f_list_files_response import FListFilesResponse as FListFilesResponse
from .replay_start_response import ReplayStartResponse as ReplayStartResponse
+from .f_create_directory_params import FCreateDirectoryParams as FCreateDirectoryParams
+from .f_delete_directory_params import FDeleteDirectoryParams as FDeleteDirectoryParams
+from .f_set_file_permissions_params import FSetFilePermissionsParams as FSetFilePermissionsParams
diff --git a/src/kernel/types/browsers/f_create_directory_params.py b/src/kernel/types/browsers/f_create_directory_params.py
new file mode 100644
index 0000000..20924f3
--- /dev/null
+++ b/src/kernel/types/browsers/f_create_directory_params.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["FCreateDirectoryParams"]
+
+
+class FCreateDirectoryParams(TypedDict, total=False):
+ path: Required[str]
+ """Absolute directory path to create."""
+
+ mode: str
+ """Optional directory mode (octal string, e.g. 755). Defaults to 755."""
diff --git a/src/kernel/types/browsers/f_delete_directory_params.py b/src/kernel/types/browsers/f_delete_directory_params.py
new file mode 100644
index 0000000..8f5a086
--- /dev/null
+++ b/src/kernel/types/browsers/f_delete_directory_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__ = ["FDeleteDirectoryParams"]
+
+
+class FDeleteDirectoryParams(TypedDict, total=False):
+ path: Required[str]
+ """Absolute path to delete."""
diff --git a/src/kernel/types/browsers/f_delete_file_params.py b/src/kernel/types/browsers/f_delete_file_params.py
new file mode 100644
index 0000000..d79bb8a
--- /dev/null
+++ b/src/kernel/types/browsers/f_delete_file_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__ = ["FDeleteFileParams"]
+
+
+class FDeleteFileParams(TypedDict, total=False):
+ path: Required[str]
+ """Absolute path to delete."""
diff --git a/src/kernel/types/browsers/f_file_info_params.py b/src/kernel/types/browsers/f_file_info_params.py
new file mode 100644
index 0000000..9ddf41e
--- /dev/null
+++ b/src/kernel/types/browsers/f_file_info_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__ = ["FFileInfoParams"]
+
+
+class FFileInfoParams(TypedDict, total=False):
+ path: Required[str]
+ """Absolute path of the file or directory."""
diff --git a/src/kernel/types/browsers/f_file_info_response.py b/src/kernel/types/browsers/f_file_info_response.py
new file mode 100644
index 0000000..7da1574
--- /dev/null
+++ b/src/kernel/types/browsers/f_file_info_response.py
@@ -0,0 +1,27 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from datetime import datetime
+
+from ..._models import BaseModel
+
+__all__ = ["FFileInfoResponse"]
+
+
+class FFileInfoResponse(BaseModel):
+ is_dir: bool
+ """Whether the path is a directory."""
+
+ mod_time: datetime
+ """Last modification time."""
+
+ mode: str
+ """File mode bits (e.g., "drwxr-xr-x" or "-rw-r--r--")."""
+
+ name: str
+ """Base name of the file or directory."""
+
+ path: str
+ """Absolute path."""
+
+ size_bytes: int
+ """Size in bytes. 0 for directories."""
diff --git a/src/kernel/types/browsers/f_list_files_params.py b/src/kernel/types/browsers/f_list_files_params.py
new file mode 100644
index 0000000..87026f5
--- /dev/null
+++ b/src/kernel/types/browsers/f_list_files_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__ = ["FListFilesParams"]
+
+
+class FListFilesParams(TypedDict, total=False):
+ path: Required[str]
+ """Absolute directory path."""
diff --git a/src/kernel/types/browsers/f_list_files_response.py b/src/kernel/types/browsers/f_list_files_response.py
new file mode 100644
index 0000000..9fca14b
--- /dev/null
+++ b/src/kernel/types/browsers/f_list_files_response.py
@@ -0,0 +1,32 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+from datetime import datetime
+from typing_extensions import TypeAlias
+
+from ..._models import BaseModel
+
+__all__ = ["FListFilesResponse", "FListFilesResponseItem"]
+
+
+class FListFilesResponseItem(BaseModel):
+ is_dir: bool
+ """Whether the path is a directory."""
+
+ mod_time: datetime
+ """Last modification time."""
+
+ mode: str
+ """File mode bits (e.g., "drwxr-xr-x" or "-rw-r--r--")."""
+
+ name: str
+ """Base name of the file or directory."""
+
+ path: str
+ """Absolute path."""
+
+ size_bytes: int
+ """Size in bytes. 0 for directories."""
+
+
+FListFilesResponse: TypeAlias = List[FListFilesResponseItem]
diff --git a/src/kernel/types/browsers/f_move_params.py b/src/kernel/types/browsers/f_move_params.py
new file mode 100644
index 0000000..d324cc9
--- /dev/null
+++ b/src/kernel/types/browsers/f_move_params.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["FMoveParams"]
+
+
+class FMoveParams(TypedDict, total=False):
+ dest_path: Required[str]
+ """Absolute destination path."""
+
+ src_path: Required[str]
+ """Absolute source path."""
diff --git a/src/kernel/types/browsers/f_read_file_params.py b/src/kernel/types/browsers/f_read_file_params.py
new file mode 100644
index 0000000..ee5d2e9
--- /dev/null
+++ b/src/kernel/types/browsers/f_read_file_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__ = ["FReadFileParams"]
+
+
+class FReadFileParams(TypedDict, total=False):
+ path: Required[str]
+ """Absolute file path to read."""
diff --git a/src/kernel/types/browsers/f_set_file_permissions_params.py b/src/kernel/types/browsers/f_set_file_permissions_params.py
new file mode 100644
index 0000000..5a02c1e
--- /dev/null
+++ b/src/kernel/types/browsers/f_set_file_permissions_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_extensions import Required, TypedDict
+
+__all__ = ["FSetFilePermissionsParams"]
+
+
+class FSetFilePermissionsParams(TypedDict, total=False):
+ mode: Required[str]
+ """File mode bits (octal string, e.g. 644)."""
+
+ path: Required[str]
+ """Absolute path whose permissions are to be changed."""
+
+ group: str
+ """New group name or GID."""
+
+ owner: str
+ """New owner username or UID."""
diff --git a/src/kernel/types/browsers/f_write_file_params.py b/src/kernel/types/browsers/f_write_file_params.py
new file mode 100644
index 0000000..557eac1
--- /dev/null
+++ b/src/kernel/types/browsers/f_write_file_params.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["FWriteFileParams"]
+
+
+class FWriteFileParams(TypedDict, total=False):
+ path: Required[str]
+ """Destination absolute file path."""
+
+ mode: str
+ """Optional file mode (octal string, e.g. 644). Defaults to 644."""
diff --git a/src/kernel/types/browsers/fs/__init__.py b/src/kernel/types/browsers/fs/__init__.py
new file mode 100644
index 0000000..ebd13d9
--- /dev/null
+++ b/src/kernel/types/browsers/fs/__init__.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .watch_start_params import WatchStartParams as WatchStartParams
+from .watch_start_response import WatchStartResponse as WatchStartResponse
+from .watch_events_response import WatchEventsResponse as WatchEventsResponse
diff --git a/src/kernel/types/browsers/fs/watch_events_response.py b/src/kernel/types/browsers/fs/watch_events_response.py
new file mode 100644
index 0000000..8df2f50
--- /dev/null
+++ b/src/kernel/types/browsers/fs/watch_events_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__ = ["WatchEventsResponse"]
+
+
+class WatchEventsResponse(BaseModel):
+ path: str
+ """Absolute path of the file or directory."""
+
+ type: Literal["CREATE", "WRITE", "DELETE", "RENAME"]
+ """Event type."""
+
+ is_dir: Optional[bool] = None
+ """Whether the affected path is a directory."""
+
+ name: Optional[str] = None
+ """Base name of the file or directory affected."""
diff --git a/src/kernel/types/browsers/fs/watch_start_params.py b/src/kernel/types/browsers/fs/watch_start_params.py
new file mode 100644
index 0000000..5afddb1
--- /dev/null
+++ b/src/kernel/types/browsers/fs/watch_start_params.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["WatchStartParams"]
+
+
+class WatchStartParams(TypedDict, total=False):
+ path: Required[str]
+ """Directory to watch."""
+
+ recursive: bool
+ """Whether to watch recursively."""
diff --git a/src/kernel/types/browsers/fs/watch_start_response.py b/src/kernel/types/browsers/fs/watch_start_response.py
new file mode 100644
index 0000000..b9f78e4
--- /dev/null
+++ b/src/kernel/types/browsers/fs/watch_start_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__ = ["WatchStartResponse"]
+
+
+class WatchStartResponse(BaseModel):
+ watch_id: Optional[str] = None
+ """Unique identifier for the directory watch"""
diff --git a/tests/api_resources/browsers/fs/__init__.py b/tests/api_resources/browsers/fs/__init__.py
new file mode 100644
index 0000000..fd8019a
--- /dev/null
+++ b/tests/api_resources/browsers/fs/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/browsers/fs/test_watch.py b/tests/api_resources/browsers/fs/test_watch.py
new file mode 100644
index 0000000..b815c8a
--- /dev/null
+++ b/tests/api_resources/browsers/fs/test_watch.py
@@ -0,0 +1,358 @@
+# 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.fs import WatchStartResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestWatch:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(
+ reason="currently no good way to test endpoints with content type text/event-stream, Prism mock server will fail"
+ )
+ @parametrize
+ def test_method_events(self, client: Kernel) -> None:
+ watch_stream = client.browsers.fs.watch.events(
+ watch_id="watch_id",
+ id="id",
+ )
+ watch_stream.response.close()
+
+ @pytest.mark.skip(
+ reason="currently no good way to test endpoints with content type text/event-stream, Prism mock server will fail"
+ )
+ @parametrize
+ def test_raw_response_events(self, client: Kernel) -> None:
+ response = client.browsers.fs.watch.with_raw_response.events(
+ watch_id="watch_id",
+ id="id",
+ )
+
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ stream = response.parse()
+ stream.close()
+
+ @pytest.mark.skip(
+ reason="currently no good way to test endpoints with content type text/event-stream, Prism mock server will fail"
+ )
+ @parametrize
+ def test_streaming_response_events(self, client: Kernel) -> None:
+ with client.browsers.fs.watch.with_streaming_response.events(
+ watch_id="watch_id",
+ 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="currently no good way to test endpoints with content type text/event-stream, Prism mock server will fail"
+ )
+ @parametrize
+ def test_path_params_events(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.browsers.fs.watch.with_raw_response.events(
+ watch_id="watch_id",
+ id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `watch_id` but received ''"):
+ client.browsers.fs.watch.with_raw_response.events(
+ watch_id="",
+ id="id",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_start(self, client: Kernel) -> None:
+ watch = client.browsers.fs.watch.start(
+ id="id",
+ path="path",
+ )
+ assert_matches_type(WatchStartResponse, watch, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_start_with_all_params(self, client: Kernel) -> None:
+ watch = client.browsers.fs.watch.start(
+ id="id",
+ path="path",
+ recursive=True,
+ )
+ assert_matches_type(WatchStartResponse, watch, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_raw_response_start(self, client: Kernel) -> None:
+ response = client.browsers.fs.watch.with_raw_response.start(
+ id="id",
+ path="path",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ watch = response.parse()
+ assert_matches_type(WatchStartResponse, watch, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_streaming_response_start(self, client: Kernel) -> None:
+ with client.browsers.fs.watch.with_streaming_response.start(
+ id="id",
+ path="path",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ watch = response.parse()
+ assert_matches_type(WatchStartResponse, watch, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_path_params_start(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.browsers.fs.watch.with_raw_response.start(
+ id="",
+ path="path",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_stop(self, client: Kernel) -> None:
+ watch = client.browsers.fs.watch.stop(
+ watch_id="watch_id",
+ id="id",
+ )
+ assert watch is None
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_raw_response_stop(self, client: Kernel) -> None:
+ response = client.browsers.fs.watch.with_raw_response.stop(
+ watch_id="watch_id",
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ watch = response.parse()
+ assert watch is None
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_streaming_response_stop(self, client: Kernel) -> None:
+ with client.browsers.fs.watch.with_streaming_response.stop(
+ watch_id="watch_id",
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ watch = response.parse()
+ assert watch is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_path_params_stop(self, client: Kernel) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
+ client.browsers.fs.watch.with_raw_response.stop(
+ watch_id="watch_id",
+ id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `watch_id` but received ''"):
+ client.browsers.fs.watch.with_raw_response.stop(
+ watch_id="",
+ id="id",
+ )
+
+
+class TestAsyncWatch:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(
+ reason="currently no good way to test endpoints with content type text/event-stream, Prism mock server will fail"
+ )
+ @parametrize
+ async def test_method_events(self, async_client: AsyncKernel) -> None:
+ watch_stream = await async_client.browsers.fs.watch.events(
+ watch_id="watch_id",
+ id="id",
+ )
+ await watch_stream.response.aclose()
+
+ @pytest.mark.skip(
+ reason="currently no good way to test endpoints with content type text/event-stream, Prism mock server will fail"
+ )
+ @parametrize
+ async def test_raw_response_events(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.fs.watch.with_raw_response.events(
+ watch_id="watch_id",
+ id="id",
+ )
+
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ stream = await response.parse()
+ await stream.close()
+
+ @pytest.mark.skip(
+ reason="currently no good way to test endpoints with content type text/event-stream, Prism mock server will fail"
+ )
+ @parametrize
+ async def test_streaming_response_events(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.fs.watch.with_streaming_response.events(
+ watch_id="watch_id",
+ 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="currently no good way to test endpoints with content type text/event-stream, Prism mock server will fail"
+ )
+ @parametrize
+ async def test_path_params_events(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.watch.with_raw_response.events(
+ watch_id="watch_id",
+ id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `watch_id` but received ''"):
+ await async_client.browsers.fs.watch.with_raw_response.events(
+ watch_id="",
+ id="id",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_start(self, async_client: AsyncKernel) -> None:
+ watch = await async_client.browsers.fs.watch.start(
+ id="id",
+ path="path",
+ )
+ assert_matches_type(WatchStartResponse, watch, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_start_with_all_params(self, async_client: AsyncKernel) -> None:
+ watch = await async_client.browsers.fs.watch.start(
+ id="id",
+ path="path",
+ recursive=True,
+ )
+ assert_matches_type(WatchStartResponse, watch, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_raw_response_start(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.fs.watch.with_raw_response.start(
+ id="id",
+ path="path",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ watch = await response.parse()
+ assert_matches_type(WatchStartResponse, watch, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_streaming_response_start(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.fs.watch.with_streaming_response.start(
+ id="id",
+ path="path",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ watch = await response.parse()
+ assert_matches_type(WatchStartResponse, watch, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_path_params_start(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.watch.with_raw_response.start(
+ id="",
+ path="path",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_stop(self, async_client: AsyncKernel) -> None:
+ watch = await async_client.browsers.fs.watch.stop(
+ watch_id="watch_id",
+ id="id",
+ )
+ assert watch is None
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_raw_response_stop(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.fs.watch.with_raw_response.stop(
+ watch_id="watch_id",
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ watch = await response.parse()
+ assert watch is None
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_streaming_response_stop(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.fs.watch.with_streaming_response.stop(
+ watch_id="watch_id",
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ watch = await response.parse()
+ assert watch is None
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_path_params_stop(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.watch.with_raw_response.stop(
+ watch_id="watch_id",
+ id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `watch_id` but received ''"):
+ await async_client.browsers.fs.watch.with_raw_response.stop(
+ watch_id="",
+ id="id",
+ )
diff --git a/tests/api_resources/browsers/test_fs.py b/tests/api_resources/browsers/test_fs.py
new file mode 100644
index 0000000..c82e09d
--- /dev/null
+++ b/tests/api_resources/browsers/test_fs.py
@@ -0,0 +1,977 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import httpx
+import pytest
+from respx import MockRouter
+
+from kernel import Kernel, AsyncKernel
+from tests.utils import assert_matches_type
+from kernel._response import (
+ BinaryAPIResponse,
+ AsyncBinaryAPIResponse,
+ StreamedBinaryAPIResponse,
+ AsyncStreamedBinaryAPIResponse,
+)
+from kernel.types.browsers import (
+ FFileInfoResponse,
+ FListFilesResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestFs:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_create_directory(self, client: Kernel) -> None:
+ f = client.browsers.fs.create_directory(
+ id="id",
+ path="/J!",
+ )
+ assert f is None
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_create_directory_with_all_params(self, client: Kernel) -> None:
+ f = client.browsers.fs.create_directory(
+ id="id",
+ path="/J!",
+ mode="0611",
+ )
+ assert f is None
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_raw_response_create_directory(self, client: Kernel) -> None:
+ response = client.browsers.fs.with_raw_response.create_directory(
+ id="id",
+ path="/J!",
+ )
+
+ 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()
+ @parametrize
+ def test_streaming_response_create_directory(self, client: Kernel) -> None:
+ with client.browsers.fs.with_streaming_response.create_directory(
+ id="id",
+ path="/J!",
+ ) 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()
+ @parametrize
+ def test_path_params_create_directory(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.create_directory(
+ id="",
+ path="/J!",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_delete_directory(self, client: Kernel) -> None:
+ f = client.browsers.fs.delete_directory(
+ id="id",
+ path="/J!",
+ )
+ assert f is None
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_raw_response_delete_directory(self, client: Kernel) -> None:
+ response = client.browsers.fs.with_raw_response.delete_directory(
+ id="id",
+ path="/J!",
+ )
+
+ 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()
+ @parametrize
+ def test_streaming_response_delete_directory(self, client: Kernel) -> None:
+ with client.browsers.fs.with_streaming_response.delete_directory(
+ id="id",
+ path="/J!",
+ ) 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()
+ @parametrize
+ def test_path_params_delete_directory(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.delete_directory(
+ id="",
+ path="/J!",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_delete_file(self, client: Kernel) -> None:
+ f = client.browsers.fs.delete_file(
+ id="id",
+ path="/J!",
+ )
+ assert f is None
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_raw_response_delete_file(self, client: Kernel) -> None:
+ response = client.browsers.fs.with_raw_response.delete_file(
+ id="id",
+ path="/J!",
+ )
+
+ 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()
+ @parametrize
+ def test_streaming_response_delete_file(self, client: Kernel) -> None:
+ with client.browsers.fs.with_streaming_response.delete_file(
+ id="id",
+ path="/J!",
+ ) 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()
+ @parametrize
+ def test_path_params_delete_file(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.delete_file(
+ id="",
+ path="/J!",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_file_info(self, client: Kernel) -> None:
+ f = client.browsers.fs.file_info(
+ id="id",
+ path="/J!",
+ )
+ assert_matches_type(FFileInfoResponse, f, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_raw_response_file_info(self, client: Kernel) -> None:
+ response = client.browsers.fs.with_raw_response.file_info(
+ id="id",
+ path="/J!",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ f = response.parse()
+ assert_matches_type(FFileInfoResponse, f, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_streaming_response_file_info(self, client: Kernel) -> None:
+ with client.browsers.fs.with_streaming_response.file_info(
+ id="id",
+ path="/J!",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ f = response.parse()
+ assert_matches_type(FFileInfoResponse, f, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_path_params_file_info(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.file_info(
+ id="",
+ path="/J!",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_list_files(self, client: Kernel) -> None:
+ f = client.browsers.fs.list_files(
+ id="id",
+ path="/J!",
+ )
+ assert_matches_type(FListFilesResponse, f, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_raw_response_list_files(self, client: Kernel) -> None:
+ response = client.browsers.fs.with_raw_response.list_files(
+ id="id",
+ path="/J!",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ f = response.parse()
+ assert_matches_type(FListFilesResponse, f, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_streaming_response_list_files(self, client: Kernel) -> None:
+ with client.browsers.fs.with_streaming_response.list_files(
+ id="id",
+ path="/J!",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ f = response.parse()
+ assert_matches_type(FListFilesResponse, f, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_path_params_list_files(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.list_files(
+ id="",
+ path="/J!",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_move(self, client: Kernel) -> None:
+ f = client.browsers.fs.move(
+ id="id",
+ dest_path="/J!",
+ src_path="/J!",
+ )
+ assert f is None
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_raw_response_move(self, client: Kernel) -> None:
+ response = client.browsers.fs.with_raw_response.move(
+ id="id",
+ dest_path="/J!",
+ src_path="/J!",
+ )
+
+ 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()
+ @parametrize
+ def test_streaming_response_move(self, client: Kernel) -> None:
+ with client.browsers.fs.with_streaming_response.move(
+ id="id",
+ dest_path="/J!",
+ src_path="/J!",
+ ) 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()
+ @parametrize
+ def test_path_params_move(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.move(
+ id="",
+ dest_path="/J!",
+ src_path="/J!",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_method_read_file(self, client: Kernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/browsers/id/fs/read_file").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ f = client.browsers.fs.read_file(
+ 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)
+
+ @pytest.mark.skip()
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_raw_response_read_file(self, client: Kernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/browsers/id/fs/read_file").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+
+ f = client.browsers.fs.with_raw_response.read_file(
+ 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)
+
+ @pytest.mark.skip()
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_streaming_response_read_file(self, client: Kernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/browsers/id/fs/read_file").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ with client.browsers.fs.with_streaming_response.read_file(
+ 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
+
+ @pytest.mark.skip()
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_path_params_read_file(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.read_file(
+ id="",
+ path="/J!",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_set_file_permissions(self, client: Kernel) -> None:
+ f = client.browsers.fs.set_file_permissions(
+ id="id",
+ mode="0611",
+ path="/J!",
+ )
+ assert f is None
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_set_file_permissions_with_all_params(self, client: Kernel) -> None:
+ f = client.browsers.fs.set_file_permissions(
+ id="id",
+ mode="0611",
+ path="/J!",
+ group="group",
+ owner="owner",
+ )
+ assert f is None
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_raw_response_set_file_permissions(self, client: Kernel) -> None:
+ response = client.browsers.fs.with_raw_response.set_file_permissions(
+ id="id",
+ mode="0611",
+ path="/J!",
+ )
+
+ 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()
+ @parametrize
+ def test_streaming_response_set_file_permissions(self, client: Kernel) -> None:
+ with client.browsers.fs.with_streaming_response.set_file_permissions(
+ id="id",
+ mode="0611",
+ path="/J!",
+ ) 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()
+ @parametrize
+ def test_path_params_set_file_permissions(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.set_file_permissions(
+ id="",
+ mode="0611",
+ path="/J!",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_write_file(self, client: Kernel) -> None:
+ f = client.browsers.fs.write_file(
+ id="id",
+ contents=b"raw file contents",
+ path="/J!",
+ )
+ assert f is None
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_method_write_file_with_all_params(self, client: Kernel) -> None:
+ f = client.browsers.fs.write_file(
+ id="id",
+ contents=b"raw file contents",
+ path="/J!",
+ mode="0611",
+ )
+ assert f is None
+
+ @pytest.mark.skip()
+ @parametrize
+ def test_raw_response_write_file(self, client: Kernel) -> None:
+ response = client.browsers.fs.with_raw_response.write_file(
+ id="id",
+ contents=b"raw file contents",
+ path="/J!",
+ )
+
+ 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()
+ @parametrize
+ def test_streaming_response_write_file(self, client: Kernel) -> None:
+ with client.browsers.fs.with_streaming_response.write_file(
+ id="id",
+ contents=b"raw file contents",
+ path="/J!",
+ ) 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()
+ @parametrize
+ def test_path_params_write_file(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.write_file(
+ id="",
+ contents=b"raw file contents",
+ path="/J!",
+ )
+
+
+class TestAsyncFs:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_create_directory(self, async_client: AsyncKernel) -> None:
+ f = await async_client.browsers.fs.create_directory(
+ id="id",
+ path="/J!",
+ )
+ assert f is None
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_create_directory_with_all_params(self, async_client: AsyncKernel) -> None:
+ f = await async_client.browsers.fs.create_directory(
+ id="id",
+ path="/J!",
+ mode="0611",
+ )
+ assert f is None
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_raw_response_create_directory(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.fs.with_raw_response.create_directory(
+ id="id",
+ path="/J!",
+ )
+
+ 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()
+ @parametrize
+ async def test_streaming_response_create_directory(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.fs.with_streaming_response.create_directory(
+ id="id",
+ path="/J!",
+ ) 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()
+ @parametrize
+ async def test_path_params_create_directory(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.create_directory(
+ id="",
+ path="/J!",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_delete_directory(self, async_client: AsyncKernel) -> None:
+ f = await async_client.browsers.fs.delete_directory(
+ id="id",
+ path="/J!",
+ )
+ assert f is None
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_raw_response_delete_directory(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.fs.with_raw_response.delete_directory(
+ id="id",
+ path="/J!",
+ )
+
+ 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()
+ @parametrize
+ async def test_streaming_response_delete_directory(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.fs.with_streaming_response.delete_directory(
+ id="id",
+ path="/J!",
+ ) 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()
+ @parametrize
+ async def test_path_params_delete_directory(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.delete_directory(
+ id="",
+ path="/J!",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_delete_file(self, async_client: AsyncKernel) -> None:
+ f = await async_client.browsers.fs.delete_file(
+ id="id",
+ path="/J!",
+ )
+ assert f is None
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_raw_response_delete_file(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.fs.with_raw_response.delete_file(
+ id="id",
+ path="/J!",
+ )
+
+ 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()
+ @parametrize
+ async def test_streaming_response_delete_file(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.fs.with_streaming_response.delete_file(
+ id="id",
+ path="/J!",
+ ) 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()
+ @parametrize
+ async def test_path_params_delete_file(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.delete_file(
+ id="",
+ path="/J!",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_file_info(self, async_client: AsyncKernel) -> None:
+ f = await async_client.browsers.fs.file_info(
+ id="id",
+ path="/J!",
+ )
+ assert_matches_type(FFileInfoResponse, f, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_raw_response_file_info(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.fs.with_raw_response.file_info(
+ id="id",
+ path="/J!",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ f = await response.parse()
+ assert_matches_type(FFileInfoResponse, f, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_streaming_response_file_info(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.fs.with_streaming_response.file_info(
+ id="id",
+ path="/J!",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ f = await response.parse()
+ assert_matches_type(FFileInfoResponse, f, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_path_params_file_info(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.file_info(
+ id="",
+ path="/J!",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_list_files(self, async_client: AsyncKernel) -> None:
+ f = await async_client.browsers.fs.list_files(
+ id="id",
+ path="/J!",
+ )
+ assert_matches_type(FListFilesResponse, f, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_raw_response_list_files(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.fs.with_raw_response.list_files(
+ id="id",
+ path="/J!",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ f = await response.parse()
+ assert_matches_type(FListFilesResponse, f, path=["response"])
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_streaming_response_list_files(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.fs.with_streaming_response.list_files(
+ id="id",
+ path="/J!",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ f = await response.parse()
+ assert_matches_type(FListFilesResponse, f, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_path_params_list_files(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.list_files(
+ id="",
+ path="/J!",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_move(self, async_client: AsyncKernel) -> None:
+ f = await async_client.browsers.fs.move(
+ id="id",
+ dest_path="/J!",
+ src_path="/J!",
+ )
+ assert f is None
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_raw_response_move(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.fs.with_raw_response.move(
+ id="id",
+ dest_path="/J!",
+ src_path="/J!",
+ )
+
+ 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()
+ @parametrize
+ async def test_streaming_response_move(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.fs.with_streaming_response.move(
+ id="id",
+ dest_path="/J!",
+ src_path="/J!",
+ ) 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()
+ @parametrize
+ async def test_path_params_move(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.move(
+ id="",
+ dest_path="/J!",
+ src_path="/J!",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_method_read_file(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/browsers/id/fs/read_file").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ f = await async_client.browsers.fs.read_file(
+ 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)
+
+ @pytest.mark.skip()
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_raw_response_read_file(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/browsers/id/fs/read_file").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+
+ f = await async_client.browsers.fs.with_raw_response.read_file(
+ 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)
+
+ @pytest.mark.skip()
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_streaming_response_read_file(self, async_client: AsyncKernel, respx_mock: MockRouter) -> None:
+ respx_mock.get("/browsers/id/fs/read_file").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+ async with async_client.browsers.fs.with_streaming_response.read_file(
+ 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
+
+ @pytest.mark.skip()
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_path_params_read_file(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.read_file(
+ id="",
+ path="/J!",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_set_file_permissions(self, async_client: AsyncKernel) -> None:
+ f = await async_client.browsers.fs.set_file_permissions(
+ id="id",
+ mode="0611",
+ path="/J!",
+ )
+ assert f is None
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_set_file_permissions_with_all_params(self, async_client: AsyncKernel) -> None:
+ f = await async_client.browsers.fs.set_file_permissions(
+ id="id",
+ mode="0611",
+ path="/J!",
+ group="group",
+ owner="owner",
+ )
+ assert f is None
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_raw_response_set_file_permissions(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.fs.with_raw_response.set_file_permissions(
+ id="id",
+ mode="0611",
+ path="/J!",
+ )
+
+ 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()
+ @parametrize
+ async def test_streaming_response_set_file_permissions(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.fs.with_streaming_response.set_file_permissions(
+ id="id",
+ mode="0611",
+ path="/J!",
+ ) 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()
+ @parametrize
+ async def test_path_params_set_file_permissions(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.set_file_permissions(
+ id="",
+ mode="0611",
+ path="/J!",
+ )
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_write_file(self, async_client: AsyncKernel) -> None:
+ f = await async_client.browsers.fs.write_file(
+ id="id",
+ contents=b"raw file contents",
+ path="/J!",
+ )
+ assert f is None
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_method_write_file_with_all_params(self, async_client: AsyncKernel) -> None:
+ f = await async_client.browsers.fs.write_file(
+ id="id",
+ contents=b"raw file contents",
+ path="/J!",
+ mode="0611",
+ )
+ assert f is None
+
+ @pytest.mark.skip()
+ @parametrize
+ async def test_raw_response_write_file(self, async_client: AsyncKernel) -> None:
+ response = await async_client.browsers.fs.with_raw_response.write_file(
+ id="id",
+ contents=b"raw file contents",
+ path="/J!",
+ )
+
+ 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()
+ @parametrize
+ async def test_streaming_response_write_file(self, async_client: AsyncKernel) -> None:
+ async with async_client.browsers.fs.with_streaming_response.write_file(
+ id="id",
+ contents=b"raw file contents",
+ path="/J!",
+ ) 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()
+ @parametrize
+ async def test_path_params_write_file(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.write_file(
+ id="",
+ contents=b"raw file contents",
+ path="/J!",
+ )
From b3cb7da385f70061f4c37e748e0b1bf69c6b77c2 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 8 Aug 2025 11:18:20 +0000
Subject: [PATCH 3/3] release: 0.9.0
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 13 +++++++++++++
pyproject.toml | 2 +-
src/kernel/_version.py | 2 +-
4 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index a3bdfd2..6d78745 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.8.3"
+ ".": "0.9.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8fe9a35..fddb4f5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,18 @@
# Changelog
+## 0.9.0 (2025-08-08)
+
+Full Changelog: [v0.8.3...v0.9.0](https://github.com/onkernel/kernel-python-sdk/compare/v0.8.3...v0.9.0)
+
+### Features
+
+* **api:** browser instance file i/o ([14667cd](https://github.com/onkernel/kernel-python-sdk/commit/14667cdfd06540585ffac570b8963b322cf9ef23))
+
+
+### Chores
+
+* **internal:** fix ruff target version ([07b55e4](https://github.com/onkernel/kernel-python-sdk/commit/07b55e4a4b65b403b3b6f69b95b221e2020cf30b))
+
## 0.8.3 (2025-08-01)
Full Changelog: [v0.8.2...v0.8.3](https://github.com/onkernel/kernel-python-sdk/compare/v0.8.2...v0.8.3)
diff --git a/pyproject.toml b/pyproject.toml
index a4c4b29..49f04c9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "kernel"
-version = "0.8.3"
+version = "0.9.0"
description = "The official Python library for the kernel API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/kernel/_version.py b/src/kernel/_version.py
index 98240d7..a21b043 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.8.3" # x-release-please-version
+__version__ = "0.9.0" # x-release-please-version