@hasna/connectors 1.3.18 → 1.3.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/index.js +28 -8
- package/bin/mcp.js +8 -1
- package/bin/serve.js +24 -4
- package/connectors/connect-googlegemini/src/api/images.ts +0 -3
- package/connectors/connect-googlegemini/src/cli/index.ts +0 -2
- package/connectors/connect-googlegemini/src/types/index.ts +0 -1
- package/connectors/connect-minimax/CLAUDE.md +47 -0
- package/connectors/connect-minimax/package.json +41 -0
- package/connectors/connect-minimax/src/api/client.ts +123 -0
- package/connectors/connect-minimax/src/api/image.ts +65 -0
- package/connectors/connect-minimax/src/api/index.ts +53 -0
- package/connectors/connect-minimax/src/api/music.ts +78 -0
- package/connectors/connect-minimax/src/api/sound-effects.ts +59 -0
- package/connectors/connect-minimax/src/api/tts.ts +56 -0
- package/connectors/connect-minimax/src/api/video.ts +78 -0
- package/connectors/connect-minimax/src/cli/index.ts +246 -0
- package/connectors/connect-minimax/src/index.ts +19 -0
- package/connectors/connect-minimax/src/types/index.ts +182 -0
- package/connectors/connect-minimax/src/utils/config.ts +125 -0
- package/connectors/connect-minimax/src/utils/output.ts +40 -0
- package/connectors/connect-minimax/tsconfig.json +8 -0
- package/dist/index.js +8 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/serve.d.ts +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { MinimaxClient } from './client';
|
|
2
|
+
import type {
|
|
3
|
+
SoundEffectRequest,
|
|
4
|
+
SoundEffectResponse,
|
|
5
|
+
SoundEffectStatusResponse,
|
|
6
|
+
} from '../types';
|
|
7
|
+
|
|
8
|
+
export interface SoundEffectOptions {
|
|
9
|
+
duration?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class SoundEffectsApi {
|
|
13
|
+
constructor(private readonly client: MinimaxClient) {}
|
|
14
|
+
|
|
15
|
+
async generate(prompt: string, options: SoundEffectOptions = {}): Promise<SoundEffectResponse> {
|
|
16
|
+
const request: SoundEffectRequest = {
|
|
17
|
+
model: 'sound-effects-01',
|
|
18
|
+
prompt,
|
|
19
|
+
duration: options.duration,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
return this.client.post<SoundEffectResponse>('/sound_generation', request);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async getStatus(taskId: string): Promise<SoundEffectStatusResponse> {
|
|
26
|
+
return this.client.get<SoundEffectStatusResponse>('/query/sound_generation', { task_id: taskId });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async download(audioUrl: string): Promise<Buffer> {
|
|
30
|
+
return this.client.downloadFile(audioUrl);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async generateAndWait(
|
|
34
|
+
prompt: string,
|
|
35
|
+
options: SoundEffectOptions = {},
|
|
36
|
+
pollIntervalMs = 3000,
|
|
37
|
+
maxAttempts = 60
|
|
38
|
+
): Promise<{ audioUrl: string }> {
|
|
39
|
+
const job = await this.generate(prompt, options);
|
|
40
|
+
const taskId = job.task_id;
|
|
41
|
+
|
|
42
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
43
|
+
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
|
44
|
+
const status = await this.getStatus(taskId);
|
|
45
|
+
|
|
46
|
+
if (status.status === 'Success') {
|
|
47
|
+
const audioUrl = status.extra_info?.audio_url || status.audio_file;
|
|
48
|
+
if (!audioUrl) throw new Error('No audio URL in completed response');
|
|
49
|
+
return { audioUrl };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (status.status === 'Fail') {
|
|
53
|
+
throw new Error(`Sound effect generation failed: ${status.base_resp?.status_msg || 'Unknown error'}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
throw new Error('Sound effect generation timed out');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { MinimaxClient } from './client';
|
|
2
|
+
import type { TTSModel, TTSRequest, TTSResponse } from '../types';
|
|
3
|
+
|
|
4
|
+
export interface TTSOptions {
|
|
5
|
+
model?: TTSModel;
|
|
6
|
+
voiceId?: string;
|
|
7
|
+
speed?: number;
|
|
8
|
+
volume?: number;
|
|
9
|
+
pitch?: number;
|
|
10
|
+
emotion?: string;
|
|
11
|
+
format?: 'mp3' | 'wav' | 'pcm' | 'flac';
|
|
12
|
+
sampleRate?: number;
|
|
13
|
+
languageBoost?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class TTSApi {
|
|
17
|
+
constructor(private readonly client: MinimaxClient) {}
|
|
18
|
+
|
|
19
|
+
async generate(text: string, options: TTSOptions = {}): Promise<TTSResponse> {
|
|
20
|
+
const request: TTSRequest = {
|
|
21
|
+
model: options.model || 'speech-02-hd',
|
|
22
|
+
text,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
if (options.voiceId || options.speed || options.volume || options.pitch || options.emotion) {
|
|
26
|
+
request.voice_setting = {};
|
|
27
|
+
if (options.voiceId) request.voice_setting.voice_id = options.voiceId;
|
|
28
|
+
if (options.speed) request.voice_setting.speed = options.speed;
|
|
29
|
+
if (options.volume) request.voice_setting.vol = options.volume;
|
|
30
|
+
if (options.pitch) request.voice_setting.pitch = options.pitch;
|
|
31
|
+
if (options.emotion) request.voice_setting.emotion = options.emotion;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (options.format || options.sampleRate) {
|
|
35
|
+
request.audio_setting = {};
|
|
36
|
+
if (options.format) request.audio_setting.format = options.format;
|
|
37
|
+
if (options.sampleRate) request.audio_setting.sample_rate = options.sampleRate;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (options.languageBoost) {
|
|
41
|
+
request.language_boost = options.languageBoost;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return this.client.post<TTSResponse>('/t2a_v2', request);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async generateToBuffer(text: string, options: TTSOptions = {}): Promise<Buffer> {
|
|
48
|
+
const response = await this.generate(text, options);
|
|
49
|
+
|
|
50
|
+
if (!response.data?.audio) {
|
|
51
|
+
throw new Error('No audio data in TTS response');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return Buffer.from(response.data.audio, 'hex');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { MinimaxClient } from './client';
|
|
2
|
+
import type {
|
|
3
|
+
VideoModel,
|
|
4
|
+
VideoGenerateRequest,
|
|
5
|
+
VideoGenerateResponse,
|
|
6
|
+
VideoStatusResponse,
|
|
7
|
+
VideoFileResponse,
|
|
8
|
+
} from '../types';
|
|
9
|
+
|
|
10
|
+
export interface VideoOptions {
|
|
11
|
+
model?: VideoModel;
|
|
12
|
+
firstFrameImage?: string;
|
|
13
|
+
subjectReference?: string[];
|
|
14
|
+
promptOptimizer?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class VideoApi {
|
|
18
|
+
constructor(private readonly client: MinimaxClient) {}
|
|
19
|
+
|
|
20
|
+
async generate(prompt: string, options: VideoOptions = {}): Promise<VideoGenerateResponse> {
|
|
21
|
+
const request: VideoGenerateRequest = {
|
|
22
|
+
model: options.model || 'T2V-01',
|
|
23
|
+
prompt,
|
|
24
|
+
prompt_optimizer: options.promptOptimizer ?? true,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
if (options.firstFrameImage) {
|
|
28
|
+
request.first_frame_image = options.firstFrameImage;
|
|
29
|
+
request.model = options.model || 'I2V-01';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (options.subjectReference) {
|
|
33
|
+
request.subject_reference = options.subjectReference;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return this.client.post<VideoGenerateResponse>('/video_generation', request);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async getStatus(taskId: string): Promise<VideoStatusResponse> {
|
|
40
|
+
return this.client.get<VideoStatusResponse>('/query/video_generation', { task_id: taskId });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async getFileUrl(fileId: string): Promise<string> {
|
|
44
|
+
const response = await this.client.get<VideoFileResponse>('/files/retrieve', { file_id: fileId });
|
|
45
|
+
return response.file.download_url;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async download(fileId: string): Promise<Buffer> {
|
|
49
|
+
const url = await this.getFileUrl(fileId);
|
|
50
|
+
return this.client.downloadFile(url);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async generateAndWait(
|
|
54
|
+
prompt: string,
|
|
55
|
+
options: VideoOptions = {},
|
|
56
|
+
pollIntervalMs = 10000,
|
|
57
|
+
maxAttempts = 120
|
|
58
|
+
): Promise<{ fileId: string; downloadUrl: string }> {
|
|
59
|
+
const job = await this.generate(prompt, options);
|
|
60
|
+
const taskId = job.task_id;
|
|
61
|
+
|
|
62
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
63
|
+
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
|
64
|
+
const status = await this.getStatus(taskId);
|
|
65
|
+
|
|
66
|
+
if (status.status === 'Success' && status.file_id) {
|
|
67
|
+
const url = await this.getFileUrl(status.file_id);
|
|
68
|
+
return { fileId: status.file_id, downloadUrl: url };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (status.status === 'Fail') {
|
|
72
|
+
throw new Error(`Video generation failed: ${status.base_resp?.status_msg || 'Unknown error'}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
throw new Error('Video generation timed out');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { writeFile } from 'fs/promises';
|
|
4
|
+
import { resolve } from 'path';
|
|
5
|
+
import { Minimax } from '../api';
|
|
6
|
+
import {
|
|
7
|
+
getApiKey,
|
|
8
|
+
setApiKey,
|
|
9
|
+
getGroupId,
|
|
10
|
+
setGroupId,
|
|
11
|
+
clearConfig,
|
|
12
|
+
getConfigDir,
|
|
13
|
+
setProfileOverride,
|
|
14
|
+
getCurrentProfile,
|
|
15
|
+
setCurrentProfile,
|
|
16
|
+
listProfiles,
|
|
17
|
+
createProfile,
|
|
18
|
+
deleteProfile,
|
|
19
|
+
profileExists,
|
|
20
|
+
} from '../utils/config';
|
|
21
|
+
import { success, error, info, print } from '../utils/output';
|
|
22
|
+
import type { OutputFormat } from '../utils/output';
|
|
23
|
+
|
|
24
|
+
const CONNECTOR_NAME = 'connect-minimax';
|
|
25
|
+
const VERSION = '0.1.0';
|
|
26
|
+
|
|
27
|
+
const program = new Command();
|
|
28
|
+
|
|
29
|
+
program
|
|
30
|
+
.name(CONNECTOR_NAME)
|
|
31
|
+
.description('Minimax API connector - Video, music, image, TTS, and sound effects')
|
|
32
|
+
.version(VERSION)
|
|
33
|
+
.option('-k, --api-key <key>', 'API key (overrides config)')
|
|
34
|
+
.option('-f, --format <format>', 'Output format (json, pretty)', 'pretty')
|
|
35
|
+
.option('-p, --profile <profile>', 'Use a specific profile')
|
|
36
|
+
.hook('preAction', (thisCommand) => {
|
|
37
|
+
const opts = thisCommand.opts();
|
|
38
|
+
if (opts.profile) {
|
|
39
|
+
if (!profileExists(opts.profile)) {
|
|
40
|
+
error(`Profile "${opts.profile}" does not exist.`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
setProfileOverride(opts.profile);
|
|
44
|
+
}
|
|
45
|
+
if (opts.apiKey) process.env.MINIMAX_API_KEY = opts.apiKey;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
function getFormat(cmd: Command): OutputFormat {
|
|
49
|
+
return (cmd.parent?.opts().format || 'pretty') as OutputFormat;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getClient(): Minimax {
|
|
53
|
+
const apiKey = getApiKey();
|
|
54
|
+
if (!apiKey) {
|
|
55
|
+
error(`No API key configured. Run "${CONNECTOR_NAME} config set-key <key>" or set MINIMAX_API_KEY.`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
const groupId = getGroupId();
|
|
59
|
+
return new Minimax({ apiKey, groupId });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Config
|
|
63
|
+
const configCmd = program.command('config').description('Manage configuration');
|
|
64
|
+
|
|
65
|
+
configCmd.command('set-key <key>').description('Set API key').action((key) => {
|
|
66
|
+
setApiKey(key);
|
|
67
|
+
success('API key saved');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
configCmd.command('set-group <id>').description('Set group ID').action((id) => {
|
|
71
|
+
setGroupId(id);
|
|
72
|
+
success('Group ID saved');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
configCmd.command('show').description('Show current config').action(() => {
|
|
76
|
+
const key = getApiKey();
|
|
77
|
+
const group = getGroupId();
|
|
78
|
+
info(`Profile: ${getCurrentProfile()}`);
|
|
79
|
+
info(`API Key: ${key ? key.substring(0, 6) + '...' : 'not set'}`);
|
|
80
|
+
info(`Group ID: ${group || 'not set'}`);
|
|
81
|
+
info(`Config dir: ${getConfigDir()}`);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
configCmd.command('clear').description('Clear config').action(() => {
|
|
85
|
+
clearConfig();
|
|
86
|
+
success('Config cleared');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Profile
|
|
90
|
+
const profileCmd = program.command('profile').description('Manage profiles');
|
|
91
|
+
profileCmd.command('list').description('List profiles').action(() => {
|
|
92
|
+
const profiles = listProfiles();
|
|
93
|
+
const current = getCurrentProfile();
|
|
94
|
+
if (profiles.length === 0) { info('No profiles'); return; }
|
|
95
|
+
profiles.forEach(p => console.log(p === current ? `* ${p}` : ` ${p}`));
|
|
96
|
+
});
|
|
97
|
+
profileCmd.command('create <name>').description('Create profile').action((name) => {
|
|
98
|
+
createProfile(name) ? success(`Profile "${name}" created`) : error(`Profile "${name}" already exists`);
|
|
99
|
+
});
|
|
100
|
+
profileCmd.command('use <name>').description('Switch profile').action((name) => {
|
|
101
|
+
setCurrentProfile(name);
|
|
102
|
+
success(`Switched to profile "${name}"`);
|
|
103
|
+
});
|
|
104
|
+
profileCmd.command('delete <name>').description('Delete profile').action((name) => {
|
|
105
|
+
deleteProfile(name) ? success(`Profile "${name}" deleted`) : error(`Cannot delete "${name}"`);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Video
|
|
109
|
+
const videoCmd = program.command('video').description('Video generation');
|
|
110
|
+
videoCmd
|
|
111
|
+
.command('generate <prompt>')
|
|
112
|
+
.description('Generate a video from a text prompt')
|
|
113
|
+
.option('-m, --model <model>', 'Model (T2V-01, I2V-01)', 'T2V-01')
|
|
114
|
+
.option('-o, --output <path>', 'Save video to file')
|
|
115
|
+
.option('--image <url>', 'First frame image (switches to I2V)')
|
|
116
|
+
.option('--no-optimize', 'Disable prompt optimizer')
|
|
117
|
+
.action(async (prompt, opts, cmd) => {
|
|
118
|
+
const client = getClient();
|
|
119
|
+
info('Starting video generation...');
|
|
120
|
+
try {
|
|
121
|
+
const result = await client.video.generateAndWait(prompt, {
|
|
122
|
+
model: opts.model,
|
|
123
|
+
firstFrameImage: opts.image,
|
|
124
|
+
promptOptimizer: opts.optimize !== false,
|
|
125
|
+
});
|
|
126
|
+
if (opts.output) {
|
|
127
|
+
const buffer = await client.video.download(result.fileId);
|
|
128
|
+
await writeFile(resolve(opts.output), buffer);
|
|
129
|
+
success(`Video saved to: ${opts.output}`);
|
|
130
|
+
} else {
|
|
131
|
+
print(result, getFormat(cmd));
|
|
132
|
+
}
|
|
133
|
+
} catch (e: any) { error(e.message); process.exit(1); }
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Music
|
|
137
|
+
const musicCmd = program.command('music').description('Music generation');
|
|
138
|
+
musicCmd
|
|
139
|
+
.command('generate <prompt>')
|
|
140
|
+
.description('Generate music from a prompt')
|
|
141
|
+
.option('-o, --output <path>', 'Save audio to file')
|
|
142
|
+
.option('--lyrics <text>', 'Lyrics for the song')
|
|
143
|
+
.option('--genre <genre>', 'Music genre')
|
|
144
|
+
.option('--mood <mood>', 'Desired mood')
|
|
145
|
+
.option('--tempo <bpm>', 'Tempo in BPM', parseInt)
|
|
146
|
+
.option('--duration <seconds>', 'Duration in seconds', parseInt)
|
|
147
|
+
.action(async (prompt, opts, cmd) => {
|
|
148
|
+
const client = getClient();
|
|
149
|
+
info('Starting music generation...');
|
|
150
|
+
try {
|
|
151
|
+
const result = await client.music.generateAndWait(prompt, {
|
|
152
|
+
lyrics: opts.lyrics,
|
|
153
|
+
genre: opts.genre,
|
|
154
|
+
mood: opts.mood,
|
|
155
|
+
tempo: opts.tempo,
|
|
156
|
+
duration: opts.duration,
|
|
157
|
+
});
|
|
158
|
+
if (opts.output) {
|
|
159
|
+
const buffer = await client.music.download(result.audioUrl);
|
|
160
|
+
await writeFile(resolve(opts.output), buffer);
|
|
161
|
+
success(`Music saved to: ${opts.output}`);
|
|
162
|
+
} else {
|
|
163
|
+
print(result, getFormat(cmd));
|
|
164
|
+
}
|
|
165
|
+
} catch (e: any) { error(e.message); process.exit(1); }
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// TTS
|
|
169
|
+
const ttsCmd = program.command('tts').description('Text-to-speech');
|
|
170
|
+
ttsCmd
|
|
171
|
+
.command('generate <text>')
|
|
172
|
+
.description('Generate speech from text')
|
|
173
|
+
.option('-o, --output <path>', 'Save audio to file (required)')
|
|
174
|
+
.option('-m, --model <model>', 'Model', 'speech-02-hd')
|
|
175
|
+
.option('--voice <id>', 'Voice ID')
|
|
176
|
+
.option('--speed <n>', 'Speed (0.5-2.0)', parseFloat)
|
|
177
|
+
.option('--format <fmt>', 'Audio format (mp3, wav, flac)', 'mp3')
|
|
178
|
+
.option('--language <code>', 'Language boost code')
|
|
179
|
+
.action(async (text, opts, cmd) => {
|
|
180
|
+
const client = getClient();
|
|
181
|
+
if (!opts.output) { error('--output is required'); process.exit(1); }
|
|
182
|
+
info('Generating speech...');
|
|
183
|
+
try {
|
|
184
|
+
const buffer = await client.tts.generateToBuffer(text, {
|
|
185
|
+
model: opts.model,
|
|
186
|
+
voiceId: opts.voice,
|
|
187
|
+
speed: opts.speed,
|
|
188
|
+
format: opts.format,
|
|
189
|
+
languageBoost: opts.language,
|
|
190
|
+
});
|
|
191
|
+
await writeFile(resolve(opts.output), buffer);
|
|
192
|
+
success(`Audio saved to: ${opts.output}`);
|
|
193
|
+
} catch (e: any) { error(e.message); process.exit(1); }
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Image
|
|
197
|
+
const imageCmd = program.command('image').description('Image generation');
|
|
198
|
+
imageCmd
|
|
199
|
+
.command('generate <prompt>')
|
|
200
|
+
.description('Generate an image from a prompt')
|
|
201
|
+
.option('-o, --output <path>', 'Save image to file')
|
|
202
|
+
.option('--aspect <ratio>', 'Aspect ratio (1:1, 16:9, 9:16, 4:3, 3:4)', '1:1')
|
|
203
|
+
.option('-n, --count <n>', 'Number of images', parseInt, 1)
|
|
204
|
+
.action(async (prompt, opts, cmd) => {
|
|
205
|
+
const client = getClient();
|
|
206
|
+
info('Starting image generation...');
|
|
207
|
+
try {
|
|
208
|
+
const result = await client.image.generateAndWait(prompt, {
|
|
209
|
+
aspectRatio: opts.aspect,
|
|
210
|
+
n: opts.count,
|
|
211
|
+
});
|
|
212
|
+
if (opts.output) {
|
|
213
|
+
const buffer = await client.image.download(result.fileId);
|
|
214
|
+
await writeFile(resolve(opts.output), buffer);
|
|
215
|
+
success(`Image saved to: ${opts.output}`);
|
|
216
|
+
} else {
|
|
217
|
+
print(result, getFormat(cmd));
|
|
218
|
+
}
|
|
219
|
+
} catch (e: any) { error(e.message); process.exit(1); }
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Sound Effects
|
|
223
|
+
const sfxCmd = program.command('sfx').description('Sound effects generation');
|
|
224
|
+
sfxCmd
|
|
225
|
+
.command('generate <prompt>')
|
|
226
|
+
.description('Generate a sound effect from a prompt')
|
|
227
|
+
.option('-o, --output <path>', 'Save audio to file')
|
|
228
|
+
.option('--duration <seconds>', 'Duration in seconds', parseInt)
|
|
229
|
+
.action(async (prompt, opts, cmd) => {
|
|
230
|
+
const client = getClient();
|
|
231
|
+
info('Generating sound effect...');
|
|
232
|
+
try {
|
|
233
|
+
const result = await client.soundEffects.generateAndWait(prompt, {
|
|
234
|
+
duration: opts.duration,
|
|
235
|
+
});
|
|
236
|
+
if (opts.output) {
|
|
237
|
+
const buffer = await client.soundEffects.download(result.audioUrl);
|
|
238
|
+
await writeFile(resolve(opts.output), buffer);
|
|
239
|
+
success(`Sound effect saved to: ${opts.output}`);
|
|
240
|
+
} else {
|
|
241
|
+
print(result, getFormat(cmd));
|
|
242
|
+
}
|
|
243
|
+
} catch (e: any) { error(e.message); process.exit(1); }
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
program.parse();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { Minimax, Connector } from './api';
|
|
2
|
+
export * from './types';
|
|
3
|
+
|
|
4
|
+
export { MinimaxClient, VideoApi, MusicApi, TTSApi, ImageApi, SoundEffectsApi } from './api';
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
getApiKey,
|
|
8
|
+
setApiKey,
|
|
9
|
+
getGroupId,
|
|
10
|
+
setGroupId,
|
|
11
|
+
getCurrentProfile,
|
|
12
|
+
setCurrentProfile,
|
|
13
|
+
listProfiles,
|
|
14
|
+
createProfile,
|
|
15
|
+
deleteProfile,
|
|
16
|
+
loadProfile,
|
|
17
|
+
saveProfile,
|
|
18
|
+
clearConfig,
|
|
19
|
+
} from './utils/config';
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
export interface MinimaxConfig {
|
|
2
|
+
apiKey: string;
|
|
3
|
+
groupId?: string;
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// Models
|
|
8
|
+
export type VideoModel = 'T2V-01' | 'T2V-01-Director' | 'I2V-01' | 'I2V-01-Director' | 'S2V-01';
|
|
9
|
+
export type MusicModel = 'music-01';
|
|
10
|
+
export type TTSModel = 'speech-02' | 'speech-02-hd' | 'speech-02-turbo';
|
|
11
|
+
export type ImageModel = 'image-01';
|
|
12
|
+
|
|
13
|
+
// Video Generation
|
|
14
|
+
export interface VideoGenerateRequest {
|
|
15
|
+
model: VideoModel;
|
|
16
|
+
prompt?: string;
|
|
17
|
+
first_frame_image?: string;
|
|
18
|
+
subject_reference?: string[];
|
|
19
|
+
prompt_optimizer?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface VideoGenerateResponse {
|
|
23
|
+
task_id: string;
|
|
24
|
+
base_resp?: { status_code: number; status_msg: string };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface VideoStatusResponse {
|
|
28
|
+
task_id: string;
|
|
29
|
+
status: 'Queueing' | 'Processing' | 'Success' | 'Fail';
|
|
30
|
+
file_id?: string;
|
|
31
|
+
base_resp?: { status_code: number; status_msg: string };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface VideoFileResponse {
|
|
35
|
+
file: {
|
|
36
|
+
file_id: string;
|
|
37
|
+
bytes: number;
|
|
38
|
+
created_at: number;
|
|
39
|
+
filename: string;
|
|
40
|
+
purpose: string;
|
|
41
|
+
download_url: string;
|
|
42
|
+
};
|
|
43
|
+
base_resp?: { status_code: number; status_msg: string };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Music Generation
|
|
47
|
+
export interface MusicGenerateRequest {
|
|
48
|
+
model: MusicModel;
|
|
49
|
+
lyrics?: string;
|
|
50
|
+
refer_voice?: string;
|
|
51
|
+
refer_instrumental?: string;
|
|
52
|
+
prompt?: string;
|
|
53
|
+
genre?: string;
|
|
54
|
+
mood?: string;
|
|
55
|
+
tempo?: number;
|
|
56
|
+
duration?: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface MusicGenerateResponse {
|
|
60
|
+
task_id: string;
|
|
61
|
+
base_resp?: { status_code: number; status_msg: string };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface MusicStatusResponse {
|
|
65
|
+
task_id: string;
|
|
66
|
+
status: 'Queueing' | 'Processing' | 'Success' | 'Fail';
|
|
67
|
+
audio_file?: string;
|
|
68
|
+
extra_info?: {
|
|
69
|
+
audio_url?: string;
|
|
70
|
+
lyrics?: string;
|
|
71
|
+
instrumental_url?: string;
|
|
72
|
+
};
|
|
73
|
+
base_resp?: { status_code: number; status_msg: string };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// TTS (Text to Audio)
|
|
77
|
+
export interface TTSRequest {
|
|
78
|
+
model: TTSModel;
|
|
79
|
+
text: string;
|
|
80
|
+
voice_setting?: {
|
|
81
|
+
voice_id?: string;
|
|
82
|
+
speed?: number;
|
|
83
|
+
vol?: number;
|
|
84
|
+
pitch?: number;
|
|
85
|
+
emotion?: string;
|
|
86
|
+
};
|
|
87
|
+
audio_setting?: {
|
|
88
|
+
sample_rate?: number;
|
|
89
|
+
bitrate?: number;
|
|
90
|
+
format?: 'mp3' | 'wav' | 'pcm' | 'flac';
|
|
91
|
+
channel?: number;
|
|
92
|
+
};
|
|
93
|
+
language_boost?: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface TTSResponse {
|
|
97
|
+
data?: {
|
|
98
|
+
audio?: string;
|
|
99
|
+
};
|
|
100
|
+
extra_info?: {
|
|
101
|
+
audio_length?: number;
|
|
102
|
+
audio_sample_rate?: number;
|
|
103
|
+
audio_size?: number;
|
|
104
|
+
bitrate?: number;
|
|
105
|
+
word_count?: number;
|
|
106
|
+
invisible_character_ratio?: number;
|
|
107
|
+
};
|
|
108
|
+
base_resp?: { status_code: number; status_msg: string };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Image Generation
|
|
112
|
+
export interface ImageGenerateRequest {
|
|
113
|
+
model: ImageModel;
|
|
114
|
+
prompt: string;
|
|
115
|
+
aspect_ratio?: '1:1' | '16:9' | '9:16' | '4:3' | '3:4';
|
|
116
|
+
n?: number;
|
|
117
|
+
prompt_optimizer?: boolean;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface ImageGenerateResponse {
|
|
121
|
+
task_id: string;
|
|
122
|
+
base_resp?: { status_code: number; status_msg: string };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface ImageStatusResponse {
|
|
126
|
+
task_id: string;
|
|
127
|
+
status: 'Queueing' | 'Processing' | 'Success' | 'Fail';
|
|
128
|
+
file_id?: string;
|
|
129
|
+
base_resp?: { status_code: number; status_msg: string };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Sound Effects
|
|
133
|
+
export interface SoundEffectRequest {
|
|
134
|
+
model: string;
|
|
135
|
+
prompt: string;
|
|
136
|
+
duration?: number;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface SoundEffectResponse {
|
|
140
|
+
task_id: string;
|
|
141
|
+
base_resp?: { status_code: number; status_msg: string };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface SoundEffectStatusResponse {
|
|
145
|
+
task_id: string;
|
|
146
|
+
status: 'Queueing' | 'Processing' | 'Success' | 'Fail';
|
|
147
|
+
audio_file?: string;
|
|
148
|
+
extra_info?: {
|
|
149
|
+
audio_url?: string;
|
|
150
|
+
};
|
|
151
|
+
base_resp?: { status_code: number; status_msg: string };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Voice Clone
|
|
155
|
+
export interface VoiceCloneRequest {
|
|
156
|
+
file: Buffer;
|
|
157
|
+
voice_id?: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface VoiceListResponse {
|
|
161
|
+
voices: Array<{
|
|
162
|
+
voice_id: string;
|
|
163
|
+
name: string;
|
|
164
|
+
language?: string;
|
|
165
|
+
description?: string;
|
|
166
|
+
}>;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Shared
|
|
170
|
+
export type OutputFormat = 'json' | 'pretty';
|
|
171
|
+
|
|
172
|
+
export class MinimaxApiError extends Error {
|
|
173
|
+
public readonly statusCode: number;
|
|
174
|
+
public readonly error?: { status_code: number; status_msg: string };
|
|
175
|
+
|
|
176
|
+
constructor(message: string, statusCode: number, error?: { status_code: number; status_msg: string }) {
|
|
177
|
+
super(message);
|
|
178
|
+
this.name = 'MinimaxApiError';
|
|
179
|
+
this.statusCode = statusCode;
|
|
180
|
+
this.error = error;
|
|
181
|
+
}
|
|
182
|
+
}
|