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.
Files changed (21) hide show
  1. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/.gitignore +2 -0
  2. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/PKG-INFO +36 -5
  3. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/README.md +33 -2
  4. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/pyproject.toml +3 -3
  5. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/src/axio_tools_docker/handler.py +10 -10
  6. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/src/axio_tools_docker/plugin.py +2 -2
  7. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/tests/test_handler.py +6 -6
  8. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/.github/workflows/publish.yml +0 -0
  9. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/.github/workflows/tests.yml +0 -0
  10. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/LICENSE +0 -0
  11. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/Makefile +0 -0
  12. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/src/axio_tools_docker/__init__.py +0 -0
  13. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/src/axio_tools_docker/config.py +0 -0
  14. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/src/axio_tools_docker/manager.py +0 -0
  15. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/src/axio_tools_docker/settings.py +0 -0
  16. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/src/axio_tools_docker/tui/__init__.py +0 -0
  17. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/src/axio_tools_docker/tui/docker.py +0 -0
  18. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/tests/test_config.py +0 -0
  19. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/tests/test_manager.py +0 -0
  20. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/tests/test_plugin.py +0 -0
  21. {axio_tools_docker-0.6.1 → axio_tools_docker-0.7.0}/uv.lock +0 -0
@@ -10,3 +10,5 @@ dist/
10
10
  build/
11
11
  _build/
12
12
  .DS_Store
13
+ *.sqlite
14
+ *.db
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: axio-tools-docker
3
- Version: 0.6.1
3
+ Version: 0.7.0
4
4
  Summary: Docker sandbox tools for Axio
5
- Project-URL: Homepage, https://github.com/axio-agent/axio-tools-docker
6
- Project-URL: Repository, https://github.com/axio-agent/axio-tools-docker
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
  [![Python](https://img.shields.io/pypi/pyversions/axio-tools-docker)](https://pypi.org/project/axio-tools-docker/)
18
18
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
19
19
 
20
- Docker sandbox tools for [axio](https://github.com/axio-agent/axio).
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/axio) · [axio-tools-local](https://github.com/axio-agent/axio-tools-local) · [axio-tools-mcp](https://github.com/axio-agent/axio-tools-mcp) · [axio-tui](https://github.com/axio-agent/axio-tui)
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
  [![Python](https://img.shields.io/pypi/pyversions/axio-tools-docker)](https://pypi.org/project/axio-tools-docker/)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
6
 
7
- Docker sandbox tools for [axio](https://github.com/axio-agent/axio).
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/axio) · [axio-tools-local](https://github.com/axio-agent/axio-tools-local) · [axio-tools-mcp](https://github.com/axio-agent/axio-tools-mcp) · [axio-tui](https://github.com/axio-agent/axio-tui)
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.6.1"
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/axio-tools-docker"
17
- Repository = "https://github.com/axio-agent/axio-tools-docker"
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