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 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 --video-resolution 480p "make a beat-synced video"` |
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
- --video-resolution 480p \
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 7), `--video <file|url>`, `--duration <seconds>`, `--aspect 9:16|16:9|1:1`, `--video-model seedance-fast|seedance-mini|seedance|kling|grok|google-omni`, `--video-resolution auto|480p|720p|1080p|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; 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.
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 run at a time per project. New message interrupts previous run.
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 --video-resolution 480p "用这个音乐做卡点视频"
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 > MAX_AUDIO_REFERENCE_DURATION + MAX_AUDIO_REFERENCE_DURATION_TOLERANCE) {
1395
- return { ok: false, error: `Audio too long: ${formatSeconds(duration)}s (max ${MAX_AUDIO_REFERENCE_DURATION}s, with ${MAX_AUDIO_REFERENCE_DURATION_TOLERANCE}s metadata tolerance).` };
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 > MAX_VIDEO_UPLOAD_FILE_SIZE) {
1482
- return { ok: false, error: `Video too large: ${(stat.size / 1024 / 1024).toFixed(1)}MB (max ${MAX_VIDEO_UPLOAD_FILE_SIZE_MB}MB). The CLI uploads directly to Storage; use the frontend to transcode larger videos first.` };
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 (!['mp4', 'mov', 'webm'].includes(ext)) {
1486
- return { ok: false, error: `Unsupported video format: .${ext}. Use MP4, MOV, or WebM.` };
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 > MAX_VIDEO_FRAME_PIXELS) {
1496
- return { ok: false, error: `Video resolution too high: ${meta.width}x${meta.height} (${meta.width * meta.height} px). Max is <=1080p (${MAX_VIDEO_FRAME_PIXELS} px). Re-upload through the frontend to transcode, or export a smaller video.` };
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> | --video <public-url>] [--duration 10] [--aspect 9:16] [--video-model seedance-fast|seedance-mini|seedance|kling|grok|google-omni] [--video-resolution auto|480p|720p|1080p|4k] [--keep-original-sound]');
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 (standalone; Grok does not support video refs)
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' && args[i + 1]) videoResolution = args[++i];
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
- let finalPrompt = resolvedSkill ? `[Active skill: ${resolvedSkill}]\n${prompt}` : prompt;
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 video = null, keepOriginalSound = false;
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]) video = args[++i];
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 isSeedanceModel = selectedVideoModel === 'seedance-fast' || selectedVideoModel === 'seedance-mini' || selectedVideoModel === 'seedance';
2450
- if (!script || (!images.length && !video && !isSeedanceModel)) {
2451
- console.error('Usage: makaron video create --script "..." [--image <url> | --video <public-url>] [--duration 10] [--aspect 9:16] [--video-model seedance-fast|seedance-mini|seedance|kling|grok|google-omni] [--video-resolution auto|480p|720p|1080p|4k] [--keep-original-sound]');
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
- let videoUrl = isHttpUrl(video) ? video : null;
2461
- let inputVideoMeta = null;
2462
- if (videoUrl) {
2463
- process.stderr.write(`📹 Assuming public video URL already matches provider reference limits. Seedance requires ≤${MAX_VIDEO_PROVIDER_REFERENCE_DURATION}s, ≤50MB, sides 300-6000px, frame pixels 409,600-${MAX_VIDEO_FRAME_PIXELS}; Kling requires ≤200MB and ≤2K; Google Omni accepts one reference video in Makaron; Grok does not support video references.\n`);
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
- if (video && !videoUrl) {
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: MAX_VIDEO_PROVIDER_REFERENCE_DURATION,
2502
+ maxDuration: providerMaxDuration,
2468
2503
  durationTolerance: MAX_VIDEO_PROVIDER_REFERENCE_DURATION_TOLERANCE,
2469
- ...(selectedVideoModel === 'seedance' || selectedVideoModel === 'seedance-fast' || selectedVideoModel === 'seedance-mini' ? {
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 = valid.meta;
2521
+ inputVideoMeta ||= valid.meta;
2479
2522
  process.stderr.write(`📹 Uploading ${path.basename(video)} (${(fs.statSync(video).size/1024/1024).toFixed(1)}MB)...\n`);
2480
- videoUrl = await uploadFileViaSignedUrl(baseUrl, headers, undefined, video, valid.mime);
2481
- if (!videoUrl) process.exit(1);
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 vArgs = videoUrl
2487
- ? { videoUrl, editPrompt: script, images, videoModel: selectedVideoModel, videoResolution, referType: (selectedVideoModel === 'seedance' || selectedVideoModel === 'seedance-fast' || selectedVideoModel === 'seedance-mini') ? 'feature' : 'base' }
2488
- : { script, images, videoModel: selectedVideoModel, videoResolution };
2489
- const effectiveDuration = duration || (inputVideoMeta?.duration ? Math.min(MAX_VIDEO_PROVIDER_REFERENCE_DURATION, Math.round(inputVideoMeta.duration)) : undefined);
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 && videoUrl) vArgs.keepOriginalSound = true;
2493
- const result = await callMcpTool(baseUrl, headers, videoUrl ? 'makaron_edit_video' : 'makaron_create_video', vArgs);
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 (standalone; Grok does not support video refs)
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "makaron-cli",
3
- "version": "0.13.3",
3
+ "version": "0.13.4",
4
4
  "description": "Talk to Makaron Agent from the terminal — create projects, edit images, generate videos",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -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 --video-resolution 480p "make a beat-synced video"` |
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
- --video-resolution 480p \
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 7), `--video <file|url>`, `--duration <seconds>`, `--aspect 9:16|16:9|1:1`, `--video-model seedance-fast|seedance-mini|seedance|kling|grok|google-omni`, `--video-resolution auto|480p|720p|1080p|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; 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.
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 run at a time per project. New message interrupts previous run.
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.