Skill: isaacgounton/pollinations (version 1.0.2)

--- name: pollinations version: 1.0.2 description: "Pollinations.ai API for AI generation and analysis - text, images, videos, audio, vision, and transcription. Use when user requests AI-powered content (text completion, image generation/editing, video generation, audio/TTS, image/video analysis, audio transcription) or mentions Pollinations. Supports 25+ models with OpenAI-compatible endpoints." metadata: {"openclaw":{"emoji":"🧬","homepage":"https://pollinations.ai","os":["darwin","linux","win32"],"requires":{"bins":["curl","jq","base64"],"env":["POLLINATIONS_API_KEY"]},"primaryEnv":"POLLINATIONS_API_KEY","install":[{"id":"jq-brew","kind":"brew","formula":"jq","bins":["jq"],"label":"Install jq via Homebrew","os":["darwin"]},{"id":"jq-apt","kind":"shell","command":"sudo apt-get install -y jq","bins":["jq"],"label":"Install jq via apt","os":["linux"]}]}} --- # Pollinations v1.0.2 Unified AI platform for generating and analyzing text, images, videos, and audio with 25+ models. ## API Key Get free or paid keys at https://enter.pollinations.ai - Secret Keys (`sk_`): Server-side, no rate limits (recommended) - Optional for many operations (free tier available) ### Runtime Requirements | Type | Name | Required | |------|------|----------| | Env | `POLLINATIONS_API_KEY` | Optional (free tier works without) | | Bin | `curl` | Yes | | Bin | `jq` | Yes | | Bin | `base64` | Yes | ## Operations & Scripts ### 1. Text/Chat Generation (`scripts/chat.sh`) Generate text using 25+ LLM models with OpenAI-compatible API. **Usage:** ```bash scripts/chat.sh "your message" scripts/chat.sh "your message" --model claude --temp 0.7 scripts/chat.sh "explain quantum physics" --model openai --max-tokens 500 scripts/chat.sh "list 3 colors" --json --model openai scripts/chat.sh "solve this step by step" --model o3 --reasoning-effort high scripts/chat.sh "translate to French" --system "You are a translator" --model gemini ``` **Options:** - `--model MODEL` — Model name (default: openai) - `--temp N` — Temperature 0-2 (default: 1) - `--max-tokens N` — Max response length - `--top-p N` — Nucleus sampling 0-1 - `--seed N` — Reproducibility (-1 = random) - `--system "PROMPT"` — System prompt - `--json` — Force structured JSON response - `--reasoning-effort LVL` — For o1/o3/R1 models: high/medium/low/minimal/none - `--thinking-budget N` — Token budget for reasoning models **Models:** openai, claude, gemini, gemini-large, gemini-search, mistral, deepseek, grok, qwen, perplexity, o1, o3, gpt-4, and 15+ more. Use `scripts/models.sh text` to list all. **Simple text (no script needed):** ```bash curl "https://gen.pollinations.ai/text/Hello%20world" ``` ### 2. Image Generation (`scripts/image.sh`) Generate images from text prompts with multiple models and options. **Usage:** ```bash scripts/image.sh "a sunset over mountains" scripts/image.sh "a portrait" --model flux --width 1024 --height 1024 scripts/image.sh "logo design" --model gptimage --quality hd --transparent scripts/image.sh "photo" --enhance --nologo --private scripts/image.sh "art" --negative "blurry, low quality" --seed 42 ``` **Options:** - `--model MODEL` — Model (default: flux) - `--width N` — Width 16-2048px (default: 1024) - `--height N` — Height 16-2048px (default: 1024) - `--seed N` — Reproducibility - `--output FILE` — Output filename - `--enhance` — AI prompt improvement - `--negative "TEXT"` — Negative prompt (what to avoid) - `--nologo` — Remove watermark - `--private` — Private generation - `--safe` — Enable NSFW filter - `--quality LEVEL` — low/medium/high/hd (gptimage only) - `--transparent` — Transparent background PNG (gptimage only) - `--image-url URL` — Source image for image-to-image **Models:** flux (default), turbo, gptimage, kontext, seedream, nanobanana, nanobanana-pro. Use `scripts/models.sh image` to list all. ### 3. Image Editing / Image-to-Image (`scripts/image-edit.sh`) Transform or edit existing images using AI. **Usage:** ```bash scripts/image-edit.sh "make it blue" --source "https://example.com/photo.jpg" scripts/image-edit.sh "add sunglasses" --source photo.jpg --model kontext scripts/image-edit.sh "convert to watercolor" --source input.png --output watercolor.jpg ``` **Options:** - `--source URL/FILE` — Source image (URL or local file, required) - `--model MODEL` — Model (default: kontext) - `--seed N` — Reproducibility - `--negative "TEXT"` — Negative prompt - `--output FILE` — Output filename ### 4. Video Generation (`scripts/image.sh` with video models) Generate videos from text prompts or images. **Usage:** ```bash scripts/image.sh "a cat playing piano" --model veo --duration 6 scripts/image.sh "ocean waves" --model seedance --duration 8 --aspect-ratio 16:9 scripts/image.sh "timelapse" --model veo --duration 4 --audio scripts/image.sh "animate this" --model seedance --image-url "https://example.com/photo.jpg" ``` **Options (in addition to image options):** - `--model veo|seedance` — Video model (required) - `--duration N` — Length in seconds (veo: 4/6/8, seedance: 2-10) - `--aspect-ratio RATIO` — 16:9 or 9:16 - `--audio` — Enable audio generation (veo only) - `--image-url URL` — Source image for image-to-video **Frame interpolation (veo):** Pass two images for first/last frame interpolation using the API directly: ``` https://gen.pollinations.ai/image/prompt?model=veo&image[0]=first_frame_url&image[1]=last_frame_url ``` **Models:** veo (4-8s, audio support, frame interpolation), seedance (2-10s, image-to-video) ### 5. Text-to-Speech / Audio (`scripts/tts.sh`) Convert text to speech with multiple voices and formats. **Usage:** ```bash scripts/tts.sh "Hello world" scripts/tts.sh "Bonjour le monde" --voice nova --format mp3 scripts/tts.sh "Welcome" --voice coral --format wav --output welcome.wav ``` **Options:** - `--voice VOICE` — Voice selection (default: nova) - `--format FORMAT` — Output format (default: mp3) - `--model MODEL` — Model (default: openai-audio) - `--output FILE` — Output filename **Voices (13):** alloy, amuch, ash, ballad, coral, dan, echo, fable, nova, onyx, sage, shimmer, verse **Formats (5):** mp3, wav, flac, opus, pcm16 ### 6. Image Analysis / Vision (`scripts/analyze-image.sh`) Analyze and describe images using vision-capable AI models. **Usage:** ```bash scripts/analyze-image.sh "https://example.com/photo.jpg" scripts/analyze-image.sh photo.jpg --prompt "What objects are in this image?" scripts/analyze-image.sh image.png --model claude --prompt "Extract all text from this image" ``` **Options:** - `--prompt "TEXT"` — Analysis question (default: "Describe this image in detail") - `--model MODEL` — Vision model (default: gemini) **Input:** URL or local file (jpg, png, gif, webp) **Models:** gemini, gemini-large, claude, openai, and other vision-capable models. Use `scripts/models.sh vision` to list all. ### 7. Video Analysis (`scripts/analyze-video.sh`) Analyze video content using AI vision models. **Usage:** ```bash scripts/analyze-video.sh "https://example.com/video.mp4" scripts/analyze-video.sh recording.mp4 --prompt "Summarize the key moments" scripts/analyze-video.sh clip.mov --model gemini-large --prompt "Count the people" ``` **Options:** - `--prompt "TEXT"` — Analysis question (default: "Describe this video in detail") - `--model MODEL` — Video-capable model (default: gemini) **Input:** URL or local file (mp4, mov, avi) **Models:** gemini, gemini-large, claude, openai (video-capable models) ### 8. Audio Transcription (`scripts/transcribe.sh`) Transcribe audio files to text. **Usage:** ```bash scripts/transcribe.sh recording.mp3 scripts/transcribe.sh podcast.wav --model gemini-large scripts/transcribe.sh "https://example.com/audio.mp3" --prompt "Transcribe in French" ``` **Options:** - `--prompt "TEXT"` — Transcription instructions (default: accurate transcription) - `--model MODEL` — Audio-capable model (default: gemini) **Input:** Local file or URL (mp3, wav, flac, ogg, m4a) **Models:** gemini, gemini-large, gemini-legacy, openai-audio ### 9. List Available Models (`scripts/models.sh`) Dynamically list all available models from the API. **Usage:** ```bash scripts/models.sh # List all models scripts/models.sh text # Text/chat models only scripts/models.sh image # Image generation models scripts/models.sh video # Video generation models scripts/models.sh vision # Vision/analysis models scripts/models.sh audio # Audio/TTS models ``` ## API Endpoints Reference | Operation | Endpoint | Method | |-----------|----------|--------| | Simple Text | `/text/{prompt}` | GET | | Chat Completion | `/v1/chat/completions` | POST | | Image Generation | `/image/{prompt}?{params}` | GET | | Image-to-Image | `/image/{prompt}?image={url}&{params}` | GET | | Video Generation | `/image/{prompt}?model=veo&{params}` | GET | | Image Analysis | `/v1/chat/completions` (with image_url) | POST | | Video Analysis | `/v1/chat/completions` (with video_url) | POST | | Audio/TTS | `/v1/chat/completions` (openai-audio) | POST | | Audio Transcription | `/v1/chat/completions` (with input_audio) | POST | | List Text Models | `/v1/models` | GET | | List Image Models | `/image/models` | GET | | List Vision Models | `/text/models` | GET | ## Tips 1. **Free tier available**: Many operations work without an API key (rate limited) 2. **OpenAI-compatible**: Chat endpoint works with existing OpenAI integrations 3. **Reproducibility**: Use `seed` parameter for consistent outputs across all operations 4. **Image enhancement**: Use `--enhance` for AI-improved prompts on image generation 5. **JSON mode**: Use `--json` flag on chat for structured data extraction 6. **Reasoning models**: Use `--reasoning-effort` with o1/o3/R1 for controlled thinking depth 7. **Video from images**: Use `--image-url` with seedance for image-to-video, or veo frame interpolation 8. **Audio on video**: Use `--audio` with veo model for videos with sound 9. **Local files**: Analysis scripts (image, video, transcription) accept both URLs and local files 10. **Private mode**: Use `--private` to keep generations off public feed ## API Documentation Full docs: https://enter.pollinations.ai/api/docs

