terok-executor 0.2.0__tar.gz → 0.3.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 (122) hide show
  1. {terok_executor-0.2.0 → terok_executor-0.3.0}/PKG-INFO +28 -22
  2. {terok_executor-0.2.0 → terok_executor-0.3.0}/README.md +18 -12
  3. {terok_executor-0.2.0 → terok_executor-0.3.0}/pyproject.toml +18 -16
  4. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/__init__.py +1 -1
  5. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/cli.py +8 -0
  6. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/commands.py +182 -20
  7. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/container/build.py +117 -37
  8. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/container/env.py +21 -19
  9. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/container/runner.py +140 -55
  10. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/credentials/auth.py +27 -2
  11. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/credentials/extractors.py +1 -0
  12. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/integrations/sandbox.py +4 -0
  13. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/paths.py +34 -0
  14. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/agents/claude.yaml +1 -1
  15. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/agents/codex.yaml +1 -1
  16. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/agents/copilot.yaml +1 -1
  17. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/agents/glab.yaml +6 -0
  18. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/agents/opencode.yaml +1 -1
  19. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/agents/pi.yaml +1 -1
  20. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/agents/vibe.yaml +1 -1
  21. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/hilfe +37 -17
  22. terok_executor-0.3.0/src/terok_executor/resources/scripts/terok-agents.py +392 -0
  23. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/templates/l1.agent-cli.Dockerfile.template +1 -0
  24. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/roster/loader.py +2 -0
  25. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/roster/schema.py +9 -0
  26. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/roster/types.py +12 -0
  27. terok_executor-0.2.0/src/terok_executor/container/sidecar.py +0 -166
  28. terok_executor-0.2.0/src/terok_executor/resources/scripts/terok-agents.py +0 -227
  29. {terok_executor-0.2.0 → terok_executor-0.3.0}/LICENSE +0 -0
  30. {terok_executor-0.2.0 → terok_executor-0.3.0}/LICENSES/Apache-2.0.txt +0 -0
  31. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/_tree.py +0 -0
  32. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/_util/__init__.py +0 -0
  33. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/_util/_timezone.py +0 -0
  34. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/acp/__init__.py +0 -0
  35. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/acp/cache.py +0 -0
  36. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/acp/daemon.py +0 -0
  37. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/acp/endpoint.py +0 -0
  38. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/acp/model_options.py +0 -0
  39. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/acp/probe.py +0 -0
  40. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/acp/proxy.py +0 -0
  41. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/acp/roster.py +0 -0
  42. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/config.py +0 -0
  43. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/config_schema.py +0 -0
  44. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/container/__init__.py +0 -0
  45. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/container/cache.py +0 -0
  46. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/container/inject.py +0 -0
  47. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/credentials/__init__.py +0 -0
  48. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/credentials/vault_commands.py +0 -0
  49. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/credentials/vault_config.py +0 -0
  50. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/credentials/vendor_files.py +0 -0
  51. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/doctor.py +0 -0
  52. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/integrations/__init__.py +0 -0
  53. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/krun.py +0 -0
  54. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/preflight.py +0 -0
  55. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/provider/__init__.py +0 -0
  56. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/provider/agents.py +0 -0
  57. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/provider/instructions.py +0 -0
  58. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/provider/providers.py +0 -0
  59. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/provider/wrappers.py +0 -0
  60. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/py.typed +0 -0
  61. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/__init__.py +0 -0
  62. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/agents/__init__.py +0 -0
  63. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/agents/caddy.yaml +0 -0
  64. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/agents/coderabbit.yaml +0 -0
  65. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/agents/gh.yaml +0 -0
  66. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/agents/sonar.yaml +0 -0
  67. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/agents/toad.yaml +0 -0
  68. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/instructions/__init__.py +0 -0
  69. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/instructions/default.md +0 -0
  70. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/providers/anthropic.yaml +0 -0
  71. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/providers/blablador.yaml +0 -0
  72. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/providers/coderabbit.yaml +0 -0
  73. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/providers/github.yaml +0 -0
  74. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/providers/gitlab.yaml +0 -0
  75. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/providers/kisski.yaml +0 -0
  76. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/providers/mistral.yaml +0 -0
  77. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/providers/openai.yaml +0 -0
  78. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/providers/openrouter.yaml +0 -0
  79. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/providers/sonarcloud.yaml +0 -0
  80. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/Caddyfile +0 -0
  81. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/__init__.py +0 -0
  82. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/allthethings.sh +0 -0
  83. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/init-ssh-and-repo.sh +0 -0
  84. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/mistral-model-sync.py +0 -0
  85. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/opencode-provider +0 -0
  86. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/opencode-provider-acp +0 -0
  87. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/opencode-session-plugin.mjs +0 -0
  88. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/opencode-toad +0 -0
  89. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/pi-env.sh +0 -0
  90. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/pi-git-identity.ts +0 -0
  91. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/pi-provider +0 -0
  92. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/pi-vault-routes.ts +0 -0
  93. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/providers +0 -0
  94. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/setup-codex-auth.sh +0 -0
  95. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/terok-acp-env.sh +0 -0
  96. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/terok-bash-banner.sh +0 -0
  97. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/terok-claude-acp +0 -0
  98. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/terok-codex-acp +0 -0
  99. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/terok-copilot-acp +0 -0
  100. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/terok-env-git-identity.sh +0 -0
  101. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/terok-env.sh +0 -0
  102. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/terok-native-provider +0 -0
  103. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/terok-opencode-acp +0 -0
  104. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/terok-toad-entry +0 -0
  105. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/terok-trust-workspace.py +0 -0
  106. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/terok-vibe-acp +0 -0
  107. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/toad +0 -0
  108. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/update-all-the-things +0 -0
  109. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/scripts/vibe-model-sync.sh +0 -0
  110. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/templates/__init__.py +0 -0
  111. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/templates/agent-wrappers.sh.j2 +0 -0
  112. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/templates/l0.dev.Dockerfile.template +0 -0
  113. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/templates/l1.sidecar.Dockerfile.template +0 -0
  114. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/tmux/__init__.py +0 -0
  115. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/tmux/container-tmux.conf +0 -0
  116. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/toad-agents/__init__.py +0 -0
  117. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/toad-agents/blablador.helmholtz.de.toml +0 -0
  118. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/resources/toad-agents/kisski.academiccloud.de.toml +0 -0
  119. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/roster/__init__.py +0 -0
  120. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/sandbox.py +0 -0
  121. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/storage.py +0 -0
  122. {terok_executor-0.2.0 → terok_executor-0.3.0}/src/terok_executor/vault_addr.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: terok-executor
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Single-agent task runner for hardened Podman containers
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE
@@ -23,15 +23,15 @@ Classifier: Programming Language :: Python :: 3.13
23
23
  Classifier: Programming Language :: Python :: 3.14
