Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "1.4.0"
".": "1.5.0"
}
4 changes: 2 additions & 2 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 111
configured_endpoints: 112
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-84e997ca5716b9378a58a1bdf3d6616cf3be80156a6aaed1bed469fe93ba2c95.yml
openapi_spec_hash: b44a4ba1c2c3cb775c14545f2bab05a8
config_hash: 22f65246be4646c23dde9f69f51252e7
config_hash: 6c26299fd9ef01fb4713612a9a2ad17c
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## 1.5.0 (2026-01-30)

Full Changelog: [v1.4.0...v1.5.0](https://github.com/runloopai/api-client-python/compare/v1.4.0...v1.5.0)

### Features

* **devbox:** add enable_tunnel API ([#7236](https://github.com/runloopai/api-client-python/issues/7236)) ([bb58bfc](https://github.com/runloopai/api-client-python/commit/bb58bfc40c93c5832634ddb82c255ada7214c7f4))

## 1.4.0 (2026-01-30)

Full Changelog: [v1.3.2...v1.4.0](https://github.com/runloopai/api-client-python/compare/v1.3.2...v1.4.0)
Expand Down
2 changes: 2 additions & 0 deletions api.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ from runloop_api_client.types import (
DevboxSnapshotView,
DevboxTunnelView,
DevboxView,
TunnelView,
DevboxCreateSSHKeyResponse,
DevboxReadFileContentsResponse,
)
Expand All @@ -148,6 +149,7 @@ Methods:
- <code title="post /v1/devboxes/{id}/create_tunnel">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">create_tunnel</a>(id, \*\*<a href="src/runloop_api_client/types/devbox_create_tunnel_params.py">params</a>) -> <a href="./src/runloop_api_client/types/devbox_tunnel_view.py">DevboxTunnelView</a></code>
- <code title="post /v1/devboxes/disk_snapshots/{id}/delete">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">delete_disk_snapshot</a>(id) -> object</code>
- <code title="post /v1/devboxes/{id}/download_file">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">download_file</a>(id, \*\*<a href="src/runloop_api_client/types/devbox_download_file_params.py">params</a>) -> BinaryAPIResponse</code>
- <code title="post /v1/devboxes/{id}/enable_tunnel">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">enable_tunnel</a>(id, \*\*<a href="src/runloop_api_client/types/devbox_enable_tunnel_params.py">params</a>) -> <a href="./src/runloop_api_client/types/tunnel_view.py">TunnelView</a></code>
- <code title="post /v1/devboxes/{id}/execute">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">execute</a>(id, \*\*<a href="src/runloop_api_client/types/devbox_execute_params.py">params</a>) -> <a href="./src/runloop_api_client/types/devbox_async_execution_detail_view.py">DevboxAsyncExecutionDetailView</a></code>
- <code title="post /v1/devboxes/{id}/execute_async">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">execute_async</a>(id, \*\*<a href="src/runloop_api_client/types/devbox_execute_async_params.py">params</a>) -> <a href="./src/runloop_api_client/types/devbox_async_execution_detail_view.py">DevboxAsyncExecutionDetailView</a></code>
- <code title="post /v1/devboxes/{id}/execute_sync">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">execute_sync</a>(id, \*\*<a href="src/runloop_api_client/types/devbox_execute_sync_params.py">params</a>) -> <a href="./src/runloop_api_client/types/devbox_execution_detail_view.py">DevboxExecutionDetailView</a></code>
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "runloop_api_client"
version = "1.4.0"
version = "1.5.0"
description = "The official Python library for the runloop API"
dynamic = ["readme"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion src/runloop_api_client/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

__title__ = "runloop_api_client"
__version__ = "1.4.0" # x-release-please-version
__version__ = "1.5.0" # x-release-please-version
114 changes: 114 additions & 0 deletions src/runloop_api_client/resources/devboxes/devboxes.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
devbox_execute_sync_params,
devbox_create_tunnel_params,
devbox_download_file_params,
devbox_enable_tunnel_params,
devbox_execute_async_params,
devbox_remove_tunnel_params,
devbox_snapshot_disk_params,
Expand Down Expand Up @@ -99,6 +100,7 @@
)
from ...lib.polling_async import async_poll_until
from ...types.devbox_view import DevboxView
from ...types.tunnel_view import TunnelView
from ...types.devbox_tunnel_view import DevboxTunnelView
from ...types.shared_params.mount import Mount
from ...types.devbox_snapshot_view import DevboxSnapshotView
Expand Down Expand Up @@ -787,6 +789,55 @@ def download_file(
cast_to=BinaryAPIResponse,
)

def enable_tunnel(
self,
id: str,
*,
auth_mode: Optional[Literal["open", "authenticated"]] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
idempotency_key: str | None = None,
) -> TunnelView:
"""Create a V2 tunnel for an existing running Devbox.

Tunnels provide encrypted
URL-based access to the Devbox without exposing internal IDs. The tunnel URL
format is: https://{port}-{tunnel_key}.tunnel.runloop.ai

Each Devbox can have one tunnel.

Args:
auth_mode: Authentication mode for the tunnel. Defaults to 'public' if not specified.

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

idempotency_key: Specify a custom idempotency key for this request
"""
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return self._post(
f"/v1/devboxes/{id}/enable_tunnel",
body=maybe_transform({"auth_mode": auth_mode}, devbox_enable_tunnel_params.DevboxEnableTunnelParams),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
idempotency_key=idempotency_key,
),
cast_to=TunnelView,
)

def execute(
self,
id: str,
Expand Down Expand Up @@ -2347,6 +2398,57 @@ async def download_file(
cast_to=AsyncBinaryAPIResponse,
)

async def enable_tunnel(
self,
id: str,
*,
auth_mode: Optional[Literal["open", "authenticated"]] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
idempotency_key: str | None = None,
) -> TunnelView:
"""Create a V2 tunnel for an existing running Devbox.

Tunnels provide encrypted
URL-based access to the Devbox without exposing internal IDs. The tunnel URL
format is: https://{port}-{tunnel_key}.tunnel.runloop.ai

Each Devbox can have one tunnel.

Args:
auth_mode: Authentication mode for the tunnel. Defaults to 'public' if not specified.

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

idempotency_key: Specify a custom idempotency key for this request
"""
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}/enable_tunnel",
body=await async_maybe_transform(
{"auth_mode": auth_mode}, devbox_enable_tunnel_params.DevboxEnableTunnelParams
),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
idempotency_key=idempotency_key,
),
cast_to=TunnelView,
)

async def execute(
self,
id: str,
Expand Down Expand Up @@ -3293,6 +3395,9 @@ def __init__(self, devboxes: DevboxesResource) -> None:
devboxes.download_file,
BinaryAPIResponse,
)
self.enable_tunnel = to_raw_response_wrapper(
devboxes.enable_tunnel,
)
self.execute = to_raw_response_wrapper(
devboxes.execute,
)
Expand Down Expand Up @@ -3395,6 +3500,9 @@ def __init__(self, devboxes: AsyncDevboxesResource) -> None:
devboxes.download_file,
AsyncBinaryAPIResponse,
)
self.enable_tunnel = async_to_raw_response_wrapper(
devboxes.enable_tunnel,
)
self.execute = async_to_raw_response_wrapper(
devboxes.execute,
)
Expand Down Expand Up @@ -3497,6 +3605,9 @@ def __init__(self, devboxes: DevboxesResource) -> None:
devboxes.download_file,
StreamedBinaryAPIResponse,
)
self.enable_tunnel = to_streamed_response_wrapper(
devboxes.enable_tunnel,
)
self.execute = to_streamed_response_wrapper(
devboxes.execute,
)
Expand Down Expand Up @@ -3599,6 +3710,9 @@ def __init__(self, devboxes: AsyncDevboxesResource) -> None:
devboxes.download_file,
AsyncStreamedBinaryAPIResponse,
)
self.enable_tunnel = async_to_streamed_response_wrapper(
devboxes.enable_tunnel,
)
self.execute = async_to_streamed_response_wrapper(
devboxes.execute,
)
Expand Down
2 changes: 2 additions & 0 deletions src/runloop_api_client/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from .devbox_view import DevboxView as DevboxView
from .object_view import ObjectView as ObjectView
from .secret_view import SecretView as SecretView
from .tunnel_view import TunnelView as TunnelView
from .input_context import InputContext as InputContext
from .scenario_view import ScenarioView as ScenarioView
from .benchmark_view import BenchmarkView as BenchmarkView
Expand Down Expand Up @@ -91,6 +92,7 @@
from .benchmark_job_create_params import BenchmarkJobCreateParams as BenchmarkJobCreateParams
from .devbox_create_tunnel_params import DevboxCreateTunnelParams as DevboxCreateTunnelParams
from .devbox_download_file_params import DevboxDownloadFileParams as DevboxDownloadFileParams
from .devbox_enable_tunnel_params import DevboxEnableTunnelParams as DevboxEnableTunnelParams
from .devbox_execute_async_params import DevboxExecuteAsyncParams as DevboxExecuteAsyncParams
from .devbox_remove_tunnel_params import DevboxRemoveTunnelParams as DevboxRemoveTunnelParams
from .devbox_snapshot_disk_params import DevboxSnapshotDiskParams as DevboxSnapshotDiskParams
Expand Down
13 changes: 13 additions & 0 deletions src/runloop_api_client/types/devbox_enable_tunnel_params.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