path: scripts/analyze-image.sh
content: #!/bin/bash
# Pollinations Image Analysis (Vision)
# Usage: ./analyze-image.sh "image_url" [--prompt "describe this"] [--model gemini]

set -e

IMAGE_INPUT="$1"
shift || true

# Defaults
PROMPT="${PROMPT:-Describe this image in detail}"
MODEL="${MODEL:-gemini}"

# Parse args
while [[ $# -gt 0 ]]; do
  case "$1" in
    --prompt)
      PROMPT="$2"
      shift 2
      ;;
    --model)
      MODEL="$2"
      shift 2
      ;;
    *)
      echo "Unknown option: $1"
      exit 1
      ;;
  esac
done

if [[ -z "$IMAGE_INPUT" ]]; then
  echo "Usage: analyze-image.sh <image_url_or_file> [--prompt \"...\"] [--model gemini]"
  echo "Models: gemini, gemini-large, claude, openai (any vision-capable model)"
  exit 1
fi

# Build image_url content - support both URL and local file
if [[ -f "$IMAGE_INPUT" ]]; then
  # Local file: convert to base64 data URL
  MIME_TYPE="image/jpeg"
  case "$IMAGE_INPUT" in
    *.png) MIME_TYPE="image/png" ;;
    *.gif) MIME_TYPE="image/gif" ;;
    *.webp) MIME_TYPE="image/webp" ;;
  esac
  BASE64_DATA=$(base64 -w0 "$IMAGE_INPUT")
  IMAGE_URL="data:$MIME_TYPE;base64,$BASE64_DATA"
