Skip to content

Commit 8002c0a

Browse files
release: 1.12.0 (#758)
Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> Co-authored-by: james-rl <james@runloop.ai>
1 parent 682f5d8 commit 8002c0a

File tree

23 files changed

+1288
-167
lines changed

23 files changed

+1288
-167
lines changed

.github/workflows/ci.yml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
name: CI
22
on:
33
push:
4-
branches-ignore:
5-
- 'generated'
6-
- 'codegen/**'
7-
- 'integrated/**'
8-
- 'stl-preview-head/**'
9-
- 'stl-preview-base/**'
4+
branches:
5+
- '**'
6+
- '!integrated/**'
7+
- '!stl-preview-head/**'
8+
- '!stl-preview-base/**'
9+
- '!generated'
10+
- '!codegen/**'
11+
- 'codegen/stl/**'
1012
pull_request:
1113
branches-ignore:
1214
- 'stl-preview-head/**'

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "1.11.0"
2+
".": "1.12.0"
33
}

.stats.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
configured_endpoints: 118
22
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-132ed160716591bdcd9231c00da8c506d9451a5486b165fc27b2a01d93202082.yml
33
openapi_spec_hash: c2b44d9e9cda56e32141a7ea3794bbba
4-
config_hash: cbda3692cb48ab8582a0df1674b9e5c8
4+
config_hash: 3bd89c812b96708c461fb98286ebf0b5

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
11
# Changelog
22

