#!/usr/bin/env bash
#
# agent-pod — run an AI coding-agent CLI inside a throwaway Docker container
# that can only see the current project directory.
#
# Supported agents: claude, codex, opencode, cursor (built-in), plus any custom
# agents you define in ~/.agent-pod/.agent-pod.env.
#
# Only $PWD is mounted in. Per-agent auth/history is persisted on the host
# under ~/.agent-pod/<agent>. Everything else (your home dir, SSH keys, other
# projects) stays invisible to the agent.
#
# NOTE: outbound network access inside the container is NOT restricted.

set -eo pipefail

CONTAINER_HOME="/home/agent-pod"
CONTAINER_NPM_PREFIX="$CONTAINER_HOME/.npm-global"
CONTAINER_PATH="$CONTAINER_NPM_PREFIX/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
MANAGED_LABEL="com.agent-pod.managed=true"

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do
  SOURCE_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
  LINK_TARGET="$(readlink "$SOURCE")"
  case "$LINK_TARGET" in
    /*) SOURCE="$LINK_TARGET";;
    *) SOURCE="$SOURCE_DIR/$LINK_TARGET";;
  esac
done
LAUNCHER_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"

# Where AgentPod keeps its own per-user state and config. This lives with the
# CLI (one stable location per user), not in whatever directory you happen to
# run from. AGENT_POD_HOME must be a real environment variable to relocate it,
# since we need it before any env-file is loaded.
CONFIG_HOME="${AGENT_POD_HOME:-$HOME/.agent-pod}"
CONFIG_ENV_FILE="$CONFIG_HOME/.agent-pod.env"

# ---- pretty output (auto-disabled when stderr is not a TTY) ---------------
if [ -t 2 ]; then
  C_INFO=$'\033[34m'; C_OK=$'\033[32m'; C_ERR=$'\033[31m'; C_DIM=$'\033[2m'; C_OFF=$'\033[0m'
else
  C_INFO=""; C_OK=""; C_ERR=""; C_DIM=""; C_OFF=""
fi
info() { printf '%s%s%s\n' "$C_INFO" "$*" "$C_OFF" >&2; }
ok()   { printf '%s%s%s\n' "$C_OK"   "$*" "$C_OFF" >&2; }
warn() { printf '%s%s%s\n' "$C_DIM"  "$*" "$C_OFF" >&2; }
die()  { printf '%s%s%s\n' "$C_ERR"  "$*" "$C_OFF" >&2; exit 1; }

discover_env_file() {
  # Resolution order, most to least specific:
  #   1. AGENT_POD_ENV_FILE        explicit override
  #   2. ~/.agent-pod/.agent-pod.env  central config that lives with the CLI
  #   3. cwd .agent-pod.env / agent-pod.env   legacy project-local files
  #   4. launcher-dir files                    legacy source-checkout files
  if [ -n "${AGENT_POD_ENV_FILE:-}" ]; then
    printf '%s\n' "$AGENT_POD_ENV_FILE"
  elif [ -f "$CONFIG_ENV_FILE" ]; then
    printf '%s\n' "$CONFIG_ENV_FILE"
  elif [ -f ".agent-pod.env" ]; then
    printf '%s\n' ".agent-pod.env"
  elif [ -f "agent-pod.env" ]; then
    printf '%s\n' "agent-pod.env"
  elif [ -f "$LAUNCHER_DIR/.agent-pod.env" ]; then
    printf '%s\n' "$LAUNCHER_DIR/.agent-pod.env"
  elif [ -f "$LAUNCHER_DIR/agent-pod.env" ]; then
    printf '%s\n' "$LAUNCHER_DIR/agent-pod.env"
  fi
}

dotenv_get() {
  key="$1"
  file="${2:-}"
  [ -n "$file" ] && [ -f "$file" ] || return 1
  awk -v key="$key" '
    /^[[:space:]]*(#|$)/ { next }
    {
      line=$0
      sub(/^[[:space:]]*export[[:space:]]+/, "", line)
      split(line, parts, "=")
      k=parts[1]
      gsub(/^[[:space:]]+|[[:space:]]+$/, "", k)
      if (k == key) {
        sub(/^[^=]*=/, "", line)
        gsub(/^[[:space:]]+|[[:space:]]+$/, "", line)
        if (line ~ /^".*"$/ || line ~ /^'\''.*'\''$/) {
          line=substr(line, 2, length(line)-2)
        }
        print line
        found=1
      }
    }
    END { exit found ? 0 : 1 }
  ' "$file"
}

config_value() {
  key="$1"
  default_value="${2:-}"
  if [ -n "${!key+x}" ]; then
    printf '%s\n' "${!key}"
  elif value="$(dotenv_get "$key" "$ENV_FILE" 2>/dev/null)"; then
    printf '%s\n' "$value"
  else
    printf '%s\n' "$default_value"
  fi
}

truthy() {
  case "$1" in
    1|true|TRUE|yes|YES|y|Y|on|ON) return 0;;
    *) return 1;;
  esac
}

nonnegative_integer() {
  case "$1" in
    ""|*[!0-9]*) return 1;;
    *) return 0;;
  esac
}

builtin_agents() {
  printf '%s\n' "claude codex opencode cursor agent"
}

# Backward-compatible alias used in prompts and help text.
available_agents() {
  builtin_agents
}

valid_agent_id() {
  case "$1" in
    [a-z][a-z0-9_-]*) return 0;;
    *) return 1;;
  esac
}

is_builtin_agent() {
  case "$1" in
    claude|codex|opencode|cursor|agent) return 0;;
    *) return 1;;
  esac
}

agent_env_key() {
  printf '%s' "$1" | tr '[:lower:]-' '[:upper:]_'
}

agent_label() {
  local agent="$1"
  case "$agent" in
    claude) printf '%s\n' "Claude Code";;
    codex) printf '%s\n' "OpenAI Codex";;
    opencode) printf '%s\n' "OpenCode";;
    cursor) printf '%s\n' "Cursor Agent (agent alias)";;
    agent) printf '%s\n' "Cursor Agent";;
    *) printf '%s\n' "$agent";;
  esac
}

custom_agent_bin() {
  local agent="$1"
  local key
  key="$(agent_env_key "$agent")"
  config_value "AGENT_POD_${key}_BIN" ""
}

custom_agent_flags() {
  local agent="$1"
  local key
  key="$(agent_env_key "$agent")"
  config_value "AGENT_POD_${key}_FLAGS" ""
}

custom_agent_npm() {
  local agent="$1"
  local key
  key="$(agent_env_key "$agent")"
  config_value "AGENT_POD_${key}_NPM" ""
}

write_custom_agent_config() {
  local env_file="$1"
  local agent="$2"
  local bin="$3"
  local flags="$4"
  local npm_pkg="$5"
  local key
  key="$(agent_env_key "$agent")"
  upsert_env_var "$env_file" "AGENT_POD_${key}_BIN" "$bin"
  if [ -n "$flags" ]; then
    upsert_env_var "$env_file" "AGENT_POD_${key}_FLAGS" "$flags"
  fi
  if [ -n "$npm_pkg" ]; then
    upsert_env_var "$env_file" "AGENT_POD_${key}_NPM" "$npm_pkg"
  fi
}

# Sets global BIN and YOLO for the requested agent.
resolve_agent_launch() {
  local agent="$1"
  YOLO=()

  case "$agent" in
    claude)   BIN="claude";       YOLO=(--dangerously-skip-permissions);;
    codex)    BIN="codex";        YOLO=(--dangerously-bypass-approvals-and-sandbox);;
    opencode) BIN="opencode";     YOLO=();;
    cursor)   BIN="agent";        YOLO=(--force);;
    agent)    BIN="agent";        YOLO=(--force);;
    shell)    BIN="bash";         YOLO=(); return 0;;
    *)
      key="$(agent_env_key "$agent")"
      BIN="$(custom_agent_bin "$agent")"
      [ -n "$BIN" ] || die "Custom agent '$agent' has no AGENT_POD_${key}_BIN set. Add it with: agent-pod agent-add $agent --bin <cli>"
      flags="$(custom_agent_flags "$agent")"
      if [ -n "$flags" ]; then
        # shellcheck disable=SC2206
        YOLO=($flags)
      fi
      ;;
  esac

  [ "$(config_value AGENT_POD_YOLO 1)" = "0" ] && YOLO=()
  case "$agent" in
    claude) truthy "$(config_value AGENT_POD_CLAUDE_AUTO_FLAGS 1)" || YOLO=();;
    codex) truthy "$(config_value AGENT_POD_CODEX_AUTO_FLAGS 1)" || YOLO=();;
    cursor|agent) truthy "$(config_value AGENT_POD_CURSOR_AUTO_FLAGS 1)" || YOLO=();;
  esac
}

normalize_agents() {
  local raw normalized agent
  raw="$*"
  [ -n "$raw" ] || return 1
  normalized=""
  for agent in $(printf '%s\n' "$raw" | tr '[:upper:],;' '[:lower:]  '); do
    case "$agent" in
      all) printf '%s\n' "$(builtin_agents)"; return 0;;
      overlord) continue;;
      *)
        valid_agent_id "$agent" || return 1
        ;;
    esac
    case " $normalized " in
      *" $agent "*) ;;
      *) normalized="${normalized:+$normalized }$agent";;
    esac
  done
  [ -n "$normalized" ] || return 1
  printf '%s\n' "$normalized"
}

agents_csv() {
  printf '%s\n' "$*" | tr ' ' ','
}

agent_in_list() {
  local needle agent
  needle="$1"; shift
  for agent in "$@"; do
    [ "$agent" = "$needle" ] && return 0
  done
  return 1
}

supported_agents() {
  normalize_agents "$(config_value AGENT_POD_AGENTS "$(builtin_agents)")" \
    || die "Invalid AGENT_POD_AGENTS value. Use built-in agents ($(builtin_agents)) or custom agent IDs (lowercase letters, numbers, hyphens, underscores)."
}

upsert_env_var() {
  local file="$1"
  local key="$2"
  local value="$3"
  local tmp
  tmp="${file}.tmp.$$"
  if [ -f "$file" ] && grep -Eq "^[[:space:]]*(export[[:space:]]+)?${key}=" "$file"; then
    awk -v key="$key" -v value="$value" '
      BEGIN { done=0 }
      $0 ~ "^[[:space:]]*(export[[:space:]]+)?" key "=" {
        if (!done) {
          print key "=" value
          done=1
        }
        next
      }
      { print }
    ' "$file" > "$tmp"
  else
    [ -f "$file" ] && cp "$file" "$tmp" || : > "$tmp"
    printf '%s=%s\n' "$key" "$value" >> "$tmp"
  fi
  mv "$tmp" "$file"
}

prompt_yes_no() {
  prompt="$1"
  default="$2"
  [ -t 0 ] || { [ "$default" = "yes" ]; return; }
  if [ "$default" = "yes" ]; then suffix="[Y/n]"; else suffix="[y/N]"; fi
  while true; do
    printf '%s %s ' "$prompt" "$suffix" >&2
    read -r reply
    case "$reply" in
      "") [ "$default" = "yes" ] && return 0 || return 1;;
      y|Y|yes|YES) return 0;;
      n|N|no|NO) return 1;;
      *) warn "Please answer yes or no.";;
    esac
  done
}

prompt_text() {
  prompt="$1"
  [ -t 0 ] || return 1
  printf '%s ' "$prompt" >&2
  read -r reply
  printf '%s\n' "$reply"
}

prompt_agent_selection() {
  [ -t 0 ] || { builtin_agents; return 0; }
  if prompt_yes_no "Support all built-in agents? ($(builtin_agents))" "yes"; then
    builtin_agents
    return 0
  fi
  while true; do
    reply="$(prompt_text "Agents to support (space/comma separated; built-in: $(builtin_agents), or custom IDs like gemini):" || true)"
    if normalized="$(normalize_agents "$reply" 2>/dev/null)"; then
      printf '%s\n' "$normalized"
      return 0
    fi
    warn "Use built-in agents ($(builtin_agents)) or custom IDs (lowercase, e.g. gemini, my-harness)."
  done
}

prompt_custom_agents() {
  env_file="$1"
  current_agents="$2"
  next_agents="$current_agents"

  [ -t 0 ] || return 0
  if ! prompt_yes_no "Add custom agents or harnesses? (any CLI you want to run in the sandbox)" "no"; then
    printf '%s\n' "$next_agents"
    return 0
  fi

  if ! grep -q "Custom agent definitions" "$env_file" 2>/dev/null; then
    cat >> "$env_file" <<'EOF'

# Custom agent definitions (one block per agent).
# AGENT_POD_<ID>_BIN   CLI binary to run inside the container (required)
# AGENT_POD_<ID>_FLAGS space-separated default flags (optional)
# AGENT_POD_<ID>_NPM   npm package to bake into the image (optional)
EOF
  fi

  while true; do
    name="$(prompt_text "Custom agent name (blank to finish, e.g. gemini or my-harness):" || true)"
    [ -n "$name" ] || break
    name="$(printf '%s' "$name" | tr '[:upper:]' '[:lower:]')"
    if ! valid_agent_id "$name"; then
      warn "Invalid name '$name'. Use lowercase letters, numbers, hyphens, or underscores."
      continue
    fi
    if is_builtin_agent "$name"; then
      warn "'$name' is a built-in agent — add it in the previous step instead."
      continue
    fi
    bin="$(prompt_text "CLI binary to run (e.g. gemini, my-harness-cli):" || true)"
    [ -n "$bin" ] || { warn "Binary is required for custom agents."; continue; }
    flags="$(prompt_text "Default flags (optional, e.g. --force or --dangerously-skip-permissions):" || true)"
    npm_pkg="$(prompt_text "npm package to install in image (optional, e.g. @google/gemini-cli):" || true)"
    write_custom_agent_config "$env_file" "$name" "$bin" "$flags" "$npm_pkg"
    if ! agent_in_list "$name" $next_agents; then
      next_agents="${next_agents:+$next_agents }$name"
    fi
    ok "Added custom agent '$name' (binary: $bin${flags:+; flags: $flags}${npm_pkg:+; npm: $npm_pkg})"
  done

  printf '%s\n' "$next_agents"
}

open_in_editor() {
  file="$1"
  if [ -n "${VISUAL:-}" ]; then
    "$VISUAL" "$file" || return 1
  elif [ -n "${EDITOR:-}" ]; then
    "$EDITOR" "$file" || return 1
  elif command -v code >/dev/null 2>&1; then
    code "$file" || return 1
  elif command -v cursor >/dev/null 2>&1; then
    cursor "$file" || return 1
  elif command -v open >/dev/null 2>&1; then
    open "$file" || return 1
  elif command -v xdg-open >/dev/null 2>&1; then
    xdg-open "$file" || return 1
  else
    return 1
  fi
}

run_update() {
  info "Updating agent-pod-cli to the latest version..."
  if command -v npm >/dev/null 2>&1; then
    npm install -g agent-pod-cli@latest
    ok "agent-pod updated successfully."
  else
    die "npm is not installed or not on PATH. Install Node.js/npm and retry."
  fi
}

run_update_image() {
  [ $# -eq 0 ] || die "Usage: agent-pod update-image"
  info "Updating image '$IMAGE' with fresh CLI and package installs..."
  info "Per-agent auth and history under $STATE_ROOT are preserved."
  AGENT_POD_REFRESH_IMAGE=1 exec "$LAUNCHER_DIR/install.sh"
}

run_edit_env() {
  # Open the active env file in the user's editor. Creates the file (and its
  # parent directory) if it doesn't exist yet.
  target="${ENV_FILE:-$CONFIG_ENV_FILE}"
  mkdir -p "$(dirname "$target")" 2>/dev/null || true
  if [ ! -f "$target" ]; then
    : > "$target"
    chmod 600 "$target" 2>/dev/null || true
    ok "Created $target"
  fi
  if open_in_editor "$target"; then
    ok "Opened $target"
  else
    die "Could not open an editor automatically. Edit this file manually: $target"
  fi
}

run_prune() {
  [ $# -le 1 ] || die "Usage: agent-pod prune [hours]"
  until_hours="${1:-$(config_value AGENT_POD_PRUNE_UNTIL_HOURS 24)}"
  nonnegative_integer "$until_hours" \
    || die "Invalid prune age '$until_hours'. Use whole hours, or 0 for all stopped AgentPod containers."

  command -v docker >/dev/null 2>&1 || die "Docker is not installed or not on PATH."
  docker info >/dev/null 2>&1       || die "Docker daemon is not running. Start Docker and retry."

  PRUNE_FILTERS=(--filter "label=$MANAGED_LABEL")
  if [ "$until_hours" -gt 0 ]; then
    PRUNE_FILTERS+=(--filter "until=${until_hours}h")
  fi

  if [ "$until_hours" -eq 0 ]; then
    info "Pruning all stopped AgentPod containers..."
  else
    info "Pruning stopped AgentPod containers older than ${until_hours}h..."
  fi
  docker container prune --force "${PRUNE_FILTERS[@]}"
}

auto_prune_if_due() {
  truthy "$(config_value AGENT_POD_AUTO_PRUNE 1)" || return 0

  interval_hours="$(config_value AGENT_POD_PRUNE_INTERVAL_HOURS 24)"
  nonnegative_integer "$interval_hours" \
    || { warn "Invalid AGENT_POD_PRUNE_INTERVAL_HOURS='$interval_hours'; skipping auto-prune."; return 0; }

  marker="$STATE_ROOT/.last-container-prune"
  if [ -f "$marker" ] && [ "$interval_hours" -gt 0 ]; then
    interval_minutes=$((interval_hours * 60))
    if ! find "$marker" -mmin "+$interval_minutes" -print -quit 2>/dev/null | grep -q .; then
      return 0
    fi
  fi

  mkdir -p "$STATE_ROOT" 2>/dev/null || true
  if run_prune "$(config_value AGENT_POD_PRUNE_UNTIL_HOURS 24)" >/dev/null 2>&1; then
    touch "$marker" 2>/dev/null || true
  else
    warn "Automatic AgentPod container prune failed; continuing launch."
  fi
}

ensure_config_file() {
  target="${AGENT_POD_ENV_FILE:-$CONFIG_ENV_FILE}"
  mkdir -p "$(dirname "$target")" 2>/dev/null || true
  if [ ! -f "$target" ]; then
    : > "$target"
    chmod 600 "$target" 2>/dev/null || true
    ok "Created $target"
  fi
  printf '%s\n' "$target"
}

run_agents_list() {
  enabled="$(supported_agents)"
  info "Supported agents:"
  for agent in $enabled; do
    if is_builtin_agent "$agent"; then
      printf '  %-12s %s\n' "$agent" "$(agent_label "$agent")"
    else
      bin="$(custom_agent_bin "$agent")"
      flags="$(custom_agent_flags "$agent")"
      npm_pkg="$(custom_agent_npm "$agent")"
      detail="custom"
      [ -n "$bin" ] && detail="$detail; bin=$bin"
      [ -n "$flags" ] && detail="$detail; flags=$flags"
      [ -n "$npm_pkg" ] && detail="$detail; npm=$npm_pkg"
      printf '  %-12s %s\n' "$agent" "$detail"
    fi
  done
}

run_agent_add() {
  custom_bin=""
  custom_flags=""
  custom_npm=""
  agent_args=()

  while [ $# -gt 0 ]; do
    case "$1" in
      --bin)
        custom_bin="${2:-}"
        shift 2
        ;;
      --flags)
        custom_flags="${2:-}"
        shift 2
        ;;
      --npm)
        custom_npm="${2:-}"
        shift 2
        ;;
      --) shift; agent_args+=("$@"); break;;
      -*) die "Unknown option '$1'. Custom agents accept --bin, --flags, and --npm.";;
      *) agent_args+=("$1"); shift;;
    esac
  done

  [ ${#agent_args[@]} -ge 1 ] || die "Usage: agent-pod agent-add <agent> [agent...] [--bin <cli>] [--flags \"<flags>\"] [--npm <package>]"

  add_agents="$(normalize_agents "${agent_args[@]}" 2>/dev/null)" \
    || die "Invalid agent ID. Use built-in agents ($(builtin_agents)) or custom IDs (lowercase, e.g. gemini)."

  current_agents="$(supported_agents)"
  next_agents="$current_agents"
  env_file="$(ensure_config_file)"

  for agent in $add_agents; do
    if is_builtin_agent "$agent"; then
      if ! agent_in_list "$agent" $next_agents; then
        next_agents="${next_agents:+$next_agents }$agent"
      fi
      continue
    fi
    bin="$custom_bin"
    if [ -z "$bin" ]; then
      bin="$(custom_agent_bin "$agent")"
    fi
    [ -n "$bin" ] || die "Custom agent '$agent' requires --bin <cli>. Example: agent-pod agent-add $agent --bin my-cli --flags \"--force\""
    flags="$custom_flags"
    [ -n "$flags" ] || flags="$(custom_agent_flags "$agent")"
    npm_pkg="$custom_npm"
    [ -n "$npm_pkg" ] || npm_pkg="$(custom_agent_npm "$agent")"
    write_custom_agent_config "$env_file" "$agent" "$bin" "$flags" "$npm_pkg"
    if ! agent_in_list "$agent" $next_agents; then
      next_agents="${next_agents:+$next_agents }$agent"
    fi
  done

  upsert_env_var "$env_file" AGENT_POD_AGENTS "$(agents_csv "$next_agents")"
  ok "Supported agents: $next_agents"
  info "Rebuild the image to install newly enabled CLIs: agent-pod install-image"
}

run_agent_remove() {
  [ $# -ge 1 ] || die "Usage: agent-pod agent-remove <agent> [agent...]"
  remove_agents="$(normalize_agents "$*" 2>/dev/null)" \
    || die "Invalid agent ID."
  current_agents="$(supported_agents)"
  next_agents=""
  for agent in $current_agents; do
    if agent_in_list "$agent" $remove_agents; then
      continue
    fi
    next_agents="${next_agents:+$next_agents }$agent"
  done
  [ -n "$next_agents" ] || die "At least one agent must remain supported."
  env_file="$(ensure_config_file)"
  upsert_env_var "$env_file" AGENT_POD_AGENTS "$(agents_csv "$next_agents")"
  ok "Supported agents: $next_agents"
  info "Rebuild the image to remove disabled CLIs: agent-pod install-image"
}

package_config_key() {
  # Map a package type to the env-file key that holds its package list.
  case "$1" in
    npm) printf '%s\n' "AGENT_POD_NPM_PACKAGES";;
    apt) printf '%s\n' "AGENT_POD_APT_PACKAGES";;
    *) return 1;;
  esac
}

valid_package_token() {
  # Package lists are word-split, unquoted, into `apt-get install` /
  # `npm install -g` during the image build, so reject anything that isn't a
  # plausible package spec (no whitespace or shell metacharacters). Allows
  # scoped npm names and version specs like @scope/pkg@1.2 or pkg~=1.0.
  case "$1" in
    ""|*[!A-Za-z0-9@._/+~^=-]*) return 1;;
    *) return 0;;
  esac
}

run_packages_list() {
  [ $# -eq 0 ] || die "Usage: agent-pod packages"
  npm_pkgs="$(config_value AGENT_POD_NPM_PACKAGES "")"
  apt_pkgs="$(config_value AGENT_POD_APT_PACKAGES "")"
  info "Custom packages baked into the image:"
  printf '  %-4s %s\n' "npm" "${npm_pkgs:-(none)}"
  printf '  %-4s %s\n' "apt" "${apt_pkgs:-(none)}"
  if [ -n "$npm_pkgs$apt_pkgs" ]; then
    info "Rebuild the image to apply changes: agent-pod install-image"
  fi
}

run_package_add() {
  pkg_type="npm"
  case "${1:-}" in
    --npm) shift;;
    --apt) pkg_type="apt"; shift;;
    --*) die "Unknown option '$1'. Use --npm (default) or --apt.";;
  esac
  [ $# -ge 1 ] || die "Usage: agent-pod package-add [--npm|--apt] <package> [package...]"
  key="$(package_config_key "$pkg_type")"
  for pkg in "$@"; do
    valid_package_token "$pkg" || die "Invalid package name: '$pkg'"
  done
  next="$(config_value "$key" "")"
  for pkg in "$@"; do
    case " $next " in
      *" $pkg "*) ;;
      *) next="${next:+$next }$pkg";;
    esac
  done
  env_file="$(ensure_config_file)"
  upsert_env_var "$env_file" "$key" "$next"
  ok "Custom $pkg_type packages: ${next:-(none)}"
  info "Rebuild the image to install them: agent-pod install-image"
}

run_package_remove() {
  pkg_type="npm"
  case "${1:-}" in
    --npm) shift;;
    --apt) pkg_type="apt"; shift;;
    --*) die "Unknown option '$1'. Use --npm (default) or --apt.";;
  esac
  [ $# -ge 1 ] || die "Usage: agent-pod package-remove [--npm|--apt] <package> [package...]"
  key="$(package_config_key "$pkg_type")"
  current="$(config_value "$key" "")"
  next=""
  for existing in $current; do
    drop=0
    for pkg in "$@"; do
      [ "$existing" = "$pkg" ] && { drop=1; break; }
    done
    [ "$drop" -eq 1 ] && continue
    next="${next:+$next }$existing"
  done
  env_file="$(ensure_config_file)"
  upsert_env_var "$env_file" "$key" "$next"
  ok "Custom $pkg_type packages: ${next:-(none)}"
  info "Rebuild the image to apply the change: agent-pod install-image"
}

run_docker_access() {
  [ $# -le 1 ] || die "Usage: agent-pod docker-access [on|off|status]"
  current="$(config_value AGENT_POD_DOCKER_ACCESS 0)"
  case "${1:-status}" in
    on|enable|enabled|1|true|yes)
      env_file="$(ensure_config_file)"
      upsert_env_var "$env_file" AGENT_POD_DOCKER_ACCESS 1
      ok "Docker access enabled for future AgentPod launches."
      warn "This mounts the host Docker socket and gives agents control of the host Docker daemon."
      ;;
    off|disable|disabled|0|false|no)
      env_file="$(ensure_config_file)"
      upsert_env_var "$env_file" AGENT_POD_DOCKER_ACCESS 0
      ok "Docker access disabled for future AgentPod launches."
      ;;
    status)
      if truthy "$current"; then
        ok "Docker access is enabled."
      else
        info "Docker access is disabled."
      fi
      info "Config: ${ENV_FILE:-$CONFIG_ENV_FILE}"
      ;;
    *) die "Usage: agent-pod docker-access [on|off|status]";;
  esac
}

run_setup() {
  # Store config with the CLI (~/.agent-pod/.agent-pod.env) by default rather
  # than dropping a file into the current working directory.
  env_file="${AGENT_POD_ENV_FILE:-$CONFIG_ENV_FILE}"
  mkdir -p "$(dirname "$env_file")" 2>/dev/null || true
  if [ ! -f "$env_file" ]; then
    if [ -f "$LAUNCHER_DIR/.agent-pod.env.example" ]; then
      cp "$LAUNCHER_DIR/.agent-pod.env.example" "$env_file"
    else
      : > "$env_file"
    fi
    chmod 600 "$env_file" 2>/dev/null || true
    ok "Created $env_file"
  else
    ok "Updating $env_file"
  fi

  if ! grep -q "AgentPod autonomous flag choices" "$env_file" 2>/dev/null; then
    cat >> "$env_file" <<'EOF'

# AgentPod autonomous flag choices.
# Set a value to 0 to run that agent without AgentPod's default autonomous flag.
EOF
  fi

  chosen_agents="$(prompt_agent_selection)"
  chosen_agents="$(prompt_custom_agents "$env_file" "$chosen_agents")"
  upsert_env_var "$env_file" AGENT_POD_AGENTS "$(agents_csv "$chosen_agents")"

  if agent_in_list claude $chosen_agents; then
    if prompt_yes_no "Use Claude Code autonomous flag (--dangerously-skip-permissions)?" "yes"; then
      upsert_env_var "$env_file" AGENT_POD_CLAUDE_AUTO_FLAGS 1
    else
      upsert_env_var "$env_file" AGENT_POD_CLAUDE_AUTO_FLAGS 0
    fi
  fi

  if agent_in_list codex $chosen_agents; then
    if prompt_yes_no "Use OpenAI Codex autonomous flag (--dangerously-bypass-approvals-and-sandbox)?" "yes"; then
      upsert_env_var "$env_file" AGENT_POD_CODEX_AUTO_FLAGS 1
    else
      upsert_env_var "$env_file" AGENT_POD_CODEX_AUTO_FLAGS 0
    fi
  fi

  upsert_env_var "$env_file" AGENT_POD_OPENCODE_AUTO_FLAGS 0

  if agent_in_list cursor $chosen_agents || agent_in_list agent $chosen_agents; then
    if prompt_yes_no "Use Cursor Agent autonomous flag (--force)?" "yes"; then
      upsert_env_var "$env_file" AGENT_POD_CURSOR_AUTO_FLAGS 1
    else
      upsert_env_var "$env_file" AGENT_POD_CURSOR_AUTO_FLAGS 0
    fi
  fi

  for agent in $chosen_agents; do
    is_builtin_agent "$agent" && continue
    flags="$(custom_agent_flags "$agent")"
    if [ -n "$flags" ]; then
      continue
    fi
    if prompt_yes_no "Add default flags for custom agent '$agent'? (you can also set AGENT_POD_$(agent_env_key "$agent")_FLAGS in the env file)" "no"; then
      custom_flags="$(prompt_text "Flags for $agent (e.g. --force):" || true)"
      if [ -n "$custom_flags" ]; then
        key="$(agent_env_key "$agent")"
        upsert_env_var "$env_file" "AGENT_POD_${key}_FLAGS" "$custom_flags"
      fi
    fi
  done

  if prompt_yes_no "Install Overlord manager package (ovld)?" "yes"; then
    upsert_env_var "$env_file" AGENT_POD_OVERLORD 1
    use_overlord=1
  else
    upsert_env_var "$env_file" AGENT_POD_OVERLORD 0
    use_overlord=0
  fi

  if [ "$use_overlord" = "1" ] && prompt_yes_no "Do you use Overlord to manage agents?" "yes"; then
    current_overlord_token="$(dotenv_get OVERLORD_AGENT_TOKEN "$env_file" 2>/dev/null || true)"
    if [ -n "$current_overlord_token" ]; then
      token_prompt="Overlord agent token [leave blank to keep existing]:"
    else
      token_prompt="Overlord agent token [leave blank to skip]:"
    fi
    overlord_token="$(prompt_text "$token_prompt" || true)"
    if [ -n "$overlord_token" ]; then
      upsert_env_var "$env_file" OVERLORD_AGENT_TOKEN "$overlord_token"
    elif [ -z "$current_overlord_token" ]; then
      upsert_env_var "$env_file" OVERLORD_AGENT_TOKEN ""
    fi
  elif [ "$use_overlord" = "1" ]; then
    info "You should: https://www.ovld.ai"
  fi

  if [ ! -t 0 ]; then
    info "Edit when ready: $env_file"
  elif prompt_yes_no "Open $env_file now?" "yes"; then
    if open_in_editor "$env_file"; then
      ok "Opened $env_file"
    else
      warn "Could not open an editor automatically. Edit this file manually: $env_file"
    fi
  else
    info "Edit when ready: $env_file"
  fi

  offer_image_build
}

# Conclude setup by getting the sandbox image ready. Agents can't run until the
# image exists, so we offer to build it now rather than letting the user hit
# "Image not found" the first time they launch an agent.
offer_image_build() {
  printf '\n' >&2
  if ! command -v docker >/dev/null 2>&1; then
    info "Docker isn't installed yet. Once it is, build the image with: agent-pod install-image"
    return 0
  fi
  if ! docker info >/dev/null 2>&1; then
    info "Docker is installed but not running. Start it, then run: agent-pod install-image"
    return 0
  fi
  if docker image inspect "$IMAGE" >/dev/null 2>&1; then
    ok "Image '$IMAGE' is already built. Rebuild anytime with: agent-pod install-image"
    return 0
  fi
  if prompt_yes_no "Build the agent-pod Docker image now so it's ready for agents? (may take several minutes)" "yes"; then
    if [ -x "$LAUNCHER_DIR/install.sh" ]; then
      "$LAUNCHER_DIR/install.sh" || warn "Image build failed. Retry later with: agent-pod install-image"
    else
      warn "Could not find install.sh next to the launcher. Build later with: agent-pod install-image"
    fi
  else
    info "Build later with: agent-pod install-image"
  fi
}

run_network() {
  [ $# -le 1 ] || die "Usage: agent-pod network [<name>|off|status]"
  current="$(config_value AGENT_POD_DOCKER_NETWORK "")"
  case "${1:-status}" in
    status)
      if [ -n "$current" ]; then
        ok "Pods attach to Docker network: $current"
      else
        info "No Docker network attached (pods reach host-published ports via host.docker.internal)."
      fi
      info "Config: ${ENV_FILE:-$CONFIG_ENV_FILE}"
      ;;
    off|none|disable|disabled|0|false|no)
      env_file="$(ensure_config_file)"
      upsert_env_var "$env_file" AGENT_POD_DOCKER_NETWORK ""
      ok "Pods will no longer attach to a Docker network."
      ;;
    -*) die "Usage: agent-pod network [<name>|off|status]";;
    *)
      name="$1"
      env_file="$(ensure_config_file)"
      upsert_env_var "$env_file" AGENT_POD_DOCKER_NETWORK "$name"
      ok "Pods will attach to Docker network '$name' on future launches."
      info "List networks with: agent-pod shell docker network ls (requires agent-pod docker-access on)"
      ;;
  esac
}

# Locate the skills bundled with the CLI. They ship next to the launcher.
skills_dir() {
  printf '%s\n' "$LAUNCHER_DIR/skills"
}

# ---- agent-facing protocol surface ---------------------------------------
# `agent-pod protocol` is the structured, copy-pasteable help a coding agent
# reads to configure AgentPod on the user's behalf. Output goes to stdout so it
# is cleanly capturable. Keep each topic concrete: exact commands, the env-file
# keys they write, and the rebuild/verify step.
protocol_overview() {
  cat <<'EOF'
agent-pod protocol — controls for configuring the AgentPod sandbox

You are reading this because a user asked an agent to set up or extend AgentPod.
AgentPod runs coding-agent CLIs inside a throwaway Docker sandbox scoped to the
current project directory. These commands let you change what that sandbox can
do. Each topic below prints exact commands, the config keys they write, and the
rebuild/verify step.

Topics:
  agent-pod protocol agents     Add a custom agent or harness CLI
  agent-pod protocol packages   Bake apt/npm tools into the sandbox image
  agent-pod protocol docker     Reach other containers (e.g. local Supabase)
  agent-pod protocol skills     Where the packaged agent skills live / install them
  agent-pod protocol help       This overview

How config is applied:
  • These commands write to the central config file ~/.agent-pod/.agent-pod.env.
  • Anything that changes what is installed needs an image rebuild:
      agent-pod install-image   first build, or after agent/package changes
      agent-pod update-image    refresh CLIs/packages without resetting auth
  • Changes only affect NEW pods. A running pod keeps the image it started with.

Where to run:
  • agent-pod runs on the HOST — it drives the host Docker daemon and edits the
    host config file. Run it where `agent-pod` is on PATH, not from inside a pod.

Inspect current state:
  agent-pod agents              Enabled agents (with bin/flags/npm)
  agent-pod packages            Custom apt/npm packages baked into the image
  agent-pod docker-access status
  agent-pod network status
EOF
}

protocol_agents() {
  cat <<'EOF'
agent-pod protocol agents — add a custom agent or harness

AgentPod ships built-in agents (claude, codex, opencode, cursor/agent). You can
add ANY other CLI — another coding agent, a wrapper, or a harness you built.

Add one (writes config and enables it):
  agent-pod agent-add <id> --bin <cli> [--flags "<flags>"] [--npm <package>]

  <id>      lowercase letters/numbers/-/_ (e.g. gemini, my-harness). Not a built-in name.
  --bin     the binary to run inside the container (required)
  --flags   default flags passed every run (e.g. autonomous/skip-permission flags)
  --npm     npm package to install into the image so <bin> exists

Examples:
  agent-pod agent-add gemini --bin gemini --flags "--yolo" --npm @google/gemini-cli
  agent-pod agent-add pi --bin pi --flags "--yolo" --npm @earendil-works/pi-coding-agent
  agent-pod install-image          # rebuild so the new CLI is present
  agent-pod gemini                 # run it, scoped to the current project

If the binary comes from somewhere other than a single npm package, install it
via packages instead of --npm (see: agent-pod protocol packages), then:
  agent-pod agent-add my-harness --bin my-harness-cli --flags "--force"

Config keys written to ~/.agent-pod/.agent-pod.env:
  AGENT_POD_AGENTS=claude,codex,...,<id>     supported agent list
  AGENT_POD_<ID>_BIN=<cli>                   binary (<ID> = id uppercased, - -> _)
  AGENT_POD_<ID>_FLAGS=<flags>               optional default flags
  AGENT_POD_<ID>_NPM=<package>               optional npm package baked at build

Manage / verify:
  agent-pod agents                 list enabled agents with their details
  agent-pod agent-remove <id>      stop supporting an agent
  agent-pod install-image          rebuild after any change above

Notes:
  • AGENT_POD_YOLO=0 drops ALL default flags (built-in and custom).
  • Each agent gets its own persisted auth/history at ~/.agent-pod/<id>/.
EOF
}

protocol_packages() {
  cat <<'EOF'
agent-pod protocol packages — make sure the sandbox image has what the user needs

Tools and libraries the agent needs at runtime must be baked into the image.
Two package channels:
  --npm   global npm packages (default)        e.g. pnpm, typescript, prettier@3
  --apt   Debian system packages via apt-get   e.g. python3, build-essential, postgresql-client

Add (persisted to config, applied on next rebuild):
  agent-pod package-add --npm <pkg...>
  agent-pod package-add --apt <pkg...>

Examples:
  agent-pod package-add --npm pnpm typescript
  agent-pod package-add --apt python3 python3-pip build-essential
  agent-pod package-add --apt postgresql-client       # psql, for DB work
  agent-pod install-image                              # rebuild to install them

Remove / list:
  agent-pod package-remove --npm typescript
  agent-pod package-remove --apt python3
  agent-pod packages                                   # show what's configured

Config keys written to ~/.agent-pod/.agent-pod.env:
  AGENT_POD_NPM_PACKAGES="pnpm typescript"    space-separated global npm packages
  AGENT_POD_APT_PACKAGES="python3 build-essential"  space-separated Debian packages

Rebuild to apply:
  agent-pod install-image          builds/applies package changes
  agent-pod update-image           refresh installed CLIs/packages, keep auth

Verify a tool is present in a fresh pod:
  agent-pod shell <tool> --version     e.g. agent-pod shell python3 --version
EOF
}

protocol_docker() {
  cat <<'EOF'
agent-pod protocol docker — let the pod reach other Docker containers

By default a pod only sees the current project directory and cannot control the
host Docker daemon. A common need is reaching OTHER containers — for example a
local Supabase stack — so the agent can apply migrations, query the DB, etc.

There are two independent capabilities. Enable whichever the task needs.

1) Docker daemon control (inspect/manage containers, run docker commands)
   agent-pod docker-access on        # mount the host Docker socket into pods
   agent-pod docker-access status
   agent-pod docker-access off
   Then, inside a pod: docker ps, docker logs, docker exec, docker network ls, ...
   PRIVILEGED: socket access = full control of the host Docker daemon. Enable
   only when needed; turn it off when done.

2) Network reachability (talk to services those containers expose)
   • Host-published ports work out of the box from inside the pod via the host
     gateway. A service on the host at localhost:PORT is reachable at:
         host.docker.internal:PORT
   • To reach containers directly by name on their own Docker network, attach
     the pod to that network:
         agent-pod network <network-name>     # set the network to join
         agent-pod network status
         agent-pod network off                # stop attaching

Example — apply migrations against a local Supabase stack:
  # 1. Start Supabase on the host (publishes the DB on localhost:54322 by default)
  supabase start
  # 2. Make the Supabase/Postgres client available in the pod
  agent-pod package-add --apt postgresql-client
  agent-pod install-image
  # 3a. Reach the published DB port from inside the pod via the host gateway:
  agent-pod shell psql "postgresql://postgres:postgres@host.docker.internal:54322/postgres" -c '\dt'
  #     ...or run your migration tool against that connection string.
  # 3b. Or, to use Supabase's own network and container DNS names, find and join it:
  agent-pod docker-access on
  agent-pod shell docker network ls          # look for supabase_network_<project>
  agent-pod network supabase_network_<project>
  #     then reach the db container directly, e.g. host "supabase_db_<project>".

Config keys written to ~/.agent-pod/.agent-pod.env:
  AGENT_POD_DOCKER_ACCESS=1                 mount the host Docker socket
  AGENT_POD_DOCKER_SOCKET=/var/run/docker.sock   socket path (override if non-standard)
  AGENT_POD_DOCKER_NETWORK=<name>           Docker network pods attach to

Notes:
  • host.docker.internal is always available inside pods (no opt-in needed).
  • Network/socket settings apply to NEW pods; restart the pod after changing them.
EOF
}

protocol_skills() {
  dir="$(skills_dir)"
  case "${1:-}" in
    --install|install)
      target="${2:-.claude/skills}"
      [ -d "$dir" ] || die "Packaged skills not found at: $dir"
      mkdir -p "$target" || die "Could not create $target"
      cp -R "$dir/." "$target/" || die "Could not copy skills into $target"
      ok "Installed AgentPod skills into $target"
      info "Restart your agent (or reload skills) so it picks them up."
      ;;
    ""|list)
      cat <<EOF
agent-pod protocol skills — agent skills packaged with the CLI

AgentPod ships skills that teach coding agents how to configure the sandbox.
They simply point the agent at 'agent-pod protocol' (this command), so the
authoritative instructions stay in one place.

Location (bundled with the CLI):
  $dir

Install them into a project so a Claude Code agent discovers them:
  agent-pod protocol skills --install            # -> ./.claude/skills/
  agent-pod protocol skills --install <dir>      # custom destination

Bundled skills:
EOF
      if [ -d "$dir" ]; then
        for s in "$dir"/*/; do
          [ -d "$s" ] || continue
          printf '  %s\n' "$(basename "$s")"
        done
      else
        printf '  (none found at %s)\n' "$dir"
      fi
      ;;
    *) die "Usage: agent-pod protocol skills [--install [dir]]";;
  esac
}