else
  IMAGE_URL="$IMAGE_INPUT"
fi

# Build request body
BODY=$(jq -n -c \
  --arg model "$MODEL" \
  --arg prompt "$PROMPT" \
  --arg image_url "$IMAGE_URL" \
  '{
    model: $model,
    messages: [{
      role: "user",
      content: [
        {type: "text", text: $prompt},
        {type: "image_url", image_url: {url: $image_url}}
      ]
    }]
  }')

# Make request
echo "Analyzing image with $MODEL..."

AUTH_HEADER=""
if [[ -n "$POLLINATIONS_API_KEY" ]]; then
  AUTH_HEADER="-H \"Authorization: Bearer $POLLINATIONS_API_KEY\""
fi

RESPONSE=$(curl -s -H "Content-Type: application/json" \
  ${POLLINATIONS_API_KEY:+-H "Authorization: Bearer $POLLINATIONS_API_KEY"} \
  -X POST "https://gen.pollinations.ai/v1/chat/completions" \
  -d "$BODY")

# Extract result
RESULT=$(echo "$RESPONSE" | jq -r '.choices[0].message.content // empty')

if [[ -n "$RESULT" ]]; then
  echo "$RESULT"
else
  echo "Error: Failed to analyze image"
  echo "$RESPONSE" | jq . 2>/dev/null || echo "$RESPONSE"
  exit 1
fi


path: scripts/analyze-video.sh
content: #!/bin/bash
# Pollinations Video Analysis
# Usage: ./analyze-video.sh "video_url_or_file" [--prompt "describe this"] [--model gemini]

set -e

VIDEO_INPUT="$1"
shift || true

# Defaults
PROMPT="${PROMPT:-Describe this video in detail}"
MODEL="${MODEL:-gemini}"

# Parse args
while [[ $# -gt 0 ]]; do
  case "$1" in
    --prompt)
      PROMPT="$2"
      shift 2
      ;;
    --model)
      MODEL="$2"
      shift 2
      ;;
    *)
      echo "Unknown option: $1"
      exit 1
      ;;
  esac
done

if [[ -z "$VIDEO_INPUT" ]]; then
  echo "Usage: analyze-video.sh <video_url_or_file> [--prompt \"...\"] [--model gemini]"
  echo "Models: gemini, gemini-large, claude, openai (video-capable models)"
  echo "Formats: MP4, MOV, AVI"
  exit 1
fi

# Build JSON body using temp files to avoid ARG_MAX limits with large base64 data
BODY_FILE=$(mktemp /tmp/video_body_XXXXXX.json)
trap "rm -f '$BODY_FILE'" EXIT

if [[ -f "$VIDEO_INPUT" ]]; then
  # Local file: encode to base64 and build JSON via temp files
  FORMAT="mp4"
  case "$VIDEO_INPUT" in
    *.mov) FORMAT="mov" ;;
    *.avi) FORMAT="avi" ;;
  esac

  BASE64_FILE=$(mktemp /tmp/video_b64_XXXXXX)
  base64 -w0 "$VIDEO_INPUT" > "$BASE64_FILE"
  trap "rm -f '$BODY_FILE' '$BASE64_FILE'" EXIT

  # Build JSON using jq with file input to avoid shell arg limits
  jq -n -c --rawfile b64data "$BASE64_FILE" \
    --arg model "$MODEL" \
    --arg prompt "$PROMPT" \
    --arg format "$FORMAT" \
    '{
      model: $model,
      messages: [{
        role: "user",
        content: [
          {type: "text", text: $prompt},
          {type: "input_video", input_video: {data: ($b64data | rtrimstr("\n")), format: $format}}
        ]
      }]
    }' > "$BODY_FILE"
