voidrun 0.0.2__tar.gz → 0.0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. voidrun-0.0.4/PKG-INFO +899 -0
  2. voidrun-0.0.4/README.md +875 -0
  3. {voidrun-0.0.2 → voidrun-0.0.4}/pyproject.toml +1 -1
  4. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/create_sandbox_request.py +3 -3
  5. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/client.py +1 -1
  6. voidrun-0.0.2/PKG-INFO +0 -214
  7. voidrun-0.0.2/README.md +0 -190
  8. {voidrun-0.0.2 → voidrun-0.0.4}/.gitignore +0 -0
  9. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/__init__.py +0 -0
  10. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/__init__.py +0 -0
  11. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/api/__init__.py +0 -0
  12. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/api/authentication_api.py +0 -0
  13. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/api/execution_api.py +0 -0
  14. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/api/file_system_api.py +0 -0
  15. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/api/images_api.py +0 -0
  16. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/api/organizations_api.py +0 -0
  17. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/api/sandboxes_api.py +0 -0
  18. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/api/system_api.py +0 -0
  19. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/api_client.py +0 -0
  20. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/api_response.py +0 -0
  21. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/configuration.py +0 -0
  22. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/exceptions.py +0 -0
  23. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/__init__.py +0 -0
  24. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/activate_api_key_request.py +0 -0
  25. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/api_key_response.py +0 -0
  26. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/api_response_sandbox.py +0 -0
  27. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/api_response_sandboxes_list.py +0 -0
  28. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/api_response_sandboxes_list_meta.py +0 -0
  29. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/command_kill_response.py +0 -0
  30. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/command_list_response.py +0 -0
  31. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/command_run_response.py +0 -0
  32. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/command_wait_response.py +0 -0
  33. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/compress_request.py +0 -0
  34. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/create_file_request.py +0 -0
  35. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/create_image_request.py +0 -0
  36. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/create_pty_session200_response.py +0 -0
  37. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/create_pty_session200_response_all_of_data.py +0 -0
  38. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/create_pty_session_request.py +0 -0
  39. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/create_sandbox201_response.py +0 -0
  40. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/disk_usage.py +0 -0
  41. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/error_response.py +0 -0
  42. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/exec_request.py +0 -0
  43. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/exec_response.py +0 -0
  44. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/exec_response_data.py +0 -0
  45. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/execute_in_session_request.py +0 -0
  46. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/extract_request.py +0 -0
  47. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/file_event.py +0 -0
  48. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/file_info.py +0 -0
  49. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/file_stats.py +0 -0
  50. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/generate_api_key_request.py +0 -0
  51. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/generated_api_key_response.py +0 -0
  52. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/get_org_users200_response.py +0 -0
  53. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/get_org_users200_response_all_of_data_inner.py +0 -0
  54. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/get_pty_buffer200_response.py +0 -0
  55. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/get_pty_buffer200_response_all_of_data.py +0 -0
  56. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/get_version200_response.py +0 -0
  57. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/image.py +0 -0
  58. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/kill_background_process_request.py +0 -0
  59. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/list_files200_response.py +0 -0
  60. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/list_files200_response_data.py +0 -0
  61. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/list_pty_sessions200_response.py +0 -0
  62. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/list_pty_sessions200_response_all_of_data.py +0 -0
  63. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/organization.py +0 -0
  64. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/process_info.py +0 -0
  65. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/pty_session_info.py +0 -0
  66. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/register_request.py +0 -0
  67. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/register_response.py +0 -0
  68. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/register_response_data.py +0 -0
  69. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/resize_terminal_request.py +0 -0
  70. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/restore_sandbox_request.py +0 -0
  71. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/run_background_command_request.py +0 -0
  72. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/sandbox.py +0 -0
  73. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/session_exec_request.py +0 -0
  74. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/session_exec_response.py +0 -0
  75. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/session_exec_stream_request.py +0 -0
  76. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/snapshot.py +0 -0
  77. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/start_watch200_response.py +0 -0
  78. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/start_watch200_response_data.py +0 -0
  79. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/start_watch_request.py +0 -0
  80. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/models/success_response.py +0 -0
  81. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/py.typed +0 -0
  82. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/api_client/rest.py +0 -0
  83. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/commands.py +0 -0
  84. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/fs.py +0 -0
  85. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/interpreter.py +0 -0
  86. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/pty.py +0 -0
  87. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/response.py +0 -0
  88. {voidrun-0.0.2 → voidrun-0.0.4}/voidrun/sandbox.py +0 -0