3+
## 1.12.0 (2026-03-18)
4+
5+
Full Changelog: [v1.11.0...v1.12.0](https://github.com/runloopai/api-client-python/compare/v1.11.0...v1.12.0)
6+
7+
### Features
8+
9+
* add secrets to oo-sdk ([#756](https://github.com/runloopai/api-client-python/issues/756)) ([09c4997](https://github.com/runloopai/api-client-python/commit/09c4997908ca7ec2ab9a45173d4d0bcf33ad2ca2))
10+
11+
12+
### Bug Fixes
13+
14+
* **deps:** bump minimum typing-extensions version ([a873aad](https://github.com/runloopai/api-client-python/commit/a873aada5cbf8d29600374039f4d65bd321fa6bb))
15+
* **pydantic:** do not pass `by_alias` unless set ([baf3a9a](https://github.com/runloopai/api-client-python/commit/baf3a9a8fe64e14f92f31387781f95dc6c7753de))
16+
17+
18+
### Chores
19+
20+
* configure new SDK language ([13d3ada](https://github.com/runloopai/api-client-python/commit/13d3adaa20a78b6d02db41369505f28aed92e1f5))
21+
* **internal:** tweak CI branches ([b374486](https://github.com/runloopai/api-client-python/commit/b374486156c36b01c55135e4a0c1871b78360176))
22+
323
## 1.11.0 (2026-03-10)
424

525
Full Changelog: [v1.10.3...v1.11.0](https://github.com/runloopai/api-client-python/compare/v1.10.3...v1.11.0)

EXAMPLES.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Runnable examples live in [`examples/`](./examples).
1111
- [Devbox From Blueprint (Run Command, Shutdown)](#devbox-from-blueprint-lifecycle)
1212
- [Devbox Snapshot and Resume](#devbox-snapshot-resume)
1313
- [MCP Hub + Claude Code + GitHub](#mcp-github-tools)
14+
- [Secrets with Devbox (Create, Inject, Verify, Delete)](#secrets-with-devbox)
1415

1516
<a id="blueprint-with-build-context"></a>
1617
## Blueprint with Build Context
@@ -135,3 +136,34 @@ uv run pytest -m smoketest tests/smoketests/examples/
135136
```
136137

137138
**Source:** [`examples/mcp_github_tools.py`](./examples/mcp_github_tools.py)
139+
140+
<a id="secrets-with-devbox"></a>
141+
## Secrets with Devbox (Create, Inject, Verify, Delete)
142+
143+
**Use case:** Create a secret, inject it into a devbox as an environment variable, verify access, and clean up.
144+
145+
**Tags:** `secrets`, `devbox`, `environment-variables`, `cleanup`
146+
147+
### Workflow
148+
- Create a secret with a test value
149+
- Create a devbox with the secret mapped to an env var
150+
- Execute a command that reads the secret from the environment
151+
- Verify the value matches
152+
- Update the secret and verify
153+
- List secrets and verify the secret appears
154+
- Shutdown devbox and delete secret
155+
156+
### Prerequisites
157+
- `RUNLOOP_API_KEY`
158+
159+
### Run
160+
```sh
161+
uv run python -m examples.secrets_with_devbox
162+
```
163+
164+
### Test
165+
```sh
166+
uv run pytest -m smoketest tests/smoketests/examples/
167+
```
168+
169+
**Source:** [`examples/secrets_with_devbox.py`](./examples/secrets_with_devbox.py)

README-SDK.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ The SDK provides object-oriented interfaces for all major Runloop resources:
116116
- **`runloop.blueprint`** - Blueprint management (create, list, build blueprints)
117117
- **`runloop.snapshot`** - Snapshot management (list disk snapshots)
118118
- **`runloop.storage_object`** - Storage object management (upload, download, list objects)
119+
- **`runloop.secret`** - Secret management (create, update, list, delete encrypted key-value pairs)
119120
- **`runloop.api`** - Direct access to the underlying REST API client
120121

121122
### Devbox

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@ and offers both synchronous and asynchronous clients powered by [httpx](https://
99

1010
It is generated with [Stainless](https://www.stainless.com/).
1111

12+
## MCP Server
13+
14+
Use the Runloop MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.
15+
16+
[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=%40runloop%2Fapi-client-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBydW5sb29wL2FwaS1jbGllbnQtbWNwIl0sImVudiI6eyJSVU5MT09QX0FQSV9LRVkiOiJNeSBCZWFyZXIgVG9rZW4ifX0)
17+
[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22%40runloop%2Fapi-client-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40runloop%2Fapi-client-mcp%22%5D%2C%22env%22%3A%7B%22RUNLOOP_API_KEY%22%3A%22My%20Bearer%20Token%22%7D%7D)
18+
19+
> Note: You may need to set environment variables in your MCP client.
20+
1221
## Documentation
1322

1423
The REST API documentation can be found on

examples/mcp_github_tools.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ def recipe(ctx: RecipeContext, options: McpExampleOptions) -> RecipeOutput: # n
8080

8181
# Store the GitHub PAT as a Runloop secret
8282
secret_name = unique_name("example-github-mcp")
83-
sdk.api.secrets.create(name=secret_name, value=github_token)
83+
secret = sdk.secret.create(name=secret_name, value=github_token)
8484
resources_created.append(f"secret:{secret_name}")
85-
cleanup.add(f"secret:{secret_name}", lambda: sdk.api.secrets.delete(secret_name))
85+
cleanup.add(f"secret:{secret_name}", secret.delete)
8686

8787
# Launch a devbox with MCP Hub wiring
8888
devbox = sdk.devbox.create(

examples/registry.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from .example_types import ExampleResult
1111
from .mcp_github_tools import run_mcp_github_tools_example
12+
from .secrets_with_devbox import run_secrets_with_devbox_example
1213
from .devbox_snapshot_resume import run_devbox_snapshot_resume_example
1314
from .blueprint_with_build_context import run_blueprint_with_build_context_example
1415
from .devbox_from_blueprint_lifecycle import run_devbox_from_blueprint_lifecycle_example
@@ -44,6 +45,13 @@
4445
"required_env": ["RUNLOOP_API_KEY", "GITHUB_TOKEN", "ANTHROPIC_API_KEY"],
4546
"run": run_mcp_github_tools_example,
4647
},
48+
{
49+
"slug": "secrets-with-devbox",
50+
"title": "Secrets with Devbox (Create, Inject, Verify, Delete)",
51+
"file_name": "secrets_with_devbox.py",
52+
"required_env": ["RUNLOOP_API_KEY"],
53+
"run": run_secrets_with_devbox_example,
54+
},
4755
]
4856

4957

examples/secrets_with_devbox.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#!/usr/bin/env -S uv run python
2+
"""
3+
---
4+
title: Secrets with Devbox (Create, Inject, Verify, Delete)
5+
slug: secrets-with-devbox
6+
use_case: Create a secret, inject it into a devbox as an environment variable, verify access, and clean up.
7+
workflow:
8+
- Create a secret with a test value
9+
- Create a devbox with the secret mapped to an env var
10+
- Execute a command that reads the secret from the environment
11+
- Verify the value matches
12+
- Update the secret and verify
13+
- List secrets and verify the secret appears
14+
- Shutdown devbox and delete secret
15+
tags:
16+
- secrets
17+
- devbox
18+
- environment-variables
19+
- cleanup
20+
prerequisites:
21+
- RUNLOOP_API_KEY
22+
run: uv run python -m examples.secrets_with_devbox
23+
test: uv run pytest -m smoketest tests/smoketests/examples/
24+
---
25+
"""
26+
27+
from __future__ import annotations
28+
29+
from runloop_api_client import RunloopSDK
30+
31+
from ._harness import run_as_cli, unique_name, wrap_recipe
32+
from .example_types import ExampleCheck, RecipeOutput, RecipeContext
33+
34+
# Note: do NOT hardcode secret values in your code!
35+
# This is example code only; use environment variables instead!
36+
_EXAMPLE_SECRET_VALUE = "my-secret-value"
37+
_UPDATED_SECRET_VALUE = "updated-secret-value"
38+
39+
40+
def recipe(ctx: RecipeContext) -> RecipeOutput:
41+
"""Create a secret, inject it into a devbox, and verify it is accessible."""
42+
cleanup = ctx.cleanup
43+
44+
sdk = RunloopSDK()
45+
resources_created: list[str] = []
46+
checks: list[ExampleCheck] = []
47+
48+
secret_name = unique_name("RUNLOOP_SDK_EXAMPLE").upper().replace("-", "_")
49+
50+
secret = sdk.secret.create(name=secret_name, value=_EXAMPLE_SECRET_VALUE)
51+
resources_created.append(f"secret:{secret_name}")
52+
cleanup.add(f"secret:{secret_name}", lambda: secret.delete())
53+
54+
secret_info = secret.get_info()
55+
checks.append(
56+
ExampleCheck(
57+
name="secret created successfully",
58+
passed=secret.name == secret_name and secret_info.id.startswith("sec_"),
59+
details=f"name={secret.name}, id={secret_info.id}",
60+
)
61+
)
62+
63+
devbox = sdk.devbox.create(
64+
name=unique_name("secrets-example-devbox"),
65+
secrets={
66+
"MY_SECRET_ENV": secret.name,
67+
},
68+
launch_parameters={
69+
"resource_size_request": "X_SMALL",
70+
"keep_alive_time_seconds": 60 * 5,
71+
},
72+
)
73+
resources_created.append(f"devbox:{devbox.id}")
74+
cleanup.add(f"devbox:{devbox.id}", devbox.shutdown)
75+
76+
result = devbox.cmd.exec("echo $MY_SECRET_ENV")
77+
stdout = result.stdout().strip()
78+
checks.append(
79+
ExampleCheck(
80+
name="devbox can read secret as env var",
81+
passed=result.exit_code == 0 and stdout == _EXAMPLE_SECRET_VALUE,
82+
details=f'exit_code={result.exit_code}, stdout="{stdout}"',
83+
)
84+
)
85+
86+
updated_info = sdk.secret.update(secret, _UPDATED_SECRET_VALUE).get_info()
87+
checks.append(
88+
ExampleCheck(
89+
name="secret updated successfully",
90+
passed=updated_info.name == secret_name,
91+
details=f"update_time_ms={updated_info.update_time_ms}",
92+
)
93+
)
94+
95+
secrets = sdk.secret.list()
96+
found = next((s for s in secrets if s.name == secret_name), None)
97+
checks.append(
98+
ExampleCheck(
99+
name="secret appears in list",
100+
passed=found is not None,
101+
details=f"found name={found.name}" if found else "not found",
102+
)
103+
)
104+
105+
return RecipeOutput(resources_created=resources_created, checks=checks)
106+
107+
108+
run_secrets_with_devbox_example = wrap_recipe(recipe)
109+
110+
111+
if __name__ == "__main__":
112+
run_as_cli(run_secrets_with_devbox_example)

0 commit comments

Comments
 (0)