﻿@chcp 65001 >nul
@echo off
setlocal EnableDelayedExpansion

set "VERSION=2.5.4"
set "CONFIG_FILE=%USERPROFILE%\.mxm-cli.conf"

if "%~1"=="" goto usage
if "%~1"=="help" goto usage
if "%~1"=="-h" goto usage

:: Load config
if exist "%CONFIG_FILE%" (
    for /f "usebackq tokens=1,* delims==" %%a in ("%CONFIG_FILE%") do (
        if "%%a"=="API_URL" set "API_URL=%%b"
        if "%%a"=="API_KEY" set "API_KEY=%%b"
    )
)

if "%~1"=="config" goto cmd_config
if "%~1"=="init" goto cmd_init
if "%~1"=="chat" goto cmd_chat
if "%~1"=="image" goto cmd_image
if "%~1"=="tts" goto cmd_tts
if "%~1"=="mcp" goto cmd_mcp
if "%~1"=="lyrics" goto cmd_lyrics
if "%~1"=="music" goto cmd_music
if "%~1"=="music_cover" goto cmd_music_cover
if "%~1"=="voice_clone" goto cmd_voice_clone
if "%~1"=="voice_design" goto cmd_voice_design
if "%~1"=="models" goto cmd_models
echo 未知命令: %~1
goto usage

:usage
echo MXM CLI v%VERSION% - OpenAI API 兼容客户端
echo.
echo 用法: mxm.bat ^<命令^> [参数]
echo.
echo 命令:
echo   chat ^<消息^>              和 AI 对话
echo   image ^<描述^> [-o 文件] [-r 参考图] [-a 比例]  生成图片 (image-01/image-01-live)
echo   tts ^<文字^> [-o 文件] [-v 音色]  文字转语音 (male-qn-qingse等)
echo   voice_design ^<描述^> [-p 试听文本]  设计自定义音色
echo   mcp search ^<关键词^>      网络搜索
echo   mcp vlm ^<图片^> ^<问题^>  图片理解
echo   lyrics ^<提示^>            生成歌词
echo   music ^<提示^>             生成音乐
echo   music_cover ^<音频^> ^<风格^> 生成翻唱
echo   voice_clone ^<音频^> ^<名称^> 克隆声音
echo   models                   列出可用模型
echo   config --url ^<地址^> --key ^<密钥^>  配置
echo   init                     初始化账户
exit /b 1

:cmd_config
set "NEW_URL="
set "NEW_KEY="
:parse_config_loop
if "%~1"=="" goto do_config
if "%~1"=="--url" (
    set "NEW_URL=%~2"
    shift
    shift
    goto parse_config_loop
)
if "%~1"=="--key" (
    set "NEW_KEY=%~2"
    shift
    shift
    goto parse_config_loop
)
shift
goto parse_config_loop
:do_config
if "!NEW_URL!"=="" (
    echo 用法: config --url ^<地址^> --key ^<密钥^>
    exit /b 1
)
if "!NEW_KEY!"=="" (
    echo 用法: config --url ^<地址^> --key ^<密钥^>
    exit /b 1
)
echo API_URL=!NEW_URL! > "%CONFIG_FILE%"
echo API_KEY=!NEW_KEY! >> "%CONFIG_FILE%"
echo 配置已保存到 %CONFIG_FILE%
exit /b 0

:cmd_init
echo 初始化账户...
echo 请提供以下信息:
echo.
set /p USER_API_URL="API地址 (直接回车默认: https://mimimax.cn): "
set /p USER_API_KEY="API密钥 (sk-...): "
if "!USER_API_URL!"=="" set "USER_API_URL=https://mimimax.cn"
if "!USER_API_KEY!"=="" (
    echo API密钥不能为空
    exit /b 1
)
echo API_URL=!USER_API_URL! > "%CONFIG_FILE%"
echo API_KEY=!USER_API_KEY! >> "%CONFIG_FILE%"
echo 配置已保存到 %CONFIG_FILE%
echo.
echo 测试连接...
goto cmd_models

:check_config
if "!API_URL!"=="" (
    echo 错误: 未配置 API 地址。请运行: mxm.bat config --url ^<地址^> --key ^<密钥^>
    exit /b 1
)
if "!API_KEY!"=="" (
    echo 错误: 未配置 API 密钥。请运行: mxm.bat config --url ^<地址^> --key ^<密钥^>
    exit /b 1
)
exit /b 0

