feat: Import 35+ skills, merge duplicates, add openclaw installer

Major updates:
- Added 35+ new skills from awesome-opencode-skills and antigravity repos
- Merged SEO skills into seo-master
- Merged architecture skills into architecture
- Merged security skills into security-auditor and security-coder
- Merged testing skills into testing-master and testing-patterns
- Merged pentesting skills into pentesting
- Renamed website-creator to thai-frontend-dev
- Replaced skill-creator with github version
- Removed Chutes references (use MiniMax API instead)
- Added install-openclaw-skills.sh for cross-platform installation
- Updated .env.example with MiniMax API credentials
This commit is contained in:
Kunthawat Greethong
2026-03-26 11:37:39 +07:00
parent 48595100a1
commit 7edf5bc4d0
469 changed files with 131580 additions and 417 deletions

View File

@@ -0,0 +1,266 @@
#!/usr/bin/env bash
# MiniMax Music Generation CLI (pure bash)
#
# Usage:
# bash scripts/music/generate_music.sh --lyrics "[verse]\nHello world" --output output/song.mp3 --download
# bash scripts/music/generate_music.sh --instrumental --prompt "ambient electronic" -o output/ambient.mp3 --download
# bash scripts/music/generate_music.sh --lyrics "[verse]\nStars" --genre pop --mood happy -o output/happy.mp3 --download
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# ============================================================================
# Common functions (shared with generate_voice.sh)
# ============================================================================
load_env() {
local env_file
for env_file in "$PROJECT_ROOT/.env" "$(pwd)/.env"; do
if [[ -f "$env_file" ]]; then
while IFS= read -r line || [[ -n "$line" ]]; do
line="${line%%#*}"
line="$(echo "$line" | xargs)"
[[ -z "$line" || "$line" != *=* ]] && continue
local key="${line%%=*}" val="${line#*=}"
key="$(echo "$key" | xargs)"; val="$(echo "$val" | xargs)"
if [[ ${#val} -ge 2 ]]; then
case "$val" in
\"*\") val="${val:1:${#val}-2}" ;;
\'*\') val="${val:1:${#val}-2}" ;;
esac
fi
[[ -z "${!key:-}" ]] && export "$key=$val"
done < "$env_file"
return 0
fi
done
}
check_api_key() {
if [[ -z "${MINIMAX_API_KEY:-}" ]]; then
echo "Error: MINIMAX_API_KEY environment variable is not set." >&2
exit 1
fi
}
# ============================================================================
# Main
# ============================================================================
main() {
load_env
check_api_key
local lyrics="" prompt="" model="music-2.5" instrumental=false
local genre="" mood="" tempo="" bpm="" key="" instruments="" vocals=""
local use_case="" structure="" avoid="" references=""
local output="" output_format="url" stream=false download=false
local sample_rate="" bitrate="" format="" aigc_watermark=""
while [[ $# -gt 0 ]]; do
case "$1" in
--lyrics) lyrics="$2"; shift 2 ;;
--prompt) prompt="$2"; shift 2 ;;
--model) model="$2"; shift 2 ;;
--instrumental) instrumental=true; shift ;;
--genre) genre="$2"; shift 2 ;;
--mood) mood="$2"; shift 2 ;;
--tempo) tempo="$2"; shift 2 ;;
--bpm) bpm="$2"; shift 2 ;;
--key) key="$2"; shift 2 ;;
--instruments) instruments="$2"; shift 2 ;;
--vocals) vocals="$2"; shift 2 ;;
--use-case) use_case="$2"; shift 2 ;;
--structure) structure="$2"; shift 2 ;;
--avoid) avoid="$2"; shift 2 ;;
--references) references="$2"; shift 2 ;;
-o|--output) output="$2"; shift 2 ;;
--output-format) output_format="$2"; shift 2 ;;
--stream) stream=true; shift ;;
--download) download=true; shift ;;
--sample-rate) sample_rate="$2"; shift 2 ;;
--bitrate) bitrate="$2"; shift 2 ;;
--format) format="$2"; shift 2 ;;
--aigc-watermark) aigc_watermark="$2"; shift 2 ;;
-h|--help)
cat <<'USAGE'
MiniMax Music Generation CLI
Usage:
generate_music.sh [options]
Options:
--lyrics TEXT Song lyrics (with [verse]/[chorus] tags)
--prompt TEXT Music style/description prompt
--instrumental Generate instrumental (no vocals)
--model MODEL Model name (default: music-2.5)
--genre TEXT Genre (e.g. pop, rock, jazz)
--mood TEXT Mood (e.g. happy, melancholic)
--tempo TEXT Tempo description (e.g. fast, slow)
--bpm NUMBER Beats per minute
--key TEXT Musical key (e.g. C major, A minor)
--instruments TEXT Instruments to include
--vocals TEXT Vocal style description
--use-case TEXT Use case (e.g. background, theme song)
--structure TEXT Song structure
--avoid TEXT Elements to avoid
--references TEXT Reference tracks/artists
--output-format FMT Output format: url (default) or hex
--download Download audio file (for url format)
--sample-rate N Audio sample rate
--bitrate N Audio bitrate
--format FMT Audio format (mp3, wav, etc.)
-o, --output FILE Output file path (required)
Examples:
generate_music.sh --instrumental --prompt "ambient electronic" -o ambient.mp3 --download
generate_music.sh --lyrics "[verse]\nHello world" -o song.mp3 --download
generate_music.sh --lyrics "[verse]\nStars" --genre pop --mood happy -o happy.mp3 --download
USAGE
exit 0
;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
esac
done
if [[ -z "$output" ]]; then
echo "Error: --output / -o is required" >&2
exit 1
fi
# Build prompt from structured fields
local field_parts=()
[[ -n "$genre" ]] && field_parts+=("Genre: $genre")
[[ -n "$mood" ]] && field_parts+=("Mood: $mood")
[[ -n "$tempo" ]] && field_parts+=("Tempo: $tempo")
[[ -n "$bpm" ]] && field_parts+=("BPM: $bpm")
[[ -n "$key" ]] && field_parts+=("Key: $key")
[[ -n "$instruments" ]] && field_parts+=("Instruments: $instruments")
[[ -n "$vocals" ]] && field_parts+=("Vocals: $vocals")
[[ -n "$use_case" ]] && field_parts+=("Use case: $use_case")
[[ -n "$structure" ]] && field_parts+=("Structure: $structure")
[[ -n "$avoid" ]] && field_parts+=("Avoid: $avoid")
[[ -n "$references" ]] && field_parts+=("References: $references")
local field_prompt=""
if [[ ${#field_parts[@]} -gt 0 ]]; then
field_prompt="$(IFS='. '; echo "${field_parts[*]}")"
fi
if [[ -n "$field_prompt" ]]; then
if [[ -n "$prompt" ]]; then
prompt="$prompt. $field_prompt"
else
prompt="$field_prompt"
fi
fi
# Build payload
local payload
payload=$(jq -n \
--arg model "$model" \
--arg prompt "$prompt" \
--arg of "$output_format" \
--argjson stream "$stream" \
'{model: $model, prompt: $prompt, output_format: $of, stream: $stream}')
if $instrumental; then
# music-2.5 does not support is_instrumental — use lyrics workaround
payload=$(echo "$payload" | jq '. + {lyrics: "[intro] [outro]"}')
local current_prompt
current_prompt="$(echo "$payload" | jq -r '.prompt // ""')"
if [[ -n "$current_prompt" ]]; then
payload=$(echo "$payload" | jq --arg p "$current_prompt. pure music, no lyrics" '.prompt = $p')
else
payload=$(echo "$payload" | jq '.prompt = "pure music, no lyrics"')
fi
else
payload=$(echo "$payload" | jq --arg l "$lyrics" '. + {lyrics: $l}')
fi
# Audio settings
local audio_setting="{}"
[[ -n "$sample_rate" ]] && audio_setting=$(echo "$audio_setting" | jq --argjson sr "$sample_rate" '. + {sample_rate: $sr}')
[[ -n "$bitrate" ]] && audio_setting=$(echo "$audio_setting" | jq --argjson br "$bitrate" '. + {bitrate: $br}')
[[ -n "$format" ]] && audio_setting=$(echo "$audio_setting" | jq --arg f "$format" '. + {format: $f}')
if [[ "$audio_setting" != "{}" ]]; then
payload=$(echo "$payload" | jq --argjson as "$audio_setting" '. + {audio_setting: $as}')
fi
[[ -n "$aigc_watermark" ]] && payload=$(echo "$payload" | jq --argjson aw "$aigc_watermark" '. + {aigc_watermark: $aw}')
local api_host="${MINIMAX_API_HOST:-https://api.minimaxi.com}"
local api_url="${api_host}/v1/music_generation"
echo "Generating music with model: $model"
echo "Output format: $output_format"
# Send request via curl
local raw_output http_code response
raw_output="$(curl -s -w "\n%{http_code}" \
-X POST "$api_url" \
-H "Authorization: Bearer ${MINIMAX_API_KEY}" \
-H "Content-Type: application/json" \
--max-time 300 \
-d "$payload" 2>/dev/null)" || {
echo "Error: curl request failed" >&2
exit 1
}
http_code="${raw_output##*$'\n'}"
response="${raw_output%$'\n'*}"
if [[ "$http_code" -ge 400 ]] 2>/dev/null; then
echo "Error: API returned HTTP $http_code" >&2
echo "$response" >&2
exit 1
fi
local status_code
status_code="$(echo "$response" | jq -r '.base_resp.status_code // 0')" 2>/dev/null || true
if [[ "$status_code" != "0" && -n "$status_code" ]]; then
echo "API error: $(echo "$response" | jq '.base_resp')" >&2
exit 1
fi
mkdir -p "$(dirname "$output")"
if [[ "$output_format" == "hex" ]]; then
local audio_hex
audio_hex="$(echo "$response" | jq -r '.data.audio // empty')"
if [[ -z "$audio_hex" ]]; then
echo "Error: No audio hex data in response." >&2
exit 1
fi
echo "$audio_hex" | xxd -r -p > "$output"
echo "Audio saved to: $output"
elif [[ "$output_format" == "url" ]]; then
local audio_url
audio_url="$(echo "$response" | jq -r '.data.audio_url // .data.audio // .data.audio_file.download_url // empty')"
if [[ -z "$audio_url" ]]; then
echo "Error: No audio URL in response." >&2
echo "$response" | jq . >&2
exit 1
fi
echo "Audio URL: $audio_url"
if $download; then
curl -s -o "$output" --max-time 120 "$audio_url"
echo "Audio downloaded to: $output"
else
echo "Use --download to save the file, or download manually from the URL above."
echo "$audio_url" > "$output"
echo "URL written to: $output"
fi
fi
# Print extra info if present
local extra
extra="$(echo "$response" | jq -r '.extra_info // .data.extra_info // empty')" 2>/dev/null || true
if [[ -n "$extra" && "$extra" != "null" ]]; then
echo "Extra info: $extra"
fi
}
main "$@"