from __future__ import annotations

from typing import Optional
from typing_extensions import Literal, TypedDict

__all__ = ["DevboxEnableTunnelParams"]


class DevboxEnableTunnelParams(TypedDict, total=False):
auth_mode: Optional[Literal["open", "authenticated"]]
"""Authentication mode for the tunnel. Defaults to 'public' if not specified."""
29 changes: 3 additions & 26 deletions src/runloop_api_client/types/devbox_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
from typing_extensions import Literal

from .._models import BaseModel
from .tunnel_view import TunnelView
from .shared.launch_parameters import LaunchParameters

__all__ = ["DevboxView", "StateTransition", "GatewaySpecs", "Tunnel"]
__all__ = ["DevboxView", "StateTransition", "GatewaySpecs"]


class StateTransition(BaseModel):
Expand Down Expand Up @@ -38,30 +39,6 @@ class GatewaySpecs(BaseModel):
"""The ID of the secret containing the credential."""


class Tunnel(BaseModel):
"""
V2 tunnel information if a tunnel was created at launch time or via the createTunnel API.
"""

auth_mode: Literal["public_", "authenticated"]
"""The authentication mode for the tunnel."""

create_time_ms: int
"""Creation time of the tunnel (Unix timestamp milliseconds)."""

tunnel_key: str
"""The encrypted tunnel key used to construct the tunnel URL.

URL format: https://{port}-{tunnel_key}.tunnel.runloop.{domain}
"""

