#!/bin/bash
# OpenClaw + APort: interactive setup
# Works from a repo checkout or from the published npm package via npx.
# One run secures OpenClaw: creates a passport or wires a hosted agent_id,
# installs the APort plugin, writes config, and installs guardrail wrappers.
# After setup, start OpenClaw with the generated config (e.g. --config ~/.openclaw/config.yaml).
#
# Usage: ./bin/openclaw [agent_id]
#   agent_id (optional): Hosted passport ID from aport.io (e.g., ap_abc123...)
#
# Naming: This script has no .sh extension so it can be invoked as a single
# command (e.g. make openclaw-setup → openclaw). Binaries/entrypoints are
# typically named without extension (e.g. git, npm); .sh is for library scripts.

set -e

# Resolve REPO_ROOT: follow symlinks so npx (node_modules/.bin/agent-guardrails -> package/bin/openclaw) works
SCRIPT_PATH="${BASH_SOURCE[0]}"
while [ -L "$SCRIPT_PATH" ]; do
  TARGET="$(readlink "$SCRIPT_PATH")"
  if [[ "$TARGET" == /* ]]; then
    SCRIPT_PATH="$TARGET"
  else
    SCRIPT_PATH="$(cd "$(dirname "$SCRIPT_PATH")" && pwd)/$TARGET"
  fi
done
SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"

APORT_PLUGIN_PATH="$REPO_ROOT/extensions/openclaw-aport"
DEFAULT_CONFIG="${OPENCLAW_HOME:-$HOME/.openclaw}"

# Parse command line arguments
HOSTED_AGENT_ID=""
if [ -n "$1" ]; then
    # Handle help flags
    if [[ "$1" == "--help" || "$1" == "-h" ]]; then
        echo "Usage: $0 [agent_id]"
        echo ""
        echo "Interactive setup for APort + OpenClaw integration."
        echo ""
        echo "Arguments:"
        echo "  agent_id    Optional hosted passport ID from aport.io (e.g., ap_abc123...)"
        echo "              If provided, uses hosted passport; otherwise runs passport wizard."
        echo ""
        echo "Examples:"
        echo "  $0                                    # Local passport (wizard)"
        echo "  $0 ap_fa2f6d53bb5b4c98b9af0124285b6e0f   # Hosted passport"
        echo ""
        echo "See: https://github.com/aporthq/aport-agent-guardrails/tree/main/docs"
        exit 0
    fi
    # Validate agent_id format (ap_ followed by 32 hex chars)
    if [[ "$1" =~ ^ap_[a-f0-9]{32}$ ]]; then
        HOSTED_AGENT_ID="$1"
    else
        echo "❌ Invalid agent_id format: $1"
        echo "   Expected: ap_[32 hex characters]"
        echo "   Example: ap_fa2f6d53bb5b4c98b9af0124285b6e0f"
        echo ""
        echo "Run '$0 --help' for usage information."
        exit 1
    fi
fi

echo ""
echo "  🛡️  APort + OpenClaw Setup"
echo "  ═══════════════════════════"
echo ""

if [ -n "$HOSTED_AGENT_ID" ]; then
    echo "  ✅ Using hosted passport"
    echo "     Agent ID: $HOSTED_AGENT_ID"
    echo "     Your passport will be fetched from APort API on every call."
    echo ""
    echo "  This will:"
    echo "    1. Choose where to store your OpenClaw config"
    echo "    2. Install APort OpenClaw plugin (deterministic enforcement)"
    echo "    3. Install skill wrappers and the APort skill for OpenClaw"
    echo "    4. Show tool → policy pack mapping"
    echo ""
else
    echo "  This will:"
    echo "    1. Choose where to store your OpenClaw config (passport, decisions, audit)"
    echo "    2. Run the passport wizard (OAP v1.0)"
    echo "    3. Install APort OpenClaw plugin (deterministic enforcement)"
    echo "    4. Install skill wrappers and the APort skill for OpenClaw"
    echo "    5. Show tool → policy pack mapping"
    echo ""
fi

# 1. OpenClaw config directory
echo "  📁 OpenClaw config directory"
echo "     Default: $DEFAULT_CONFIG"
echo "     (Enter = default; or full path e.g. /Users/you/project/.openclaw or relative e.g. ../my-project)"
echo ""
read -p "  Config directory [$DEFAULT_CONFIG]: " CONFIG_DIR
CONFIG_DIR="${CONFIG_DIR:-$DEFAULT_CONFIG}"
CONFIG_DIR="${CONFIG_DIR/#\~/$HOME}"
# Resolve to absolute path
if [ "${CONFIG_DIR#/}" = "$CONFIG_DIR" ]; then
    # No leading / or ~: treat as relative, but fix common mistake (pasted path missing leading /)
    first_seg="${CONFIG_DIR%%/*}"
    case "$(echo "$first_seg" | tr 'A-Z' 'a-z')" in
        users|home) CONFIG_DIR="/$CONFIG_DIR" ;;
        *)          CONFIG_DIR="$PWD/$CONFIG_DIR" ;;
    esac
fi
# Fix duplicated path (e.g. .../aport-agent-guardrails/Users/uchi/... -> /Users/uchi/...)
if echo "$CONFIG_DIR" | grep -q '/Users/'; then
    CONFIG_DIR=$(echo "$CONFIG_DIR" | sed 's|.*/Users/|/Users/|')
elif echo "$CONFIG_DIR" | grep -q '/home/'; then
    CONFIG_DIR=$(echo "$CONFIG_DIR" | sed 's|.*/home/|/home/|')
fi
mkdir -p "$CONFIG_DIR"
CONFIG_DIR="$(cd "$CONFIG_DIR" && pwd)"

# APort data: passport, decision, audit in config_dir/aport/ (passport status = source of truth for suspend)
APORT_DIR="$CONFIG_DIR/aport"
mkdir -p "$APORT_DIR"

echo ""
echo "  ✓ Using: $CONFIG_DIR"
echo ""

# 2. Passport setup (hosted or local)
# Resolve passport path: prefer aport/, fallback to config root (legacy)
if [ -f "$APORT_DIR/passport.json" ]; then
    PASSPORT_FILE="$APORT_DIR/passport.json"
elif [ -f "$CONFIG_DIR/passport.json" ]; then
    PASSPORT_FILE="$CONFIG_DIR/passport.json"
else
    PASSPORT_FILE="$APORT_DIR/passport.json"
fi
export OPENCLAW_CONFIG_DIR="$CONFIG_DIR"
USE_HOSTED_PASSPORT=false

run_local_passport_wizard() {
    APORT_FRAMEWORK=openclaw "$REPO_ROOT/bin/aport-create-passport.sh" --output "$PASSPORT_FILE" --framework=openclaw
}

if [ -n "$HOSTED_AGENT_ID" ]; then
    # Agent ID provided via command line - use hosted passport
    echo "  📋 Passport: Hosted (agent_id: $HOSTED_AGENT_ID)"
    echo "     Skipping local passport creation."
    echo ""
    USE_HOSTED_PASSPORT=true
elif [ -f "$PASSPORT_FILE" ]; then
    # Local passport exists
    read -p "  Passport already exists. Run wizard again (overwrite)? [y/N]: " again
    if [ "$again" != "y" ] && [ "$again" != "Y" ]; then
        echo "  Skipping passport creation."
    else
        run_local_passport_wizard
    fi
else
    # No agent_id, no local passport - ask user
    echo "  📋 Passport Options:"
    echo "     1. Use hosted passport (agent_id from aport.io)"
    echo "     2. Create new local passport (wizard)"
    echo ""
    read -p "  Choice [1/2]: " passport_choice

    if [ "$passport_choice" = "1" ]; then
        echo ""
        read -p "  Enter agent_id from aport.io: " agent_id_input
        # Validate format
        if [[ ! "$agent_id_input" =~ ^ap_[a-f0-9]{32}$ ]]; then
            echo "  ❌ Invalid agent_id format. Expected: ap_[32 hex characters]"
            exit 1
        fi
        HOSTED_AGENT_ID="$agent_id_input"
        USE_HOSTED_PASSPORT=true
        echo "  ✅ Using hosted passport (agent_id: $HOSTED_AGENT_ID)"
        echo ""
    else
        # Create local passport
        run_local_passport_wizard
    fi
fi

# 3. Install OpenClaw Plugin (Deterministic Enforcement)
echo ""
echo "  🔌 OpenClaw Plugin Installation"
echo "  ════════════════════════════════"
echo ""
echo "  The APort OpenClaw plugin provides DETERMINISTIC policy enforcement:"
echo "    ✅ Platform enforces policy before EVERY tool execution"
echo "    ✅ AI cannot bypass - enforcement happens at platform level"
echo "    ✅ Fail-closed by default - blocks on error"
echo ""
echo "  Compare this to AGENTS.md (best-effort only):"
echo "    ⚠️  AI follows prompts to call guardrail"
echo "    ⚠️  Can be bypassed via prompt injection"
echo "    ⚠️  Not deterministic"
echo ""

PLUGIN_INSTALLED=false
if true; then
    # Check if openclaw CLI is available
    if ! command -v openclaw &> /dev/null; then
        echo ""
        echo "  ⚠️  OpenClaw CLI not found in PATH"
        echo "     The plugin will be configured, but you'll need to install it manually:"
        echo "     openclaw plugins install -l $APORT_PLUGIN_PATH"
        echo ""
        read -p "  Continue anyway? [Y/n]: " continue_anyway
        continue_anyway=${continue_anyway:-y}
        if [ "$continue_anyway" != "y" ] && [ "$continue_anyway" != "Y" ]; then
            echo "  Skipping plugin installation."
        fi
    else
        echo ""
        echo "  Installing plugin from: $APORT_PLUGIN_PATH"
        echo "  (Using -l to link local directory; OpenClaw accepts only registry or --link for install.)"
        if openclaw plugins install -l "$APORT_PLUGIN_PATH" 2>&1; then
            echo "  ✅ Plugin installed successfully"
            PLUGIN_INSTALLED=true
        else
            echo "  ❌ Plugin installation failed."
            echo "     OpenClaw did not accept the plugin bundle, so setup is stopping here"
            echo "     instead of writing plugin config that points at a broken install."
            echo "     Retry after fixing the bundle or install it manually:"
            echo "     openclaw plugins install -l $APORT_PLUGIN_PATH"
            exit 1
        fi
        echo ""

        # Verify installation
        if [ "$PLUGIN_INSTALLED" = true ]; then
            if openclaw plugins list 2>/dev/null | grep -q "openclaw-aport"; then
                echo "  ✅ Plugin verified in plugins list"
            else
                echo "  ⚠️  Plugin installed but not showing in 'openclaw plugins list'"
            fi
            echo ""
            export OPENCLAW_CONFIG_DIR="$CONFIG_DIR"
            # Restart only works when gateway is already running; else start in background.
            # Never let gateway start/restart fail the APort installation.
            if openclaw gateway restart 2>/dev/null; then
                echo "  ✅ Gateway restarted; plugin is active."
            else
                # Gateway was not running; start it in background so setup completes without blocking.
                # Any failure here must not break APort installation (no exit 1).
                mkdir -p "$CONFIG_DIR/logs" 2>/dev/null || true
                ( OPENCLAW_CONFIG_DIR="$CONFIG_DIR" nohup openclaw gateway start >> "$CONFIG_DIR/logs/gateway.log" 2>&1 & ) 2>/dev/null || true
                sleep 2
                if openclaw gateway probe 2>/dev/null; then
                    echo "  ✅ Gateway started in background; plugin is active."
                    echo "     Dashboard: http://127.0.0.1:18789/"
                else
                    echo "  ⚠️  Gateway start was initiated; it may still be starting."
                    echo "     If the dashboard is unreachable, run: openclaw doctor"
                    echo "     Then start in a terminal: openclaw gateway start"
                fi
            fi
        fi
    fi

    # Generate plugin configuration
    echo ""
    echo "  📝 Generating plugin configuration..."
    CONFIG_YAML="$CONFIG_DIR/config.yaml"

    # Ask for mode (skip for hosted passport - must use API)
    if [ "$USE_HOSTED_PASSPORT" = true ]; then
        PLUGIN_MODE="api"
        echo ""
        echo "  Plugin mode: api (required for hosted passport)"
        read -p "  APort API URL [https://api.aport.io]: " api_url
        api_url=${api_url:-https://api.aport.io}
    else
        echo ""
        echo "  Plugin mode:"
        echo "    1. local  - Use local guardrail script (privacy, offline)"
        echo "    2. api    - Use APort cloud API (default; for traction and cloud features)"
        echo ""
        read -p "  Mode [1=local, 2=api]: " mode_choice
        mode_choice=${mode_choice:-2}

        if [ "$mode_choice" = "2" ]; then
            PLUGIN_MODE="api"
            echo ""
            read -p "  APort API URL [https://api.aport.io]: " api_url
            api_url=${api_url:-https://api.aport.io}
        else
            PLUGIN_MODE="local"
        fi
    fi

    echo ""
    echo "  Strict mode (optional): block tools with no policy mapping."
    read -p "  Enable strict mode? [y/N]: " strict_mode
    strict_mode=${strict_mode:-n}
    if [ "$strict_mode" = "y" ] || [ "$strict_mode" = "Y" ]; then
        ALLOW_UNMAPPED="false"
    else
        ALLOW_UNMAPPED="true"
    fi

    # Create or update config.yaml
    if [ ! -f "$CONFIG_YAML" ]; then
        cat > "$CONFIG_YAML" << YAML
# OpenClaw Configuration
# Generated by APort setup script

plugins:
  enabled: true
  entries:
    openclaw-aport:
      enabled: true
      config:
        # Mode: "local" (use guardrail script) or "api" (use APort cloud API)
        mode: $PLUGIN_MODE
YAML

        # Add passport configuration (hosted = agentId only, local = passportFile)
        if [ "$USE_HOSTED_PASSPORT" = true ]; then
            cat >> "$CONFIG_YAML" << YAML

        # Hosted passport (agent_id only - no local file)
        agentId: $HOSTED_AGENT_ID
YAML
        else
            cat >> "$CONFIG_YAML" << YAML

        # Passport file location (in aport/ subdir)
        passportFile: $PASSPORT_FILE

        # Legacy compatibility field; current plugin versions use the built-in JS local evaluator
        guardrailScript: $CONFIG_DIR/.skills/aport-guardrail-bash.sh
YAML
        fi

        if [ "$PLUGIN_MODE" = "api" ]; then
            cat >> "$CONFIG_YAML" << YAML

        # For API mode: APort API endpoint
        apiUrl: $api_url
YAML
        fi

        cat >> "$CONFIG_YAML" << YAML

        # Fail-closed: block on error (default: true)
        failClosed: true

        # Allow unmapped tools (false = strict: block custom skills/ClawHub unless mapped)
        allowUnmappedTools: $ALLOW_UNMAPPED
YAML

        echo "  ✅ Created $CONFIG_YAML"
    else
        # Config exists - check if plugin section already present
        if grep -q "openclaw-aport:" "$CONFIG_YAML" 2>/dev/null; then
            echo "  ✅ Plugin configuration already present in $CONFIG_YAML"
        elif grep -qE '^[[:space:]]*plugins:[[:space:]]*$' "$CONFIG_YAML" 2>/dev/null; then
            # Safety: avoid writing a duplicate top-level "plugins:" key in existing YAML.
            # Without a YAML-aware merger (yq), provide a merge-ready snippet instead.
            APORT_SNIPPET="$CONFIG_DIR/aport-plugin-config.snippet.yaml"
            {
                echo "  entries:"
                echo "    openclaw-aport:"
                echo "      enabled: true"
                echo "      config:"
                echo "        mode: $PLUGIN_MODE"
                if [ "$USE_HOSTED_PASSPORT" = true ]; then
                    echo "        agentId: $HOSTED_AGENT_ID"
                else
                    echo "        passportFile: $PASSPORT_FILE"
                    echo "        guardrailScript: $CONFIG_DIR/.skills/aport-guardrail-bash.sh"
                fi
                if [ "$PLUGIN_MODE" = "api" ]; then
                    echo "        apiUrl: $api_url"
                fi
                echo "        failClosed: true"
                echo "        allowUnmappedTools: $ALLOW_UNMAPPED"
            } > "$APORT_SNIPPET"
            echo "  ⚠️  Existing plugins block detected in $CONFIG_YAML; skipped raw append to avoid duplicate YAML keys."
            echo "     Merge this snippet under your existing plugins block:"
            echo "     $APORT_SNIPPET"
        else
            echo "" >> "$CONFIG_YAML"
            echo "# APort Plugin Configuration" >> "$CONFIG_YAML"
            echo "plugins:" >> "$CONFIG_YAML"
            echo "  enabled: true" >> "$CONFIG_YAML"
            echo "  entries:" >> "$CONFIG_YAML"
            echo "    openclaw-aport:" >> "$CONFIG_YAML"
            echo "      enabled: true" >> "$CONFIG_YAML"
            echo "      config:" >> "$CONFIG_YAML"
            echo "        mode: $PLUGIN_MODE" >> "$CONFIG_YAML"

            # Add passport configuration (hosted or local)
            if [ "$USE_HOSTED_PASSPORT" = true ]; then
                echo "        agentId: $HOSTED_AGENT_ID" >> "$CONFIG_YAML"
            else
                echo "        passportFile: $PASSPORT_FILE" >> "$CONFIG_YAML"
                echo "        guardrailScript: $CONFIG_DIR/.skills/aport-guardrail-bash.sh" >> "$CONFIG_YAML"
            fi

            if [ "$PLUGIN_MODE" = "api" ]; then
                echo "        apiUrl: $api_url" >> "$CONFIG_YAML"
            fi

            echo "        failClosed: true" >> "$CONFIG_YAML"
            echo "        allowUnmappedTools: $ALLOW_UNMAPPED" >> "$CONFIG_YAML"
            echo "  ✅ Appended plugin configuration to $CONFIG_YAML"
        fi
    fi

    # Backup a file before overwriting (creates .bak); defined here before first use
    backup_file() {
        local f="$1"
        [ -f "$f" ] && cp "$f" "${f}.bak"
    }

    # So the default config (openclaw.json) has plugin mode/apiUrl - gateway often loads it, not config.yaml
    OPENCLAW_JSON="$CONFIG_DIR/openclaw.json"
    if [ -f "$OPENCLAW_JSON" ] && command -v jq &>/dev/null; then
        api_url_val="${api_url:-https://api.aport.io}"

        if [ "$USE_HOSTED_PASSPORT" = true ]; then
            # Hosted passport config (agentId only)
            CONFIG_JSON=$(jq -n \
                --arg mode "$PLUGIN_MODE" \
                --arg agent_id "$HOSTED_AGENT_ID" \
                --arg api_url "$api_url_val" \
                --arg allow_unmapped "$ALLOW_UNMAPPED" \
                '{ mode: $mode, agentId: $agent_id, failClosed: true, allowUnmappedTools: ($allow_unmapped == "true") } + (if $mode == "api" then { apiUrl: $api_url } else {} end)')
        else
            # Local passport config (passportFile in aport/)
            CONFIG_JSON=$(jq -n \
                --arg mode "$PLUGIN_MODE" \
                --arg pf "$PASSPORT_FILE" \
                --arg gs "$CONFIG_DIR/.skills/aport-guardrail-bash.sh" \
                --arg api_url "$api_url_val" \
                --arg allow_unmapped "$ALLOW_UNMAPPED" \
                '{ mode: $mode, passportFile: $pf, guardrailScript: $gs, failClosed: true, allowUnmappedTools: ($allow_unmapped == "true") } + (if $mode == "api" then { apiUrl: $api_url } else {} end)')
        fi

        if jq --argjson cfg "$CONFIG_JSON" '
            .plugins = (.plugins // {}) |
            .plugins.entries = (.plugins.entries // {}) |
            .plugins.entries["openclaw-aport"] = ((.plugins.entries["openclaw-aport"] // {}) | .enabled = true | .config = $cfg) |
            .plugins.installs = (.plugins.installs // {}) |
            del(.plugins.installs["openclaw-aport"]) |
            .plugins.load = (.plugins.load // {}) |
            .plugins.load.paths = ((.plugins.load.paths // []) | map(select((type == "string" and contains("/openclaw-aport")) | not)))
        ' "$OPENCLAW_JSON" > "$OPENCLAW_JSON.tmp" 2>/dev/null && { backup_file "$OPENCLAW_JSON"; mv "$OPENCLAW_JSON.tmp" "$OPENCLAW_JSON"; }; then
            if [ "$USE_HOSTED_PASSPORT" = true ]; then
                echo "  ✅ Merged plugin config into $OPENCLAW_JSON (mode=$PLUGIN_MODE, hosted passport, allowUnmappedTools=$ALLOW_UNMAPPED)"
            else
                echo "  ✅ Merged plugin config into $OPENCLAW_JSON (mode=$PLUGIN_MODE, allowUnmappedTools=$ALLOW_UNMAPPED)"
            fi
            echo "  ✅ Removed stale source-linked OpenClaw plugin paths from $OPENCLAW_JSON"
        fi
    fi

    echo ""
    echo "  ✅ Plugin setup complete!"
    echo ""
else
    echo ""
    echo "  Skipping plugin installation."
    echo "  ⚠️  Without the plugin, enforcement relies on AGENTS.md (not deterministic)"
    echo ""
fi

# 4. Store repo path and install wrappers
echo "$REPO_ROOT" > "$CONFIG_DIR/.aport-repo"
mkdir -p "$CONFIG_DIR/.skills"

write_wrapper() {
    local name="$1"
    cat > "$CONFIG_DIR/.skills/$name" << WRAP
#!/bin/bash
CONFIG_DIR="\$(cd "\$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
APORT_REPO_ROOT="\$(cat "\$CONFIG_DIR/.aport-repo" 2>/dev/null)"
[ -z "\$APORT_REPO_ROOT" ] && { echo "Error: \$CONFIG_DIR/.aport-repo missing. Re-run bin/openclaw from the guardrails repo." >&2; exit 1; }
export OPENCLAW_PASSPORT_FILE="\${OPENCLAW_PASSPORT_FILE:-\$CONFIG_DIR/aport/passport.json}"
export OPENCLAW_DECISION_FILE="\${OPENCLAW_DECISION_FILE:-\$CONFIG_DIR/aport/decision.json}"
export OPENCLAW_AUDIT_LOG="\${OPENCLAW_AUDIT_LOG:-\$CONFIG_DIR/aport/audit.log}"
exec "\$APORT_REPO_ROOT/bin/$name" "\$@"
WRAP
    chmod +x "$CONFIG_DIR/.skills/$name"
}

write_wrapper "aport-guardrail.sh"
write_wrapper "aport-guardrail-bash.sh"
write_wrapper "aport-guardrail-api.sh"
write_wrapper "aport-guardrail-v2.sh"
write_wrapper "aport-create-passport.sh"
write_wrapper "aport-status.sh"

echo ""
echo "  ✅ Wrappers installed in $CONFIG_DIR/.skills/"
echo "     (They point to this repo and use your config dir for passport/decision/audit.)"
echo ""

# 4b. Normalize passport to OAP spec (spec_version + nested limits) then ensure allowed_commands
if [ -f "$PASSPORT_FILE" ] && command -v jq &>/dev/null; then
    # 4b1. Ensure spec_version and nested limits (fix old or malformed passports)
    # OAP spec: spec_version "oap/1.0", limits per capability e.g. limits["system.command.execute"]
    NEED_FIX=0
    if [ "$(jq -r '.spec_version // "missing"' "$PASSPORT_FILE")" != "oap/1.0" ]; then
        NEED_FIX=1
    fi
    if jq -e '.limits.allowed_commands' "$PASSPORT_FILE" &>/dev/null; then
        NEED_FIX=1
    fi
    if [ "$NEED_FIX" = "1" ]; then
        echo "  📋 Normalizing passport to OAP spec (spec_version oap/1.0, nested limits)..."
        jq '
            (.spec_version = (.spec_version // "oap/1.0")) |
            (if .limits.allowed_commands then
                .limits["system.command.execute"] = ((.limits["system.command.execute"] // {}) |
                    .allowed_commands = (.limits.allowed_commands // ["*"]) |
                    .blocked_patterns = (.limits.blocked_patterns // ["rm -rf", "sudo"]) |
                    .max_execution_time = (.limits.max_execution_time // 300)) |
                .limits |= del(.allowed_commands, .blocked_patterns, .max_execution_time)
            else . end)
        ' "$PASSPORT_FILE" > "$PASSPORT_FILE.tmp" && { backup_file "$PASSPORT_FILE"; mv "$PASSPORT_FILE.tmp" "$PASSPORT_FILE"; }
        echo "  ✅ Passport normalized."
    fi
    echo "  📋 Updating passport allowed_commands (default commands: bash, sh, ls, mkdir, npm, …)..."
    # Default commands so normal exec works; user can add more in passport or via wizard
    DEFAULT_CMDS='["npm","yarn","git","node","pnpm","npx","bash","sh","mkdir","cp","ls","cat","echo","pwd","mv","touch","which","open"]'
    if jq -e '.limits["system.command.execute"]' "$PASSPORT_FILE" &>/dev/null; then
        EXISTING=$(jq -r '.limits["system.command.execute"].allowed_commands // []' "$PASSPORT_FILE")
        # Keep ["*"] if user chose allow-any in wizard; otherwise merge with default list
        if echo "$EXISTING" | jq -e 'index("*") != null' &>/dev/null; then
            MERGED="$EXISTING"
        else
            MERGED=$(jq -n \
                --argjson existing "$EXISTING" \
                --argjson default "$DEFAULT_CMDS" \
                '$existing + $default | unique')
        fi
        jq --argjson merged "$MERGED" '.limits["system.command.execute"].allowed_commands = $merged' "$PASSPORT_FILE" > "$PASSPORT_FILE.tmp" && { backup_file "$PASSPORT_FILE"; mv "$PASSPORT_FILE.tmp" "$PASSPORT_FILE"; }
    else
        # New nested block: default to ["*"] per README (blocked_patterns still apply)
        MERGED='["*"]'
        jq --argjson merged "$MERGED" \
            '.limits["system.command.execute"] = ((.limits["system.command.execute"] // {}) | .allowed_commands = $merged | .blocked_patterns = (.blocked_patterns // ["rm -rf", "sudo"]) | .max_execution_time = (.max_execution_time // 300))' "$PASSPORT_FILE" > "$PASSPORT_FILE.tmp" && { backup_file "$PASSPORT_FILE"; mv "$PASSPORT_FILE.tmp" "$PASSPORT_FILE"; }
    fi
    echo "  ✅ Passport updated: default commands are in allowed_commands."
    echo ""
    # Validate: run guardrail via same path OpenClaw will use; fail fast if denied
    GUARDRAIL_SCRIPT="$CONFIG_DIR/.skills/aport-guardrail-bash.sh"
    echo "  🔍 Self-check: running guardrail (same path OpenClaw uses)..."
    if ! OPENCLAW_PASSPORT_FILE="$PASSPORT_FILE" "$GUARDRAIL_SCRIPT" system.command.execute '{"command":"bash"}' 2>/dev/null; then
        echo ""
        echo "  ❌ Setup incomplete: guardrail self-check was DENIED."
        echo "     The passport may still be missing required commands (e.g. node)."
        echo "     Edit $PASSPORT_FILE and add needed commands to limits.system.command.execute.allowed_commands,"
        echo "     or re-run this script and choose to overwrite the passport in the wizard."
        echo ""
        exit 1
    fi
    echo "  ✅ Guardrail self-check passed (ALLOW)."
    echo ""
else
    if [ ! -f "$PASSPORT_FILE" ]; then
        echo "  ⚠️  No passport found; skip allowlist update. Run the wizard first or re-run with an existing passport."
    else
        echo "  ⚠️  jq not found; cannot auto-update passport allowed_commands. Add needed commands to limits.system.command.execute.allowed_commands manually."
    fi
    echo ""
fi

# 3b. Install APort skill so OpenClaw loads it (managed skill: ~/.openclaw/skills)
#     OpenClaw loads from: workspace/skills, then ~/.openclaw/skills, then bundled
#     See https://docs.openclaw.ai/tools/skills
#     Copy from repo so installed skill matches skills/aport-agent-guardrail/SKILL.md (single source of truth)
SKILL_DIR="$CONFIG_DIR/skills/aport-guardrail"
REPO_SKILL="$REPO_ROOT/skills/aport-agent-guardrail/SKILL.md"
mkdir -p "$SKILL_DIR"
if [ -f "$REPO_SKILL" ]; then
    cp "$REPO_SKILL" "$SKILL_DIR/SKILL.md"
else
    echo "  ⚠️  Repo SKILL.md not found at $REPO_SKILL; skipping skill install." >&2
fi
echo "✅ APort skill installed in $CONFIG_DIR/skills/aport-guardrail/"
echo "   OpenClaw will load it (managed skill). Use it before effectful actions."
echo

# 4. Tool → policy mapping (how OpenClaw knows which policy pack)
echo "📋 Tool → policy pack mapping"
echo "   OpenClaw (or your code) calls the guardrail with a tool name and context."
echo "   The guardrail maps that to a policy pack in external/aport-policies:"
echo
cat << 'TABLE'
   Tool name (examples)              → Policy pack
   ------------------------------------------------
   git.create_pr, git.merge, git.*   → code.repository.merge.v1
   exec.run, system.command.*       → system.command.execute.v1
   message.send, messaging.*        → messaging.message.send.v1
   mcp.tool.*, mcp.*                 → mcp.tool.execute.v1
   agent.session.*, session.*       → agent.session.create.v1
   agent.tool.*, tool.register      → agent.tool.register.v1
   payment.refund, finance.*        → finance.payment.refund.v1
   payment.charge                    → finance.payment.charge.v1
   database.write, data.export       → data.export.create.v1
TABLE
echo "   Full list: docs/TOOL_POLICY_MAPPING.md"
echo

# 5. Enforcement summary
echo ""
echo "  🛡️  Enforcement Summary"
echo "  ═══════════════════════"
echo ""

if [ "$PLUGIN_INSTALLED" = true ]; then
    echo "  ✅ DETERMINISTIC ENFORCEMENT: Plugin installed"
    echo ""
    echo "     The APort OpenClaw plugin enforces policies at the platform level."
    echo "     Every tool execution is checked BEFORE running - AI cannot bypass."
    echo ""
    echo "     Config: $CONFIG_DIR/config.yaml"
    echo "     Mode: $PLUGIN_MODE"
    echo ""
    echo "     To verify plugin is active:"
    echo "     openclaw plugins list | grep openclaw-aport"
    echo ""
else
    echo "  ⚠️  BEST-EFFORT ENFORCEMENT: Using AGENTS.md only"
    echo ""
    echo "     Without the plugin, enforcement relies on the AI following prompts"
    echo "     in workspace/AGENTS.md to call the guardrail before actions."
    echo ""
    echo "     This is NOT deterministic and can be bypassed."
    echo ""
    echo "     To install the plugin later, run:"
    echo "     openclaw plugins install -l $APORT_PLUGIN_PATH"
    echo ""
fi

# 6. Next steps (paths use config dir above)
echo "📝 Next steps"
echo "-------------"
echo "  (Paths below are for this config dir. Re-run setup if you use a different one.)"
echo ""
echo "  1. Smoke test – run this (one line); expect Exit: 0 = ALLOW:"
echo "     (copy the line below as-is; it has your config path)"
echo "     $CONFIG_DIR/.skills/aport-guardrail.sh system.command.execute '{\"command\":\"node --version\"}'; echo \"Exit: \$? (0=ALLOW, 1=DENY)\""
echo ""

if [ "$PLUGIN_INSTALLED" = true ]; then
    echo "  2. Start the gateway (run in a terminal and keep it open):"
    echo "     openclaw gateway start"
    echo "     (If the gateway was already running, use: openclaw gateway restart)"
    echo ""
    echo "  3. Test plugin enforcement by trying a blocked action"
    echo "     (The plugin will automatically block based on passport limits)"
    echo ""
else
    echo "  2. In OpenClaw: point skills to the guardrail script:"
    echo "     $CONFIG_DIR/.skills/aport-guardrail.sh"
    echo ""
    echo "  3. Optional – use API (self-hosted or cloud):"
    echo "     export APORT_API_URL=\"https://api.aport.io\""
    echo "     $CONFIG_DIR/.skills/aport-guardrail-api.sh system.command.execute '{\"command\":\"node --version\"}'"
    echo ""
fi
# 7. Ensure workspace has APort rule in AGENTS.md (auto-install; no manual merge)
WORKSPACE_DIR="$CONFIG_DIR/workspace"
AGENTS="$WORKSPACE_DIR/AGENTS.md"
mkdir -p "$WORKSPACE_DIR"
APORT_MARKER="Pre-Action Authorization (APort Guardrails)"
if [ ! -f "$AGENTS" ]; then
    cp "$REPO_ROOT/docs/AGENTS.md.example" "$AGENTS"
    echo "  ✅ Created $AGENTS with APort pre-action rule."
else
    if grep -q "$APORT_MARKER" "$AGENTS" 2>/dev/null; then
        echo "  ✅ APort rule already present in $AGENTS"
    else
        echo "" >> "$AGENTS"
        echo "---" >> "$AGENTS"
        cat "$REPO_ROOT/docs/AGENTS.md.example" >> "$AGENTS"
        echo "  ✅ Appended APort pre-action rule to $AGENTS"
    fi
fi
echo ""

if [ "$PLUGIN_INSTALLED" = true ]; then
    echo "  Note: AGENTS.md is installed for reference, but the plugin provides"
    echo "        deterministic enforcement (AI cannot bypass). AGENTS.md is optional."
else
    echo "  Note: Without the plugin, AGENTS.md is your only enforcement layer."
    echo "        This relies on the AI following instructions (not deterministic)."
fi

echo ""
echo "  View status: $CONFIG_DIR/.skills/aport-status.sh"
echo ""
if [ "$USE_HOSTED_PASSPORT" = true ]; then
    echo "  Running smoke test (hosted passport): API guardrail with agent_id..."
    if APORT_AGENT_ID="$HOSTED_AGENT_ID" APORT_API_URL="${api_url:-https://api.aport.io}" "$CONFIG_DIR/.skills/aport-guardrail-api.sh" system.command.execute '{"command":"node --version"}' 2>/dev/null; then
        echo "  ✅ Smoke test passed (hosted passport, exit 0 = ALLOW)"
    else
        echo "  ⚠️  Smoke test skipped or failed (hosted). Ensure API is reachable; plugin will use agentId on each tool call."
    fi
else
    echo "  Running smoke test: $CONFIG_DIR/.skills/aport-guardrail.sh system.command.execute '{\"command\":\"node --version\"}'"
    if "$CONFIG_DIR/.skills/aport-guardrail.sh" system.command.execute '{"command":"node --version"}'; then
        echo "  ✅ Smoke test passed (exit 0 = ALLOW)"
    else
        echo "  ❌ Smoke test denied or failed (exit 1). Check passport/limits or run: $CONFIG_DIR/.skills/aport-status.sh"
    fi
fi
echo ""
echo "  Done. OpenClaw config: $CONFIG_DIR"
echo ""