run_protocol() {
  topic="${1:-help}"
  [ $# -gt 0 ] && shift || true
  case "$topic" in
    help|-h|--help|"") protocol_overview;;
    agents|agent|harness|harnesses) protocol_agents;;
    packages|package|deps|dependencies) protocol_packages;;
    docker|docker-access|containers|network|networking|supabase) protocol_docker;;
    skills|skill) protocol_skills "$@";;
    *) die "Unknown protocol topic '$topic'. Run: agent-pod protocol help";;
  esac
}

usage() {
  cat >&2 <<EOF
agent-pod — sandboxed AI coding agents in Docker

Usage:
  agent-pod <agent> [args...]    Run an agent in the current directory
  agent-pod shell                Open a shell inside the sandbox
  agent-pod setup                Create/edit ~/.agent-pod/.agent-pod.env
  agent-pod agents               Show enabled agents
  agent-pod agent-add <agent...> Add supported agents
  agent-pod agent-remove <agent...>
                                  Remove supported agents
  agent-pod packages             Show custom apt/npm packages baked into the image
  agent-pod package-add [--npm|--apt] <pkg...>
                                  Add custom packages to bake into the image
  agent-pod package-remove [--npm|--apt] <pkg...>
                                  Remove custom packages from the image
  agent-pod docker-access [on|off|status]
                                  Show or toggle host Docker socket mounting
  agent-pod network [<name>|off|status]
                                  Attach launched pods to a Docker network
  agent-pod protocol [topic]     Agent-facing help for configuring the sandbox
                                  (topics: agents, packages, docker, skills)
  agent-pod edit-env             Open the active env file in your editor
  agent-pod install-image        Build the Docker image used by agent-pod
  agent-pod update-image         Refresh CLIs and packages in the Docker image
  agent-pod prune [hours]        Remove stopped AgentPod containers
  agent-pod update               Update agent-pod-cli to the latest version
  agent-pod uninstall            Remove the Docker image and ~/.agent-pod state
  agent-pod --help

Agents (built-in):
  claude     Claude Code      (claude; optional --dangerously-skip-permissions)
  codex      OpenAI Codex     (codex; optional --dangerously-bypass-approvals-and-sandbox)
  opencode   OpenCode         (opencode)
  agent      Cursor Agent     (agent; optional --force)
  cursor     Cursor Agent     (agent alias; optional --force)

Custom agents:
  agent-pod agent-add <name> --bin <cli> [--flags "<flags>"] [--npm <package>]
  agent-pod <name> [args...]   Run any configured custom agent or harness

Anything after the agent name is passed straight through to the CLI, e.g.
  agent-pod claude -p "summarize this repo"
  agent-pod codex exec "run the tests"
  agent-pod package-add --npm pnpm eslint@9
  agent-pod install-image

Configuring the sandbox (custom agents, packages, Docker/Supabase access):
  Easiest path — ask your coding agent to do it. Tell it to read
  'agent-pod protocol help' and configure what you need, e.g.
    "Check 'agent-pod protocol help', add the gemini CLI, install psql, and
     give the pod access to my local Supabase so you can apply migrations."
  Or run 'agent-pod protocol help' yourself for the exact commands.

Configuration & environment variables (PORTS, AGENT_POD_ENV_FILE,
AGENT_POD_AGENTS, AGENT_POD_DOCKER_ACCESS, auth keys, pruning, and more):
  agent-pod setup        Guided setup of ~/.agent-pod/.agent-pod.env
  agent-pod edit-env     Open the active env file in your editor
  Full reference: https://github.com/jchaselubitz/agent-pod#readme

Auth: log in interactively once (it persists), or export a provider key
(ANTHROPIC_API_KEY, OPENAI_API_KEY, CURSOR_API_KEY, ...) and it is forwarded
into the container automatically.

Only the current directory is mounted in:
  $PWD
EOF
}

