diff --git a/.stats.yml b/.stats.yml index 9f3693352..b14afa98a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ -configured_endpoints: 29 +configured_endpoints: 32 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-6cf047d01929db28b7655b33123900eeb28f3c5c12878b4a6d59676d839e8ccc.yml diff --git a/api.md b/api.md index 48c1ca6f7..16dff227f 100644 --- a/api.md +++ b/api.md @@ -50,6 +50,7 @@ from runloop_api_client.types import ( DevboxListView, DevboxSnapshotListView, DevboxSnapshotView, + DevboxTunnelView, DevboxView, DevboxCreateSSHKeyResponse, DevboxReadFileContentsResponse, @@ -63,13 +64,16 @@ Methods: - client.devboxes.retrieve(id) -> DevboxView - client.devboxes.list(\*\*params) -> DevboxListView - client.devboxes.create_ssh_key(id) -> DevboxCreateSSHKeyResponse +- client.devboxes.create_tunnel(id) -> DevboxTunnelView - client.devboxes.disk_snapshots(\*\*params) -> DevboxSnapshotListView - client.devboxes.download_file(id, \*\*params) -> BinaryAPIResponse - client.devboxes.execute_async(id, \*\*params) -> DevboxAsyncExecutionDetailView - client.devboxes.execute_sync(id, \*\*params) -> DevboxExecutionDetailView - client.devboxes.read_file_contents(id, \*\*params) -> str +- client.devboxes.resume(id) -> DevboxView - client.devboxes.shutdown(id) -> DevboxView - client.devboxes.snapshot_disk(id, \*\*params) -> DevboxSnapshotView +- client.devboxes.suspend(id) -> DevboxView - client.devboxes.upload_file(id, \*\*params) -> object - client.devboxes.write_file(id, \*\*params) -> DevboxExecutionDetailView diff --git a/src/runloop_api_client/resources/devboxes/devboxes.py b/src/runloop_api_client/resources/devboxes/devboxes.py index 36c719ff2..0ac6caf62 100644 --- a/src/runloop_api_client/resources/devboxes/devboxes.py +++ b/src/runloop_api_client/resources/devboxes/devboxes.py @@ -60,6 +60,7 @@ from ..._base_client import make_request_options from ...types.devbox_view import DevboxView from ...types.devbox_list_view import DevboxListView +from ...types.devbox_tunnel_view import DevboxTunnelView from ...types.devbox_snapshot_view import DevboxSnapshotView from ...types.devbox_snapshot_list_view import DevboxSnapshotListView from ...types.code_mount_parameters_param import CodeMountParametersParam @@ -314,6 +315,39 @@ def create_ssh_key( cast_to=DevboxCreateSSHKeyResponse, ) + def create_tunnel( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> DevboxTunnelView: + """ + Create a tunnel to an available port on the Devbox. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/v1/devboxes/{id}/create_tunnel", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DevboxTunnelView, + ) + def disk_snapshots( self, *, @@ -530,6 +564,39 @@ def read_file_contents( cast_to=str, ) + def resume( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> DevboxView: + """ + Resume a suspended devbox by id. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/v1/devboxes/{id}/resume", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DevboxView, + ) + def shutdown( self, id: str, @@ -610,6 +677,40 @@ def snapshot_disk( cast_to=DevboxSnapshotView, ) + def suspend( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> DevboxView: + """Suspend a devbox by id. + + This will take the devbox out of service. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/v1/devboxes/{id}/suspend", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DevboxView, + ) + def upload_file( self, id: str, @@ -948,6 +1049,39 @@ async def create_ssh_key( cast_to=DevboxCreateSSHKeyResponse, ) + async def create_tunnel( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> DevboxTunnelView: + """ + Create a tunnel to an available port on the Devbox. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/v1/devboxes/{id}/create_tunnel", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DevboxTunnelView, + ) + async def disk_snapshots( self, *, @@ -1164,6 +1298,39 @@ async def read_file_contents( cast_to=str, ) + async def resume( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> DevboxView: + """ + Resume a suspended devbox by id. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/v1/devboxes/{id}/resume", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DevboxView, + ) + async def shutdown( self, id: str, @@ -1244,6 +1411,40 @@ async def snapshot_disk( cast_to=DevboxSnapshotView, ) + async def suspend( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> DevboxView: + """Suspend a devbox by id. + + This will take the devbox out of service. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/v1/devboxes/{id}/suspend", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DevboxView, + ) + async def upload_file( self, id: str, @@ -1355,6 +1556,9 @@ def __init__(self, devboxes: DevboxesResource) -> None: self.create_ssh_key = to_raw_response_wrapper( devboxes.create_ssh_key, ) + self.create_tunnel = to_raw_response_wrapper( + devboxes.create_tunnel, + ) self.disk_snapshots = to_raw_response_wrapper( devboxes.disk_snapshots, ) @@ -1371,12 +1575,18 @@ def __init__(self, devboxes: DevboxesResource) -> None: self.read_file_contents = to_raw_response_wrapper( devboxes.read_file_contents, ) + self.resume = to_raw_response_wrapper( + devboxes.resume, + ) self.shutdown = to_raw_response_wrapper( devboxes.shutdown, ) self.snapshot_disk = to_raw_response_wrapper( devboxes.snapshot_disk, ) + self.suspend = to_raw_response_wrapper( + devboxes.suspend, + ) self.upload_file = to_raw_response_wrapper( devboxes.upload_file, ) @@ -1409,6 +1619,9 @@ def __init__(self, devboxes: AsyncDevboxesResource) -> None: self.create_ssh_key = async_to_raw_response_wrapper( devboxes.create_ssh_key, ) + self.create_tunnel = async_to_raw_response_wrapper( + devboxes.create_tunnel, + ) self.disk_snapshots = async_to_raw_response_wrapper( devboxes.disk_snapshots, ) @@ -1425,12 +1638,18 @@ def __init__(self, devboxes: AsyncDevboxesResource) -> None: self.read_file_contents = async_to_raw_response_wrapper( devboxes.read_file_contents, ) + self.resume = async_to_raw_response_wrapper( + devboxes.resume, + ) self.shutdown = async_to_raw_response_wrapper( devboxes.shutdown, ) self.snapshot_disk = async_to_raw_response_wrapper( devboxes.snapshot_disk, ) + self.suspend = async_to_raw_response_wrapper( + devboxes.suspend, + ) self.upload_file = async_to_raw_response_wrapper( devboxes.upload_file, ) @@ -1463,6 +1682,9 @@ def __init__(self, devboxes: DevboxesResource) -> None: self.create_ssh_key = to_streamed_response_wrapper( devboxes.create_ssh_key, ) + self.create_tunnel = to_streamed_response_wrapper( + devboxes.create_tunnel, + ) self.disk_snapshots = to_streamed_response_wrapper( devboxes.disk_snapshots, ) @@ -1479,12 +1701,18 @@ def __init__(self, devboxes: DevboxesResource) -> None: self.read_file_contents = to_streamed_response_wrapper( devboxes.read_file_contents, ) + self.resume = to_streamed_response_wrapper( + devboxes.resume, + ) self.shutdown = to_streamed_response_wrapper( devboxes.shutdown, ) self.snapshot_disk = to_streamed_response_wrapper( devboxes.snapshot_disk, ) + self.suspend = to_streamed_response_wrapper( + devboxes.suspend, + ) self.upload_file = to_streamed_response_wrapper( devboxes.upload_file, ) @@ -1517,6 +1745,9 @@ def __init__(self, devboxes: AsyncDevboxesResource) -> None: self.create_ssh_key = async_to_streamed_response_wrapper( devboxes.create_ssh_key, ) + self.create_tunnel = async_to_streamed_response_wrapper( + devboxes.create_tunnel, + ) self.disk_snapshots = async_to_streamed_response_wrapper( devboxes.disk_snapshots, ) @@ -1533,12 +1764,18 @@ def __init__(self, devboxes: AsyncDevboxesResource) -> None: self.read_file_contents = async_to_streamed_response_wrapper( devboxes.read_file_contents, ) + self.resume = async_to_streamed_response_wrapper( + devboxes.resume, + ) self.shutdown = async_to_streamed_response_wrapper( devboxes.shutdown, ) self.snapshot_disk = async_to_streamed_response_wrapper( devboxes.snapshot_disk, ) + self.suspend = async_to_streamed_response_wrapper( + devboxes.suspend, + ) self.upload_file = async_to_streamed_response_wrapper( devboxes.upload_file, ) diff --git a/src/runloop_api_client/types/__init__.py b/src/runloop_api_client/types/__init__.py index a5fe07214..3d10e51f1 100644 --- a/src/runloop_api_client/types/__init__.py +++ b/src/runloop_api_client/types/__init__.py @@ -12,6 +12,7 @@ from .devbox_list_view import DevboxListView as DevboxListView from .project_list_view import ProjectListView as ProjectListView from .devbox_list_params import DevboxListParams as DevboxListParams +from .devbox_tunnel_view import DevboxTunnelView as DevboxTunnelView from .function_list_view import FunctionListView as FunctionListView from .blueprint_build_log import BlueprintBuildLog as BlueprintBuildLog from .blueprint_list_view import BlueprintListView as BlueprintListView diff --git a/src/runloop_api_client/types/devbox_tunnel_view.py b/src/runloop_api_client/types/devbox_tunnel_view.py new file mode 100644 index 000000000..ac47c82a0 --- /dev/null +++ b/src/runloop_api_client/types/devbox_tunnel_view.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + +from .._models import BaseModel + +__all__ = ["DevboxTunnelView"] + + +class DevboxTunnelView(BaseModel): + devbox_id: str + """ID of the Devbox the tunnel routes to.""" + + port: int + """Port of the Devbox the tunnel routes to.""" + + url: str + """Public url used to access Devbox.""" diff --git a/tests/api_resources/test_devboxes.py b/tests/api_resources/test_devboxes.py index c9937b4dc..45031943d 100644 --- a/tests/api_resources/test_devboxes.py +++ b/tests/api_resources/test_devboxes.py @@ -14,6 +14,7 @@ from runloop_api_client.types import ( DevboxView, DevboxListView, + DevboxTunnelView, DevboxSnapshotView, DevboxSnapshotListView, DevboxExecutionDetailView, @@ -210,6 +211,44 @@ def test_path_params_create_ssh_key(self, client: Runloop) -> None: "", ) + @parametrize + def test_method_create_tunnel(self, client: Runloop) -> None: + devbox = client.devboxes.create_tunnel( + "id", + ) + assert_matches_type(DevboxTunnelView, devbox, path=["response"]) + + @parametrize + def test_raw_response_create_tunnel(self, client: Runloop) -> None: + response = client.devboxes.with_raw_response.create_tunnel( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + devbox = response.parse() + assert_matches_type(DevboxTunnelView, devbox, path=["response"]) + + @parametrize + def test_streaming_response_create_tunnel(self, client: Runloop) -> None: + with client.devboxes.with_streaming_response.create_tunnel( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + devbox = response.parse() + assert_matches_type(DevboxTunnelView, devbox, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create_tunnel(self, client: Runloop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.devboxes.with_raw_response.create_tunnel( + "", + ) + @parametrize def test_method_disk_snapshots(self, client: Runloop) -> None: devbox = client.devboxes.disk_snapshots() @@ -445,6 +484,44 @@ def test_path_params_read_file_contents(self, client: Runloop) -> None: file_path="file_path", ) + @parametrize + def test_method_resume(self, client: Runloop) -> None: + devbox = client.devboxes.resume( + "id", + ) + assert_matches_type(DevboxView, devbox, path=["response"]) + + @parametrize + def test_raw_response_resume(self, client: Runloop) -> None: + response = client.devboxes.with_raw_response.resume( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + devbox = response.parse() + assert_matches_type(DevboxView, devbox, path=["response"]) + + @parametrize + def test_streaming_response_resume(self, client: Runloop) -> None: + with client.devboxes.with_streaming_response.resume( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + devbox = response.parse() + assert_matches_type(DevboxView, devbox, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_resume(self, client: Runloop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.devboxes.with_raw_response.resume( + "", + ) + @parametrize def test_method_shutdown(self, client: Runloop) -> None: devbox = client.devboxes.shutdown( @@ -530,6 +607,44 @@ def test_path_params_snapshot_disk(self, client: Runloop) -> None: id="", ) + @parametrize + def test_method_suspend(self, client: Runloop) -> None: + devbox = client.devboxes.suspend( + "id", + ) + assert_matches_type(DevboxView, devbox, path=["response"]) + + @parametrize + def test_raw_response_suspend(self, client: Runloop) -> None: + response = client.devboxes.with_raw_response.suspend( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + devbox = response.parse() + assert_matches_type(DevboxView, devbox, path=["response"]) + + @parametrize + def test_streaming_response_suspend(self, client: Runloop) -> None: + with client.devboxes.with_streaming_response.suspend( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + devbox = response.parse() + assert_matches_type(DevboxView, devbox, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_suspend(self, client: Runloop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.devboxes.with_raw_response.suspend( + "", + ) + @parametrize def test_method_upload_file(self, client: Runloop) -> None: devbox = client.devboxes.upload_file( @@ -804,6 +919,44 @@ async def test_path_params_create_ssh_key(self, async_client: AsyncRunloop) -> N "", ) + @parametrize + async def test_method_create_tunnel(self, async_client: AsyncRunloop) -> None: + devbox = await async_client.devboxes.create_tunnel( + "id", + ) + assert_matches_type(DevboxTunnelView, devbox, path=["response"]) + + @parametrize + async def test_raw_response_create_tunnel(self, async_client: AsyncRunloop) -> None: + response = await async_client.devboxes.with_raw_response.create_tunnel( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + devbox = await response.parse() + assert_matches_type(DevboxTunnelView, devbox, path=["response"]) + + @parametrize + async def test_streaming_response_create_tunnel(self, async_client: AsyncRunloop) -> None: + async with async_client.devboxes.with_streaming_response.create_tunnel( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + devbox = await response.parse() + assert_matches_type(DevboxTunnelView, devbox, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create_tunnel(self, async_client: AsyncRunloop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.devboxes.with_raw_response.create_tunnel( + "", + ) + @parametrize async def test_method_disk_snapshots(self, async_client: AsyncRunloop) -> None: devbox = await async_client.devboxes.disk_snapshots() @@ -1039,6 +1192,44 @@ async def test_path_params_read_file_contents(self, async_client: AsyncRunloop) file_path="file_path", ) + @parametrize + async def test_method_resume(self, async_client: AsyncRunloop) -> None: + devbox = await async_client.devboxes.resume( + "id", + ) + assert_matches_type(DevboxView, devbox, path=["response"]) + + @parametrize + async def test_raw_response_resume(self, async_client: AsyncRunloop) -> None: + response = await async_client.devboxes.with_raw_response.resume( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + devbox = await response.parse() + assert_matches_type(DevboxView, devbox, path=["response"]) + + @parametrize + async def test_streaming_response_resume(self, async_client: AsyncRunloop) -> None: + async with async_client.devboxes.with_streaming_response.resume( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + devbox = await response.parse() + assert_matches_type(DevboxView, devbox, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_resume(self, async_client: AsyncRunloop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.devboxes.with_raw_response.resume( + "", + ) + @parametrize async def test_method_shutdown(self, async_client: AsyncRunloop) -> None: devbox = await async_client.devboxes.shutdown( @@ -1124,6 +1315,44 @@ async def test_path_params_snapshot_disk(self, async_client: AsyncRunloop) -> No id="", ) + @parametrize + async def test_method_suspend(self, async_client: AsyncRunloop) -> None: + devbox = await async_client.devboxes.suspend( + "id", + ) + assert_matches_type(DevboxView, devbox, path=["response"]) + + @parametrize + async def test_raw_response_suspend(self, async_client: AsyncRunloop) -> None: + response = await async_client.devboxes.with_raw_response.suspend( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + devbox = await response.parse() + assert_matches_type(DevboxView, devbox, path=["response"]) + + @parametrize + async def test_streaming_response_suspend(self, async_client: AsyncRunloop) -> None: + async with async_client.devboxes.with_streaming_response.suspend( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + devbox = await response.parse() + assert_matches_type(DevboxView, devbox, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_suspend(self, async_client: AsyncRunloop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.devboxes.with_raw_response.suspend( + "", + ) + @parametrize async def test_method_upload_file(self, async_client: AsyncRunloop) -> None: devbox = await async_client.devboxes.upload_file(