boxlite 0.5.7__cp312-cp312-macosx_14_0_arm64.whl
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.
Potentially problematic release.
This version of boxlite might be problematic. Click here for more details.
- boxlite/__init__.py +123 -0
- boxlite/boxlite.cpython-312-darwin.so +0 -0
- boxlite/browserbox.py +142 -0
- boxlite/codebox.py +120 -0
- boxlite/computerbox.py +302 -0
- boxlite/constants.py +25 -0
- boxlite/errors.py +44 -0
- boxlite/exec.py +27 -0
- boxlite/interactivebox.py +287 -0
- boxlite/runtime/boxlite-guest +0 -0
- boxlite/runtime/boxlite-shim +0 -0
- boxlite/runtime/debugfs +0 -0
- boxlite/runtime/libgvproxy.dylib +0 -0
- boxlite/runtime/libkrun.1.16.0.dylib +0 -0
- boxlite/runtime/libkrunfw.5.dylib +0 -0
- boxlite/runtime/mke2fs +0 -0
- boxlite/simplebox.py +256 -0
- boxlite/sync_api/__init__.py +65 -0
- boxlite/sync_api/_box.py +133 -0
- boxlite/sync_api/_boxlite.py +377 -0
- boxlite/sync_api/_codebox.py +145 -0
- boxlite/sync_api/_execution.py +203 -0
- boxlite/sync_api/_simplebox.py +180 -0
- boxlite/sync_api/_sync_base.py +137 -0
- boxlite-0.5.7.dist-info/METADATA +845 -0
- boxlite-0.5.7.dist-info/RECORD +27 -0
- boxlite-0.5.7.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,845 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: boxlite
|
|
3
|
+
Version: 0.5.7
|
|
4
|
+
Classifier: Development Status :: 4 - Beta
|
|
5
|
+
Classifier: Intended Audience :: Developers
|
|
6
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
11
|
+
Classifier: Programming Language :: Rust
|
|
12
|
+
Classifier: Operating System :: MacOS
|
|
13
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
|
+
Classifier: Topic :: Security
|
|
16
|
+
Classifier: Topic :: System :: Emulators
|
|
17
|
+
Requires-Dist: pytest>=7.0 ; extra == 'dev'
|
|
18
|
+
Requires-Dist: pytest-asyncio>=0.21 ; extra == 'dev'
|
|
19
|
+
Requires-Dist: greenlet>=3.0.0 ; extra == 'sync'
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Provides-Extra: sync
|
|
22
|
+
Summary: Python bindings for Boxlite runtime
|
|
23
|
+
Keywords: sandbox,virtual machine,vm,ai agents,code execution,isolation,container,security,kvm,hypervisor
|
|
24
|
+
Author: Dorian Zheng
|
|
25
|
+
License-Expression: Apache-2.0
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
28
|
+
Project-URL: Documentation, https://github.com/boxlite-ai/boxlite/tree/main/docs
|
|
29
|
+
Project-URL: Homepage, https://github.com/boxlite-ai/boxlite
|
|
30
|
+
Project-URL: Issues, https://github.com/boxlite-ai/boxlite/issues
|
|
31
|
+
Project-URL: Repository, https://github.com/boxlite-ai/boxlite
|
|
32
|
+
|
|
33
|
+
# BoxLite Python SDK
|
|
34
|
+
|
|
35
|
+
Python bindings for BoxLite - an embeddable virtual machine runtime for secure, isolated code execution.
|
|
36
|
+
|
|
37
|
+
## Overview
|
|
38
|
+
|
|
39
|
+
The BoxLite Python SDK provides a Pythonic API for creating and managing isolated execution environments. Built with PyO3, it wraps the Rust BoxLite runtime with async-first Python bindings.
|
|
40
|
+
|
|
41
|
+
**Version:** 0.4.4
|
|
42
|
+
**Python:** 3.10+
|
|
43
|
+
**Platforms:** macOS (Apple Silicon), Linux (x86_64, ARM64)
|
|
44
|
+
|
|
45
|
+
### Key Features
|
|
46
|
+
|
|
47
|
+
- **Async-first API** - All I/O operations use async/await
|
|
48
|
+
- **Context managers** - Automatic cleanup with `async with`
|
|
49
|
+
- **Streaming I/O** - Real-time stdout/stderr as execution happens
|
|
50
|
+
- **Multiple box types** - SimpleBox, CodeBox, BrowserBox, ComputerBox, InteractiveBox
|
|
51
|
+
- **Resource control** - Configure CPUs, memory, volumes, ports
|
|
52
|
+
- **OCI compatible** - Use any Docker/OCI image
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install boxlite
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Requires Python 3.10 or later.
|
|
61
|
+
|
|
62
|
+
### Verify Installation
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
import boxlite
|
|
66
|
+
print(boxlite.__version__) # Should print: 0.4.4
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### System Requirements
|
|
70
|
+
|
|
71
|
+
| Platform | Architecture | Requirements |
|
|
72
|
+
|----------|---------------|-------------------------------------|
|
|
73
|
+
| macOS | Apple Silicon | macOS 12+ |
|
|
74
|
+
| Linux | x86_64, ARM64 | KVM enabled (`/dev/kvm` accessible) |
|
|
75
|
+
|
|
76
|
+
On Linux, verify KVM is available:
|
|
77
|
+
```bash
|
|
78
|
+
grep -E 'vmx|svm' /proc/cpuinfo # Should show CPU virtualization support
|
|
79
|
+
ls -l /dev/kvm # Should exist and be accessible
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Quick Start
|
|
83
|
+
|
|
84
|
+
### Basic Execution
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
import asyncio
|
|
88
|
+
import boxlite
|
|
89
|
+
|
|
90
|
+
async def main():
|
|
91
|
+
# Create a box and run a command
|
|
92
|
+
async with boxlite.SimpleBox(image="python:slim") as box:
|
|
93
|
+
result = await box.exec("python", "-c", "print('Hello from BoxLite!')")
|
|
94
|
+
print(result.stdout)
|
|
95
|
+
# Output: Hello from BoxLite!
|
|
96
|
+
|
|
97
|
+
asyncio.run(main())
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Code Execution (AI Agents)
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
import asyncio
|
|
104
|
+
import boxlite
|
|
105
|
+
|
|
106
|
+
async def main():
|
|
107
|
+
# Execute untrusted Python code safely
|
|
108
|
+
code = """
|
|
109
|
+
import requests
|
|
110
|
+
response = requests.get('https://api.github.com/zen')
|
|
111
|
+
print(response.text)
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
async with boxlite.CodeBox() as codebox:
|
|
115
|
+
# CodeBox automatically installs packages
|
|
116
|
+
result = await codebox.run(code)
|
|
117
|
+
print(result)
|
|
118
|
+
|
|
119
|
+
asyncio.run(main())
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Core API Reference
|
|
123
|
+
|
|
124
|
+
### Runtime Management
|
|
125
|
+
|
|
126
|
+
#### `boxlite.Boxlite`
|
|
127
|
+
|
|
128
|
+
The main runtime for creating and managing boxes.
|
|
129
|
+
|
|
130
|
+
**Methods:**
|
|
131
|
+
|
|
132
|
+
- `Boxlite.default() -> Boxlite`
|
|
133
|
+
Create runtime with default settings (`~/.boxlite`)
|
|
134
|
+
|
|
135
|
+
- `Boxlite(options: Options) -> Boxlite`
|
|
136
|
+
Create runtime with custom options
|
|
137
|
+
|
|
138
|
+
- `create(box_options: BoxOptions) -> Box`
|
|
139
|
+
Create a new box with specified configuration
|
|
140
|
+
|
|
141
|
+
- `get(box_id: str) -> Box`
|
|
142
|
+
Reattach to an existing box by ID
|
|
143
|
+
|
|
144
|
+
- `list() -> List[BoxInfo]`
|
|
145
|
+
List all boxes (running and stopped)
|
|
146
|
+
|
|
147
|
+
- `metrics() -> RuntimeMetrics`
|
|
148
|
+
Get runtime-wide metrics
|
|
149
|
+
|
|
150
|
+
**Example:**
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
# Default runtime
|
|
154
|
+
runtime = boxlite.Boxlite.default()
|
|
155
|
+
|
|
156
|
+
# Custom runtime with different home directory
|
|
157
|
+
runtime = boxlite.Boxlite(boxlite.Options(home_dir="/custom/path"))
|
|
158
|
+
|
|
159
|
+
# Create a box
|
|
160
|
+
box = runtime.create(boxlite.BoxOptions(image="alpine:latest"))
|
|
161
|
+
|
|
162
|
+
# Reattach to existing box
|
|
163
|
+
box = runtime.get("01JJNH8...")
|
|
164
|
+
|
|
165
|
+
# List all boxes
|
|
166
|
+
boxes = runtime.list()
|
|
167
|
+
for info in boxes:
|
|
168
|
+
print(f"{info.id}: {info.status}")
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Box Configuration
|
|
172
|
+
|
|
173
|
+
#### `boxlite.BoxOptions`
|
|
174
|
+
|
|
175
|
+
Configuration options for creating a box.
|
|
176
|
+
|
|
177
|
+
**Parameters:**
|
|
178
|
+
|
|
179
|
+
- `image: str` - OCI image URI (default: `"python:slim"`)
|
|
180
|
+
- `cpus: int` - Number of CPUs (default: 1, max: host CPU count)
|
|
181
|
+
- `memory_mib: int` - Memory in MiB (default: 512, range: 128-65536)
|
|
182
|
+
- `disk_size_gb: int | None` - Persistent disk size in GB (default: None)
|
|
183
|
+
- `working_dir: str` - Working directory in container (default: `"/root"`)
|
|
184
|
+
- `env: List[Tuple[str, str]]` - Environment variables as (key, value) pairs
|
|
185
|
+
- `volumes: List[Tuple[str, str, str]]` - Volume mounts as (host_path, guest_path, mode)
|
|
186
|
+
- Mode: `"ro"` (read-only) or `"rw"` (read-write)
|
|
187
|
+
- `ports: List[Tuple[int, int, str]]` - Port forwarding as (host_port, guest_port, protocol)
|
|
188
|
+
- Protocol: `"tcp"` or `"udp"`
|
|
189
|
+
- `auto_remove: bool` - Auto cleanup after stop (default: True)
|
|
190
|
+
|
|
191
|
+
**Example:**
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
options = boxlite.BoxOptions(
|
|
195
|
+
image="postgres:latest",
|
|
196
|
+
cpus=2,
|
|
197
|
+
memory_mib=1024,
|
|
198
|
+
disk_size_gb=10, # 10 GB persistent disk
|
|
199
|
+
env=[
|
|
200
|
+
("POSTGRES_PASSWORD", "secret"),
|
|
201
|
+
("POSTGRES_DB", "mydb"),
|
|
202
|
+
],
|
|
203
|
+
volumes=[
|
|
204
|
+
("/host/data", "/mnt/data", "ro"), # Read-only mount
|
|
205
|
+
],
|
|
206
|
+
ports=[
|
|
207
|
+
(5432, 5432, "tcp"), # PostgreSQL
|
|
208
|
+
],
|
|
209
|
+
)
|
|
210
|
+
box = runtime.create(options)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Box Handle
|
|
214
|
+
|
|
215
|
+
#### `boxlite.Box`
|
|
216
|
+
|
|
217
|
+
Handle to a running or stopped box.
|
|
218
|
+
|
|
219
|
+
**Properties:**
|
|
220
|
+
|
|
221
|
+
- `id: str` - Unique box identifier (ULID format)
|
|
222
|
+
|
|
223
|
+
**Methods:**
|
|
224
|
+
|
|
225
|
+
- `exec(*args, **kwargs) -> Execution`
|
|
226
|
+
Execute a command in the box (async)
|
|
227
|
+
|
|
228
|
+
- `stop() -> None`
|
|
229
|
+
Stop the box gracefully (async)
|
|
230
|
+
|
|
231
|
+
- `remove() -> None`
|
|
232
|
+
Delete the box and its data (async)
|
|
233
|
+
|
|
234
|
+
- `info() -> BoxInfo`
|
|
235
|
+
Get box metadata (async)
|
|
236
|
+
|
|
237
|
+
- `metrics() -> BoxMetrics`
|
|
238
|
+
Get box resource usage metrics (async)
|
|
239
|
+
|
|
240
|
+
**Example:**
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
box = runtime.create(boxlite.BoxOptions(image="alpine:latest"))
|
|
244
|
+
|
|
245
|
+
# Execute commands
|
|
246
|
+
execution = await box.exec("echo", "Hello")
|
|
247
|
+
result = await execution.wait()
|
|
248
|
+
|
|
249
|
+
# Get box info
|
|
250
|
+
info = await box.info()
|
|
251
|
+
print(f"Box {info.id}: {info.status}")
|
|
252
|
+
|
|
253
|
+
# Stop and remove
|
|
254
|
+
await box.stop()
|
|
255
|
+
await box.remove()
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Command Execution
|
|
259
|
+
|
|
260
|
+
#### `boxlite.Execution`
|
|
261
|
+
|
|
262
|
+
Represents a running command execution.
|
|
263
|
+
|
|
264
|
+
**Methods:**
|
|
265
|
+
|
|
266
|
+
- `stdout() -> ExecStdout`
|
|
267
|
+
Get stdout stream (async iterator)
|
|
268
|
+
|
|
269
|
+
- `stderr() -> ExecStderr`
|
|
270
|
+
Get stderr stream (async iterator)
|
|
271
|
+
|
|
272
|
+
- `stdin() -> ExecStdin`
|
|
273
|
+
Get stdin writer
|
|
274
|
+
|
|
275
|
+
- `wait() -> ExecResult`
|
|
276
|
+
Wait for command to complete and get result (async)
|
|
277
|
+
|
|
278
|
+
- `kill(signal: int = 9) -> None`
|
|
279
|
+
Send signal to process (async)
|
|
280
|
+
|
|
281
|
+
**Example:**
|
|
282
|
+
|
|
283
|
+
```python
|
|
284
|
+
# Streaming output
|
|
285
|
+
execution = await box.exec("python", "-c", "for i in range(5): print(i)")
|
|
286
|
+
|
|
287
|
+
stdout = execution.stdout()
|
|
288
|
+
async for line in stdout:
|
|
289
|
+
print(f"Output: {line}")
|
|
290
|
+
|
|
291
|
+
# Wait for completion
|
|
292
|
+
result = await execution.wait()
|
|
293
|
+
print(f"Exit code: {result.exit_code}")
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
#### `boxlite.ExecStdout` / `boxlite.ExecStderr`
|
|
297
|
+
|
|
298
|
+
Async iterators for streaming output.
|
|
299
|
+
|
|
300
|
+
**Usage:**
|
|
301
|
+
|
|
302
|
+
```python
|
|
303
|
+
execution = await box.exec("ls", "-la")
|
|
304
|
+
|
|
305
|
+
# Stream stdout line by line
|
|
306
|
+
stdout = execution.stdout()
|
|
307
|
+
async for line in stdout:
|
|
308
|
+
print(line)
|
|
309
|
+
|
|
310
|
+
# Stream stderr
|
|
311
|
+
stderr = execution.stderr()
|
|
312
|
+
async for line in stderr:
|
|
313
|
+
print(f"Error: {line}", file=sys.stderr)
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Higher-Level APIs
|
|
317
|
+
|
|
318
|
+
#### `boxlite.SimpleBox`
|
|
319
|
+
|
|
320
|
+
Context manager for basic execution with automatic cleanup.
|
|
321
|
+
|
|
322
|
+
**Parameters:** Same as `BoxOptions`
|
|
323
|
+
|
|
324
|
+
**Methods:**
|
|
325
|
+
|
|
326
|
+
- `exec(*args, **kwargs) -> ExecResult`
|
|
327
|
+
Execute command and wait for result
|
|
328
|
+
|
|
329
|
+
**Example:**
|
|
330
|
+
|
|
331
|
+
```python
|
|
332
|
+
async with boxlite.SimpleBox(image="python:slim") as box:
|
|
333
|
+
result = await box.exec("python", "-c", "print('Hello')")
|
|
334
|
+
print(result.stdout) # "Hello\n"
|
|
335
|
+
print(result.exit_code) # 0
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
#### `boxlite.CodeBox`
|
|
339
|
+
|
|
340
|
+
Specialized box for Python code execution with package management.
|
|
341
|
+
|
|
342
|
+
**Methods:**
|
|
343
|
+
|
|
344
|
+
- `run(code: str) -> str`
|
|
345
|
+
Execute Python code and return output
|
|
346
|
+
|
|
347
|
+
- `install_package(package: str) -> None`
|
|
348
|
+
Install a Python package with pip
|
|
349
|
+
|
|
350
|
+
**Example:**
|
|
351
|
+
|
|
352
|
+
```python
|
|
353
|
+
async with boxlite.CodeBox() as codebox:
|
|
354
|
+
# Install packages
|
|
355
|
+
await codebox.install_package("requests")
|
|
356
|
+
|
|
357
|
+
# Run code
|
|
358
|
+
result = await codebox.run("""
|
|
359
|
+
import requests
|
|
360
|
+
print(requests.get('https://api.github.com/zen').text)
|
|
361
|
+
""")
|
|
362
|
+
print(result)
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
#### `boxlite.BrowserBox`
|
|
366
|
+
|
|
367
|
+
Box configured for browser automation (Chromium, Firefox, WebKit).
|
|
368
|
+
|
|
369
|
+
**Example:**
|
|
370
|
+
|
|
371
|
+
```python
|
|
372
|
+
async with boxlite.BrowserBox() as browser:
|
|
373
|
+
endpoint = browser.endpoint()
|
|
374
|
+
print(f"Connect Puppeteer to: {endpoint}")
|
|
375
|
+
# Use with Puppeteer/Playwright for browser automation
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
#### `boxlite.ComputerBox`
|
|
379
|
+
|
|
380
|
+
Box with desktop automation capabilities (mouse, keyboard, screenshots).
|
|
381
|
+
|
|
382
|
+
**Methods:**
|
|
383
|
+
|
|
384
|
+
14 desktop interaction functions including:
|
|
385
|
+
- `screenshot() -> bytes` - Capture screen
|
|
386
|
+
- `left_click()` - Click mouse
|
|
387
|
+
- `type_text(text: str)` - Type text
|
|
388
|
+
- `get_screen_size() -> Tuple[int, int]` - Get screen dimensions
|
|
389
|
+
|
|
390
|
+
**Example:**
|
|
391
|
+
|
|
392
|
+
```python
|
|
393
|
+
async with boxlite.ComputerBox() as computer:
|
|
394
|
+
# Get screen size
|
|
395
|
+
width, height = await computer.get_screen_size()
|
|
396
|
+
|
|
397
|
+
# Take screenshot
|
|
398
|
+
screenshot_bytes = await computer.screenshot()
|
|
399
|
+
|
|
400
|
+
# Mouse and keyboard
|
|
401
|
+
await computer.left_click()
|
|
402
|
+
await computer.type_text("Hello, world!")
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
#### `boxlite.InteractiveBox`
|
|
406
|
+
|
|
407
|
+
Box for interactive shell sessions.
|
|
408
|
+
|
|
409
|
+
**Example:**
|
|
410
|
+
|
|
411
|
+
```python
|
|
412
|
+
async with boxlite.InteractiveBox(image="alpine:latest") as itbox:
|
|
413
|
+
# Drop into interactive shell
|
|
414
|
+
await itbox.wait()
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
## API Patterns
|
|
418
|
+
|
|
419
|
+
### Async/Await
|
|
420
|
+
|
|
421
|
+
All I/O operations are async. Use `await` for operations and `async for` for streams.
|
|
422
|
+
|
|
423
|
+
```python
|
|
424
|
+
# Create and use box (async)
|
|
425
|
+
async with boxlite.SimpleBox(image="alpine") as box:
|
|
426
|
+
result = await box.exec("echo", "Hello")
|
|
427
|
+
|
|
428
|
+
# Stream output (async iterator)
|
|
429
|
+
execution = await box.exec("python", "script.py")
|
|
430
|
+
async for line in execution.stdout():
|
|
431
|
+
print(line)
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Context Managers
|
|
435
|
+
|
|
436
|
+
Use `async with` for automatic cleanup:
|
|
437
|
+
|
|
438
|
+
```python
|
|
439
|
+
# SimpleBox - auto cleanup
|
|
440
|
+
async with boxlite.SimpleBox() as box:
|
|
441
|
+
result = await box.exec("command")
|
|
442
|
+
# Box automatically stopped and removed
|
|
443
|
+
|
|
444
|
+
# Manual cleanup (if not using context manager)
|
|
445
|
+
box = runtime.create(boxlite.BoxOptions(image="alpine"))
|
|
446
|
+
try:
|
|
447
|
+
await box.exec("command")
|
|
448
|
+
finally:
|
|
449
|
+
await box.stop()
|
|
450
|
+
await box.remove()
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Streaming I/O
|
|
454
|
+
|
|
455
|
+
Stream output line-by-line as it's produced:
|
|
456
|
+
|
|
457
|
+
```python
|
|
458
|
+
execution = await box.exec("tail", "-f", "/var/log/app.log")
|
|
459
|
+
|
|
460
|
+
# Process output in real-time
|
|
461
|
+
stdout = execution.stdout()
|
|
462
|
+
async for line in stdout:
|
|
463
|
+
if "ERROR" in line:
|
|
464
|
+
print(f"Alert: {line}")
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Error Handling
|
|
468
|
+
|
|
469
|
+
Catch exceptions from BoxLite operations:
|
|
470
|
+
|
|
471
|
+
```python
|
|
472
|
+
import boxlite
|
|
473
|
+
from boxlite import BoxliteError, ExecError
|
|
474
|
+
|
|
475
|
+
try:
|
|
476
|
+
async with boxlite.SimpleBox(image="invalid:image") as box:
|
|
477
|
+
result = await box.exec("command")
|
|
478
|
+
except BoxliteError as e:
|
|
479
|
+
print(f"BoxLite error: {e}")
|
|
480
|
+
except ExecError as e:
|
|
481
|
+
print(f"Execution error: {e}")
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
## Configuration Reference
|
|
485
|
+
|
|
486
|
+
### Image Selection
|
|
487
|
+
|
|
488
|
+
Any OCI-compatible image from Docker Hub, GHCR, ECR, or other registries:
|
|
489
|
+
|
|
490
|
+
```python
|
|
491
|
+
# Docker Hub (default registry)
|
|
492
|
+
boxlite.BoxOptions(image="python:3.11-slim")
|
|
493
|
+
boxlite.BoxOptions(image="alpine:latest")
|
|
494
|
+
boxlite.BoxOptions(image="ubuntu:22.04")
|
|
495
|
+
|
|
496
|
+
# GitHub Container Registry
|
|
497
|
+
boxlite.BoxOptions(image="ghcr.io/owner/repo:tag")
|
|
498
|
+
|
|
499
|
+
# Amazon ECR
|
|
500
|
+
boxlite.BoxOptions(image="123456.dkr.ecr.us-east-1.amazonaws.com/repo:tag")
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### Resource Limits
|
|
504
|
+
|
|
505
|
+
```python
|
|
506
|
+
boxlite.BoxOptions(
|
|
507
|
+
cpus=4, # 4 CPU cores
|
|
508
|
+
memory_mib=2048, # 2 GB RAM
|
|
509
|
+
)
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### Environment Variables
|
|
513
|
+
|
|
514
|
+
```python
|
|
515
|
+
boxlite.BoxOptions(
|
|
516
|
+
env=[
|
|
517
|
+
("DATABASE_URL", "postgresql://localhost/db"),
|
|
518
|
+
("API_KEY", "secret"),
|
|
519
|
+
("DEBUG", "true"),
|
|
520
|
+
]
|
|
521
|
+
)
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
### Volume Mounts
|
|
525
|
+
|
|
526
|
+
```python
|
|
527
|
+
boxlite.BoxOptions(
|
|
528
|
+
volumes=[
|
|
529
|
+
# Read-only mount
|
|
530
|
+
("/host/config", "/etc/app/config", "ro"),
|
|
531
|
+
|
|
532
|
+
# Read-write mount
|
|
533
|
+
("/host/data", "/mnt/data", "rw"),
|
|
534
|
+
]
|
|
535
|
+
)
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### Port Forwarding
|
|
539
|
+
|
|
540
|
+
```python
|
|
541
|
+
boxlite.BoxOptions(
|
|
542
|
+
ports=[
|
|
543
|
+
(8080, 80, "tcp"), # HTTP
|
|
544
|
+
(8443, 443, "tcp"), # HTTPS
|
|
545
|
+
(5432, 5432, "tcp"), # PostgreSQL
|
|
546
|
+
(53, 53, "udp"), # DNS
|
|
547
|
+
]
|
|
548
|
+
)
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
### Persistent Storage
|
|
552
|
+
|
|
553
|
+
```python
|
|
554
|
+
# Ephemeral (default) - data lost on box removal
|
|
555
|
+
boxlite.BoxOptions(image="postgres")
|
|
556
|
+
|
|
557
|
+
# Persistent - data survives stop/restart via QCOW2 disk
|
|
558
|
+
boxlite.BoxOptions(
|
|
559
|
+
image="postgres",
|
|
560
|
+
disk_size_gb=20, # 20 GB persistent disk
|
|
561
|
+
)
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
## Examples Gallery
|
|
565
|
+
|
|
566
|
+
The `examples/python/` directory contains 9 comprehensive examples:
|
|
567
|
+
|
|
568
|
+
### 1. **simplebox_example.py** - Foundation Patterns
|
|
569
|
+
Demonstrates core BoxLite features:
|
|
570
|
+
- Basic command execution with results
|
|
571
|
+
- Separate stdout/stderr handling
|
|
572
|
+
- Environment variables and working directory
|
|
573
|
+
- Error handling and exit codes
|
|
574
|
+
- Multiple commands in same box
|
|
575
|
+
- Data processing pipeline
|
|
576
|
+
|
|
577
|
+
[View source](../../examples/python/simplebox_example.py)
|
|
578
|
+
|
|
579
|
+
### 2. **codebox_example.py** - AI Code Execution
|
|
580
|
+
Secure Python code execution for AI agents:
|
|
581
|
+
- Basic code execution
|
|
582
|
+
- Dynamic package installation
|
|
583
|
+
- Data processing (AI agent use case)
|
|
584
|
+
- Isolation demonstration
|
|
585
|
+
|
|
586
|
+
[View source](../../examples/python/codebox_example.py)
|
|
587
|
+
|
|
588
|
+
### 3. **browserbox_example.py** - Browser Automation
|
|
589
|
+
Browser automation with Puppeteer/Playwright:
|
|
590
|
+
- Basic Chromium setup
|
|
591
|
+
- Custom browser configurations (Firefox, WebKit)
|
|
592
|
+
- Cross-browser testing patterns
|
|
593
|
+
- Integration examples
|
|
594
|
+
|
|
595
|
+
[View source](../../examples/python/browserbox_example.py)
|
|
596
|
+
|
|
597
|
+
### 4. **computerbox_example.py** - Desktop Automation
|
|
598
|
+
Desktop interaction for agent workflows:
|
|
599
|
+
- 14 desktop functions (mouse, keyboard, screenshots)
|
|
600
|
+
- Screen size detection
|
|
601
|
+
- Workflow automation
|
|
602
|
+
- GUI interaction patterns
|
|
603
|
+
|
|
604
|
+
[View source](../../examples/python/computerbox_example.py)
|
|
605
|
+
|
|
606
|
+
### 5. **lifecycle_example.py** - Box Lifecycle Management
|
|
607
|
+
Managing box state:
|
|
608
|
+
- Stop and restart operations
|
|
609
|
+
- State persistence
|
|
610
|
+
- Data persistence verification
|
|
611
|
+
- Resource cleanup
|
|
612
|
+
|
|
613
|
+
[View source](../../examples/python/lifecycle_example.py)
|
|
614
|
+
|
|
615
|
+
### 6. **list_boxes_example.py** - Runtime Introspection
|
|
616
|
+
Enumerate and inspect boxes:
|
|
617
|
+
- List all boxes with status
|
|
618
|
+
- Display box metadata (ID, name, state, resources)
|
|
619
|
+
- Filter by status
|
|
620
|
+
|
|
621
|
+
[View source](../../examples/python/list_boxes_example.py)
|
|
622
|
+
|
|
623
|
+
### 7. **cross_process_example.py** - Multi-Process Operations
|
|
624
|
+
Cross-process box management:
|
|
625
|
+
- Reattach to running boxes from different processes
|
|
626
|
+
- Restart stopped boxes
|
|
627
|
+
- Multi-process runtime handling
|
|
628
|
+
|
|
629
|
+
[View source](../../examples/python/cross_process_example.py)
|
|
630
|
+
|
|
631
|
+
### 8. **interactivebox_example.py** - Interactive Shells
|
|
632
|
+
Direct shell access:
|
|
633
|
+
- Interactive terminal sessions
|
|
634
|
+
- Terminal mode handling
|
|
635
|
+
- Simple container experience
|
|
636
|
+
|
|
637
|
+
[View source](../../examples/python/interactivebox_example.py)
|
|
638
|
+
|
|
639
|
+
### 9. **native_example.py** - Low-Level API
|
|
640
|
+
Using the Rust API directly from Python:
|
|
641
|
+
- Default and custom runtime initialization
|
|
642
|
+
- Resource limits (CPU, memory, volumes, ports)
|
|
643
|
+
- Box information retrieval
|
|
644
|
+
- Streaming execution
|
|
645
|
+
|
|
646
|
+
[View source](../../examples/python/native_example.py)
|
|
647
|
+
|
|
648
|
+
## Metrics & Monitoring
|
|
649
|
+
|
|
650
|
+
### Runtime Metrics
|
|
651
|
+
|
|
652
|
+
Get aggregate metrics across all boxes:
|
|
653
|
+
|
|
654
|
+
```python
|
|
655
|
+
runtime = boxlite.Boxlite.default()
|
|
656
|
+
metrics = runtime.metrics()
|
|
657
|
+
|
|
658
|
+
print(f"Boxes created: {metrics.boxes_created}")
|
|
659
|
+
print(f"Boxes destroyed: {metrics.boxes_destroyed}")
|
|
660
|
+
print(f"Total exec calls: {metrics.total_exec_calls}")
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
**RuntimeMetrics Fields:**
|
|
664
|
+
- `boxes_created: int` - Total boxes created
|
|
665
|
+
- `boxes_destroyed: int` - Total boxes destroyed
|
|
666
|
+
- `total_exec_calls: int` - Total command executions
|
|
667
|
+
- `active_boxes: int` - Currently running boxes
|
|
668
|
+
|
|
669
|
+
### Box Metrics
|
|
670
|
+
|
|
671
|
+
Get per-box resource usage:
|
|
672
|
+
|
|
673
|
+
```python
|
|
674
|
+
box = runtime.create(boxlite.BoxOptions(image="alpine"))
|
|
675
|
+
metrics = await box.metrics()
|
|
676
|
+
|
|
677
|
+
print(f"CPU time: {metrics.cpu_time_ms}ms")
|
|
678
|
+
print(f"Memory: {metrics.memory_usage_bytes / (1024**2):.2f} MB")
|
|
679
|
+
print(f"Network sent: {metrics.network_bytes_sent}")
|
|
680
|
+
print(f"Network received: {metrics.network_bytes_received}")
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
**BoxMetrics Fields:**
|
|
684
|
+
- `cpu_time_ms: int` - Total CPU time in milliseconds
|
|
685
|
+
- `memory_usage_bytes: int` - Current memory usage
|
|
686
|
+
- `network_bytes_sent: int` - Total bytes sent
|
|
687
|
+
- `network_bytes_received: int` - Total bytes received
|
|
688
|
+
|
|
689
|
+
## Error Handling
|
|
690
|
+
|
|
691
|
+
### Exception Types
|
|
692
|
+
|
|
693
|
+
```python
|
|
694
|
+
from boxlite import BoxliteError, ExecError, TimeoutError, ParseError
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
**BoxliteError** - Base exception for all BoxLite errors
|
|
698
|
+
|
|
699
|
+
**ExecError** - Command execution failed
|
|
700
|
+
|
|
701
|
+
**TimeoutError** - Operation timed out
|
|
702
|
+
|
|
703
|
+
**ParseError** - Failed to parse output
|
|
704
|
+
|
|
705
|
+
### Common Error Patterns
|
|
706
|
+
|
|
707
|
+
```python
|
|
708
|
+
import boxlite
|
|
709
|
+
|
|
710
|
+
async def safe_execution():
|
|
711
|
+
try:
|
|
712
|
+
async with boxlite.SimpleBox(image="python:slim") as box:
|
|
713
|
+
result = await box.exec("python", "script.py")
|
|
714
|
+
|
|
715
|
+
# Check exit code
|
|
716
|
+
if result.exit_code != 0:
|
|
717
|
+
print(f"Command failed: {result.stderr}")
|
|
718
|
+
|
|
719
|
+
except boxlite.BoxliteError as e:
|
|
720
|
+
# Handle BoxLite-specific errors
|
|
721
|
+
print(f"BoxLite error: {e}")
|
|
722
|
+
except Exception as e:
|
|
723
|
+
# Handle other errors
|
|
724
|
+
print(f"Unexpected error: {e}")
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
## Troubleshooting
|
|
728
|
+
|
|
729
|
+
### Installation Issues
|
|
730
|
+
|
|
731
|
+
**Problem:** `pip install boxlite` fails
|
|
732
|
+
|
|
733
|
+
**Solutions:**
|
|
734
|
+
- Ensure Python 3.10+: `python --version`
|
|
735
|
+
- Update pip: `pip install --upgrade pip`
|
|
736
|
+
- Check platform support (macOS ARM64, Linux x86_64/ARM64 only)
|
|
737
|
+
|
|
738
|
+
### Runtime Errors
|
|
739
|
+
|
|
740
|
+
**Problem:** "KVM not available" error on Linux
|
|
741
|
+
|
|
742
|
+
**Solutions:**
|
|
743
|
+
```bash
|
|
744
|
+
# Check if KVM is loaded
|
|
745
|
+
lsmod | grep kvm
|
|
746
|
+
|
|
747
|
+
# Check if /dev/kvm exists
|
|
748
|
+
ls -l /dev/kvm
|
|
749
|
+
|
|
750
|
+
# Add user to kvm group (may require logout/login)
|
|
751
|
+
sudo usermod -aG kvm $USER
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
**Problem:** "Hypervisor.framework not available" on macOS
|
|
755
|
+
|
|
756
|
+
**Solutions:**
|
|
757
|
+
- Ensure macOS 12+ (Monterey or later)
|
|
758
|
+
- Verify Apple Silicon (ARM64) - Intel Macs not supported
|
|
759
|
+
- Check System Settings → Privacy & Security → Developer Tools
|
|
760
|
+
|
|
761
|
+
### Image Pull Failures
|
|
762
|
+
|
|
763
|
+
**Problem:** "Failed to pull image" error
|
|
764
|
+
|
|
765
|
+
**Solutions:**
|
|
766
|
+
- Check internet connectivity
|
|
767
|
+
- Verify image name and tag exist: `docker pull <image>`
|
|
768
|
+
- For private images, authenticate with registry first
|
|
769
|
+
|
|
770
|
+
### Performance Issues
|
|
771
|
+
|
|
772
|
+
**Problem:** Box is slow or unresponsive
|
|
773
|
+
|
|
774
|
+
**Solutions:**
|
|
775
|
+
```python
|
|
776
|
+
# Increase resource limits
|
|
777
|
+
boxlite.BoxOptions(
|
|
778
|
+
cpus=4, # More CPUs
|
|
779
|
+
memory_mib=4096, # More memory
|
|
780
|
+
)
|
|
781
|
+
|
|
782
|
+
# Check metrics
|
|
783
|
+
metrics = await box.metrics()
|
|
784
|
+
print(f"Memory usage: {metrics.memory_usage_bytes / (1024**2):.2f} MB")
|
|
785
|
+
print(f"CPU time: {metrics.cpu_time_ms}ms")
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
### Debug Logging
|
|
789
|
+
|
|
790
|
+
Enable debug logging to troubleshoot issues:
|
|
791
|
+
|
|
792
|
+
```bash
|
|
793
|
+
# Set RUST_LOG environment variable
|
|
794
|
+
RUST_LOG=debug python script.py
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
Log levels: `trace`, `debug`, `info`, `warn`, `error`
|
|
798
|
+
|
|
799
|
+
## Contributing
|
|
800
|
+
|
|
801
|
+
We welcome contributions to the Python SDK!
|
|
802
|
+
|
|
803
|
+
### Development Setup
|
|
804
|
+
|
|
805
|
+
```bash
|
|
806
|
+
# Clone repository
|
|
807
|
+
git clone https://github.com/boxlite-labs/boxlite.git
|
|
808
|
+
cd boxlite
|
|
809
|
+
|
|
810
|
+
# Initialize submodules
|
|
811
|
+
git submodule update --init --recursive
|
|
812
|
+
|
|
813
|
+
# Build Python SDK in development mode
|
|
814
|
+
make dev:python
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
### Running Tests
|
|
818
|
+
|
|
819
|
+
```bash
|
|
820
|
+
# Install dev dependencies
|
|
821
|
+
pip install -e ".[dev]"
|
|
822
|
+
|
|
823
|
+
# Run tests
|
|
824
|
+
python -m pytest sdks/python/tests/
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
### Building Wheels
|
|
828
|
+
|
|
829
|
+
```bash
|
|
830
|
+
# Build portable wheel
|
|
831
|
+
make dist:python
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
## Further Documentation
|
|
835
|
+
|
|
836
|
+
- [BoxLite Main README](../../README.md) - Project overview
|
|
837
|
+
- [Architecture Documentation](../../docs/architecture/README.md) - How BoxLite works
|
|
838
|
+
- [Getting Started Guide](../../docs/getting-started/README.md) - Installation and setup
|
|
839
|
+
- [How-to Guides](../../docs/guides/README.md) - Practical guides
|
|
840
|
+
- [API Reference](../../docs/reference/README.md) - Complete API documentation
|
|
841
|
+
|
|
842
|
+
## License
|
|
843
|
+
|
|
844
|
+
Licensed under the Apache License, Version 2.0. See [LICENSE](../../LICENSE) for details.
|
|
845
|
+
|