# Env-file ergonomics. Docker reads KEY=VALUE lines and comments. Prefer a
# project-local file, then fall back to a central file next to this launcher.
ENV_FILE="$(discover_env_file)"

IMAGE="$(config_value AGENT_POD_IMAGE agent-pod)"
STATE_ROOT="$(config_value AGENT_POD_HOME "$HOME/.agent-pod")"
PORTS_VALUE="$(config_value PORTS "${PORTS:-}")"
AGENT_POD_ENV_VALUE="$(config_value AGENT_POD_ENV "${AGENT_POD_ENV:-}")"

# ---- argument parsing -----------------------------------------------------
[ $# -ge 1 ] || { usage; exit 1; }
case "$1" in -h|--help|help) usage; exit 0;; esac
case "$1" in
  setup) run_setup; exit 0;;
  agents) run_agents_list; exit 0;;
  agent-add|add-agent) shift; run_agent_add "$@"; exit 0;;
  agent-remove|remove-agent) shift; run_agent_remove "$@"; exit 0;;
  packages) shift; run_packages_list "$@"; exit 0;;
  package-add|add-package) shift; run_package_add "$@"; exit 0;;
  package-remove|remove-package) shift; run_package_remove "$@"; exit 0;;
  docker-access) shift; run_docker_access "$@"; exit 0;;
  network) shift; run_network "$@"; exit 0;;
  protocol) shift; run_protocol "$@"; exit 0;;
  edit-env) run_edit_env; exit 0;;
  update) run_update; exit 0;;
  update-image) shift; run_update_image "$@"; exit 0;;
  prune) shift; run_prune "$@"; exit 0;;
  install-image)
    exec "$LAUNCHER_DIR/install.sh"
    ;;
  uninstall)
    exec "$LAUNCHER_DIR/uninstall.sh"
    ;;