else
  # URL input
  jq -n -c \
    --arg model "$MODEL" \
    --arg prompt "$PROMPT" \
    --arg url "$VIDEO_INPUT" \
    '{
      model: $model,
      messages: [{
        role: "user",
        content: [
          {type: "text", text: $prompt},
          {type: "video_url", video_url: {url: $url}}
        ]
      }]
    }' > "$BODY_FILE"
fi

# Make request (longer timeout for video processing)
echo "Analyzing video with $MODEL..."

RESPONSE=$(curl -s --max-time 300 -H "Content-Type: application/json" \
  ${POLLINATIONS_API_KEY:+-H "Authorization: Bearer $POLLINATIONS_API_KEY"} \
  -X POST "https://gen.pollinations.ai/v1/chat/completions" \
  -d @"$BODY_FILE")

# Extract result
RESULT=$(echo "$RESPONSE" | jq -r '.choices[0].message.content // empty')

if [[ -n "$RESULT" ]]; then
  echo "$RESULT"
else
  echo "Error: Failed to analyze video"
  echo "$RESPONSE" | jq . 2>/dev/null || echo "$RESPONSE"
  exit 1
fi


path: scripts/chat.sh
content: #!/bin/bash
# Pollinations Chat Completions
# Usage: ./chat.sh "message" [--model model] [--temp N] [--max N] [--system "prompt"] [--json] [--seed N]

PROMPT="$1"
shift || true

# Defaults
MODEL="${MODEL:-openai}"
TEMPERATURE="${TEMPERATURE:-}"
MAX_TOKENS="${MAX_TOKENS:-}"
TOP_P="${TOP_P:-}"
SEED="${SEED:-}"
SYSTEM_PROMPT="${SYSTEM_PROMPT:-}"
JSON_MODE=""
REASONING_EFFORT=""
THINKING_BUDGET=""

# Parse args
while [[ $# -gt 0 ]]; do
  case "$1" in
    --model)
      MODEL="$2"
      shift 2
      ;;
    --temp|--temperature)
      TEMPERATURE="$2"
      shift 2
      ;;
    --max|--max-tokens)
      MAX_TOKENS="$2"
      shift 2
      ;;
    --top-p)
      TOP_P="$2"
      shift 2
      ;;
    --seed)
      SEED="$2"
      shift 2
      ;;
    --system)
      SYSTEM_PROMPT="$2"
      shift 2
      ;;
    --json)
      JSON_MODE="true"
      shift
      ;;
    --reasoning-effort)
      REASONING_EFFORT="$2"
      shift 2
      ;;
    --thinking-budget)
      THINKING_BUDGET="$2"
      shift 2
      ;;
    *)
      shift
      ;;
  esac
done

if [[ -z "$PROMPT" ]]; then
  echo "Usage: chat.sh \"message\" [options]"
  echo ""
  echo "Options:"
  echo "  --model MODEL          Model (default: openai)"
  echo "  --temp N               Temperature 0-2 (default: 1)"
  echo "  --max-tokens N         Max response tokens"
  echo "  --top-p N              Nucleus sampling 0-1"
  echo "  --seed N               Seed for reproducibility (-1 = random)"
  echo "  --system \"PROMPT\"      System prompt"
  echo "  --json                 Force JSON response format"
  echo "  --reasoning-effort LVL For o1/o3/R1: high/medium/low/minimal/none"
  echo "  --thinking-budget N    Token budget for reasoning models"
  exit 1
fi

# Build messages array
if [[ -n "$SYSTEM_PROMPT" ]]; then
  MESSAGES=$(jq -n -c \
    --arg system "$SYSTEM_PROMPT" \
    --arg prompt "$PROMPT" \
    '[{role: "system", content: $system}, {role: "user", content: $prompt}]')
else
  MESSAGES=$(jq -n -c \
    --arg prompt "$PROMPT" \
    '[{role: "user", content: $prompt}]')
fi

# Build request body
BODY=$(jq -n -c \
  --arg model "$MODEL" \
  --argjson messages "$MESSAGES" \
  '{model: $model, messages: $messages}')

# Add optional parameters
if [[ -n "$TEMPERATURE" ]]; then
  BODY=$(echo "$BODY" | jq -c --arg v "$TEMPERATURE" '. + {temperature: ($v | tonumber)}')
fi
if [[ -n "$MAX_TOKENS" ]]; then
  BODY=$(echo "$BODY" | jq -c --arg v "$MAX_TOKENS" '. + {max_tokens: ($v | tonumber)}')
fi
if [[ -n "$TOP_P" ]]; then
  BODY=$(echo "$BODY" | jq -c --arg v "$TOP_P" '. + {top_p: ($v | tonumber)}')
