Skip to content

Commit d23288f

Browse files
committed
made command positional arg in exec and exec_async
1 parent bf8bf33 commit d23288f

18 files changed

Lines changed: 94 additions & 84 deletions

README-SDK.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,12 @@ runloop = RunloopSDK()
3838

3939
# Create a ready-to-use devbox
4040
with runloop.devbox.create(name="my-devbox") as devbox:
41-
result = devbox.cmd.exec(command="echo 'Hello from Runloop!'")
41+
result = devbox.cmd.exec("echo 'Hello from Runloop!'")
4242
print(result.stdout())
4343

4444
# Stream stdout in real time
4545
devbox.cmd.exec(
46-
command="ls -la",
46+
"ls -la",
4747
stdout=lambda line: print("stdout:", line),
4848
)
4949

@@ -68,13 +68,13 @@ from runloop_api_client import AsyncRunloopSDK
6868
async def main():
6969
runloop = AsyncRunloopSDK()
7070
async with await runloop.devbox.create(name="async-devbox") as devbox:
71-
result = await devbox.cmd.exec(command="pwd")
71+
result = await devbox.cmd.exec("pwd")
7272
print(await result.stdout())
7373

7474
def capture(line: str) -> None:
7575
print(">>", line)
7676

77-
await devbox.cmd.exec(command="ls", stdout=capture)
77+
await devbox.cmd.exec("ls", stdout=capture)
7878

7979
asyncio.run(main())
8080
```
@@ -147,13 +147,13 @@ Execute commands synchronously or asynchronously:
147147

148148
```python
149149
# Synchronous command execution (waits for completion)
150-
result = devbox.cmd.exec(command="ls -la")
150+
result = devbox.cmd.exec("ls -la")
151151
print("Output:", result.stdout())
152152
print("Exit code:", result.exit_code)
153153
print("Success:", result.success)
154154

155155
# Asynchronous command execution (returns immediately)
156-
execution = devbox.cmd.exec_async(command="npm run dev")
156+
execution = devbox.cmd.exec_async("npm run dev")
157157

158158
# Check execution status
159159
state = execution.get_state()
@@ -173,7 +173,7 @@ The `Execution` object provides fine-grained control over asynchronous command e
173173

174174
```python
175175
# Start a long-running process
176-
execution = devbox.cmd.exec_async(command="python train_model.py")
176+
execution = devbox.cmd.exec_async("python train_model.py")
177177

178178
# Get the execution ID
179179
print("Execution ID:", execution.execution_id)
@@ -208,10 +208,10 @@ The `ExecutionResult` object contains the output and exit status of a completed
208208

209209
```python
210210
# From synchronous execution
211-
result = devbox.cmd.exec(command="ls -la /tmp")
211+
result = devbox.cmd.exec("ls -la /tmp")
212212

213213
# Or from asynchronous execution
214-
execution = devbox.cmd.exec_async(command="echo 'test'")
214+
execution = devbox.cmd.exec_async("echo 'test'")
215215
result = execution.result()
216216

217217
# Access execution results
@@ -250,7 +250,7 @@ def handle_output(line: str) -> None:
250250
print("LOG:", line)
251251

