makaron-cli 0.6.1 → 0.6.2

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.
Files changed (2) hide show
  1. package/bin/makaron.mjs +39 -14
  2. package/package.json +1 -1
package/bin/makaron.mjs CHANGED
@@ -499,15 +499,11 @@ async function createProject(baseUrl, headers, opts) {
499
499
  if (opts.imageUrls?.length) {
500
500
  body.imageUrls = opts.imageUrls;
501
501
  } else if (opts.images?.length) {
502
- body.imageBase64s = opts.images.map(f => {
503
- const buf = fs.readFileSync(f);
504
- return `data:image/jpeg;base64,${buf.toString('base64')}`;
505
- });
502
+ body.imageBase64s = opts.images.map(f => readImageAsDataUrl(f));
506
503
  } else if (opts.imageUrl) {
507
504
  body.imageUrl = opts.imageUrl;
508
505
  } else if (opts.image) {
509
- const buf = fs.readFileSync(opts.image);
510
- body.imageBase64 = `data:image/jpeg;base64,${buf.toString('base64')}`;
506
+ body.imageBase64 = readImageAsDataUrl(opts.image);
511
507
  }
512
508
  if (opts.title) body.title = opts.title;
513
509
 
@@ -567,12 +563,43 @@ async function callMcpTool(baseUrl, headers, toolName, args) {
567
563
  return data.result;
568
564
  }
569
565
 
566
+ // ─── Image Validation ────────────────────────────────────────────────────────
567
+
568
+ function detectMime(buf) {
569
+ if (buf[0]===0xFF && buf[1]===0xD8) return 'image/jpeg';
570
+ if (buf[0]===0x89 && buf[1]===0x50 && buf[2]===0x4E && buf[3]===0x47) return 'image/png';
571
+ if (buf[0]===0x52 && buf[1]===0x49 && buf[2]===0x46 && buf[3]===0x46 && buf[8]===0x57 && buf[9]===0x45 && buf[10]===0x42 && buf[11]===0x50) return 'image/webp';
572
+ if (buf[0]===0x47 && buf[1]===0x49 && buf[2]===0x46) return 'image/gif';
573
+ if (buf.length >= 8 && buf[4]===0x66 && buf[5]===0x74 && buf[6]===0x79 && buf[7]===0x70) return 'image/heic';
574
+ return null;
575
+ }
576
+
577
+ function validateImage(filePath) {
578
+ if (!fs.existsSync(filePath)) return { error: `File not found: ${filePath}` };
579
+ const stat = fs.statSync(filePath);
580
+ if (stat.size === 0) return { error: `File is empty: ${filePath}` };
581
+ if (stat.size > 10 * 1024 * 1024) return { error: `File too large: ${(stat.size/1024/1024).toFixed(1)}MB (max 10MB). Resize before uploading.` };
582
+ const header = Buffer.alloc(12);
583
+ const fd = fs.openSync(filePath, 'r');
584
+ fs.readSync(fd, header, 0, 12, 0);
585
+ fs.closeSync(fd);
586
+ const mime = detectMime(header);
587
+ if (!mime) return { error: `Unsupported format: ${path.extname(filePath)}. Supported: JPEG, PNG, WebP` };
588
+ if (mime === 'image/heic') return { error: `HEIC format not supported via CLI. Convert first:\n sips -s format jpeg "${filePath}" --out output.jpg` };
589
+ if (mime === 'image/gif') return { error: `GIF format not supported. Convert to JPEG or PNG first.` };
590
+ return { ok: true, mime };
591
+ }
592
+
593
+ function readImageAsDataUrl(filePath) {
594
+ const v = validateImage(filePath);
595
+ if (!v.ok) { console.error(`❌ Cannot upload: ${path.basename(filePath)}\n ${v.error}`); process.exit(1); }
596
+ const buf = fs.readFileSync(filePath);
597
+ return `data:${v.mime};base64,${buf.toString('base64')}`;
598
+ }
599
+
570
600
  function imageToArg(imgPath) {
571
601
  if (imgPath.startsWith('http://') || imgPath.startsWith('https://')) return imgPath;
572
- const buf = fs.readFileSync(imgPath);
573
- const ext = imgPath.split('.').pop()?.toLowerCase();
574
- const mime = { jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png', webp: 'image/webp' }[ext] || 'image/jpeg';
575
- return `data:${mime};base64,${buf.toString('base64')}`;
602
+ return readImageAsDataUrl(imgPath);
576
603
  }
577
604
 
578
605
  function saveMcpImage(result, outputPath) {
@@ -659,8 +686,7 @@ if (command === 'login') {
659
686
  // Create project with images
660
687
  const base64s = chatImages.map(imgPath => {
661
688
  process.stderr.write(`📤 Uploading ${path.basename(imgPath)}...\n`);
662
- const buf = fs.readFileSync(imgPath);
663
- return `data:image/jpeg;base64,${buf.toString('base64')}`;
689
+ return readImageAsDataUrl(imgPath);
664
690
  });
665
691
  const res = await fetch(`${baseUrl}/api/projects/create`, {
666
692
  method: 'POST',
@@ -678,8 +704,7 @@ if (command === 'login') {
678
704
  if (chatImages.length > 0) {
679
705
  const base64s = chatImages.map(imgPath => {
680
706
  process.stderr.write(`📤 Uploading ${path.basename(imgPath)}...\n`);
681
- const buf = fs.readFileSync(imgPath);
682
- return `data:image/jpeg;base64,${buf.toString('base64')}`;
707
+ return readImageAsDataUrl(imgPath);
683
708
  });
684
709
  const res = await fetch(`${baseUrl}/api/projects/create`, {
685
710
  method: 'POST',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "makaron-cli",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "description": "Talk to Makaron Agent from the terminal — create projects, edit images, generate videos",
5
5
  "type": "module",
6
6
  "bin": {