voidrun-0.0.4/PKG-INFO ADDED
@@ -0,0 +1,899 @@
1
+ Metadata-Version: 2.4
2
+ Name: voidrun
3
+ Version: 0.0.4
4
+ Summary: Python SDK for VoidRun AI Sandbox
5
+ Project-URL: Homepage, https://voidrun.io
6
+ Project-URL: Documentation, https://docs.voidrun.io
7
+ Project-URL: Repository, https://github.com/voidrun/py-sdk
8
+ Author-email: VoidRun Team <support@voidrun.io>
9
+ License-Expression: MIT
10
+ Classifier: Framework :: AsyncIO
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Requires-Python: >=3.9
15
+ Requires-Dist: certifi
16
+ Requires-Dist: httpx>=0.24.0
17
+ Requires-Dist: pydantic>=2.0.0
18
+ Requires-Dist: python-dateutil>=2.8.2
19
+ Requires-Dist: python-dotenv>=1.0.0
20
+ Requires-Dist: typing-extensions>=4.0.0
21
+ Requires-Dist: urllib3>=2.0.0
22
+ Requires-Dist: websockets>=11.0
23
+ Description-Content-Type: text/markdown
24
+
25
+ # VoidRun Python SDK
26
+
27
+ A powerful Python SDK for interacting with VoidRun AI Sandboxes. Execute code, manage files, watch file changes, and interact with pseudo-terminals in isolated environments.
28
+
29
+ [![PyPI version](https://img.shields.io/pypi/v/voidrun)](https://pypi.org/project/voidrun/)
30
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
31
+
32
+ ## Features
33
+
34
+ - 🏗️ **Sandbox Management** - Create, list, start, stop, pause, resume, and remove sandboxes
35
+ - 🚀 **Code Execution** - Execute commands with real-time streaming output capture
36
+ - 📁 **File Operations** - Create, read, delete, compress, and extract files
37
+ - 👀 **File Watching** - Monitor file changes in real-time via WebSocket
38
+ - 💻 **Pseudo-Terminal (PTY)** - Interactive terminal sessions (ephemeral & persistent)
39
+ - 🧠 **Code Interpreter** - Easy multi-language code execution (Python, JavaScript, Bash)
40
+ - ⚡ **Background Commands** - Run, list, kill, and attach to background processes
41
+ - 🎯 **Sync & Async** - Both synchronous and asynchronous APIs
42
+
43
+ ## Installation
44
+
45
+ With pip:
46
+
47
+ ```bash
48
+ pip install voidrun
49
+ ```
50
+
51
+ With Poetry:
52
+
53
+ ```bash
54
+ poetry add voidrun
55
+ ```
56
+
57
+ ## Quick Start
58
+
59
+ ### Basic Usage (Sync)
60
+
61
+ ```python
62
+ from voidrun import VoidRun
63
+
64
+ # Initialize the SDK with your credentials
65
+ vr = VoidRun(api_key="your-api-key-here")
66
+
67
+ # Create a sandbox
68
+ sandbox = vr.sandboxes.create(mem=1024, cpu=1).data
69
+
70
+ # Execute a command
71
+ result = sandbox.exec('echo "Hello from VoidRun"')
72
+ print(result.data.data.stdout)
73
+
74
+ # Clean up
75
+ sandbox.delete()
76
+ ```
77
+
78
+ ### Basic Usage (Async)
79
+
80
+ ```python
81
+ import asyncio
82
+ from voidrun import AsyncVoidRun
83
+
84
+ async def main():
85
+ vr = AsyncVoidRun(api_key="your-api-key-here")
86
+
87
+ sandbox = await vr.sandboxes.create(mem=1024, cpu=1)
88
+
89
+ result = sandbox.exec('echo "Hello from VoidRun"')
90
+ print(result.data.data.stdout)
91
+
92
+ await sandbox.delete_async()
93
+ await vr.aclose()
94
+
95
+ asyncio.run(main())
96
+ ```
97
+
98
+ ## Core Concepts
99
+
100
+ ### Sandboxes
101
+
102
+ An isolated environment where you can execute code, manage files, and run terminals.
103
+
104
+ ```python
105
+ # Create a sandbox with options (sync)
106
+ sandbox = vr.sandboxes.create(
107
+ name="my-sandbox", # Optional: Sandbox name
108
+ mem=1024, # Memory in MB (optional, has defaults)
109
+ cpu=1, # CPU cores (optional, has defaults)
110
+ image="template-id", # Optional: Image ID
111
+ env_vars={ # Optional: Environment variables
112
+ "DEBUG": "true",
113
+ "LOG_LEVEL": "info"
114
+ }
115
+ ).data
116
+
117
+ # Using context manager (auto-cleanup)
118
+ with vr.sandboxes.create(name="my-sandbox").data as sandbox:
119
+ # Work with sandbox
120
+ pass
121
+ # Sandbox automatically deleted
122
+
123
+ # List all sandboxes
124
+ resp = vr.sandboxes.list()
125
+ sandboxes = resp.data
126
+ print(f"Total sandboxes: {len(sandboxes)}")
127
+
128
+ # Get a specific sandbox
129
+ existing = vr.sandboxes.get(sandbox_id).data
130
+
131
+ # Sandbox lifecycle management
132
+ sandbox.start() # Start a stopped sandbox
133
+ sandbox.stop() # Stop a running sandbox
134
+ sandbox.pause() # Pause a running sandbox
135
+ sandbox.resume() # Resume a paused sandbox
136
+
137
+ # Remove a sandbox
138
+ sandbox.delete()
139
+ ```
140
+
141
+ ### Code Execution
142
+
143
+ Execute commands and capture output, errors, and exit codes.
144
+
145
+ #### Synchronous Execution
146
+
147
+ ```python
148
+ result = sandbox.exec("ls -la /home")
149
+
150
+ print(result.data.data.stdout) # standard output
151
+ print(result.data.data.stderr) # standard error
152
+ print(result.data.data.exit_code) # exit code
153
+ ```
154
+
155
+ #### Streaming Execution (SSE)
156
+
157
+ For real-time output, provide streaming handlers:
158
+
159
+ ```python
160
+ def on_stdout(data):
161
+ print("stdout:", data)
162
+
163
+ def on_stderr(data):
164
+ print("stderr:", data)
165
+
166
+ def on_exit(result):
167
+ print("exit:", result)
168
+
169
+ def on_error(error):
170
+ print("error:", error)
171
+
172
+ sandbox.exec_stream(
173
+ "seq 1 10 | while read i; do echo \"Line $i\"; sleep 1; done",
174
+ on_stdout=on_stdout,
175
+ on_stderr=on_stderr,
176
+ on_exit=on_exit,
177
+ on_error=on_error
178
+ )
179
+ ```
180
+
181
+ #### Execution with Options
182
+
183
+ ```python
184
+ result = sandbox.exec(
185
+ "echo $MY_VAR && pwd",
186
+ cwd="/tmp", # Working directory
187
+ env={"MY_VAR": "test_value"}, # Environment variables
188
+ timeout=30 # Timeout in seconds
189
+ )
190
+ ```
191
+
192
+ ### Code Interpreter
193
+
194
+ Execute code in multiple programming languages with a simple, intuitive API.
195
+
196
+ ```python
197
+ # Execute Python code
198
+ result = sandbox.interpreter.run('print(2 + 2)', language="python")
199
+ print(result.data.stdout.strip()) # "4"
200
+ print(result.data.success) # True
201
+
202
+ # Execute JavaScript code
203
+ js_result = sandbox.interpreter.run('console.log("Hello")', language="javascript")
204
+
205
+ # Check execution result
206
+ print(result.data.exit_code) # 0 for success
207
+ print(result.data.results) # Parsed results
208
+ print(result.data.logs) # {"stdout": [...], "stderr": [...]}
209
+ ```
210
+
211
+ **Supported Languages:** `python`, `javascript`, `typescript`, `node`, `bash`, `sh`
212
+
213
+ ### Background Commands
214
+
215
+ Run long-running processes in the background and manage them.
216
+
217
+ ```python
218
+ # Start a background process
219
+ run_result = sandbox.commands.run(
220
+ "sleep 100 && echo 'Done'", # command
221
+ {"DEBUG": "true"}, # env (optional)
222
+ "/tmp", # cwd (optional)
223
+ 0 # timeout (0 = no timeout)
224
+ )
225
+ print(run_result.data.pid) # Process ID
226
+
227
+ # List all running processes
228
+ list_result = sandbox.commands.list()
229
+ print(list_result.data) # Array of ProcessInfo
230
+
231
+ # Attach to a process and stream output
232
+ def on_stdout(data):
233
+ print(data)
234
+
235
+ def on_stderr(data):
236
+ print(data)
237
+
238
+ def on_exit(result):
239
+ print("Process exited:", result)
240
+
241
+ sandbox.commands.connect(
242
+ run_result.data.pid,
243
+ on_stdout=on_stdout,
244
+ on_stderr=on_stderr,
245
+ on_exit=on_exit
246
+ )
247
+
248
+ # Wait for a process to complete
249
+ wait_result = sandbox.commands.wait(run_result.data.pid)
250
+ print(wait_result.data.exit_code)
251
+
252
+ # Kill a running process
253
+ kill_result = sandbox.commands.kill(run_result.data.pid)
254
+ print(kill_result.data.success)
255
+ ```
256
+
257
+ ### File Operations
258
+
259
+ Create, read, update, and manage files in the sandbox.
260
+
261
+ ```python
262
+ # Create a file
263
+ sandbox.fs.create_file("/tmp/hello.txt")
264
+
265
+ # Upload content to file
266
+ sandbox.fs.upload_file("/tmp/hello.txt", "Hello, World!")
267
+
268
+ # Upload from local file path
269
+ sandbox.fs.upload_file_from_path("/tmp/remote.txt", "/local/file.txt")
270
+
271
+ # Read a file
272
+ data = sandbox.fs.download_file("/tmp/hello.txt")
273
+ content = data.decode("utf-8")
274
+
275
+ # Delete a file
276
+ sandbox.fs.delete_file("/tmp/hello.txt")
277
+
278
+ # List directory
279
+ result = sandbox.fs.list_files("/tmp")
280
+ files = result.data
281
+ print([f.name for f in files])
282
+
283
+ # Get file stats
284
+ stats = sandbox.fs.stat_file("/tmp/hello.txt")
285
+
286
+ # Create directory
287
+ sandbox.fs.create_directory("/tmp/mydir")
288
+
289
+ # Move file
290
+ sandbox.fs.move_file("/tmp/file.txt", "/tmp/newfile.txt")
291
+
292
+ # Copy file
293
+ sandbox.fs.copy_file("/tmp/file.txt", "/tmp/copy.txt")
294
+
295
+ # Change permissions
296
+ sandbox.fs.change_permissions("/tmp/file.txt", "755")
297
+
298
+ # Head/Tail - read first or last lines
299
+ head = sandbox.fs.head_tail("/tmp/file.txt", head=True, lines=10)
300
+ tail = sandbox.fs.head_tail("/tmp/file.txt", head=False, lines=10)
301
+
302
+ # Search files by pattern
303
+ search = sandbox.fs.search_files("/tmp", "*.txt")
304
+
305
+ # Get folder size
306
+ size = sandbox.fs.disk_usage("/tmp")
307
+
308
+ # Compress files
309
+ archive = sandbox.fs.compress_file("/tmp", "tar.gz")
310
+ print(archive.data)
311
+
312
+ # Extract archive
313
+ sandbox.fs.extract_archive("/tmp/archive.tar.gz", "/tmp/extracted")
314
+ ```
315
+
316
+ ### File Watching (Async)
317
+
318
+ Monitor file changes in real-time.
319
+
320
+ ```python
321
+ import asyncio
322
+ from voidrun import AsyncVoidRun
323
+
324
+ async def watch_tmp():
325
+ vr = AsyncVoidRun()
326
+ sandbox = await vr.sandboxes.create()
327
+
328
+ watcher = await sandbox.fs.watch(
329
+ "/app",
330
+ recursive=True,
331
+ on_event=lambda evt: print(f"File changed: {evt.get('path')} - {evt.get('type')}"),
332
+ on_error=lambda err: print("Watch error:", err),
333
+ )
334
+
335
+ # Keep watching for a while
336
+ await asyncio.sleep(60)
337
+
338
+ # Stop watching
339
+ watcher.close()
340
+
341
+ await sandbox.delete_async()
342
+ await vr.aclose()
343
+
344
+ asyncio.run(watch_tmp())
345
+ ```
346
+
347
+ ### Pseudo-Terminal (PTY)
348
+
349
+ Interactive terminal sessions with two modes:
350
+
351
+ #### Ephemeral Sessions (Temporary)
352
+
353
+ ```python
354
+ import asyncio
355
+ from voidrun import AsyncVoidRun
356
+
357
+ async def ephemeral_pty():
358
+ vr = AsyncVoidRun()
359
+ sandbox = await vr.sandboxes.create()
360
+
361
+ # Connect to ephemeral PTY (no session management - temporary shell)
362
+ pty = await sandbox.pty.connect(
363
+ on_data=lambda data: print(data, end=""),
364
+ on_error=lambda err: print("PTY error:", err),
365
+ )
366
+
367
+ # Send commands
368
+ pty.send_input('echo "Hello"\n')
369
+ pty.send_input("pwd\n")
370
+
371
+ await asyncio.sleep(2)
372
+
373
+ # Close connection
374
+ await pty.close()
375
+
376
+ await sandbox.delete_async()
377
+ await vr.aclose()
378
+
379
+ asyncio.run(ephemeral_pty())
380
+ ```
381
+
382
+ #### Persistent Sessions
383
+
384
+ ```python
385
+ import asyncio
386
+ from voidrun import AsyncVoidRun
387
+
388
+ async def persistent_pty():
389
+ vr = AsyncVoidRun()
390
+ sandbox = await vr.sandboxes.create()
391
+
392
+ # Create a persistent session
393
+ response = sandbox.pty.create_session()
394
+ session_id = response.data.data.session_id
395
+
396
+ # Connect to the session
397
+ pty = await sandbox.pty.connect(
398
+ session_id=session_id,
399
+ on_data=lambda data: print(data, end=""),
400
+ )
401
+
402
+ # Send commands
403
+ pty.send_input('echo "Hello"\n')
404
+
405
+ # Close connection (session persists)
406
+ await pty.close()
407
+
408
+ # Reconnect later - session and output persist
409
+ reconnected = await sandbox.pty.connect(
410
+ session_id=session_id,
411
+ on_data=lambda data: print(data, end=""), # Includes buffered output
412
+ )
413
+
414
+ await reconnected.close()
415
+
416
+ # Delete the session when done
417
+ sandbox.pty.delete_session(session_id)
418
+
419
+ await sandbox.delete_async()
420
+ await vr.aclose()
421
+
422
+ asyncio.run(persistent_pty())
423
+ ```
424
+
425
+ #### Interactive Commands
426
+
427
+ Run commands with automatic prompt detection:
428
+
429
+ ```python
430
+ pty = await sandbox.pty.connect(session_id=session_id)
431
+
432
+ output = await pty.run_command(
433
+ "ls -la",
434
+ timeout=5000,
435
+ prompt="# " # Prompt to detect (default: "# ")
436
+ )
437
+
438
+ print("Output:", output)
439
+ ```
440
+
441
+ #### Resize Terminal
442
+
443
+ ```python
444
+ await pty.resize(80, 24) # columns, rows
445
+ ```
446
+
447
+ #### Session Management
448
+
449
+ ```python
450
+ # List all sessions
451
+ sessions = sandbox.pty.list()
452
+
453
+ # Delete a session
454
+ sandbox.pty.delete_session(session_id)
455
+ ```
456
+
457
+ ## API Reference
458
+
459
+ ### VoidRun Class
460
+
461
+ Main synchronous client for interacting with the API.
462
+
463
+ ```python
464
+ vr = VoidRun(api_key="...")
465
+ ```
466
+
467
+ **Options:**
468
+
469
+ - `api_key: str` - API key (defaults to `os.environ.get("VR_API_KEY")`)
470
+
471
+ **Properties:**
472
+
473
+ - `sandboxes: SandboxesFacade` - Sandbox management interface
474
+
475
+ **Methods:**
476
+
477
+ - `sandboxes.create(...)` - Create a new sandbox
478
+ - `name?: str` - Sandbox name
479
+ - `cpu?: int` - CPU cores
480
+ - `mem?: int` - Memory in MB
481
+ - `image?: str` - Image ID
482
+ - `env_vars?: dict` - Environment variables
483
+ - `sandboxes.list(page=1, limit=50)` - List all sandboxes
484
+ - `sandboxes.get(id: str)` - Get a specific sandbox
485
+ - `sandboxes.delete(id: str)` - Delete a sandbox
486
+
487
+ ### AsyncVoidRun Class
488
+
489
+ Main asynchronous client for interacting with the API.
490
+
491
+ ```python
492
+ vr = AsyncVoidRun(api_key="...")
493
+ ```
494
+
495
+ **Options:** Same as `VoidRun`
496
+
497
+ **Methods:**
498
+
499
+ - `sandboxes.create(...)` - Create a new sandbox (async)
500
+ - `sandboxes.list(...)` - List all sandboxes (async)
501
+ - `sandboxes.get(id)` - Get a specific sandbox (async)
502
+ - `sandboxes.delete(id)` - Delete a sandbox (async)
503
+ - `aclose()` - Close the async client
504
+
505
+ ### Sandbox Class
506
+
507
+ Represents an isolated sandbox environment.
508
+
509
+ **Properties:**
510
+
511
+ - `id: str` - Sandbox ID
512
+ - `name: str` - Sandbox name
513
+ - `cpu: int` - CPU cores
514
+ - `mem: int` - Memory in MB
515
+ - `org_id: str` - Organization ID
516
+ - `status: str` - Sandbox status
517
+ - `env_vars: dict` - Environment variables
518
+ - `fs: FS` - File system interface
519
+ - `pty: PTY` - PTY interface
520
+ - `interpreter: Interpreter` - Code interpreter
521
+ - `commands: Commands` - Background commands interface
522
+
523
+ **Methods:**
524
+
525
+ - `exec(command: str, timeout=30, env=None, cwd=None)` - Execute a command
526
+ - `exec_stream(command: str, timeout=30, env=None, cwd=None, on_stdout=None, on_stderr=None, on_exit=None, on_error=None)` - Streaming execution
527
+ - `interpreter.run(code: str, language="python", timeout=60)` - Execute code
528
+ - `start()` - Start the sandbox
529
+ - `stop()` - Stop the sandbox
530
+ - `pause()` - Pause the sandbox
531
+ - `resume()` - Resume the sandbox
532
+ - `delete()` - Delete the sandbox
533
+ - `delete_async()` - Delete the sandbox (async)
534
+
535
+ **Exec Response:**
536
+
537
+ ```python
538
+ {
539
+ data: {
540
+ stdout: str, # standard output
541
+ stderr: str, # standard error
542
+ exit_code: int # exit code
543
+ }
544
+ }
545
+ ```
546
+
547
+ **Code Execution Result:**
548
+
549
+ ```python
550
+ {
551
+ success: bool,
552
+ results: Any, # Parsed results
553
+ stdout: str, # Combined stdout
554
+ stderr: str, # Combined stderr
555
+ error: Optional[str], # Error message if any
556
+ exit_code: Optional[int], # Process exit code
557
+ logs: {
558
+ stdout: List[str], # Individual stdout lines
559
+ stderr: List[str] # Individual stderr lines
560
+ }
561
+ }
562
+ ```
563
+
564
+ ### Commands Class
565
+
566
+ Background process management.
567
+
568
+ **Methods:**
569
+
570
+ - `run(command: str, env=None, cwd=None, timeout=0)` - Start a background process
571
+ - `list()` - List all running processes
572
+ - `kill(pid: int)` - Kill a process
573
+ - `connect(pid: int, on_stdout=None, on_stderr=None, on_exit=None, on_error=None)` - Attach to process output stream
574
+ - `wait(pid: int)` - Wait for process to complete
575
+
576
+ ### FS Class
577
+
578
+ Manage files and directories.
579
+
580
+ **Methods:**
581
+
582
+ - `create_file(path: str)` - Create a file
583
+ - `upload_file(path: str, content: str)` - Upload file content
584
+ - `upload_file_from_path(path: str, file_path: str)` - Upload from local path
585
+ - `download_file(path: str)` - Download file as bytes
586
+ - `delete_file(path: str)` - Delete a file/directory
587
+ - `list_files(path: str)` - List directory contents
588
+ - `stat_file(path: str)` - Get file metadata
589
+ - `create_directory(path: str)` - Create a directory
590
+ - `compress_file(path: str, format="tar.gz")` - Create archive
591
+ - `extract_archive(archive: str, dest=None)` - Extract archive
592
+ - `move_file(source: str, destination: str)` - Move/rename file
593
+ - `copy_file(source: str, destination: str)` - Copy file
594
+ - `change_permissions(path: str, mode: str)` - Change file permissions
595
+ - `head_tail(path: str, lines=10, head=True)` - Read file head/tail
596
+ - `search_files(path: str, pattern: str)` - Search files by pattern
597
+ - `disk_usage(path: str)` - Get folder size
598
+ - `watch(path: str, recursive=True, ignore_hidden=True, on_event=None, on_error=None)` - Watch for file changes (async)
599
+
600
+ ### PTY Class
601
+
602
+ Pseudo-terminal operations.
603
+
604
+ **Methods:**
605
+
606
+ - `list()` - List active sessions
607
+ - `create_session(cols=80, rows=24)` - Create a persistent session
608
+ - `connect(session_id=None, on_data=None, on_close=None, on_error=None)` - Connect to PTY
609
+ - `delete_session(session_id: str)` - Delete a session
610
+
611
+ ### PtySession Methods
612
+
613
+ - `send_input(data: str)` - Send data to terminal
614
+ - `run_command(cmd: str, timeout=30000, prompt="# ")` - Execute with prompt detection
615
+ - `resize(cols: int, rows: int)` - Resize terminal
616
+ - `close()` - Close connection
617
+
618
+ ## Examples
619
+
620
+ ### Execute Python Script
621
+
622
+ ```python
623
+ from voidrun import VoidRun
624
+
625
+ vr = VoidRun()
626
+ sandbox = vr.sandboxes.create(mem=1024, cpu=1).data
627
+
628
+ # Create Python script
629
+ sandbox.fs.create_file("/tmp/script.py")
630
+ sandbox.fs.upload_file(
631
+ "/tmp/script.py",
632
+ """
633
+ import sys
634
+ print("Python version:", sys.version)
635
+ print("Hello from Python!")
636
+ """
637
+ )
638
+
639
+ result = sandbox.exec("python3 /tmp/script.py")
640
+ print(result.data.data.stdout)
641
+
642
+ sandbox.delete()
643
+ ```
644
+
645
+ ### Code Interpreter Workflow
646
+
647
+ ```python
648
+ sandbox = vr.sandboxes.create(name="interpreter-demo").data
649
+
650
+ # Python data analysis
651
+ result = sandbox.interpreter.run("""
652
+ import json
653
+ data = [1, 2, 3, 4, 5]
654
+ result = {
655
+ "sum": sum(data),
656
+ "avg": sum(data) / len(data),
657
+ "max": max(data)
658
+ }
659
+ print(json.dumps(result))
660
+ """, language="python")
661
+
662
+ print(result.data.results) # Parsed JSON output
663
+
664
+ # JavaScript
665
+ js_result = sandbox.interpreter.run("""
666
+ const fib = (n) => n <= 1 ? n : fib(n-1) + fib(n-2);
667
+ console.log(fib(10));
668
+ """, language="javascript")
669
+
670
+ sandbox.delete()
671
+ ```
672
+
673
+ ### Background Process Management
674
+
675
+ ```python
676
+ sandbox = vr.sandboxes.create().data
677
+
678
+ # Start a long-running process
679
+ run_result = sandbox.commands.run("tail -f /var/log/syslog")
680
+ pid = run_result.data.pid
681
+
682
+ # Attach to stream output
683
+ sandbox.commands.connect(
684
+ pid,
685
+ on_stdout=lambda data: print(data),
686
+ on_exit=lambda result: print("Exited:", result)
687
+ )
688
+
689
+ # Later, kill the process
690
+ sandbox.commands.kill(pid)
691
+
692
+ sandbox.delete()
693
+ ```
694
+
695
+ ### Monitor Code Changes
696
+
697
+ ```python
698
+ import asyncio
699
+ from voidrun import AsyncVoidRun
700
+
701
+ async def watch_changes():
702
+ vr = AsyncVoidRun()
703
+ sandbox = await vr.sandboxes.create(mem=1024, cpu=1)
704
+
705
+ # Watch for file changes
706
+ watcher = await sandbox.fs.watch(
707
+ "/app/src",
708
+ recursive=True,
709
+ on_event=lambda event: print(f"File {event.get('type')}: {event.get('path')}"),
710
+ )
711
+
712
+ # Keep watching for a while
713
+ await asyncio.sleep(60)
714
+
715
+ watcher.close()
716
+ await sandbox.delete_async()
717
+ await vr.aclose()
718
+
719
+ asyncio.run(watch_changes())
720
+ ```
721
+
722
+ ### Build & Test Workflow
723
+
724
+ ```python
725
+ sandbox = vr.sandboxes.create(mem=2048, cpu=2).data
726
+
727
+ # Upload source code
728
+ sandbox.fs.create_file("/app/main.js")
729
+ sandbox.fs.upload_file("/app/main.js", "console.log('Hello World');")
730
+
731
+ # Install dependencies
732
+ result = sandbox.exec("npm install")
733
+ if result.data.data.exit_code != 0:
734
+ raise Error("Install failed")
735
+
736
+ # Run tests
737
+ result = sandbox.exec("npm test")
738
+ print("Test output:", result.data.data.stdout)
739
+
740
+ # Build
741
+ result = sandbox.exec("npm run build")
742
+ print("Build output:", result.data.data.stdout)
743
+
744
+ sandbox.delete()
745
+ ```
746
+
747
+ ## Configuration
748
+
749
+ The SDK can be configured by passing options to the constructor or using environment variables:
750
+
751
+ ```python
752
+ vr = VoidRun(
753
+ api_key="your-api-key", # Required: Your API key
754
+ )
755
+ ```
756
+
757
+ Environment variables:
758
+
759
+ ```bash
760
+ export VR_API_KEY="your-api-key"
761
+ ```
762
+
763
+ ## Error Handling
764
+
765
+ ```python
766
+ from voidrun import VoidRun
767
+
768
+ try:
769
+ sandbox = vr.sandboxes.create(mem=256, cpu=0.5)
770
+ except Exception as e:
771
+ print("Error:", str(e))
772
+ ```
773
+
774
+ Common errors:
775
+
776
+ - **Validation Error** - Invalid sandbox parameters
777
+ - **Authentication Error** - Invalid or missing API key
778
+ - **Not Found** - Sandbox or session doesn't exist
779
+ - **Timeout** - Operation took too long
780
+
781
+ ## Testing
782
+
783
+ Run the examples:
784
+
785
+ ```bash
786
+ # Set your API key
787
+ export VR_API_KEY="your-api-key"
788
+
789
+ # Run sync example
790
+ python -m examples.sync_usage
791
+
792
+ # Run async example
793
+ python -m examples.async_usage
794
+
795
+ # Run other examples
796
+ python -m examples.test_sandbox_exec
797
+ python -m examples.test_sandbox_fs
798
+ python -m examples.test_sandbox_lifecycle
799
+ python -m examples.test_pty
800
+ python -m examples.test_background_exec
801
+ python -m examples.code_interpreter_example
802
+ ```
803
+
804
+ ## Building from Source
805
+
806
+ ```bash
807
+ # Install dependencies
808
+ pip install -e .
809
+
810
+ # Or with poetry
811
+ poetry install
812
+ ```
813
+
814
+ ## Publishing
815
+
816
+ ```bash
817
+ # Build and publish to PyPI
818
+ poetry build
819
+ poetry publish
820
+ ```
821
+
822
+ ## Troubleshooting
823
+
824
+ ### "API key is required (pass it or set VR_API_KEY/API_KEY)"
825
+
826
+ Pass your API key in the constructor:
827
+
828
+ ```python
829
+ vr = VoidRun(api_key="your-api-key")
830
+ ```
831
+
832
+ Or set environment variable:
833
+
834
+ ```bash
835
+ export VR_API_KEY="your-api-key"
836
+ ```
837
+
838
+ ### "Sandbox creation failed"
839
+
840
+ Ensure your sandbox parameters are valid:
841
+
842
+ - `mem`: minimum 1024 MB
843
+ - `cpu`: minimum 1 core
844
+
845
+ ```python
846
+ sandbox = vr.sandboxes.create(
847
+ mem=1024, # At least 1GB
848
+ cpu=1, # At least 1 core
849
+ )
850
+ ```
851
+
852
+ ### "PTY Connection Timeout"
853
+
854
+ Increase timeout for slow systems:
855
+
856
+ ```python
857
+ pty = await sandbox.pty.connect(
858
+ session_id=session_id,
859
+ on_data=lambda data: print(data),
860
+ )
861
+
862
+ # For run_command
863
+ output = await pty.run_command("slow-command", timeout=30000) # 30 seconds
864
+ ```
865
+
866
+ ### "File Not Found"
867
+
868
+ Check the file path:
869
+
870
+ ```python
871
+ # List files to verify path
872
+ files = sandbox.fs.list_files("/app")
873
+ print([f.name for f in files.data])
874
+
875
+ # Then access specific file
876
+ content = sandbox.fs.download_file("/app/file.txt")
877
+ ```
878
+
879
+ ## API Documentation
880
+
881
+ Full API documentation is available at the VoidRun docs site.
882
+
883
+ ## Contributing
884
+
885
+ Contributions are welcome! Please check the main repository for guidelines.
886
+
887
+ ## License
888
+
889
+ MIT License - See LICENSE file for details
890
+
891
+ ## Support
892
+
893
+ - 📧 Email: support@void-run.com
894
+ - 🐛 Issues: [GitHub Issues](https://github.com/voidrun/py-sdk/issues)
895
+ - 💬 Discussions: [GitHub Discussions](https://github.com/voidrun/py-sdk/discussions)
896
+
897
+ ---
898
+
899
+ **Made with ❤️ by VoidRun**