auth_token: Optional[str] = None
"""Bearer token for tunnel authentication.

Only present when auth_mode is 'authenticated'.
"""


class DevboxView(BaseModel):
"""A Devbox represents a virtual development environment.

Expand Down Expand Up @@ -137,7 +114,7 @@ class DevboxView(BaseModel):
Snapshot.
"""

tunnel: Optional[Tunnel] = None
tunnel: Optional[TunnelView] = None
"""
V2 tunnel information if a tunnel was created at launch time or via the
createTunnel API.
Expand Down
34 changes: 34 additions & 0 deletions src/runloop_api_client/types/tunnel_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# 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__ = ["TunnelView"]


class TunnelView(BaseModel):
"""A V2 tunnel provides secure HTTP access to services running on a Devbox.

Tunnels allow external clients to reach web servers, APIs, or other HTTP services running inside a Devbox without requiring direct network access. Each tunnel is uniquely identified by an encrypted tunnel_key and can be configured for either open (public) or authenticated access.
Usage: https://{port}-{tunnel_key}.tunnel.runloop.ai
"""

auth_mode: Literal["public_", "authenticated"]
"""The authentication mode for the tunnel."""

create_time_ms: int
"""Creation time of the tunnel (Unix timestamp milliseconds)."""

tunnel_key: str
"""The encrypted tunnel key used to construct the tunnel URL.

URL format: https://{port}-{tunnel_key}.tunnel.runloop.{domain}
"""

auth_token: Optional[str] = None
"""Bearer token for tunnel authentication.

Only present when auth_mode is 'authenticated'.
"""
Loading