#!/bin/bash set -e SKILL_DIR="$(dirname "$(dirname "$0")")" DOWNLOADER_PORT="${DOWNLOADER_PORT:-8083}" SKILLS_DIR="${SKILLS_DIR:-/skills}" GIT_HOST="${GIT_HOST:-git.vibe-overflow.com}" GIT_USER="${GIT_USER:-azat}" GIT_TOKEN="${GIT_TOKEN:-}" # Create HTTP handler create_handler() { cat > /tmp/downloader_handler.sh << 'HANDLER' #!/bin/bash SKILLS_DIR="${SKILLS_DIR:-/skills}" GIT_HOST="${GIT_HOST:-git.vibe-overflow.com}" GIT_USER="${GIT_USER:-azat}" GIT_TOKEN="${GIT_TOKEN:-}" # Read HTTP request read -r request_line method=$(echo "$request_line" | cut -d' ' -f1) path=$(echo "$request_line" | cut -d' ' -f2) # Read headers content_length=0 while read -r header; do header=$(echo "$header" | tr -d '\r') [ -z "$header" ] && break if [[ "$header" =~ ^[Cc]ontent-[Ll]ength:\ *([0-9]+) ]]; then content_length="${BASH_REMATCH[1]}" fi done # Read body if present body="" if [ "$content_length" -gt 0 ]; then body=$(head -c "$content_length") fi # Response helper send_response() { local status="$1" local body="$2" local body_length=${#body} printf "HTTP/1.1 %s\r\n" "$status" printf "Content-Type: application/json\r\n" printf "Content-Length: %d\r\n" "$body_length" printf "Access-Control-Allow-Origin: *\r\n" printf "Connection: close\r\n" printf "\r\n" printf "%s" "$body" } # Git URL with token git_url() { local repo="$1" if [ -n "$GIT_TOKEN" ]; then echo "https://${GIT_USER}:${GIT_TOKEN}@${GIT_HOST}/${GIT_USER}/${repo}.git" else echo "https://${GIT_HOST}/${GIT_USER}/${repo}.git" fi } # Health check if [ "$path" = "/health" ]; then send_response "200 OK" '{"status":"ok"}' exit 0 fi # List available skills from Gitea API if [ "$path" = "/available" ] && [ "$method" = "GET" ]; then api_url="https://${GIT_HOST}/api/v1/users/${GIT_USER}/repos" if [ -n "$GIT_TOKEN" ]; then repos=$(curl -s -H "Authorization: token $GIT_TOKEN" "$api_url") else repos=$(curl -s "$api_url") fi # Transform to our format skills=$(echo "$repos" | jq '[.[] | {name: .name, description: .description, url: .html_url}]') send_response "200 OK" "{\"skills\":$skills}" exit 0 fi # List installed skills if [ "$path" = "/installed" ] && [ "$method" = "GET" ]; then skills="[]" for skill_dir in "$SKILLS_DIR"/*/; do [ -d "$skill_dir" ] || continue skill_name=$(basename "$skill_dir") [ "$skill_name" = "*" ] && continue skill_md="$skill_dir/SKILL.md" if [ -f "$skill_md" ]; then desc=$(sed -n '/^description:/s/description: *//p' "$skill_md" | head -1) skills=$(echo "$skills" | jq --arg name "$skill_name" --arg desc "$desc" '. + [{"name":$name,"description":$desc}]') else skills=$(echo "$skills" | jq --arg name "$skill_name" '. + [{"name":$name,"description":""}]') fi done send_response "200 OK" "{\"skills\":$skills}" exit 0 fi # Install skill: POST /install/{name} if [[ "$path" =~ ^/install/([a-zA-Z0-9_-]+)$ ]] && [ "$method" = "POST" ]; then skill_name="${BASH_REMATCH[1]}" skill_path="$SKILLS_DIR/$skill_name" # Check if already installed if [ -d "$skill_path" ]; then send_response "409 Conflict" "{\"success\":false,\"error\":\"Skill already installed\",\"code\":\"ALREADY_EXISTS\"}" exit 0 fi # Clone url=$(git_url "$skill_name") if ! git clone "$url" "$skill_path" 2>&1; then send_response "404 Not Found" "{\"success\":false,\"error\":\"Failed to clone repository\",\"code\":\"CLONE_FAILED\"}" exit 0 fi # Run autorun.sh if exists autorun="$skill_path/scripts/autorun.sh" if [ -f "$autorun" ]; then chmod +x "$autorun" if ! bash "$autorun" 2>&1; then send_response "500 Internal Server Error" "{\"success\":false,\"error\":\"Autorun failed\",\"code\":\"AUTORUN_FAILED\"}" exit 0 fi fi # Make run.sh executable run_script="$skill_path/scripts/run.sh" [ -f "$run_script" ] && chmod +x "$run_script" # Notify supervisor supervisorctl reread 2>/dev/null || true supervisorctl update 2>/dev/null || true send_response "200 OK" "{\"success\":true,\"name\":\"$skill_name\"}" exit 0 fi # Update skill: POST /update/{name} if [[ "$path" =~ ^/update/([a-zA-Z0-9_-]+)$ ]] && [ "$method" = "POST" ]; then skill_name="${BASH_REMATCH[1]}" skill_path="$SKILLS_DIR/$skill_name" if [ ! -d "$skill_path" ]; then send_response "404 Not Found" "{\"success\":false,\"error\":\"Skill not installed\",\"code\":\"NOT_FOUND\"}" exit 0 fi # Git pull cd "$skill_path" if ! git pull 2>&1; then send_response "500 Internal Server Error" "{\"success\":false,\"error\":\"Git pull failed\",\"code\":\"UPDATE_FAILED\"}" exit 0 fi # Rerun autorun.sh autorun="$skill_path/scripts/autorun.sh" if [ -f "$autorun" ]; then chmod +x "$autorun" bash "$autorun" 2>&1 || true fi # Restart skill supervisorctl restart "$skill_name" 2>/dev/null || true send_response "200 OK" "{\"success\":true,\"name\":\"$skill_name\"}" exit 0 fi # Remove skill: DELETE /remove/{name} if [[ "$path" =~ ^/remove/([a-zA-Z0-9_-]+)$ ]] && [ "$method" = "DELETE" ]; then skill_name="${BASH_REMATCH[1]}" skill_path="$SKILLS_DIR/$skill_name" if [ ! -d "$skill_path" ]; then send_response "404 Not Found" "{\"success\":false,\"error\":\"Skill not installed\",\"code\":\"NOT_FOUND\"}" exit 0 fi # Stop skill supervisorctl stop "$skill_name" 2>/dev/null || true # Remove directory rm -rf "$skill_path" # Update supervisor supervisorctl reread 2>/dev/null || true supervisorctl update 2>/dev/null || true send_response "200 OK" "{\"success\":true,\"name\":\"$skill_name\"}" exit 0 fi # Not found send_response "404 Not Found" '{"error":"Endpoints: GET /available, GET /installed, POST /install/{name}, POST /update/{name}, DELETE /remove/{name}, GET /health"}' HANDLER chmod +x /tmp/downloader_handler.sh # Export vars for handler export SKILLS_DIR export GIT_HOST export GIT_USER export GIT_TOKEN } echo "Starting Skill Downloader on port $DOWNLOADER_PORT..." echo "Skills directory: $SKILLS_DIR" echo "Registry: https://$GIT_HOST/$GIT_USER" create_handler exec socat TCP-LISTEN:$DOWNLOADER_PORT,reuseaddr,fork EXEC:"/tmp/downloader_handler.sh"