cage-bro 0.1.0__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.
- cage_bro-0.1.0/PKG-INFO +99 -0
- cage_bro-0.1.0/README.md +87 -0
- cage_bro-0.1.0/cage_bro/__init__.py +4 -0
- cage_bro-0.1.0/cage_bro/client.py +145 -0
- cage_bro-0.1.0/cage_bro.egg-info/PKG-INFO +99 -0
- cage_bro-0.1.0/cage_bro.egg-info/SOURCES.txt +9 -0
- cage_bro-0.1.0/cage_bro.egg-info/dependency_links.txt +1 -0
- cage_bro-0.1.0/cage_bro.egg-info/requires.txt +5 -0
- cage_bro-0.1.0/cage_bro.egg-info/top_level.txt +1 -0
- cage_bro-0.1.0/pyproject.toml +20 -0
- cage_bro-0.1.0/setup.cfg +4 -0
cage_bro-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cage-bro
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for cage-bro sandbox
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: httpx>=0.24.0
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: pytest; extra == "dev"
|
|
11
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
12
|
+
|
|
13
|
+
# cage-bro
|
|
14
|
+
|
|
15
|
+
Python SDK for [cage-bro](https://github.com/aeroxy/cage-bro) — a sandboxed execution environment for AI agents.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install cage-bro
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from cage_bro import CageBro
|
|
27
|
+
|
|
28
|
+
cage = CageBro("http://localhost:8080")
|
|
29
|
+
|
|
30
|
+
# Run shell commands
|
|
31
|
+
result = cage.shell_exec("ls -la")
|
|
32
|
+
print(result["stdout"])
|
|
33
|
+
|
|
34
|
+
# Execute code
|
|
35
|
+
result = cage.python("print(2 + 2)")
|
|
36
|
+
print(result["stdout"])
|
|
37
|
+
|
|
38
|
+
# Read/write files
|
|
39
|
+
cage.file_write("hello.txt", "world")
|
|
40
|
+
content = cage.file_read("hello.txt")
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## API
|
|
44
|
+
|
|
45
|
+
### Sandbox
|
|
46
|
+
|
|
47
|
+
| Method | Description |
|
|
48
|
+
|---|---|
|
|
49
|
+
| `info()` | Get sandbox info |
|
|
50
|
+
| `health()` | Health check |
|
|
51
|
+
|
|
52
|
+
### Shell
|
|
53
|
+
|
|
54
|
+
| Method | Description |
|
|
55
|
+
|---|---|
|
|
56
|
+
| `shell_exec(command, timeout_ms=None)` | Execute a shell command |
|
|
57
|
+
| `shell_create_session(shell=None)` | Create a persistent shell session |
|
|
58
|
+
|
|
59
|
+
### Files
|
|
60
|
+
|
|
61
|
+
| Method | Description |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `file_read(path)` | Read a file |
|
|
64
|
+
| `file_write(path, content)` | Write to a file |
|
|
65
|
+
| `file_edit(path, old_text, new_text)` | Edit a file (find & replace) |
|
|
66
|
+
| `file_list(path=".")` | List directory contents |
|
|
67
|
+
| `file_search(query, path=None)` | Search files for text |
|
|
68
|
+
| `file_delete(path)` | Delete a file or directory |
|
|
69
|
+
|
|
70
|
+
### Code Execution
|
|
71
|
+
|
|
72
|
+
| Method | Description |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `python(code, timeout_ms=None)` | Execute Python code |
|
|
75
|
+
| `node(code, timeout_ms=None)` | Execute Node.js code |
|
|
76
|
+
|
|
77
|
+
### Browser
|
|
78
|
+
|
|
79
|
+
| Method | Description |
|
|
80
|
+
|---|---|
|
|
81
|
+
| `browser_launch(port=None, stealth=True)` | Launch the browser |
|
|
82
|
+
| `browser_navigate(url)` | Navigate to a URL |
|
|
83
|
+
| `browser_screenshot()` | Take a screenshot |
|
|
84
|
+
| `browser_click(selector)` | Click an element |
|
|
85
|
+
| `browser_type(selector, text)` | Type text into an element |
|
|
86
|
+
| `browser_evaluate(expression)` | Evaluate JavaScript |
|
|
87
|
+
| `browser_content()` | Get current page content |
|
|
88
|
+
| `browser_close()` | Close the browser |
|
|
89
|
+
|
|
90
|
+
## Context Manager
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
with CageBro("http://localhost:8080") as cage:
|
|
94
|
+
cage.shell_exec("echo hello")
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
Apache-2.0
|
cage_bro-0.1.0/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# cage-bro
|
|
2
|
+
|
|
3
|
+
Python SDK for [cage-bro](https://github.com/aeroxy/cage-bro) — a sandboxed execution environment for AI agents.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install cage-bro
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from cage_bro import CageBro
|
|
15
|
+
|
|
16
|
+
cage = CageBro("http://localhost:8080")
|
|
17
|
+
|
|
18
|
+
# Run shell commands
|
|
19
|
+
result = cage.shell_exec("ls -la")
|
|
20
|
+
print(result["stdout"])
|
|
21
|
+
|
|
22
|
+
# Execute code
|
|
23
|
+
result = cage.python("print(2 + 2)")
|
|
24
|
+
print(result["stdout"])
|
|
25
|
+
|
|
26
|
+
# Read/write files
|
|
27
|
+
cage.file_write("hello.txt", "world")
|
|
28
|
+
content = cage.file_read("hello.txt")
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## API
|
|
32
|
+
|
|
33
|
+
### Sandbox
|
|
34
|
+
|
|
35
|
+
| Method | Description |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `info()` | Get sandbox info |
|
|
38
|
+
| `health()` | Health check |
|
|
39
|
+
|
|
40
|
+
### Shell
|
|
41
|
+
|
|
42
|
+
| Method | Description |
|
|
43
|
+
|---|---|
|
|
44
|
+
| `shell_exec(command, timeout_ms=None)` | Execute a shell command |
|
|
45
|
+
| `shell_create_session(shell=None)` | Create a persistent shell session |
|
|
46
|
+
|
|
47
|
+
### Files
|
|
48
|
+
|
|
49
|
+
| Method | Description |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `file_read(path)` | Read a file |
|
|
52
|
+
| `file_write(path, content)` | Write to a file |
|
|
53
|
+
| `file_edit(path, old_text, new_text)` | Edit a file (find & replace) |
|
|
54
|
+
| `file_list(path=".")` | List directory contents |
|
|
55
|
+
| `file_search(query, path=None)` | Search files for text |
|
|
56
|
+
| `file_delete(path)` | Delete a file or directory |
|
|
57
|
+
|
|
58
|
+
### Code Execution
|
|
59
|
+
|
|
60
|
+
| Method | Description |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `python(code, timeout_ms=None)` | Execute Python code |
|
|
63
|
+
| `node(code, timeout_ms=None)` | Execute Node.js code |
|
|
64
|
+
|
|
65
|
+
### Browser
|
|
66
|
+
|
|
67
|
+
| Method | Description |
|
|
68
|
+
|---|---|
|
|
69
|
+
| `browser_launch(port=None, stealth=True)` | Launch the browser |
|
|
70
|
+
| `browser_navigate(url)` | Navigate to a URL |
|
|
71
|
+
| `browser_screenshot()` | Take a screenshot |
|
|
72
|
+
| `browser_click(selector)` | Click an element |
|
|
73
|
+
| `browser_type(selector, text)` | Type text into an element |
|
|
74
|
+
| `browser_evaluate(expression)` | Evaluate JavaScript |
|
|
75
|
+
| `browser_content()` | Get current page content |
|
|
76
|
+
| `browser_close()` | Close the browser |
|
|
77
|
+
|
|
78
|
+
## Context Manager
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
with CageBro("http://localhost:8080") as cage:
|
|
82
|
+
cage.shell_exec("echo hello")
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
Apache-2.0
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
from typing import Optional, Dict, Any, List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class CageBro:
|
|
6
|
+
"""Python client for cage-bro sandbox API."""
|
|
7
|
+
|
|
8
|
+
def __init__(self, base_url: str = "http://localhost:8080", timeout: float = 30.0):
|
|
9
|
+
self.base_url = base_url.rstrip("/")
|
|
10
|
+
self._client = httpx.Client(base_url=self.base_url, timeout=timeout)
|
|
11
|
+
|
|
12
|
+
def close(self):
|
|
13
|
+
self._client.close()
|
|
14
|
+
|
|
15
|
+
def __enter__(self):
|
|
16
|
+
return self
|
|
17
|
+
|
|
18
|
+
def __exit__(self, *args):
|
|
19
|
+
self.close()
|
|
20
|
+
|
|
21
|
+
# --- Sandbox ---
|
|
22
|
+
|
|
23
|
+
def info(self) -> Dict[str, Any]:
|
|
24
|
+
"""Get sandbox info."""
|
|
25
|
+
return self._client.get("/v1/sandbox/info").json()
|
|
26
|
+
|
|
27
|
+
def health(self) -> Dict[str, Any]:
|
|
28
|
+
"""Health check."""
|
|
29
|
+
return self._client.get("/health").json()
|
|
30
|
+
|
|
31
|
+
# --- Shell ---
|
|
32
|
+
|
|
33
|
+
def shell_exec(self, command: str, timeout_ms: Optional[int] = None) -> Dict[str, Any]:
|
|
34
|
+
"""Execute a shell command."""
|
|
35
|
+
payload: Dict[str, Any] = {"command": command}
|
|
36
|
+
if timeout_ms:
|
|
37
|
+
payload["timeout_ms"] = timeout_ms
|
|
38
|
+
return self._client.post("/v1/shell/exec", json=payload).json()
|
|
39
|
+
|
|
40
|
+
def shell_create_session(self, shell: Optional[str] = None) -> Dict[str, Any]:
|
|
41
|
+
"""Create a persistent shell session."""
|
|
42
|
+
payload = {}
|
|
43
|
+
if shell:
|
|
44
|
+
payload["shell"] = shell
|
|
45
|
+
return self._client.post("/v1/shell/session", json=payload).json()
|
|
46
|
+
|
|
47
|
+
# --- Files ---
|
|
48
|
+
|
|
49
|
+
def file_read(self, path: str) -> str:
|
|
50
|
+
"""Read a file and return its content."""
|
|
51
|
+
resp = self._client.post("/v1/file/read", json={"path": path})
|
|
52
|
+
data = resp.json()
|
|
53
|
+
if "error" in data:
|
|
54
|
+
raise FileNotFoundError(data["error"])
|
|
55
|
+
return data["content"]
|
|
56
|
+
|
|
57
|
+
def file_write(self, path: str, content: str) -> None:
|
|
58
|
+
"""Write content to a file."""
|
|
59
|
+
resp = self._client.post("/v1/file/write", json={"path": path, "content": content})
|
|
60
|
+
data = resp.json()
|
|
61
|
+
if "error" in data:
|
|
62
|
+
raise IOError(data["error"])
|
|
63
|
+
|
|
64
|
+
def file_edit(self, path: str, old_text: str, new_text: str) -> None:
|
|
65
|
+
"""Edit a file by replacing old_text with new_text."""
|
|
66
|
+
resp = self._client.post("/v1/file/edit", json={
|
|
67
|
+
"path": path, "old_text": old_text, "new_text": new_text
|
|
68
|
+
})
|
|
69
|
+
data = resp.json()
|
|
70
|
+
if "error" in data:
|
|
71
|
+
raise IOError(data["error"])
|
|
72
|
+
|
|
73
|
+
def file_list(self, path: str = ".") -> List[Dict[str, Any]]:
|
|
74
|
+
"""List directory contents."""
|
|
75
|
+
resp = self._client.post("/v1/file/list", json={"path": path})
|
|
76
|
+
return resp.json().get("entries", [])
|
|
77
|
+
|
|
78
|
+
def file_search(self, query: str, path: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
79
|
+
"""Search files for text."""
|
|
80
|
+
payload: Dict[str, Any] = {"query": query}
|
|
81
|
+
if path:
|
|
82
|
+
payload["path"] = path
|
|
83
|
+
resp = self._client.post("/v1/file/search", json=payload)
|
|
84
|
+
return resp.json().get("results", [])
|
|
85
|
+
|
|
86
|
+
def file_delete(self, path: str) -> None:
|
|
87
|
+
"""Delete a file or directory."""
|
|
88
|
+
resp = self._client.post("/v1/file/delete", json={"path": path})
|
|
89
|
+
data = resp.json()
|
|
90
|
+
if "error" in data:
|
|
91
|
+
raise IOError(data["error"])
|
|
92
|
+
|
|
93
|
+
# --- Code ---
|
|
94
|
+
|
|
95
|
+
def python(self, code: str, timeout_ms: Optional[int] = None) -> Dict[str, Any]:
|
|
96
|
+
"""Execute Python code."""
|
|
97
|
+
payload: Dict[str, Any] = {"code": code}
|
|
98
|
+
if timeout_ms:
|
|
99
|
+
payload["timeout_ms"] = timeout_ms
|
|
100
|
+
return self._client.post("/v1/code/python", json=payload).json()
|
|
101
|
+
|
|
102
|
+
def node(self, code: str, timeout_ms: Optional[int] = None) -> Dict[str, Any]:
|
|
103
|
+
"""Execute Node.js code."""
|
|
104
|
+
payload: Dict[str, Any] = {"code": code}
|
|
105
|
+
if timeout_ms:
|
|
106
|
+
payload["timeout_ms"] = timeout_ms
|
|
107
|
+
return self._client.post("/v1/code/node", json=payload).json()
|
|
108
|
+
|
|
109
|
+
# --- Browser ---
|
|
110
|
+
|
|
111
|
+
def browser_launch(self, port: Optional[int] = None, stealth: bool = True) -> Dict[str, Any]:
|
|
112
|
+
"""Launch the browser."""
|
|
113
|
+
payload: Dict[str, Any] = {"stealth": stealth}
|
|
114
|
+
if port:
|
|
115
|
+
payload["port"] = port
|
|
116
|
+
return self._client.post("/v1/browser/launch", json=payload).json()
|
|
117
|
+
|
|
118
|
+
def browser_navigate(self, url: str) -> Dict[str, Any]:
|
|
119
|
+
"""Navigate to a URL."""
|
|
120
|
+
return self._client.post("/v1/browser/navigate", json={"url": url}).json()
|
|
121
|
+
|
|
122
|
+
def browser_screenshot(self) -> Dict[str, Any]:
|
|
123
|
+
"""Take a screenshot."""
|
|
124
|
+
return self._client.post("/v1/browser/screenshot", json={}).json()
|
|
125
|
+
|
|
126
|
+
def browser_click(self, selector: str) -> Dict[str, Any]:
|
|
127
|
+
"""Click an element."""
|
|
128
|
+
return self._client.post("/v1/browser/click", json={"selector": selector}).json()
|
|
129
|
+
|
|
130
|
+
def browser_type(self, selector: str, text: str) -> Dict[str, Any]:
|
|
131
|
+
"""Type text into an element."""
|
|
132
|
+
return self._client.post("/v1/browser/type", json={"selector": selector, "text": text}).json()
|
|
133
|
+
|
|
134
|
+
def browser_evaluate(self, expression: str) -> Any:
|
|
135
|
+
"""Evaluate JavaScript."""
|
|
136
|
+
resp = self._client.post("/v1/browser/evaluate", json={"expression": expression})
|
|
137
|
+
return resp.json().get("result")
|
|
138
|
+
|
|
139
|
+
def browser_content(self) -> Dict[str, Any]:
|
|
140
|
+
"""Get current page content."""
|
|
141
|
+
return self._client.post("/v1/browser/content", json={}).json()
|
|
142
|
+
|
|
143
|
+
def browser_close(self) -> None:
|
|
144
|
+
"""Close the browser."""
|
|
145
|
+
self._client.post("/v1/browser/close", json={})
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cage-bro
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for cage-bro sandbox
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: httpx>=0.24.0
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: pytest; extra == "dev"
|
|
11
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
12
|
+
|
|
13
|
+
# cage-bro
|
|
14
|
+
|
|
15
|
+
Python SDK for [cage-bro](https://github.com/aeroxy/cage-bro) — a sandboxed execution environment for AI agents.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install cage-bro
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from cage_bro import CageBro
|
|
27
|
+
|
|
28
|
+
cage = CageBro("http://localhost:8080")
|
|
29
|
+
|
|
30
|
+
# Run shell commands
|
|
31
|
+
result = cage.shell_exec("ls -la")
|
|
32
|
+
print(result["stdout"])
|
|
33
|
+
|
|
34
|
+
# Execute code
|
|
35
|
+
result = cage.python("print(2 + 2)")
|
|
36
|
+
print(result["stdout"])
|
|
37
|
+
|
|
38
|
+
# Read/write files
|
|
39
|
+
cage.file_write("hello.txt", "world")
|
|
40
|
+
content = cage.file_read("hello.txt")
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## API
|
|
44
|
+
|
|
45
|
+
### Sandbox
|
|
46
|
+
|
|
47
|
+
| Method | Description |
|
|
48
|
+
|---|---|
|
|
49
|
+
| `info()` | Get sandbox info |
|
|
50
|
+
| `health()` | Health check |
|
|
51
|
+
|
|
52
|
+
### Shell
|
|
53
|
+
|
|
54
|
+
| Method | Description |
|
|
55
|
+
|---|---|
|
|
56
|
+
| `shell_exec(command, timeout_ms=None)` | Execute a shell command |
|
|
57
|
+
| `shell_create_session(shell=None)` | Create a persistent shell session |
|
|
58
|
+
|
|
59
|
+
### Files
|
|
60
|
+
|
|
61
|
+
| Method | Description |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `file_read(path)` | Read a file |
|
|
64
|
+
| `file_write(path, content)` | Write to a file |
|
|
65
|
+
| `file_edit(path, old_text, new_text)` | Edit a file (find & replace) |
|
|
66
|
+
| `file_list(path=".")` | List directory contents |
|
|
67
|
+
| `file_search(query, path=None)` | Search files for text |
|
|
68
|
+
| `file_delete(path)` | Delete a file or directory |
|
|
69
|
+
|
|
70
|
+
### Code Execution
|
|
71
|
+
|
|
72
|
+
| Method | Description |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `python(code, timeout_ms=None)` | Execute Python code |
|
|
75
|
+
| `node(code, timeout_ms=None)` | Execute Node.js code |
|
|
76
|
+
|
|
77
|
+
### Browser
|
|
78
|
+
|
|
79
|
+
| Method | Description |
|
|
80
|
+
|---|---|
|
|
81
|
+
| `browser_launch(port=None, stealth=True)` | Launch the browser |
|
|
82
|
+
| `browser_navigate(url)` | Navigate to a URL |
|
|
83
|
+
| `browser_screenshot()` | Take a screenshot |
|
|
84
|
+
| `browser_click(selector)` | Click an element |
|
|
85
|
+
| `browser_type(selector, text)` | Type text into an element |
|
|
86
|
+
| `browser_evaluate(expression)` | Evaluate JavaScript |
|
|
87
|
+
| `browser_content()` | Get current page content |
|
|
88
|
+
| `browser_close()` | Close the browser |
|
|
89
|
+
|
|
90
|
+
## Context Manager
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
with CageBro("http://localhost:8080") as cage:
|
|
94
|
+
cage.shell_exec("echo hello")
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
Apache-2.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cage_bro
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cage-bro"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python SDK for cage-bro sandbox"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "Apache-2.0"
|
|
11
|
+
requires-python = ">=3.8"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"httpx>=0.24.0",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[project.optional-dependencies]
|
|
17
|
+
dev = [
|
|
18
|
+
"pytest",
|
|
19
|
+
"pytest-asyncio",
|
|
20
|
+
]
|
cage_bro-0.1.0/setup.cfg
ADDED