Opencode improvements

This commit is contained in:
Samuel Aubertin
2026-04-15 03:31:04 +02:00
parent 6ca643830f
commit 273e42dd2d
8 changed files with 537 additions and 165 deletions

View File

@@ -20,3 +20,5 @@ Do not remove existing instructions unless they are outdated or wrong.
`bash tests/run_tests.sh` (you can also run them separately)
- When running tests from inside sloptrap, inherited `CODEX_HOME=/codex` plus `SLOPTRAP_PREFER_CODEX_HOME=1` can leak into host-style child launches; ignore that preference when `HOME` has been redirected elsewhere and the runtime hints still point into the inherited `/codex` tree.
- Capability-enabled runs are Podman-only. `packet-capture` uses a dedicated helper container/pod, and host-network capture must prompt for an explicit acknowledgement on every runtime launch.
- `agent=opencode` should download the latest Linux CLI release artifact from GitHub into the build context and verify its digest; it should not depend on a host-installed `opencode` binary.
- For isolated networking, sloptrap exposes the host inside the container as `sloptrap.host`; opencode localhost URLs should be rewritten to that alias, and Podman `slirp4netns` runs need `allow_host_loopback=true` for host-local servers.

View File

@@ -1,28 +0,0 @@
# Dockerfile.sloptrap
ARG BASE_IMAGE=debian:trixie-slim
FROM ${BASE_IMAGE}
ENV DEBIAN_FRONTEND=noninteractive
ARG BASE_PACKAGES="curl bash ca-certificates libstdc++6 ripgrep xxd file procps util-linux"
ARG EXTRA_PACKAGES=""
RUN apt-get update \
&& apt-get install -y --no-install-recommends apt-utils ${BASE_PACKAGES} ${EXTRA_PACKAGES} \
&& rm -rf /var/lib/apt/lists/*
ARG CODEX_UID=1337
ARG CODEX_GID=1337
RUN groupadd --gid ${CODEX_GID} sloptrap \
&& useradd --create-home --home-dir /home/sloptrap \
--gid sloptrap --uid ${CODEX_UID} --shell /bin/bash sloptrap
ARG CODEX_BIN=codex
ARG CODEX_BIN_PATH=/usr/local/bin/codex
COPY ${CODEX_BIN} ${CODEX_BIN_PATH}
RUN chmod 0755 ${CODEX_BIN_PATH} \
&& chown -R sloptrap:sloptrap /home/sloptrap
WORKDIR /workspace
ENV SHELL=/bin/bash HOME=/home/sloptrap
ENTRYPOINT ["${CODEX_BIN_PATH}"]

View File

@@ -63,8 +63,9 @@ Supported keys when the manifest is present:
| `name` | project directory name | Must match `^[A-Za-z0-9_.-]+$`. Used for image/container naming. |
| `packages_extra` | *empty* | Additional Debian packages installed during `docker/podman build`. Tokens must be alphanumeric plus `+.-`. |
| `agent` | `codex` | AI backend: `codex` (OpenAI Codex CLI) or `opencode` (Anomaly opencode CLI). |
| `opencode_server` | `http://localhost:11434` | OpenAI-compatible server URL (opencode only). Supports llama.cpp, Ollama, vLLM, etc. |
| `opencode_model` | `llama3` | Model name on the server (opencode only). |
| `opencode_server` | `http://localhost:8080` | OpenAI-compatible server URL (opencode only). Supports llama.cpp, Ollama, vLLM, etc. |
| `opencode_model` | `bartowski/Qwen_Qwen3.5-9B-GGUF:Q8_0` | Model name on the server (opencode only). |
| `opencode_context` | `256K` | Context window for the opencode model. Accepts an integer optionally suffixed with `K`, `M`, or `G`. |
| `allow_host_network` | `false` | `true` opts into `--network host`; keep `false` unless the project absolutely requires direct access to host-local services. |
Values containing `$`, `` ` ``, or newlines are rejected to prevent command injection. Setting illegal keys or malformed values aborts the run before containers start.
@@ -73,7 +74,7 @@ Values containing `$`, `` ` ``, or newlines are rejected to prevent command inje
**Codex** (default): Uses OpenAI Codex CLI with state stored in `~/.codex/`. Supports login mode for credential sharing.
**opencode**: Uses Anomaly opencode CLI with state stored in `~/.opencode/`. Connects to any OpenAI-compatible inference server (llama.cpp, Ollama, vLLM, etc.). No authentication required for self-hosted models; API keys supported via manifest if needed.
**opencode**: Uses Anomaly opencode CLI with state stored in `~/.opencode/`. sloptrap downloads the latest Linux CLI release artifact from Anomaly during image builds, verifies its digest from the GitHub release metadata, and copies it into the container image. Connects to any OpenAI-compatible inference server (llama.cpp, Ollama, vLLM, etc.). When `opencode_server` points at `localhost` under isolated networking, sloptrap rewrites it to `http://sloptrap.host:...` so host-local model servers remain reachable from inside the container. No authentication required for self-hosted models; API keys supported via manifest if needed.
### `.sloptrapignore`
@@ -142,7 +143,7 @@ The launcher executes targets sequentially, so `./sloptrap repo build run` perfo
- **Codex**: project directory at `/workspace`; `${HOME}/.codex/sloptrap/state/<project-hash>` at `/codex`; auth at `/codex/auth.json`.
- **opencode**: project directory at `/workspace`; `${HOME}/.opencode/sloptrap/state/<project-hash>` at `/codex/state/opencode`; state at `/codex/state`.
- Ignore filter: `.sloptrapignore` entries are overlaid with tmpfs directories or empty bind mounts so data remains unavailable to the agent.
- Network: isolated networking is used by default; `allow_host_network=true` opts into `--network host`.
- Network: isolated networking is used by default; `allow_host_network=true` opts into `--network host`. For isolated runs, sloptrap injects `sloptrap.host` as a container-side hostname for the host gateway. On Podman `slirp4netns`, opencode runs also enable host loopback access so host-local servers bound to `localhost` remain reachable.
- Process context: standard runs drop capabilities, set `no-new-privileges`, use a read-only root filesystem, and keep scratch paths (`/tmp`, `/run`, `/run/lock`) on tmpfs.
- Agent configuration:
- **Codex**: runtime flags fixed to `--sandbox danger-full-access --ask-for-approval never`. Supports login mode for credential sharing.

512
sloptrap
View File

@@ -242,7 +242,11 @@ ALLOW_HOST_NETWORK=false
BACKEND="codex"
OPENCODE_SERVER=""
OPENCODE_MODEL=""
OPENCODE_CONTEXT=""
OPENCODE_STATE_HOME_HOST=""
OPENCODE_CONFIG_HOST=""
OPENCODE_CONFIG_CONT=""
SLOPTRAP_HOST_ALIAS=""
declare -a SLOPTRAP_TEMP_PATHS=()
@@ -302,24 +306,35 @@ create_temp_dir() {
write_embedded_dockerfile() {
if [[ "$BACKEND" == "opencode" ]]; then
cat <<'EOF'
local opencode_pkg
case "$(uname -m)" in
x86_64|amd64)
opencode_pkg="opencode-desktop-linux-amd64.deb"
;;
arm64|aarch64)
opencode_pkg="opencode-desktop-linux-arm64.deb"
;;
*)
error "unsupported architecture for opencode"
;;
esac
local dockerfile_content
dockerfile_content=$(cat <<'DOCKERFILE_EOF'
# Dockerfile.sloptrap
ARG BASE_IMAGE=debian:trixie-slim
FROM ${BASE_IMAGE}
ENV DEBIAN_FRONTEND=noninteractive
ARG BASE_PACKAGES="curl bash ca-certificates libstdc++6 ripgrep xxd file procps util-linux"
ARG EXTRA_PACKAGES=""
RUN apt-get update \
&& apt-get install -y --no-install-recommends apt-utils ${BASE_PACKAGES} ${EXTRA_PACKAGES} \
&& rm -rf /var/lib/apt/lists/*
ARG BASE_PACKAGES="curl bash ca-certificates libstdc++6 wget ripgrep xxd file procps util-linux binutils"
RUN apt-get update && apt-get install -y --no-install-recommends apt-utils ${BASE_PACKAGES} ${EXTRA_PACKAGES} && rm -rf /var/lib/apt/lists/*
ARG CODEX_UID=1337
ARG CODEX_GID=1337
RUN groupadd --gid ${CODEX_GID} sloptrap \
&& useradd --create-home --home-dir /home/sloptrap \
--gid sloptrap --uid ${CODEX_UID} --shell /bin/bash sloptrap
--gid sloptrap --uid ${CODEX_UID} --shell /bin/bash sloptrap
ARG OPENCODE_BIN=opencode
ARG OPENCODE_CONF=config/config.toml
@@ -330,8 +345,12 @@ RUN chmod 0755 /usr/local/bin/opencode \
WORKDIR /workspace
ENV SHELL=/bin/bash HOME=/home/sloptrap
ENTRYPOINT ["/usr/local/bin/opencode"]
EOF
ENTRYPOINT ["opencode"]
DOCKERFILE_EOF
)
# Replace placeholder with actual package name
dockerfile_content=$(printf '%s' "$dockerfile_content" | sed "s|PLACEHOLDER_OPENCODE_PKG|${opencode_pkg}|g")
printf '%s' "$dockerfile_content"
else
cat <<'EOF'
# Dockerfile.sloptrap
@@ -725,6 +744,24 @@ validate_package_list() {
done
}
# Common package name aliases for user convenience
expand_package_alias() {
local pkg=$1
case "$pkg" in
rg) printf 'ripgrep' ;;
git) printf 'git' ;;
curl) printf 'curl' ;;
bash) printf 'bash' ;;
ca-certificates) printf 'ca-certificates' ;;
libstdc++6) printf 'libstdc++6' ;;
xxd) printf 'xxd' ;;
file) printf 'file' ;;
procps) printf 'procps' ;;
util-linux) printf 'util-linux' ;;
*) printf '%s' "$pkg" ;;
esac
}
detect_container_engine() {
local override=${SLOPTRAP_CONTAINER_ENGINE-}
if [[ -n $override ]]; then
@@ -815,6 +852,26 @@ normalize_wizard_allow_host_network() {
esac
}
parse_opencode_context() {
local raw=$1
local upper=${raw^^}
local number suffix multiplier=1
[[ -n $upper ]] || error "$MANIFEST_PATH: opencode_context must not be empty"
if [[ $upper =~ ^([0-9]+)([KMG]?)$ ]]; then
number=${BASH_REMATCH[1]}
suffix=${BASH_REMATCH[2]}
else
error "$MANIFEST_PATH: opencode_context must be an integer optionally suffixed with K, M, or G (got '$raw')"
fi
case "$suffix" in
"") multiplier=1 ;;
K) multiplier=1024 ;;
M) multiplier=$((1024 * 1024)) ;;
G) multiplier=$((1024 * 1024 * 1024)) ;;
esac
printf '%s' $(( number * multiplier ))
}
run_wizard() {
local manifest_path=$1
if [[ -L $manifest_path ]]; then
@@ -832,13 +889,15 @@ run_wizard() {
local default_allow_host_network
local default_opencode_server
local default_opencode_model
local default_opencode_context
default_name=$(manifest_default_value "name" "$(basename "$CODE_DIR")")
default_packages_extra=$(manifest_default_value "packages_extra" "")
default_agent=$(manifest_default_value "agent" "codex")
default_allow_host_network=$(manifest_default_value "allow_host_network" "false")
default_opencode_server=$(manifest_default_value "opencode_server" "http://localhost:11434")
default_opencode_model=$(manifest_default_value "opencode_model" "llama3")
default_opencode_server=$(manifest_default_value "opencode_server" "http://localhost:8080")
default_opencode_model=$(manifest_default_value "opencode_model" "bartowski/Qwen_Qwen3.5-9B-GGUF:Q8_0")
default_opencode_context=$(manifest_default_value "opencode_context" "256K")
local action="Creating"
if [[ -f $manifest_path ]]; then
@@ -886,22 +945,32 @@ run_wizard() {
# If opencode, prompt for server and model
if [[ "$default_agent" == "opencode" ]]; then
while true; do
info_line "opencode_server: OpenAI-compatible server URL (e.g., http://localhost:11434).\n"
info_line "opencode_server: OpenAI-compatible server URL (e.g., http://localhost:8080).\n"
value=$(prompt_manifest_value "opencode_server" "$default_opencode_server")
value=$(trim "$value")
[[ -n $value ]] || value="http://localhost:11434"
[[ -n $value ]] || value="http://localhost:8080"
default_opencode_server=$value
break
done
while true; do
info_line "opencode_model: Model name on the server (e.g., llama3).\n"
info_line "opencode_model: Model name on the server (e.g., bartowski/Qwen_Qwen3.5-9B-GGUF:Q8_0).\n"
value=$(prompt_manifest_value "opencode_model" "$default_opencode_model")
value=$(trim "$value")
[[ -n $value ]] || value="llama3"
[[ -n $value ]] || value="bartowski/Qwen_Qwen3.5-9B-GGUF:Q8_0"
default_opencode_model=$value
break
done
while true; do
info_line "opencode_context: Context window for the model (e.g., 256K).\n"
value=$(prompt_manifest_value "opencode_context" "$default_opencode_context")
value=$(trim "$value")
[[ -n $value ]] || value="256K"
parse_opencode_context "$value" >/dev/null
default_opencode_context=$value
break
done
fi
while true; do
@@ -924,6 +993,7 @@ EOF
cat >> "$manifest_path" <<EOF
opencode_server=$default_opencode_server
opencode_model=$default_opencode_model
opencode_context=$default_opencode_context
EOF
fi
info_line "Wrote %s\n" "$manifest_path"
@@ -948,7 +1018,10 @@ print_config() {
info_line "backend=%s\n" "opencode"
info_line "opencode_server=%s\n" "$OPENCODE_SERVER"
info_line "opencode_model=%s\n" "$OPENCODE_MODEL"
info_line "opencode_context=%s\n" "$OPENCODE_CONTEXT"
info_line "opencode_state_home=%s\n" "$OPENCODE_STATE_HOME_HOST"
info_line "opencode_config=%s\n" "$OPENCODE_CONFIG_HOST"
info_line "runtime_flags=\n"
else
info_line "backend=%s\n" "codex"
info_line "codex_home=%s\n" "$CODEX_STATE_HOME_HOST"
@@ -959,9 +1032,10 @@ print_config() {
info_line "codex_home_bootstrap=%s\n" "$CODEX_HOME_BOOTSTRAP"
info_line "codex_archive=%s\n" "$SLOPTRAP_CODEX_ARCHIVE"
info_line "codex_url=%s\n" "$SLOPTRAP_CODEX_URL"
info_line "runtime_flags=%s\n" "$CODEX_ARGS_DISPLAY"
fi
info_line "host_alias=%s\n" "${SLOPTRAP_HOST_ALIAS:-}"
info_line "needs_login=%s\n" "$NEED_LOGIN"
info_line "runtime_flags=%s\n" "$CODEX_ARGS_DISPLAY"
info_line "ignore_stub_base=%s\n" "$IGNORE_STUB_BASE"
if [[ ${#SLOPTRAP_IGNORE_ENTRIES[@]} -gt 0 ]]; then
local ignore_paths
@@ -990,10 +1064,16 @@ print_manifest_summary() {
comment_line " backend=%s\n" "opencode"
comment_line " opencode_server=%s\n" "$OPENCODE_SERVER"
comment_line " opencode_model=%s\n" "$OPENCODE_MODEL"
comment_line " opencode_context=%s\n" "$OPENCODE_CONTEXT"
comment_line " opencode_config=%s\n" "$OPENCODE_CONFIG_CONT"
comment_line " runtime_flags=\n"
else
comment_line " backend=%s\n" "codex"
comment_line " runtime_flags=%s\n" "$CODEX_ARGS_DISPLAY"
fi
if [[ -n $SLOPTRAP_HOST_ALIAS ]]; then
comment_line " host_alias=%s\n" "$SLOPTRAP_HOST_ALIAS"
fi
comment_line " runtime_flags=%s\n" "$CODEX_ARGS_DISPLAY"
comment_line " allow_host_network=%s\n" "$ALLOW_HOST_NETWORK"
}
@@ -1019,6 +1099,10 @@ Current resolved sloptrap state:
- network_mode=$network_mode (host when host networking is enabled; otherwise isolated)
EOF
)
if [[ -n $SLOPTRAP_HOST_ALIAS ]]; then
prompt+=$'\n'
prompt+="- host_alias=$SLOPTRAP_HOST_ALIAS (container-side address for services running on the host)\n"
fi
printf '%s' "$prompt"
}
@@ -1063,7 +1147,7 @@ get_env_default() {
validate_codex_archive_name() {
local name=$1
[[ $name =~ ^codex-[A-Za-z0-9_.-]+$ ]] || error "invalid Codex archive name '$name'"
[[ $name =~ ^(codex|opencode)-[A-Za-z0-9_.-]+$ ]] || error "invalid binary name '$name'"
}
detect_codex_archive_name() {
@@ -1186,6 +1270,106 @@ ensure_opencode_storage_paths() {
ensure_codex_directory "$state_root" "sloptrap Opencode namespace"
ensure_codex_directory "$state_bucket" "sloptrap Opencode state root"
ensure_codex_directory "$CODEX_STATE_HOME_HOST" "project Opencode state"
ensure_codex_directory "$OPENCODE_STATE_HOME_HOST" "project Opencode runtime state"
}
ensure_opencode_config() {
local provider_id="llama.cpp"
local provider_name="llama-server (local)"
local base_url="$OPENCODE_SERVER"
local context_limit
context_limit=$(parse_opencode_context "$OPENCODE_CONTEXT")
local model_key="${OPENCODE_MODEL} - ${context_limit}"
local model_ref="${provider_id}/${model_key}"
local config_dir
case "$base_url" in
*/v1|*/v1/|*/v1\?*|*/v1\#*)
;;
*)
base_url="${base_url%/}/v1"
;;
esac
config_dir=$(dirname "$OPENCODE_CONFIG_HOST")
if $DRY_RUN; then
print_command mkdir -p "$config_dir"
print_command env OPENCODE_CONFIG="$OPENCODE_CONFIG_CONT" opencode
return 0
fi
ensure_codex_directory "$config_dir" "project Opencode config"
if ! jq -n \
--arg schema "https://opencode.ai/config.json" \
--arg provider_id "$provider_id" \
--arg provider_name "$provider_name" \
--arg base_url "$base_url" \
--arg model_id "$OPENCODE_MODEL" \
--arg model_key "$model_key" \
--arg model_ref "$model_ref" \
--argjson context_limit "$context_limit" \
'{
"$schema": $schema,
enabled_providers: [$provider_id],
share: "disabled",
autoupdate: false,
provider: {
($provider_id): {
npm: "@ai-sdk/openai-compatible",
name: $provider_name,
options: {
baseURL: $base_url,
timeout: 1800000,
chunkTimeout: 180000
},
models: {
($model_key): {
name: $model_id,
limit: {
context: $context_limit,
output: 32768
},
cost: {
input: 0.0221,
output: 0.1684,
cache_read: 0.0001,
cache_write: 0.0001
},
tool_call: true,
reasoning: true
}
}
}
},
model: $model_ref,
compaction: {
auto: true,
prune: true,
reserved: 16000
},
watcher: {
ignore: [
".git/**",
"node_modules/**",
"dist/**",
".run/**"
]
},
permission: {
read: "allow",
edit: "allow",
glob: "allow",
grep: "allow",
list: "allow",
bash: "allow",
task: "allow",
question: "allow",
webfetch: "allow",
websearch: "allow",
codesearch: "allow",
external_directory: "deny",
doom_loop: "ask"
}
}' >"$OPENCODE_CONFIG_HOST"; then
error "failed to write opencode config '$OPENCODE_CONFIG_HOST'"
fi
}
fetch_latest_codex_digest() {
@@ -1208,42 +1392,43 @@ fetch_latest_codex_digest() {
printf '%s' "$digest_line"
}
detect_opencode_archive_name() {
local os arch opencode_os opencode_arch
os=$(uname -s 2>/dev/null || true)
arch=$(uname -m 2>/dev/null || true)
[[ -n $os ]] || error "failed to detect host OS for opencode download"
[[ -n $arch ]] || error "failed to detect host architecture for opencode download"
case "$os" in
Linux|Darwin) opencode_os="unknown-linux-gnu" ;;
*) error "unsupported host OS '$os' for opencode download" ;;
esac
case "$arch" in
x86_64|amd64) opencode_arch="x86_64" ;;
arm64|aarch64) opencode_arch="arm64" ;;
*) error "unsupported host architecture '$arch' for opencode download" ;;
esac
printf 'opencode-%s-%s' "$opencode_arch" "$opencode_os"
detect_opencode_asset_name() {
local arch
arch=$(uname -m 2>/dev/null || true)
[[ -n $arch ]] || error "failed to detect host architecture for opencode download"
case "$arch" in
x86_64|amd64)
printf 'opencode-linux-x64.tar.gz'
;;
arm64|aarch64)
printf 'opencode-linux-arm64.tar.gz'
;;
*)
error "unsupported host architecture '$arch' for opencode download"
;;
esac
}
fetch_latest_opencode_digest() {
local api_url="https://api.github.com/repos/anomalyco/opencode/releases/latest"
local target_asset="${SLOPTRAP_CODEX_BIN_NAME}.tar.gz"
[[ -n $SLOPTRAP_CODEX_BIN_NAME ]] || error "opencode binary name is not set"
if ! command -v jq >/dev/null 2>&1; then
error "jq is required to verify the opencode binary digest"
fi
local response
if ! response=$(curl -fsSL "$api_url"); then
error "failed to download opencode release metadata from GitHub"
fi
local digest_line
digest_line=$(jq -r --arg name "$target_asset" '.assets[] | select(.name == $name) | .digest' <<<"$response" | head -n 1)
if [[ -z $digest_line || $digest_line == "null" ]]; then
error "failed to resolve opencode digest from GitHub response"
fi
digest_line=${digest_line#sha256:}
printf '%s' "$digest_line"
local api_url="https://api.github.com/repos/anomalyco/opencode/releases/latest"
local target_asset
target_asset=$(detect_opencode_asset_name)
[[ -n $target_asset ]] || error "opencode binary name is not set"
if ! command -v jq >/dev/null 2>&1; then
error "jq is required to verify the opencode binary digest"
fi
local response
if ! response=$(curl -fsSL "$api_url"); then
error "failed to download opencode release metadata from GitHub"
fi
local digest_line
digest_line=$(jq -r --arg name "$target_asset" '.assets[] | select(.name == $name) | .digest' <<<"$response" | head -n 1)
if [[ -z $digest_line || $digest_line == "null" ]]; then
error "failed to resolve opencode digest from GitHub response (looking for $target_asset)"
fi
digest_line=${digest_line#sha256:}
printf '%s' "$digest_line"
}
ensure_codex_binary() {
@@ -1285,28 +1470,30 @@ ensure_opencode_binary() {
if [[ -x $CODEX_BIN_PATH ]]; then
return 0
fi
local tar_transform="s/${SLOPTRAP_CODEX_BIN_NAME}/${SLOPTRAP_CODEX_BIN_NAME}/"
local target_asset
target_asset=$(detect_opencode_asset_name)
local download_dir
download_dir=$(create_temp_dir "opencode")
local tmp_archive="$download_dir/${SLOPTRAP_CODEX_BIN_NAME}.tar.gz"
local tmp_archive="$download_dir/$target_asset"
local opencode_url="https://github.com/anomalyco/opencode/releases/latest/download/${target_asset}"
if $DRY_RUN; then
print_command curl -Lso "$tmp_archive" "https://github.com/anomalyco/opencode/releases/latest/download/${SLOPTRAP_CODEX_BIN_NAME}.tar.gz"
print_command curl -Lso "$tmp_archive" "$opencode_url"
print_command sha256sum -c -
print_command tar -xzf "$tmp_archive" --transform="$tar_transform" -C "$SLOPTRAP_BUILD_CONTEXT"
print_command tar -xzf "$tmp_archive" --transform="s/opencode/$SLOPTRAP_CODEX_BIN_NAME/" -C "$SLOPTRAP_BUILD_CONTEXT"
print_command chmod 0755 "$CODEX_BIN_PATH"
return 0
fi
if ! curl -Lso "$tmp_archive" "https://github.com/anomalyco/opencode/releases/latest/download/${SLOPTRAP_CODEX_BIN_NAME}.tar.gz"; then
if ! curl -Lso "$tmp_archive" "$opencode_url"; then
rm -rf "$download_dir"
error "failed to download opencode binary from https://github.com/anomalyco/opencode/releases/latest/download/${SLOPTRAP_CODEX_BIN_NAME}.tar.gz"
error "failed to download opencode binary from '$opencode_url'"
fi
local expected_digest
expected_digest=$(fetch_latest_opencode_digest)
if ! printf "%s %s\n" "$expected_digest" "$tmp_archive" | sha256sum -c - >/dev/null 2>&1; then
rm -rf "$download_dir" "$CODEX_BIN_PATH"
error "checksum verification failed for ${SLOPTRAP_CODEX_BIN_NAME}"
error "checksum verification failed for $SLOPTRAP_CODEX_BIN_NAME"
fi
if ! tar -xzf "$tmp_archive" --transform="$tar_transform" -C "$SLOPTRAP_BUILD_CONTEXT"; then
if ! tar -xzf "$tmp_archive" --transform="s/opencode/$SLOPTRAP_CODEX_BIN_NAME/" -C "$SLOPTRAP_BUILD_CONTEXT"; then
rm -rf "$download_dir"
error "failed to extract opencode binary"
fi
@@ -1345,7 +1532,13 @@ normalize_package_list() {
[[ -z $raw ]] && return 0
local -a tokens=()
read -r -a tokens <<< "$raw"
printf '%s' "${tokens[*]}"
local -a normalized=()
local token
for token in "${tokens[@]}"; do
# Expand package aliases
normalized+=("$(expand_package_alias "$token")")
done
printf '%s' "${normalized[*]}"
}
targets_need_build() {
@@ -1364,6 +1557,30 @@ targets_need_build() {
return 1
}
normalize_local_server_url() {
local url=$1
local replacement_host=$2
if [[ $url =~ ^([A-Za-z][A-Za-z0-9+.-]*://)(localhost|127\.0\.0\.1|\[::1\])([:/?#].*)?$ ]]; then
printf '%s%s%s' "${BASH_REMATCH[1]}" "$replacement_host" "${BASH_REMATCH[3]}"
return 0
fi
printf '%s' "$url"
}
ensure_host_loopback_network_access() {
if [[ $SLOPTRAP_NETWORK_NAME != slirp4netns* ]]; then
return 0
fi
if [[ $SLOPTRAP_NETWORK_NAME == *allow_host_loopback=* ]]; then
return 0
fi
if [[ $SLOPTRAP_NETWORK_NAME == "slirp4netns" ]]; then
SLOPTRAP_NETWORK_NAME="slirp4netns:allow_host_loopback=true"
else
SLOPTRAP_NETWORK_NAME="${SLOPTRAP_NETWORK_NAME},allow_host_loopback=true"
fi
}
prepare_container_runtime() {
resolve_container_workdir
SLOPTRAP_PACKAGES_EXTRA_RESOLVED=$PACKAGES_EXTRA
@@ -1390,7 +1607,7 @@ prepare_container_runtime() {
SLOPTRAP_DOCKERFILE_SOURCE=""
fi
SLOPTRAP_PACKAGES_BASE=$(get_env_default "SLOPTRAP_PACKAGES" "curl bash ca-certificates libstdc++6 git ripgrep xxd file procps util-linux")
SLOPTRAP_PACKAGES_BASE=$(get_env_default "SLOPTRAP_PACKAGES" "curl bash ca-certificates libstdc++6 wget git ripgrep xxd file procps util-linux")
validate_package_list "SLOPTRAP_PACKAGES" "$SLOPTRAP_PACKAGES_BASE" "SLOPTRAP_PACKAGES"
local default_codex_archive
default_codex_archive=$(detect_codex_archive_name)
@@ -1413,8 +1630,15 @@ prepare_container_runtime() {
SLOPTRAP_CODEX_ARCHIVE=$inferred_archive
fi
fi
SLOPTRAP_CODEX_BIN_NAME=$(get_env_default "SLOPTRAP_CODEX_BIN" "codex")
if [[ "$BACKEND" == "opencode" ]]; then
SLOPTRAP_CODEX_BIN_NAME=$(get_env_default "SLOPTRAP_CODEX_BIN" "opencode")
else
SLOPTRAP_CODEX_BIN_NAME=$(get_env_default "SLOPTRAP_CODEX_BIN" "codex")
fi
SLOPTRAP_CODEX_HOME_CONT=$(get_env_default "SLOPTRAP_CODEX_HOME_CONT" "/codex")
if [[ "$BACKEND" == "opencode" ]]; then
OPENCODE_CONFIG_CONT="$SLOPTRAP_CODEX_HOME_CONT/config/opencode/opencode.json"
fi
SLOPTRAP_CODEX_UID=$(get_env_default "SLOPTRAP_CODEX_UID" "1337")
SLOPTRAP_CODEX_GID=$(get_env_default "SLOPTRAP_CODEX_GID" "1337")
SLOPTRAP_SECURITY_OPTS_EXTRA=$(get_env_default "SLOPTRAP_SECURITY_OPTS_EXTRA" "")
@@ -1432,6 +1656,9 @@ prepare_container_runtime() {
elif [[ $SLOPTRAP_NETWORK_NAME == "host" ]]; then
error "$MANIFEST_PATH: host networking requires allow_host_network=true"
fi
if [[ "$BACKEND" == "opencode" && $ALLOW_HOST_NETWORK == false ]]; then
ensure_host_loopback_network_access
fi
SLOPTRAP_LIMITS_PID=$(get_env_default "SLOPTRAP_LIMITS_PID" "1024")
SLOPTRAP_LIMITS_RAM=$(get_env_default "SLOPTRAP_LIMITS_RAM" "1024m")
SLOPTRAP_LIMITS_SWP=$(get_env_default "SLOPTRAP_LIMITS_SWP" "1024m")
@@ -1510,6 +1737,7 @@ prepare_container_runtime() {
if [[ "$BACKEND" == "opencode" ]]; then
env_args+=(
-e "OPENCODE_HOME=$SLOPTRAP_CODEX_HOME_CONT"
-e "OPENCODE_CONFIG=$OPENCODE_CONFIG_CONT"
-e "OPENCODE_SERVER=$OPENCODE_SERVER"
-e "OPENCODE_MODEL=$OPENCODE_MODEL"
)
@@ -1527,6 +1755,16 @@ prepare_container_runtime() {
env_args+=(-e "SLOPTRAP_HOST_USER=$user")
fi
local -a network_opts=(--network "$SLOPTRAP_NETWORK_NAME")
if [[ $ALLOW_HOST_NETWORK == false ]]; then
SLOPTRAP_HOST_ALIAS="sloptrap.host"
network_opts+=(--add-host "$SLOPTRAP_HOST_ALIAS:host-gateway")
env_args+=(-e "SLOPTRAP_HOST_ALIAS=$SLOPTRAP_HOST_ALIAS")
if [[ "$BACKEND" == "opencode" ]]; then
OPENCODE_SERVER=$(normalize_local_server_url "$OPENCODE_SERVER" "$SLOPTRAP_HOST_ALIAS")
fi
else
SLOPTRAP_HOST_ALIAS=""
fi
local -a user_opts=("--user" "$uid:$gid")
if [[ $CONTAINER_ENGINE == "podman" ]]; then
user_opts=(--userns="keep-id:uid=$uid,gid=$gid" "${user_opts[@]}")
@@ -1560,55 +1798,57 @@ prepare_container_runtime() {
}
build_image() {
if [[ "$BACKEND" == "opencode" ]]; then
ensure_opencode_binary
else
ensure_codex_binary
fi
if [[ $SKIP_BUILD_BANNER != true ]]; then
print_banner
fi
print_manifest_summary
local extra_packages_arg
extra_packages_arg=$(normalize_package_list "$SLOPTRAP_PACKAGES_EXTRA_RESOLVED")
if ! $DRY_RUN; then
status_line "Building %s\n" "$SLOPTRAP_IMAGE_NAME"
fi
local -a cmd=(
"$CONTAINER_ENGINE" build --quiet
-t "$SLOPTRAP_IMAGE_NAME"
-f "$SLOPTRAP_DOCKERFILE_PATH"
--network "$SLOPTRAP_NETWORK_NAME"
--label "$SLOPTRAP_IMAGE_LABEL"
--build-arg "BASE_PACKAGES=$SLOPTRAP_PACKAGES_BASE"
--build-arg "CODEX_BIN=$SLOPTRAP_CODEX_BIN_NAME"
--build-arg "CODEX_UID=$SLOPTRAP_CODEX_UID"
--build-arg "CODEX_GID=$SLOPTRAP_CODEX_GID"
)
if [[ -n $extra_packages_arg ]]; then
cmd+=(--build-arg "EXTRA_PACKAGES=$extra_packages_arg")
fi
cmd+=("$SLOPTRAP_BUILD_CONTEXT")
if $DRY_RUN; then
run_or_print "${cmd[@]}"
return
fi
prepare_build_context
if [[ "$BACKEND" == "opencode" ]]; then
ensure_opencode_binary
else
ensure_codex_binary
fi
if [[ $SKIP_BUILD_BANNER != true ]]; then
print_banner
fi
print_manifest_summary
local extra_packages_arg
extra_packages_arg=$(normalize_package_list "$SLOPTRAP_PACKAGES_EXTRA_RESOLVED")
if ! $DRY_RUN; then
status_line "Building %s\n" "$SLOPTRAP_IMAGE_NAME"
fi
local -a cmd=(
"$CONTAINER_ENGINE" build --quiet
-t "$SLOPTRAP_IMAGE_NAME"
-f "$SLOPTRAP_DOCKERFILE_PATH"
--network "$SLOPTRAP_NETWORK_NAME"
--label "$SLOPTRAP_IMAGE_LABEL"
--build-arg "BASE_PACKAGES=$SLOPTRAP_PACKAGES_BASE"
--build-arg "CODEX_BIN=$SLOPTRAP_CODEX_BIN_NAME"
--build-arg "CODEX_UID=$SLOPTRAP_CODEX_UID"
--build-arg "CODEX_GID=$SLOPTRAP_CODEX_GID"
)
if [[ -n $extra_packages_arg ]]; then
cmd+=(--build-arg "EXTRA_PACKAGES=$extra_packages_arg")
fi
cmd+=("$SLOPTRAP_BUILD_CONTEXT")
if $DRY_RUN; then
run_or_print "${cmd[@]}"
return
fi
local build_output
if ! build_output=$("${cmd[@]}"); then
return 1
fi
build_output=$(trim "$build_output")
if [[ -n $build_output ]]; then
comment_line "Image %s\n" "$build_output"
fi
local build_output
if ! build_output=$("${cmd[@]}"); then
return 1
fi
build_output=$(trim "$build_output")
if [[ -n $build_output ]]; then
comment_line "Image %s\n" "$build_output"
fi
}
rebuild_image() {
if [[ "$BACKEND" == "opencode" ]]; then
ensure_opencode_binary
else
ensure_codex_binary
prepare_build_context
if [[ "$BACKEND" == "opencode" ]]; then
ensure_opencode_binary
else
ensure_codex_binary
fi
if [[ $SKIP_BUILD_BANNER != true ]]; then
print_banner
@@ -1720,15 +1960,14 @@ run_codex_command() {
local -a auth_mount=()
if [[ "$BACKEND" == "opencode" ]]; then
ensure_opencode_storage_paths
ensure_opencode_config
else
ensure_codex_storage_paths
fi
append_auth_mount_arg false auth_mount
local -a cmd=("${BASE_CONTAINER_CMD[@]}" "${auth_mount[@]}" "${source_args[@]}" "$BACKEND")
local -a cmd=("${BASE_CONTAINER_CMD[@]}" "${auth_mount[@]}" "${source_args[@]}")
if [[ "$BACKEND" == "opencode" ]]; then
cmd+=("--server" "$OPENCODE_SERVER")
cmd+=("--model" "$OPENCODE_MODEL")
cmd+=("--sandbox" "workspace-write")
true
else
if [[ ${#CODEX_ARGS_ARRAY[@]} -gt 0 ]]; then
cmd+=("${CODEX_ARGS_ARRAY[@]}")
@@ -1744,9 +1983,13 @@ run_codex() {
if ! $DRY_RUN; then
status_line "Running %s\n" "$SLOPTRAP_IMAGE_NAME"
fi
local runtime_prompt
runtime_prompt=$(build_runtime_context_prompt)
run_codex_command "$runtime_prompt"
if [[ "$BACKEND" == "opencode" ]]; then
run_codex_command
else
local runtime_prompt
runtime_prompt=$(build_runtime_context_prompt)
run_codex_command "$runtime_prompt"
fi
}
run_login_target() {
@@ -1757,19 +2000,23 @@ run_login_target() {
status_line "Login %s\n" "$SLOPTRAP_IMAGE_NAME"
fi
append_auth_mount_arg true auth_mount
local -a cmd=("${BASE_CONTAINER_CMD[@]}" "${auth_mount[@]}" "${source_args[@]}" "codex" login)
local -a cmd=("${BASE_CONTAINER_CMD[@]}" "${auth_mount[@]}" "${source_args[@]}" login)
run_runtime_container_cmd "${cmd[@]}"
}
run_shell_target() {
ensure_codex_storage_paths
if [[ "$BACKEND" == "opencode" ]]; then
ensure_opencode_storage_paths
else
ensure_codex_storage_paths
fi
local -a source_args=("$SLOPTRAP_IMAGE_NAME")
local -a auth_mount=()
if ! $DRY_RUN; then
status_line "Shell %s\n" "$SLOPTRAP_IMAGE_NAME"
fi
append_auth_mount_arg false auth_mount
local -a cmd=("${BASE_CONTAINER_CMD[@]}" "${auth_mount[@]}" "${source_args[@]}" /bin/bash)
local -a cmd=("${BASE_CONTAINER_CMD[@]}" --entrypoint /bin/bash "${auth_mount[@]}" "${source_args[@]}")
run_runtime_container_cmd "${cmd[@]}"
}
@@ -1778,7 +2025,11 @@ run_resume_target() {
if ! $DRY_RUN; then
status_line "Resume %s (%s)\n" "$SLOPTRAP_IMAGE_NAME" "$session_id"
fi
run_codex_command resume "$session_id"
if [[ "$BACKEND" == "opencode" ]]; then
run_codex_command --session "$session_id"
else
run_codex_command resume "$session_id"
fi
}
process_resume_target() {
@@ -1930,10 +2181,6 @@ ensure_ignore_helper_root
IGNORE_STUB_BASE="$IGNORE_HELPER_ROOT/session-${BASHPID:-$$}"
resolve_sloptrap_ignore "$CODE_DIR"
resolve_container_workdir
NEED_LOGIN=false
if [[ ! -s "$CODEX_AUTH_FILE_HOST" ]]; then
NEED_LOGIN=true
fi
TARGETS=("${TARGETS_INPUT[@]}")
if [[ ${#TARGETS[@]} -eq 0 ]]; then
@@ -1942,7 +2189,7 @@ fi
DEFAULT_TARGETS=("${TARGETS[@]}")
PACKAGES_EXTRA=${MANIFEST[packages_extra]-}
PACKAGES_EXTRA=$(normalize_package_list "${MANIFEST[packages_extra]:-}")
if [[ -n ${MANIFEST[allow_host_network]-} ]]; then
case "${MANIFEST[allow_host_network],,}" in
1|true|yes)
@@ -1982,22 +2229,29 @@ select_backend() {
opencode) BACKEND="opencode" ;;
codex|*) BACKEND="codex" ;;
esac
if [[ "$BACKEND" == "opencode" && ! -x "$(command -v opencode)" ]]; then
error "opencode CLI not found; install from https://opencode.ai"
fi
}
select_backend
if [[ "$BACKEND" == "opencode" ]]; then
OPENCODE_SERVER="${MANIFEST[opencode_server]:-http://localhost:11434}"
OPENCODE_MODEL="${MANIFEST[opencode_model]:-llama3}"
OPENCODE_SERVER="${MANIFEST[opencode_server]:-http://localhost:8080}"
OPENCODE_MODEL="${MANIFEST[opencode_model]:-bartowski/Qwen_Qwen3.5-9B-GGUF:Q8_0}"
OPENCODE_CONTEXT="${MANIFEST[opencode_context]:-256K}"
OPENCODE_STATE_HOME_HOST="$CODEX_STATE_HOME_HOST/opencode"
OPENCODE_CONFIG_HOST="$CODEX_STATE_HOME_HOST/config/opencode/opencode.json"
OPENCODE_CONFIG_CONT=""
else
OPENCODE_SERVER=""
OPENCODE_MODEL=""
OPENCODE_CONTEXT=""
OPENCODE_STATE_HOME_HOST=""
OPENCODE_CONFIG_HOST=""
OPENCODE_CONFIG_CONT=""
fi
NEED_LOGIN=false
if [[ "$BACKEND" != "opencode" && ! -s "$CODEX_AUTH_FILE_HOST" ]]; then
NEED_LOGIN=true
fi
CONTAINER_ENGINE="$(detect_container_engine)"

View File

@@ -189,6 +189,10 @@ EOF
cat >"$STUB_BIN/jq" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
if [[ ${1-} == "-n" ]]; then
shift
exec /usr/bin/jq -n "$@"
fi
while [[ $# -gt 0 ]]; do
case "$1" in
-r)
@@ -454,12 +458,28 @@ run_resume_omits_runtime_context() {
if grep -q -- "You are running inside sloptrap" "$STUB_LOG"; then
record_failure "resume_omits_runtime_context: resume should not receive startup prompt"
fi
if ! grep -q -- "codex --sandbox danger-full-access --ask-for-approval never resume $session_id" "$STUB_LOG"; then
if ! grep -q -- "--sandbox danger-full-access --ask-for-approval never resume $session_id" "$STUB_LOG"; then
record_failure "resume_omits_runtime_context: resume invocation missing"
fi
teardown_stub_env
}
run_shell_target_uses_entrypoint() {
local scenario_dir="$TEST_ROOT/opencode_localhost"
printf '==> shell_target_uses_entrypoint\n'
setup_stub_env
if ! PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
"$SLOPTRAP_BIN" "$scenario_dir" shell </dev/null >/dev/null 2>&1; then
record_failure "shell_target_uses_entrypoint: shell target failed"
teardown_stub_env
return
fi
if ! grep -q -- "--entrypoint /bin/bash" "$STUB_LOG"; then
record_failure "shell_target_uses_entrypoint: missing entrypoint override"
fi
teardown_stub_env
}
run_auth_file_mount() {
local scenario_dir
scenario_dir=$(cd "$TEST_ROOT/resume_target" && pwd -P)
@@ -732,6 +752,125 @@ run_wizard_build_trigger() {
fi
}
run_opencode_build_downloads_release_cli() {
local scenario_dir="$TEST_ROOT/opencode_build"
printf '==> opencode_build_downloads_release_cli\n'
setup_stub_env
mkdir -p "$scenario_dir"
cat > "$scenario_dir/.sloptrap" <<'EOF'
name=opencode-build
packages_extra=
agent=opencode
allow_host_network=false
EOF
if ! env PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
"$SLOPTRAP_BIN" "$scenario_dir" build >/dev/null 2>&1; then
record_failure "opencode_build_downloads_release_cli: build failed"
teardown_stub_env
return
fi
if ! grep -q -- "FAKE PODMAN: build " "$STUB_LOG"; then
record_failure "opencode_build_downloads_release_cli: build not invoked"
fi
teardown_stub_env
}
run_opencode_localhost_rewrite() {
local scenario_dir="$TEST_ROOT/opencode_localhost"
printf '==> opencode_localhost_rewrite\n'
setup_stub_env
mkdir -p "$scenario_dir"
cat > "$scenario_dir/.sloptrap" <<'EOF'
name=opencode-localhost
packages_extra=
agent=opencode
opencode_server=http://localhost:8080
allow_host_network=false
EOF
cat > "$STUB_BIN/slirp4netns" <<'EOF'
#!/usr/bin/env bash
exit 0
EOF
chmod +x "$STUB_BIN/slirp4netns"
if ! env PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
"$SLOPTRAP_BIN" "$scenario_dir" </dev/null >/dev/null 2>&1; then
record_failure "opencode_localhost_rewrite: run failed"
teardown_stub_env
return
fi
local config_path
config_path=$(find "$STUB_HOME" -path '*/config/opencode/opencode.json' | head -n 1 || true)
if [[ -z $config_path || ! -f $config_path ]]; then
record_failure "opencode_localhost_rewrite: opencode config not written"
teardown_stub_env
return
fi
if ! grep -q -- "--network slirp4netns:allow_host_loopback=true" "$STUB_LOG"; then
record_failure "opencode_localhost_rewrite: slirp host loopback not enabled"
fi
if ! grep -q -- "--add-host sloptrap.host:host-gateway" "$STUB_LOG"; then
record_failure "opencode_localhost_rewrite: host alias not injected"
fi
if ! grep -q -- "OPENCODE_CONFIG=/codex/config/opencode/opencode.json" "$STUB_LOG"; then
record_failure "opencode_localhost_rewrite: opencode config path not exported"
fi
if ! grep -q -- '"baseURL": "http://sloptrap.host:8080/v1"' "$config_path"; then
record_failure "opencode_localhost_rewrite: localhost server not rewritten in config"
fi
if [[ $(jq -r '.provider["llama.cpp"].name' "$config_path") != "llama-server (local)" ]]; then
record_failure "opencode_localhost_rewrite: provider name not merged into config"
fi
if [[ $(jq -r '.model' "$config_path") != "llama.cpp/bartowski/Qwen_Qwen3.5-9B-GGUF:Q8_0 - 262144" ]]; then
record_failure "opencode_localhost_rewrite: opencode model not written to config"
fi
if [[ $(jq -r '.enabled_providers[0]' "$config_path") != "llama.cpp" ]]; then
record_failure "opencode_localhost_rewrite: enabled_providers not merged into config"
fi
if [[ $(jq -r '.provider["llama.cpp"].models["bartowski/Qwen_Qwen3.5-9B-GGUF:Q8_0 - 262144"].limit.context' "$config_path") != "262144" ]]; then
record_failure "opencode_localhost_rewrite: opencode context not written to config"
fi
if grep -q -- "--server " "$STUB_LOG"; then
record_failure "opencode_localhost_rewrite: deprecated --server flag should not be passed"
fi
if grep -q -- "--sandbox workspace-write" "$STUB_LOG"; then
record_failure "opencode_localhost_rewrite: codex sandbox args leaked into opencode run"
fi
if grep -q -- "You are running inside sloptrap" "$STUB_LOG"; then
record_failure "opencode_localhost_rewrite: codex-style startup prompt should not be passed to opencode"
fi
teardown_stub_env
}
run_opencode_print_config_runtime_flags() {
local scenario_dir="$TEST_ROOT/opencode_print_config"
printf '==> opencode_print_config_runtime_flags\n'
setup_stub_env
mkdir -p "$scenario_dir"
cat > "$scenario_dir/.sloptrap" <<'EOF'
name=opencode-print-config
packages_extra=
agent=opencode
allow_host_network=false
EOF
local output
if ! output=$(env PATH="$STUB_BIN:$PATH" HOME="$STUB_HOME" FAKE_PODMAN_LOG="$STUB_LOG" FAKE_PODMAN_INSPECT_FAIL=1 \
"$SLOPTRAP_BIN" --print-config "$scenario_dir" 2>/dev/null); then
record_failure "opencode_print_config_runtime_flags: print-config failed"
teardown_stub_env
return
fi
if ! grep -q 'runtime_flags=' <<<"$output"; then
record_failure "opencode_print_config_runtime_flags: runtime_flags line missing for opencode"
fi
if ! grep -q 'opencode_config=' <<<"$output"; then
record_failure "opencode_print_config_runtime_flags: opencode config path missing"
fi
if grep -q -- '--sandbox danger-full-access --ask-for-approval never' <<<"$output"; then
record_failure "opencode_print_config_runtime_flags: codex runtime flags leaked into opencode config"
fi
teardown_stub_env
}
run_symlink_escape
run_manifest_injection
run_helper_symlink
@@ -740,6 +879,7 @@ run_resume_target
run_runtime_context_prompt
run_sh_reexec
run_resume_omits_runtime_context
run_shell_target_uses_entrypoint
run_auth_file_mount
run_codex_home_override
run_project_state_isolation
@@ -749,6 +889,9 @@ run_root_directory_project
run_wizard_create_manifest
run_wizard_existing_defaults
run_wizard_build_trigger
run_opencode_build_downloads_release_cli
run_opencode_localhost_rewrite
run_opencode_print_config_runtime_flags
if [[ ${#failures[@]} -gt 0 ]]; then
printf '\nTest failures:\n'

View File

@@ -1,4 +1,4 @@
name=wizard_build
packages_extra=
capabilities=
agent=codex
allow_host_network=false

View File

@@ -1,4 +1,4 @@
name=wizard_empty
packages_extra=
capabilities=
agent=codex
allow_host_network=false

View File

@@ -1,4 +1,4 @@
name=custom-wizard
packages_extra=make git
capabilities=apt-install
agent=codex
allow_host_network=true