Opencode improvements
This commit is contained in:
512
sloptrap
512
sloptrap
@@ -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)"
|
||||
|
||||
Reference in New Issue
Block a user