esac

AGENT="$1"; shift

if [ "$AGENT" = "shell" ]; then
  BIN="bash"
  YOLO=()
else
  valid_agent_id "$AGENT" \
    || die "Unknown agent '$AGENT'. Run: agent-pod agents"
  ENABLED_AGENTS="$(supported_agents)"
  agent_in_list "$AGENT" $ENABLED_AGENTS \
    || die "'$AGENT' is not enabled. Add it with: agent-pod agent-add $AGENT && agent-pod update-image"
  resolve_agent_launch "$AGENT"
fi

# ---- preflight ------------------------------------------------------------
command -v docker >/dev/null 2>&1 || die "Docker is not installed or not on PATH."
docker info >/dev/null 2>&1       || die "Docker daemon is not running. Start Docker and retry."
auto_prune_if_due
docker image inspect "$IMAGE" >/dev/null 2>&1 \
  || die "Image '$IMAGE' not found. Build it first:  ./install.sh"

# ---- per-agent state dir (becomes the container's HOME) --------------------
STATE_DIR="$STATE_ROOT/$AGENT"
mkdir -p "$STATE_DIR"
mkdir -p "$STATE_DIR/.npm-global"
# Owner-only: this holds auth tokens and conversation transcripts.
chmod 700 "$STATE_ROOT" "$STATE_DIR" 2>/dev/null || true

