makaron-cli 0.2.0 → 0.3.0

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
@@ -101,6 +101,76 @@ npx makaron-cli abort <runId>
101
101
 
102
102
  Press `Ctrl+C` during `chat` to abort automatically.
103
103
 
104
+ ### `edit` — AI image editing (direct MCP tool call)
105
+
106
+ Unlike `chat` (which uses the Agent with project context), `edit` directly calls the image generation model for one-shot results.
107
+
108
+ ```bash
109
+ # Text-to-image (no input image)
110
+ npx makaron-cli edit "a cyberpunk cityscape at night, neon reflections"
111
+
112
+ # Edit an existing image
113
+ npx makaron-cli edit --image photo.jpg "add cinematic warm lighting"
114
+
115
+ # With model and skill
116
+ npx makaron-cli edit --image photo.jpg --model openai --skill captions "add elegant title"
117
+
118
+ # With reference images (up to 3)
119
+ npx makaron-cli edit --image photo.jpg --ref style1.jpg --ref style2.jpg "match this style"
120
+
121
+ # Specify output path and aspect ratio
122
+ npx makaron-cli edit --out result.jpg --aspect 9:16 "vertical poster design"
123
+ ```
124
+
125
+ Options:
126
+ - `--image <file|url>` — input image (omit for text-to-image)
127
+ - `--model gemini|qwen|openai|pony|wai` — model selection (default: auto)
128
+ - `--skill enhance|creative|wild|captions` — activate skill template
129
+ - `--ref <file|url>` — reference image (repeatable, up to 3)
130
+ - `--aspect <ratio>` — target aspect ratio (e.g. `4:5`, `1:1`, `16:9`)
131
+ - `--out <path>` — output file path (default: `makaron-output-{timestamp}.jpg`)
132
+
133
+ Output: saves image to local file, prints the file path to stdout.
134
+
135
+ ### `video` — Video generation
136
+
137
+ ```bash
138
+ # Write a video script from images
139
+ npx makaron-cli video script --image img1.jpg --image img2.jpg "cinematic story"
140
+ npx makaron-cli video script --image img1.jpg --image img2.jpg --lang zh "电影感故事"
141
+
142
+ # Submit video rendering (images must be public URLs)
143
+ npx makaron-cli video create --script "Shot 1..." --image https://...img1.jpg --duration 10
144
+ npx makaron-cli video create --script-file script.txt --image https://...jpg --model seedance
145
+
146
+ # Check status
147
+ npx makaron-cli video status <taskId>
148
+ ```
149
+
150
+ Options for `video create`:
151
+ - `--script "..."` or `--script-file <path>` — video script
152
+ - `--image <url>` — public image URL (repeatable, up to 7)
153
+ - `--duration 3|5|7|10|15` — seconds (omit for smart mode)
154
+ - `--aspect 9:16|16:9|1:1` — aspect ratio
155
+ - `--model kling|seedance` — video model (default: kling)
156
+
157
+ ### `music` — Music generation
158
+
159
+ ```bash
160
+ # Generate instrumental music
161
+ npx makaron-cli music create "gentle piano, warm strings, cinematic"
162
+
163
+ # With vocals and style
164
+ npx makaron-cli music create --vocals --style "lo-fi, ambient" "rainy day vibes"
165
+
166
+ # Check status
167
+ npx makaron-cli music status <taskId>
168
+ ```
169
+
170
+ Options for `music create`:
171
+ - `--vocals` — include vocals (default: instrumental only)
172
+ - `--style "genre"` — genre/mood tags
173
+
104
174
  ## Example: Full workflow
105
175
 
