makaron-cli 0.8.3 → 0.8.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "makaron-cli",
3
- "version": "0.7.3",
3
+ "version": "0.8.4",
4
4
  "description": "AI image editing, video generation, and music creation via CLI. Agents can self-register, create projects, and produce creative media.",
5
5
  "author": {
6
6
  "name": "Makaron AI",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "makaron-cli",
3
- "version": "0.7.3",
3
+ "version": "0.8.4",
4
4
  "description": "AI image editing, video generation, and music creation via CLI. Agents can self-register, create projects, and produce creative media.",
5
5
  "displayName": "Makaron",
6
6
  "shortDescription": "AI image/video/music creation from the terminal",
package/README.md CHANGED
@@ -206,9 +206,9 @@ For project/timeline video editing, use:
206
206
  npx makaron-cli chat --project <id|auto> --video input.mp4 -b "make it funny"
207
207
  ```
208
208
 
209
- 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`, `--model kling|seedance`. SeeDance accepts integer output duration 4-15s (default 5s); Kling supports 5-10s.
209
+ 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`, `--model kling|seedance`. SeeDance accepts integer output duration 4-15s (default 5s); Kling supports 5-15s.
210
210
 
211
- Video edit model behavior: `--model kling --video` uses Kling base/direct edit internally; `--model seedance --video` uses the Seedance video-reference path and requires target <=15s, <=1080p input. Tiny metadata padding up to 15.5s is accepted and output duration is clamped to 15s.
211
+ Video edit model behavior: `--model kling --video` uses Kling base/direct edit internally; `--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. Tiny metadata padding up to 15.5s is accepted and output duration is clamped to 15s.
212
212
 
213
213
  ### `music` — Music generation
214
214
 
package/SKILL.md CHANGED
@@ -198,9 +198,9 @@ npx makaron-cli video status <taskId>
198
198
  npx makaron-cli chat --project <id|auto> --video input.mp4 -b "make it funny"
199
199
  ```
200
200
 
201
- 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`, `--model kling|seedance`. SeeDance accepts integer output duration 4-15s (default 5s); Kling supports 5-10s.
201
+ 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`, `--model kling|seedance`. SeeDance accepts integer output duration 4-15s (default 5s); Kling supports 5-15s.
202
202
 
203
- Video edit model behavior: `--model kling --video` uses Kling base/direct edit internally; `--model seedance --video` uses the Seedance video-reference path and requires target <=15s, <=1080p input. Tiny metadata padding up to 15.5s is accepted and output duration is clamped to 15s.
203
+ Video edit model behavior: `--model kling --video` uses Kling base/direct edit internally; `--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. Tiny metadata padding up to 15.5s is accepted and output duration is clamped to 15s.
204
204
 
205
205
  ### `music` — Music generation
206
206
 
package/bin/makaron.mjs CHANGED
@@ -34,6 +34,11 @@ const MAX_VIDEO_UPLOAD_DURATION_TOLERANCE = 1;
34
34
  const MAX_VIDEO_PROVIDER_REFERENCE_DURATION = 15;
35
35
  const MAX_VIDEO_PROVIDER_REFERENCE_DURATION_TOLERANCE = 0.5;
36
36
  const MAX_VIDEO_FRAME_PIXELS = 2_086_876;
37
+ const SEEDANCE_MIN_VIDEO_FRAME_PIXELS = 409_600;
38
+ const SEEDANCE_MIN_VIDEO_SIDE = 300;
39
+ const SEEDANCE_MAX_VIDEO_SIDE = 6000;
40
+ const SEEDANCE_MIN_VIDEO_ASPECT = 0.4;
41
+ const SEEDANCE_MAX_VIDEO_ASPECT = 2.5;
37
42
 
38
43
  function getCliVersion() {
39
44
  try {
@@ -768,6 +773,11 @@ function probeLocalVideo(videoPath) {
768
773
  function validateVideoFile(videoPath, options = {}) {
769
774
  const maxDuration = options.maxDuration ?? MAX_VIDEO_UPLOAD_DURATION;
770
775
  const durationTolerance = options.durationTolerance ?? MAX_VIDEO_UPLOAD_DURATION_TOLERANCE;
776
+ const minFramePixels = options.minFramePixels ?? 0;
777
+ const minSide = options.minSide ?? 0;
778
+ const maxSide = options.maxSide ?? Infinity;
779
+ const minAspect = options.minAspect ?? 0;
780
+ const maxAspect = options.maxAspect ?? Infinity;
771
781
  if (!fs.existsSync(videoPath)) {
772
782
  return { ok: false, error: `Video file not found: ${videoPath}` };
773
783
  }
@@ -789,6 +799,19 @@ function validateVideoFile(videoPath, options = {}) {
789
799
  if (meta.width * meta.height > MAX_VIDEO_FRAME_PIXELS) {
790
800
  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.` };
791
801
  }
802
+ const framePixels = meta.width * meta.height;
803
+ const aspect = meta.width / meta.height;
804
+ if (
805
+ framePixels < minFramePixels ||
806
+ meta.width < minSide ||
807
+ meta.height < minSide ||
808
+ meta.width > maxSide ||
809
+ meta.height > maxSide ||
810
+ aspect < minAspect ||
811
+ aspect > maxAspect
812
+ ) {
813
+ return { ok: false, error: `Video size does not meet provider limits: ${meta.width}x${meta.height} (${framePixels} px, aspect ${aspect.toFixed(2)}). Required: frame pixels >=${minFramePixels}, sides ${minSide}-${Number.isFinite(maxSide) ? maxSide : '∞'}px, aspect ${minAspect}-${Number.isFinite(maxAspect) ? maxAspect : '∞'}. Resize/pad with FFmpeg before submitting.` };
814
+ }
792
815
  const mime = ext === 'mov' ? 'video/quicktime' : ext === 'webm' ? 'video/webm' : 'video/mp4';
793
816
  return { ok: true, mime, meta };
794
817
  }
@@ -850,12 +873,133 @@ async function analyzeVideoCli(baseUrl, headers, rawVideo, questionParts) {
850
873
  if (text) console.log(text);
851
874
  }
852
875
 
876
+ // ─── Help ───────────────────────────────────────────────────────────────────
877
+
878
+ function hasHelpFlag(values) {
879
+ return values.includes('--help') || values.includes('-h');
880
+ }
881
+
882
+ function printRootHelp() {
883
+ console.log(`Makaron CLI — Talk to Makaron Agent from the terminal
884
+
885
+ Commands:
886
+ register --json Get challenge for agent self-registration
887
+ register --verify --challenge-id <id> --answer <n> Verify and save API key
888
+ claim Get claim URL for human to link account
889
+ login Log in to Makaron (human interactive)
890
+ list (ls) List all projects
891
+ project media <projectId> --json List timeline media for a project
892
+ create --image <file> Create project from local image
893
+ create --image-url <url> Create project from URL
894
+ create --title "name" Create empty project (text-to-image)
895
+
896
+ chat --project <id> "message" Chat (non-blocking, polls for result)
897
+ chat --project <id> --video <file> Attach video to conversation
898
+ chat --project <id> -b "message" Background: submit and print runId
899
+ chat --project <id> --stream "msg" Legacy: stream SSE in real-time
900
+ chat --project <id> --json "msg" Output structured JSON result
901
+
902
+ responses get <runId> Get run status and results
903
+ responses get <runId> --wait Poll until completed
904
+ responses list --project <id> List runs for a project
905
+ abort <runId> Abort a running Agent
906
+
907
+ edit [--image <file>] "prompt" AI image edit / text-to-image
908
+ analyze --video <file|url> Analyze video content
909
+ video script|create|status Video generation
910
+ music create|status Music generation
911
+
912
+ admin Admin commands (skills, upload, set-admin)
913
+
914
+ Environment:
915
+ MAKARON_API_KEY API key (mk_live_xxx) — recommended for agents
916
+ MAKARON_URL API base (default: ${DEFAULT_URL})
917
+ `);
918
+ }
919
+
920
+ function printHelp(topic, subtopic) {
921
+ if (topic === 'login') {
922
+ console.log('Usage: makaron login');
923
+ } else if (topic === 'create') {
924
+ console.log('Usage: makaron create --image <file> [--image <file2>] | --image-url <url> | --title "name"');
925
+ } else if (topic === 'chat') {
926
+ console.log('Usage: makaron chat --project <id|auto> [--image <file>] [--video <file|url>] [--stream] [--background|-b] [--json] "your message"');
927
+ } else if (topic === 'responses' || topic === 'run') {
928
+ if (subtopic === 'get') console.log('Usage: makaron responses get <runId> [--wait] [--json] [--pick <field>]');
929
+ else if (subtopic === 'watch') console.log('Usage: makaron responses watch <runId> [--jsonl] [--interval <ms>]');
930
+ else if (subtopic === 'list') console.log('Usage: makaron responses list --project <id>');
931
+ else console.log(`Responses commands:
932
+ responses get <runId> Get status and output (JSON)
933
+ responses get <runId> --wait Poll until completed
934
+ responses get <runId> --pick <field> Extract: first_image_url, first_video_url, project_url, output
935
+ responses watch <runId> --jsonl Watch until done (incremental events)
936
+ responses list --project <id> List runs for a project
937
+ `);
938
+ } else if (topic === 'list' || topic === 'ls') {
939
+ console.log('Usage: makaron list');
940
+ } else if (topic === 'project' || topic === 'projects') {
941
+ if (subtopic === 'media') console.log('Usage: makaron project media <projectId> [--json]');
942
+ else console.log(`Project commands:
943
+ project media <projectId> --json List timeline media for a project
944
+ `);
945
+ } else if (topic === 'abort') {
946
+ console.log('Usage: makaron abort <runId>');
947
+ } else if (topic === 'edit') {
948
+ console.log('Usage: makaron edit [--image <file|url>] [--model gemini|qwen|openai] [--skill enhance|creative|wild|captions] [--ref <file>] [--out <file>] "prompt"');
949
+ } else if (topic === 'analyze') {
950
+ console.log('Usage: makaron analyze --video <file|url> ["question"]');
951
+ } else if (topic === 'video') {
952
+ if (subtopic === 'script') console.log('Usage: makaron video script --image <file> [--image <file>] [--lang en|zh] "direction"');
953
+ else if (subtopic === 'create') console.log('Usage: makaron video create --script "..." (--image <url> | --video <public-url>) [--duration 10] [--aspect 9:16] [--model kling|seedance] [--keep-original-sound]');
954
+ else if (subtopic === 'status') console.log('Usage: makaron video status <taskId> | --snapshot <snapshotId> [--wait]');
955
+ else console.log(`Video commands:
956
+ video script --image <file> [--image <file>] "direction" Write video script
957
+ video create --script "..." --image <url> [--duration 10] Submit video task
958
+ video create --script "..." --video <public-url> [--model kling|seedance] Edit a video (standalone)
959
+ video status <taskId> Check video status
960
+ video status --snapshot <snapshotId> [--wait] Check v2 video snapshot
961
+ `);
962
+ } else if (topic === 'music') {
963
+ if (subtopic === 'create') console.log('Usage: makaron music create [--vocals] [--style "genre"] "description"');
964
+ else if (subtopic === 'status') console.log('Usage: makaron music status <taskId>');
965
+ else console.log(`Music commands:
966
+ music create [--vocals] [--style "genre"] "description" Generate music
967
+ music status <taskId> Check music status
968
+ `);
969
+ } else if (topic === 'admin') {
970
+ if (subtopic === 'skills') console.log('Usage: makaron admin skills [add|update|delete] ...');
971
+ else if (subtopic === 'upload') console.log('Usage: makaron admin upload <local-file> <storage-path>');
972
+ else if (subtopic === 'fetch-skill') console.log('Usage: makaron admin fetch-skill <share-code|url>');
973
+ else if (subtopic === 'set-admin') console.log('Usage: makaron admin set-admin <email>');
974
+ else console.log(`Admin commands:
975
+ admin skills List all marketplace skills
976
+ admin skills add '<json>' Add a new skill
977
+ admin skills update <id> '<json>' Update a skill
978
+ admin skills delete <id> Delete a skill
979
+ admin upload <file> <storage-path> Upload file to Storage
980
+ admin fetch-skill <code|url> Download skill from share link
981
+ admin set-admin <email> Grant admin access to a user
982
+ `);
983
+ } else if (topic === 'register') {
984
+ if (subtopic === '--verify') console.log('Usage: makaron register --verify --challenge-id <id> --answer <number>');
985
+ else console.log('Usage: makaron register --json | makaron register --verify --challenge-id <id> --answer <number>');
986
+ } else if (topic === 'claim') {
987
+ console.log('Usage: makaron claim');
988
+ } else {
989
+ printRootHelp();
990
+ }
991
+ }
992
+
853
993
  // ─── Main ────────────────────────────────────────────────────────────────────
854
994
 
855
995
  const args = process.argv.slice(2);
856
996
  const command = args[0];
857
997
 
858
- if (command === '--version' || command === '-v' || command === 'version') {
998
+ if (!command || command === '--help' || command === '-h' || command === 'help') {
999
+ printRootHelp();
1000
+ } else if (hasHelpFlag(args)) {
1001
+ printHelp(command, args[1]);
1002
+ } else if (command === '--version' || command === '-v' || command === 'version') {
859
1003
  console.log(getCliVersion());
860
1004
  } else if (command === 'login') {
861
1005
  await login();
@@ -886,11 +1030,7 @@ if (command === '--version' || command === '-v' || command === 'version') {
886
1030
  let videoModel = undefined;
887
1031
  let preferredModel = undefined;
888
1032
  for (let i = 1; i < args.length; i++) {
889
- if (args[i] === '--help' || args[i] === '-h') {
890
- console.error('Usage: makaron chat --project <id|auto> [--image <file>] [--video <file|url>] [--stream] [--background|-b] [--json] "your message"');
891
- process.exit(0);
892
- }
893
- else if (args[i] === '--project' && args[i + 1]) projectId = args[++i];
1033
+ if (args[i] === '--project' && args[i + 1]) projectId = args[++i];
894
1034
  else if (args[i] === '--image' && args[i + 1]) chatImages.push(args[++i]);
895
1035
  else if (args[i] === '--video' && args[i + 1]) chatVideos.push(args[++i]);
896
1036
  else if (args[i] === '--stream') useStream = true;
@@ -1236,13 +1376,21 @@ if (command === '--version' || command === '-v' || command === 'version') {
1236
1376
 
1237
1377
  let videoUrl = isHttpUrl(video) ? video : null;
1238
1378
  let inputVideoMeta = null;
1379
+ const selectedVideoModel = videoModel || 'kling';
1239
1380
  if (videoUrl) {
1240
- process.stderr.write(`📹 Assuming public video URL already matches provider reference limits: ≤${MAX_VIDEO_PROVIDER_REFERENCE_DURATION}s, ≤${MAX_VIDEO_UPLOAD_FILE_SIZE_MB}MB,1080p.\n`);
1381
+ 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.\n`);
1241
1382
  }
1242
1383
  if (video && !videoUrl) {
1243
1384
  const valid = validateVideoFile(video, {
1244
1385
  maxDuration: MAX_VIDEO_PROVIDER_REFERENCE_DURATION,
1245
1386
  durationTolerance: MAX_VIDEO_PROVIDER_REFERENCE_DURATION_TOLERANCE,
1387
+ ...(selectedVideoModel === 'seedance' ? {
1388
+ minFramePixels: SEEDANCE_MIN_VIDEO_FRAME_PIXELS,
1389
+ minSide: SEEDANCE_MIN_VIDEO_SIDE,
1390
+ maxSide: SEEDANCE_MAX_VIDEO_SIDE,
1391
+ minAspect: SEEDANCE_MIN_VIDEO_ASPECT,
1392
+ maxAspect: SEEDANCE_MAX_VIDEO_ASPECT,
1393
+ } : {}),
1246
1394
  });
1247
1395
  if (!valid.ok) { console.error(`❌ ${valid.error}`); process.exit(1); }
1248
1396
  inputVideoMeta = valid.meta;
@@ -1254,7 +1402,7 @@ if (command === '--version' || command === '-v' || command === 'version') {
1254
1402
  // Standalone MCP tool (no project timeline write)
1255
1403
  process.stderr.write('🎬 Submitting video...\n');
1256
1404
  const vArgs = videoUrl
1257
- ? { videoUrl, editPrompt: script, images, videoModel: videoModel || 'kling', referType: (videoModel || 'kling') === 'seedance' ? 'feature' : 'base' }
1405
+ ? { videoUrl, editPrompt: script, images, videoModel: selectedVideoModel, referType: selectedVideoModel === 'seedance' ? 'feature' : 'base' }
1258
1406
  : { script, images };
1259
1407
  const effectiveDuration = duration || (inputVideoMeta?.duration ? Math.min(MAX_VIDEO_PROVIDER_REFERENCE_DURATION, Math.round(inputVideoMeta.duration)) : undefined);
1260
1408
  if (effectiveDuration) vArgs.duration = effectiveDuration;
@@ -1549,39 +1697,5 @@ if (command === '--version' || command === '-v' || command === 'version') {
1549
1697
  console.log(JSON.stringify(data));
1550
1698
  console.error(`🔗 Share this link with a human: ${data.claim_url}`);
1551
1699
  } else {
1552
- console.log(`Makaron CLI — Talk to Makaron Agent from the terminal
1553
-
1554
- Commands:
1555
- register --json Get challenge for agent self-registration
1556
- register --verify --challenge-id <id> --answer <n> Verify and save API key
1557
- claim Get claim URL for human to link account
1558
- login Log in to Makaron (human interactive)
1559
- list (ls) List all projects
1560
- project media <projectId> --json List timeline media for a project
1561
- create --image <file> Create project from local image
1562
- create --image-url <url> Create project from URL
1563
- create --title "name" Create empty project (text-to-image)
1564
-
1565
- chat --project <id> "message" Chat (non-blocking, polls for result)
1566
- chat --project <id> --video <file> Attach video to conversation
1567
- chat --project <id> -b "message" Background: submit and print runId
1568
- chat --project <id> --stream "msg" Legacy: stream SSE in real-time
1569
- chat --project <id> --json "msg" Output structured JSON result
1570
-
1571
- responses get <runId> Get run status and results
1572
- responses get <runId> --wait Poll until completed
1573
- responses list --project <id> List runs for a project
1574
- abort <runId> Abort a running Agent
1575
-
1576
- edit [--image <file>] "prompt" AI image edit / text-to-image
1577
- analyze --video <file|url> Analyze video content
1578
- video script|create|status Video generation
1579
- music create|status Music generation
1580
-
1581
- admin Admin commands (skills, upload, set-admin)
1582
-
1583
- Environment:
1584
- MAKARON_API_KEY API key (mk_live_xxx) — recommended for agents
1585
- MAKARON_URL API base (default: ${DEFAULT_URL})
1586
- `);
1700
+ printRootHelp();
1587
1701
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "makaron-cli",
3
- "version": "0.8.3",
3
+ "version": "0.8.4",
4
4
  "description": "Talk to Makaron Agent from the terminal — create projects, edit images, generate videos",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -9,7 +9,7 @@
9
9
  "test:live": "MAKARON_LIVE=1 node test/live-smoke.mjs"
10
10
  },
11
11
  "bin": {
12
- "makaron": "./bin/makaron.mjs"
12
+ "makaron": "bin/makaron.mjs"
13
13
  },
14
14
  "files": [
15
15
  "bin/",
@@ -30,6 +30,6 @@
30
30
  "license": "MIT",
31
31
  "repository": {
32
32
  "type": "git",
33
- "url": "https://github.com/vegekyd/ai-image-editor"
33
+ "url": "git+https://github.com/vegekyd/ai-image-editor.git"
34
34
  }
35
35
  }
@@ -198,9 +198,9 @@ npx makaron-cli video status <taskId>
198
198
  npx makaron-cli chat --project <id|auto> --video input.mp4 -b "make it funny"
199
199
  ```
200
200
 
201
- 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`, `--model kling|seedance`. SeeDance accepts integer output duration 4-15s (default 5s); Kling supports 5-10s.
201
+ 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`, `--model kling|seedance`. SeeDance accepts integer output duration 4-15s (default 5s); Kling supports 5-15s.
202
202
 
203
- Video edit model behavior: `--model kling --video` uses Kling base/direct edit internally; `--model seedance --video` uses the Seedance video-reference path and requires target <=15s, <=1080p input. Tiny metadata padding up to 15.5s is accepted and output duration is clamped to 15s.
203
+ Video edit model behavior: `--model kling --video` uses Kling base/direct edit internally; `--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. Tiny metadata padding up to 15.5s is accepted and output duration is clamped to 15s.
204
204
 
205
205
  ### `music` — Music generation
206
206