fi
if [[ -n "$SEED" ]]; then
  BODY=$(echo "$BODY" | jq -c --arg v "$SEED" '. + {seed: ($v | tonumber)}')
fi
if [[ -n "$JSON_MODE" ]]; then
  BODY=$(echo "$BODY" | jq -c '. + {jsonMode: true}')
fi
if [[ -n "$REASONING_EFFORT" ]]; then
  BODY=$(echo "$BODY" | jq -c --arg v "$REASONING_EFFORT" '. + {reasoning_effort: $v}')
fi
if [[ -n "$THINKING_BUDGET" ]]; then
  BODY=$(echo "$BODY" | jq -c --arg v "$THINKING_BUDGET" '. + {thinking_budget: ($v | tonumber)}')
fi

# Make request
URL="https://gen.pollinations.ai/v1/chat/completions"

RESPONSE=$(curl -s -H "Content-Type: application/json" \
  ${POLLINATIONS_API_KEY:+-H "Authorization: Bearer $POLLINATIONS_API_KEY"} \
  -X POST "$URL" -d "$BODY")

# Extract and output result
RESULT=$(echo "$RESPONSE" | jq -r '.choices[0].message.content // empty')

if [[ -n "$RESULT" ]]; then
  echo "$RESULT"
else
  echo "Error: No response received"
  echo "$RESPONSE" | jq . 2>/dev/null || echo "$RESPONSE"
  exit 1
fi


path: scripts/image-edit.sh
content: #!/bin/bash
# Pollinations Image-to-Image Editing
# Usage: ./image-edit.sh "edit instructions" --source "image_url_or_file" [--model kontext] [--seed N] [--output file]

set -e

PROMPT="$1"
shift || true

# Defaults
MODEL="${MODEL:-kontext}"
SEED="${SEED:-}"
OUTPUT="${OUTPUT:-}"
SOURCE=""
NEGATIVE=""

# URL encode function
urlencode() {
  local string="$1"
  local encoded=""
  local length="${#string}"
  for ((i = 0; i < length; i++)); do
    local c="${string:$i:1}"
    case $c in
      [a-zA-Z0-9.~_-]) encoded+="$c" ;;
      *) encoded+="%$(printf '%X' "'$c")" ;;
    esac
  done
  echo "$encoded"
}

# Parse args
while [[ $# -gt 0 ]]; do
  case "$1" in
    --source|--image)
      SOURCE="$2"
      shift 2
      ;;
    --model)
      MODEL="$2"
      shift 2
      ;;
    --seed)
      SEED="$2"
      shift 2
      ;;
    --output)
      OUTPUT="$2"
      shift 2
      ;;
    --negative)
      NEGATIVE="$2"
      shift 2
      ;;
    *)
      echo "Unknown option: $1"
      exit 1
      ;;
  esac
done

if [[ -z "$PROMPT" || -z "$SOURCE" ]]; then
  echo "Usage: image-edit.sh \"edit instructions\" --source <image_url_or_file> [options]"
  echo ""
  echo "Options:"
  echo "  --source URL/FILE  Source image (URL or local file)"
  echo "  --model MODEL      Model (default: kontext)"
  echo "  --seed N           Seed for reproducibility"
  echo "  --negative TEXT    Negative prompt"
  echo "  --output FILE      Output filename"
  echo ""
  echo "Note: Local files are auto-uploaded to a temp host (0x0.st). URL sources are faster."
  exit 1
fi

# Determine output filename
if [[ -z "$OUTPUT" ]]; then
  OUTPUT="edited_$(date +%s).jpg"
fi

# Sanitize prompt
SANITIZED_PROMPT=$(echo "$PROMPT" | sed 's/%/percent/g')
ENCODED_PROMPT=$(urlencode "$SANITIZED_PROMPT")

