agent-containers 0.1.0.dev0__py3-none-any.whl

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 (86) hide show
  1. agent_containers/__init__.py +1 -0
  2. agent_containers/__main__.py +6 -0
  3. agent_containers/_assets/agent-images/adal/Dockerfile +103 -0
  4. agent_containers/_assets/agent-images/adal/examples/egress-allowlist.txt +10 -0
  5. agent_containers/_assets/agent-images/adal/packages-apt.txt +13 -0
  6. agent_containers/_assets/agent-images/adal/packages-npm.txt +2 -0
  7. agent_containers/_assets/agent-images/adal/packages-uv.txt +6 -0
  8. agent_containers/_assets/agent-images/adal/tools-uv.txt +4 -0
  9. agent_containers/_assets/agent-images/agent-gateway/Dockerfile +50 -0
  10. agent_containers/_assets/agent-images/agent-gateway/entrypoint.sh +28 -0
  11. agent_containers/_assets/agent-images/agent-gateway/sshd_config.agent-gateway +11 -0
  12. agent_containers/_assets/agent-images/aider/Dockerfile +114 -0
  13. agent_containers/_assets/agent-images/aider/examples/egress-allowlist.txt +16 -0
  14. agent_containers/_assets/agent-images/aider/packages-apt.txt +16 -0
  15. agent_containers/_assets/agent-images/aider/packages-npm.txt +7 -0
  16. agent_containers/_assets/agent-images/aider/packages-uv.txt +9 -0
  17. agent_containers/_assets/agent-images/aider/tools-uv.txt +9 -0
  18. agent_containers/_assets/agent-images/claude-code/Dockerfile +171 -0
  19. agent_containers/_assets/agent-images/claude-code/entrypoint.sh +22 -0
  20. agent_containers/_assets/agent-images/claude-code/examples/egress-allowlist.txt +14 -0
  21. agent_containers/_assets/agent-images/claude-code/examples/settings.local-model.json +7 -0
  22. agent_containers/_assets/agent-images/claude-code/packages-apt.txt +16 -0
  23. agent_containers/_assets/agent-images/claude-code/packages-npm.txt +7 -0
  24. agent_containers/_assets/agent-images/claude-code/packages-uv.txt +9 -0
  25. agent_containers/_assets/agent-images/claude-code/plugin-marketplaces.txt +23 -0
  26. agent_containers/_assets/agent-images/claude-code/plugins.txt +13 -0
  27. agent_containers/_assets/agent-images/claude-code/proxy-ca.d/.keep +1 -0
  28. agent_containers/_assets/agent-images/claude-code/tools-uv.txt +9 -0
  29. agent_containers/_assets/agent-images/codex/Dockerfile +147 -0
  30. agent_containers/_assets/agent-images/codex/codex-wrapper.sh +13 -0
  31. agent_containers/_assets/agent-images/codex/examples/egress-allowlist.txt +10 -0
  32. agent_containers/_assets/agent-images/codex/packages-apt.txt +13 -0
  33. agent_containers/_assets/agent-images/codex/packages-npm.txt +2 -0
  34. agent_containers/_assets/agent-images/codex/packages-uv.txt +6 -0
  35. agent_containers/_assets/agent-images/codex/plugin-marketplaces.txt +2 -0
  36. agent_containers/_assets/agent-images/codex/plugins.txt +2 -0
  37. agent_containers/_assets/agent-images/codex/proxy-ca.d/.keep +1 -0
  38. agent_containers/_assets/agent-images/codex/tools-uv.txt +4 -0
  39. agent_containers/_assets/agent-images/hermes/Dockerfile +122 -0
  40. agent_containers/_assets/agent-images/hermes/entrypoint.sh +49 -0
  41. agent_containers/_assets/agent-images/hermes/packages-apt.txt +16 -0
  42. agent_containers/_assets/agent-images/hermes/packages-npm.txt +7 -0
  43. agent_containers/_assets/agent-images/hermes/packages-uv.txt +9 -0
  44. agent_containers/_assets/agent-images/hermes/proxy-ca.d/.keep +1 -0
  45. agent_containers/_assets/agent-images/hermes/tools-uv.txt +9 -0
  46. agent_containers/_assets/agent-images/kilo-code/Dockerfile +101 -0
  47. agent_containers/_assets/agent-images/kilo-code/examples/egress-allowlist.txt +7 -0
  48. agent_containers/_assets/agent-images/kilo-code/packages-apt.txt +5 -0
  49. agent_containers/_assets/agent-images/kilo-code/packages-npm.txt +1 -0
  50. agent_containers/_assets/agent-images/kilo-code/packages-uv.txt +4 -0
  51. agent_containers/_assets/agent-images/kilo-code/tools-uv.txt +4 -0
  52. agent_containers/_assets/agent-images/opencode/Dockerfile +126 -0
  53. agent_containers/_assets/agent-images/opencode/examples/egress-allowlist.txt +6 -0
  54. agent_containers/_assets/agent-images/opencode/packages-apt.txt +13 -0
  55. agent_containers/_assets/agent-images/opencode/packages-npm.txt +2 -0
  56. agent_containers/_assets/agent-images/opencode/packages-uv.txt +6 -0
  57. agent_containers/_assets/agent-images/opencode/proxy-ca.d/.keep +1 -0
  58. agent_containers/_assets/agent-images/opencode/tools-uv.txt +4 -0
  59. agent_containers/_assets/agent-images/qwen-code/Dockerfile +104 -0
  60. agent_containers/_assets/agent-images/qwen-code/examples/egress-allowlist.txt +10 -0
  61. agent_containers/_assets/agent-images/qwen-code/packages-apt.txt +13 -0
  62. agent_containers/_assets/agent-images/qwen-code/packages-npm.txt +2 -0
  63. agent_containers/_assets/agent-images/qwen-code/packages-uv.txt +6 -0
  64. agent_containers/_assets/agent-images/qwen-code/tools-uv.txt +4 -0
  65. agent_containers/_assets/agent-images/shared/egress-allowlist.sh +138 -0
  66. agent_containers/_assets/agent-images/shared/install-additional-packages.sh +79 -0
  67. agent_containers/_assets/agent-images/shared/install-workload-base.sh +46 -0
  68. agent_containers/_assets/agent-images/shared/workload-entrypoint.sh +82 -0
  69. agent_containers/_assets/agent-images/volume-bridge/Dockerfile +37 -0
  70. agent_containers/_assets/agent-images/volume-bridge/entrypoint.sh +29 -0
  71. agent_containers/_assets/agent-images/volume-bridge/serve.sh +66 -0
  72. agent_containers/assets.py +38 -0
  73. agent_containers/build_context.py +116 -0
  74. agent_containers/cli.py +212 -0
  75. agent_containers/creation.py +177 -0
  76. agent_containers/docker.py +562 -0
  77. agent_containers/lifecycle.py +325 -0
  78. agent_containers/planner.py +99 -0
  79. agent_containers/profile.py +377 -0
  80. agent_containers/shortcuts.py +159 -0
  81. agent_containers/state.py +154 -0
  82. agent_containers-0.1.0.dev0.dist-info/METADATA +195 -0
  83. agent_containers-0.1.0.dev0.dist-info/RECORD +86 -0
  84. agent_containers-0.1.0.dev0.dist-info/WHEEL +4 -0
  85. agent_containers-0.1.0.dev0.dist-info/entry_points.txt +3 -0
  86. agent_containers-0.1.0.dev0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,177 @@