:cmd_models
call :check_config
if errorlevel 1 exit /b 1
echo 可用模型:
curl -s --connect-timeout 10 --max-time 60 --retry 2 --retry-delay 2 "!API_URL!/v1/models" -H "Authorization: Bearer !API_KEY!" -H "Content-Type: application/json"
exit /b 0

:cmd_chat
set "MESSAGE=%~2"
if "!MESSAGE!"=="" (
    echo 用法: mxm.bat chat ^<消息^>
    exit /b 1
)
call :check_config
if errorlevel 1 exit /b 1
echo 思考中...
:: Read message from arg, write JSON to file
python -c "import json,sys; msg = sys.argv[1]; print(json.dumps({'model':'MiniMax-M2.7-highspeed','messages':[{'role':'user','content':msg}]}))" "%MESSAGE%" > "%TEMP%\chat_payload.json"
curl -s --connect-timeout 10 --max-time 60 --retry 2 --retry-delay 2 "!API_URL!/v1/chat/completions" -H "Authorization: Bearer !API_KEY!" -H "Content-Type: application/json" -d "@%TEMP%\chat_payload.json"
del "%TEMP%\chat_payload.json" 2>nul
echo.
exit /b 0

:cmd_image
set "PROMPT="
set "OUTPUT_FILE="
set "MODEL=image-01"
set "REFERENCE="
set "ASPECT=1:1"
:parse_image_loop
if "%~1"=="-o" (
    set "OUTPUT_FILE=%~2"
    shift & shift
    goto parse_image_loop
)
if "%~1"=="-m" (
    set "MODEL=%~2"
    shift & shift
    goto parse_image_loop
)
if "%~1"=="-r" (
    set "REFERENCE=%~2"
    shift & shift
    goto parse_image_loop
)
if "%~1"=="-a" (
    set "ASPECT=%~2"
    shift & shift
    goto parse_image_loop
)
set "PROMPT=!PROMPT! %~1"
shift
if "%~1" neq "" goto parse_image_loop
call :check_config
if errorlevel 1 exit /b 1
if "!OUTPUT_FILE!"=="" set "OUTPUT_FILE=image.png"
echo 正在生成图片 (!MODEL!)...
powershell -NoProfile -Command "$body=@{'model'='!MODEL!';'prompt'='!PROMPT!'.Trim();'aspect_ratio'='!ASPECT!'}; if ('!REFERENCE!' -ne '') { if (Test-Path '!REFERENCE!') { $b64=[Convert]::ToBase64String([IO.File]::ReadAllBytes('!REFERENCE!')); $ext=[IO.Path]::GetExtension('!REFERENCE!').TrimStart('.'); if ($ext -eq 'png') { $mime='png' } else { $mime='jpeg' }; $ref=@{'type'='character';'image_file'='data:image/'+$mime+';base64,'+$b64} } else { $ref=@{'type'='character';'image_file'='!REFERENCE!'} }; $body['subject_reference']=@($ref) }; $json=$body | ConvertTo-Json -Compress; Invoke-WebRequest -Uri '!API_URL!/v1/images/generations' -Method POST -Headers @{'Authorization'='Bearer !API_KEY!';'Content-Type'='application/json'} -Body ([System.Text.Encoding]::UTF8.GetBytes($json)) -OutFile '%TEMP%\mxm_image_response.json'"
powershell -NoProfile -Command "$d=Get-Content '%TEMP%\mxm_image_response.json' -Raw | ConvertFrom-Json; $urls=@(); if ($d.data -is [System.Array]) { foreach($item in $d.data) { if ($item.url) { $urls+=$item.url } } } elseif ($d.data.image_urls) { $urls=$d.data.image_urls }; if ($urls.Count -gt 0) { $url=$urls[0]; Write-Host 'Downloading...'; try { Invoke-WebRequest -Uri $url -OutFile '!OUTPUT_FILE!' -UseBasicParsing; Write-Host '图片已保存到 !OUTPUT_FILE!' } catch { Write-Host '下载失败: ' $_ } } else { Write-Host 'API错误'; exit 1 }"
del "%TEMP%\mxm_image_response.json" 2>nul
exit /b 0

