axio-tools-docker 0.7.0__tar.gz → 0.9.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 (30) hide show
  1. axio_tools_docker-0.9.0/PKG-INFO +244 -0
  2. axio_tools_docker-0.9.0/README.md +230 -0
  3. axio_tools_docker-0.9.0/conftest.py +28 -0
  4. {axio_tools_docker-0.7.0 → axio_tools_docker-0.9.0}/pyproject.toml +3 -7
  5. axio_tools_docker-0.9.0/src/axio_tools_docker/__init__.py +5 -0
  6. axio_tools_docker-0.9.0/src/axio_tools_docker/py.typed +0 -0
  7. axio_tools_docker-0.9.0/src/axio_tools_docker/sandbox.py +535 -0
  8. axio_tools_docker-0.9.0/tests/__init__.py +0 -0
  9. axio_tools_docker-0.9.0/tests/test_integration.py +381 -0
  10. axio_tools_docker-0.9.0/tests/test_sandbox.py +976 -0
  11. axio_tools_docker-0.7.0/PKG-INFO +0 -142
  12. axio_tools_docker-0.7.0/README.md +0 -129
  13. axio_tools_docker-0.7.0/src/axio_tools_docker/__init__.py +0 -7
  14. axio_tools_docker-0.7.0/src/axio_tools_docker/config.py +0 -46
  15. axio_tools_docker-0.7.0/src/axio_tools_docker/handler.py +0 -73
  16. axio_tools_docker-0.7.0/src/axio_tools_docker/manager.py +0 -158
  17. axio_tools_docker-0.7.0/src/axio_tools_docker/plugin.py +0 -87
  18. axio_tools_docker-0.7.0/src/axio_tools_docker/settings.py +0 -5
  19. axio_tools_docker-0.7.0/src/axio_tools_docker/tui/__init__.py +0 -15
  20. axio_tools_docker-0.7.0/src/axio_tools_docker/tui/docker.py +0 -152
  21. axio_tools_docker-0.7.0/tests/test_config.py +0 -54
  22. axio_tools_docker-0.7.0/tests/test_handler.py +0 -100
  23. axio_tools_docker-0.7.0/tests/test_manager.py +0 -258
  24. axio_tools_docker-0.7.0/tests/test_plugin.py +0 -85
  25. {axio_tools_docker-0.7.0 → axio_tools_docker-0.9.0}/.github/workflows/publish.yml +0 -0
  26. {axio_tools_docker-0.7.0 → axio_tools_docker-0.9.0}/.github/workflows/tests.yml +0 -0
  27. {axio_tools_docker-0.7.0 → axio_tools_docker-0.9.0}/.gitignore +0 -0
  28. {axio_tools_docker-0.7.0 → axio_tools_docker-0.9.0}/LICENSE +0 -0
  29. {axio_tools_docker-0.7.0 → axio_tools_docker-0.9.0}/Makefile +0 -0
  30. {axio_tools_docker-0.7.0 → axio_tools_docker-0.9.0}/uv.lock +0 -0
