Skip to content
Draft
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: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ dependencies = [
"distro>=1.7.0, <2",
"sniffio",
"uuid-utils>=0.11.0",
"websocket-client>=1.8.0, <2",
"websockets>=13.0, <16",
]

requires-python = ">= 3.9"
Expand Down
32 changes: 30 additions & 2 deletions requirements-dev.lock
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ certifi==2026.1.4
# httpx
colorama==0.4.6 ; sys_platform == 'win32'
# via pytest
coverage==7.10.7 ; python_full_version < '3.10'
# via pytest-cov
coverage==7.13.4 ; python_full_version >= '3.10'
# via pytest-cov
dirty-equals==0.11
distro==1.9.0
# via runloop-api-client
Expand All @@ -26,12 +30,18 @@ execnet==2.1.2
# via pytest-xdist
h11==0.16.0
# via httpcore
h2==4.3.0
# via httpx
hpack==4.1.0
# via h2
httpcore==1.0.9
# via httpx
httpx==0.28.1
# via
# respx
# runloop-api-client
hyperframe==6.1.0
# via h2
idna==3.11
# via
# anyio
Expand All @@ -57,7 +67,9 @@ packaging==25.0
pathspec==1.0.3
# via mypy
pluggy==1.6.0
# via pytest
# via
# pytest
# pytest-cov
pydantic==2.12.5
# via runloop-api-client
pydantic-core==2.41.5
Expand All @@ -70,16 +82,25 @@ pyright==1.1.399
pytest==8.4.2 ; python_full_version < '3.10'
# via
# pytest-asyncio
# pytest-cov
# pytest-timeout
# pytest-xdist
pytest==9.0.2 ; python_full_version >= '3.10'
# via
# pytest-asyncio
# pytest-cov
# pytest-timeout
# pytest-xdist
pytest-asyncio==1.2.0 ; python_full_version < '3.10'
pytest-asyncio==1.3.0 ; python_full_version >= '3.10'
pytest-cov==7.0.0
pytest-timeout==2.4.0
pytest-xdist==3.8.0
python-dateutil==2.9.0.post0 ; python_full_version < '3.10'
# via time-machine
python-frontmatter==1.1.0
pyyaml==6.0.3
# via python-frontmatter
respx==0.22.0
rich==14.2.0
ruff==0.14.13
Expand All @@ -89,8 +110,9 @@ sniffio==1.3.1
# via runloop-api-client
time-machine==2.19.0 ; python_full_version < '3.10'
time-machine==3.2.0 ; python_full_version >= '3.10'
tomli==2.4.0 ; python_full_version < '3.11'
tomli==2.4.0 ; python_full_version <= '3.11'
# via
# coverage
# mypy
# pytest
typing-extensions==4.15.0
Expand All @@ -106,5 +128,11 @@ typing-extensions==4.15.0
# typing-inspection
typing-inspection==0.4.2
# via pydantic
uuid-utils==0.14.1
# via runloop-api-client
websocket-client==1.9.0
# via runloop-api-client
websockets==15.0.1
# via runloop-api-client
zipp==3.23.0
# via importlib-metadata
38 changes: 38 additions & 0 deletions src/runloop_api_client/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

if TYPE_CHECKING:
from .resources import (
pty,
axons,
agents,
apikeys,
Expand All @@ -52,6 +53,7 @@
restricted_keys,
network_policies,
)
from .resources.pty import PtyResource, AsyncPtyResource
from .resources.agents import AgentsResource, AsyncAgentsResource
from .resources.apikeys import ApikeysResource, AsyncApikeysResource
from .resources.objects import ObjectsResource, AsyncObjectsResource
Expand Down Expand Up @@ -184,6 +186,12 @@ def devboxes(self) -> DevboxesResource:

return DevboxesResource(self)

@cached_property
def pty(self) -> PtyResource:
from .resources.pty import PtyResource

return PtyResource(self)

@cached_property
def scenarios(self) -> ScenariosResource:
from .resources.scenarios import ScenariosResource
Expand Down Expand Up @@ -466,6 +474,12 @@ def devboxes(self) -> AsyncDevboxesResource:

return AsyncDevboxesResource(self)

@cached_property
def pty(self) -> AsyncPtyResource:
from .resources.pty import AsyncPtyResource

return AsyncPtyResource(self)

@cached_property
def scenarios(self) -> AsyncScenariosResource:
from .resources.scenarios import AsyncScenariosResource
Expand Down Expand Up @@ -683,6 +697,12 @@ def devboxes(self) -> devboxes.DevboxesResourceWithRawResponse:

return DevboxesResourceWithRawResponse(self._client.devboxes)

@cached_property
def pty(self) -> pty.PtyResourceWithRawResponse:
from .resources.pty import PtyResourceWithRawResponse

return PtyResourceWithRawResponse(self._client.pty)

@cached_property
def scenarios(self) -> scenarios.ScenariosResourceWithRawResponse:
from .resources.scenarios import ScenariosResourceWithRawResponse
Expand Down Expand Up @@ -780,6 +800,12 @@ def devboxes(self) -> devboxes.AsyncDevboxesResourceWithRawResponse:

return AsyncDevboxesResourceWithRawResponse(self._client.devboxes)