:cmd_tts
set "TEXT="
set "OUTPUT_FILE="
set "VOICE="
:parse_tts_loop
if "%~1"=="-o" (
    set "OUTPUT_FILE=%~2"
    shift & shift
    goto parse_tts_loop
)
if "%~1"=="-v" (
    set "VOICE=%~2"
    shift & shift
    goto parse_tts_loop
)
if "%~1" neq "" (
    set "TEXT=!TEXT! %~1"
    shift
    goto parse_tts_loop
)
call :check_config
if errorlevel 1 exit /b 1
if "!OUTPUT_FILE!"=="" set "OUTPUT_FILE=output.mp3"
if "!VOICE!"=="" (
    curl -s --connect-timeout 10 --max-time 60 --retry 2 --retry-delay 2 "!API_URL!/v1/audio/speech" -H "Authorization: Bearer !API_KEY!" -H "Content-Type: application/json" -d "{\"model\":\"speech-2.8-hd\",\"input\":\"!TEXT!\"}" --output "%TEMP%\mxm_tts_response.json"
) else (
    curl -s --connect-timeout 10 --max-time 60 --retry 2 --retry-delay 2 "!API_URL!/v1/audio/speech" -H "Authorization: Bearer !API_KEY!" -H "Content-Type: application/json" -d "{\"model\":\"speech-2.8-hd\",\"input\":\"!TEXT!\",\"voice\":\"!VOICE!\"}" --output "%TEMP%\mxm_tts_response.json"
)
powershell -NoProfile -Command "$d=Get-Content '%TEMP%\mxm_tts_response.json' -Raw | ConvertFrom-Json; $a=$d.data.audio; try { [System.IO.File]::WriteAllBytes('!OUTPUT_FILE!', [byte[]]::new($a.Length/2) | ForEach-Object { [Convert]::ToByte($a.Substring($_ * 2, 2), 16) }) } catch { [System.IO.File]::WriteAllBytes('!OUTPUT_FILE!', [Convert]::FromBase64String($a)) }"
del "%TEMP%\mxm_tts_response.json" 2>nul
if exist "!OUTPUT_FILE!" (
    echo 语音已保存到 !OUTPUT_FILE!
) else (
    echo 错误: 未能保存文件
)
exit /b 0

:cmd_mcp
if "%~2"=="search" goto cmd_mcp_search
if "%~2"=="vlm" goto cmd_mcp_vlm
goto usage

:cmd_mcp_search
set "QUERY=%~3"
if "!QUERY!"=="" (
    echo 用法: mxm.bat mcp search ^<关键词^>
    exit /b 1
)
call :check_config
if errorlevel 1 exit /b 1
curl -s --connect-timeout 10 --max-time 60 --retry 2 --retry-delay 2 "!API_URL!/v1/coding_plan/search" -H "Authorization: Bearer !API_KEY!" -H "Content-Type: application/json" -d "{\"q\":\"!QUERY!\"}" --output "%TEMP%\mcp_search.json"
powershell -NoProfile -Command "$d=Get-Content '%TEMP%\mcp_search.json' | ConvertFrom-Json; if ($d.organic) { $d.organic | ForEach-Object { Write-Host '【' $_.title '】'; Write-Host $_.snippet.Substring(0, [Math]::Min(200, $_.snippet.Length)); Write-Host } } else { Get-Content '%TEMP%\mcp_search.json' }"
del "%TEMP%\mcp_search.json" 2>nul
exit /b 0

:cmd_mcp_vlm
set "IMAGE_PATH=%~3"
set "QUESTION=%~4"
if not exist "!IMAGE_PATH!" (
    echo 错误: 文件不存在: !IMAGE_PATH!
    exit /b 1
)
if "!QUESTION!"=="" set "QUESTION=描述这张图片"
call :check_config
if errorlevel 1 exit /b 1
:: Convert image to base64
powershell -NoProfile -Command "[Convert]::ToBase64String([IO.File]::ReadAllBytes('!IMAGE_PATH!'))" > "%TEMP%\vlm_b64.txt"
set /p IMAGE_B64=<"%TEMP%\vlm_b64.txt"
del "%TEMP%\vlm_b64.txt"
:: Determine file extension
for %%F in ("!IMAGE_PATH!") do set "EXT=%%~xF"
set "EXT=!EXT:~1!"
if "!EXT!"=="jpg" set "EXT=jpeg"
curl -s --connect-timeout 10 --max-time 60 --retry 2 --retry-delay 2 "!API_URL!/v1/coding_plan/vlm" -H "Authorization: Bearer !API_KEY!" -H "Content-Type: application/json" -d "{\"model\":\"image-01\",\"image_url\":\"data:image/!EXT!;base64,!IMAGE_B64!\",\"question\":\"!QUESTION!\"}"
exit /b 0