# ---- git identity (written to the container's ~/.gitconfig) ---------------
GIT_USER_EMAIL_VALUE="$(config_value GIT_USER_EMAIL "")"
GIT_USER_NAME_VALUE="$(config_value GIT_USER_NAME "")"
if [ -n "$GIT_USER_EMAIL_VALUE" ] || [ -n "$GIT_USER_NAME_VALUE" ]; then
  if command -v git >/dev/null 2>&1; then
    [ -n "$GIT_USER_EMAIL_VALUE" ] && git config --file "$STATE_DIR/.gitconfig" user.email "$GIT_USER_EMAIL_VALUE"
    [ -n "$GIT_USER_NAME_VALUE"  ] && git config --file "$STATE_DIR/.gitconfig" user.name  "$GIT_USER_NAME_VALUE"
  else
    warn "git not found on host; skipping git identity setup (GIT_USER_EMAIL / GIT_USER_NAME)."
  fi
fi

# ---- TTY handling ---------------------------------------------------------
# Always forward stdin (-i) so piping works; only allocate a pty (-t) when both
# ends are real terminals.
TTY_FLAGS=(-i)
if [ -t 0 ] && [ -t 1 ]; then TTY_FLAGS+=(-t); fi

# ---- port publishing ------------------------------------------------------
PORT_FLAGS=()
if [ -n "$PORTS_VALUE" ]; then
  OLDIFS="$IFS"; IFS=','
  for p in $PORTS_VALUE; do
    p="${p// /}"
    [ -z "$p" ] && continue
    case "$p" in *[!0-9]*) die "Invalid port: '$p'";; esac
    { [ "$p" -ge 1 ] && [ "$p" -le 65535 ]; } || die "Port out of range (1-65535): $p"
    PORT_FLAGS+=(-p "127.0.0.1:$p:$p")
  done
  IFS="$OLDIFS"