if [[ -f "$SOURCE" ]]; then
  # Local file: upload to temp host to get a URL, then use GET endpoint
  # (Pollinations image API is GET-only, no POST support)
  echo "Uploading local file to temporary host..."
  TEMP_URL=$(curl -s -F "reqtype=fileupload" -F "time=1h" -F "fileToUpload=@$SOURCE" https://litterbox.catbox.moe/resources/internals/api.php)

  if [[ -z "$TEMP_URL" || ! "$TEMP_URL" =~ ^https?:// ]]; then
    echo "Error: Failed to upload local file. Please provide a URL instead."
    echo "Example: image-edit.sh \"$PROMPT\" --source https://example.com/image.jpg"
    exit 1
  fi

  echo "Uploaded: $TEMP_URL"
  SOURCE="$TEMP_URL"
fi

# Build GET URL with query params
PARAMS="model=$MODEL&image=$(urlencode "$SOURCE")"

if [[ -n "$SEED" ]]; then
  PARAMS="$PARAMS&seed=$SEED"
fi
if [[ -n "$NEGATIVE" ]]; then
  PARAMS="$PARAMS&negative_prompt=$(urlencode "$NEGATIVE")"
fi

URL="https://gen.pollinations.ai/image/$ENCODED_PROMPT?$PARAMS"

echo "Editing image: $PROMPT"
echo "Model: $MODEL"

curl -s --max-time 300 \
  ${POLLINATIONS_API_KEY:+-H "Authorization: Bearer $POLLINATIONS_API_KEY"} \
  -o "$OUTPUT" "$URL"

# Check if file was created
if [[ -s "$OUTPUT" ]]; then
  FILE_SIZE=$(ls -lh "$OUTPUT" | awk '{print $5}')
  echo "Saved to: $OUTPUT ($FILE_SIZE)"
else
  echo "Failed to edit image"
  exit 1
fi


path: scripts/image.sh
content: #!/bin/bash
# Pollinations Image/Video Generation
# Usage: ./image.sh "prompt" [--model model] [--width N] [--height N] [--seed N] [--output file] [--duration N]

set -e

PROMPT="$1"
shift

# Defaults
MODEL="${MODEL:-flux}"
WIDTH="${WIDTH:-1024}"
HEIGHT="${HEIGHT:-1024}"
SEED="${SEED:-}"
OUTPUT="${OUTPUT:-}"
DURATION=""
NOLOGO="${NOLOGO:-}"
PRIVATE="${PRIVATE:-}"
SAFE="${SAFE:-}"
ENHANCE="${ENHANCE:-}"
NEGATIVE=""
QUALITY=""
TRANSPARENT=""
IMAGE_URL=""
AUDIO=""
ASPECT_RATIO=""

# URL encode function
urlencode() {
  local string="$1"
  local encoded=""
  local length="${#string}"
  for ((i = 0; i < length; i++)); do
    local c="${string:$i:1}"
    case $c in
      [a-zA-Z0-9.~_-]) encoded+="$c" ;;
      *) encoded+="%$(printf '%X' "'$c")" ;;
    esac
  done
  echo "$encoded"
}

# Parse args
while [[ $# -gt 0 ]]; do
  case "$1" in
    --model)
      MODEL="$2"
      shift 2
      ;;
    --width)
      WIDTH="$2"
      shift 2
      ;;
    --height)
      HEIGHT="$2"
      shift 2
      ;;
    --seed)
      SEED="$2"
      shift 2
      ;;
    --output)
      OUTPUT="$2"
      shift 2
      ;;
    --duration)
      DURATION="$2"
      shift 2
      ;;
    --nologo)
      NOLOGO="true"
      shift
      ;;
    --private)
      PRIVATE="true"
      shift
      ;;
    --safe)
      SAFE="true"
      shift
      ;;
    --enhance)
      ENHANCE="true"
      shift
      ;;
    --negative)
      NEGATIVE="$2"
      shift 2
      ;;
    --quality)
      QUALITY="$2"
      shift 2
      ;;
    --transparent)
      TRANSPARENT="true"
      shift
      ;;
    --audio)
      AUDIO="true"
      shift
      ;;
    --aspect-ratio)
      ASPECT_RATIO="$2"
      shift 2
      ;;
    --image-url)
      IMAGE_URL="$2"
      shift 2
      ;;
    *)
      echo "Unknown option: $1"
      exit 1
      ;;
  esac
done

# Sanitize prompt (replace % with 'percent' to avoid API 400 errors)
SANITIZED_PROMPT=$(echo "$PROMPT" | sed 's/%/percent/g')

# Build query params
PARAMS="model=$MODEL&width=$WIDTH&height=$HEIGHT"

if [[ -n "$SEED" ]]; then
  PARAMS="$PARAMS&seed=$SEED"
fi
if [[ -n "$DURATION" ]]; then
  PARAMS="$PARAMS&duration=$DURATION"
fi
if [[ -n "$NOLOGO" ]]; then
  PARAMS="$PARAMS&nologo=true"
fi
if [[ -n "$PRIVATE" ]]; then
  PARAMS="$PARAMS&private=true"
fi
if [[ -n "$SAFE" ]]; then
  PARAMS="$PARAMS&safe=true"
fi
if [[ -n "$ENHANCE" ]]; then
  PARAMS="$PARAMS&enhance=true"
fi
if [[ -n "$NEGATIVE" ]]; then
  PARAMS="$PARAMS&negative_prompt=$(urlencode "$NEGATIVE")"
fi
if [[ -n "$QUALITY" ]]; then
  PARAMS="$PARAMS&quality=$QUALITY"
fi
if [[ -n "$TRANSPARENT" ]]; then
  PARAMS="$PARAMS&transparent=true"
fi
if [[ -n "$AUDIO" ]]; then
  PARAMS="$PARAMS&audio=true"
fi
if [[ -n "$ASPECT_RATIO" ]]; then
  PARAMS="$PARAMS&aspectRatio=$ASPECT_RATIO"
fi
if [[ -n "$IMAGE_URL" ]]; then
  PARAMS="$PARAMS&image=$(urlencode "$IMAGE_URL")"
fi

# Build URL
ENCODED_PROMPT=$(urlencode "$SANITIZED_PROMPT")
URL="https://gen.pollinations.ai/image/$ENCODED_PROMPT?$PARAMS"

# Determine output filename
if [[ -z "$OUTPUT" ]]; then
  EXT="jpg"
  [[ "$MODEL" =~ (veo|seedance) ]] && EXT="mp4"
  OUTPUT="media_$(date +%s).$EXT"
