Skip to content

Commit 9f4b49d

Browse files
dines-rlclaude
andauthored
feat(devbox): adding gateway config (#736)
Co-authored-by: Claude Code <[email protected]> Co-authored-by: Claude Sonnet 4.5 <[email protected]>
1 parent 764b445 commit 9f4b49d

File tree

14 files changed

+1022
-0
lines changed

14 files changed

+1022
-0
lines changed

scripts/lint

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,8 @@ uv run mypy .
1313
echo "==> Running lints"
1414
uv run ruff check . "$@"
1515

16+
echo "==> Running format check"
17+
uv run ruff format --check .
18+
1619
echo "==> Making sure it imports"
1720
uv run python -c 'import runloop_api_client'

src/runloop_api_client/sdk/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
SnapshotOps,
1515
BenchmarkOps,
1616
BlueprintOps,
17+
GatewayConfigOps,
1718
NetworkPolicyOps,
1819
StorageObjectOps,
1920
)
@@ -28,6 +29,7 @@
2829
AsyncSnapshotOps,
2930
AsyncBenchmarkOps,
3031
AsyncBlueprintOps,
32+
AsyncGatewayConfigOps,
3133
AsyncNetworkPolicyOps,
3234
AsyncStorageObjectOps,
3335
)
@@ -45,6 +47,7 @@
4547
from .benchmark_run import BenchmarkRun
4648
from .async_scenario import AsyncScenario
4749
from .async_snapshot import AsyncSnapshot
50+
from .gateway_config import GatewayConfig
4851
from .network_policy import NetworkPolicy
4952
from .storage_object import StorageObject
5053
from .async_benchmark import AsyncBenchmark
@@ -54,6 +57,7 @@
5457
from .scenario_builder import ScenarioBuilder
5558
from .async_scenario_run import AsyncScenarioRun
5659
from .async_benchmark_run import AsyncBenchmarkRun
60+
from .async_gateway_config import AsyncGatewayConfig
5761
from .async_network_policy import AsyncNetworkPolicy
5862
from .async_storage_object import AsyncStorageObject
5963
from .async_execution_result import AsyncExecutionResult
@@ -82,6 +86,8 @@
8286
"AsyncStorageObjectOps",
8387
"NetworkPolicyOps",
8488
"AsyncNetworkPolicyOps",
89+
"GatewayConfigOps",
90+
"AsyncGatewayConfigOps",
8591
# Resource classes
8692
"Agent",
8793
"AsyncAgent",
@@ -112,6 +118,8 @@
112118
"AsyncStorageObject",
113119
"NetworkPolicy",
114120
"AsyncNetworkPolicy",
121+
"GatewayConfig",
122+
"AsyncGatewayConfig",
115123
"NamedShell",
116124
"AsyncNamedShell",
117125
]

src/runloop_api_client/sdk/_types.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,15 @@
1919
BenchmarkUpdateParams,
2020
BlueprintCreateParams,
2121
DevboxUploadFileParams,
22+
GatewayConfigListParams,
2223
NetworkPolicyListParams,
2324
DevboxCreateTunnelParams,
2425
DevboxDownloadFileParams,
2526
DevboxEnableTunnelParams,
2627
DevboxRemoveTunnelParams,
2728
DevboxSnapshotDiskParams,
29+
GatewayConfigCreateParams,
30+
GatewayConfigUpdateParams,
2831
NetworkPolicyCreateParams,
2932
NetworkPolicyUpdateParams,
3033
DevboxReadFileContentsParams,
@@ -262,3 +265,15 @@ class SDKNetworkPolicyListParams(NetworkPolicyListParams, BaseRequestOptions):
262265

263266
class SDKNetworkPolicyUpdateParams(NetworkPolicyUpdateParams, LongRequestOptions):
264267
pass
268+
269+
270+
class SDKGatewayConfigCreateParams(GatewayConfigCreateParams, LongRequestOptions):
271+
pass
272+
273+
274+
class SDKGatewayConfigListParams(GatewayConfigListParams, BaseRequestOptions):
275+
pass
276+
277+
278+
class SDKGatewayConfigUpdateParams(GatewayConfigUpdateParams, LongRequestOptions):
279+
pass

