Skip to content

Commit 212175a

Browse files
committed
merged
2 parents aa1ee9b + e31e345 commit 212175a

44 files changed

Lines changed: 1888 additions & 606 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 14 additions & 8 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/**'
@@ -58,14 +60,18 @@ jobs:
5860
run: uv build
5961

6062
- name: Get GitHub OIDC Token
61-
if: github.repository == 'stainless-sdks/runloop-python'
63+
if: |-
64+
github.repository == 'stainless-sdks/runloop-python' &&
65+
!startsWith(github.ref, 'refs/heads/stl/')
6266
id: github-oidc
6367
uses: actions/github-script@v8
6468
with:
6569
script: core.setOutput('github_token', await core.getIDToken());
6670

6771
- name: Upload tarball
68-
if: github.repository == 'stainless-sdks/runloop-python'
72+
if: |-
73+
github.repository == 'stainless-sdks/runloop-python' &&
74+
!startsWith(github.ref, 'refs/heads/stl/')
6975
env:
7076
URL: https://pkg.stainless.com/s
7177
AUTH: ${{ steps.github-oidc.outputs.github_token }}

.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.10.3"
2+
".": "1.12.1"
33
}

.stats.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
configured_endpoints: 118
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-132ed160716591bdcd9231c00da8c506d9451a5486b165fc27b2a01d93202082.yml
3-
openapi_spec_hash: c2b44d9e9cda56e32141a7ea3794bbba
4-
config_hash: cbda3692cb48ab8582a0df1674b9e5c8
1+
configured_endpoints: 117
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-0568973e19e8af9fa953b2ded109ab2b69e76e90e2b74f33617dbf7092e26274.yml
3+
openapi_spec_hash: 10ba804ce69510d7985e05c77d0ffcf6
4+
config_hash: de99cfce88e2d1f02246dc6c2f43bc6c

CHANGELOG.md

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

3+
## 1.12.1 (2026-03-19)
4+
5+
Full Changelog: [v1.12.0...v1.12.1](https://github.com/runloopai/api-client-python/compare/v1.12.0...v1.12.1)
6+
7+
### Chores
8+
9+
* remove create_tunnel endpoint ([627c994](https://github.com/runloopai/api-client-python/commit/627c9945cdaf3b2d0ca46a2832092dd4fa011863))
10+
11+
## 1.12.0 (2026-03-18)
12+
13+
Full Changelog: [v1.11.0...v1.12.0](https://github.com/runloopai/api-client-python/compare/v1.11.0...v1.12.0)
14+
15+
### Features
16+
17+
* 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))
18+
19+
20+
### Bug Fixes
21+
22+
* **deps:** bump minimum typing-extensions version ([a873aad](https://github.com/runloopai/api-client-python/commit/a873aada5cbf8d29600374039f4d65bd321fa6bb))
23+
* **pydantic:** do not pass `by_alias` unless set ([baf3a9a](https://github.com/runloopai/api-client-python/commit/baf3a9a8fe64e14f92f31387781f95dc6c7753de))
24+
25+
26+
### Chores
27+
28+
* configure new SDK language ([13d3ada](https://github.com/runloopai/api-client-python/commit/13d3adaa20a78b6d02db41369505f28aed92e1f5))
29+
* **internal:** tweak CI branches ([b374486](https://github.com/runloopai/api-client-python/commit/b374486156c36b01c55135e4a0c1871b78360176))
30+
31+
## 1.11.0 (2026-03-10)
32+
33+
Full Changelog: [v1.10.3...v1.11.0](https://github.com/runloopai/api-client-python/compare/v1.10.3...v1.11.0)
34+
35+
### Features
36+
37+
* add snapshots example ([#753](https://github.com/runloopai/api-client-python/issues/753)) ([f13916a](https://github.com/runloopai/api-client-python/commit/f13916a570fe708aef56c6fbfd5c06b9e0dc158e))
38+
* **documentation:** added llms.txt, examples and referenced these artifacts via README ([#748](https://github.com/runloopai/api-client-python/issues/748)) ([bceb953](https://github.com/runloopai/api-client-python/commit/bceb953c8486bec6bbe5bca1276ed54d625eda5d))
39+
40+
41+
### Bug Fixes
42+
43+
* add logs to devboxes, smoke tests & examples ([#755](https://github.com/runloopai/api-client-python/issues/755)) ([da5faa4](https://github.com/runloopai/api-client-python/commit/da5faa4e69348dd6f034a538046ab9ff2ff4831b))
44+
45+
46+
### Chores
47+
48+
* **ci:** skip uploading artifacts on stainless-internal branches ([6874252](https://github.com/runloopai/api-client-python/commit/6874252fe355ac8a44be6bc57cd6bb15a3f035be))
49+
* **documentation:** correct exec advice ([#752](https://github.com/runloopai/api-client-python/issues/752)) ([fa16c1d](https://github.com/runloopai/api-client-python/commit/fa16c1d602ec301200e82a48fe23c2218ae82a08))
50+
* **test:** do not count install time for mock server timeout ([f6fde05](https://github.com/runloopai/api-client-python/commit/f6fde050a5f4b985c01003cd7593c1619ede028f))
51+
* update placeholder string ([5dfcd93](https://github.com/runloopai/api-client-python/commit/5dfcd93bb05cda783901377ee7fc03a6acffe0ae))
52+
353
## 1.10.3 (2026-02-27)
454

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

EXAMPLES.md

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,13 @@ Runnable examples live in [`examples/`](./examples).
99

1010
- [Blueprint with Build Context](#blueprint-with-build-context)
1111
- [Devbox From Blueprint (Run Command, Shutdown)](#devbox-from-blueprint-lifecycle)
12+
<<<<<<< HEAD
1213
- [Devbox Tunnel (HTTP Server Access)](#devbox-tunnel)
14+
=======
15+
- [Devbox Snapshot and Resume](#devbox-snapshot-resume)
16+
>>>>>>> main
1317
- [MCP Hub + Claude Code + GitHub](#mcp-github-tools)
18+
- [Secrets with Devbox (Create, Inject, Verify, Delete)](#secrets-with-devbox)
1419

1520
<a id="blueprint-with-build-context"></a>
1621
## Blueprint with Build Context
@@ -45,15 +50,17 @@ uv run pytest -m smoketest tests/smoketests/examples/
4550
<a id="devbox-from-blueprint-lifecycle"></a>
4651
## Devbox From Blueprint (Run Command, Shutdown)
4752

48-
**Use case:** Create a devbox from a blueprint, run a command, validate output, and cleanly tear everything down.
53+
**Use case:** Create a devbox from a blueprint, run a command, fetch logs, validate output, and cleanly tear everything down.
4954

50-
**Tags:** `devbox`, `blueprint`, `commands`, `cleanup`
55+
**Tags:** `devbox`, `blueprint`, `commands`, `logs`, `cleanup`
5156

5257
### Workflow
5358
- Create a blueprint
59+
- Fetch blueprint build logs
5460
- Create a devbox from the blueprint
5561
- Execute a command in the devbox
56-
- Validate exit code and stdout
62+
- Fetch devbox logs
63+
- Validate exit code, stdout, and logs
5764
- Shutdown devbox and delete blueprint
5865

5966
### Prerequisites
@@ -71,6 +78,7 @@ uv run pytest -m smoketest tests/smoketests/examples/
7178

7279
**Source:** [`examples/devbox_from_blueprint_lifecycle.py`](./examples/devbox_from_blueprint_lifecycle.py)
7380

81+
<<<<<<< HEAD
7482
<a id="devbox-tunnel"></a>
7583
## Devbox Tunnel (HTTP Server Access)
7684

@@ -85,21 +93,46 @@ uv run pytest -m smoketest tests/smoketests/examples/
8593
- Make an HTTP request from the local machine through the tunnel
8694
- Validate the response
8795
- Shutdown the devbox
96+
=======
97+
<a id="devbox-snapshot-resume"></a>
98+
## Devbox Snapshot and Resume
99+
100+
**Use case:** Create a devbox, snapshot its disk, resume from the snapshot, and demonstrate that changes in the original devbox do not affect the clone. Uses the async SDK.
101+
102+
**Tags:** `devbox`, `snapshot`, `resume`, `cleanup`, `async`
103+
104+
### Workflow
105+
- Create a devbox
106+
- Write a file to the devbox
107+
- Create a disk snapshot
108+
- Create a new devbox from the snapshot
109+
- Modify the file on the original devbox
110+
- Verify the clone has the original content
111+
- Shutdown both devboxes and delete the snapshot
112+
>>>>>>> main
88113
89114
### Prerequisites
90115
- `RUNLOOP_API_KEY`
91116

92117
### Run
93118
```sh
119+
<<<<<<< HEAD
94120
uv run python -m examples.devbox_tunnel
121+
=======
122+
uv run python -m examples.devbox_snapshot_resume
123+
>>>>>>> main
95124
```
96125

97126
### Test
98127
```sh
99128
uv run pytest -m smoketest tests/smoketests/examples/
100129
```
101130

131+
<<<<<<< HEAD
102132
**Source:** [`examples/devbox_tunnel.py`](./examples/devbox_tunnel.py)
133+
=======
134+
**Source:** [`examples/devbox_snapshot_resume.py`](./examples/devbox_snapshot_resume.py)
135+
>>>>>>> main
103136
104137
<a id="mcp-github-tools"></a>
105138
## MCP Hub + Claude Code + GitHub
@@ -132,3 +165,34 @@ uv run pytest -m smoketest tests/smoketests/examples/
132165
```
133166

134167
**Source:** [`examples/mcp_github_tools.py`](./examples/mcp_github_tools.py)
168+
169+
<a id="secrets-with-devbox"></a>
170+
## Secrets with Devbox (Create, Inject, Verify, Delete)
171+
172+
**Use case:** Create a secret, inject it into a devbox as an environment variable, verify access, and clean up.
173+
174+
**Tags:** `secrets`, `devbox`, `environment-variables`, `cleanup`
175+
176+
### Workflow
177+
- Create a secret with a test value
178+
- Create a devbox with the secret mapped to an env var
179+
- Execute a command that reads the secret from the environment
180+
- Verify the value matches
181+
- Update the secret and verify
182+
- List secrets and verify the secret appears
183+
- Shutdown devbox and delete secret
184+
185+
### Prerequisites
186+
- `RUNLOOP_API_KEY`
187+
188+
### Run
189+
```sh
190+
uv run python -m examples.secrets_with_devbox
191+
```
192+
193+
### Test
194+
```sh
195+
uv run pytest -m smoketest tests/smoketests/examples/
196+
```
197+
198+
**Source:** [`examples/secrets_with_devbox.py`](./examples/secrets_with_devbox.py)

README-SDK.md

Lines changed: 22 additions & 2 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
@@ -160,13 +161,13 @@ print(f"Devbox {info.name} is {info.status}")
160161
Execute commands synchronously or asynchronously:
161162

162163
```python
163-
# Synchronous command execution (waits for completion)
164+
# exec blocks until completion - use for commands that return immediately
164165
result = devbox.cmd.exec("ls -la")
165166
print("Output:", result.stdout())
166167
print("Exit code:", result.exit_code)
167168
print("Success:", result.success)
168169

169-
# Asynchronous command execution (returns immediately)
170+
# exec_async returns immediately - use for long-running processes
170171
execution = devbox.cmd.exec_async("npm run dev")
171172

172173
# Check execution status
@@ -393,11 +394,30 @@ async with await runloop.devbox.create(name="temp-devbox") as devbox:
393394
# devbox is automatically shutdown when exiting the context
394395
```
395396

397+
#### Devbox Logs
398+
399+
Retrieve logs from a devbox, optionally filtered by execution ID or shell name:
400+
401+
```python
402+
# Get all devbox logs
403+
logs = devbox.logs()
404+
for log in logs.logs:
405+
print(f"[{log.level}] {log.message}")
406+
407+
# Filter logs by execution ID
408+
result = devbox.cmd.exec('echo "hello"')
409+
exec_logs = devbox.logs(execution_id=result.execution_id)
410+
411+
# Filter logs by shell name
412+
shell_logs = devbox.logs(shell_name="my-shell")
413+
```
414+
396415
**Key methods:**
397416

398417
- `devbox.get_info()` - Get devbox details and status
399418
- `devbox.cmd.exec()` - Execute commands synchronously
400419
- `devbox.cmd.exec_async()` - Execute commands asynchronously
420+
- `devbox.logs()` - Retrieve devbox logs (optionally filter by execution_id or shell_name)
401421
- `devbox.file.read()` - Read file contents
402422
- `devbox.file.write()` - Write file contents
403423
- `devbox.file.upload()` - Upload files

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,17 @@ print(runloop_api_client.__version__)
497497

498498
Python 3.9 or higher.
499499

500+
## Development
501+
502+
After cloning the repository, run the bootstrap script and install git hooks:
503+
504+
```sh
505+
./scripts/bootstrap
506+
./scripts/install-hooks
507+
```
508+
509+
This installs pre-push hooks that run linting and verify generated files are up to date.
510+
500511
## Contributing
501512

502513
See [the contributing documentation](./CONTRIBUTING.md).

api.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,6 @@ from runloop_api_client.types import (
131131
DevboxSendStdInResult,
132132
DevboxSnapshotListView,
133133
DevboxSnapshotView,
134-
DevboxTunnelView,
135134
DevboxView,
136135
TunnelView,
137136
DevboxCreateSSHKeyResponse,
@@ -147,7 +146,6 @@ Methods:
147146
- <code title="post /v1/devboxes/{id}">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">update</a>(id, \*\*<a href="src/runloop_api_client/types/devbox_update_params.py">params</a>) -> <a href="./src/runloop_api_client/types/devbox_view.py">DevboxView</a></code>
148147
- <code title="get /v1/devboxes">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">list</a>(\*\*<a href="src/runloop_api_client/types/devbox_list_params.py">params</a>) -> <a href="./src/runloop_api_client/types/devbox_view.py">SyncDevboxesCursorIDPage[DevboxView]</a></code>
149148
- <code title="post /v1/devboxes/{id}/create_ssh_key">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">create_ssh_key</a>(id) -> <a href="./src/runloop_api_client/types/devbox_create_ssh_key_response.py">DevboxCreateSSHKeyResponse</a></code>
150-
- <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>
151149
- <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>
152150
- <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>
153151
- <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>

examples/devbox_from_blueprint_lifecycle.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,20 @@
33
---
44
title: Devbox From Blueprint (Run Command, Shutdown)
55
slug: devbox-from-blueprint-lifecycle
6-
use_case: Create a devbox from a blueprint, run a command, validate output, and cleanly tear everything down.
6+
use_case: Create a devbox from a blueprint, run a command, fetch logs, validate output, and cleanly tear everything down.
77
workflow:
88
- Create a blueprint
9+
- Fetch blueprint build logs
910
- Create a devbox from the blueprint
1011
- Execute a command in the devbox
11-
- Validate exit code and stdout
12+
- Fetch devbox logs
13+
- Validate exit code, stdout, and logs
1214
- Shutdown devbox and delete blueprint
1315
tags:
1416
- devbox
1517
- blueprint
1618
- commands
19+
- logs
1720
- cleanup
1821
prerequisites:
1922
- RUNLOOP_API_KEY
@@ -34,7 +37,7 @@
3437

3538

3639
def recipe(ctx: RecipeContext) -> RecipeOutput:
37-
"""Create a devbox from a blueprint, run a command, and clean up."""
40+
"""Create a devbox from a blueprint, run a command, fetch logs, and clean up."""
3841
cleanup = ctx.cleanup
3942

4043
sdk = RunloopSDK()
@@ -46,6 +49,9 @@ def recipe(ctx: RecipeContext) -> RecipeOutput:
4649
)
4750
cleanup.add(f"blueprint:{blueprint.id}", blueprint.delete)
4851

52+
# Fetch blueprint build logs
53+
blueprint_logs = blueprint.logs()
54+
4955
devbox = blueprint.create_devbox(
5056
name=unique_name("example-devbox"),
5157
launch_parameters={
@@ -58,6 +64,9 @@ def recipe(ctx: RecipeContext) -> RecipeOutput:
5864
result = devbox.cmd.exec('echo "Hello from your devbox"')
5965
stdout = result.stdout()
6066

67+
# Fetch devbox logs
68+
devbox_logs = devbox.logs()
69+
6170
return RecipeOutput(
6271
resources_created=[f"blueprint:{blueprint.id}", f"devbox:{devbox.id}"],
6372
checks=[
@@ -71,6 +80,16 @@ def recipe(ctx: RecipeContext) -> RecipeOutput:
7180
passed="Hello from your devbox" in stdout,
7281
details=stdout.strip(),
7382
),
83+
ExampleCheck(
84+
name="blueprint build logs are retrievable",
85+
passed=hasattr(blueprint_logs, "logs"),
86+
details=f"blueprint_log_count={len(blueprint_logs.logs)}",
87+
),
88+
ExampleCheck(
89+
name="devbox logs are retrievable",
90+
passed=hasattr(devbox_logs, "logs"),
91+
details=f"devbox_log_count={len(devbox_logs.logs)}",
92+
),
7493
],
7594
)
7695

0 commit comments

Comments
 (0)