24
24
  Classifier: Topic :: Software Development :: Quality Assurance
25
25
  Classifier: Typing :: Typed
26
- Requires-Dist: Jinja2 (>=3.1)
27
- Requires-Dist: agent-client-protocol (>=0.10.1)
28
- Requires-Dist: prompt-toolkit (>=3.0)
29
- Requires-Dist: pydantic (>=2.9)
30
- Requires-Dist: rich (>=13.0)
31
- Requires-Dist: ruamel.yaml (>=0.18)
32
- Requires-Dist: terok-sandbox (>=0.2.0,<0.3.0)
33
- Requires-Dist: terok-util (>=0.2.0,<0.3.0)
34
- Requires-Dist: tomli-w (>=1.0)
26
+ Requires-Dist: Jinja2 (>=3.1,<4.0)
27
+ Requires-Dist: agent-client-protocol (==0.10.1)
28
+ Requires-Dist: prompt-toolkit (>=3.0,<4.0)
29
+ Requires-Dist: pydantic (>=2.13,<3.0)
30
+ Requires-Dist: rich (>=15.0,<16.0)
31
+ Requires-Dist: ruamel.yaml (==0.19.1)
32
+ Requires-Dist: terok-sandbox (>=0.4.0,<0.5.0)
33
+ Requires-Dist: terok-util (>=0.2.1,<0.3.0)
34
+ Requires-Dist: tomli-w (>=1.2,<2.0)
35
35
  Project-URL: Changelog, https://github.com/terok-ai/terok-executor/blob/master/CHANGELOG.md
36
36
  Project-URL: Documentation, https://terok-ai.github.io/terok-executor/
37
37
  Project-URL: Homepage, https://terok-ai.github.io/terok-executor/
@@ -70,7 +70,7 @@ Python when you want library-grade control.
70
70
 
71
71
  ```bash
72
72
  pip install terok-executor
73
- terok-executor run claude ~/my-workspace
73
+ terok-executor run claude ~/my-workspace -p "Fix the bug"
74
74
  ```
75
75
 
76
76
  The first `run` interactively offers any missing prerequisites — sandbox
@@ -112,16 +112,18 @@ hardening guarantees.
112
112
  | Claude Code | OAuth*, API key | Anthropic Claude Code |
113
113
  | Codex | OAuth*, API key | OpenAI Codex CLI |
114
114
  | Vibe | API key | Mistral Vibe |
