boxlite 0.3.0.post1__cp312-cp312-manylinux_2_28_x86_64.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 +92 -0
- boxlite/boxlite.cpython-312-x86_64-linux-gnu.so +0 -0
- boxlite/browserbox.py +138 -0
- boxlite/codebox.py +120 -0
- boxlite/computerbox.py +502 -0
- boxlite/errors.py +38 -0
- boxlite/exec.py +26 -0
- boxlite/interactivebox.py +292 -0
- boxlite/runtime/boxlite-guest +0 -0
- boxlite/runtime/boxlite-shim +0 -0
- boxlite/runtime/debugfs +0 -0
- boxlite/runtime/libgvproxy.so +0 -0
- boxlite/runtime/libkrun.so.1.15.1 +0 -0
- boxlite/runtime/libkrunfw.so.4 +0 -0
- boxlite/runtime/mke2fs +0 -0
- boxlite/simplebox.py +208 -0
- boxlite-0.3.0.post1.dist-info/METADATA +10 -0
- boxlite-0.3.0.post1.dist-info/RECORD +19 -0
- boxlite-0.3.0.post1.dist-info/WHEEL +5 -0
boxlite/__init__.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BoxLite - Lightweight, secure containerization for any environment.
|
|
3
|
+
|
|
4
|
+
Following SQLite philosophy: "BoxLite" for branding, "boxlite" for code APIs.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import warnings
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
# Import core Rust API
|
|
12
|
+
try:
|
|
13
|
+
from .boxlite import (
|
|
14
|
+
Options,
|
|
15
|
+
BoxOptions,
|
|
16
|
+
Boxlite,
|
|
17
|
+
Box,
|
|
18
|
+
Execution,
|
|
19
|
+
ExecStdout,
|
|
20
|
+
ExecStderr,
|
|
21
|
+
BoxInfo,
|
|
22
|
+
RuntimeMetrics,
|
|
23
|
+
BoxMetrics,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
# Core Rust API
|
|
28
|
+
"Options",
|
|
29
|
+
"BoxOptions",
|
|
30
|
+
"Boxlite",
|
|
31
|
+
"Box",
|
|
32
|
+
"Execution",
|
|
33
|
+
"ExecStdout",
|
|
34
|
+
"ExecStderr",
|
|
35
|
+
"BoxInfo",
|
|
36
|
+
"RuntimeMetrics",
|
|
37
|
+
"BoxMetrics",
|
|
38
|
+
]
|
|
39
|
+
except ImportError as e:
|
|
40
|
+
warnings.warn(f"BoxLite native extension not available: {e}", ImportWarning)
|
|
41
|
+
__all__ = []
|
|
42
|
+
|
|
43
|
+
# Import Python convenience wrappers
|
|
44
|
+
try:
|
|
45
|
+
from .simplebox import SimpleBox
|
|
46
|
+
from .exec import ExecResult
|
|
47
|
+
from .codebox import CodeBox
|
|
48
|
+
from .errors import BoxliteError, ExecError, TimeoutError, ParseError
|
|
49
|
+
|
|
50
|
+
__all__.extend([
|
|
51
|
+
# Python convenience wrappers
|
|
52
|
+
"SimpleBox",
|
|
53
|
+
"CodeBox",
|
|
54
|
+
"ExecResult",
|
|
55
|
+
# Error types
|
|
56
|
+
"BoxliteError",
|
|
57
|
+
"ExecError",
|
|
58
|
+
"TimeoutError",
|
|
59
|
+
"ParseError",
|
|
60
|
+
])
|
|
61
|
+
except ImportError:
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
# Specialized containers
|
|
65
|
+
try:
|
|
66
|
+
from .browserbox import BrowserBox, BrowserBoxOptions
|
|
67
|
+
|
|
68
|
+
__all__.extend(["BrowserBox", "BrowserBoxOptions"])
|
|
69
|
+
except ImportError:
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
from .computerbox import ComputerBox
|
|
74
|
+
|
|
75
|
+
__all__.extend(["ComputerBox"])
|
|
76
|
+
except ImportError:
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
from .interactivebox import InteractiveBox
|
|
81
|
+
|
|
82
|
+
__all__.extend(["InteractiveBox"])
|
|
83
|
+
except ImportError:
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
# Get version from package metadata
|
|
87
|
+
try:
|
|
88
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
89
|
+
__version__ = version("boxlite")
|
|
90
|
+
except PackageNotFoundError:
|
|
91
|
+
# Package not installed (e.g., development mode)
|
|
92
|
+
__version__ = "0.0.0+dev"
|
|
Binary file
|
boxlite/browserbox.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BrowserBox - Secure browser with remote debugging.
|
|
3
|
+
|
|
4
|
+
Provides a minimal, elegant API for running isolated browsers that can be
|
|
5
|
+
controlled from outside using standard tools like Puppeteer or Playwright.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from typing import Optional, TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
from .simplebox import SimpleBox, StreamType
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from .boxlite import Boxlite
|
|
15
|
+
|
|
16
|
+
__all__ = ["BrowserBox", "BrowserBoxOptions"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class BrowserBoxOptions:
|
|
21
|
+
"""
|
|
22
|
+
Configuration for BrowserBox.
|
|
23
|
+
|
|
24
|
+
Example:
|
|
25
|
+
>>> opts = BrowserBoxOptions(
|
|
26
|
+
... browser="chromium",
|
|
27
|
+
... memory=2048,
|
|
28
|
+
... cpu=2
|
|
29
|
+
... )
|
|
30
|
+
>>> async with BrowserBox(opts) as browser:
|
|
31
|
+
... print(browser.endpoint())
|
|
32
|
+
"""
|
|
33
|
+
browser: str = "chromium" # chromium, firefox, or webkit
|
|
34
|
+
memory: int = 2048 # Memory in MiB
|
|
35
|
+
cpu: int = 2 # Number of CPU cores
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class BrowserBox(SimpleBox):
|
|
39
|
+
"""
|
|
40
|
+
Secure browser environment with remote debugging.
|
|
41
|
+
|
|
42
|
+
Auto-starts a browser with Chrome DevTools Protocol enabled.
|
|
43
|
+
Connect from outside using Puppeteer, Playwright, Selenium, or DevTools.
|
|
44
|
+
|
|
45
|
+
Usage:
|
|
46
|
+
>>> async with BrowserBox() as browser:
|
|
47
|
+
... print(f"Connect to: {browser.endpoint()}")
|
|
48
|
+
... # Use Puppeteer/Playwright from your host to connect
|
|
49
|
+
... await asyncio.sleep(60)
|
|
50
|
+
|
|
51
|
+
Example with custom options:
|
|
52
|
+
>>> opts = BrowserBoxOptions(browser="firefox", memory=4096)
|
|
53
|
+
>>> async with BrowserBox(opts) as browser:
|
|
54
|
+
... endpoint = browser.endpoint()
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
# Default Playwright images (with retry logic now!)
|
|
58
|
+
_DEFAULT_IMAGE = "mcr.microsoft.com/playwright:v1.47.2-jammy"
|
|
59
|
+
|
|
60
|
+
# CDP port for each browser type
|
|
61
|
+
_PORTS = {"chromium": 9222, "firefox": 9223, "webkit": 9224}
|
|
62
|
+
|
|
63
|
+
def __init__(self, options: Optional[BrowserBoxOptions] = None,
|
|
64
|
+
runtime: Optional['Boxlite'] = None,
|
|
65
|
+
**kwargs):
|
|
66
|
+
"""
|
|
67
|
+
Create and auto-start a browser.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
options: Browser configuration (uses defaults if None)
|
|
71
|
+
runtime: Optional runtime instance (uses global default if None)
|
|
72
|
+
**kwargs: Additional configuration options (volumes, env, etc.)
|
|
73
|
+
"""
|
|
74
|
+
opts = options or BrowserBoxOptions()
|
|
75
|
+
|
|
76
|
+
self._browser = opts.browser
|
|
77
|
+
self._port = self._PORTS.get(opts.browser, 9222)
|
|
78
|
+
|
|
79
|
+
# Initialize base box
|
|
80
|
+
super().__init__(
|
|
81
|
+
image=self._DEFAULT_IMAGE,
|
|
82
|
+
memory_mib=opts.memory,
|
|
83
|
+
cpus=opts.cpu,
|
|
84
|
+
runtime=runtime,
|
|
85
|
+
**kwargs
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
async def __aenter__(self):
|
|
89
|
+
"""Start browser automatically on context enter."""
|
|
90
|
+
await super().__aenter__()
|
|
91
|
+
await self._start_browser()
|
|
92
|
+
return self
|
|
93
|
+
|
|
94
|
+
async def _start_browser(self):
|
|
95
|
+
"""Internal: Start browser with remote debugging."""
|
|
96
|
+
if self._browser == "chromium":
|
|
97
|
+
binary = "/ms-playwright/chromium-*/chrome-linux/chrome"
|
|
98
|
+
cmd = (
|
|
99
|
+
f"{binary} --headless --no-sandbox --disable-dev-shm-usage "
|
|
100
|
+
f"--disable-gpu --remote-debugging-address=0.0.0.0 "
|
|
101
|
+
f"--remote-debugging-port={self._port} "
|
|
102
|
+
f"> /tmp/browser.log 2>&1 &"
|
|
103
|
+
)
|
|
104
|
+
elif self._browser == "firefox":
|
|
105
|
+
binary = "/ms-playwright/firefox-*/firefox/firefox"
|
|
106
|
+
cmd = (
|
|
107
|
+
f"{binary} --headless "
|
|
108
|
+
f"--remote-debugging-port={self._port} "
|
|
109
|
+
f"> /tmp/browser.log 2>&1 &"
|
|
110
|
+
)
|
|
111
|
+
else: # webkit
|
|
112
|
+
cmd = (
|
|
113
|
+
f"playwright run-server --browser webkit "
|
|
114
|
+
f"--port {self._port} > /tmp/browser.log 2>&1 &"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Start browser in background
|
|
118
|
+
await self.exec("sh", "-c", f"nohup {cmd}")
|
|
119
|
+
|
|
120
|
+
# Wait for browser to be ready
|
|
121
|
+
await self.exec("sleep", "3")
|
|
122
|
+
|
|
123
|
+
def endpoint(self) -> str:
|
|
124
|
+
"""
|
|
125
|
+
Get the connection endpoint for remote debugging.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
HTTP endpoint URL for Chrome DevTools Protocol
|
|
129
|
+
|
|
130
|
+
Example:
|
|
131
|
+
>>> async with BrowserBox() as browser:
|
|
132
|
+
... url = browser.endpoint()
|
|
133
|
+
... # Use with Puppeteer:
|
|
134
|
+
... # puppeteer.connect({ browserURL: url })
|
|
135
|
+
... # Use with Playwright:
|
|
136
|
+
... # chromium.connect_over_cdp(url)
|
|
137
|
+
"""
|
|
138
|
+
return f"http://localhost:{self._port}"
|
boxlite/codebox.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CodeBox - Secure Python code execution container.
|
|
3
|
+
|
|
4
|
+
Provides a simple, secure environment for running untrusted Python code.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Optional, TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
from .simplebox import SimpleBox
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from .boxlite import Boxlite
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CodeBox(SimpleBox):
|
|
16
|
+
"""
|
|
17
|
+
Secure container for executing Python code.
|
|
18
|
+
|
|
19
|
+
CodeBox provides an isolated environment for running untrusted Python code
|
|
20
|
+
with built-in safety and result formatting.
|
|
21
|
+
|
|
22
|
+
Usage:
|
|
23
|
+
>>> async with CodeBox() as cb:
|
|
24
|
+
... result = await cb.run("print('Hello, World!')")
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
image: str = "python:slim",
|
|
30
|
+
memory_mib: Optional[int] = None,
|
|
31
|
+
cpus: Optional[int] = None,
|
|
32
|
+
runtime: Optional['Boxlite'] = None,
|
|
33
|
+
**kwargs
|
|
34
|
+
):
|
|
35
|
+
"""
|
|
36
|
+
Create a new CodeBox.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
image: Container images with Python (default: python:slim)
|
|
40
|
+
memory_mib: Memory limit in MiB (default: system default)
|
|
41
|
+
cpus: Number of CPU cores (default: system default)
|
|
42
|
+
runtime: Optional runtime instance (uses global default if None)
|
|
43
|
+
**kwargs: Additional configuration options
|
|
44
|
+
"""
|
|
45
|
+
super().__init__(image, memory_mib, cpus, runtime, **kwargs)
|
|
46
|
+
|
|
47
|
+
async def run(self, code: str, timeout: Optional[int] = None) -> str:
|
|
48
|
+
"""
|
|
49
|
+
Execute Python code in the secure container.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
code: Python code to execute
|
|
53
|
+
timeout: Execution timeout in seconds (not yet implemented)
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Execution output as a string (stdout + stderr)
|
|
57
|
+
|
|
58
|
+
Example:
|
|
59
|
+
>>> async with CodeBox() as cb:
|
|
60
|
+
... result = await cb.run("print('Hello, World!')")
|
|
61
|
+
... print(result)
|
|
62
|
+
Hello, World!
|
|
63
|
+
|
|
64
|
+
Note:
|
|
65
|
+
Uses python3 from the container images.
|
|
66
|
+
For custom Python paths, use exec() directly:
|
|
67
|
+
result = await cb.exec("/path/to/python", "-c", code)
|
|
68
|
+
"""
|
|
69
|
+
# Execute Python code using python3 -c
|
|
70
|
+
result = await self.exec("/usr/local/bin/python", "-c", code)
|
|
71
|
+
return result.stdout + result.stderr
|
|
72
|
+
|
|
73
|
+
async def run_script(self, script_path: str) -> str:
|
|
74
|
+
"""
|
|
75
|
+
Execute a Python script file in the container.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
script_path: Path to the Python script on the host
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Execution output as a string
|
|
82
|
+
"""
|
|
83
|
+
with open(script_path, 'r') as f:
|
|
84
|
+
code = f.read()
|
|
85
|
+
return await self.run(code)
|
|
86
|
+
|
|
87
|
+
async def install_package(self, package: str) -> str:
|
|
88
|
+
"""
|
|
89
|
+
Install a Python package in the container using pip.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
package: Package name (e.g., 'requests', 'numpy==1.24.0')
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
Installation output
|
|
96
|
+
|
|
97
|
+
Example:
|
|
98
|
+
>>> async with CodeBox() as cb:
|
|
99
|
+
... await cb.install_package("requests")
|
|
100
|
+
... result = await cb.run("import requests; print(requests.__version__)")
|
|
101
|
+
"""
|
|
102
|
+
result = await self.exec("pip", "install", package)
|
|
103
|
+
return result.stdout + result.stderr
|
|
104
|
+
|
|
105
|
+
async def install_packages(self, *packages: str) -> str:
|
|
106
|
+
"""
|
|
107
|
+
Install multiple Python packages.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
*packages: Package names to install
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Installation output
|
|
114
|
+
|
|
115
|
+
Example:
|
|
116
|
+
>>> async with CodeBox() as cb:
|
|
117
|
+
... await cb.install_packages("requests", "numpy", "pandas")
|
|
118
|
+
"""
|
|
119
|
+
result = await self.exec("pip", "install", *packages)
|
|
120
|
+
return result.stdout + result.stderr
|