agent-containers 0.1.0.dev0__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.
- agent_containers-0.1.0.dev0/LICENSE +21 -0
- agent_containers-0.1.0.dev0/PKG-INFO +195 -0
- agent_containers-0.1.0.dev0/README.md +176 -0
- agent_containers-0.1.0.dev0/pyproject.toml +108 -0
- agent_containers-0.1.0.dev0/pyproject.toml.orig +86 -0
- agent_containers-0.1.0.dev0/src/agent_containers/__init__.py +1 -0
- agent_containers-0.1.0.dev0/src/agent_containers/__main__.py +6 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/adal/Dockerfile +103 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/adal/examples/egress-allowlist.txt +10 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/adal/packages-apt.txt +13 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/adal/packages-npm.txt +2 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/adal/packages-uv.txt +6 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/adal/tools-uv.txt +4 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/agent-gateway/Dockerfile +50 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/agent-gateway/entrypoint.sh +28 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/agent-gateway/sshd_config.agent-gateway +11 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/aider/Dockerfile +114 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/aider/examples/egress-allowlist.txt +16 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/aider/packages-apt.txt +16 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/aider/packages-npm.txt +7 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/aider/packages-uv.txt +9 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/aider/tools-uv.txt +9 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/Dockerfile +171 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/entrypoint.sh +22 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/examples/egress-allowlist.txt +14 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/examples/settings.local-model.json +7 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/packages-apt.txt +16 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/packages-npm.txt +7 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/packages-uv.txt +9 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/plugin-marketplaces.txt +23 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/plugins.txt +13 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/proxy-ca.d/.keep +1 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/claude-code/tools-uv.txt +9 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/codex/Dockerfile +147 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/codex/codex-wrapper.sh +13 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/codex/examples/egress-allowlist.txt +10 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/codex/packages-apt.txt +13 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/codex/packages-npm.txt +2 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/codex/packages-uv.txt +6 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/codex/plugin-marketplaces.txt +2 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/codex/plugins.txt +2 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/codex/proxy-ca.d/.keep +1 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/codex/tools-uv.txt +4 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/hermes/Dockerfile +122 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/hermes/entrypoint.sh +49 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/hermes/packages-apt.txt +16 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/hermes/packages-npm.txt +7 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/hermes/packages-uv.txt +9 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/hermes/proxy-ca.d/.keep +1 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/hermes/tools-uv.txt +9 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/kilo-code/Dockerfile +101 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/kilo-code/examples/egress-allowlist.txt +7 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/kilo-code/packages-apt.txt +5 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/kilo-code/packages-npm.txt +1 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/kilo-code/packages-uv.txt +4 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/kilo-code/tools-uv.txt +4 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/opencode/Dockerfile +126 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/opencode/examples/egress-allowlist.txt +6 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/opencode/packages-apt.txt +13 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/opencode/packages-npm.txt +2 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/opencode/packages-uv.txt +6 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/opencode/proxy-ca.d/.keep +1 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/opencode/tools-uv.txt +4 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/qwen-code/Dockerfile +104 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/qwen-code/examples/egress-allowlist.txt +10 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/qwen-code/packages-apt.txt +13 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/qwen-code/packages-npm.txt +2 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/qwen-code/packages-uv.txt +6 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/qwen-code/tools-uv.txt +4 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/shared/egress-allowlist.sh +138 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/shared/install-additional-packages.sh +79 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/shared/install-workload-base.sh +46 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/shared/workload-entrypoint.sh +82 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/volume-bridge/Dockerfile +37 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/volume-bridge/entrypoint.sh +29 -0
- agent_containers-0.1.0.dev0/src/agent_containers/_assets/agent-images/volume-bridge/serve.sh +66 -0
- agent_containers-0.1.0.dev0/src/agent_containers/assets.py +38 -0
- agent_containers-0.1.0.dev0/src/agent_containers/build_context.py +116 -0
- agent_containers-0.1.0.dev0/src/agent_containers/cli.py +212 -0
- agent_containers-0.1.0.dev0/src/agent_containers/creation.py +177 -0
- agent_containers-0.1.0.dev0/src/agent_containers/docker.py +562 -0
- agent_containers-0.1.0.dev0/src/agent_containers/lifecycle.py +325 -0
- agent_containers-0.1.0.dev0/src/agent_containers/planner.py +99 -0
- agent_containers-0.1.0.dev0/src/agent_containers/profile.py +377 -0
- agent_containers-0.1.0.dev0/src/agent_containers/shortcuts.py +159 -0
- agent_containers-0.1.0.dev0/src/agent_containers/state.py +154 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Anirban Basu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agent-containers
|
|
3
|
+
Version: 0.1.0.dev0
|
|
4
|
+
Summary: Deterministic onboarding and maintenance for hardened coding-agent containers
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Requires-Dist: pydantic>=2.11,<3
|
|
12
|
+
Requires-Dist: tomlkit>=0.13,<1
|
|
13
|
+
Requires-Dist: typer>=0.16,<1
|
|
14
|
+
Requires-Python: >=3.12
|
|
15
|
+
Project-URL: Repository, https://github.com/anirbanbasu/agent-containers
|
|
16
|
+
Project-URL: Issues, https://github.com/anirbanbasu/agent-containers/issues
|
|
17
|
+
Project-URL: Download, https://pypi.org/project/agent-containers/
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# agent-containers CLI
|
|
21
|
+
|
|
22
|
+
The Python package is tested on Python 3.14 (with Python 3.12+ support). It
|
|
23
|
+
validates profiles, produces offline plans, materializes bundled image recipes,
|
|
24
|
+
and applies a direct-Docker deployment without launching an agent session.
|
|
25
|
+
|
|
26
|
+
This directory will contain the standalone Python project for deterministic
|
|
27
|
+
onboarding and maintenance of this repository's hardened agent images. The
|
|
28
|
+
distribution and executable name is `agent-containers`; the Python import
|
|
29
|
+
package will be `agent_containers`.
|
|
30
|
+
|
|
31
|
+
## Agreed scope
|
|
32
|
+
|
|
33
|
+
- Human-editable, versioned TOML profiles, with separate machine-managed
|
|
34
|
+
deployment state. Interactive prompts and noninteractive inputs use the same
|
|
35
|
+
validation model; unknown fields are errors.
|
|
36
|
+
- Claude Code, OpenCode, Codex, and Hermes adapters, targeting Linux Docker Engine
|
|
37
|
+
and macOS Docker Desktop. Support claims require actual integration tests.
|
|
38
|
+
- All four optional package categories: apt packages, npm packages, isolated uv
|
|
39
|
+
tools, and system Python libraries installed with uv.
|
|
40
|
+
- Explicit custom file/directory mounts and copy-once configuration seeds,
|
|
41
|
+
without duplicating each agent's settings schema. Relative input paths resolve
|
|
42
|
+
against the profile directory; the workspace defaults to the launch directory.
|
|
43
|
+
- Composable hosted/local/custom model endpoints, HTTP(S) proxies, CA trust,
|
|
44
|
+
egress gateways, and experimental observability integrations. Decant is
|
|
45
|
+
experimental and currently limited to Claude Code and Codex; it is disabled
|
|
46
|
+
unless a profile opts in. Opted-in Decant launches use user-scoped image,
|
|
47
|
+
container, and data-volume names and mount selected agent collection
|
|
48
|
+
subdirectories directly; the delegated volume bridge remains a separate
|
|
49
|
+
manual setup. Local inference engine installation and model
|
|
50
|
+
downloads are outside the agreed initial scope.
|
|
51
|
+
- Explicit application and updates, retained prior deployments, and rollback of
|
|
52
|
+
the selected image and managed launch configuration. Rollback does not restore
|
|
53
|
+
mutable home-volume data or terminate existing sessions automatically.
|
|
54
|
+
- Image recipes bundled from canonical `../agent-images/` sources in both wheel
|
|
55
|
+
and source distributions. No maintained duplicate or installed-package edits.
|
|
56
|
+
|
|
57
|
+
## Safety and verification
|
|
58
|
+
|
|
59
|
+
Preserve the repository's containment contract. Package selection must not
|
|
60
|
+
silently expand runtime egress. Credentials must not appear in profiles, build
|
|
61
|
+
arguments, images, logs, or command output; profiles reference runtime inputs.
|
|
62
|
+
|
|
63
|
+
When a profile declares a proxy, its generated Docker launch exports both
|
|
64
|
+
uppercase and lowercase `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` variables,
|
|
65
|
+
and enables Node's environment proxy support. A profile `proxy.ca_file` or
|
|
66
|
+
`proxy.ca_dir` is copied into the isolated image build context and installed
|
|
67
|
+
into the image's system trust store. The generated launch points
|
|
68
|
+
`SSL_CERT_FILE`, `REQUESTS_CA_BUNDLE`, and `NODE_EXTRA_CA_CERTS` at the merged
|
|
69
|
+
system bundle. Certificate inputs are never placed in the shared canonical
|
|
70
|
+
image assets; they are deployment-specific and excluded from command output.
|
|
71
|
+
|
|
72
|
+
Gateway profiles require a host and port plus a read-only SSH key and pinned
|
|
73
|
+
known-hosts input. These can be supplied through the dedicated profile paths or
|
|
74
|
+
explicit mounts at `/etc/agent/gateway-key` and
|
|
75
|
+
`/etc/agent/gateway-known-hosts`; bootstrap rules are restricted to literal
|
|
76
|
+
IP addresses/CIDRs. Gateway user, Cloudflare Access hostname, and bootstrap
|
|
77
|
+
allow rules are emitted only when gateway mode is explicitly configured.
|
|
78
|
+
|
|
79
|
+
Langfuse support is experimental and opt-in for Claude Code, Codex, and
|
|
80
|
+
OpenCode. The CLI installs the vendor-supported integration for the selected
|
|
81
|
+
agent and emits only references to credential environment variables; it never
|
|
82
|
+
stores credential values. A Langfuse endpoint must be present in the profile's
|
|
83
|
+
egress allowlist (or be reached through a configured gateway). Langfuse is one
|
|
84
|
+
of many possible OpenTelemetry backends, not a default telemetry provider.
|
|
85
|
+
|
|
86
|
+
Unit and distribution tests must run without Docker. Integration tests require
|
|
87
|
+
Docker and disposable resources, without real account logins. Offline plans and
|
|
88
|
+
diagnostics must distinguish recorded state from inspected live state and mark
|
|
89
|
+
runtime checks unavailable when Docker cannot be reached.
|
|
90
|
+
|
|
91
|
+
Use direct Docker commands, not Compose, and package with `uv build` and the
|
|
92
|
+
native `uv_build` backend, not Hatchling, with Python 3.12 or newer. Use `ty`
|
|
93
|
+
for type checking and Ruff for linting/formatting. Both live in the `dev`
|
|
94
|
+
dependency group; pytest and coverage live in `test`.
|
|
95
|
+
|
|
96
|
+
From this directory, install contributor dependencies with
|
|
97
|
+
`uv sync --group test --python 3.12`. Root `just test-cli` runs unit and packaging
|
|
98
|
+
tests under coverage; `just check-cli` runs Ruff and ty. Coverage must reach 100%.
|
|
99
|
+
Do not suppress type or coverage errors without a demonstrated, documented need.
|
|
100
|
+
The packaging test builds a standalone source distribution and installs its
|
|
101
|
+
rebuilt wheel in a fresh temporary environment; dependency downloads may be needed.
|
|
102
|
+
|
|
103
|
+
The CLI prints its required ASCII banner before every invocation. Deployment
|
|
104
|
+
state defaults to `$XDG_STATE_HOME/agent-containers/` (or
|
|
105
|
+
`~/.local/state/agent-containers/`). On macOS Docker Desktop, `apply` keeps the
|
|
106
|
+
host UID but uses image GID `1000`: the conventional macOS GID 20 collides with
|
|
107
|
+
an existing Linux image group, and the project deliberately retains its
|
|
108
|
+
Dockerfile collision rejection rather than silently joining that group.
|
|
109
|
+
|
|
110
|
+
If the host mounts `/tmp` with `noexec`, use a dedicated executable temporary
|
|
111
|
+
directory for pytest's `--basetemp` option. Pytest owns and clears that directory:
|
|
112
|
+
never point it at an existing directory containing your files. No mount flags
|
|
113
|
+
need changing. Tests still install outside the repository and use a fresh venv.
|
|
114
|
+
|
|
115
|
+
The test suite covers CLI behavior, profile/state validation, generated build
|
|
116
|
+
contexts, Docker argv construction, lifecycle transitions, and standalone
|
|
117
|
+
sdist-to-wheel installation at 100% source coverage. It does not establish all
|
|
118
|
+
runtime containment or agent compatibility claims; broader Docker integration
|
|
119
|
+
tests remain to be implemented.
|
|
120
|
+
|
|
121
|
+
Profile-managed npm and uv tools are installed into image-owned locations
|
|
122
|
+
outside the persistent home during image creation only. Runtime root filesystems
|
|
123
|
+
stay read-only, with the existing explicit writable mounts preserved. Manual
|
|
124
|
+
home installations take PATH precedence; diagnostics must warn when they shadow
|
|
125
|
+
image-managed executables. Do not delete existing home tools.
|
|
126
|
+
|
|
127
|
+
The private implementation handoff is maintained in
|
|
128
|
+
`../tmp-onboarding-codex.log`; the original discussion is in
|
|
129
|
+
`../tmp-onboarding.md`. Both are intentionally ignored by Git.
|
|
130
|
+
|
|
131
|
+
## Current commands
|
|
132
|
+
|
|
133
|
+
`agent-containers create PROFILE.toml` interactively prompts for every profile
|
|
134
|
+
section, validates the resulting model, and writes a new TOML file atomically.
|
|
135
|
+
It refuses to replace an existing file and never contacts Docker. Secrets are
|
|
136
|
+
never prompted for; provider authentication is represented only by an
|
|
137
|
+
environment-variable name.
|
|
138
|
+
|
|
139
|
+
`agent-containers validate PROFILE.toml` parses and validates a profile without
|
|
140
|
+
contacting Docker or changing files.
|
|
141
|
+
|
|
142
|
+
`agent-containers plan PROFILE.toml` compares the desired profile with optional
|
|
143
|
+
recorded deployment JSON (`--state STATE.json`). It reports image versus launch
|
|
144
|
+
changes and always discloses that live Docker state was not inspected.
|
|
145
|
+
|
|
146
|
+
`agent-containers apply PROFILE.toml` materializes an isolated profile-owned
|
|
147
|
+
build context, builds or verifies its retained image, copies any new seed only
|
|
148
|
+
when its target is absent, and atomically selects deployment state. State is
|
|
149
|
+
written to the standard per-user location unless `--state STATE.json` overrides
|
|
150
|
+
it. `apply` does not launch an agent or terminate an existing session. After a
|
|
151
|
+
successful apply it also refreshes the CLI-managed
|
|
152
|
+
`$XDG_CONFIG_HOME/agent-containers/profiles.sh` (or
|
|
153
|
+
`~/.config/agent-containers/profiles.sh`) with a namespaced shortcut for the
|
|
154
|
+
profile. Source that file once from your shell startup file:
|
|
155
|
+
|
|
156
|
+
```sh
|
|
157
|
+
source "$HOME/.config/agent-containers/profiles.sh"
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
For example, a profile named `work-codex` becomes
|
|
161
|
+
`agent_containers_work_codex`. The generated function evaluates the current
|
|
162
|
+
directory when invoked, selects the profile's retained image and home volume,
|
|
163
|
+
and forwards arguments to the agent. It does not accept arbitrary Docker
|
|
164
|
+
options; use the documented generic shortcuts when an intentional one-off
|
|
165
|
+
override is needed.
|
|
166
|
+
|
|
167
|
+
By default, profile resources are namespaced with the host username and UID:
|
|
168
|
+
an image tag and home volume created for `work` by one user cannot collide with
|
|
169
|
+
the corresponding `work` resources created by another user on the same Docker
|
|
170
|
+
host. Set `home_volume = "an-existing-volume"` in the profile when an existing
|
|
171
|
+
named volume should be reused; that explicit name is an intentional sharing
|
|
172
|
+
choice and is never silently renamed.
|
|
173
|
+
|
|
174
|
+
Provider launch mappings are agent-specific: Claude Code uses its endpoint and
|
|
175
|
+
model environment variables, Codex uses per-run configuration overrides, and
|
|
176
|
+
Hermes uses per-run `--provider`/`--model` flags plus the documented base-URL
|
|
177
|
+
environment for supported `custom`, `openai`, and `anthropic` routes. OpenCode
|
|
178
|
+
endpoint/model fields are rendered into a secret-free JSON provider
|
|
179
|
+
configuration for that invocation. The shared entrypoint writes it under
|
|
180
|
+
`/tmp` and sets `OPENCODE_CONFIG`, so the profile does not modify the
|
|
181
|
+
persistent OpenCode home.
|
|
182
|
+
|
|
183
|
+
`agent-containers rollback PROFILE.toml` verifies and selects the immediately
|
|
184
|
+
previous retained image and launch record. It previews restored egress,
|
|
185
|
+
proxy/CA, and endpoint choices. It never restores persistent-home data or stops
|
|
186
|
+
an existing agent session, and refreshes that profile's generated shortcut.
|
|
187
|
+
|
|
188
|
+
`agent-containers doctor PROFILE.toml` is read-only. It reports Docker-daemon
|
|
189
|
+
availability, the expected deployment-state file, and whether the selected
|
|
190
|
+
image can be inspected; it creates, starts, and changes nothing.
|
|
191
|
+
|
|
192
|
+
Before building a distribution, run `uv run --project cli python cli/scripts/bundle_image_assets.py`;
|
|
193
|
+
`just build-cli` does this automatically.
|
|
194
|
+
The generated package-data directory is ignored, derived only from canonical
|
|
195
|
+
`agent-images/`, and included in the wheel and source distribution.
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# agent-containers CLI
|
|
2
|
+
|
|
3
|
+
The Python package is tested on Python 3.14 (with Python 3.12+ support). It
|
|
4
|
+
validates profiles, produces offline plans, materializes bundled image recipes,
|
|
5
|
+
and applies a direct-Docker deployment without launching an agent session.
|
|
6
|
+
|
|
7
|
+
This directory will contain the standalone Python project for deterministic
|
|
8
|
+
onboarding and maintenance of this repository's hardened agent images. The
|
|
9
|
+
distribution and executable name is `agent-containers`; the Python import
|
|
10
|
+
package will be `agent_containers`.
|
|
11
|
+
|
|
12
|
+
## Agreed scope
|
|
13
|
+
|
|
14
|
+
- Human-editable, versioned TOML profiles, with separate machine-managed
|
|
15
|
+
deployment state. Interactive prompts and noninteractive inputs use the same
|
|
16
|
+
validation model; unknown fields are errors.
|
|
17
|
+
- Claude Code, OpenCode, Codex, and Hermes adapters, targeting Linux Docker Engine
|
|
18
|
+
and macOS Docker Desktop. Support claims require actual integration tests.
|
|
19
|
+
- All four optional package categories: apt packages, npm packages, isolated uv
|
|
20
|
+
tools, and system Python libraries installed with uv.
|
|
21
|
+
- Explicit custom file/directory mounts and copy-once configuration seeds,
|
|
22
|
+
without duplicating each agent's settings schema. Relative input paths resolve
|
|
23
|
+
against the profile directory; the workspace defaults to the launch directory.
|
|
24
|
+
- Composable hosted/local/custom model endpoints, HTTP(S) proxies, CA trust,
|
|
25
|
+
egress gateways, and experimental observability integrations. Decant is
|
|
26
|
+
experimental and currently limited to Claude Code and Codex; it is disabled
|
|
27
|
+
unless a profile opts in. Opted-in Decant launches use user-scoped image,
|
|
28
|
+
container, and data-volume names and mount selected agent collection
|
|
29
|
+
subdirectories directly; the delegated volume bridge remains a separate
|
|
30
|
+
manual setup. Local inference engine installation and model
|
|
31
|
+
downloads are outside the agreed initial scope.
|
|
32
|
+
- Explicit application and updates, retained prior deployments, and rollback of
|
|
33
|
+
the selected image and managed launch configuration. Rollback does not restore
|
|
34
|
+
mutable home-volume data or terminate existing sessions automatically.
|
|
35
|
+
- Image recipes bundled from canonical `../agent-images/` sources in both wheel
|
|
36
|
+
and source distributions. No maintained duplicate or installed-package edits.
|
|
37
|
+
|
|
38
|
+
## Safety and verification
|
|
39
|
+
|
|
40
|
+
Preserve the repository's containment contract. Package selection must not
|
|
41
|
+
silently expand runtime egress. Credentials must not appear in profiles, build
|
|
42
|
+
arguments, images, logs, or command output; profiles reference runtime inputs.
|
|
43
|
+
|
|
44
|
+
When a profile declares a proxy, its generated Docker launch exports both
|
|
45
|
+
uppercase and lowercase `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` variables,
|
|
46
|
+
and enables Node's environment proxy support. A profile `proxy.ca_file` or
|
|
47
|
+
`proxy.ca_dir` is copied into the isolated image build context and installed
|
|
48
|
+
into the image's system trust store. The generated launch points
|
|
49
|
+
`SSL_CERT_FILE`, `REQUESTS_CA_BUNDLE`, and `NODE_EXTRA_CA_CERTS` at the merged
|
|
50
|
+
system bundle. Certificate inputs are never placed in the shared canonical
|
|
51
|
+
image assets; they are deployment-specific and excluded from command output.
|
|
52
|
+
|
|
53
|
+
Gateway profiles require a host and port plus a read-only SSH key and pinned
|
|
54
|
+
known-hosts input. These can be supplied through the dedicated profile paths or
|
|
55
|
+
explicit mounts at `/etc/agent/gateway-key` and
|
|
56
|
+
`/etc/agent/gateway-known-hosts`; bootstrap rules are restricted to literal
|
|
57
|
+
IP addresses/CIDRs. Gateway user, Cloudflare Access hostname, and bootstrap
|
|
58
|
+
allow rules are emitted only when gateway mode is explicitly configured.
|
|
59
|
+
|
|
60
|
+
Langfuse support is experimental and opt-in for Claude Code, Codex, and
|
|
61
|
+
OpenCode. The CLI installs the vendor-supported integration for the selected
|
|
62
|
+
agent and emits only references to credential environment variables; it never
|
|
63
|
+
stores credential values. A Langfuse endpoint must be present in the profile's
|
|
64
|
+
egress allowlist (or be reached through a configured gateway). Langfuse is one
|
|
65
|
+
of many possible OpenTelemetry backends, not a default telemetry provider.
|
|
66
|
+
|
|
67
|
+
Unit and distribution tests must run without Docker. Integration tests require
|
|
68
|
+
Docker and disposable resources, without real account logins. Offline plans and
|
|
69
|
+
diagnostics must distinguish recorded state from inspected live state and mark
|
|
70
|
+
runtime checks unavailable when Docker cannot be reached.
|
|
71
|
+
|
|
72
|
+
Use direct Docker commands, not Compose, and package with `uv build` and the
|
|
73
|
+
native `uv_build` backend, not Hatchling, with Python 3.12 or newer. Use `ty`
|
|
74
|
+
for type checking and Ruff for linting/formatting. Both live in the `dev`
|
|
75
|
+
dependency group; pytest and coverage live in `test`.
|
|
76
|
+
|
|
77
|
+
From this directory, install contributor dependencies with
|
|
78
|
+
`uv sync --group test --python 3.12`. Root `just test-cli` runs unit and packaging
|
|
79
|
+
tests under coverage; `just check-cli` runs Ruff and ty. Coverage must reach 100%.
|
|
80
|
+
Do not suppress type or coverage errors without a demonstrated, documented need.
|
|
81
|
+
The packaging test builds a standalone source distribution and installs its
|
|
82
|
+
rebuilt wheel in a fresh temporary environment; dependency downloads may be needed.
|
|
83
|
+
|
|
84
|
+
The CLI prints its required ASCII banner before every invocation. Deployment
|
|
85
|
+
state defaults to `$XDG_STATE_HOME/agent-containers/` (or
|
|
86
|
+
`~/.local/state/agent-containers/`). On macOS Docker Desktop, `apply` keeps the
|
|
87
|
+
host UID but uses image GID `1000`: the conventional macOS GID 20 collides with
|
|
88
|
+
an existing Linux image group, and the project deliberately retains its
|
|
89
|
+
Dockerfile collision rejection rather than silently joining that group.
|
|
90
|
+
|
|
91
|
+
If the host mounts `/tmp` with `noexec`, use a dedicated executable temporary
|
|
92
|
+
directory for pytest's `--basetemp` option. Pytest owns and clears that directory:
|
|
93
|
+
never point it at an existing directory containing your files. No mount flags
|
|
94
|
+
need changing. Tests still install outside the repository and use a fresh venv.
|
|
95
|
+
|
|
96
|
+
The test suite covers CLI behavior, profile/state validation, generated build
|
|
97
|
+
contexts, Docker argv construction, lifecycle transitions, and standalone
|
|
98
|
+
sdist-to-wheel installation at 100% source coverage. It does not establish all
|
|
99
|
+
runtime containment or agent compatibility claims; broader Docker integration
|
|
100
|
+
tests remain to be implemented.
|
|
101
|
+
|
|
102
|
+
Profile-managed npm and uv tools are installed into image-owned locations
|
|
103
|
+
outside the persistent home during image creation only. Runtime root filesystems
|
|
104
|
+
stay read-only, with the existing explicit writable mounts preserved. Manual
|
|
105
|
+
home installations take PATH precedence; diagnostics must warn when they shadow
|
|
106
|
+
image-managed executables. Do not delete existing home tools.
|
|
107
|
+
|
|
108
|
+
The private implementation handoff is maintained in
|
|
109
|
+
`../tmp-onboarding-codex.log`; the original discussion is in
|
|
110
|
+
`../tmp-onboarding.md`. Both are intentionally ignored by Git.
|
|
111
|
+
|
|
112
|
+
## Current commands
|
|
113
|
+
|
|
114
|
+
`agent-containers create PROFILE.toml` interactively prompts for every profile
|
|
115
|
+
section, validates the resulting model, and writes a new TOML file atomically.
|
|
116
|
+
It refuses to replace an existing file and never contacts Docker. Secrets are
|
|
117
|
+
never prompted for; provider authentication is represented only by an
|
|
118
|
+
environment-variable name.
|
|
119
|
+
|
|
120
|
+
`agent-containers validate PROFILE.toml` parses and validates a profile without
|
|
121
|
+
contacting Docker or changing files.
|
|
122
|
+
|
|
123
|
+
`agent-containers plan PROFILE.toml` compares the desired profile with optional
|
|
124
|
+
recorded deployment JSON (`--state STATE.json`). It reports image versus launch
|
|
125
|
+
changes and always discloses that live Docker state was not inspected.
|
|
126
|
+
|
|
127
|
+
`agent-containers apply PROFILE.toml` materializes an isolated profile-owned
|
|
128
|
+
build context, builds or verifies its retained image, copies any new seed only
|
|
129
|
+
when its target is absent, and atomically selects deployment state. State is
|
|
130
|
+
written to the standard per-user location unless `--state STATE.json` overrides
|
|
131
|
+
it. `apply` does not launch an agent or terminate an existing session. After a
|
|
132
|
+
successful apply it also refreshes the CLI-managed
|
|
133
|
+
`$XDG_CONFIG_HOME/agent-containers/profiles.sh` (or
|
|
134
|
+
`~/.config/agent-containers/profiles.sh`) with a namespaced shortcut for the
|
|
135
|
+
profile. Source that file once from your shell startup file:
|
|
136
|
+
|
|
137
|
+
```sh
|
|
138
|
+
source "$HOME/.config/agent-containers/profiles.sh"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
For example, a profile named `work-codex` becomes
|
|
142
|
+
`agent_containers_work_codex`. The generated function evaluates the current
|
|
143
|
+
directory when invoked, selects the profile's retained image and home volume,
|
|
144
|
+
and forwards arguments to the agent. It does not accept arbitrary Docker
|
|
145
|
+
options; use the documented generic shortcuts when an intentional one-off
|
|
146
|
+
override is needed.
|
|
147
|
+
|
|
148
|
+
By default, profile resources are namespaced with the host username and UID:
|
|
149
|
+
an image tag and home volume created for `work` by one user cannot collide with
|
|
150
|
+
the corresponding `work` resources created by another user on the same Docker
|
|
151
|
+
host. Set `home_volume = "an-existing-volume"` in the profile when an existing
|
|
152
|
+
named volume should be reused; that explicit name is an intentional sharing
|
|
153
|
+
choice and is never silently renamed.
|
|
154
|
+
|
|
155
|
+
Provider launch mappings are agent-specific: Claude Code uses its endpoint and
|
|
156
|
+
model environment variables, Codex uses per-run configuration overrides, and
|
|
157
|
+
Hermes uses per-run `--provider`/`--model` flags plus the documented base-URL
|
|
158
|
+
environment for supported `custom`, `openai`, and `anthropic` routes. OpenCode
|
|
159
|
+
endpoint/model fields are rendered into a secret-free JSON provider
|
|
160
|
+
configuration for that invocation. The shared entrypoint writes it under
|
|
161
|
+
`/tmp` and sets `OPENCODE_CONFIG`, so the profile does not modify the
|
|
162
|
+
persistent OpenCode home.
|
|
163
|
+
|
|
164
|
+
`agent-containers rollback PROFILE.toml` verifies and selects the immediately
|
|
165
|
+
previous retained image and launch record. It previews restored egress,
|
|
166
|
+
proxy/CA, and endpoint choices. It never restores persistent-home data or stops
|
|
167
|
+
an existing agent session, and refreshes that profile's generated shortcut.
|
|
168
|
+
|
|
169
|
+
`agent-containers doctor PROFILE.toml` is read-only. It reports Docker-daemon
|
|
170
|
+
availability, the expected deployment-state file, and whether the selected
|
|
171
|
+
image can be inspected; it creates, starts, and changes nothing.
|
|
172
|
+
|
|
173
|
+
Before building a distribution, run `uv run --project cli python cli/scripts/bundle_image_assets.py`;
|
|
174
|
+
`just build-cli` does this automatically.
|
|
175
|
+
The generated package-data directory is ignored, derived only from canonical
|
|
176
|
+
`agent-images/`, and included in the wheel and source distribution.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["uv_build>=0.12.10,<0.13"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "agent-containers"
|
|
7
|
+
version = "0.1.0.dev0"
|
|
8
|
+
description = "Deterministic onboarding and maintenance for hardened coding-agent containers"
|
|
9
|
+
license = "MIT"
|
|
10
|
+
license-files = ["LICEN[CS]E*"]
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
requires-python = ">=3.12"
|
|
13
|
+
dependencies = [
|
|
14
|
+
"pydantic>=2.11,<3",
|
|
15
|
+
"tomlkit>=0.13,<1",
|
|
16
|
+
"typer>=0.16,<1",
|
|
17
|
+
]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 2 - Pre-Alpha",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Repository = "https://github.com/anirbanbasu/agent-containers"
|
|
27
|
+
Issues = "https://github.com/anirbanbasu/agent-containers/issues"
|
|
28
|
+
Download = "https://pypi.org/project/agent-containers/"
|
|
29
|
+
|
|
30
|
+
[project.scripts]
|
|
31
|
+
agent-containers = "agent_containers.cli:app"
|
|
32
|
+
|
|
33
|
+
[dependency-groups]
|
|
34
|
+
dev = [
|
|
35
|
+
"ty>=0.0.1,<1",
|
|
36
|
+
"ruff>=0.11,<1",
|
|
37
|
+
]
|
|
38
|
+
test = [
|
|
39
|
+
"pytest>=8.4.1,<10",
|
|
40
|
+
"coverage>=7.9.1,<8",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[tool.pytest.ini_options]
|
|
44
|
+
testpaths = ["tests"]
|
|
45
|
+
addopts = [
|
|
46
|
+
"--import-mode=importlib",
|
|
47
|
+
"--strict-markers",
|
|
48
|
+
]
|
|
49
|
+
markers = [
|
|
50
|
+
"packaging: builds and installs distributions without Docker",
|
|
51
|
+
"integration: requires an explicitly enabled disposable Docker environment",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
[tool.ruff]
|
|
55
|
+
target-version = "py312"
|
|
56
|
+
line-length = 120
|
|
57
|
+
include = [
|
|
58
|
+
"src/agent_containers/**/*.py",
|
|
59
|
+
"tests/**/*.py",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
[tool.ruff.lint]
|
|
63
|
+
extend-select = [
|
|
64
|
+
"Q",
|
|
65
|
+
"RUF100",
|
|
66
|
+
"RUF018",
|
|
67
|
+
"C90",
|
|
68
|
+
"UP",
|
|
69
|
+
"I",
|
|
70
|
+
"D",
|
|
71
|
+
"TID251",
|
|
72
|
+
]
|
|
73
|
+
ignore = [
|
|
74
|
+
"D100",
|
|
75
|
+
"D102",
|
|
76
|
+
"D104",
|
|
77
|
+
"D105",
|
|
78
|
+
"D107",
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
[tool.ruff.lint.mccabe]
|
|
82
|
+
max-complexity = 15
|
|
83
|
+
|
|
84
|
+
[tool.ruff.lint.isort]
|
|
85
|
+
combine-as-imports = true
|
|
86
|
+
known-first-party = ["agent_containers"]
|
|
87
|
+
|
|
88
|
+
[tool.ruff.lint.pydocstyle]
|
|
89
|
+
convention = "google"
|
|
90
|
+
|
|
91
|
+
[tool.ruff.format]
|
|
92
|
+
docstring-code-format = false
|
|
93
|
+
|
|
94
|
+
[tool.ty.environment]
|
|
95
|
+
python-version = "3.12"
|
|
96
|
+
|
|
97
|
+
[tool.ty.rules]
|
|
98
|
+
possibly-unresolved-reference = "warn"
|
|
99
|
+
|
|
100
|
+
[tool.coverage.run]
|
|
101
|
+
source = ["agent_containers"]
|
|
102
|
+
omit = ["tests/*"]
|
|
103
|
+
|
|
104
|
+
[tool.coverage.report]
|
|
105
|
+
fail_under = 100
|
|
106
|
+
skip_covered = true
|
|
107
|
+
show_missing = true
|
|
108
|
+
precision = 2
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["uv_build>=0.12.10,<0.13"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "agent-containers"
|
|
7
|
+
version = "0.1.0.dev0"
|
|
8
|
+
description = "Deterministic onboarding and maintenance for hardened coding-agent containers"
|
|
9
|
+
license = "MIT"
|
|
10
|
+
license-files = ["LICEN[CS]E*"]
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
requires-python = ">=3.12"
|
|
13
|
+
dependencies = [
|
|
14
|
+
"pydantic>=2.11,<3",
|
|
15
|
+
"tomlkit>=0.13,<1",
|
|
16
|
+
"typer>=0.16,<1",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 2 - Pre-Alpha",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Repository = "https://github.com/anirbanbasu/agent-containers"
|
|
28
|
+
Issues = "https://github.com/anirbanbasu/agent-containers/issues"
|
|
29
|
+
Download = "https://pypi.org/project/agent-containers/"
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
agent-containers = "agent_containers.cli:app"
|
|
33
|
+
|
|
34
|
+
[dependency-groups]
|
|
35
|
+
dev = [
|
|
36
|
+
"ty>=0.0.1,<1",
|
|
37
|
+
"ruff>=0.11,<1",
|
|
38
|
+
]
|
|
39
|
+
test = [
|
|
40
|
+
"pytest>=8.4.1,<10",
|
|
41
|
+
"coverage>=7.9.1,<8",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[tool.pytest.ini_options]
|
|
45
|
+
testpaths = ["tests"]
|
|
46
|
+
addopts = ["--import-mode=importlib", "--strict-markers"]
|
|
47
|
+
markers = [
|
|
48
|
+
"packaging: builds and installs distributions without Docker",
|
|
49
|
+
"integration: requires an explicitly enabled disposable Docker environment",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
[tool.ruff]
|
|
53
|
+
target-version = "py312"
|
|
54
|
+
line-length = 120
|
|
55
|
+
include = ["src/agent_containers/**/*.py", "tests/**/*.py"]
|
|
56
|
+
|
|
57
|
+
[tool.ruff.lint]
|
|
58
|
+
extend-select = ["Q", "RUF100", "RUF018", "C90", "UP", "I", "D", "TID251"]
|
|
59
|
+
mccabe = { max-complexity = 15 }
|
|
60
|
+
ignore = ["D100", "D102", "D104", "D105", "D107"]
|
|
61
|
+
|
|
62
|
+
[tool.ruff.lint.isort]
|
|
63
|
+
combine-as-imports = true
|
|
64
|
+
known-first-party = ["agent_containers"]
|
|
65
|
+
|
|
66
|
+
[tool.ruff.lint.pydocstyle]
|
|
67
|
+
convention = "google"
|
|
68
|
+
|
|
69
|
+
[tool.ruff.format]
|
|
70
|
+
docstring-code-format = false
|
|
71
|
+
|
|
72
|
+
[tool.ty.environment]
|
|
73
|
+
python-version = "3.12"
|
|
74
|
+
|
|
75
|
+
[tool.ty.rules]
|
|
76
|
+
possibly-unresolved-reference = "warn"
|
|
77
|
+
|
|
78
|
+
[tool.coverage.run]
|
|
79
|
+
source = ["agent_containers"]
|
|
80
|
+
omit = ["tests/*"]
|
|
81
|
+
|
|
82
|
+
[tool.coverage.report]
|
|
83
|
+
fail_under = 100
|
|
84
|
+
skip_covered = true
|
|
85
|
+
show_missing = true
|
|
86
|
+
precision = 2
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Deterministic onboarding and maintenance for hardened agent containers."""
|