:cmd_lyrics
set "PROMPT=%~2"
if "!PROMPT!"=="" (
    echo 用法: mxm.bat lyrics ^<提示词^>
    exit /b 1
)
call :check_config
if errorlevel 1 exit /b 1
curl -s --connect-timeout 10 --max-time 60 --retry 2 --retry-delay 2 "!API_URL!/v1/lyrics_generation" -H "Authorization: Bearer !API_KEY!" -H "Content-Type: application/json" -d "{\"mode\":\"write_full_song\",\"prompt\":\"!PROMPT!\"}"
exit /b 0

:cmd_music
set "PROMPT=%~2"
set "OUTPUT_FILE="
set "INSTRUMENTAL=0"
if "!PROMPT!"=="" (
    echo 用法: mxm.bat music ^<提示词^> [-o 输出文件^] [-i 纯音乐]
    exit /b 1
)
shift
shift
:parse_music_loop
if "%~1"=="-o" (
    set "OUTPUT_FILE=%~2"
    shift
    shift
    goto parse_music_loop
)
if "%~1"=="-i" (
    set "INSTRUMENTAL=1"
    shift
    goto parse_music_loop
)
if "%~1" neq "" (
    set "PROMPT=!PROMPT! %~1"
    shift
    goto parse_music_loop
)
call :check_config
if errorlevel 1 exit /b 1
if "!INSTRUMENTAL!"=="1" (
    curl -s --connect-timeout 10 --max-time 60 --retry 2 --retry-delay 2 "!API_URL!/v1/music_generation" -H "Authorization: Bearer !API_KEY!" -H "Content-Type: application/json" -d "{\"model\":\"music-2.6\",\"prompt\":\"!PROMPT!\",\"mode\":\"instrumental\"}" --output "%TEMP%\mxm_music_response.json"
) else (
    curl -s --connect-timeout 10 --max-time 60 --retry 2 --retry-delay 2 "!API_URL!/v1/music_generation" -H "Authorization: Bearer !API_KEY!" -H "Content-Type: application/json" -d "{\"model\":\"music-2.6\",\"prompt\":\"!PROMPT!\",\"lyrics_optimizer\":true}" --output "%TEMP%\mxm_music_response.json"
)
powershell -NoProfile -Command "$d=Get-Content '%TEMP%\mxm_music_response.json' -Raw | ConvertFrom-Json; $a=$d.data.audio; try { [System.IO.File]::WriteAllBytes('music.mp3', [byte[]]::new($a.Length/2) | ForEach-Object { [Convert]::ToByte($a.Substring($_ * 2, 2), 16) }) } catch { [System.IO.File]::WriteAllBytes('music.mp3', [Convert]::FromBase64String($a)) }"
del "%TEMP%\mxm_music_response.json" 2>nul
if exist "music.mp3" (
    echo 音乐已保存到 music.mp3
) else (
    echo 错误: 未能生成音乐
)
exit /b 0

:cmd_music_cover
set "AUDIO_PATH=%~2"
set "STYLE=%~3"
if not exist "!AUDIO_PATH!" (
    echo 错误: 音频文件不存在: !AUDIO_PATH!
    exit /b 1
)
if "!STYLE!"=="" (
    echo 用法: mxm.bat music_cover ^<音频文件^> ^<风格描述^>
    exit /b 1
)
call :check_config
if errorlevel 1 exit /b 1
powershell -NoProfile -Command "[Convert]::ToBase64String([IO.File]::ReadAllBytes('!AUDIO_PATH!'))" > "%TEMP%\cover_b64.txt"
set /p AUDIO_B64=<"%TEMP%\cover_b64.txt"
curl -s --connect-timeout 10 --max-time 60 --retry 2 --retry-delay 2 "!API_URL!/v1/music_generation" -H "Authorization: Bearer !API_KEY!" -H "Content-Type: application/json" -d "{\"model\":\"music-cover\",\"prompt\":\"!STYLE!\",\"audio_base64\":\"!AUDIO_B64!\",\"output_format\":\"hex\"}" --output "%TEMP%\mxm_cover_response.json"
powershell -NoProfile -Command "$d=Get-Content '%TEMP%\mxm_cover_response.json' -Raw | ConvertFrom-Json; $a=$d.data.audio; try { [System.IO.File]::WriteAllBytes('cover.mp3', [byte[]]::new($a.Length/2) | ForEach-Object { [Convert]::ToByte($a.Substring($_ * 2, 2), 16) }) } catch { [System.IO.File]::WriteAllBytes('cover.mp3', [Convert]::FromBase64String($a)) }"
del "%TEMP%\cover_b64.txt" 2>nul
del "%TEMP%\mxm_cover_response.json" 2>nul
if exist "cover.mp3" (
    echo 翻唱已保存到 cover.mp3
) else (
    echo 错误: 未能生成翻唱
)
exit /b 0