@cached_property
def pty(self) -> pty.AsyncPtyResourceWithRawResponse:
from .resources.pty import AsyncPtyResourceWithRawResponse

return AsyncPtyResourceWithRawResponse(self._client.pty)

@cached_property
def scenarios(self) -> scenarios.AsyncScenariosResourceWithRawResponse:
from .resources.scenarios import AsyncScenariosResourceWithRawResponse
Expand Down Expand Up @@ -877,6 +903,12 @@ def devboxes(self) -> devboxes.DevboxesResourceWithStreamingResponse:

return DevboxesResourceWithStreamingResponse(self._client.devboxes)

@cached_property
def pty(self) -> pty.PtyResourceWithStreamingResponse:
from .resources.pty import PtyResourceWithStreamingResponse

return PtyResourceWithStreamingResponse(self._client.pty)

@cached_property
def scenarios(self) -> scenarios.ScenariosResourceWithStreamingResponse:
from .resources.scenarios import ScenariosResourceWithStreamingResponse
Expand Down Expand Up @@ -974,6 +1006,12 @@ def devboxes(self) -> devboxes.AsyncDevboxesResourceWithStreamingResponse:

return AsyncDevboxesResourceWithStreamingResponse(self._client.devboxes)

@cached_property
def pty(self) -> pty.AsyncPtyResourceWithStreamingResponse:
from .resources.pty import AsyncPtyResourceWithStreamingResponse

return AsyncPtyResourceWithStreamingResponse(self._client.pty)

@cached_property
def scenarios(self) -> scenarios.AsyncScenariosResourceWithStreamingResponse:
from .resources.scenarios import AsyncScenariosResourceWithStreamingResponse
Expand Down
14 changes: 14 additions & 0 deletions src/runloop_api_client/resources/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

from .pty import (
PtyResource,
AsyncPtyResource,
PtyResourceWithRawResponse,
AsyncPtyResourceWithRawResponse,
PtyResourceWithStreamingResponse,
AsyncPtyResourceWithStreamingResponse,
)
from .axons import (
AxonsResource,
AsyncAxonsResource,
Expand Down Expand Up @@ -206,6 +214,12 @@
"AsyncApikeysResourceWithRawResponse",
"ApikeysResourceWithStreamingResponse",
"AsyncApikeysResourceWithStreamingResponse",
"PtyResource",
"AsyncPtyResource",
"PtyResourceWithRawResponse",
"AsyncPtyResourceWithRawResponse",
"PtyResourceWithStreamingResponse",
"AsyncPtyResourceWithStreamingResponse",
"RestrictedKeysResource",
"AsyncRestrictedKeysResource",
"RestrictedKeysResourceWithRawResponse",
Expand Down
62 changes: 62 additions & 0 deletions src/runloop_api_client/resources/devboxes/devboxes.py
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,31 @@ def enable_tunnel(
cast_to=TunnelView,
)

def create_pty_tunnel(
self,
id: str,
*,
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 an authenticated tunnel for high-level PTY access."""
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return self._post(
path_template("/v1/devboxes/{id}/create_pty_tunnel", id=id),
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 @@ -2395,6 +2420,31 @@ async def enable_tunnel(
cast_to=TunnelView,
)

async def create_pty_tunnel(
self,
id: str,
*,
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 an authenticated tunnel for high-level PTY access."""
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return await self._post(
path_template("/v1/devboxes/{id}/create_pty_tunnel", id=id),
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 @@ -3388,6 +3438,9 @@ def __init__(self, devboxes: DevboxesResource) -> None:
self.enable_tunnel = to_raw_response_wrapper(
devboxes.enable_tunnel,
)
self.create_pty_tunnel = to_raw_response_wrapper(
devboxes.create_pty_tunnel,
)
self.execute = to_raw_response_wrapper(
devboxes.execute,
)
Expand Down Expand Up @@ -3481,6 +3534,9 @@ def __init__(self, devboxes: AsyncDevboxesResource) -> None:
self.enable_tunnel = async_to_raw_response_wrapper(
devboxes.enable_tunnel,
)
self.create_pty_tunnel = async_to_raw_response_wrapper(
devboxes.create_pty_tunnel,
)
self.execute = async_to_raw_response_wrapper(
devboxes.execute,
)
Expand Down Expand Up @@ -3574,6 +3630,9 @@ def __init__(self, devboxes: DevboxesResource) -> None:
self.enable_tunnel = to_streamed_response_wrapper(
devboxes.enable_tunnel,
)
self.create_pty_tunnel = to_streamed_response_wrapper(
devboxes.create_pty_tunnel,
)
self.execute = to_streamed_response_wrapper(
devboxes.execute,
)
Expand Down Expand Up @@ -3667,6 +3726,9 @@ def __init__(self, devboxes: AsyncDevboxesResource) -> None:
self.enable_tunnel = async_to_streamed_response_wrapper(
devboxes.enable_tunnel,
)
self.create_pty_tunnel = async_to_streamed_response_wrapper(
devboxes.create_pty_tunnel,
)
self.execute = async_to_streamed_response_wrapper(
devboxes.execute,
)
Expand Down
Loading
Loading