fi

# Download media
echo "Generating $([[ "$MODEL" =~ (veo|seedance) ]] && echo "video" || echo "image"): $PROMPT"
echo "Model: $MODEL, Size: ${WIDTH}x${HEIGHT}"

if [[ -n "$POLLINATIONS_API_KEY" ]]; then
  curl -s -H "Authorization: Bearer $POLLINATIONS_API_KEY" -o "$OUTPUT" "$URL"
else
  curl -s -o "$OUTPUT" "$URL"
fi

# Check if file was created
if [[ -s "$OUTPUT" ]]; then
  FILE_SIZE=$(ls -lh "$OUTPUT" | awk '{print $5}')
  echo "✓ Saved to: $OUTPUT ($FILE_SIZE)"
else
  echo "✗ Failed to generate media"
  exit 1
fi


path: scripts/models.sh
content: #!/bin/bash
# Pollinations Model Listing
# Usage: ./models.sh [--type text|image|audio|vision|video]

set -e

TYPE="${1:-all}"

# Parse args
while [[ $# -gt 0 ]]; do
  case "$1" in
    --type)
      TYPE="$2"
      shift 2
      ;;
    text|image|audio|vision|video|all)
      TYPE="$1"
      shift
      ;;
    *)
      echo "Unknown option: $1"
      exit 1
      ;;
  esac
done

fetch_text_models() {
  local data
  data=$(curl -s "https://gen.pollinations.ai/v1/models")
  # Handle both array format and {data: [...]} format
  echo "$data" | jq -r 'if type == "array" then .[].id else .data[].id end' 2>/dev/null
}

fetch_image_models() {
  curl -s "https://gen.pollinations.ai/image/models" | jq -r '.[] | (.id // .name)' 2>/dev/null
}

fetch_vision_models() {
  local data
  data=$(curl -s "https://gen.pollinations.ai/text/models")
  echo "$data" | jq -r '.[] | select(.input_modalities? // [] | index("image")) | (.id // .name)' 2>/dev/null
  # Fallback if jq filter returns nothing
  if [[ -z "$(echo "$data" | jq -r '.[] | select(.input_modalities? // [] | index("image")) | (.id // .name)' 2>/dev/null)" ]]; then
    echo "gemini"
    echo "gemini-large"
    echo "claude"
    echo "openai"
  fi
}

case "$TYPE" in
  text)
    echo "=== Text/Chat Models ==="
    fetch_text_models
    ;;
  image)
    echo "=== Image Models ==="
    fetch_image_models
    ;;
  video)
    echo "=== Video Models ==="
    curl -s "https://gen.pollinations.ai/image/models" | jq -r '.[] | select(.output_modalities? // [] | index("video")) | (.id // .name)' 2>/dev/null
    # Fallback
    if [[ -z "$(curl -s "https://gen.pollinations.ai/image/models" | jq -r '.[] | select(.output_modalities? // [] | index("video")) | (.id // .name)' 2>/dev/null)" ]]; then
      echo "veo"
      echo "seedance"
    fi
    ;;
  audio)
    echo "=== Audio/TTS Models ==="
    echo "openai-audio"
    ;;
  vision)
    echo "=== Vision Models (image analysis) ==="
    fetch_vision_models
    ;;
  all)
    echo "=== Text/Chat Models ==="
    fetch_text_models
    echo ""
    echo "=== Image Models ==="
    fetch_image_models
    echo ""
    echo "=== Video Models ==="
    echo "veo"
    echo "seedance"
    echo ""
    echo "=== Audio/TTS ==="
    echo "openai-audio"
    echo ""
    echo "=== Vision Models ==="
    fetch_vision_models
    ;;
  *)
    echo "Usage: models.sh [--type text|image|audio|vision|video|all]"
    exit 1
    ;;
esac


path: scripts/transcribe.sh
content: #!/bin/bash
# Pollinations Audio Transcription
# Usage: ./transcribe.sh "audio_file_or_url" [--prompt "transcribe this"] [--model gemini]

set -e

AUDIO_INPUT="$1"
shift || true

# Defaults
PROMPT="${PROMPT:-Transcribe this audio accurately. Return only the transcription text, nothing else.}"
MODEL="${MODEL:-gemini}"

# Parse args
while [[ $# -gt 0 ]]; do
  case "$1" in
    --prompt)
      PROMPT="$2"
      shift 2
      ;;
    --model)
      MODEL="$2"
      shift 2
      ;;
    *)
      echo "Unknown option: $1"
      exit 1
      ;;
  esac
done

if [[ -z "$AUDIO_INPUT" ]]; then
  echo "Usage: transcribe.sh <audio_file_or_url> [--prompt \"...\"] [--model gemini]"
  echo "Models: gemini, gemini-large, gemini-legacy, openai-audio"
  echo "Formats: MP3, WAV, FLAC, OGG, M4A"
  exit 1
fi

# Temp files for large data
BODY_FILE=$(mktemp /tmp/transcribe_body_XXXXXX.json)
B64_FILE=$(mktemp /tmp/transcribe_b64_XXXXXX)
trap "rm -f '$BODY_FILE' '$B64_FILE'" EXIT