115
- | OpenCode | API key | Generic LLM endpoint driver bundled defaults for Helmholtz Blablador, KISSKI AcademicCloud, and your own endpoint |
115
+ | Copilot | | GitHub Copilot (no vault route yet) |
116
+ | OpenCode | — (uses provider keys) | Harness that drives any OpenAI-compatible provider — curated configs for Helmholtz Blablador, KISSKI AcademicCloud, and OpenRouter (each authenticated with its own API key) |
117
+ | Pi | — (uses provider keys) | Multi-provider harness; routes through the phantom tokens of co-installed providers |
116
118
  | gh | OAuth, API key | GitHub CLI |
117
119
  | glab | API key | GitLab CLI |
120
+ | SonarCloud | API key | SonarCloud scanner |
118
121
  | CodeRabbit | API key | CodeRabbit (sidecar tool) |
119
- | SonarCloud | API key | SonarCloud scanner (sidecar tool) |
120
122
 
121
- \* Claude and Codex OAuth are experimental, and support must be explicitly allowed in the config file.
123
+ \* Claude and Codex OAuth are experimental.
122
124
 
123
- `terok-executor agents` lists the live roster (add `--all` to
124
- include the tool entries).
125
+ `terok-executor agents list` lists the live roster (add `--all` to
126
+ include tools and harness-driven providers).
125
127
 
126
128
  ## Where it sits in the stack
127
129
 
@@ -131,7 +133,8 @@ runs across many projects. Below it, terok-executor delegates the
131
133
  host-side security boundary