106
176
  ```bash
package/bin/makaron.mjs CHANGED
@@ -337,6 +337,43 @@ function timeSince(date) {
337
337
  return `${Math.floor(s / 86400)}d ago`;
338
338
  }
339
339
 
340
+ // ─── MCP Tool Caller ─────────────────────────────────────────────────────────
341
+
342
+ async function callMcpTool(baseUrl, headers, toolName, args) {
343
+ const res = await fetch(`${baseUrl}/api/mcp`, {
344
+ method: 'POST',
345
+ headers: { 'Content-Type': 'application/json', 'Accept': 'application/json, text/event-stream', ...headers },
346
+ body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/call', params: { name: toolName, arguments: args } }),
347
+ });
348
+ if (!res.ok) { console.error(`MCP error ${res.status}:`, await res.text()); process.exit(1); }
349
+ const data = await res.json();
350
+ if (data.error) { console.error(`MCP error:`, data.error.message); process.exit(1); }
351
+ return data.result;
352
+ }
353
+
354
+ function imageToArg(imgPath) {
355
+ if (imgPath.startsWith('http://') || imgPath.startsWith('https://')) return imgPath;
356
+ const buf = fs.readFileSync(imgPath);
357
+ const ext = imgPath.split('.').pop()?.toLowerCase();
358
+ const mime = { jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png', webp: 'image/webp' }[ext] || 'image/jpeg';
359
+ return `data:${mime};base64,${buf.toString('base64')}`;
360
+ }
361
+
362
+ function saveMcpImage(result, outputPath) {
363
+ const content = result?.content || [];
364
+ const textBlock = content.find(c => c.type === 'text');
365
+ const imageBlock = content.find(c => c.type === 'image');
366
+ if (textBlock) process.stderr.write(`${textBlock.text}\n`);
367
+ if (imageBlock) {
368
+ const out = outputPath || `makaron-output-${Date.now()}.jpg`;
369
+ fs.writeFileSync(out, Buffer.from(imageBlock.data, 'base64'));
370
+ console.log(out);
371
+ return out;
372
+ }
373
+ if (textBlock) console.log(textBlock.text);
374
+ return null;
375
+ }
376
+
340
377
  // ─── Main ────────────────────────────────────────────────────────────────────
341
378
 
342
379
  const args = process.argv.slice(2);
@@ -419,6 +456,119 @@ if (command === 'login') {
419
456
  } else {
420
457
  console.error(`❌ Abort failed:`, await res.text());
421
458
  }
459
+ } else if (command === 'edit') {
460
+ const { headers, baseUrl } = getAuth();
461
+ const editArgs = {};
462
+ const promptParts = [];
463
+ let outputPath = null;
464
+ for (let i = 1; i < args.length; i++) {
465
+ if (args[i] === '--image' && args[i + 1]) editArgs.image = imageToArg(args[++i]);
466
+ else if (args[i] === '--model' && args[i + 1]) editArgs.model = args[++i];
467
+ else if (args[i] === '--skill' && args[i + 1]) editArgs.skill = args[++i];
468
+ else if (args[i] === '--ref' && args[i + 1]) {
469
+ editArgs.referenceImages = editArgs.referenceImages || [];
470
+ editArgs.referenceImages.push(imageToArg(args[++i]));
471
+ }
472
+ else if (args[i] === '--aspect' && args[i + 1]) editArgs.aspectRatio = args[++i];
473
+ else if (args[i] === '--out' && args[i + 1]) outputPath = args[++i];
474
+ else promptParts.push(args[i]);
475
+ }
476
+ editArgs.editPrompt = promptParts.join(' ');
477
+ if (!editArgs.editPrompt) { console.error('Usage: makaron edit [--image <file|url>] [--model gemini|qwen|openai] [--skill enhance|creative|wild|captions] [--ref <file>] [--out <file>] "prompt"'); process.exit(1); }
478
+ process.stderr.write('🎨 Generating...\n');
479
+ const result = await callMcpTool(baseUrl, headers, 'makaron_edit_image', editArgs);
480
+ saveMcpImage(result, outputPath);
481
+
482
+ } else if (command === 'video') {
483
+ const { headers, baseUrl } = getAuth();
484
+ const sub = args[1];
485
+
486
+ if (sub === 'script') {
487
+ const images = [];
488
+ const promptParts = [];
489
+ let language = 'en';
490
+ for (let i = 2; i < args.length; i++) {
491
+ if (args[i] === '--image' && args[i + 1]) images.push(imageToArg(args[++i]));
492
+ else if (args[i] === '--lang' && args[i + 1]) language = args[++i];
493
+ else promptParts.push(args[i]);
494
+ }
495
+ if (!images.length) { console.error('Usage: makaron video script --image <file> [--image <file>] [--lang en|zh] "direction"'); process.exit(1); }
496
+ process.stderr.write('🎬 Writing script...\n');
497
+ const result = await callMcpTool(baseUrl, headers, 'makaron_write_video_script', { images, userRequest: promptParts.join(' ') || undefined, language });
498
+ const text = result?.content?.find(c => c.type === 'text')?.text;
499
+ if (text) console.log(text);
500
+
501
+ } else if (sub === 'create') {
502
+ const images = [];
503
+ let script = '', duration = undefined, aspectRatio = undefined, videoModel = undefined;
504
+ for (let i = 2; i < args.length; i++) {
505
+ if (args[i] === '--image' && args[i + 1]) images.push(args[++i]);
506
+ else if (args[i] === '--script' && args[i + 1]) script = args[++i];
507
+ else if (args[i] === '--script-file' && args[i + 1]) script = fs.readFileSync(args[++i], 'utf-8');
508
+ else if (args[i] === '--duration' && args[i + 1]) duration = Number(args[++i]);
509
+ else if (args[i] === '--aspect' && args[i + 1]) aspectRatio = args[++i];
510
+ else if (args[i] === '--model' && args[i + 1]) videoModel = args[++i];
511
+ }
512
+ if (!images.length || !script) { console.error('Usage: makaron video create --script "..." --image <url> [--duration 10] [--aspect 9:16] [--model kling|seedance]'); process.exit(1); }
513
+ process.stderr.write('🎬 Submitting video...\n');
514
+ const vArgs = { script, images };
515
+ if (duration) vArgs.duration = duration;
516
+ if (aspectRatio) vArgs.aspectRatio = aspectRatio;
517
+ if (videoModel) vArgs.videoModel = videoModel;
518
+ const result = await callMcpTool(baseUrl, headers, 'makaron_create_video', vArgs);
519
+ const text = result?.content?.find(c => c.type === 'text')?.text;
520
+ if (text) console.log(text);
521
+
522
+ } else if (sub === 'status') {
523
+ const taskId = args[2];
524
+ if (!taskId) { console.error('Usage: makaron video status <taskId>'); process.exit(1); }
525
+ const result = await callMcpTool(baseUrl, headers, 'makaron_get_video_status', { taskId });
526
+ const text = result?.content?.find(c => c.type === 'text')?.text;
527
+ if (text) console.log(text);
528
+
529
+ } else {
530
+ console.log(`Video commands:
531
+ video script --image <file> [--image <file>] "direction" Write video script
532
+ video create --script "..." --image <url> [--duration 10] Submit video task
533
+ video status <taskId> Check video status
534
+ `);
535
+ }
536
+
537
+ } else if (command === 'music') {
538
+ const { headers, baseUrl } = getAuth();
539
+ const sub = args[1];
540
+
541
+ if (sub === 'create') {
542
+ const promptParts = [];
543
+ let instrumental = true, style = undefined;
544
+ for (let i = 2; i < args.length; i++) {
545
+ if (args[i] === '--vocals') instrumental = false;
546
+ else if (args[i] === '--style' && args[i + 1]) style = args[++i];
547
+ else promptParts.push(args[i]);
548
+ }
549
+ const prompt = promptParts.join(' ');
550
+ if (!prompt) { console.error('Usage: makaron music create [--vocals] [--style "lo-fi"] "gentle piano"'); process.exit(1); }
551
+ process.stderr.write('🎵 Generating music...\n');
552
+ const mArgs = { prompt, instrumental };
553
+ if (style) mArgs.style = style;
554
+ const result = await callMcpTool(baseUrl, headers, 'makaron_create_music', mArgs);
555
+ const text = result?.content?.find(c => c.type === 'text')?.text;
556
+ if (text) console.log(text);
557
+
558
+ } else if (sub === 'status') {
559
+ const taskId = args[2];
560
+ if (!taskId) { console.error('Usage: makaron music status <taskId>'); process.exit(1); }
561
+ const result = await callMcpTool(baseUrl, headers, 'makaron_get_music_status', { taskId });
562
+ const text = result?.content?.find(c => c.type === 'text')?.text;
563
+ if (text) console.log(text);
564
+
565
+ } else {
566
+ console.log(`Music commands:
567
+ music create [--vocals] [--style "genre"] "description" Generate music
568
+ music status <taskId> Check music status
569
+ `);
570
+ }
571
+
422
572
  } else if (command === 'admin') {
423
573
  const { headers, baseUrl } = getAuth();
424
574
  const sub = args[1];
@@ -549,6 +699,11 @@ Commands:
549
699
  chat --project <id> "message" Chat with Makaron Agent
550
700
  chat --project <id> --image <file> "message" Add image + chat
551
701
  abort <runId> Abort a running Agent
702
+
703
+ edit [--image <file>] "prompt" AI image edit / text-to-image
704
+ video script|create|status Video generation
705
+ music create|status Music generation
706
+
552
707
  admin Admin commands (skills, upload, set-admin)
553
708
 
554
709
  Environment:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "makaron-cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Talk to Makaron Agent from the terminal — create projects, edit images, generate videos",
5
5
  "type": "module",
6
6
  "bin": {