252252
result = devbox.cmd.exec(
253-
command="python train.py",
253+
"python train.py",
254254
stdout=handle_output,
255255
stderr=lambda line: print("ERR:", line),
256256
output=lambda line: print("ANY:", line),
@@ -267,7 +267,7 @@ def capture(line: str) -> None:
267267
log_queue.put_nowait(line)
268268

269269
await devbox.cmd.exec(
270-
command="tail -f /var/log/app.log",
270+
"tail -f /var/log/app.log",
271271
stdout=capture,
272272
)
273273
```
@@ -365,13 +365,13 @@ Devboxes support context managers for automatic cleanup:
365365
```python
366366
# Synchronous
367367
with runloop.devbox.create(name="temp-devbox") as devbox:
368-
result = devbox.cmd.exec(command="echo 'Hello'")
368+
result = devbox.cmd.exec("echo 'Hello'")
369369
print(result.stdout())
370370
# devbox is automatically shutdown when exiting the context
371371

372372
# Asynchronous
373373
async with await runloop.devbox.create(name="temp-devbox") as devbox:
374-
result = await devbox.cmd.exec(command="echo 'Hello'")
374+
result = await devbox.cmd.exec("echo 'Hello'")
375375
print(await result.stdout())
376376
# devbox is automatically shutdown when exiting the context
377377
```
@@ -586,7 +586,7 @@ devbox = runloop.devbox.create(
586586
)
587587

588588
# The storage object is now accessible at /home/user/data.txt in the devbox
589-
result = devbox.cmd.exec(command="cat /home/user/data.txt")
589+
result = devbox.cmd.exec("cat /home/user/data.txt")
590590
print(result.stdout()) # "Hello, World!"
591591

592592
# Mount archived objects (tar, tgz, gzip) - they get extracted to a directory
@@ -607,7 +607,7 @@ devbox_with_archive = runloop.devbox.create(
607607
)
608608

609609
# Access extracted archive contents
610-
result = devbox_with_archive.cmd.exec(command="ls -la /home/user/project/")
610+
result = devbox_with_archive.cmd.exec("ls -la /home/user/project/")
611611
print(result.stdout())
612612
```
613613

@@ -634,7 +634,7 @@ runloop = RunloopSDK()
634634

635635
try:
636636
devbox = runloop.devbox.create(name="example-devbox")
637-
result = devbox.cmd.exec(command="invalid-command")
637+
result = devbox.cmd.exec("invalid-command")
638638
except runloop_api_client.APIConnectionError as e:
639639
print("The server could not be reached")
640640
print(e.__cause__) # an underlying Exception, likely raised within httpx.
@@ -694,14 +694,14 @@ async def main():
694694

695695
# All the same operations, but with await
696696
async with await runloop.devbox.create(name="async-devbox") as devbox:
697-
result = await devbox.cmd.exec(command="pwd")
697+
result = await devbox.cmd.exec("pwd")
698698
print(await result.stdout())
699699

700700
# Streaming (note: callbacks must be synchronous)
701701
def capture(line: str) -> None:
702702
print(">>", line)
703703

704-
await devbox.cmd.exec(command="ls", stdout=capture)
704+
await devbox.cmd.exec("ls", stdout=capture)
705705

706706
# Async file operations
707707
await devbox.file.write(path="/tmp/test.txt", contents="Hello")

docs/index.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Synchronous Example
3434
3535
# Create a ready-to-use devbox
3636
with runloop.devbox.create(name="my-devbox") as devbox:
37-
result = devbox.cmd.exec(command="echo 'Hello from Runloop!'")
37+
result = devbox.cmd.exec("echo 'Hello from Runloop!'")
3838
print(result.stdout())
3939
4040
Asynchronous Example
@@ -49,7 +49,7 @@ Asynchronous Example
4949
runloop = AsyncRunloopSDK()
5050
5151
async with await runloop.devbox.create(name="my-devbox") as devbox:
52-
result = await devbox.cmd.exec(command="echo 'Hello from Runloop!'")
52+
result = await devbox.cmd.exec("echo 'Hello from Runloop!'")
5353
print(await result.stdout())
5454
5555
asyncio.run(main())

src/runloop_api_client/sdk/_types.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from ..types.devbox_upload_file_params import DevboxUploadFileParams
1515
from ..types.devbox_create_tunnel_params import DevboxCreateTunnelParams
1616
from ..types.devbox_download_file_params import DevboxDownloadFileParams
17-
from ..types.devbox_execute_async_params import DevboxExecuteAsyncParams
17+
from ..types.devbox_execute_async_params import DevboxNiceExecuteAsyncParams
1818
from ..types.devbox_remove_tunnel_params import DevboxRemoveTunnelParams
1919
from ..types.devbox_snapshot_disk_params import DevboxSnapshotDiskParams
2020
from ..types.devbox_read_file_contents_params import DevboxReadFileContentsParams
@@ -70,11 +70,11 @@ class SDKDevboxCreateFromImageParams(DevboxBaseCreateParams, LongPollingRequestO
7070
pass
7171

7272

73-
class SDKDevboxExecuteParams(DevboxExecuteAsyncParams, ExecuteStreamingCallbacks, LongPollingRequestOptions):
73+
class SDKDevboxExecuteParams(DevboxNiceExecuteAsyncParams, ExecuteStreamingCallbacks, LongPollingRequestOptions):
7474
pass
7575

7676

77-
class SDKDevboxExecuteAsyncParams(DevboxExecuteAsyncParams, ExecuteStreamingCallbacks, LongRequestOptions):
77+
class SDKDevboxExecuteAsyncParams(DevboxNiceExecuteAsyncParams, ExecuteStreamingCallbacks, LongRequestOptions):
7878
pass
7979

8080

src/runloop_api_client/sdk/async_.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ class AsyncRunloopSDK:
496496
Example:
497497
>>> runloop = AsyncRunloopSDK() # Uses RUNLOOP_API_KEY env var
498498
>>> devbox = await runloop.devbox.create(name="my-devbox")
499-
>>> result = await devbox.cmd.exec(command="echo 'hello'")
499+
>>> result = await devbox.cmd.exec("echo 'hello'")
500500
>>> print(await result.stdout())
501501
>>> await devbox.shutdown()
502502
"""

src/runloop_api_client/sdk/async_devbox.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
from ..types.devboxes import ExecutionUpdateChunk
3838
from .async_execution import AsyncExecution, _AsyncStreamingGroup
3939
from .async_execution_result import AsyncExecutionResult
40-
from ..types.devbox_execute_async_params import DevboxExecuteAsyncParams
40+
from ..types.devbox_execute_async_params import DevboxNiceExecuteAsyncParams
4141
from ..types.devbox_async_execution_detail_view import DevboxAsyncExecutionDetailView
4242

4343
StreamFactory = Callable[[], Awaitable[AsyncStream[ExecutionUpdateChunk]]]
@@ -56,7 +56,7 @@ class AsyncDevbox:
5656
Example:
5757
>>> devbox = await sdk.devbox.create(name="my-devbox")
5858
>>> async with devbox:
59-
... result = await devbox.cmd.exec(command="echo 'hello'")
59+
... result = await devbox.cmd.exec("echo 'hello'")
6060
... print(await result.stdout())
6161
# Devbox is automatically shut down on exit
6262
"""
@@ -368,6 +368,7 @@ def __init__(self, devbox: AsyncDevbox) -> None:
368368

369369
async def exec(
370370
self,
371+
command: str,
371372
**params: Unpack[SDKDevboxExecuteParams],
372373
) -> AsyncExecutionResult:
373374
"""Execute a command synchronously and wait for completion.
@@ -377,7 +378,7 @@ async def exec(
377378
:rtype: AsyncExecutionResult
378379
379380
Example:
380-
>>> result = await devbox.cmd.exec(command="echo 'hello'")
381+
>>> result = await devbox.cmd.exec("echo 'hello'")
381382
>>> print(await result.stdout())
382383
>>> print(f"Exit code: {result.exit_code}")
383384
"""
@@ -386,7 +387,8 @@ async def exec(
386387

387388
execution: DevboxAsyncExecutionDetailView = await client.devboxes.execute_async(
388389
devbox.id,
389-
**filter_params(params, DevboxExecuteAsyncParams),
390+
command=command,
391+
**filter_params(params, DevboxNiceExecuteAsyncParams),
390392
**filter_params(params, LongRequestOptions),
391393
)
392394
streaming_group = devbox._start_streaming(
@@ -421,6 +423,7 @@ async def command_coro() -> DevboxAsyncExecutionDetailView:
421423

422424
async def exec_async(
423425
self,
426+
command: str,
424427
**params: Unpack[SDKDevboxExecuteAsyncParams],
425428
) -> AsyncExecution:
426429
"""Execute a command asynchronously without waiting for completion.
@@ -434,7 +437,7 @@ async def exec_async(
434437
:rtype: AsyncExecution
435438
436439
Example:
437-
>>> execution = await devbox.cmd.exec_async(command="sleep 10")
440+
>>> execution = await devbox.cmd.exec_async("sleep 10")
438441
>>> state = await execution.get_state()
439442
>>> print(f"Status: {state.status}")
440443
>>> await execution.kill() # Terminate early if needed
@@ -444,7 +447,8 @@ async def exec_async(
444447

445448
execution: DevboxAsyncExecutionDetailView = await client.devboxes.execute_async(
446449
devbox.id,
447-
**filter_params(params, DevboxExecuteAsyncParams),
450+
command=command,
451+
**filter_params(params, DevboxNiceExecuteAsyncParams),
448452
**filter_params(params, LongRequestOptions),
449453
)
450454

src/runloop_api_client/sdk/async_execution.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class AsyncExecution:
4343
terminate the running process. Created by ``await devbox.cmd.exec_async()``.
4444
4545
Example:
46-
>>> execution = await devbox.cmd.exec_async(command="python train.py")
46+
>>> execution = await devbox.cmd.exec_async("python train.py")
4747
>>> state = await execution.get_state()
4848
>>> if state.status == "running":
4949
... await execution.kill()

src/runloop_api_client/sdk/devbox.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
from ..lib.polling import PollingConfig
3939
from ..types.devboxes import ExecutionUpdateChunk
4040
from .execution_result import ExecutionResult
41-
from ..types.devbox_execute_async_params import DevboxExecuteAsyncParams
41+
from ..types.devbox_execute_async_params import DevboxNiceExecuteAsyncParams
4242
from ..types.devbox_async_execution_detail_view import DevboxAsyncExecutionDetailView
4343

4444
if TYPE_CHECKING:
@@ -386,6 +386,7 @@ def __init__(self, devbox: Devbox) -> None:
386386

387387
def exec(
388388
self,
389+
command: str,
389390
**params: Unpack[SDKDevboxExecuteParams],
390391
) -> ExecutionResult:
391392
"""Execute a command synchronously and wait for completion.
@@ -395,7 +396,7 @@ def exec(
395396
:rtype: ExecutionResult
396397
397398
Example:
398-
>>> result = devbox.cmd.exec(command="ls -la")
399+
>>> result = devbox.cmd.exec("ls -la")
399400
>>> print(result.stdout())
400401
>>> print(f"Exit code: {result.exit_code}")
401402
"""
@@ -404,7 +405,8 @@ def exec(
404405

405406
execution: DevboxAsyncExecutionDetailView = client.devboxes.execute_async(
406407
devbox.id,
407-
**filter_params(params, DevboxExecuteAsyncParams),
408+
command=command,
409+
**filter_params(params, DevboxNiceExecuteAsyncParams),
408410
**filter_params(params, LongRequestOptions),
409411
)
410412
streaming_group = devbox._start_streaming(
@@ -429,6 +431,7 @@ def exec(
429431

430432
def exec_async(
431433
self,
434+
command: str,
432435
**params: Unpack[SDKDevboxExecuteAsyncParams],
433436
) -> Execution:
434437
"""Execute a command asynchronously without waiting for completion.
@@ -442,7 +445,7 @@ def exec_async(
442445
:rtype: Execution
443446
444447
Example:
445-
>>> execution = devbox.cmd.exec_async(command="sleep 10")
448+
>>> execution = devbox.cmd.exec_async("sleep 10")
446449
>>> state = execution.get_state()
447450
>>> print(f"Status: {state.status}")
448451
>>> execution.kill() # Terminate early if needed
@@ -452,7 +455,8 @@ def exec_async(
452455

453456
execution: DevboxAsyncExecutionDetailView = client.devboxes.execute_async(
454457
devbox.id,
455-
**filter_params(params, DevboxExecuteAsyncParams),
458+
command=command,
459+
**filter_params(params, DevboxNiceExecuteAsyncParams),
456460
**filter_params(params, LongRequestOptions),
457461
)
458462

src/runloop_api_client/sdk/execution.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class Execution:
4242
the running process. Created by ``devbox.cmd.exec_async()``.
4343
4444
Example:
45-
>>> execution = devbox.cmd.exec_async(command="python train.py")
45+
>>> execution = devbox.cmd.exec_async("python train.py")
4646
>>> state = execution.get_state()
4747
>>> if state.status == "running":
4848
... execution.kill()

src/runloop_api_client/sdk/sync.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ class RunloopSDK:
491491
Example:
492492
>>> runloop = RunloopSDK() # Uses RUNLOOP_API_KEY env var
493493
>>> devbox = runloop.devbox.create(name="my-devbox")
494-
>>> result = devbox.cmd.exec(command="echo 'hello'")
494+
>>> result = devbox.cmd.exec("echo 'hello'")
495495
>>> print(result.stdout())
496496
>>> devbox.shutdown()
497497
"""

src/runloop_api_client/types/devbox_execute_async_params.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,7 @@
88
__all__ = ["DevboxExecuteAsyncParams"]
99

1010

11-
class DevboxExecuteAsyncParams(TypedDict, total=False):
12-
command: Required[str]
13-
"""The command to execute via the Devbox shell.
14-
15-
By default, commands are run from the user home directory unless shell_name is
16-
specified. If shell_name is specified the command is run from the directory
17-
based on the recent state of the persistent shell.
18-
"""
19-
11+
class DevboxNiceExecuteAsyncParams(TypedDict, total=False):
2012
attach_stdin: Optional[bool]
2113
"""Whether to attach stdin streaming for async commands.
2214
@@ -29,3 +21,13 @@ class DevboxExecuteAsyncParams(TypedDict, total=False):
2921
When using a persistent shell, the command will run from the directory at the
3022
end of the previous command and environment variables will be preserved.
3123
"""
24+
25+
26+
class DevboxExecuteAsyncParams(DevboxNiceExecuteAsyncParams, total=False):
27+
command: Required[str]
28+
"""The command to execute via the Devbox shell.
29+
30+
By default, commands are run from the user home directory unless shell_name is
31+
specified. If shell_name is specified the command is run from the directory
32+
based on the recent state of the persistent shell.
33+
"""

0 commit comments

Comments
 (0)