src/runloop_api_client/sdk/async_.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
SDKBenchmarkCreateParams,
2727
SDKBlueprintCreateParams,
2828
SDKDiskSnapshotListParams,
29+
SDKGatewayConfigListParams,
2930
SDKNetworkPolicyListParams,
31+
SDKGatewayConfigCreateParams,
3032
SDKNetworkPolicyCreateParams,
3133
SDKDevboxCreateFromImageParams,
3234
)
@@ -41,6 +43,7 @@
4143
from .async_benchmark import AsyncBenchmark
4244
from .async_blueprint import AsyncBlueprint
4345
from ..lib.context_loader import TarFilter, build_directory_tar
46+
from .async_gateway_config import AsyncGatewayConfig
4447
from .async_network_policy import AsyncNetworkPolicy
4548
from .async_storage_object import AsyncStorageObject
4649
from .async_scenario_builder import AsyncScenarioBuilder
@@ -924,6 +927,90 @@ async def list(self, **params: Unpack[SDKNetworkPolicyListParams]) -> list[Async
924927
return [AsyncNetworkPolicy(self._client, item.id) for item in page.network_policies]
925928

926929

930+
class AsyncGatewayConfigOps:
931+
"""High-level async manager for creating and managing gateway configurations.
932+
933+
Accessed via ``runloop.gateway_config`` from :class:`AsyncRunloopSDK`, provides
934+
coroutines to create, retrieve, update, delete, and list gateway configs. Gateway configs
935+
define how to proxy API requests through the credential gateway, enabling secure API
936+
proxying without exposing API keys.
937+
938+
Example:
939+
>>> runloop = AsyncRunloopSDK()
940+
>>> gateway_config = await runloop.gateway_config.create(
941+
... name="my-api-gateway",
942+
... endpoint="https://api.example.com",
943+
... auth_mechanism={"type": "bearer"},
944+
... )
945+
>>> # Use with a devbox
946+
>>> devbox = await runloop.devbox.create(
947+
... name="my-devbox",
948+
... gateways={
949+
... "MY_API": {
950+
... "gateway": gateway_config.id,
951+
... "secret": "my-api-key-secret",
952+
... },
953+
... },
954+
... )
955+
"""
956+
957+
def __init__(self, client: AsyncRunloop) -> None:
958+
"""Initialize AsyncGatewayConfigOps.
959+
960+
:param client: AsyncRunloop client instance
961+
:type client: AsyncRunloop
962+
"""
963+
self._client = client
964+
965+
async def create(self, **params: Unpack[SDKGatewayConfigCreateParams]) -> AsyncGatewayConfig:
966+
"""Create a new gateway config.
967+
968+
Example:
969+
>>> gateway_config = await runloop.gateway_config.create(
970+
... name="my-gateway",
971+
... endpoint="https://api.example.com",
972+
... auth_mechanism={"type": "header", "key": "x-api-key"},
973+
... description="Gateway for My API",
974+
... )
975+
976+
:param params: See :typeddict:`~runloop_api_client.sdk._types.SDKGatewayConfigCreateParams` for available parameters
977+
:return: The newly created gateway config
978+
:rtype: AsyncGatewayConfig
979+
"""
980+
response = await self._client.gateway_configs.create(**params)
981+
return AsyncGatewayConfig(self._client, response.id)
982+
983+
def from_id(self, gateway_config_id: str) -> AsyncGatewayConfig:
984+
"""Get an AsyncGatewayConfig instance for an existing gateway config ID.
985+
986+
Example:
987+
>>> gateway_config = runloop.gateway_config.from_id("gwc_1234567890")
988+
>>> info = await gateway_config.get_info()
989+
>>> print(f"Gateway Config name: {info.name}")
990+
991+
:param gateway_config_id: ID of the gateway config
992+
:type gateway_config_id: str
993+
:return: AsyncGatewayConfig instance for the given ID
994+
:rtype: AsyncGatewayConfig
995+
"""
996+
return AsyncGatewayConfig(self._client, gateway_config_id)
997+
998+
async def list(self, **params: Unpack[SDKGatewayConfigListParams]) -> list[AsyncGatewayConfig]:
999+
"""List all gateway configs, optionally filtered by parameters.
1000+
1001+
Example:
1002+
>>> configs = await runloop.gateway_config.list(limit=10)
1003+
>>> for config in configs:
1004+
... print(config.id)
1005+
1006+
:param params: See :typeddict:`~runloop_api_client.sdk._types.SDKGatewayConfigListParams` for available parameters
1007+
:return: List of gateway configs
1008+
:rtype: list[AsyncGatewayConfig]
1009+
"""
1010+
page = await self._client.gateway_configs.list(**params)
1011+
return [AsyncGatewayConfig(self._client, item.id) for item in page.gateway_configs]
1012+
1013+
9271014
class AsyncRunloopSDK:
9281015
"""High-level asynchronous entry point for the Runloop SDK.
9291016
@@ -951,6 +1038,8 @@ class AsyncRunloopSDK:
9511038
:vartype storage_object: AsyncStorageObjectOps
9521039
:ivar network_policy: High-level async interface for network policy management
9531040
:vartype network_policy: AsyncNetworkPolicyOps
1041+
:ivar gateway_config: High-level async interface for gateway config management
1042+
:vartype gateway_config: AsyncGatewayConfigOps
9541043
9551044
Example:
9561045
>>> runloop = AsyncRunloopSDK() # Uses RUNLOOP_API_KEY env var
@@ -965,6 +1054,7 @@ class AsyncRunloopSDK:
9651054
benchmark: AsyncBenchmarkOps
9661055
devbox: AsyncDevboxOps
9671056
blueprint: AsyncBlueprintOps
1057+
gateway_config: AsyncGatewayConfigOps
9681058
network_policy: AsyncNetworkPolicyOps
9691059
scenario: AsyncScenarioOps
9701060
scorer: AsyncScorerOps
@@ -1013,6 +1103,7 @@ def __init__(
10131103
self.benchmark = AsyncBenchmarkOps(self.api)
10141104
self.devbox = AsyncDevboxOps(self.api)
10151105
self.blueprint = AsyncBlueprintOps(self.api)
1106+
self.gateway_config = AsyncGatewayConfigOps(self.api)
10161107
self.network_policy = AsyncNetworkPolicyOps(self.api)
10171108
self.scenario = AsyncScenarioOps(self.api)
10181109
self.scorer = AsyncScorerOps(self.api)
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
"""GatewayConfig resource class for asynchronous operations."""
2+
3+
from __future__ import annotations
4+
5+
from typing_extensions import Unpack, override
6+
7+
from ._types import BaseRequestOptions, LongRequestOptions, SDKGatewayConfigUpdateParams
8+
from .._client import AsyncRunloop
9+
from ..types.gateway_config_view import GatewayConfigView
10+
11+
12+
class AsyncGatewayConfig:
13+
"""Asynchronous wrapper around a gateway config resource.
14+
15+
Gateway configs define how to proxy API requests through the credential gateway.
16+
They specify the target endpoint and how credentials should be applied. Use with
17+
devboxes to securely proxy requests to external APIs without exposing API keys.
18+
19+
Example:
20+
>>> runloop = AsyncRunloopSDK()
21+
>>> gateway_config = await runloop.gateway_config.create(
22+
... name="my-api-gateway",
23+
... endpoint="https://api.example.com",
24+
... auth_mechanism={"type": "bearer"},
25+
... )
26+
>>> info = await gateway_config.get_info()
27+
>>> print(f"Gateway Config: {info.name}")
28+
"""
29+
30+
def __init__(
31+
self,
32+
client: AsyncRunloop,
33+
gateway_config_id: str,
34+
) -> None:
35+
"""Initialize the wrapper.
36+
37+
:param client: Generated AsyncRunloop client
38+
:type client: AsyncRunloop
39+
:param gateway_config_id: GatewayConfig ID returned by the API
40+
:type gateway_config_id: str
41+
"""
42+
self._client = client
43+
self._id = gateway_config_id
44+
45+
@override
46+
def __repr__(self) -> str:
47+
return f"<AsyncGatewayConfig id={self._id!r}>"
48+
49+
@property
50+
def id(self) -> str:
51+
"""Return the gateway config ID.
52+
53+
:return: Unique gateway config ID
54+
:rtype: str
55+
"""
56+
return self._id
57+
58+
async def get_info(
59+
self,
60+
**options: Unpack[BaseRequestOptions],
61+
) -> GatewayConfigView:
62+
"""Retrieve the latest gateway config details.
63+
64+
Example:
65+
>>> info = await gateway_config.get_info()
66+
>>> print(f"Gateway Config: {info.name}, endpoint: {info.endpoint}")
67+
68+
:param options: Optional request configuration
69+
:return: API response describing the gateway config
70+
:rtype: GatewayConfigView
71+
"""
72+
return await self._client.gateway_configs.retrieve(
73+
self._id,
74+
**options,
75+
)
76+
77+
async def update(self, **params: Unpack[SDKGatewayConfigUpdateParams]) -> GatewayConfigView:
78+
"""Update the gateway config.
79+
80+
Example:
81+
>>> updated = await gateway_config.update(
82+
... name="updated-gateway-name",
83+
... description="Updated description",
84+
... )
85+
86+
:param params: See :typeddict:`~runloop_api_client.sdk._types.SDKGatewayConfigUpdateParams` for available parameters
87+
:return: Updated gateway config view
88+
:rtype: GatewayConfigView
89+
"""
90+
return await self._client.gateway_configs.update(self._id, **params)
91+
92+
async def delete(
93+
self,
94+
**options: Unpack[LongRequestOptions],
95+
) -> GatewayConfigView:
96+
"""Delete the gateway config. This action is irreversible.
97+
98+
Example:
99+
>>> await gateway_config.delete()
100+
101+
:param options: Optional long-running request configuration
102+
:return: API response acknowledging deletion
103+
:rtype: GatewayConfigView
104+
"""
105+
return await self._client.gateway_configs.delete(
106+
self._id,
107+
**options,
108+
)

0 commit comments

Comments
 (0)