axio-tools-docker 0.6.1__tar.gz → 0.7.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.
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/.gitignore +2 -0
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/PKG-INFO +36 -5
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/README.md +33 -2
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/pyproject.toml +3 -3
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/src/axio_tools_docker/handler.py +10 -10
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/src/axio_tools_docker/plugin.py +2 -2
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/tests/test_handler.py +6 -6
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/.github/workflows/publish.yml +0 -0
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/.github/workflows/tests.yml +0 -0
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/LICENSE +0 -0
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/Makefile +0 -0
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/src/axio_tools_docker/__init__.py +0 -0
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/src/axio_tools_docker/config.py +0 -0
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/src/axio_tools_docker/manager.py +0 -0
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/src/axio_tools_docker/settings.py +0 -0
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/src/axio_tools_docker/tui/__init__.py +0 -0
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/src/axio_tools_docker/tui/docker.py +0 -0
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/tests/test_config.py +0 -0
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/tests/test_manager.py +0 -0
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/tests/test_plugin.py +0 -0
- {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/uv.lock +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: axio-tools-docker
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Docker sandbox tools for Axio
|
|
5
|
-
Project-URL: Homepage, https://github.com/axio-agent
|
|
6
|
-
Project-URL: Repository, https://github.com/axio-agent
|
|
5
|
+
Project-URL: Homepage, https://github.com/mosquito/axio-agent
|
|
6
|
+
Project-URL: Repository, https://github.com/mosquito/axio-agent
|
|
7
7
|
License: MIT
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Keywords: agent,ai,docker,llm,sandbox,tools
|
|
@@ -17,7 +17,7 @@ Description-Content-Type: text/markdown
|
|
|
17
17
|
[](https://pypi.org/project/axio-tools-docker/)
|
|
18
18
|
[](LICENSE)
|
|
19
19
|
|
|
20
|
-
Docker sandbox tools for [axio](https://github.com/axio-agent
|
|
20
|
+
Docker sandbox tools for [axio](https://github.com/mosquito/axio-agent).
|
|
21
21
|
|
|
22
22
|
Run agent-generated code and commands inside isolated Docker containers. The agent gets `sandbox_exec`, `sandbox_write`, and `sandbox_read` tools that operate entirely within the sandbox — the host filesystem stays untouched.
|
|
23
23
|
|
|
@@ -81,8 +81,39 @@ async def main() -> None:
|
|
|
81
81
|
| `sandbox_write` | Write a file into the container's filesystem |
|
|
82
82
|
| `sandbox_read` | Read a file from the container's filesystem |
|
|
83
83
|
|
|
84
|
+
## Container lifecycle
|
|
85
|
+
|
|
86
|
+
The sandbox container is created **lazily** on the first tool call (`sandbox_exec`,
|
|
87
|
+
`sandbox_write`, or `sandbox_read`) — `plugin.init()` itself does not start Docker.
|
|
88
|
+
The same container is reused for all subsequent tool calls within the session.
|
|
89
|
+
|
|
90
|
+
When `await plugin.close()` is called the container is stopped and removed
|
|
91
|
+
(`docker rm -f`). You should always call `close()` when the agent session ends
|
|
92
|
+
to avoid leaving containers behind:
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
async def run_with_cleanup(agent, ctx, plugin):
|
|
96
|
+
try:
|
|
97
|
+
result = await agent.run("...", ctx)
|
|
98
|
+
finally:
|
|
99
|
+
await plugin.close()
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
If Docker is not installed or the `docker` CLI is not on `PATH`, the container
|
|
103
|
+
creation step will raise a `RuntimeError` on the first tool call. You can detect
|
|
104
|
+
this before starting a session with `SandboxManager.docker_available()` (returns
|
|
105
|
+
`True` if the `docker` executable is found on `PATH`).
|
|
106
|
+
|
|
84
107
|
## Configuration
|
|
85
108
|
|
|
109
|
+
| Field | Type | Default | Description |
|
|
110
|
+
|-------|------|---------|-------------|
|
|
111
|
+
| `image` | `str` | `"python:latest"` | Docker image to use for the sandbox container |
|
|
112
|
+
| `memory` | `str` | `"256m"` | Memory limit passed to `docker run --memory` (e.g., `"512m"`, `"1g"`) |
|
|
113
|
+
| `cpus` | `str` | `"1.0"` | CPU limit passed to `docker run --cpus` |
|
|
114
|
+
| `network` | `bool` | `False` | Whether to allow network access. When `False`, `--network none` is set on the container. |
|
|
115
|
+
| `workdir` | `str` | `"/workspace"` | Working directory inside the container |
|
|
116
|
+
|
|
86
117
|
<!-- name: test_readme_config -->
|
|
87
118
|
```python
|
|
88
119
|
from axio_tools_docker.config import SandboxConfig
|
|
@@ -104,7 +135,7 @@ docker = "axio_tools_docker.plugin:DockerPlugin"
|
|
|
104
135
|
|
|
105
136
|
## Part of the axio ecosystem
|
|
106
137
|
|
|
107
|
-
[axio](https://github.com/axio-agent
|
|
138
|
+
[axio](https://github.com/mosquito/axio-agent) · [axio-tools-local](https://github.com/mosquito/axio-agent) · [axio-tools-mcp](https://github.com/mosquito/axio-agent) · [axio-tui](https://github.com/mosquito/axio-agent)
|
|
108
139
|
|
|
109
140
|
## License
|
|
110
141
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://pypi.org/project/axio-tools-docker/)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
|
|
7
|
-
Docker sandbox tools for [axio](https://github.com/axio-agent
|
|
7
|
+
Docker sandbox tools for [axio](https://github.com/mosquito/axio-agent).
|
|
8
8
|
|
|
9
9
|
Run agent-generated code and commands inside isolated Docker containers. The agent gets `sandbox_exec`, `sandbox_write`, and `sandbox_read` tools that operate entirely within the sandbox — the host filesystem stays untouched.
|
|
10
10
|
|
|
@@ -68,8 +68,39 @@ async def main() -> None:
|
|
|
68
68
|
| `sandbox_write` | Write a file into the container's filesystem |
|
|
69
69
|
| `sandbox_read` | Read a file from the container's filesystem |
|
|
70
70
|
|
|
71
|
+
## Container lifecycle
|
|
72
|
+
|
|
73
|
+
The sandbox container is created **lazily** on the first tool call (`sandbox_exec`,
|
|
74
|
+
`sandbox_write`, or `sandbox_read`) — `plugin.init()` itself does not start Docker.
|
|
75
|
+
The same container is reused for all subsequent tool calls within the session.
|
|
76
|
+
|
|
77
|
+
When `await plugin.close()` is called the container is stopped and removed
|
|
78
|
+
(`docker rm -f`). You should always call `close()` when the agent session ends
|
|
79
|
+
to avoid leaving containers behind:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
async def run_with_cleanup(agent, ctx, plugin):
|
|
83
|
+
try:
|
|
84
|
+
result = await agent.run("...", ctx)
|
|
85
|
+
finally:
|
|
86
|
+
await plugin.close()
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
If Docker is not installed or the `docker` CLI is not on `PATH`, the container
|
|
90
|
+
creation step will raise a `RuntimeError` on the first tool call. You can detect
|
|
91
|
+
this before starting a session with `SandboxManager.docker_available()` (returns
|
|
92
|
+
`True` if the `docker` executable is found on `PATH`).
|
|
93
|
+
|
|
71
94
|
## Configuration
|
|
72
95
|
|
|
96
|
+
| Field | Type | Default | Description |
|
|
97
|
+
|-------|------|---------|-------------|
|
|
98
|
+
| `image` | `str` | `"python:latest"` | Docker image to use for the sandbox container |
|
|
99
|
+
| `memory` | `str` | `"256m"` | Memory limit passed to `docker run --memory` (e.g., `"512m"`, `"1g"`) |
|
|
100
|
+
| `cpus` | `str` | `"1.0"` | CPU limit passed to `docker run --cpus` |
|
|
101
|
+
| `network` | `bool` | `False` | Whether to allow network access. When `False`, `--network none` is set on the container. |
|
|
102
|
+
| `workdir` | `str` | `"/workspace"` | Working directory inside the container |
|
|
103
|
+
|
|
73
104
|
<!-- name: test_readme_config -->
|
|
74
105
|
```python
|
|
75
106
|
from axio_tools_docker.config import SandboxConfig
|
|
@@ -91,7 +122,7 @@ docker = "axio_tools_docker.plugin:DockerPlugin"
|
|
|
91
122
|
|
|
92
123
|
## Part of the axio ecosystem
|
|
93
124
|
|
|
94
|
-
[axio](https://github.com/axio-agent
|
|
125
|
+
[axio](https://github.com/mosquito/axio-agent) · [axio-tools-local](https://github.com/mosquito/axio-agent) · [axio-tools-mcp](https://github.com/mosquito/axio-agent) · [axio-tui](https://github.com/mosquito/axio-agent)
|
|
95
126
|
|
|
96
127
|
## License
|
|
97
128
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "axio-tools-docker"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.7.0"
|
|
4
4
|
description = "Docker sandbox tools for Axio"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -13,8 +13,8 @@ docker = "axio_tools_docker.plugin:DockerPlugin"
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
[project.urls]
|
|
16
|
-
Homepage = "https://github.com/axio-agent
|
|
17
|
-
Repository = "https://github.com/axio-agent
|
|
16
|
+
Homepage = "https://github.com/mosquito/axio-agent"
|
|
17
|
+
Repository = "https://github.com/mosquito/axio-agent"
|
|
18
18
|
[build-system]
|
|
19
19
|
requires = ["hatchling"]
|
|
20
20
|
build-backend = "hatchling.build"
|
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import ClassVar
|
|
5
|
+
from typing import Any, ClassVar
|
|
6
6
|
|
|
7
7
|
from axio.tool import ToolHandler
|
|
8
8
|
|
|
9
9
|
from .manager import SandboxManager
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
def build_sandbox_exec(manager: SandboxManager) -> type[ToolHandler]:
|
|
12
|
+
def build_sandbox_exec(manager: SandboxManager) -> type[ToolHandler[Any]]:
|
|
13
13
|
"""Create a ToolHandler that executes commands in the sandbox."""
|
|
14
14
|
|
|
15
|
-
class SandboxExecHandler(ToolHandler):
|
|
15
|
+
class SandboxExecHandler(ToolHandler[Any]):
|
|
16
16
|
"""Execute a shell command inside a Docker sandbox container.
|
|
17
17
|
Returns combined stdout/stderr. Non-zero exit codes are reported.
|
|
18
18
|
Use for running code, tests, or CLI tools in an isolated environment."""
|
|
@@ -22,7 +22,7 @@ def build_sandbox_exec(manager: SandboxManager) -> type[ToolHandler]:
|
|
|
22
22
|
|
|
23
23
|
_manager: ClassVar[SandboxManager]
|
|
24
24
|
|
|
25
|
-
async def __call__(self) -> str:
|
|
25
|
+
async def __call__(self, context: Any) -> str:
|
|
26
26
|
if not self._manager.docker_available():
|
|
27
27
|
return "Error: Docker is not installed or not on PATH"
|
|
28
28
|
return await self._manager.exec(self.command, timeout=self.timeout)
|
|
@@ -31,10 +31,10 @@ def build_sandbox_exec(manager: SandboxManager) -> type[ToolHandler]:
|
|
|
31
31
|
return SandboxExecHandler
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
def build_sandbox_write(manager: SandboxManager) -> type[ToolHandler]:
|
|
34
|
+
def build_sandbox_write(manager: SandboxManager) -> type[ToolHandler[Any]]:
|
|
35
35
|
"""Create a ToolHandler that writes files in the sandbox."""
|
|
36
36
|
|
|
37
|
-
class SandboxWriteHandler(ToolHandler):
|
|
37
|
+
class SandboxWriteHandler(ToolHandler[Any]):
|
|
38
38
|
"""Write content to a file inside the Docker sandbox container.
|
|
39
39
|
Creates parent directories as needed. Use for placing source code,
|
|
40
40
|
config files, or test data in the sandbox."""
|
|
@@ -44,7 +44,7 @@ def build_sandbox_write(manager: SandboxManager) -> type[ToolHandler]:
|
|
|
44
44
|
|
|
45
45
|
_manager: ClassVar[SandboxManager]
|
|
46
46
|
|
|
47
|
-
async def __call__(self) -> str:
|
|
47
|
+
async def __call__(self, context: Any) -> str:
|
|
48
48
|
if not self._manager.docker_available():
|
|
49
49
|
return "Error: Docker is not installed or not on PATH"
|
|
50
50
|
return await self._manager.write_file(self.path, self.content)
|
|
@@ -53,10 +53,10 @@ def build_sandbox_write(manager: SandboxManager) -> type[ToolHandler]:
|
|
|
53
53
|
return SandboxWriteHandler
|
|
54
54
|
|
|
55
55
|
|
|
56
|
-
def build_sandbox_read(manager: SandboxManager) -> type[ToolHandler]:
|
|
56
|
+
def build_sandbox_read(manager: SandboxManager) -> type[ToolHandler[Any]]:
|
|
57
57
|
"""Create a ToolHandler that reads files from the sandbox."""
|
|
58
58
|
|
|
59
|
-
class SandboxReadHandler(ToolHandler):
|
|
59
|
+
class SandboxReadHandler(ToolHandler[Any]):
|
|
60
60
|
"""Read the contents of a file from the Docker sandbox container.
|
|
61
61
|
Returns the full file content as text."""
|
|
62
62
|
|
|
@@ -64,7 +64,7 @@ def build_sandbox_read(manager: SandboxManager) -> type[ToolHandler]:
|
|
|
64
64
|
|
|
65
65
|
_manager: ClassVar[SandboxManager]
|
|
66
66
|
|
|
67
|
-
async def __call__(self) -> str:
|
|
67
|
+
async def __call__(self, context: Any) -> str:
|
|
68
68
|
if not self._manager.docker_available():
|
|
69
69
|
return "Error: Docker is not installed or not on PATH"
|
|
70
70
|
return await self._manager.read_file(self.path)
|
|
@@ -23,7 +23,7 @@ class DockerPlugin:
|
|
|
23
23
|
|
|
24
24
|
def __init__(self) -> None:
|
|
25
25
|
self._manager = SandboxManager()
|
|
26
|
-
self._tools: list[Tool] = []
|
|
26
|
+
self._tools: list[Tool[Any]] = []
|
|
27
27
|
self._config: Any = None
|
|
28
28
|
self._global_config: Any = None
|
|
29
29
|
|
|
@@ -75,7 +75,7 @@ class DockerPlugin:
|
|
|
75
75
|
]
|
|
76
76
|
|
|
77
77
|
@property
|
|
78
|
-
def all_tools(self) -> list[Tool]:
|
|
78
|
+
def all_tools(self) -> list[Tool[Any]]:
|
|
79
79
|
return self._tools
|
|
80
80
|
|
|
81
81
|
def settings_screen(self) -> Any:
|
|
@@ -17,7 +17,7 @@ async def test_exec_handler_forwards_to_manager() -> None:
|
|
|
17
17
|
instance = cast(type[Any], handler_cls)(command="echo hi", timeout=10)
|
|
18
18
|
|
|
19
19
|
with patch.object(SandboxManager, "docker_available", return_value=True):
|
|
20
|
-
result = await instance()
|
|
20
|
+
result = await instance({})
|
|
21
21
|
|
|
22
22
|
assert result == "output"
|
|
23
23
|
manager.exec.assert_awaited_once_with("echo hi", timeout=10)
|
|
@@ -31,7 +31,7 @@ async def test_write_handler_forwards_to_manager() -> None:
|
|
|
31
31
|
instance = cast(type[Any], handler_cls)(path="/workspace/test.py", content="print('hi')")
|
|
32
32
|
|
|
33
33
|
with patch.object(SandboxManager, "docker_available", return_value=True):
|
|
34
|
-
result = await instance()
|
|
34
|
+
result = await instance({})
|
|
35
35
|
|
|
36
36
|
assert result == "Wrote /workspace/test.py"
|
|
37
37
|
manager.write_file.assert_awaited_once_with("/workspace/test.py", "print('hi')")
|
|
@@ -45,7 +45,7 @@ async def test_read_handler_forwards_to_manager() -> None:
|
|
|
45
45
|
instance = cast(type[Any], handler_cls)(path="/workspace/test.py")
|
|
46
46
|
|
|
47
47
|
with patch.object(SandboxManager, "docker_available", return_value=True):
|
|
48
|
-
result = await instance()
|
|
48
|
+
result = await instance({})
|
|
49
49
|
|
|
50
50
|
assert result == "file content"
|
|
51
51
|
manager.read_file.assert_awaited_once_with("/workspace/test.py")
|
|
@@ -57,7 +57,7 @@ async def test_exec_handler_returns_error_when_docker_missing() -> None:
|
|
|
57
57
|
instance = cast(type[Any], handler_cls)(command="echo hi")
|
|
58
58
|
|
|
59
59
|
with patch.object(SandboxManager, "docker_available", return_value=False):
|
|
60
|
-
result = await instance()
|
|
60
|
+
result = await instance({})
|
|
61
61
|
|
|
62
62
|
assert "not installed" in result
|
|
63
63
|
|
|
@@ -68,7 +68,7 @@ async def test_write_handler_returns_error_when_docker_missing() -> None:
|
|
|
68
68
|
instance = cast(type[Any], handler_cls)(path="/test", content="x")
|
|
69
69
|
|
|
70
70
|
with patch.object(SandboxManager, "docker_available", return_value=False):
|
|
71
|
-
result = await instance()
|
|
71
|
+
result = await instance({})
|
|
72
72
|
|
|
73
73
|
assert "not installed" in result
|
|
74
74
|
|
|
@@ -79,7 +79,7 @@ async def test_read_handler_returns_error_when_docker_missing() -> None:
|
|
|
79
79
|
instance = cast(type[Any], handler_cls)(path="/test")
|
|
80
80
|
|
|
81
81
|
with patch.object(SandboxManager, "docker_available", return_value=False):
|
|
82
|
-
result = await instance()
|
|
82
|
+
result = await instance({})
|
|
83
83
|
|
|
84
84
|
assert "not installed" in result
|
|
85
85
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|