@j-o-r/hello-dave 0.1.0 → 0.1.4
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/CHANGELOG.md +42 -25
- package/README.md +81 -221
- package/TODO.md +173 -35
- package/agents/agent_creator.js +105 -0
- package/agents/agent_creator.prompt.md +371 -0
- package/agents/ask_agent.js +64 -127
- package/agents/claude_agent.js +68 -0
- package/agents/code_agent.js +55 -135
- package/agents/code_agent.prompt.md +50 -0
- package/agents/echo_agent.js +76 -0
- package/agents/financial_expert.js +75 -0
- package/agents/gpt_agent.js +52 -103
- package/agents/gpt_code.js +81 -0
- package/agents/grok_agent.js +58 -114
- package/agents/minimax_agent.js +92 -0
- package/agents/mureka_agent.js +77 -0
- package/agents/planner_agent.js +172 -0
- package/agents/stability_agent.js +87 -0
- package/agents/test_agent.js +75 -157
- package/agents/weather_agent.js +73 -0
- package/agents/workflow_agent.js +189 -0
- package/bin/dave.js +436 -184
- package/docs/bin-dave.md +85 -35
- package/docs/cdn-ssh.md +100 -0
- package/docs/creating-agents.md +301 -0
- package/docs/creating-toolsets.md +336 -0
- package/docs/docs-organization.md +48 -0
- package/docs/project-overview.md +86 -51
- package/lib/API/elevenlabs.io/music.compose.md +441 -0
- package/lib/API/elevenlabs.io/music.create-composition-plan.md +370 -0
- package/lib/API/elevenlabs.io/music.stream.md +425 -0
- package/lib/API/lalal.ai/lalal.js +445 -0
- package/lib/API/lalal.ai/openapi.json +2614 -0
- package/lib/API/minimax/ImageToolset.js +82 -37
- package/lib/API/minimax/MusicToolset.js +125 -79
- package/lib/API/minimax/VideoToolset.js +170 -167
- package/lib/API/minimax/image.js +5 -1
- package/lib/API/minimax/music.js +210 -23
- package/lib/API/minimax/video.js +242 -53
- package/lib/API/mureka/MusicToolset.js +646 -0
- package/lib/API/mureka/README.md +41 -0
- package/lib/API/mureka/index.js +7 -0
- package/lib/API/mureka/music.js +658 -0
- package/lib/API/openai.com/index.js +7 -0
- package/lib/API/openai.com/{reponses/text.js → responses.js} +64 -18
- package/lib/API/openai.com/video.create.character.md +40 -0
- package/lib/API/openai.com/video.create.md +219 -0
- package/lib/API/openai.com/video.delete.md +44 -0
- package/lib/API/openai.com/video.download.md +31 -0
- package/lib/API/openai.com/video.edit.md +155 -0
- package/lib/API/openai.com/video.extend.md +166 -0
- package/lib/API/openai.com/video.fetch.character.md +43 -0
- package/lib/API/openai.com/video.js +784 -0
- package/lib/API/openai.com/video.list.md +201 -0
- package/lib/API/openai.com/video.remix.md +175 -0
- package/lib/API/openai.com/video.retrieve.md +139 -0
- package/lib/API/openai.com/videoToolset.js +616 -0
- package/lib/API/stability.ai/ImageToolset.js +131 -40
- package/lib/API/stability.ai/MusicToolset.js +79 -47
- package/lib/API/stability.ai/audio.js +63 -131
- package/lib/API/x.ai/chat.responses.md +1040 -0
- package/lib/API/x.ai/image.js +229 -59
- package/lib/API/x.ai/imageToolset.js +376 -0
- package/lib/API/x.ai/index.js +1 -3
- package/lib/API/x.ai/responses.js +9 -18
- package/lib/Agent.js +271 -0
- package/lib/Agent.js.old +284 -0
- package/lib/AgentLauncher.js +562 -0
- package/lib/Cli.js +87 -13
- package/lib/Prompt.js +23 -1
- package/lib/Session.js +5 -4
- package/lib/ToolSet.js +102 -6
- package/lib/agentLoader.js +369 -0
- package/lib/cdn.js +67 -231
- package/lib/{CdnToolset.js → cdnToolset.js} +47 -64
- package/lib/defaultToolsets.js +43 -0
- package/lib/fafs.js +1 -1
- package/lib/genericToolset.js +442 -119
- package/lib/handOffToolset.js +179 -0
- package/lib/index.js +34 -27
- package/lib/toolsetLoader.js +248 -0
- package/package.json +11 -5
- package/types/API/lalal.ai/lalal.d.ts +116 -0
- package/types/API/minimax/image.d.ts +2 -1
- package/types/API/minimax/music.d.ts +189 -26
- package/types/API/minimax/video.d.ts +100 -31
- package/types/API/mureka/index.d.ts +7 -0
- package/types/API/mureka/music.d.ts +472 -0
- package/types/API/openai.com/index.d.ts +7 -0
- package/types/API/openai.com/{reponses/text.d.ts → responses.d.ts} +11 -11
- package/types/API/openai.com/video.d.ts +409 -0
- package/types/API/openai.com/videoToolset.d.ts +24 -0
- package/types/API/stability.ai/audio.d.ts +14 -103
- package/types/API/stability.ai/image.d.ts +2 -2
- package/types/API/x.ai/image.d.ts +138 -26
- package/types/API/x.ai/imageToolset.d.ts +3 -0
- package/types/API/x.ai/index.d.ts +1 -3
- package/types/API/x.ai/responses.d.ts +4 -4
- package/types/Agent.d.ts +123 -0
- package/types/AgentLauncher.d.ts +222 -0
- package/types/Cli.d.ts +28 -8
- package/types/Prompt.d.ts +23 -5
- package/types/Session.d.ts +1 -1
- package/types/ToolSet.d.ts +10 -0
- package/types/agentLoader.d.ts +78 -0
- package/types/cdn.d.ts +15 -90
- package/types/defaultToolsets.d.ts +9 -0
- package/types/fafs.d.ts +1 -1
- package/types/genericToolset.d.ts +1 -1
- package/types/handOffToolset.d.ts +28 -0
- package/types/index.d.ts +19 -16
- package/types/toolsetLoader.d.ts +114 -0
- package/utils/format_log.js +101 -23
- package/utils/launch_agent.js +18 -0
- package/utils/list_sessions.sh +13 -5
- package/utils/search_sessions.sh +65 -29
- package/utils/toolsets.js +33 -0
- package/README.md.bak.1779452127 +0 -240
- package/agents/codeserver.sh +0 -47
- package/agents/daisy_agent.js +0 -173
- package/agents/docs_agent.js +0 -148
- package/agents/memory_agent.js +0 -263
- package/agents/minimax.js +0 -173
- package/agents/npm_agent.js +0 -202
- package/agents/prompt_agent.js +0 -133
- package/agents/readme_agent.js +0 -148
- package/agents/spawn_agent.js +0 -160
- package/agents/stability.js +0 -173
- package/agents/todo_agent.js +0 -175
- package/bin/codeDave +0 -58
- package/docs/agent-dave-websocket-protocol.md +0 -180
- package/docs/agent-manager.md +0 -244
- package/docs/codeserver-pattern.md +0 -191
- package/docs/generic-toolset.md +0 -326
- package/docs/howtos/agent-networking.md +0 -253
- package/docs/howtos/spawn-agents.md.bak +0 -200
- package/docs/howtos/spawn-agents.md.bak_new +0 -200
- package/docs/multi-agent-clusters.md +0 -265
- package/docs/music-toolsets.md +0 -137
- package/docs/path-resolution-best-practices.md +0 -104
- package/docs/plans/minimax-music-generation.md +0 -80
- package/docs/plans/unified-agent-architecture.md +0 -146
- package/docs/plans/websocket-streaming-plan.md.bak +0 -317
- package/docs/prompt/spawn_agent.md +0 -175
- package/docs/prompt/spawn_agent.md.bak +0 -201
- package/docs/prompt/task_clarification_and_documentation.md +0 -35
- package/docs/prompt-class.md +0 -141
- package/docs/todo-archive-infra-2026-04-21.md +0 -15
- package/docs/todo-archive-v0.0.8.md +0 -1
- package/docs/todo-archive-v0.1.0.md +0 -32
- package/docs/todo-archive.md +0 -44
- package/docs/tools-syntax-validation.md +0 -121
- package/docs/toolset.md +0 -164
- package/docs/xai-responses.md +0 -111
- package/docs/xai_collections.md +0 -106
- package/lib/API/x.ai/ImageToolset.js +0 -165
- package/lib/API/x.ai/text.js +0 -415
- package/lib/AgentClient.js +0 -248
- package/lib/AgentManager.js +0 -245
- package/lib/AgentServer.js +0 -404
- package/lib/wsCli.js +0 -287
- package/lib/wsIO.js +0 -90
- package/types/API/x.ai/text.d.ts +0 -286
- package/types/AgentClient.d.ts +0 -109
- package/types/AgentManager.d.ts +0 -100
- package/types/AgentServer.d.ts +0 -89
- package/types/wsCli.d.ts +0 -17
- package/types/wsIO.d.ts +0 -30
- package/utils/test.sh +0 -46
- /package/docs/{suggestions.md → _notes/token-counts.md} +0 -0
- /package/lib/API/openai.com/{reponses/MESSAGES.md → MESSAGES.md} +0 -0
- /package/types/API/{x.ai/ImageToolset.d.ts → mureka/MusicToolset.d.ts} +0 -0
- /package/types/{CdnToolset.d.ts → cdnToolset.d.ts} +0 -0
package/lib/API/minimax/video.js
CHANGED
|
@@ -2,23 +2,39 @@
|
|
|
2
2
|
* @file lib/API/minimax/video.js
|
|
3
3
|
* @module minimax/video
|
|
4
4
|
* @description Pure HTTP wrapper for the Minimax Video Generation API.
|
|
5
|
-
* Fully aligned with the official documentation:
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
11
|
-
* - lib/API/minimax/video.from.subject.md
|
|
5
|
+
* Fully aligned with the official documentation in:
|
|
6
|
+
* - video.generation.md (Text-to-Video)
|
|
7
|
+
* - video.from.image.md (Image-to-Video)
|
|
8
|
+
* - video.first.last.image.md (First & Last Frame)
|
|
9
|
+
* - video.from.subject.md (Subject Reference)
|
|
10
|
+
* - video.query.md + video.download.md
|
|
12
11
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* ============================================================
|
|
13
|
+
* CLEAR MODEL ↔ TASK / MODE MAPPING (from official docs)
|
|
14
|
+
* ============================================================
|
|
15
|
+
*
|
|
16
|
+
* | Mode / Task | Supported Models | Required Fields | Notes |
|
|
17
|
+
* |------------------------------|-------------------------------------------------------|----------------------------------|-------|
|
|
18
|
+
* | Text-to-Video | MiniMax-Hailuo-2.3, MiniMax-Hailuo-02, T2V-01-Director, T2V-01 | prompt | Default mode |
|
|
19
|
+
* | Image-to-Video | MiniMax-Hailuo-2.3, MiniMax-Hailuo-2.3-Fast, MiniMax-Hailuo-02, I2V-01-Director, I2V-01-live, I2V-01 | first_frame_image | - |
|
|
20
|
+
* | First & Last Frame Video | **MiniMax-Hailuo-02 ONLY** | last_frame_image (first optional) | Does NOT support 512P. Error if you use Hailuo-2.3 here. |
|
|
21
|
+
* | Subject-Reference Video | **S2V-01 ONLY** | subject_reference (array) | Character/face reference |
|
|
22
|
+
*
|
|
23
|
+
* Default model in this wrapper: 'MiniMax-Hailuo-2.3'
|
|
24
|
+
* → This is why you got the error with First-and-Last-Frame + Hailuo-2.3.
|
|
25
|
+
*
|
|
26
|
+
* ============================================================
|
|
27
|
+
* IMAGE REFERENCE RULES (STRICT)
|
|
28
|
+
* ============================================================
|
|
29
|
+
* - All image references (`first_frame_image`, `last_frame_image`, `subject_reference[].image`)
|
|
30
|
+
* **MUST be public HTTP/HTTPS URLs**.
|
|
31
|
+
* - **Base64 / data: URLs are NOT supported** and will be rejected with a clear error.
|
|
32
|
+
* - The wrapper validates this before calling the API.
|
|
33
|
+
* - For subject_reference (S2V-01): each item must have `image: [exactly one URL]` (length === 1).
|
|
15
34
|
*
|
|
16
35
|
* This module only handles communication with Minimax endpoints.
|
|
17
36
|
* It does NOT handle file publishing, SSH, or CDN logic.
|
|
18
37
|
*
|
|
19
|
-
* For publishing generated or reference files to a remote CDN,
|
|
20
|
-
* use the reusable `lib/cdn.js` module instead.
|
|
21
|
-
*
|
|
22
38
|
* Video generation is asynchronous:
|
|
23
39
|
* 1. Create task → returns task_id
|
|
24
40
|
* 2. Poll query until status === 'Success' (or Fail)
|
|
@@ -35,6 +51,128 @@ const BASE_URL = 'https://api.minimax.io/v1';
|
|
|
35
51
|
// All temporary files (audio, images, video) are saved under this directory
|
|
36
52
|
const TMP_DIR = path.join(process.cwd(), '.cache', 'minimax');
|
|
37
53
|
|
|
54
|
+
/** Supported modes for clear documentation and validation */
|
|
55
|
+
const SUPPORTED_MODES = ['text', 'image', 'first-last', 'subject'];
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Checks if a string is a valid public HTTP/HTTPS URL.
|
|
59
|
+
* Rejects data: URLs, Base64, and non-HTTP schemes.
|
|
60
|
+
* @param {string} str
|
|
61
|
+
* @returns {boolean}
|
|
62
|
+
*/
|
|
63
|
+
function isValidPublicUrl(str) {
|
|
64
|
+
if (typeof str !== 'string' || !str.trim()) return false;
|
|
65
|
+
const trimmed = str.trim();
|
|
66
|
+
if (trimmed.startsWith('data:')) return false; // explicitly rule out Base64
|
|
67
|
+
if (trimmed.startsWith('http://') || trimmed.startsWith('https://')) return true;
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Validates that an image reference is a public URL (rejects Base64).
|
|
73
|
+
* Throws a clear error if invalid.
|
|
74
|
+
* @param {string} fieldName - e.g. 'first_frame_image'
|
|
75
|
+
* @param {string} value
|
|
76
|
+
*/
|
|
77
|
+
function validateImageUrl(fieldName, value) {
|
|
78
|
+
if (!value) return;
|
|
79
|
+
if (!isValidPublicUrl(value)) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`${fieldName} must be a public HTTP/HTTPS URL. ` +
|
|
82
|
+
`Base64 / data: URLs are not supported. ` +
|
|
83
|
+
`Got: ${typeof value === 'string' ? value.substring(0, 80) + '...' : value}`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Validates subject_reference array for S2V-01.
|
|
90
|
+
* - Each reference must have `image` as an array of **exactly length 1**.
|
|
91
|
+
* - The single image must be a public HTTP/HTTPS URL (no Base64).
|
|
92
|
+
* Throws a clear, actionable error on any violation.
|
|
93
|
+
* @param {Array} subjectRef
|
|
94
|
+
*/
|
|
95
|
+
function validateSubjectReference(subjectRef) {
|
|
96
|
+
if (!subjectRef || !Array.isArray(subjectRef)) return;
|
|
97
|
+
|
|
98
|
+
subjectRef.forEach((ref, idx) => {
|
|
99
|
+
if (!ref) {
|
|
100
|
+
throw new Error(`subject_reference[${idx}] is invalid (must be an object).`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!Array.isArray(ref.image)) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
`subject_reference[${idx}].image must be an array containing exactly one public URL ` +
|
|
106
|
+
`(S2V-01 requirement). Got: ${typeof ref.image}`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (ref.image.length !== 1) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`subject_reference[${idx}].image must contain **exactly one** URL (length === 1). ` +
|
|
113
|
+
`S2V-01 does not support multiple images per reference. ` +
|
|
114
|
+
`Got length: ${ref.image.length}`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const img = ref.image[0];
|
|
119
|
+
if (!isValidPublicUrl(img)) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
`subject_reference[${idx}].image[0] must be a public HTTP/HTTPS URL. ` +
|
|
122
|
+
`Base64 / data: URLs are not supported. ` +
|
|
123
|
+
`Got: ${typeof img === 'string' ? img.substring(0, 80) + '...' : img}`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Returns the list of officially supported models for a given mode.
|
|
131
|
+
* @param {string} mode - 'text' | 'image' | 'first-last' | 'subject'
|
|
132
|
+
* @returns {string[]}
|
|
133
|
+
*/
|
|
134
|
+
function getSupportedModelsForMode(mode) {
|
|
135
|
+
switch (mode) {
|
|
136
|
+
case 'first-last':
|
|
137
|
+
return ['MiniMax-Hailuo-02'];
|
|
138
|
+
case 'subject':
|
|
139
|
+
return ['S2V-01'];
|
|
140
|
+
case 'image':
|
|
141
|
+
return ['MiniMax-Hailuo-2.3', 'MiniMax-Hailuo-2.3-Fast', 'MiniMax-Hailuo-02', 'I2V-01-Director', 'I2V-01-live', 'I2V-01'];
|
|
142
|
+
case 'text':
|
|
143
|
+
default:
|
|
144
|
+
return ['MiniMax-Hailuo-2.3', 'MiniMax-Hailuo-02', 'T2V-01-Director', 'T2V-01'];
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Auto-detects the most likely mode based on provided options.
|
|
150
|
+
* @param {Object} options
|
|
151
|
+
* @returns {string} one of SUPPORTED_MODES
|
|
152
|
+
*/
|
|
153
|
+
function detectMode(options = {}) {
|
|
154
|
+
if (options.last_frame_image) return 'first-last';
|
|
155
|
+
if (options.subject_reference) return 'subject';
|
|
156
|
+
if (options.first_frame_image) return 'image';
|
|
157
|
+
return 'text';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Validates that the chosen model is compatible with the detected/provided mode.
|
|
162
|
+
* Throws a clear, helpful error if not.
|
|
163
|
+
*/
|
|
164
|
+
function validateModelForMode(model, mode, options = {}) {
|
|
165
|
+
const supported = getSupportedModelsForMode(mode);
|
|
166
|
+
if (!supported.includes(model)) {
|
|
167
|
+
const supportedList = supported.join(', ');
|
|
168
|
+
throw new Error(
|
|
169
|
+
`Model "${model}" does NOT support "${mode}" mode.\n` +
|
|
170
|
+
`Supported models for "${mode}": ${supportedList}\n` +
|
|
171
|
+
`Please choose a compatible model or omit the model to use the default for this mode.`
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
38
176
|
/**
|
|
39
177
|
* Get authentication headers for Minimax API.
|
|
40
178
|
*
|
|
@@ -49,7 +187,7 @@ const getHeaders = () => {
|
|
|
49
187
|
'Content-Type': 'application/json',
|
|
50
188
|
'Authorization': `Bearer ${process.env.MINIMAX_API_KEY}`
|
|
51
189
|
};
|
|
52
|
-
}
|
|
190
|
+
}
|
|
53
191
|
|
|
54
192
|
/**
|
|
55
193
|
* Saves video data from a download URL to a local temporary file.
|
|
@@ -97,7 +235,6 @@ async function waitForVideoReady(taskId, maxWaitMs = 300000, pollIntervalMs = 50
|
|
|
97
235
|
const status = result.status;
|
|
98
236
|
|
|
99
237
|
if (status !== lastStatus) {
|
|
100
|
-
console.log(`[Minimax Video] Task ${taskId} status: ${status}`);
|
|
101
238
|
lastStatus = status;
|
|
102
239
|
}
|
|
103
240
|
|
|
@@ -111,7 +248,7 @@ async function waitForVideoReady(taskId, maxWaitMs = 300000, pollIntervalMs = 50
|
|
|
111
248
|
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
|
112
249
|
}
|
|
113
250
|
|
|
114
|
-
throw new Error(`Video generation timed out after ${maxWaitMs / 1000}s. Last status: ${lastStatus}`);
|
|
251
|
+
throw new Error(`minimax task_id : ${taskId} - Video generation timed out after ${maxWaitMs / 1000}s. Last status: ${lastStatus}`);
|
|
115
252
|
}
|
|
116
253
|
|
|
117
254
|
/**
|
|
@@ -120,30 +257,82 @@ async function waitForVideoReady(taskId, maxWaitMs = 300000, pollIntervalMs = 50
|
|
|
120
257
|
* Uses POST /v1/video_generation.
|
|
121
258
|
* Supports all documented models and input types via options.
|
|
122
259
|
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
260
|
+
* The function now includes **clear validation** so you get an explicit error
|
|
261
|
+
* instead of a cryptic API rejection.
|
|
262
|
+
*
|
|
263
|
+
* @param {string} [prompt=''] - Text description (required for most modes). Max 2000 chars.
|
|
264
|
+
* Supports camera commands in [] for supported models, e.g. [Pan left], [Truck right].
|
|
265
|
+
* @param {Object} [options={}] - Generation options.
|
|
266
|
+
*
|
|
267
|
+
* @param {string} [options.model] - Model name. If omitted, a sensible default is chosen based on mode.
|
|
268
|
+
* See the mapping table at the top of this file.
|
|
269
|
+
*
|
|
270
|
+
* @param {string} [options.first_frame_image] - **Public HTTP/HTTPS URL only** (no Base64/data: URLs).
|
|
271
|
+
*
|
|
272
|
+
* @param {string} [options.last_frame_image] - **Public HTTP/HTTPS URL only** (no Base64/data: URLs) — First & Last Frame mode only.
|
|
273
|
+
*
|
|
274
|
+
* @param {Array<Object>} [options.subject_reference] - For subject-reference mode (S2V-01 only).
|
|
275
|
+
* Each item must be `{ type: "character", image: ["https://..."] }` with **exactly one URL** in the image array.
|
|
276
|
+
*
|
|
277
|
+
* @param {boolean} [options.prompt_optimizer=true] - Auto-optimize prompt (default true).
|
|
278
|
+
*
|
|
279
|
+
* @param {boolean} [options.fast_pretreatment=false] - Reduce optimization time (Hailuo models only).
|
|
280
|
+
*
|
|
281
|
+
* @param {number} [options.duration=6] - Video length in seconds (model/resolution dependent).
|
|
282
|
+
*
|
|
283
|
+
* @param {string} [options.resolution] - e.g. '768P', '1080P', '720P' (model dependent).
|
|
125
284
|
*
|
|
126
|
-
* @param {string} [options.
|
|
127
|
-
* Supported: MiniMax-Hailuo-2.3, MiniMax-Hailuo-02, T2V-01, I2V-01, S2V-01, etc.
|
|
285
|
+
* @param {string} [options.callback_url] - Optional webhook for async status updates.
|
|
128
286
|
*
|
|
129
|
-
* @param {
|
|
130
|
-
* @param {string} [options.last_frame_image] - For first & last frame mode.
|
|
131
|
-
* @param {Array<Object>} [options.subject_reference] - For subject-reference mode.
|
|
287
|
+
* @param {Object} [options.extra] - Any additional parameters.
|
|
132
288
|
*
|
|
133
|
-
* @
|
|
134
|
-
* @param {boolean} [options.fast_pretreatment=false]
|
|
135
|
-
* @param {number} [options.duration=6]
|
|
136
|
-
* @param {string} [options.resolution] - e.g. '768P', '1080P'
|
|
137
|
-
* @param {string} [options.callback_url]
|
|
289
|
+
* @returns {Promise<{task_id: string, duration: number, raw: object}>}
|
|
138
290
|
*
|
|
139
|
-
* @
|
|
291
|
+
* @example
|
|
292
|
+
* // Text-to-Video (default)
|
|
293
|
+
* await createVideoGenerationTask("A cat jumping [Pan left]", { model: "MiniMax-Hailuo-2.3" });
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* // First & Last Frame — MUST use MiniMax-Hailuo-02 + public URLs
|
|
297
|
+
* await createVideoGenerationTask("", {
|
|
298
|
+
* model: "MiniMax-Hailuo-02",
|
|
299
|
+
* first_frame_image: "https://example.com/first.jpg",
|
|
300
|
+
* last_frame_image: "https://example.com/last.jpg"
|
|
301
|
+
* });
|
|
302
|
+
*
|
|
303
|
+
* @example
|
|
304
|
+
* // Subject-Reference (S2V-01) — exactly one image per reference
|
|
305
|
+
* await createVideoGenerationTask("...", {
|
|
306
|
+
* model: "S2V-01",
|
|
307
|
+
* subject_reference: [
|
|
308
|
+
* { type: "character", image: ["https://example.com/puppet.jpg"] },
|
|
309
|
+
* { type: "character", image: ["https://example.com/girl.png"] }
|
|
310
|
+
* ]
|
|
311
|
+
* });
|
|
140
312
|
*/
|
|
141
313
|
async function createVideoGenerationTask(prompt = '', options = {}) {
|
|
314
|
+
const mode = detectMode(options);
|
|
315
|
+
let model = options.model;
|
|
316
|
+
|
|
317
|
+
// Choose a sensible default if no model provided
|
|
318
|
+
if (!model) {
|
|
319
|
+
const supported = getSupportedModelsForMode(mode);
|
|
320
|
+
model = supported[0];
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Validate model compatibility
|
|
324
|
+
validateModelForMode(model, mode, options);
|
|
325
|
+
|
|
326
|
+
// === STRICT URL VALIDATION (no Base64) ===
|
|
327
|
+
validateImageUrl('first_frame_image', options.first_frame_image);
|
|
328
|
+
validateImageUrl('last_frame_image', options.last_frame_image);
|
|
329
|
+
validateSubjectReference(options.subject_reference);
|
|
330
|
+
|
|
142
331
|
const headers = getHeaders();
|
|
143
332
|
const url = `${BASE_URL}/video_generation`;
|
|
144
333
|
|
|
145
334
|
const body = {
|
|
146
|
-
model
|
|
335
|
+
model,
|
|
147
336
|
prompt: prompt,
|
|
148
337
|
prompt_optimizer: options.prompt_optimizer ?? true,
|
|
149
338
|
fast_pretreatment: options.fast_pretreatment ?? false,
|
|
@@ -161,7 +350,7 @@ async function createVideoGenerationTask(prompt = '', options = {}) {
|
|
|
161
350
|
const res = await doRequest(url, 'POST', headers, body);
|
|
162
351
|
const duration = Date.now() - start;
|
|
163
352
|
|
|
164
|
-
if (res.status !== 200) {
|
|
353
|
+
if (res.status !== 200 || !res.response.task_id) {
|
|
165
354
|
throw new Error(`Minimax video generation error ${res.status}: ${JSON.stringify(res.response)}`);
|
|
166
355
|
}
|
|
167
356
|
|
|
@@ -177,7 +366,7 @@ async function createVideoGenerationTask(prompt = '', options = {}) {
|
|
|
177
366
|
*
|
|
178
367
|
* GET /v1/query/video_generation?task_id=...
|
|
179
368
|
*
|
|
180
|
-
* @param {string} taskId
|
|
369
|
+
* @param {string} taskId - The task_id returned by createVideoGenerationTask.
|
|
181
370
|
* @returns {Promise<{
|
|
182
371
|
* task_id: string,
|
|
183
372
|
* status: 'Preparing' | 'Queueing' | 'Processing' | 'Success' | 'Fail',
|
|
@@ -193,7 +382,7 @@ async function queryVideoGenerationTask(taskId) {
|
|
|
193
382
|
|
|
194
383
|
const res = await doRequest(url, 'GET', headers);
|
|
195
384
|
|
|
196
|
-
if (res.status !== 200) {
|
|
385
|
+
if (res.status !== 200 || !res.response.task_id) {
|
|
197
386
|
throw new Error(`Minimax query error ${res.status}: ${JSON.stringify(res.response)}`);
|
|
198
387
|
}
|
|
199
388
|
|
|
@@ -212,7 +401,7 @@ async function queryVideoGenerationTask(taskId) {
|
|
|
212
401
|
*
|
|
213
402
|
* GET /v1/files/retrieve?file_id=...
|
|
214
403
|
*
|
|
215
|
-
* @param {string|number} fileId
|
|
404
|
+
* @param {string|number} fileId - The file_id from a successful query.
|
|
216
405
|
* @returns {Promise<{
|
|
217
406
|
* file_id: string,
|
|
218
407
|
* filename: string,
|
|
@@ -227,7 +416,7 @@ async function retrieveVideoFile(fileId) {
|
|
|
227
416
|
|
|
228
417
|
const res = await doRequest(url, 'GET', headers);
|
|
229
418
|
|
|
230
|
-
if (res.status !== 200) {
|
|
419
|
+
if (res.status !== 200 || !res.response.file) {
|
|
231
420
|
throw new Error(`Minimax retrieve file error ${res.status}: ${JSON.stringify(res.response)}`);
|
|
232
421
|
}
|
|
233
422
|
|
|
@@ -247,13 +436,13 @@ async function retrieveVideoFile(fileId) {
|
|
|
247
436
|
* Handles the full async flow: create task → poll until ready → retrieve → download to .cache/minimax/
|
|
248
437
|
*
|
|
249
438
|
* Supports all video generation modes (text, image-to-video, first-last, subject-ref)
|
|
250
|
-
* by passing the appropriate options.
|
|
439
|
+
* by passing the appropriate options. **Model validation + strict public-URL validation** are enforced.
|
|
251
440
|
*
|
|
252
441
|
* @param {string} prompt - Text prompt (can be empty for some modes).
|
|
253
|
-
* @param {Object} [options] - Same as createVideoGenerationTask + polling options.
|
|
442
|
+
* @param {Object} [options={}] - Same as createVideoGenerationTask + polling options.
|
|
254
443
|
*
|
|
255
|
-
* @param {number} [options.max_wait_ms=300000]
|
|
256
|
-
* @param {number} [options.poll_interval_ms=5000]
|
|
444
|
+
* @param {number} [options.max_wait_ms=300000] - Max polling time (5 minutes).
|
|
445
|
+
* @param {number} [options.poll_interval_ms=5000] - Polling interval in ms.
|
|
257
446
|
*
|
|
258
447
|
* @returns {Promise<{
|
|
259
448
|
* task_id: string,
|
|
@@ -267,24 +456,17 @@ async function retrieveVideoFile(fileId) {
|
|
|
267
456
|
* }>}
|
|
268
457
|
*
|
|
269
458
|
* @example
|
|
270
|
-
* //
|
|
271
|
-
* const result = await generateVideo("
|
|
272
|
-
* model: "MiniMax-Hailuo-
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
* });
|
|
276
|
-
*
|
|
277
|
-
* @example
|
|
278
|
-
* // Image-to-Video
|
|
279
|
-
* const result = await generateVideo("The character walks forward", {
|
|
280
|
-
* model: "MiniMax-Hailuo-2.3",
|
|
281
|
-
* first_frame_image: "https://example.com/image.jpg"
|
|
459
|
+
* // First & Last Frame (correct model + public URLs only)
|
|
460
|
+
* const result = await generateVideo("", {
|
|
461
|
+
* model: "MiniMax-Hailuo-02",
|
|
462
|
+
* first_frame_image: "https://example.com/first.jpg",
|
|
463
|
+
* last_frame_image: "https://example.com/last.jpg"
|
|
282
464
|
* });
|
|
283
465
|
*/
|
|
284
466
|
async function generateVideo(prompt = '', options = {}) {
|
|
285
467
|
const start = Date.now();
|
|
286
468
|
|
|
287
|
-
// Step 1: Create task
|
|
469
|
+
// Step 1: Create task (validation happens inside createVideoGenerationTask)
|
|
288
470
|
const createResult = await createVideoGenerationTask(prompt, options);
|
|
289
471
|
const taskId = createResult.task_id;
|
|
290
472
|
|
|
@@ -301,7 +483,7 @@ async function generateVideo(prompt = '', options = {}) {
|
|
|
301
483
|
|
|
302
484
|
const fileId = readyResult.file_id;
|
|
303
485
|
if (!fileId) {
|
|
304
|
-
throw new Error('No file_id in successful video response');
|
|
486
|
+
throw new Error('No file_id in successful video response:\n' + JSON.stringify(readyResult));
|
|
305
487
|
}
|
|
306
488
|
|
|
307
489
|
// Step 3: Retrieve download URL
|
|
@@ -335,5 +517,12 @@ export {
|
|
|
335
517
|
queryVideoGenerationTask,
|
|
336
518
|
retrieveVideoFile,
|
|
337
519
|
generateVideo,
|
|
338
|
-
waitForVideoReady
|
|
520
|
+
waitForVideoReady,
|
|
521
|
+
// New exports for clarity / testing
|
|
522
|
+
getSupportedModelsForMode,
|
|
523
|
+
detectMode,
|
|
524
|
+
validateModelForMode,
|
|
525
|
+
isValidPublicUrl,
|
|
526
|
+
validateImageUrl,
|
|
527
|
+
validateSubjectReference
|
|
339
528
|
};
|