makaron-cli 0.13.3 → 0.13.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/README.md +11 -7
- package/bin/makaron.mjs +102 -42
- package/package.json +1 -1
- package/skills/makaron/SKILL.md +11 -7
package/README.md
CHANGED
|
@@ -115,7 +115,7 @@ Returns immediately:
|
|
|
115
115
|
| Fix one moment in a video from a screenshot | `npx makaron-cli chat --project <id> --image screenshot.png "@4 this frame should be Paris; only fix this moment"` |
|
|
116
116
|
| Cut or assemble video | `npx makaron-cli chat --project <id> --video clip.mp4 "cut out the dead air and keep the best 20 seconds"` |
|
|
117
117
|
| Add music | `npx makaron-cli chat --project <id> "add calm piano background music"` |
|
|
118
|
-
| Beat-sync video from audio | `npx makaron-cli chat --project auto --audio beat.mp3
|
|
118
|
+
| Beat-sync video from audio | `npx makaron-cli chat --project auto --audio beat.mp3 "use Seedance Mini at 480p to make a beat-synced video"` |
|
|
119
119
|
| Create motion design | `npx makaron-cli chat --project <id> "make an animated Instagram story with this image"` |
|
|
120
120
|
|
|
121
121
|
### Marketplace skills
|
|
@@ -216,12 +216,13 @@ Attach a short song, beat, or voice recording when the video should follow audio
|
|
|
216
216
|
```bash
|
|
217
217
|
npx makaron-cli chat --project auto \
|
|
218
218
|
--audio beat.mp3 \
|
|
219
|
-
|
|
220
|
-
-b "make a 15s beat-synced video"
|
|
219
|
+
-b "use Seedance Mini at 480p to make a 15s beat-synced video"
|
|
221
220
|
```
|
|
222
221
|
|
|
223
222
|
`--audio` accepts repeatable local files or public URLs. Local MP3/WAV files must be 2-15s and <=15MB; reference audio currently works with Seedance video generation.
|
|
224
223
|
|
|
224
|
+
`chat` intentionally has no video model or resolution flags. State both in the chat message so the Agent selects a compatible provider and resolution together. Use `video create` only when you explicitly need direct provider controls.
|
|
225
|
+
|
|
225
226
|
### Fix one video moment from a screenshot
|
|
226
227
|
|
|
227
228
|
When a video is mostly good but one moment needs a local fix, attach a screenshot of the problem frame and describe the correction in normal language:
|
|
@@ -303,8 +304,9 @@ npx makaron-cli video create --script "Shot 1 (5s): <<<image_1>>> ..." --image h
|
|
|
303
304
|
npx makaron-cli video create --script "Shot 1 (5s): <<<image_1>>> slow cinematic push-in with native ambience" --image https://...jpg --duration 5 --video-model grok
|
|
304
305
|
npx makaron-cli video create --script "Shot 1 (15s): <<<image_1>>> and <<<image_2>>> build a neon one-person studio" --image https://...jpg --image https://...webp --duration 15 --video-model seedance-mini --video-resolution 480p --aspect 9:16
|
|
305
306
|
|
|
306
|
-
# 3b. Native SeeDance text-to-video (no image required)
|
|
307
|
+
# 3b. Native SeeDance or MiniMax H3 text-to-video (no image required)
|
|
307
308
|
npx makaron-cli video create --script "Shot 1 (5s): A neon one-person studio wakes at dawn" --duration 5 --video-model seedance-fast --aspect 16:9
|
|
309
|
+
npx makaron-cli video create --script "Shot 1 (15s): A premium creative editor comes alive" --duration 15 --video-model minimax-h3 --video-resolution 2k --aspect 16:9
|
|
308
310
|
|
|
309
311
|
# 3c. Edit a video from a local file or public URL
|
|
310
312
|
npx makaron-cli video create --script "make it funny" --video input.mp4 --duration 5 --video-model seedance-fast
|
|
@@ -320,9 +322,11 @@ For project/timeline video editing, use:
|
|
|
320
322
|
npx makaron-cli chat --project <id|auto> --video input.mp4 -b "make it funny"
|
|
321
323
|
```
|
|
322
324
|
|
|
323
|
-
Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to
|
|
325
|
+
Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to the selected model limit), `--video <file|url>` and `--audio <file|url>` (repeatable where supported), `--duration <seconds>`, `--aspect 9:16|16:9|1:1`, `--video-model seedance-fast|seedance-mini|seedance|seedance-2.5|kling|grok|google-omni|minimax-h3`, `--video-resolution auto|480p|720p|768p|1080p|2k|4k`. Default model is `seedance-fast`. SeeDance accepts native text-to-video with no image and integer output duration 4-15s (default 5s); `seedance-mini` supports 480p/720p and is best for cheaper drafts/multi-size tests; MiniMax H3 accepts native text-to-video, 4-15s output, and up to 9 image, up to 3 video, and up to 3 audio references through Makaron Agent/chat. H3 defaults to public 2k output; 768p requires provider preview access plus `MINIMAX_H3_ENABLE_768P=true` on the Makaron server. Kling supports 5-15s; Grok 1.5 supports 1-15s single-image-to-video only; Gemini Omni supports 3-10s fast 720p image/video generation and editing with native generated audio, including up to 6 image references when no video reference is provided. For `--video-model grok`, forced `--aspect` is ignored to avoid xAI stretching the source image; pad/create the image at the target shape first or use another model.
|
|
326
|
+
|
|
327
|
+
Seedance 2.5: use `--video-model seedance-2.5` for 4-30 second output at 480p/720p. `--image` accepts local files or URLs (up to 30), while repeatable `--video` and `--audio` accept up to 10 each. Use `--video-operation generate|edit|extend`, `--extend-direction forward|backward`, `--output-format mp4|mov`, `--web-search`, `--generated-audio` / `--no-generated-audio`, and `--relaxed-content-filter`. Edit/extend require a video reference. The Evolink route does not currently expose 4K output.
|
|
324
328
|
|
|
325
|
-
Video edit model behavior: `--video-model kling --video` uses Kling base/direct edit internally; `--video-model seedance-fast --video`, `--video-model seedance-mini --video`, or `--video-model seedance --video` uses the SeeDance video-reference path and requires target <=15s, <=50MB, width/height 300-6000px, aspect ratio 0.4-2.5, and frame pixels 409,600-2,086,876. `--video-model google-omni --video` uses Gemini Omni direct video editing and accepts one reference video in Makaron. Output duration is clamped to 3-10s. Grok does not support video references.
|
|
329
|
+
Video edit model behavior: `--video-model kling --video` uses Kling base/direct edit internally; `--video-model seedance-fast --video`, `--video-model seedance-mini --video`, or `--video-model seedance --video` uses the SeeDance video-reference path and requires target <=15s, <=50MB, width/height 300-6000px, aspect ratio 0.4-2.5, and frame pixels 409,600-2,086,876. `--video-model minimax-h3 --video` uses H3 feature/reference mode: up to 3 video references totaling <=15s, each <=50MB with width/height 256-5760px and aspect ratio 0.4-2.5. `--video-model google-omni --video` uses Gemini Omni direct video editing and accepts one reference video in Makaron. Output duration is clamped to 3-10s. Grok does not support video references.
|
|
326
330
|
|
|
327
331
|
### `music` — Music generation
|
|
328
332
|
|
|
@@ -435,7 +439,7 @@ send_message "All done!"
|
|
|
435
439
|
## Important Notes
|
|
436
440
|
|
|
437
441
|
- One project = one conversation thread. All history is preserved.
|
|
438
|
-
- One
|
|
442
|
+
- One active Agent Run at a time per project. A new message received while it is active is appended to that same Agent Run and processed at a durable work-unit boundary; it does not interrupt the execution or create a second owner for an in-progress Studio workflow.
|
|
439
443
|
- Multi-image: `create --image a.jpg --image b.jpg` or `chat --image ref.jpg`.
|
|
440
444
|
- Provider-generated videos can take 3-5 minutes; Grok is usually around 30-40 seconds; Gemini Omni is usually around 30-70 seconds plus Storage handoff. Remotion compositions should be converted with `materialize` / `responses get --materialize`, and timing should be read from `duration_seconds`, `render_seconds`, and `realtime_ratio`.
|
|
441
445
|
- Music takes ~60 seconds. Appears in output when done.
|
package/bin/makaron.mjs
CHANGED
|
@@ -45,11 +45,15 @@ const MAX_AUDIO_REFERENCE_DURATION_TOLERANCE = 0.5;
|
|
|
45
45
|
const MAX_AUDIO_REFERENCE_FILE_SIZE_MB = 15;
|
|
46
46
|
const MAX_AUDIO_REFERENCE_FILE_SIZE = MAX_AUDIO_REFERENCE_FILE_SIZE_MB * 1024 * 1024;
|
|
47
47
|
const MAX_VIDEO_FRAME_PIXELS = 2_086_876;
|
|
48
|
+
const SEEDANCE25_MAX_VIDEO_REFERENCE_DURATION = 30;
|
|
49
|
+
const SEEDANCE25_MAX_VIDEO_FRAME_PIXELS = 8_295_044;
|
|
48
50
|
const SEEDANCE_MIN_VIDEO_FRAME_PIXELS = 409_600;
|
|
49
51
|
const SEEDANCE_MIN_VIDEO_SIDE = 300;
|
|
50
52
|
const SEEDANCE_MAX_VIDEO_SIDE = 6000;
|
|
51
53
|
const SEEDANCE_MIN_VIDEO_ASPECT = 0.4;
|
|
52
54
|
const SEEDANCE_MAX_VIDEO_ASPECT = 2.5;
|
|
55
|
+
const MINIMAX_H3_MIN_VIDEO_SIDE = 256;
|
|
56
|
+
const MINIMAX_H3_MAX_VIDEO_SIDE = 5760;
|
|
53
57
|
|
|
54
58
|
function warnLegacyModelFlag(replacement) {
|
|
55
59
|
process.stderr.write(`⚠️ --model is deprecated here; use ${replacement}.\n`);
|
|
@@ -282,7 +286,6 @@ Options:
|
|
|
282
286
|
--video <file|url> Attach a video to the project timeline. Repeatable.
|
|
283
287
|
--audio <file|url> Attach a song, beat, or voice reference. MP3/WAV, repeatable.
|
|
284
288
|
--skill <id|label|name> Use an installed skill or auto-install a matched marketplace skill.
|
|
285
|
-
--video-resolution <res> Video resolution: auto, 480p, 720p, 1080p, or 4k.
|
|
286
289
|
--background, -b Submit and print a runId.
|
|
287
290
|
--json Output structured JSON.
|
|
288
291
|
--stream Legacy live SSE stream.
|
|
@@ -314,7 +317,7 @@ What you can ask:
|
|
|
314
317
|
makaron chat --project <id> "add calm piano background music"
|
|
315
318
|
|
|
316
319
|
Reference audio / beat sync
|
|
317
|
-
makaron chat --project auto --audio beat.mp3
|
|
320
|
+
makaron chat --project auto --audio beat.mp3 "用 Seedance Mini 480p 做卡点视频"
|
|
318
321
|
makaron chat --project <id> --audio https://example.com/beat.mp3 "add this as the soundtrack"
|
|
319
322
|
|
|
320
323
|
Motion design
|
|
@@ -351,7 +354,6 @@ async function streamAgent(baseUrl, headers, projectId, prompt, opts = {}) {
|
|
|
351
354
|
projectId,
|
|
352
355
|
prompt,
|
|
353
356
|
headless: true,
|
|
354
|
-
...(opts.videoResolution ? { videoResolution: opts.videoResolution } : {}),
|
|
355
357
|
...(opts.uploadedVideoCount ? { uploadedVideoCount: opts.uploadedVideoCount } : {}),
|
|
356
358
|
...(opts.turnMediaCount ? { turnMediaCount: opts.turnMediaCount } : {}),
|
|
357
359
|
}),
|
|
@@ -460,7 +462,6 @@ async function streamAgent(baseUrl, headers, projectId, prompt, opts = {}) {
|
|
|
460
462
|
|
|
461
463
|
async function submitRun(baseUrl, headers, projectId, prompt, opts = {}) {
|
|
462
464
|
const body = { projectId, prompt };
|
|
463
|
-
if (opts.videoResolution) body.videoResolution = opts.videoResolution;
|
|
464
465
|
if (opts.currentSnapshotIndex != null) body.currentSnapshotIndex = opts.currentSnapshotIndex;
|
|
465
466
|
if (opts.isNsfw) body.isNsfw = opts.isNsfw;
|
|
466
467
|
if (opts.audioAttachments?.length) body.audioAttachments = opts.audioAttachments;
|
|
@@ -1368,7 +1369,8 @@ function getAudioMimeFromExt(ext) {
|
|
|
1368
1369
|
return null;
|
|
1369
1370
|
}
|
|
1370
1371
|
|
|
1371
|
-
function validateAudioReferenceFile(audioPath) {
|
|
1372
|
+
function validateAudioReferenceFile(audioPath, options = {}) {
|
|
1373
|
+
const maxDuration = options.maxDuration ?? MAX_AUDIO_REFERENCE_DURATION;
|
|
1372
1374
|
if (!fs.existsSync(audioPath)) {
|
|
1373
1375
|
return { ok: false, error: `Audio file not found: ${audioPath}` };
|
|
1374
1376
|
}
|
|
@@ -1391,8 +1393,8 @@ function validateAudioReferenceFile(audioPath) {
|
|
|
1391
1393
|
if (duration < MIN_AUDIO_REFERENCE_DURATION) {
|
|
1392
1394
|
return { ok: false, error: `Audio too short: ${formatSeconds(duration)}s (min ${MIN_AUDIO_REFERENCE_DURATION}s).` };
|
|
1393
1395
|
}
|
|
1394
|
-
if (duration >
|
|
1395
|
-
return { ok: false, error: `Audio too long: ${formatSeconds(duration)}s (max ${
|
|
1396
|
+
if (duration > maxDuration + MAX_AUDIO_REFERENCE_DURATION_TOLERANCE) {
|
|
1397
|
+
return { ok: false, error: `Audio too long: ${formatSeconds(duration)}s (max ${maxDuration}s, with ${MAX_AUDIO_REFERENCE_DURATION_TOLERANCE}s metadata tolerance).` };
|
|
1396
1398
|
}
|
|
1397
1399
|
return { ok: true, mime, meta: { duration, fileSizeBytes: stat.size } };
|
|
1398
1400
|
}
|
|
@@ -1474,16 +1476,19 @@ function validateVideoFile(videoPath, options = {}) {
|
|
|
1474
1476
|
const maxSide = options.maxSide ?? Infinity;
|
|
1475
1477
|
const minAspect = options.minAspect ?? 0;
|
|
1476
1478
|
const maxAspect = options.maxAspect ?? Infinity;
|
|
1479
|
+
const allowedExtensions = options.allowedExtensions ?? ['mp4', 'mov', 'webm'];
|
|
1480
|
+
const maxFramePixels = options.maxFramePixels ?? MAX_VIDEO_FRAME_PIXELS;
|
|
1481
|
+
const maxFileSize = options.maxFileSize ?? MAX_VIDEO_UPLOAD_FILE_SIZE;
|
|
1477
1482
|
if (!fs.existsSync(videoPath)) {
|
|
1478
1483
|
return { ok: false, error: `Video file not found: ${videoPath}` };
|
|
1479
1484
|
}
|
|
1480
1485
|
const stat = fs.statSync(videoPath);
|
|
1481
|
-
if (stat.size >
|
|
1482
|
-
return { ok: false, error: `Video too large: ${(stat.size / 1024 / 1024).toFixed(1)}MB (max ${
|
|
1486
|
+
if (stat.size > maxFileSize) {
|
|
1487
|
+
return { ok: false, error: `Video too large: ${(stat.size / 1024 / 1024).toFixed(1)}MB (max ${(maxFileSize / 1024 / 1024).toFixed(0)}MB).` };
|
|
1483
1488
|
}
|
|
1484
1489
|
const ext = path.extname(videoPath).slice(1).toLowerCase();
|
|
1485
|
-
if (!
|
|
1486
|
-
return { ok: false, error: `Unsupported video format: .${ext}. Use
|
|
1490
|
+
if (!allowedExtensions.includes(ext)) {
|
|
1491
|
+
return { ok: false, error: `Unsupported video format: .${ext}. Use ${allowedExtensions.map(value => value.toUpperCase()).join(' or ')}.` };
|
|
1487
1492
|
}
|
|
1488
1493
|
const meta = probeLocalVideo(videoPath);
|
|
1489
1494
|
if (!meta) {
|
|
@@ -1492,8 +1497,8 @@ function validateVideoFile(videoPath, options = {}) {
|
|
|
1492
1497
|
if (meta.duration > maxDuration + durationTolerance) {
|
|
1493
1498
|
return { ok: false, error: `Video too long: ${formatSeconds(meta.duration)}s (max ${maxDuration}s, with ${durationTolerance}s metadata tolerance)` };
|
|
1494
1499
|
}
|
|
1495
|
-
if (meta.width * meta.height >
|
|
1496
|
-
return { ok: false, error: `Video resolution too high: ${meta.width}x${meta.height} (${meta.width * meta.height} px). Max is
|
|
1500
|
+
if (meta.width * meta.height > maxFramePixels) {
|
|
1501
|
+
return { ok: false, error: `Video resolution too high: ${meta.width}x${meta.height} (${meta.width * meta.height} px). Max is ${maxFramePixels} pixels.` };
|
|
1497
1502
|
}
|
|
1498
1503
|
const framePixels = meta.width * meta.height;
|
|
1499
1504
|
const aspect = meta.width / meta.height;
|
|
@@ -1745,13 +1750,14 @@ Use with chat:
|
|
|
1745
1750
|
console.log('Usage: makaron analyze --video <file|url> ["question"]');
|
|
1746
1751
|
} else if (topic === 'video') {
|
|
1747
1752
|
if (subtopic === 'script') console.log('Usage: makaron video script --image <file> [--image <file>] [--lang en|zh] "direction"');
|
|
1748
|
-
else if (subtopic === 'create') console.log('Usage: makaron video create --script "..." [--image <url>
|
|
1753
|
+
else if (subtopic === 'create') console.log('Usage: makaron video create --script "..." [--image <url> ...] [--video <url> ...] [--audio <url> ...] [--duration 10] [--aspect 9:16] [--video-model seedance-fast|seedance-mini|seedance|seedance-2.5|kling|grok|google-omni|minimax-h3] [--operation generate|edit|extend] [--video-resolution auto|480p|720p|768p|1080p|2k|4k] [--keep-original-sound]');
|
|
1749
1754
|
else if (subtopic === 'status') console.log('Usage: makaron video status <taskId> | --snapshot <snapshotId> [--wait]');
|
|
1750
1755
|
else console.log(`Video commands:
|
|
1751
1756
|
video script --image <file> [--image <file>] "direction" Write video script
|
|
1752
1757
|
video create --script "..." --video-model seedance-fast Native text-to-video (no image required)
|
|
1758
|
+
video create --script "..." --video-model minimax-h3 --video-resolution 2k MiniMax H3 native 2K text-to-video
|
|
1753
1759
|
video create --script "..." --image <url> [--duration 10] Submit video task
|
|
1754
|
-
video create --script "..." --video <public-url> [--video-model seedance-fast|seedance-mini|seedance|kling|google-omni] Edit a video (
|
|
1760
|
+
video create --script "..." --video <public-url> [--video-model seedance-fast|seedance-mini|seedance|seedance-2.5|kling|google-omni|minimax-h3] Edit/reference a video (Grok does not support video refs)
|
|
1755
1761
|
video status <taskId> Check video status
|
|
1756
1762
|
video status --snapshot <snapshotId> [--wait] Check v2 video snapshot
|
|
1757
1763
|
`);
|
|
@@ -1862,7 +1868,6 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
1862
1868
|
let background = false;
|
|
1863
1869
|
let jsonOutput = false;
|
|
1864
1870
|
let activeSkill = undefined;
|
|
1865
|
-
let videoResolution = undefined;
|
|
1866
1871
|
for (let i = 1; i < args.length; i++) {
|
|
1867
1872
|
if (args[i] === '--project' && args[i + 1]) projectId = args[++i];
|
|
1868
1873
|
else if (args[i] === '--image' && args[i + 1]) chatImages.push(args[++i]);
|
|
@@ -1873,7 +1878,10 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
1873
1878
|
else if (args[i] === '--stream') useStream = true;
|
|
1874
1879
|
else if (args[i] === '--background' || args[i] === '-b') background = true;
|
|
1875
1880
|
else if (args[i] === '--json') jsonOutput = true;
|
|
1876
|
-
else if (args[i] === '--video-resolution'
|
|
1881
|
+
else if (args[i] === '--video-resolution' || args[i].startsWith('--video-resolution=')) {
|
|
1882
|
+
process.stderr.write('❌ makaron chat chooses video model and resolution together. Put the requested resolution in your chat message, for example: "use MiniMax H3 at 2K".\n');
|
|
1883
|
+
process.exit(1);
|
|
1884
|
+
}
|
|
1877
1885
|
else if (
|
|
1878
1886
|
['--agent-model', '--image-model', '--video-model', '--model'].includes(args[i])
|
|
1879
1887
|
|| ['--agent-model=', '--image-model=', '--video-model=', '--model='].some(prefix => args[i].startsWith(prefix))
|
|
@@ -1988,7 +1996,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
1988
1996
|
const resolvedSkill = await resolveChatSkill(baseUrl, headers, activeSkill);
|
|
1989
1997
|
|
|
1990
1998
|
// Upload videos to project timeline (via /api/projects/create with videoUrls)
|
|
1991
|
-
|
|
1999
|
+
const finalPrompt = resolvedSkill ? `[Active skill: ${resolvedSkill}]\n${prompt}` : prompt;
|
|
1992
2000
|
let audioAttachments = [];
|
|
1993
2001
|
if (chatAudios.length > 0) {
|
|
1994
2002
|
const audioImports = [];
|
|
@@ -2079,7 +2087,6 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2079
2087
|
if (useStream) {
|
|
2080
2088
|
// Legacy SSE mode
|
|
2081
2089
|
const { results } = await streamAgent(baseUrl, headers, projectId, finalPrompt, {
|
|
2082
|
-
videoResolution,
|
|
2083
2090
|
uploadedVideoCount: uploadedTurnVideoCount,
|
|
2084
2091
|
turnMediaCount: uploadedTurnMediaCount,
|
|
2085
2092
|
});
|
|
@@ -2092,7 +2099,6 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2092
2099
|
} else {
|
|
2093
2100
|
// Default: fire-and-forget + poll
|
|
2094
2101
|
const { runId } = await submitRun(baseUrl, headers, projectId, finalPrompt, {
|
|
2095
|
-
videoResolution,
|
|
2096
2102
|
audioAttachments,
|
|
2097
2103
|
uploadedVideoCount: uploadedTurnVideoCount,
|
|
2098
2104
|
turnMediaCount: uploadedTurnMediaCount,
|
|
@@ -2422,11 +2428,15 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2422
2428
|
|
|
2423
2429
|
} else if (sub === 'create') {
|
|
2424
2430
|
const images = [];
|
|
2431
|
+
const videos = [];
|
|
2432
|
+
const audios = [];
|
|
2425
2433
|
let script = '', duration = undefined, aspectRatio = undefined, videoModel = undefined, videoResolution = undefined, wait = false;
|
|
2426
|
-
let
|
|
2434
|
+
let keepOriginalSound = false, videoOperation = undefined, extendDirection = undefined, outputFormat = undefined;
|
|
2435
|
+
let generateAudio = undefined, contentFilter = undefined, webSearch = false;
|
|
2427
2436
|
for (let i = 2; i < args.length; i++) {
|
|
2428
2437
|
if (args[i] === '--image' && args[i + 1]) images.push(args[++i]);
|
|
2429
|
-
else if (args[i] === '--video' && args[i + 1])
|
|
2438
|
+
else if (args[i] === '--video' && args[i + 1]) videos.push(args[++i]);
|
|
2439
|
+
else if (args[i] === '--audio' && args[i + 1]) audios.push(args[++i]);
|
|
2430
2440
|
else if (args[i] === '--script' && args[i + 1]) script = args[++i];
|
|
2431
2441
|
else if (args[i] === '--script-file' && args[i + 1]) script = fs.readFileSync(args[++i], 'utf-8');
|
|
2432
2442
|
else if (args[i] === '--duration' && args[i + 1]) duration = Number(args[++i]);
|
|
@@ -2439,6 +2449,13 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2439
2449
|
else if (args[i] === '--video-resolution' && args[i + 1]) videoResolution = args[++i];
|
|
2440
2450
|
else if (args[i] === '--resolution' && args[i + 1]) videoResolution = args[++i];
|
|
2441
2451
|
else if (args[i] === '--keep-original-sound') keepOriginalSound = true;
|
|
2452
|
+
else if (args[i] === '--video-operation' && args[i + 1]) videoOperation = args[++i];
|
|
2453
|
+
else if (args[i] === '--extend-direction' && args[i + 1]) extendDirection = args[++i];
|
|
2454
|
+
else if (args[i] === '--output-format' && args[i + 1]) outputFormat = args[++i];
|
|
2455
|
+
else if (args[i] === '--no-generated-audio') generateAudio = false;
|
|
2456
|
+
else if (args[i] === '--generated-audio') generateAudio = true;
|
|
2457
|
+
else if (args[i] === '--relaxed-content-filter') contentFilter = false;
|
|
2458
|
+
else if (args[i] === '--web-search') webSearch = true;
|
|
2442
2459
|
else if (args[i] === '--project') {
|
|
2443
2460
|
console.error('Usage: video create no longer supports --project. Use: makaron chat --project <id> --video <file|url> "your request"');
|
|
2444
2461
|
process.exit(1);
|
|
@@ -2446,27 +2463,53 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2446
2463
|
else if (args[i] === '--wait') wait = true;
|
|
2447
2464
|
}
|
|
2448
2465
|
const selectedVideoModel = videoModel || 'seedance-fast';
|
|
2449
|
-
const
|
|
2450
|
-
|
|
2451
|
-
|
|
2466
|
+
const isSeedance25 = selectedVideoModel === 'seedance-2.5';
|
|
2467
|
+
const isSeedanceModel = selectedVideoModel === 'seedance-fast' || selectedVideoModel === 'seedance-mini' || selectedVideoModel === 'seedance' || isSeedance25;
|
|
2468
|
+
const isMinimaxH3 = selectedVideoModel === 'minimax-h3';
|
|
2469
|
+
const supportsNativeTextToVideo = isSeedanceModel || isMinimaxH3;
|
|
2470
|
+
if (!script || (!images.length && !videos.length && !audios.length && !supportsNativeTextToVideo)) {
|
|
2471
|
+
console.error('Usage: makaron video create --script "..." [--image <url>] [--video <file|url>] [--audio <file|url>] [--duration 30] [--video-model seedance-2.5|minimax-h3]');
|
|
2452
2472
|
process.exit(1);
|
|
2453
2473
|
}
|
|
2474
|
+
if (isSeedance25 && images.length > 30) { console.error('Seedance 2.5 supports at most 30 image references.'); process.exit(1); }
|
|
2475
|
+
if (isSeedance25 && videos.length > 10) { console.error('Seedance 2.5 supports at most 10 video references.'); process.exit(1); }
|
|
2476
|
+
if (isSeedance25 && audios.length > 10) { console.error('Seedance 2.5 supports at most 10 audio references.'); process.exit(1); }
|
|
2477
|
+
if (isMinimaxH3 && images.length > 9) { console.error('MiniMax H3 supports at most 9 image references.'); process.exit(1); }
|
|
2478
|
+
if (isMinimaxH3 && videos.length > 3) { console.error('MiniMax H3 supports at most 3 video references.'); process.exit(1); }
|
|
2479
|
+
if (isMinimaxH3 && audios.length > 3) { console.error('MiniMax H3 supports at most 3 audio references.'); process.exit(1); }
|
|
2480
|
+
if (videoOperation && !['generate', 'edit', 'extend'].includes(videoOperation)) { console.error('--video-operation must be generate, edit, or extend.'); process.exit(1); }
|
|
2481
|
+
if (extendDirection && !['forward', 'backward'].includes(extendDirection)) { console.error('--extend-direction must be forward or backward.'); process.exit(1); }
|
|
2482
|
+
if (outputFormat && !['mp4', 'mov'].includes(outputFormat)) { console.error('--output-format must be mp4 or mov.'); process.exit(1); }
|
|
2454
2483
|
|
|
2455
2484
|
if (wait) {
|
|
2456
2485
|
console.error('Usage: --wait is only supported for project timeline tasks. Use chat --project for project video generation, or poll the returned taskId with video status.');
|
|
2457
2486
|
process.exit(1);
|
|
2458
2487
|
}
|
|
2459
2488
|
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2489
|
+
const providerMaxDuration = isSeedance25 ? SEEDANCE25_MAX_VIDEO_REFERENCE_DURATION : MAX_VIDEO_PROVIDER_REFERENCE_DURATION;
|
|
2490
|
+
const providerMaxPixels = isMinimaxH3 ? Infinity : isSeedance25 ? SEEDANCE25_MAX_VIDEO_FRAME_PIXELS : MAX_VIDEO_FRAME_PIXELS;
|
|
2491
|
+
const localImages = images.filter(image => !isHttpUrl(image));
|
|
2492
|
+
if (localImages.length) {
|
|
2493
|
+
const uploadedImages = await uploadImageFilesViaSignedUrl(baseUrl, headers, undefined, localImages);
|
|
2494
|
+
const uploadedByPath = new Map(localImages.map((image, index) => [image, uploadedImages[index]]));
|
|
2495
|
+
images = images.map(image => isHttpUrl(image) ? image : uploadedByPath.get(image)).filter(Boolean);
|
|
2464
2496
|
}
|
|
2465
|
-
|
|
2497
|
+
const videoUrls = [];
|
|
2498
|
+
let inputVideoMeta = null;
|
|
2499
|
+
for (const video of videos) {
|
|
2500
|
+
if (isHttpUrl(video)) { videoUrls.push(video); continue; }
|
|
2466
2501
|
const valid = validateVideoFile(video, {
|
|
2467
|
-
maxDuration:
|
|
2502
|
+
maxDuration: providerMaxDuration,
|
|
2468
2503
|
durationTolerance: MAX_VIDEO_PROVIDER_REFERENCE_DURATION_TOLERANCE,
|
|
2469
|
-
|
|
2504
|
+
maxFramePixels: providerMaxPixels,
|
|
2505
|
+
maxFileSize: isSeedance25 ? 200 * 1024 * 1024 : MAX_VIDEO_UPLOAD_FILE_SIZE,
|
|
2506
|
+
...(selectedVideoModel === 'minimax-h3' ? {
|
|
2507
|
+
allowedExtensions: ['mp4', 'mov'],
|
|
2508
|
+
minSide: MINIMAX_H3_MIN_VIDEO_SIDE,
|
|
2509
|
+
maxSide: MINIMAX_H3_MAX_VIDEO_SIDE,
|
|
2510
|
+
minAspect: SEEDANCE_MIN_VIDEO_ASPECT,
|
|
2511
|
+
maxAspect: SEEDANCE_MAX_VIDEO_ASPECT,
|
|
2512
|
+
} : isSeedanceModel ? {
|
|
2470
2513
|
minFramePixels: SEEDANCE_MIN_VIDEO_FRAME_PIXELS,
|
|
2471
2514
|
minSide: SEEDANCE_MIN_VIDEO_SIDE,
|
|
2472
2515
|
maxSide: SEEDANCE_MAX_VIDEO_SIDE,
|
|
@@ -2475,22 +2518,38 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2475
2518
|
} : {}),
|
|
2476
2519
|
});
|
|
2477
2520
|
if (!valid.ok) { console.error(`❌ ${valid.error}`); process.exit(1); }
|
|
2478
|
-
inputVideoMeta
|
|
2521
|
+
inputVideoMeta ||= valid.meta;
|
|
2479
2522
|
process.stderr.write(`📹 Uploading ${path.basename(video)} (${(fs.statSync(video).size/1024/1024).toFixed(1)}MB)...\n`);
|
|
2480
|
-
|
|
2481
|
-
if (!
|
|
2523
|
+
const uploaded = await uploadFileViaSignedUrl(baseUrl, headers, undefined, video, valid.mime);
|
|
2524
|
+
if (!uploaded) process.exit(1);
|
|
2525
|
+
videoUrls.push(uploaded);
|
|
2482
2526
|
process.stderr.write(`📹 Uploaded: ${path.basename(video)}\n`);
|
|
2483
2527
|
}
|
|
2528
|
+
const audioUrls = [];
|
|
2529
|
+
for (const audio of audios) {
|
|
2530
|
+
if (isHttpUrl(audio)) { audioUrls.push(audio); continue; }
|
|
2531
|
+
const valid = validateAudioReferenceFile(audio, { maxDuration: isSeedance25 ? 30 : MAX_AUDIO_REFERENCE_DURATION });
|
|
2532
|
+
if (!valid.ok) { console.error(`❌ ${valid.error}`); process.exit(1); }
|
|
2533
|
+
process.stderr.write(`🎵 Uploading ${path.basename(audio)}...\n`);
|
|
2534
|
+
const uploaded = await uploadFileViaSignedUrl(baseUrl, headers, undefined, audio, valid.mime, { uploadKind: 'audio' });
|
|
2535
|
+
if (!uploaded) process.exit(1);
|
|
2536
|
+
audioUrls.push(uploaded);
|
|
2537
|
+
}
|
|
2484
2538
|
// Standalone MCP tool (no project timeline write)
|
|
2485
2539
|
process.stderr.write('🎬 Submitting video...\n');
|
|
2486
|
-
const
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2540
|
+
const resolvedOperation = videoOperation || (isSeedance25 && videoUrls.length ? 'edit' : 'generate');
|
|
2541
|
+
const vArgs = isSeedance25
|
|
2542
|
+
? { script, images, videoUrls, audioUrls, videoModel: selectedVideoModel, videoResolution, operation: resolvedOperation, extendDirection, outputFormat, generateAudio, contentFilter, webSearch }
|
|
2543
|
+
: isMinimaxH3
|
|
2544
|
+
? { script, images, videoUrls, audioUrls, videoModel: selectedVideoModel, videoResolution }
|
|
2545
|
+
: videoUrls[0]
|
|
2546
|
+
? { videoUrl: videoUrls[0], editPrompt: script, images, videoModel: selectedVideoModel, videoResolution, referType: isSeedanceModel ? 'feature' : 'base' }
|
|
2547
|
+
: { script, images, videoModel: selectedVideoModel, videoResolution };
|
|
2548
|
+
const effectiveDuration = resolvedOperation === 'edit' ? undefined : duration || (inputVideoMeta?.duration ? Math.min(providerMaxDuration, Math.round(inputVideoMeta.duration)) : undefined);
|
|
2490
2549
|
if (effectiveDuration) vArgs.duration = effectiveDuration;
|
|
2491
2550
|
if (aspectRatio) vArgs.aspectRatio = aspectRatio;
|
|
2492
|
-
if (keepOriginalSound &&
|
|
2493
|
-
const result = await callMcpTool(baseUrl, headers,
|
|
2551
|
+
if (keepOriginalSound && videoUrls.length && !isSeedance25) vArgs.keepOriginalSound = true;
|
|
2552
|
+
const result = await callMcpTool(baseUrl, headers, videoUrls.length && !isSeedance25 && !isMinimaxH3 ? 'makaron_edit_video' : 'makaron_create_video', vArgs);
|
|
2494
2553
|
const text = result?.content?.find(c => c.type === 'text')?.text;
|
|
2495
2554
|
if (text) {
|
|
2496
2555
|
console.log(text);
|
|
@@ -2534,8 +2593,9 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2534
2593
|
console.log(`Video commands:
|
|
2535
2594
|
video script --image <file> [--image <file>] "direction" Write video script
|
|
2536
2595
|
video create --script "..." --video-model seedance-fast Native text-to-video (no image required)
|
|
2596
|
+
video create --script "..." --video-model minimax-h3 --video-resolution 2k MiniMax H3 native 2K text-to-video
|
|
2537
2597
|
video create --script "..." --image <url> [--duration 10] Submit video task
|
|
2538
|
-
video create --script "..." --video <public-url> [--video-model seedance-fast|seedance-mini|seedance|kling|google-omni] Edit a video (
|
|
2598
|
+
video create --script "..." --video <public-url> [--video-model seedance-fast|seedance-mini|seedance|seedance-2.5|kling|google-omni|minimax-h3] Edit/reference a video (Grok does not support video refs)
|
|
2539
2599
|
video status <taskId> Check video status
|
|
2540
2600
|
video status --snapshot <snapshotId> [--wait] Check v2 video snapshot
|
|
2541
2601
|
`);
|
package/package.json
CHANGED
package/skills/makaron/SKILL.md
CHANGED
|
@@ -102,7 +102,7 @@ Returns immediately:
|
|
|
102
102
|
| Fix one moment in a video from a screenshot | `npx makaron-cli chat --project <id> --image screenshot.png "@4 this frame should be Paris; only fix this moment"` |
|
|
103
103
|
| Cut or assemble video | `npx makaron-cli chat --project <id> --video clip.mp4 "cut out the dead air and keep the best 20 seconds"` |
|
|
104
104
|
| Add music | `npx makaron-cli chat --project <id> "add calm piano background music"` |
|
|
105
|
-
| Beat-sync video from audio | `npx makaron-cli chat --project auto --audio beat.mp3
|
|
105
|
+
| Beat-sync video from audio | `npx makaron-cli chat --project auto --audio beat.mp3 "use Seedance Mini at 480p to make a beat-synced video"` |
|
|
106
106
|
| Create motion design | `npx makaron-cli chat --project <id> "make an animated Instagram story with this image"` |
|
|
107
107
|
|
|
108
108
|
### Marketplace skills
|
|
@@ -176,12 +176,13 @@ Attach a short song, beat, or voice recording when the video should follow audio
|
|
|
176
176
|
```bash
|
|
177
177
|
npx makaron-cli chat --project auto \
|
|
178
178
|
--audio beat.mp3 \
|
|
179
|
-
|
|
180
|
-
-b "make a 15s beat-synced video"
|
|
179
|
+
-b "use Seedance Mini at 480p to make a 15s beat-synced video"
|
|
181
180
|
```
|
|
182
181
|
|
|
183
182
|
`--audio` accepts repeatable local files or public URLs. Local MP3/WAV files must be 2-15s and <=15MB; reference audio currently works with Seedance video generation.
|
|
184
183
|
|
|
184
|
+
`chat` intentionally has no video model or resolution flags. State both in the chat message so the Agent selects a compatible provider and resolution together. Use `video create` only when you explicitly need direct provider controls.
|
|
185
|
+
|
|
185
186
|
### Fix one video moment from a screenshot
|
|
186
187
|
|
|
187
188
|
When a video is mostly good but one moment needs a local fix, attach a screenshot of the problem frame and describe the correction in normal language:
|
|
@@ -262,8 +263,9 @@ npx makaron-cli analyze --video input.mp4 "describe the key actions and pacing"
|
|
|
262
263
|
npx makaron-cli video create --script "Shot 1 (5s): <<<image_1>>> ..." --image https://...jpg --duration 5 --video-model kling
|
|
263
264
|
npx makaron-cli video create --script "Shot 1 (15s): <<<image_1>>> and <<<image_2>>> build a neon one-person studio" --image https://...jpg --image https://...webp --duration 15 --video-model seedance-mini --video-resolution 480p --aspect 9:16
|
|
264
265
|
|
|
265
|
-
# 3b. Native SeeDance text-to-video (no image required)
|
|
266
|
+
# 3b. Native SeeDance or MiniMax H3 text-to-video (no image required)
|
|
266
267
|
npx makaron-cli video create --script "Shot 1 (5s): A neon one-person studio wakes at dawn" --duration 5 --video-model seedance-fast --aspect 16:9
|
|
268
|
+
npx makaron-cli video create --script "Shot 1 (15s): A premium creative editor comes alive" --duration 15 --video-model minimax-h3 --video-resolution 2k --aspect 16:9
|
|
267
269
|
|
|
268
270
|
# 3c. Edit a video from a local file or public URL
|
|
269
271
|
npx makaron-cli video create --script "make it funny" --video input.mp4 --duration 5 --video-model seedance-fast
|
|
@@ -279,9 +281,11 @@ npx makaron-cli video status <taskId>
|
|
|
279
281
|
npx makaron-cli chat --project <id|auto> --video input.mp4 -b "make it funny"
|
|
280
282
|
```
|
|
281
283
|
|
|
282
|
-
Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to
|
|
284
|
+
Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to the selected model limit), `--video <file|url>` and `--audio <file|url>` (repeatable where supported), `--duration <seconds>`, `--aspect 9:16|16:9|1:1`, `--video-model seedance-fast|seedance-mini|seedance|seedance-2.5|kling|grok|google-omni|minimax-h3`, `--video-resolution auto|480p|720p|768p|1080p|2k|4k`. Default model is `seedance-fast`. SeeDance accepts native text-to-video with no image and integer output duration 4-15s (default 5s); `seedance-mini` supports 480p/720p and is best for cheaper drafts/multi-size tests; MiniMax H3 accepts native text-to-video, 4-15s output, and up to 9 image, up to 3 video, and up to 3 audio references through Makaron Agent/chat. H3 defaults to public 2k output; 768p requires provider preview access plus `MINIMAX_H3_ENABLE_768P=true` on the Makaron server. Kling supports 5-15s; Grok 1.5 supports 1-15s single-image-to-video only; Gemini Omni supports 3-10s fast 720p image/video generation and editing with native generated audio, including up to 6 image references when no video reference is provided. For `--video-model grok`, forced `--aspect` is ignored to avoid xAI stretching the source image; pad/create the image at the target shape first or use another model.
|
|
285
|
+
|
|
286
|
+
Seedance 2.5 uses `--video-model seedance-2.5` and supports 4-30s at 480p/720p, up to 30 images + 10 videos + 10 audios, repeatable local/URL references, `--video-operation generate|edit|extend`, `--extend-direction`, `--output-format mp4|mov`, and `--web-search`. The Evolink route does not currently expose 4K output.
|
|
283
287
|
|
|
284
|
-
Video edit model behavior: `--video-model kling --video` uses Kling base/direct edit internally; `--video-model seedance-fast --video`, `--video-model seedance-mini --video`, or `--video-model seedance --video` uses the SeeDance video-reference path and requires target <=15s, <=50MB, width/height 300-6000px, aspect ratio 0.4-2.5, and frame pixels 409,600-2,086,876. `--video-model google-omni --video` uses Gemini Omni direct video editing and accepts one reference video in Makaron. Output duration is clamped to 3-10s. Grok does not support video references.
|
|
288
|
+
Video edit model behavior: `--video-model kling --video` uses Kling base/direct edit internally; `--video-model seedance-fast --video`, `--video-model seedance-mini --video`, or `--video-model seedance --video` uses the SeeDance video-reference path and requires target <=15s, <=50MB, width/height 300-6000px, aspect ratio 0.4-2.5, and frame pixels 409,600-2,086,876. `--video-model minimax-h3 --video` uses H3 feature/reference mode: up to 3 video references totaling <=15s, each <=50MB with width/height 256-5760px and aspect ratio 0.4-2.5. `--video-model google-omni --video` uses Gemini Omni direct video editing and accepts one reference video in Makaron. Output duration is clamped to 3-10s. Grok does not support video references.
|
|
285
289
|
|
|
286
290
|
### `music` — Music generation
|
|
287
291
|
|
|
@@ -412,7 +416,7 @@ send_message "All done!"
|
|
|
412
416
|
## Important Notes
|
|
413
417
|
|
|
414
418
|
- One project = one conversation thread. All history is preserved.
|
|
415
|
-
- One
|
|
419
|
+
- One active Agent Run at a time per project. A new message received while it is active is appended to that same Agent Run and processed at a durable work-unit boundary; it does not interrupt the execution or create a second owner for an in-progress Studio workflow.
|
|
416
420
|
- Multi-image: `create --image a.jpg --image b.jpg` or `chat --image ref.jpg`.
|
|
417
421
|
- Provider-generated videos can take 3-5 minutes; Grok is usually around 30-40 seconds; Gemini Omni is usually around 30-70 seconds plus Storage handoff. Remotion compositions should be converted with `materialize` / `responses get --materialize`, and timing should be read from `duration_seconds`, `render_seconds`, and `realtime_ratio`.
|
|
418
422
|
- Music takes ~60 seconds. Appears in output when done.
|