:cmd_voice_design
set "PROMPT="
set "PREVIEW="
set "PART_INDEX=3"
:parse_vd_loop
if "%~1"=="-p" (
    set "PREVIEW=%~2"
    shift
    shift
    goto :parse_vd_loop
)
if "%~2" neq "" (
    set "PROMPT=%~2"
    shift
    goto :parse_vd_loop
)
if "!PROMPT!"=="" (
    echo 用法: mxm.bat voice_design ^<音色描述^> [-p 试听文本^]
    exit /b 1
)
call :check_config
if errorlevel 1 exit /b 1
echo 正在设计音色...
powershell -NoProfile -Command "$body=@{'prompt'='!PROMPT!'} | ConvertTo-Json -Compress; if ('!PREVIEW!' -ne '') { $body=@{'prompt'='!PROMPT!';'preview_text'='!PREVIEW!'} | ConvertTo-Json -Compress }; Invoke-WebRequest -Uri '!API_URL!/v1/voice_design' -Method POST -Headers @{'Authorization'='Bearer !API_KEY!';'Content-Type'='application/json'} -Body ([System.Text.Encoding]::UTF8.GetBytes($body)) -OutFile '%TEMP%\mxm_vd_response.json'"
powershell -NoProfile -Command "$d=Get-Content '%TEMP%\mxm_vd_response.json' -Raw | ConvertFrom-Json; if ($d.base_resp.status_code -eq 0) { Write-Host '音色设计成功!'; Write-Host 'Voice ID: ' $d.voice_id; if ($d.trial_audio) { $hex=$d.trial_audio; $bytes=[byte[]]::new($hex.Length/2); for($i=0;$i -lt $hex.Length;$i+=2){$bytes[$i/2]=[Convert]::ToByte($hex.Substring($i,2),16)}; [IO.File]::WriteAllBytes('voice_design_preview.mp3',$bytes); Write-Host '试听已保存到 voice_design_preview.mp3' }; Write-Host '使用: mxm.bat tts 你好 -v ' $d.voice_id } else { Write-Host '错误: ' $d.base_resp.status_msg; exit 1 }"
del "%TEMP%\mxm_vd_response.json" 2>nul
exit /b 0

:cmd_voice_clone
set "AUDIO_PATH=%~2"
set "VOICE_NAME=%~3"
if not exist "!AUDIO_PATH!" (
    echo 错误: 音频文件不存在: !AUDIO_PATH!
    exit /b 1
)
if "!VOICE_NAME!"=="" (
    echo 用法: mxm.bat voice_clone ^<音频文件^> ^<声音名称^>
    exit /b 1
)
call :check_config
if errorlevel 1 exit /b 1
echo 正在克隆声音...
powershell -NoProfile -Command "[Convert]::ToBase64String([IO.File]::ReadAllBytes('!AUDIO_PATH!'))" > "%TEMP%\voice_b64.txt"
set /p AUDIO_B64=<"%TEMP%\voice_b64.txt"
set "VOICE_ID=!VOICE_NAME: =_!"
powershell -NoProfile -Command "$body=@{'voice_name'='!VOICE_NAME!';'voice_id'='!VOICE_ID!';'audio_file'='!AUDIO_B64!'} | ConvertTo-Json -Compress; Invoke-WebRequest -Uri '!API_URL!/v1/voice_clone' -Method POST -Headers @{'Authorization'='Bearer !API_KEY!';'Content-Type'='application/json'} -Body ([System.Text.Encoding]::UTF8.GetBytes($body)) -OutFile '%TEMP%\mxm_voice_response.json'"
powershell -NoProfile -Command "$d=Get-Content '%TEMP%\mxm_voice_response.json' -Raw | ConvertFrom-Json; if ($d.base_resp.status_code -eq 0) { Write-Host '声音克隆成功!'; Write-Host '名称: ' $d.voice_name; Write-Host 'ID: ' $d.voice_id; if ($d.demo_audio) { Write-Host '示例音频: ' $d.demo_audio } } else { Write-Host '错误: ' $d.base_resp.status_msg; exit 1 }"
del "%TEMP%\voice_b64.txt" 2>nul
del "%TEMP%\mxm_voice_response.json" 2>nul
exit /b 0
