makaron-cli 0.6.0 → 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.
package/README.md CHANGED
@@ -22,15 +22,17 @@ npm install -g makaron-cli
22
22
  **Option B: Agent Self-Registration (no human required)**
23
23
  ```bash
24
24
  # Step 1: Get challenge
25
- curl -X POST https://www.makaron.app/api/agent/register
25
+ npx makaron-cli register --json
26
+ # → { "challenge_id": "...", "challenge": "...", "expected_format": "numeric, round to 2 decimal places" }
26
27
 
27
- # Step 2: Solve the obfuscated math problem and verify
28
- curl -X POST https://www.makaron.app/api/agent/register/verify \
29
- -H "Content-Type: application/json" \
30
- -d '{"challenge_id": "...", "answer": "34.5"}'
31
- # → { "api_key": "mk_live_...", "credits": 500 }
28
+ # Step 2: Solve the math problem and verify
29
+ npx makaron-cli register --verify --challenge-id <id> --answer 34.5
30
+ # Key saved to ~/.makaron/auth.json
31
+ # { "api_key": "mk_live_...", "credits": N, "claim_url": "..." }
32
32
  ```
33
33
 
34
+ After registration, the key is saved locally — no need to export `MAKARON_API_KEY`.
35
+
34
36
  Discovery endpoint (returns full registration flow + CLI usage as JSON):
35
37
  ```bash
36
38
  curl https://www.makaron.app/api/agent/register
@@ -46,13 +48,12 @@ Verify: `npx makaron-cli list` should show projects.
46
48
 
47
49
  ### Let a human claim your account
48
50
 
49
- After registering, you can generate a link for a human to link your API key to their account:
51
+ After registering, generate a link for a human to link your API key to their account:
50
52
  ```bash
51
- curl -X POST https://www.makaron.app/api/agent/claim \
52
- -H "Authorization: Bearer mk_live_your_key"
53
+ npx makaron-cli claim
53
54
  # → { "claim_url": "https://www.makaron.app/claim?token=clm_..." }
54
55
  ```
55
- Share the `claim_url` with a human. They log in and the API key gets linked to their account.
56
+ Share the `claim_url` with a human. They log in and the API key gets linked to their account. Claim links are valid for 7 days. Run `claim` again anytime to get a new link.
56
57
 
57
58
  ## Core Workflow
58
59
 
package/SKILL.md CHANGED
@@ -21,23 +21,23 @@ Makaron is a multimodal AI creative agent. You talk to it via `makaron chat`, an
21
21
  **Option B: Self-Registration (no human required)**
22
22
  ```bash
23
23
  # Step 1: Get challenge
24
- curl -X POST https://www.makaron.app/api/agent/register
25
- # → { "challenge_id": "uuid", "challenge": "obfuscated math problem" }
24
+ npx makaron-cli register --json
25
+ # → { "challenge_id": "...", "challenge": "...", "expected_format": "numeric, round to 2 decimal places" }
26
26
 
27
27
  # Step 2: Solve and verify
28
- curl -X POST https://www.makaron.app/api/agent/register/verify \
29
- -H "Content-Type: application/json" \
30
- -d '{"challenge_id": "...", "answer": "34.5"}'
31
- # → { "api_key": "mk_live_...", "credits": 500 }
32
-
33
- # Step 3: (Optional) Let a human claim this account
34
- curl -X POST https://www.makaron.app/api/agent/claim \
35
- -H "Authorization: Bearer mk_live_your_key"
36
- # → { "claim_url": "..." } — share with human to link key to their account
28
+ npx makaron-cli register --verify --challenge-id <id> --answer 34.5
29
+ # Key saved to ~/.makaron/auth.json
30
+ # { "api_key": "mk_live_...", "credits": N, "claim_url": "..." }
31
+
32
+ # (Optional) Let a human claim this account
33
+ npx makaron-cli claim
34
+ # { "claim_url": "..." } — share with human to link key to their account (valid 7 days)
37
35
  ```
38
36
 
39
37
  Discovery endpoint: `GET https://www.makaron.app/api/agent/register` — returns full registration flow + CLI usage as JSON.
40
38
 
39
+ After self-registration the key is saved locally — no need to export `MAKARON_API_KEY`.
40
+
41
41
  ```bash
42
42
  export MAKARON_API_KEY=mk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
43
43
  ```
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.0",
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": {