fi

# ---- forward known provider / tooling credentials (only if set) ----------
ENV_FLAGS=()
for v in \
  ANTHROPIC_API_KEY ANTHROPIC_AUTH_TOKEN ANTHROPIC_BASE_URL \
  OPENAI_API_KEY OPENAI_BASE_URL OPENAI_ORGANIZATION \
  CURSOR_API_KEY \
  GEMINI_API_KEY GOOGLE_API_KEY OPENROUTER_API_KEY GROQ_API_KEY \
  XAI_API_KEY DEEPSEEK_API_KEY MISTRAL_API_KEY \
  GITHUB_TOKEN GH_TOKEN \
  OVERLORD_URL OVERLORD_AGENT_TOKEN OVERLORD_DEVICE_FINGERPRINT; do
  [ -n "${!v:-}" ] && ENV_FLAGS+=(-e "$v")
done

# Forward any extra host variables requested for this project/session.
# Accept commas or spaces so both `FOO,BAR` and `FOO BAR` work.
if [ -n "$AGENT_POD_ENV_VALUE" ]; then
  for v in ${AGENT_POD_ENV_VALUE//,/ }; do
    [ -z "$v" ] && continue
    case "$v" in
      [A-Za-z_]*) ;;
      *) die "Invalid environment variable name in AGENT_POD_ENV: '$v'";;
    esac
    case "$v" in
      *[!A-Za-z0-9_]*) die "Invalid environment variable name in AGENT_POD_ENV: '$v'";;
    esac
    if [ -n "${!v+x}" ]; then
      ENV_FLAGS+=(-e "$v")
    else
      warn "Skipping unset environment variable from AGENT_POD_ENV: $v"
    fi
  done