@@ -0,0 +1,244 @@
1
+ Metadata-Version: 2.4
2
+ Name: axio-tools-docker
3
+ Version: 0.9.0
4
+ Summary: Docker sandbox tools for Axio
5
+ Project-URL: Homepage, https://github.com/mosquito/axio-agent
6
+ Project-URL: Repository, https://github.com/mosquito/axio-agent
7
+ License: MIT
8
+ License-File: LICENSE
9
+ Keywords: agent,ai,docker,llm,sandbox,tools
10
+ Requires-Python: >=3.12
11
+ Requires-Dist: aiodocker>=0.26
12
+ Requires-Dist: axio
13
+ Description-Content-Type: text/markdown
14
+
15
+ # axio-tools-docker
16
+
17
+ [![PyPI](https://img.shields.io/pypi/v/axio-tools-docker)](https://pypi.org/project/axio-tools-docker/)
18
+ [![Python](https://img.shields.io/pypi/pyversions/axio-tools-docker)](https://pypi.org/project/axio-tools-docker/)
19
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
20
+
21
+ Docker sandbox tools for [axio](https://github.com/mosquito/axio-agent).
22
+
23
+ `DockerSandbox` is an async context manager that spins up an isolated Docker
24
+ container on entry and removes it on exit. Inside the context it exposes six
25
+ `axio` tools - the same `shell`, `write_file`, `read_file`, `list_files`,
26
+ `run_python`, and `patch_file` as `axio-tools-local`, but every operation runs
27
+ inside the container, never on the host.
28
+
29
+ ## Requirements
30
+
31
+ Docker must be installed and running:
32
+
33
+ ```bash
34
+ docker info # should succeed
35
+ ```
36
+
37
+ The package talks to the Docker Engine API directly via
38
+ [aiodocker](https://aiodocker.readthedocs.io/) - the `docker` CLI is not
39
+ required.
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ pip install axio-tools-docker
45
+ ```
46
+
47
+ ## Quick start
48
+
49
+ <!-- name: test_readme_usage; fixtures: docker -->
50
+ ```python
51
+ import asyncio
52
+ from axio.agent import Agent
53
+ from axio.context import MemoryContextStore
54
+ from axio.testing import StubTransport, make_text_response
55
+ from axio_tools_docker import DockerSandbox
56
+
57
+ async def main() -> None:
58
+ transport = StubTransport([make_text_response("Done.")])
59
+ async with DockerSandbox(image="python:3.12-alpine") as sandbox:
60
+ agent = Agent(
61
+ system="You are a coding assistant. Use the sandbox tools to run code safely.",
62
+ tools=sandbox.tools,
63
+ transport=transport,
64
+ )
65
+ ctx = MemoryContextStore()
66
+ result = await agent.run("Print hello from Python.", ctx)
67
+ print(result)
68
+
69
+ asyncio.run(main())
70
+ ```
71
+
72
+ ## Sandbox tools
73
+
74
+ These mirror `axio-tools-local` exactly - same names and field schemas:
75
+
76
+ | Tool | Description |
77
+ |---|---|
78
+ | `shell` | Run a shell command; returns stdout + stderr. Supports `timeout`, `cwd`, `stdin`. |
79
+ | `write_file` | Create or overwrite a file. Parent directories are created automatically. |
80
+ | `read_file` | Read a file with optional `start_line`/`end_line`, `line_numbers`, `max_chars`. |
81
+ | `list_files` | List a directory; directories first with a trailing `/`. |
82
+ | `run_python` | Execute a Python snippet in a subprocess. Supports `timeout`, `cwd`, `stdin`. |
83
+ | `patch_file` | Replace lines `from_line`..`to_line` (1-indexed, inclusive). `to_line = from_line - 1` inserts. |
84
+
85
+ ## Container lifecycle
86
+
87
+ The container is created on `__aenter__` and removed on `__aexit__` (`docker rm -f`).
88
+ Cleanup runs even when the body raises an exception:
89
+
90
+ ```python
91
+ from axio_tools_docker import DockerSandbox
92
+ from axio import Agent
93
+
94
+ async def run(ctx):
95
+ agent = Agent(
96
+ system="You are a coding assistant. Use the sandbox tools to run code safely.",
97
+ tools=ctx.sandbox.tools,
98
+ )
99
+
100
+ async with DockerSandbox(image="alpine:latest") as sandbox:
101
+ await agent.run("...", ctx)
102
+
103
+ # container removed here (unless remove=False)
104
+ ```
105
+
106
+ The image is pulled automatically if not present locally. If the Docker daemon
107
+ is unreachable, `__aenter__` raises immediately:
108
+
109
+ ```
110
+ RuntimeError: Docker daemon not available at 'unix:///var/run/docker.sock': ...
111
+ ```
112
+
113
+ The running container's ID is available as `sandbox.container_id` inside the
114
+ `async with` block.
115
+
116
+ ## Named containers and reuse
117
+
118
+ Pass `name=` to give the container a fixed name. If a running container with
119
+ that name already exists, the sandbox attaches to it instead of creating a new
120
+ one, and never removes it on exit - regardless of `remove`:
121
+
122
+ ```python
123
+ import asyncio
124
+ from axio_tools_docker import DockerSandbox
125
+
126
+ async def first_session() -> None:
127
+ async with DockerSandbox(image="python:3.12-slim", name="my-sandbox", remove=False) as sb:
128
+ await sb.exec("pip install requests")
129
+
130
+ async def second_session() -> None:
131
+ async with DockerSandbox(name="my-sandbox") as sb:
132
+ result = await sb.exec("python3 -c 'import requests; print(requests.__version__)'")
133
+
134
+ asyncio.run(first_session())
135
+ asyncio.run(second_session())
136
+ ```
137
+
138
+ ## Named volumes
139
+
140
+ Named volumes are managed by the Docker daemon and persist across container
141
+ restarts. Use them to share state between sandbox sessions:
142
+
143
+ <!-- name: test_readme_example; fixtures: docker -->
144
+ ```python
145
+ import asyncio
146
+ from axio_tools_docker import DockerSandbox
147
+
148
+ async def main() -> None:
149
+ async with DockerSandbox(
150
+ image="python:3.12-alpine",
151
+ named_volumes={"/data": "my-project-data"},
152
+ ) as sb:
153
+ await sb.write_file("/data/state.json", '{"count": 1}')
154
+ # Container removed, volume survives.
155
+
156
+ async with DockerSandbox(
157
+ image="python:3.12-alpine",
158
+ named_volumes={"/data": "my-project-data"},
159
+ volumes_remove=True, # delete the volume on exit
160
+ ) as sb:
161
+ raw = await sb.read_file_bytes("/data/state.json")
162
+ assert raw.decode() == '{"count": 1}'
163
+
164
+ asyncio.run(main())
165
+ ```
166
+
167
+ Docker creates the volume automatically if it does not exist yet.
168
+
169
+ ## Configuration
170
+
171
+ <!-- name: test_readme_config; fixtures: docker -->
172
+ ```python
173
+ import os
174
+ from axio_tools_docker import DockerSandbox
175
+
176
+ sandbox = DockerSandbox(
177
+ os.getenv("DOCKER_HOST", "unix:///var/run/docker.sock"), # Docker daemon URL, optional
178
+ image="python:3.12-slim",
179
+ memory="512m", # memory limit: "256m", "1g", …
180
+ cpus="2.0", # CPU limit
181
+ network=False, # False=none, True=default, str=explicit mode
182
+ workdir="/workspace",
183
+ volumes={"/workspace": "/tmp/host-dir"}, # {container_path: host_path}
184
+ named_volumes={"/data": "my-project-data"}, # named Docker volumes
185
+ volumes_remove=False, # remove named volumes on exit
186
+ env={"PYTHONPATH": "/app"},
187
+ user="nobody",
188
+ name="my-agent-sandbox",
189
+ remove=False,
190
+ read_only=True, # read-only root filesystem
191
+ shm_size="64m", # /dev/shm size
192
+ cap_add=["NET_ADMIN"], # add Linux capabilities
193
+ cap_drop=["ALL"], # drop Linux capabilities
194
+ privileged=False,
195
+ ulimits={"nofile": (1024, 65536), "nproc": 512},
196
+ tmpfs={"/tmp": "size=128m,mode=1777"},
197
+ ports={8080: 8080}, # {container_port: host_port}
198
+ platform="linux/amd64",
199
+ extra_hosts={"host.docker.internal": "host-gateway"},
200
+ devices=["/dev/net/tun", "/dev/sda:/dev/xvda:r"],
201
+ dns=["8.8.8.8", "1.1.1.1"],
202
+ )
203
+
204
+ assert sandbox.image == "python:3.12-slim"
205
+ assert sandbox.memory == "512m"
206
+ assert sandbox.cpus == "2.0"
207
+ assert sandbox.network == False
208
+ ```
209
+
210
+ | Parameter | Type | Default | Description |
211
+ |-----------|------|---------|-------------|
212
+ | `url` | `str` | `"unix:///var/run/docker.sock"` | Docker daemon URL (unix socket or TCP). Positional. |
213
+ | `image` | `str` | `"python:latest"` | Container image. Pulled automatically if not present. |
214
+ | `memory` | `str` | `"256m"` | Memory limit. Accepts `k`/`m`/`g` suffixes. |
215
+ | `cpus` | `str` | `"1.0"` | CPU limit as a decimal string. |
216
+ | `network` | `bool \| str` | `False` | Network mode. `False` → `none`. `True` → Docker default. String → explicit `NetworkMode` (e.g. `"host"`, `"bridge"`, `"my-net"`). |
217
+ | `workdir` | `str` | `"/workspace"` | Working directory inside the container. |
218
+ | `volumes` | `dict[str, str]` | `{}` | Bind mounts as `{container_path: host_path}`. |
219
+ | `named_volumes` | `dict[str, str]` | `{}` | Named Docker volumes as `{container_path: volume_name}`. Created automatically if absent. |
220
+ | `volumes_remove` | `bool` | `False` | Remove named volumes on exit. No effect when attached to an existing container. |
221
+ | `env` | `dict[str, str]` | `{}` | Environment variables passed to all commands. |
222
+ | `user` | `str` | `""` | User to run as (e.g. `"nobody"`, `"1000"`). |
223
+ | `name` | `str` | `""` | Container name. Attaches to existing container if found; creates new one otherwise. |
224
+ | `remove` | `bool` | `True` | Remove container on exit. No effect when attached to an existing container. |
225
+ | `read_only` | `bool` | `False` | Read-only root filesystem. Combine with `tmpfs` for writable scratch paths. |
226
+ | `shm_size` | `str` | `""` | `/dev/shm` size (e.g. `"64m"`). Useful for PyTorch / shared-memory IPC. |
227
+ | `cap_add` | `list[str]` | `[]` | Linux capabilities to add (e.g. `["NET_ADMIN", "SYS_PTRACE"]`). |
228
+ | `cap_drop` | `list[str]` | `[]` | Linux capabilities to drop (e.g. `["ALL"]`). |
229
+ | `privileged` | `bool` | `False` | Extended privileges - full capability set and device access. Use with care. |
230
+ | `ulimits` | `dict[str, int \| tuple[int, int]]` | `{}` | Resource limits. `{"nofile": 1024}` → soft=hard=1024. `{"nofile": (1024, 65536)}` → soft/hard split. |
231
+ | `tmpfs` | `dict[str, str]` | `{}` | Tmpfs mounts as `{path: options}` (e.g. `{"/tmp": "size=128m,mode=1777"}`). |
232
+ | `ports` | `dict[int, int]` | `{}` | Port bindings as `{container_port: host_port}`. Only meaningful when `network != False`. |
233
+ | `platform` | `str` | `""` | Platform override (e.g. `"linux/amd64"`, `"linux/arm64"`). |
234
+ | `extra_hosts` | `dict[str, str]` | `{}` | Extra `/etc/hosts` entries as `{hostname: ip}`. |
235
+ | `devices` | `list[str]` | `[]` | Host devices to expose. Format: `"/dev/sda"`, `"/dev/sda:/dev/xvda"`, `"/dev/sda:/dev/xvda:r"`. |
236
+ | `dns` | `list[str]` | `[]` | DNS servers (e.g. `["8.8.8.8"]`). Only meaningful when `network != False`. |
237
+
238
+ ## Part of the axio ecosystem
239
+
240
+ [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)
241
+
242
+ ## License
243
+
244
+ MIT
@@ -0,0 +1,230 @@
1
+ # axio-tools-docker
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/axio-tools-docker)](https://pypi.org/project/axio-tools-docker/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/axio-tools-docker)](https://pypi.org/project/axio-tools-docker/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
+
7
+ Docker sandbox tools for [axio](https://github.com/mosquito/axio-agent).
8
+
9
+ `DockerSandbox` is an async context manager that spins up an isolated Docker
10
+ container on entry and removes it on exit. Inside the context it exposes six
11
+ `axio` tools - the same `shell`, `write_file`, `read_file`, `list_files`,
12
+ `run_python`, and `patch_file` as `axio-tools-local`, but every operation runs
13
+ inside the container, never on the host.
14
+
15
+ ## Requirements
16
+
17
+ Docker must be installed and running:
18
+
19
+ ```bash
20
+ docker info # should succeed
21
+ ```
22
+
23
+ The package talks to the Docker Engine API directly via
24
+ [aiodocker](https://aiodocker.readthedocs.io/) - the `docker` CLI is not
25
+ required.
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ pip install axio-tools-docker
31
+ ```
32
+
33
+ ## Quick start
34
+
35
+ <!-- name: test_readme_usage; fixtures: docker -->
36
+ ```python
37
+ import asyncio
38
+ from axio.agent import Agent
39
+ from axio.context import MemoryContextStore
40
+ from axio.testing import StubTransport, make_text_response
41
+ from axio_tools_docker import DockerSandbox
42
+
43
+ async def main() -> None:
44
+ transport = StubTransport([make_text_response("Done.")])
45
+ async with DockerSandbox(image="python:3.12-alpine") as sandbox:
46
+ agent = Agent(
47
+ system="You are a coding assistant. Use the sandbox tools to run code safely.",
48
+ tools=sandbox.tools,
49
+ transport=transport,
50
+ )
51
+ ctx = MemoryContextStore()
52
+ result = await agent.run("Print hello from Python.", ctx)
53
+ print(result)
54
+
55
+ asyncio.run(main())
56
+ ```
57
+
58
+ ## Sandbox tools
59
+
60
+ These mirror `axio-tools-local` exactly - same names and field schemas:
61
+
62
+ | Tool | Description |
63
+ |---|---|
64
+ | `shell` | Run a shell command; returns stdout + stderr. Supports `timeout`, `cwd`, `stdin`. |
65
+ | `write_file` | Create or overwrite a file. Parent directories are created automatically. |
66
+ | `read_file` | Read a file with optional `start_line`/`end_line`, `line_numbers`, `max_chars`. |
67
+ | `list_files` | List a directory; directories first with a trailing `/`. |
68
+ | `run_python` | Execute a Python snippet in a subprocess. Supports `timeout`, `cwd`, `stdin`. |
69
+ | `patch_file` | Replace lines `from_line`..`to_line` (1-indexed, inclusive). `to_line = from_line - 1` inserts. |
70
+
71
+ ## Container lifecycle
72
+
73
+ The container is created on `__aenter__` and removed on `__aexit__` (`docker rm -f`).
74
+ Cleanup runs even when the body raises an exception:
75
+
76
+ ```python
77
+ from axio_tools_docker import DockerSandbox
78
+ from axio import Agent
79
+
80
+ async def run(ctx):
81
+ agent = Agent(
82
+ system="You are a coding assistant. Use the sandbox tools to run code safely.",
83
+ tools=ctx.sandbox.tools,
84
+ )
85
+
86
+ async with DockerSandbox(image="alpine:latest") as sandbox:
87
+ await agent.run("...", ctx)
88
+
89
+ # container removed here (unless remove=False)
90
+ ```
91
+
92
+ The image is pulled automatically if not present locally. If the Docker daemon
93
+ is unreachable, `__aenter__` raises immediately:
94
+
95
+ ```
96
+ RuntimeError: Docker daemon not available at 'unix:///var/run/docker.sock': ...
97
+ ```
98
+
99
+ The running container's ID is available as `sandbox.container_id` inside the
100
+ `async with` block.
101
+
102
+ ## Named containers and reuse
103
+
104
+ Pass `name=` to give the container a fixed name. If a running container with
105
+ that name already exists, the sandbox attaches to it instead of creating a new
106
+ one, and never removes it on exit - regardless of `remove`:
107
+
108
+ ```python
109
+ import asyncio
110
+ from axio_tools_docker import DockerSandbox
111
+
112
+ async def first_session() -> None:
113
+ async with DockerSandbox(image="python:3.12-slim", name="my-sandbox", remove=False) as sb:
114
+ await sb.exec("pip install requests")
115
+
116
+ async def second_session() -> None:
117
+ async with DockerSandbox(name="my-sandbox") as sb:
118
+ result = await sb.exec("python3 -c 'import requests; print(requests.__version__)'")
119
+
120
+ asyncio.run(first_session())
121
+ asyncio.run(second_session())
122
+ ```
123
+
124
+ ## Named volumes
125
+
126
+ Named volumes are managed by the Docker daemon and persist across container
127
+ restarts. Use them to share state between sandbox sessions:
128
+
129
+ <!-- name: test_readme_example; fixtures: docker -->
130
+ ```python
131
+ import asyncio
132
+ from axio_tools_docker import DockerSandbox
133
+
134
+ async def main() -> None:
135
+ async with DockerSandbox(
136
+ image="python:3.12-alpine",
137
+ named_volumes={"/data": "my-project-data"},
138
+ ) as sb:
139
+ await sb.write_file("/data/state.json", '{"count": 1}')
140
+ # Container removed, volume survives.
141
+
142
+ async with DockerSandbox(
143
+ image="python:3.12-alpine",
144
+ named_volumes={"/data": "my-project-data"},
145
+ volumes_remove=True, # delete the volume on exit
146
+ ) as sb:
147
+ raw = await sb.read_file_bytes("/data/state.json")
148
+ assert raw.decode() == '{"count": 1}'
149
+
150
+ asyncio.run(main())
151
+ ```
152
+
153
+ Docker creates the volume automatically if it does not exist yet.
154
+
155
+ ## Configuration
156
+
157
+ <!-- name: test_readme_config; fixtures: docker -->
158
+ ```python
159
+ import os
160
+ from axio_tools_docker import DockerSandbox
161
+
162
+ sandbox = DockerSandbox(
163
+ os.getenv("DOCKER_HOST", "unix:///var/run/docker.sock"), # Docker daemon URL, optional
164
+ image="python:3.12-slim",
165
+ memory="512m", # memory limit: "256m", "1g", …
166
+ cpus="2.0", # CPU limit
167
+ network=False, # False=none, True=default, str=explicit mode
168
+ workdir="/workspace",
169
+ volumes={"/workspace": "/tmp/host-dir"}, # {container_path: host_path}
170
+ named_volumes={"/data": "my-project-data"}, # named Docker volumes
171
+ volumes_remove=False, # remove named volumes on exit
172
+ env={"PYTHONPATH": "/app"},
173
+ user="nobody",
174
+ name="my-agent-sandbox",
175
+ remove=False,
176
+ read_only=True, # read-only root filesystem
177
+ shm_size="64m", # /dev/shm size
178
+ cap_add=["NET_ADMIN"], # add Linux capabilities
179
+ cap_drop=["ALL"], # drop Linux capabilities
180
+ privileged=False,
181
+ ulimits={"nofile": (1024, 65536), "nproc": 512},
182
+ tmpfs={"/tmp": "size=128m,mode=1777"},
183
+ ports={8080: 8080}, # {container_port: host_port}
184
+ platform="linux/amd64",
185
+ extra_hosts={"host.docker.internal": "host-gateway"},
186
+ devices=["/dev/net/tun", "/dev/sda:/dev/xvda:r"],
187
+ dns=["8.8.8.8", "1.1.1.1"],
188
+ )
189
+
190
+ assert sandbox.image == "python:3.12-slim"
191
+ assert sandbox.memory == "512m"
192
+ assert sandbox.cpus == "2.0"
193
+ assert sandbox.network == False
194
+ ```
195
+
196
+ | Parameter | Type | Default | Description |
197
+ |-----------|------|---------|-------------|
198
+ | `url` | `str` | `"unix:///var/run/docker.sock"` | Docker daemon URL (unix socket or TCP). Positional. |
199
+ | `image` | `str` | `"python:latest"` | Container image. Pulled automatically if not present. |
200
+ | `memory` | `str` | `"256m"` | Memory limit. Accepts `k`/`m`/`g` suffixes. |
201
+ | `cpus` | `str` | `"1.0"` | CPU limit as a decimal string. |
202
+ | `network` | `bool \| str` | `False` | Network mode. `False` → `none`. `True` → Docker default. String → explicit `NetworkMode` (e.g. `"host"`, `"bridge"`, `"my-net"`). |
203
+ | `workdir` | `str` | `"/workspace"` | Working directory inside the container. |
204
+ | `volumes` | `dict[str, str]` | `{}` | Bind mounts as `{container_path: host_path}`. |
205
+ | `named_volumes` | `dict[str, str]` | `{}` | Named Docker volumes as `{container_path: volume_name}`. Created automatically if absent. |
206
+ | `volumes_remove` | `bool` | `False` | Remove named volumes on exit. No effect when attached to an existing container. |
207
+ | `env` | `dict[str, str]` | `{}` | Environment variables passed to all commands. |
208
+ | `user` | `str` | `""` | User to run as (e.g. `"nobody"`, `"1000"`). |
209
+ | `name` | `str` | `""` | Container name. Attaches to existing container if found; creates new one otherwise. |
210
+ | `remove` | `bool` | `True` | Remove container on exit. No effect when attached to an existing container. |
211
+ | `read_only` | `bool` | `False` | Read-only root filesystem. Combine with `tmpfs` for writable scratch paths. |
212
+ | `shm_size` | `str` | `""` | `/dev/shm` size (e.g. `"64m"`). Useful for PyTorch / shared-memory IPC. |
213
+ | `cap_add` | `list[str]` | `[]` | Linux capabilities to add (e.g. `["NET_ADMIN", "SYS_PTRACE"]`). |
214
+ | `cap_drop` | `list[str]` | `[]` | Linux capabilities to drop (e.g. `["ALL"]`). |
215
+ | `privileged` | `bool` | `False` | Extended privileges - full capability set and device access. Use with care. |
216
+ | `ulimits` | `dict[str, int \| tuple[int, int]]` | `{}` | Resource limits. `{"nofile": 1024}` → soft=hard=1024. `{"nofile": (1024, 65536)}` → soft/hard split. |
217
+ | `tmpfs` | `dict[str, str]` | `{}` | Tmpfs mounts as `{path: options}` (e.g. `{"/tmp": "size=128m,mode=1777"}`). |
218
+ | `ports` | `dict[int, int]` | `{}` | Port bindings as `{container_port: host_port}`. Only meaningful when `network != False`. |
219
+ | `platform` | `str` | `""` | Platform override (e.g. `"linux/amd64"`, `"linux/arm64"`). |
220
+ | `extra_hosts` | `dict[str, str]` | `{}` | Extra `/etc/hosts` entries as `{hostname: ip}`. |
221
+ | `devices` | `list[str]` | `[]` | Host devices to expose. Format: `"/dev/sda"`, `"/dev/sda:/dev/xvda"`, `"/dev/sda:/dev/xvda:r"`. |
222
+ | `dns` | `list[str]` | `[]` | DNS servers (e.g. `["8.8.8.8"]`). Only meaningful when `network != False`. |
223
+
224
+ ## Part of the axio ecosystem
225
+
226
+ [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)
227
+
228
+ ## License
229
+
230
+ MIT
@@ -0,0 +1,28 @@
1
+ """Pytest configuration for axio-tools-docker."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import os
7
+
8
+ import aiodocker
9
+ import pytest
10
+
11
+
12
+ @pytest.fixture(scope="session")
13
+ def docker() -> str:
14
+ docker_url = os.getenv("DOCKER_HOST", "unix:///var/run/docker.sock")
15
+
16
+ async def _probe() -> bool:
17
+ # noinspection PyBroadException
18
+ try:
19
+ async with aiodocker.Docker(url=docker_url) as client:
20
+ await client.system.info()
21
+ return True
22
+ except Exception:
23
+ return False
24
+
25
+ if not asyncio.run(_probe()):
26
+ pytest.skip("Skipping due to missing Docker daemon")
27
+
28
+ return docker_url
@@ -1,16 +1,12 @@
1
1
  [project]
2
2
  name = "axio-tools-docker"
3
- version = "0.7.0"
3
+ version = "0.9.0"
4
4
  description = "Docker sandbox tools for Axio"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
7
7
  license = {text = "MIT"}
8
8
  keywords = ["llm", "agent", "ai", "docker", "sandbox", "tools"]
9
- dependencies = ["axio"]
10
-
11
- [project.entry-points."axio.tools.settings"]
12
- docker = "axio_tools_docker.plugin:DockerPlugin"
13
-
9
+ dependencies = ["axio", "aiodocker>=0.26"]
14
10
 
15
11
  [project.urls]
16
12
  Homepage = "https://github.com/mosquito/axio-agent"
@@ -45,6 +41,6 @@ dev = [
45
41
  "ruff>=0.9",
46
42
  "pytest-cov>=7.1.0",
47
43
  "markdown-pytest>=0.6.0",
48
- "textual>=2.1.0",
44
+ "aiodocker>=0.26",
49
45
  ]
50
46
 
@@ -0,0 +1,5 @@
1
+ """Docker sandbox tools for Axio."""
2
+
3
+ from .sandbox import DockerSandbox
4
+
5
+ __all__ = ["DockerSandbox"]
File without changes