1
+ """Interactive creation of validated TOML profiles."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import tempfile
7
+ from pathlib import Path
8
+
9
+ import tomlkit
10
+ import typer
11
+
12
+ from agent_containers.profile import (
13
+ AgentName,
14
+ DecantConfig,
15
+ EgressConfig,
16
+ LangfuseConfig,
17
+ MountConfig,
18
+ MountType,
19
+ PackageSet,
20
+ Profile,
21
+ ProviderConfig,
22
+ ProxyConfig,
23
+ )
24
+
25
+
26
+ class ProfileCreationError(ValueError):
27
+ """Raised when an interactive profile cannot be written safely."""
28
+
29
+
30
+ def prompt_profile(path: Path) -> Profile:
31
+ """Prompt for every profile section and return the validated model."""
32
+ name = typer.prompt("Profile name", default=path.stem)
33
+ agent = typer.prompt("Agent", default=AgentName.CODEX.value)
34
+ home_volume = _optional_prompt("Home Docker volume name (blank uses a user-scoped default)")
35
+ packages = PackageSet(
36
+ apt=_csv_prompt("APT packages (comma-separated)"),
37
+ npm=_csv_prompt("NPM packages (comma-separated)"),
38
+ uv_tools=_csv_prompt("uv tools (comma-separated)"),
39
+ uv_libraries=_csv_prompt("uv libraries (comma-separated)"),
40
+ )
41
+ provider = _prompt_provider()
42
+ proxy = _prompt_proxy()
43
+ egress = _prompt_egress()
44
+ decant = _prompt_decant()
45
+ langfuse = _prompt_langfuse()
46
+ mounts = _prompt_mounts()
47
+ return Profile(
48
+ name=name,
49
+ agent=agent,
50
+ home_volume=home_volume,
51
+ packages=packages,
52
+ provider=provider,
53
+ proxy=proxy,
54
+ egress=egress,
55
+ decant=decant,
56
+ langfuse=langfuse,
57
+ mounts=mounts,
58
+ )
59
+
60
+
61
+ def write_profile(path: Path, profile: Profile) -> None:
62
+ """Write a new profile atomically and refuse to replace an existing file."""
63
+ path = path.expanduser().resolve()
64
+ if path.exists():
65
+ raise ProfileCreationError(f"profile already exists: {path}")
66
+ path.parent.mkdir(parents=True, exist_ok=True)
67
+ document = tomlkit.dumps(profile.model_dump(mode="json", exclude_none=True))
68
+ descriptor, temporary = tempfile.mkstemp(prefix=f".{path.name}.", dir=path.parent, text=True)
69
+ temporary_path = Path(temporary)
70
+ try:
71
+ with os.fdopen(descriptor, "w", encoding="utf-8") as profile_file:
72
+ profile_file.write(document)
73
+ profile_file.flush()
74
+ os.fsync(profile_file.fileno())
75
+ temporary_path.chmod(0o600)
76
+ temporary_path.replace(path)
77
+ finally:
78
+ temporary_path.unlink(missing_ok=True)
79
+
80
+
81
+ def _prompt_provider() -> ProviderConfig | None:
82
+ if not typer.confirm("Configure a model provider?", default=False):
83
+ return None
84
+ return ProviderConfig(
85
+ kind=typer.prompt("Provider kind"),
86
+ endpoint=_optional_prompt("Provider endpoint"),
87
+ model=_optional_prompt("Provider model"),
88
+ api_key_env=_optional_prompt("API-key environment variable"),
89
+ )
90
+
91
+
92
+ def _prompt_proxy() -> ProxyConfig | None:
93
+ if not typer.confirm("Configure an HTTP(S) proxy?", default=False):
94
+ return None
95
+ return ProxyConfig(
96
+ http=_optional_prompt("HTTP proxy URL"),
97
+ https=_optional_prompt("HTTPS proxy URL"),
98
+ no_proxy=_csv_prompt("Proxy bypass hosts (comma-separated)"),
99
+ ca_file=_optional_prompt("CA file path"),
100
+ ca_dir=_optional_prompt("CA directory path"),
101
+ )
102
+
103
+
104
+ def _prompt_egress() -> EgressConfig:
105
+ mode = typer.prompt("Egress mode (deny/allowlist/unrestricted)", default="allowlist")
106
+ hosts = _csv_prompt("Egress hosts (comma-separated)") if mode.strip().lower() == "allowlist" else []
107
+ gateway_host = _optional_prompt("Gateway host")
108
+ gateway_port = typer.prompt("Gateway port", type=int) if gateway_host is not None else None
109
+ gateway_user = _optional_prompt("Gateway SSH user") if gateway_host is not None else None
110
+ gateway_access_hostname = _optional_prompt("Gateway Access hostname") if gateway_host is not None else None
111
+ gateway_bootstrap_allow = _csv_prompt("Gateway bootstrap IPs/CIDRs (comma-separated)") if gateway_host else []
112
+ gateway_key_file = _optional_prompt("Gateway SSH key path") if gateway_host is not None else None
113
+ gateway_known_hosts_file = _optional_prompt("Gateway known-hosts path") if gateway_host is not None else None
114
+ return EgressConfig(
115
+ mode=mode,
116
+ hosts=hosts,
117
+ gateway_host=gateway_host,
118
+ gateway_port=gateway_port,
119
+ gateway_user=gateway_user,
120
+ gateway_access_hostname=gateway_access_hostname,
121
+ gateway_bootstrap_allow=gateway_bootstrap_allow,
122
+ gateway_key_file=gateway_key_file,
123
+ gateway_known_hosts_file=gateway_known_hosts_file,
124
+ )
125
+
126
+
127
+ def _prompt_decant() -> DecantConfig:
128
+ enabled = typer.confirm("Enable Decant?", default=False)
129
+ if not enabled:
130
+ return DecantConfig()
131
+ return DecantConfig(
132
+ enabled=True,
133
+ source_profiles=_csv_prompt("Decant source profiles (comma-separated)"),
134
+ image=_optional_prompt("Decant account-matched image override (blank uses a user-scoped default)"),
135
+ data_volume=_optional_prompt("Decant data Docker volume (blank uses a user-scoped default)"),
136
+ bind_address=typer.prompt("Decant bind address", default="127.0.0.1"),
137
+ port=typer.prompt("Decant port", default=8787, type=int),
138
+ )
139
+
140
+
141
+ def _prompt_langfuse() -> LangfuseConfig:
142
+ if not typer.confirm("Enable experimental Langfuse observability?", default=False):
143
+ return LangfuseConfig()
144
+ return LangfuseConfig(
145
+ enabled=True,
146
+ base_url=typer.prompt("Langfuse base URL"),
147
+ public_key_env=typer.prompt("Langfuse public-key environment variable", default="LANGFUSE_PUBLIC_KEY"),
148
+ secret_key_env=typer.prompt("Langfuse secret-key environment variable", default="LANGFUSE_SECRET_KEY"),
149
+ environment=_optional_prompt("Langfuse environment label"),
150
+ user_id=_optional_prompt("Langfuse user ID"),
151
+ )
152
+
153
+
154
+ def _prompt_mounts() -> list[MountConfig]:
155
+ count = typer.prompt("Number of custom mounts", default=0, type=int)
156
+ mounts: list[MountConfig] = []
157
+ for index in range(count):
158
+ typer.echo(f"Mount {index + 1} of {count}")
159
+ mounts.append(
160
+ MountConfig(
161
+ type=typer.prompt("Mount type (bind/directory/seed)", default=MountType.BIND.value),
162
+ source=typer.prompt("Mount source"),
163
+ target=typer.prompt("Mount target"),
164
+ read_only=typer.confirm("Mount read-only?", default=True),
165
+ )
166
+ )
167
+ return mounts
168
+
169
+
170
+ def _csv_prompt(label: str) -> list[str]:
171
+ value = typer.prompt(label, default="")
172
+ return [item.strip() for item in value.split(",") if item.strip()]
173
+
174
+
175
+ def _optional_prompt(label: str) -> str | None:
176
+ value = typer.prompt(label, default="")
177
+ return value.strip() or None