fi

ENV_FILE_FLAGS=()
if [ -n "$ENV_FILE" ]; then
  [ -f "$ENV_FILE" ] || die "AGENT_POD_ENV_FILE does not exist: $ENV_FILE"
  ENV_FILE_FLAGS+=(--env-file "$ENV_FILE")
fi

# ---- optional host Docker daemon access ----------------------------------
DOCKER_ACCESS_FLAGS=()
if truthy "$(config_value AGENT_POD_DOCKER_ACCESS 0)"; then
  DOCKER_SOCKET="${AGENT_POD_DOCKER_SOCKET:-/var/run/docker.sock}"
  [ -S "$DOCKER_SOCKET" ] || die "AGENT_POD_DOCKER_ACCESS is enabled, but Docker socket was not found: $DOCKER_SOCKET"
  DOCKER_ACCESS_FLAGS+=(-v "$DOCKER_SOCKET:/var/run/docker.sock")
  if socket_gid="$(stat -f '%g' "$DOCKER_SOCKET" 2>/dev/null || stat -c '%g' "$DOCKER_SOCKET" 2>/dev/null)"; then
    case "$socket_gid" in
      ""|*[!0-9]*) ;;
      *) DOCKER_ACCESS_FLAGS+=(--group-add "$socket_gid");;
    esac
  fi
  warn "Docker access enabled: agents can control the host Docker daemon."