# Determine audio format
detect_format() {
  case "$1" in
    *.wav) echo "wav" ;;
    *.flac) echo "flac" ;;
    *.ogg) echo "ogg" ;;
    *.m4a) echo "m4a" ;;
    *) echo "mp3" ;;
  esac
}

if [[ -f "$AUDIO_INPUT" ]]; then
  FORMAT=$(detect_format "$AUDIO_INPUT")
  base64 -w0 "$AUDIO_INPUT" > "$B64_FILE"
else
  # URL: download first
  echo "Downloading audio..."
  TEMP_AUDIO=$(mktemp /tmp/audio_dl_XXXXXX)
  trap "rm -f '$BODY_FILE' '$B64_FILE' '$TEMP_AUDIO'" EXIT
  curl -s -o "$TEMP_AUDIO" "$AUDIO_INPUT"
  FORMAT=$(detect_format "$AUDIO_INPUT")
  base64 -w0 "$TEMP_AUDIO" > "$B64_FILE"
fi

# Build JSON using jq with file input to avoid shell arg limits
jq -n -c --rawfile b64data "$B64_FILE" \
  --arg model "$MODEL" \
  --arg prompt "$PROMPT" \
  --arg format "$FORMAT" \
  '{
    model: $model,
    messages: [{
      role: "user",
      content: [
        {type: "text", text: $prompt},
        {type: "input_audio", input_audio: {data: ($b64data | rtrimstr("\n")), format: $format}}
      ]
    }]
  }' > "$BODY_FILE"

# Make request
echo "Transcribing audio with $MODEL..."

RESPONSE=$(curl -s --max-time 300 -H "Content-Type: application/json" \
  ${POLLINATIONS_API_KEY:+-H "Authorization: Bearer $POLLINATIONS_API_KEY"} \
  -X POST "https://gen.pollinations.ai/v1/chat/completions" \
  -d @"$BODY_FILE")

# Extract result
RESULT=$(echo "$RESPONSE" | jq -r '.choices[0].message.content // empty')

if [[ -n "$RESULT" ]]; then
  echo "$RESULT"
else
  echo "Error: Failed to transcribe audio"
  echo "$RESPONSE" | jq . 2>/dev/null || echo "$RESPONSE"
  exit 1
fi


path: scripts/tts.sh
content: #!/bin/bash
# Pollinations Text-to-Speech
# Usage: ./tts.sh "text" [--voice voice] [--format format] [--output file]

set -e

TEXT="$1"
shift

# Defaults
VOICE="${VOICE:-nova}"
FORMAT="${FORMAT:-mp3}"
MODEL="${MODEL:-openai-audio}"
OUTPUT="${OUTPUT:-}"

# Parse args
while [[ $# -gt 0 ]]; do
  case "$1" in
    --voice)
      VOICE="$2"
      shift 2
      ;;
    --format)
      FORMAT="$2"
      shift 2
      ;;
    --model)
      MODEL="$2"
      shift 2
      ;;
    --output)
      OUTPUT="$2"
      shift 2
      ;;
    *)
      echo "Unknown option: $1"
      exit 1
      ;;
  esac
done

# Determine output filename
if [[ -z "$OUTPUT" ]]; then
  OUTPUT="speech_$(date +%s).$FORMAT"
fi

# Build request body
BODY=$(jq -n -c \
  --arg model "$MODEL" \
  --arg text "Say: $TEXT" \
  "{
    model: \$model,
    messages: [
      {role: \"system\", content: \"You are a text reader. Read the user text exactly without responding, adding conversation, or changing anything.\"},
      {role: \"user\", content: \$text}
    ],
    modalities: [\"text\", \"audio\"],
    audio: {voice: \"$VOICE\", format: \"$FORMAT\"}
  }")

# Make request and save audio directly
echo "Generating audio..."
TEMP_RESPONSE=$(mktemp)

# Add auth header if key provided
if [[ -n "$POLLINATIONS_API_KEY" ]]; then
  curl -s -H "Content-Type: application/json" -H "Authorization: Bearer $POLLINATIONS_API_KEY" \
    -X POST "https://gen.pollinations.ai/v1/chat/completions" -d "$BODY" -o "$TEMP_RESPONSE"
else
  curl -s -H "Content-Type: application/json" \
    -X POST "https://gen.pollinations.ai/v1/chat/completions" -d "$BODY" -o "$TEMP_RESPONSE"
fi

# Extract base64 audio data and decode
jq -r '.choices[0].message.audio.data' "$TEMP_RESPONSE" | base64 -d > "$OUTPUT"
rm "$TEMP_RESPONSE"

# Check if file was created
if [[ -s "$OUTPUT" ]]; then
  FILE_SIZE=$(ls -lh "$OUTPUT" | awk '{print $5}')
  echo "✓ Saved to: $OUTPUT ($FILE_SIZE)"
  echo "Voice: $VOICE, Format: $FORMAT"
else
  echo "✗ Failed to generate audio"
  exit 1
fi


Security scan verdicts — ClawScan: suspicious | static analysis:  | VirusTotal: malicious | SkillSpector: suspicious (CRITICAL)