132
134
  ([terok-sandbox](https://github.com/terok-ai/terok-sandbox)): the
133
135
  credential vault, the git gate, the egress firewall hooks, the
134
- systemd service lifecycle.
136
+ per-container supervisor lifecycle (OCI `createRuntime`/`poststop`
137
+ hooks).
135
138
 
136
139
  ## Commands
137
140
 
@@ -141,13 +144,16 @@ systemd service lifecycle.
141
144
  | `setup` | Bootstrap sandbox services + container images |
142
145
  | `uninstall` | Remove sandbox services + container images |
143
146
  | `auth` | Authenticate a provider |
144
- | `agents` | List the agent roster |
147
+ | `agents` | Inspect the agent roster (`list`) and set the build-time default selection (`set`) |
145
148
  | `build` | Build base + agent images explicitly |
146
- | `run-tool` | Run a sidecar tool (CodeRabbit, SonarCloud) |
147
- | `list` | List running containers |
148
- | `stop` | Stop a running container |
149
+ | `run-tool` | Run a sidecar tool (CodeRabbit) |
150
+ | `list` | List containers |
151
+ | `start` | Start a stopped container |
152
+ | `stop` | Stop a container (kept for a later `start`) |
153
+ | `rm` | Remove a container and its host-side state |
149
154
  | `show-config` | Print the effective `SandboxConfig` as YAML (diffable across orchestrators) |
150
- | `vault` | Vault management (start, stop, status, install, routes) |
155
+ | `vault` | Vault management (status, unlock, lock, list, passphrase, routes, clean) |
156
+ | `sandbox` | Full terok-sandbox command tree (shield, vault, ssh, doctor, …) |
151
157
 
152
158
  ### Config override
153
159
 
@@ -28,7 +28,7 @@ Python when you want library-grade control.
28
28
 
29
29
  ```bash
30
30
  pip install terok-executor
31
- terok-executor run claude ~/my-workspace
31
+ terok-executor run claude ~/my-workspace -p "Fix the bug"
32
32
  ```
33
33
 
34
34
  The first `run` interactively offers any missing prerequisites — sandbox
@@ -70,16 +70,18 @@ hardening guarantees.
70
70
  | Claude Code | OAuth*, API key | Anthropic Claude Code |
71
71
  | Codex | OAuth*, API key | OpenAI Codex CLI |
72
72
  | Vibe | API key | Mistral Vibe |
73
- | OpenCode | API key | Generic LLM endpoint driver bundled defaults for Helmholtz Blablador, KISSKI AcademicCloud, and your own endpoint |
73
+ | Copilot | | GitHub Copilot (no vault route yet) |
74
+ | OpenCode | — (uses provider keys) | Harness that drives any OpenAI-compatible provider — curated configs for Helmholtz Blablador, KISSKI AcademicCloud, and OpenRouter (each authenticated with its own API key) |
75
+ | Pi | — (uses provider keys) | Multi-provider harness; routes through the phantom tokens of co-installed providers |
74
76
  | gh | OAuth, API key | GitHub CLI |
75
77
  | glab | API key | GitLab CLI |
78
+ | SonarCloud | API key | SonarCloud scanner |
76
79
  | CodeRabbit | API key | CodeRabbit (sidecar tool) |
77
- | SonarCloud | API key | SonarCloud scanner (sidecar tool) |
78
80
 
79
- \* Claude and Codex OAuth are experimental, and support must be explicitly allowed in the config file.
81
+ \* Claude and Codex OAuth are experimental.
80
82
 
81
- `terok-executor agents` lists the live roster (add `--all` to
82
- include the tool entries).
83
+ `terok-executor agents list` lists the live roster (add `--all` to
84
+ include tools and harness-driven providers).
83
85
 
84
86
  ## Where it sits in the stack
85
87
 
@@ -89,7 +91,8 @@ runs across many projects. Below it, terok-executor delegates the
89
91
  host-side security boundary
90
92
  ([terok-sandbox](https://github.com/terok-ai/terok-sandbox)): the
91
93
  credential vault, the git gate, the egress firewall hooks, the
92
- systemd service lifecycle.
94
+ per-container supervisor lifecycle (OCI `createRuntime`/`poststop`
95
+ hooks).
93
96
 
94
97
  ## Commands
95
98
 
@@ -99,13 +102,16 @@ systemd service lifecycle.
99
102
  | `setup` | Bootstrap sandbox services + container images |
100
103
  | `uninstall` | Remove sandbox services + container images |
101
104
  | `auth` | Authenticate a provider |
102
- | `agents` | List the agent roster |
105
+ | `agents` | Inspect the agent roster (`list`) and set the build-time default selection (`set`) |
103
106
  | `build` | Build base + agent images explicitly |
104
- | `run-tool` | Run a sidecar tool (CodeRabbit, SonarCloud) |
105
- | `list` | List running containers |
106
- | `stop` | Stop a running container |
107
+ | `run-tool` | Run a sidecar tool (CodeRabbit) |
108
+ | `list` | List containers |
109
+ | `start` | Start a stopped container |
110
+ | `stop` | Stop a container (kept for a later `start`) |
111
+ | `rm` | Remove a container and its host-side state |
107
112
  | `show-config` | Print the effective `SandboxConfig` as YAML (diffable across orchestrators) |
108
- | `vault` | Vault management (start, stop, status, install, routes) |
113
+ | `vault` | Vault management (status, unlock, lock, list, passphrase, routes, clean) |
114
+ | `sandbox` | Full terok-sandbox command tree (shield, vault, ssh, doctor, …) |
109
115
 
110
116
  ### Config override
111
117
 
@@ -16,18 +16,20 @@ maintainers = [
16
16
  keywords = ["podman", "containers", "agent", "ai", "coding-agent"]
17
17
  requires-python = ">=3.12,<3.15"
18
18
  dynamic = ["classifiers"]
19
+ # Dependency pins follow the policy in AGENTS.md ("Dependency Pinning &
20
+ # pyproject.toml Hygiene"). Keep the dependency list comment-free.
19
21
  dependencies = [
20
- "terok-sandbox>=0.2.0,<0.3.0",
21
- "terok-util>=0.2.0,<0.3.0",
22
- "ruamel.yaml>=0.18",
23
- "Jinja2>=3.1",
24
- "tomli-w>=1.0",
25
- "prompt-toolkit>=3.0",
26
- "pydantic>=2.9",
27
- "rich>=13.0",
28
- "agent-client-protocol>=0.10.1",
22
+ "terok-sandbox>=0.4.0,<0.5.0",
23
+ "terok-util>=0.2.1,<0.3.0",
24
+ "ruamel.yaml==0.19.1",
25
+ "Jinja2~=3.1",
26
+ "tomli-w~=1.2",
27
+ "prompt-toolkit~=3.0",
28
+ "pydantic~=2.13",
29
+ "rich~=15.0",
30
+ "agent-client-protocol==0.10.1",
29
31
  ]
30
- version = "0.2.0"
32
+ version = "0.3.0"
31
33
 
32
34
  [project.urls]
33
35
  Homepage = "https://terok-ai.github.io/terok-executor/"
@@ -58,18 +60,18 @@ packages = [
58
60
 
59
61
  [tool.poetry.group.dev.dependencies]
60
62
  pre-commit = ">=4.0"
61
- ruff = ">=0.15.16"
63
+ ruff = ">=0.15.20"
62
64
  bandit = ">=1.7"
63
65
  docstr-coverage = "^2.3.2"
64
- complexipy = ">=5.5.0"
66
+ complexipy = ">=6.0.0"
65
67
  vulture = ">=2.12"
66
68
  reuse = "^6.2.0"
67
69
  tach = "^0.35.0"
68
70
  mypy = ">=2.1.0,<3.0"
69
- import-linter = ">=2.1"
71
+ import-linter = ">=2.12"
70
72
 
71
73
  [tool.poetry.group.test.dependencies]
72
- pytest = ">=9.0.3"
74
+ pytest = ">=9.1.1"
73
75
  pytest-cov = ">=4.0"
74
76
  pytest-timeout = ">=2.0"
75
77
 
@@ -129,7 +131,7 @@ quote-style = "double"
129
131
  indent-style = "space"
130
132
  docstring-code-format = true
131
133
 
132
- # Static type checking see `make typecheck`.
134
+ # Static type checking -- see `make typecheck`.
133
135
  # ``resources/`` is excluded entirely: it holds container-side scripts and
134
136
  # bundled agent assets that aren't part of the host-side import graph
135
137
  # (``resources/toad-agents/`` isn't even a valid Python package name).
@@ -150,7 +152,7 @@ pretty = true
150
152
  show_error_codes = true
151
153
 
152
154
  # ``resources/`` holds container-side scripts (run inside task containers, not
153
- # part of the host import graph the same reason mypy excludes it). Tests
155
+ # part of the host import graph -- the same reason mypy excludes it). Tests
154
156
  # exercise them by path-loading (``SourceFileLoader``), which coverage.py can't
155
157
  # attribute, so they report 0%; omit them rather than chase phantom misses.
156
158
  [tool.coverage.run]
@@ -20,7 +20,7 @@ in their submodules; reach into ``terok_executor.<sub>`` when you
20
20
  need them.
21
21
  """
22
22
 
23
- __version__: str = "0.2.0" # placeholder; replaced at build time
23
+ __version__: str = "0.3.0" # placeholder; replaced at build time
24
24
 
25
25
  from importlib.metadata import PackageNotFoundError, version as _meta_version
26
26
 
@@ -36,6 +36,13 @@ except PackageNotFoundError:
36
36
  __version__ = "0.0.0"
37
37
 
38
38
 
39
+ #: Spelling under which sandbox setup is reachable in this front-end;
40
+ #: declared at CLI entry so sandbox-composed re-run hints name a verb
41
+ #: this CLI actually exposes (protocol: env var name, shared by string).
42
+ _SETUP_INVOCATION_ENV = "TEROK_SETUP_INVOCATION"
43
+ _SETUP_INVOCATION = "terok-executor setup"
44
+
45
+
39
46
  def main() -> None:
40
47
  """Run the terok-executor CLI.
41
48
 
@@ -44,6 +51,7 @@ def main() -> None:
44
51
  subparser convention, matching the placement used by ``docker`` and
45
52
  ``kubectl``.
46
53
  """
54
+ os.environ.setdefault(_SETUP_INVOCATION_ENV, _SETUP_INVOCATION)
47
55
  parser = argparse.ArgumentParser(
48
56
  prog="terok-executor",
49
57
  description="Single-agent task runner for hardened Podman containers",
@@ -202,7 +202,7 @@ def _resolve_host_git_identity() -> tuple[str | None, str | None]:
202
202
  def _handle_run(
203
203
  *,
204
204
  agent: str,
205
- repo: str = ".",
205
+ repo: str | None = None,
206
206
  prompt: str | None = None,
207
207
  model: str | None = None,
208
208
  max_turns: int | None = None,
@@ -218,6 +218,8 @@ def _handle_run(
218
218
  gpu: bool = False,
219
219
  memory: str | None = None,
220
220
  cpus: str | None = None,
221
+ workspace: str | None = None,
222
+ ephemeral: bool = False,
221
223
  git_identity_from_host: bool = False,
222
224
  shared_dir: str | None = None,
223
225
  shared_mount: str = "/shared",
@@ -249,6 +251,11 @@ def _handle_run(
249
251
  else:
250
252
  print("Warning: --git-identity-from-host: git config user.name not set, skipping")
251
253
 
254
+ # Bare ``run <agent>`` works on an isolated clone of the current repo;
255
+ # with --workspace the mounted directory's own content is the default.
256
+ if repo is None and workspace is None:
257
+ repo = "."
258
+
252
259
  effective_gate = gate and not no_gate
253
260
  runner = AgentRunner(base_image=base, family=family, cfg=cfg)
254
261
  resolved_shared_dir = Path(shared_dir) if shared_dir else None
@@ -260,6 +267,8 @@ def _handle_run(
260
267
  "gpu": gpu,
261
268
  "memory": memory,
262
269
  "cpus": cpus,
270
+ "workspace": Path(workspace) if workspace else None,
271
+ "ephemeral": ephemeral,
263
272
  "human_name": human_name,
264
273
  "human_email": human_email,
265
274
  "authorship": authorship,
@@ -295,12 +304,14 @@ def _handle_run(
295
304
  def _handle_run_tool(
296
305
  *,
297
306
  tool: str,
298
- repo: str = ".",
307
+ repo: str | None = None,
299
308
  branch: str | None = None,
300
309
  gate: bool = True,
301
310
  no_gate: bool = False,
302
311
  name: str | None = None,
303
312
  timeout: int = 600,
313
+ workspace: str | None = None,
314
+ ephemeral: bool = False,
304
315
  tool_args: list[str] | None = None,
305
316
  base: str = DEFAULT_BASE_IMAGE,
306
317
  family: str | None = None,
@@ -318,6 +329,11 @@ def _handle_run_tool(
318
329
 
319
330
  from .container.runner import AgentRunner
320
331
 
332
+ # Same default policy as ``run``: bare invocation works on an isolated
333
+ # clone of the current repo; --workspace brings its own content.
334
+ if repo is None and workspace is None:
335
+ repo = "."
336
+
321
337
  effective_gate = gate and not no_gate
322
338
  runner = AgentRunner(base_image=base, family=family, cfg=cfg)
323
339
  cname = runner.run_tool(
@@ -328,6 +344,8 @@ def _handle_run_tool(
328
344
  gate=effective_gate,
329
345
  name=name,
330
346
  timeout=timeout,
347
+ workspace=Path(workspace) if workspace else None,
348
+ ephemeral=ephemeral,
331
349
  timezone=timezone,
332
350
  )
333
351
  print(f"Container: {cname}")
@@ -338,10 +356,27 @@ def _handle_auth(
338
356
  agent: str,
339
357
  api_key: str | None = None,
340
358
  base_image: str | None = None,
359
+ device_auth: bool = False,
341
360
  ) -> None:
342
- """Run auth flow for an agent."""
361
+ """Run auth flow for an agent.
362
+
363
+ With *device_auth* the interactive method chooser is skipped and the
364
+ provider's headless device-code login runs directly — for remote or
365
+ headless hosts where the browser callback can't open. ``--api-key`` and
366
+ ``--device-auth`` are mutually exclusive; passing both takes the API-key
367
+ route (no container) and warns that ``--device-auth`` was ignored.
368
+ """
343
369
  from .credentials.auth import AUTH_PROVIDERS, Authenticator, store_api_key
344
370
 
371
+ if api_key is not None and device_auth:
372
+ import sys
373
+
374
+ print(
375
+ "Warning: --device-auth is ignored when --api-key is given; "
376
+ "the API key takes precedence (no auth container is launched).",
377
+ file=sys.stderr,
378
+ )
379
+
345
380
  if api_key is not None:
346
381
  if not api_key.strip():
347
382
  raise SystemExit("API key cannot be empty.")
@@ -361,6 +396,7 @@ def _handle_auth(
361
396
  None,
362
397
  mounts_dir=mounts_dir(),
363
398
  image=lambda: ImageBuilder(base).ensure_default_l1(),
399
+ device_auth=device_auth,
364
400
  )
365
401
 
366
402
  # Write vault URLs to shared config files (e.g. Vibe config.toml, gh config.yml)
@@ -455,12 +491,27 @@ def _handle_build(
455
491
 
456
492
 
457
493
  def _handle_list() -> None:
458
- """List running terok-executor containers."""
494
+ """List executor-managed containers and their states.
495
+
496
+ Two name sources meet here: the ``terok-executor-`` default-name
497
+ prefix, and the per-container state dirs under
498
+ [`container_state_root`][terok_executor.paths.container_state_root]
499
+ — the latter makes ``--name`` overrides visible. Podman stays the
500
+ truth for liveness: the dirs only contribute names to ask about,
501
+ and a name whose container is gone is skipped.
502
+ """
459
503
  from terok_executor.integrations.sandbox import PodmanRuntime
504
+ from terok_executor.paths import container_state_root
460
505
 
461
- states = PodmanRuntime().container_states("terok-executor")
506
+ runtime = PodmanRuntime()
507
+ states = runtime.container_states("terok-executor")
508
+ run_root = container_state_root()
509
+ named = (p.name for p in run_root.iterdir() if p.is_dir()) if run_root.is_dir() else ()
510
+ for name in named:
511
+ if name not in states and (state := runtime.container(name).state) is not None:
512
+ states[name] = state
462
513
  if not states:
463
- print("No running containers.")
514
+ print("No containers.")
464
515
  return
465
516
  for name, state in sorted(states.items()):
466
517
  print(f"{name} {state}")
@@ -510,19 +561,57 @@ def _handle_show_config(*, cfg: SandboxConfig | None = None) -> None:
510
561
  yaml.dump(data, sys.stdout)
511
562
 
512
563
 
513
- def _handle_stop(*, name: str) -> None:
514
- """Stop a running container (best-effort)."""
515
- from terok_executor.integrations.sandbox import PodmanRuntime
564
+ def _handle_start(*, name: str) -> None:
565
+ """Start a stopped container, re-establishing its host scaffolding."""
566
+ from terok_executor.integrations.sandbox import PodmanRuntime, Sandbox
516
567
 
517
- runtime = PodmanRuntime()
518
- container = runtime.container(name)
519
- if container.state is None:
520
- print(f"Container not found: {name}")
521
- return
522
- runtime.force_remove([container])
568
+ try:
569
+ Sandbox(runtime=PodmanRuntime()).start(name)
570
+ except RuntimeError as exc:
571
+ raise SystemExit(str(exc)) from exc
572
+ print(f"Started: {name}")
573
+
574
+
575
+ def _handle_stop(*, name: str, timeout: int = 10) -> None:
576
+ """Stop a container, keeping it for a later ``start``.
577
+
578
+ A missing container is an error, exactly as ``podman stop`` treats
579
+ it — the facade's ``RuntimeError`` carries podman's own message.
580
+ """
581
+ from terok_executor.integrations.sandbox import PodmanRuntime, Sandbox
582
+
583
+ try:
584
+ Sandbox(runtime=PodmanRuntime()).stop([name], timeout=timeout)
585
+ except RuntimeError as exc:
586
+ raise SystemExit(str(exc)) from exc
523
587
  print(f"Stopped: {name}")
524
588
 
525
589
 
590
+ def _handle_rm(*, name: str) -> None:
591
+ """Remove a container together with its host-side state.
592
+
593
+ Also sweeps the per-container state when the container is already
594
+ gone — the one situation (crash, out-of-band ``podman rm``) where
595
+ residue outlives its container.
596
+ """
597
+ import shutil
598
+
599
+ from terok_executor.integrations.sandbox import (
600
+ PodmanRuntime,
601
+ Sandbox,
602
+ remove_container_state,
603
+ )
604
+ from terok_executor.paths import container_state_dir
605
+
606
+ sandbox = Sandbox(runtime=PodmanRuntime())
607
+ [result] = sandbox.rm([name])
608
+ if not result.removed:
609
+ raise SystemExit(f"Could not remove {name}: {result.error}")
610
+ remove_container_state(name, cfg=sandbox.config)
611
+ shutil.rmtree(container_state_dir(name), ignore_errors=True)
612
+ print(f"Removed: {name}")
613
+
614
+
526
615
  def _handle_setup(
527
616
  *,
528
617
  check: bool = False,
@@ -530,6 +619,7 @@ def _handle_setup(
530
619
  no_images: bool = False,
531
620
  base: str = DEFAULT_BASE_IMAGE,
532
621
  family: str | None = None,
622
+ passphrase_tier: str | None = None,
533
623
  cfg: SandboxConfig | None = None,
534
624
  ) -> None:
535
625
  """Bootstrap the full terok-executor stack on a fresh host.
@@ -546,7 +636,7 @@ def _handle_setup(
546
636
  if not no_sandbox:
547
637
  from .sandbox import ensure_sandbox_ready
548
638
 
549
- ensure_sandbox_ready(cfg=cfg)
639
+ ensure_sandbox_ready(cfg=cfg, passphrase_tier=passphrase_tier)
550
640
 
551
641
  if not no_images:
552
642
  _build_images_with_banner(base, family)
@@ -656,7 +746,13 @@ RUN_COMMAND = CommandDef(
656
746
  handler=_handle_run,
657
747
  args=(
658
748
  ArgDef(name="agent", help="Agent name (claude, codex, vibe, ...)"),
659
- ArgDef(name="repo", nargs="?", default=".", help="Local path or git URL (default: .)"),
749
+ ArgDef(
750
+ name="repo",
751
+ nargs="?",
752
+ default=None,
753
+ help="Git URL or local dir cloned into the workspace via the gate "
754
+ "(default: . — unless --workspace brings its own content)",
755
+ ),
660
756
  ArgDef(name="-p", dest="prompt", help="Prompt for headless mode"),
661
757
  ArgDef(name="-m", dest="model", help="Model override"),
662
758
  ArgDef(name="--max-turns", type=int, help="Maximum agent turns"),
@@ -676,6 +772,17 @@ RUN_COMMAND = CommandDef(
676
772
  ArgDef(name="--gpu", action="store_true", help="Enable GPU passthrough"),
677
773
  ArgDef(name="--memory", help="Container memory limit (e.g. 4g, 512m)"),
678
774
  ArgDef(name="--cpus", help="Container CPU limit (e.g. 2.0, 0.5)"),
775
+ ArgDef(
776
+ name="--workspace",
777
+ help="Host directory to mount at /workspace "
778
+ "(default: the workspace lives in the container)",
779
+ ),
780
+ ArgDef(
781
+ name="--rm",
782
+ dest="ephemeral",
783
+ action="store_true",
784
+ help="Remove the container when it exits (podman --rm)",
785
+ ),
679
786
  ArgDef(
680
787
  name="--git-identity-from-host",
681
788
  action="store_true",
@@ -726,12 +833,29 @@ RUN_TOOL_COMMAND = CommandDef(
726
833
  handler=_handle_run_tool,
727
834
  args=(
728
835
  ArgDef(name="tool", help="Tool name (coderabbit)"),
729
- ArgDef(name="repo", nargs="?", default=".", help="Local path or git URL (default: .)"),
836
+ ArgDef(
837
+ name="repo",
838
+ nargs="?",
839
+ default=None,
840
+ help="Git URL or local dir cloned into the workspace via the gate "
841
+ "(default: . — unless --workspace brings its own content)",
842
+ ),
730
843
  ArgDef(name="--branch", help="Git branch to check out"),
731
844
  ArgDef(name="--gate", action="store_true", default=True, help="Use gate (default)"),
732
845
  ArgDef(name="--no-gate", action="store_true", help="Disable gate"),
733
846
  ArgDef(name="--name", help="Container name override"),
734
847
  ArgDef(name="--timeout", type=int, default=600, help="Timeout in seconds (default: 600)"),
848
+ ArgDef(
849
+ name="--workspace",
850
+ help="Host directory to mount at /workspace "
851
+ "(default: the workspace lives in the container)",
852
+ ),
853
+ ArgDef(
854
+ name="--rm",
855
+ dest="ephemeral",
856
+ action="store_true",
857
+ help="Remove the container when it exits (podman --rm)",
858
+ ),
735
859
  ArgDef(name="tool_args", nargs="*", help="Extra args passed to the tool (after --)"),
736
860
  ArgDef(
737
861
  name="--base",
@@ -773,6 +897,11 @@ AUTH_COMMAND = CommandDef(
773
897
  args=(
774
898
  ArgDef(name="agent", help="Agent or tool name (claude, codex, gh, ...)"),
775
899
  ArgDef(name="--api-key", help="Store an API key directly (skip interactive auth)"),
900
+ ArgDef(
901
+ name="--device-auth",
902
+ action="store_true",
903
+ help="Force the headless device-code login (skip the method chooser)",
904
+ ),
776
905
  ArgDef(
777
906
  name="--base-image",
778
907
  help=(
@@ -871,7 +1000,7 @@ ACP_COMMAND = CommandDef(
871
1000
  )
872
1001
 
873
1002
 
874
- LIST_COMMAND = CommandDef(name="list", help="List running containers", handler=_handle_list)
1003
+ LIST_COMMAND = CommandDef(name="list", help="List containers", handler=_handle_list)
875
1004
 
876
1005
  SHOW_CONFIG_COMMAND = CommandDef(
877
1006
  name="show-config",
@@ -879,10 +1008,32 @@ SHOW_CONFIG_COMMAND = CommandDef(
879
1008
  handler=_handle_show_config,
880
1009
  )
881
1010
 
1011
+ START_COMMAND = CommandDef(
1012
+ name="start",
1013
+ help="Start a stopped container",
1014
+ handler=_handle_start,
1015
+ args=(ArgDef(name="name", help="Container name"),),
1016
+ )
1017
+
882
1018
  STOP_COMMAND = CommandDef(
883
1019
  name="stop",
884
- help="Stop a running container",
1020
+ help="Stop a container (kept for a later start)",
885
1021
  handler=_handle_stop,
1022
+ args=(
1023
+ ArgDef(name="name", help="Container name"),
1024
+ ArgDef(
1025
+ name="--timeout",
1026
+ type=int,
1027
+ default=10,
1028
+ help="Seconds before the stop escalates to SIGKILL (default: 10)",
1029
+ ),
1030
+ ),
1031
+ )
1032
+
1033
+ RM_COMMAND = CommandDef(
1034
+ name="rm",
1035
+ help="Remove a container and its host-side state",
1036
+ handler=_handle_rm,
886
1037
  args=(ArgDef(name="name", help="Container name"),),
887
1038
  )
888
1039
 
@@ -918,6 +1069,15 @@ SETUP_COMMAND = CommandDef(
918
1069
  default=None,
919
1070
  help="Override package family for unknown base images (deb or rpm)",
920
1071
  ),
1072
+ ArgDef(
1073
+ name="--passphrase-tier",
1074
+ default=None,
1075
+ help=(
1076
+ "Force credentials-DB passphrase storage to a specific tier"
1077
+ " (systemd-creds | keyring | session-file | config); required"
1078
+ " on a non-TTY host without systemd-creds"
1079
+ ),
1080
+ ),
921
1081
  ),
922
1082
  )
923
1083
 
@@ -956,7 +1116,9 @@ COMMANDS: tuple[CommandDef, ...] = (
956
1116
  SETUP_COMMAND,
957
1117
  UNINSTALL_COMMAND,
958
1118
  LIST_COMMAND,
1119
+ START_COMMAND,
959
1120
  STOP_COMMAND,
1121
+ RM_COMMAND,
960
1122
  SHOW_CONFIG_COMMAND,
961
1123
  ACP_COMMAND,
962
1124
  )