fi

# ---- container networking -------------------------------------------------
# Always give pods a name for the host so they can reach host-published ports
# (e.g. a local Supabase stack on 127.0.0.1:54322) via host.docker.internal,
# including on Linux where that name isn't resolved automatically. Optionally
# attach the pod to a Docker network so it can reach other containers by name.
NETWORK_FLAGS=(--add-host "host.docker.internal:host-gateway")
DOCKER_NETWORK_VALUE="$(config_value AGENT_POD_DOCKER_NETWORK "")"
if [ -n "$DOCKER_NETWORK_VALUE" ]; then
  NETWORK_FLAGS+=(--network "$DOCKER_NETWORK_VALUE")
  info "Attaching pod to Docker network: $DOCKER_NETWORK_VALUE"
fi

# ---- assemble the command -------------------------------------------------
CMD=("$BIN")
[ ${#YOLO[@]} -gt 0 ] && CMD+=("${YOLO[@]}")
[ $# -gt 0 ] && CMD+=("$@")

info "agent-pod: running '$AGENT' in $PWD"
info "  state: $STATE_DIR"

exec docker run --rm \
  "${TTY_FLAGS[@]}" \
  --label "$MANAGED_LABEL" \
  --label "com.agent-pod.agent=$AGENT" \
  --user "$(id -u):$(id -g)" \
  -e HOME="$CONTAINER_HOME" \
  -e TERM="${TERM:-xterm-256color}" \
  "${ENV_FLAGS[@]}" \
  "${ENV_FILE_FLAGS[@]}" \
  -e NPM_CONFIG_PREFIX="$CONTAINER_NPM_PREFIX" \
  -e PATH="$CONTAINER_PATH" \
  -v "$STATE_DIR:$CONTAINER_HOME" \
  -v "$PWD:$PWD" \
  -w "$PWD" \
  "${PORT_FLAGS[@]}" \
  "${DOCKER_ACCESS_FLAGS[@]}" \
  "${NETWORK_FLAGS[@]}" \
  --cap-drop ALL \
  --security-opt no-new-privileges \
  "$IMAGE" \
  "${CMD[@]}"
