makaron-cli 0.13.1 → 0.13.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "makaron-cli",
3
- "version": "0.13.1",
3
+ "version": "0.13.2",
4
4
  "description": "AI image editing, video generation, music creation, and marketplace skill workflows via CLI. Agents can self-register, install skills, 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.13.1",
3
+ "version": "0.13.2",
4
4
  "description": "AI image editing, video generation, music creation, and marketplace skill workflows via CLI. Agents can self-register, install skills, 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
@@ -48,6 +48,12 @@ export MAKARON_API_KEY=mk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
48
48
 
49
49
  Verify: `npx makaron-cli list` should show projects.
50
50
 
51
+ Check the current credit balance and subscription:
52
+ ```bash
53
+ npx makaron-cli credits
54
+ npx makaron-cli credits --json
55
+ ```
56
+
51
57
  ### Let a human claim your account
52
58
 
53
59
  After registering, generate a link for a human to link your API key to their account:
package/bin/makaron.mjs CHANGED
@@ -28,6 +28,7 @@ const NPM_PACKAGE_NAME = 'makaron-cli';
28
28
  const UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
29
29
  const UPDATE_CHECK_TIMEOUT_MS = 400;
30
30
  const AGENT_MODELS = ['auto', 'gpt-5.6-terra', 'gpt-5.6-sol', 'gpt-5.6-luna', 'grok-4.5', 'deepseek-v4-pro'];
31
+ const AGENT_WAIT_TIMEOUT_SECONDS = Math.max(900, Number(process.env.MAKARON_AGENT_WAIT_TIMEOUT_SECONDS || 10_800));
31
32
 
32
33
  // Public anon key (safe to embed — only enables auth, not data access)
33
34
  const SUPABASE_URL = 'https://sdyrtztrjgmmpnirswxt.supabase.co';
@@ -363,6 +364,8 @@ async function streamAgent(baseUrl, headers, projectId, prompt, opts = {}) {
363
364
  ...(opts.videoModel ? { videoModel: opts.videoModel } : {}),
364
365
  ...(opts.videoResolution ? { videoResolution: opts.videoResolution } : {}),
365
366
  ...(opts.agentModel && opts.agentModel !== 'auto' ? { agentModel: opts.agentModel } : {}),
367
+ ...(opts.uploadedVideoCount ? { uploadedVideoCount: opts.uploadedVideoCount } : {}),
368
+ ...(opts.turnMediaCount ? { turnMediaCount: opts.turnMediaCount } : {}),
366
369
  }),
367
370
  signal: controller.signal,
368
371
  });
@@ -476,6 +479,8 @@ async function submitRun(baseUrl, headers, projectId, prompt, opts = {}) {
476
479
  if (opts.currentSnapshotIndex != null) body.currentSnapshotIndex = opts.currentSnapshotIndex;
477
480
  if (opts.isNsfw) body.isNsfw = opts.isNsfw;
478
481
  if (opts.audioAttachments?.length) body.audioAttachments = opts.audioAttachments;
482
+ if (opts.uploadedVideoCount) body.uploadedVideoCount = opts.uploadedVideoCount;
483
+ if (opts.turnMediaCount) body.turnMediaCount = opts.turnMediaCount;
479
484
 
480
485
  const res = await fetch(`${baseUrl}/api/agent/run`, {
481
486
  method: 'POST',
@@ -519,7 +524,7 @@ async function pollRun(baseUrl, headers, runId, opts = {}) {
519
524
  try {
520
525
  const res = await fetch(`${baseUrl}/api/agent/run/${runId}?${params}`, { headers });
521
526
  if (!res.ok) {
522
- if (elapsed > 800) { process.stderr.write(`\n❌ Timeout after ${elapsed}s\n`); process.exit(1); }
527
+ if (elapsed > AGENT_WAIT_TIMEOUT_SECONDS) { process.stderr.write(`\n❌ Timeout after ${elapsed}s\n`); process.exit(1); }
523
528
  continue;
524
529
  }
525
530
  data = await res.json();
@@ -569,6 +574,13 @@ async function pollRun(baseUrl, headers, runId, opts = {}) {
569
574
  case 'music_task':
570
575
  process.stderr.write(`\n🎵 Music submitted: ${ev.data?.taskId}\n`);
571
576
  break;
577
+ case 'studio_run': {
578
+ const stage = ev.data?.currentStage || ev.data?.current_stage || 'complete';
579
+ const recipe = ev.data?.recipe || 'studio';
580
+ const status = ev.data?.status || 'running';
581
+ process.stderr.write(`\nStudio Run: ${recipe} / ${stage} / ${status}\n`);
582
+ break;
583
+ }
572
584
  case 'error':
573
585
  process.stderr.write(`\n❌ Error: ${ev.data?.message}\n`);
574
586
  break;
@@ -580,6 +592,7 @@ async function pollRun(baseUrl, headers, runId, opts = {}) {
580
592
 
581
593
  // Check terminal status
582
594
  if (data.status === 'completed' || data.status === 'failed' || data.status === 'aborted') {
595
+ normalizeRunResponse(data);
583
596
  if (printedText && !json) process.stdout.write('\n');
584
597
  if (data.status === 'completed' && exportCompositions) {
585
598
  data = await exportAnimatedCompositionsFromRun(baseUrl, headers, data, {
@@ -611,7 +624,7 @@ async function pollRun(baseUrl, headers, runId, opts = {}) {
611
624
  process.stderr.write(`🔗 ${APP_URL}/projects/${data.projectId}\n`);
612
625
  }
613
626
 
614
- if (data.status === 'failed') process.exit(1);
627
+ if (data.status === 'failed' || data.status === 'aborted') process.exit(1);
615
628
  return data;
616
629
  }
617
630
  }
@@ -620,11 +633,15 @@ async function pollRun(baseUrl, headers, runId, opts = {}) {
620
633
  // ─── Pick Helper ────────────────────────────────────────────────────────────
621
634
 
622
635
  function applyPick(data, field) {
636
+ const videoUrls = [...new Set([
637
+ ...(data.output || []).filter(o => o.type === 'video' && o.url).map(o => o.url),
638
+ ...(data.result?.videos || []).filter(v => v.videoUrl).map(v => v.videoUrl),
639
+ ])];
623
640
  switch (field) {
624
641
  case 'first_image_url': return data.output?.find(o => o.type === 'image')?.url || null;
625
642
  case 'image_urls': return (data.output || []).filter(o => o.type === 'image' && o.url).map(o => o.url);
626
- case 'first_video_url': return data.output?.find(o => o.type === 'video' && o.url)?.url || null;
627
- case 'video_urls': return (data.output || []).filter(o => o.type === 'video' && o.url).map(o => o.url);
643
+ case 'first_video_url': return videoUrls[0] || null;
644
+ case 'video_urls': return videoUrls;
628
645
  case 'first_design_url': return data.output?.find(o => o.type === 'design')?.url || null;
629
646
  case 'design_urls': return (data.output || []).filter(o => o.type === 'design' && o.url).map(o => o.url);
630
647
  case 'first_music_url': return data.output?.find(o => o.type === 'music' && o.url)?.url || null;
@@ -635,6 +652,8 @@ function applyPick(data, field) {
635
652
  description: action.description,
636
653
  source: action.source,
637
654
  }));
655
+ case 'studio_run': return [...(data.output || [])].reverse().find(o => o.type === 'studio_run') || null;
656
+ case 'studio_recipe': return [...(data.output || [])].reverse().find(o => o.type === 'studio_run')?.recipe || null;
638
657
  case 'project_url': return data.project_url || data.projectUrl || null;
639
658
  case 'output': return data.output || [];
640
659
  case 'text': return data.output?.find(o => o.type === 'text')?.content || null;
@@ -656,7 +675,7 @@ async function watchRun(baseUrl, headers, runId, opts = {}) {
656
675
  try {
657
676
  const res = await fetch(`${baseUrl}/api/agent/run/${runId}`, { headers });
658
677
  if (!res.ok) {
659
- if (elapsed > 800) { process.stderr.write(`Timeout after ${elapsed}s\n`); process.exit(2); }
678
+ if (elapsed > AGENT_WAIT_TIMEOUT_SECONDS) { process.stderr.write(`Timeout after ${elapsed}s\n`); process.exit(2); }
660
679
  await new Promise(r => setTimeout(r, interval));
661
680
  continue;
662
681
  }
@@ -1042,6 +1061,33 @@ async function fetchMarketplaceSkills(baseUrl, opts = {}) {
1042
1061
  return skills.map(normalizeMarketplaceSkill);
1043
1062
  }
1044
1063
 
1064
+ async function fetchBuiltInSkills(baseUrl) {
1065
+ const res = await fetch(`${baseUrl}/api/skills?include=internal`);
1066
+ if (!res.ok) {
1067
+ process.stderr.write(`Error ${res.status}: ${await res.text()}\n`);
1068
+ process.exit(1);
1069
+ }
1070
+ const data = await res.json();
1071
+ return (data.skills || []).filter(skill => skill.builtIn);
1072
+ }
1073
+
1074
+ function printBuiltInSkills(skills) {
1075
+ if (!skills.length) {
1076
+ console.log('No built-in skills found.');
1077
+ return;
1078
+ }
1079
+ console.log(`Built-in skills: ${skills.length}\n`);
1080
+ for (const skill of skills) {
1081
+ const recipe = skill.studioRunRecipe ? ` [Studio Run: ${skill.studioRunRecipe}]` : '';
1082
+ const source = skill.sourceMediaRequired ? ' [source media required]' : '';
1083
+ const adapter = skill.sourceProject === 'openmontage'
1084
+ ? ` [OpenMontage: ${skill.supportLevel || 'adapted'}${skill.canonicalSkill && skill.canonicalSkill !== skill.name ? ` -> ${skill.canonicalSkill}` : ''}]`
1085
+ : '';
1086
+ console.log(` ${skill.name}${recipe}${source}${adapter}`);
1087
+ if (skill.description) console.log(` ${String(skill.description).replace(/\s+/g, ' ').trim()}`);
1088
+ }
1089
+ }
1090
+
1045
1091
  function marketplaceSearchText(skill) {
1046
1092
  const localized = [...localizedValues(skill.labels), ...localizedValues(skill.prompts)];
1047
1093
  return [
@@ -1554,6 +1600,7 @@ Commands:
1554
1600
  register --verify --challenge-id <id> --answer <n> Verify and save API key
1555
1601
  claim Get claim URL for human to link account
1556
1602
  login Log in to Makaron (human interactive)
1603
+ credits Show current credit balance
1557
1604
  list (ls) List all projects
1558
1605
  project media <projectId> --json List timeline media for a project
1559
1606
  create --image <file> Create project from local image
@@ -1561,7 +1608,7 @@ Commands:
1561
1608
  create --title "name" Create empty project (text-to-image)
1562
1609
 
1563
1610
  chat --project <id> "message" Chat (non-blocking, polls for result)
1564
- chat --project <id> --skill <id> Use or auto-install a marketplace skill
1611
+ chat --project <id> --skill <id> Use a built-in or marketplace skill
1565
1612
  chat --project <id> --video <file> Attach video to conversation
1566
1613
  chat --project <id> --audio <file> Attach song/beat/voice reference
1567
1614
  chat --project <id> -b "message" Background: submit and print runId
@@ -1578,7 +1625,7 @@ Commands:
1578
1625
  Export editable Remotion composition to MP4
1579
1626
  responses list --project <id> List runs for a project
1580
1627
  abort <runId> Abort a running Agent
1581
- skills list|search|show|install Browse and install marketplace skills
1628
+ skills list|search|show|install Browse built-in and marketplace skills
1582
1629
 
1583
1630
  edit [--image <file>] "prompt" AI image edit / text-to-image
1584
1631
  analyze --video <file|url> Analyze video content
@@ -1677,6 +1724,8 @@ function printHelp(topic, subtopic) {
1677
1724
  `);
1678
1725
  } else if (topic === 'list' || topic === 'ls') {
1679
1726
  console.log('Usage: makaron list');
1727
+ } else if (topic === 'credits' || topic === 'credit' || topic === 'balance') {
1728
+ console.log('Usage: makaron credits [--json]');
1680
1729
  } else if (topic === 'project' || topic === 'projects') {
1681
1730
  if (subtopic === 'media') console.log('Usage: makaron project media <projectId> [--json]');
1682
1731
  else console.log(`Project commands:
@@ -1689,13 +1738,17 @@ function printHelp(topic, subtopic) {
1689
1738
  } else if (topic === 'install-skill') {
1690
1739
  console.log('Usage: makaron install-skill [--global] [--agent <agent>] [--yes]');
1691
1740
  } else if (topic === 'skills') {
1692
- if (subtopic === 'list') console.log('Usage: makaron skills list [--json]');
1741
+ if (subtopic === 'list') console.log('Usage: makaron skills list [--built-in] [--json]');
1693
1742
  else if (subtopic === 'search') console.log('Usage: makaron skills search <query> [--json]');
1694
1743
  else if (subtopic === 'show') console.log('Usage: makaron skills show <marketplace-id|label> [--json]');
1695
1744
  else if (subtopic === 'install') console.log('Usage: makaron skills install <marketplace-id|label> [--json]');
1696
- else console.log(`Skill marketplace commands:
1745
+ else console.log(`Skill commands:
1746
+ skills list --built-in List all built-in Makaron skills and Studio Run recipes
1747
+ skills list --built-in --openmontage
1748
+ List OpenMontage-native adapters only
1697
1749
  skills list List marketplace skills
1698
1750
  skills search <query> Search marketplace skills
1751
+ skills show <id|label> --built-in Show a built-in skill
1699
1752
  skills show <id|label> Show a marketplace skill
1700
1753
  skills install <id|label> Install a marketplace skill to your workspace
1701
1754
 
@@ -1783,6 +1836,29 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1783
1836
  installAgentSkill(args.slice(1));
1784
1837
  } else if (command === 'login') {
1785
1838
  await login();
1839
+ } else if (command === 'credits' || command === 'credit' || command === 'balance') {
1840
+ const { headers, baseUrl } = getAuth();
1841
+ const res = await fetch(`${baseUrl}/api/billing/credits`, { headers });
1842
+ const data = await res.json().catch(() => null);
1843
+ if (!res.ok || !data) {
1844
+ const message = data?.error || data?.message || (res.ok ? 'Invalid response' : `HTTP ${res.status}`);
1845
+ process.stderr.write(`Failed to get credits: ${message}\n`);
1846
+ process.exit(1);
1847
+ }
1848
+ if (args.includes('--json')) {
1849
+ console.log(JSON.stringify(data));
1850
+ } else {
1851
+ console.log(`Credits: ${data.balance ?? 0}`);
1852
+ console.log(`Lifetime purchased: ${data.lifetimePurchased ?? 0}`);
1853
+ console.log(`Lifetime used: ${data.lifetimeUsed ?? 0}`);
1854
+ if (data.subscription) {
1855
+ const plan = data.subscription.planId || 'unknown';
1856
+ const status = data.subscription.status ? ` (${data.subscription.status})` : '';
1857
+ console.log(`Subscription: ${plan}${status}`);
1858
+ } else {
1859
+ console.log('Subscription: none');
1860
+ }
1861
+ }
1786
1862
  } else if (command === 'create') {
1787
1863
  const { headers, baseUrl } = getAuth();
1788
1864
  const opts = { images: [], imageUrls: [] };
@@ -1888,6 +1964,9 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1888
1964
  process.exit(1);
1889
1965
  }
1890
1966
 
1967
+ let uploadedTurnMediaCount = 0;
1968
+ let uploadedTurnVideoCount = 0;
1969
+
1891
1970
  // --project auto: create a new project (with images/videos if provided)
1892
1971
  if (!projectId || projectId === 'auto') {
1893
1972
  // Create an empty project first, then attach media by URL. Local images use
@@ -1925,6 +2004,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1925
2004
  process.exit(1);
1926
2005
  }
1927
2006
  process.stderr.write(`📤 Added ${addedCount} image(s) to project\n`);
2007
+ uploadedTurnMediaCount += addedCount;
1928
2008
  } else {
1929
2009
  process.stderr.write(`❌ Failed to add images: ${await res.text()}\n`);
1930
2010
  process.exit(1);
@@ -2016,19 +2096,25 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
2016
2096
  const data = await res.json();
2017
2097
  const videoSnaps = (data.snapshots || []).filter(s => s.type === 'video');
2018
2098
  process.stderr.write(`📹 Added ${videoSnaps.length} video(s) to timeline\n`);
2099
+ uploadedTurnVideoCount += videoSnaps.length;
2100
+ uploadedTurnMediaCount += videoSnaps.length;
2019
2101
  } else {
2020
2102
  process.stderr.write(`⚠️ Failed to add videos: ${await res.text()}\n`);
2021
2103
  }
2022
2104
  }
2023
2105
 
2024
- // Inject hint so Agent knows videos are available
2025
- const hint = `[User uploaded ${chatVideos.length === 1 ? 'a video' : `${chatVideos.length} videos`}. Use analyze_video to understand the content.]`;
2026
- finalPrompt = `${finalPrompt}\n\n${hint}`;
2027
2106
  }
2028
2107
 
2029
2108
  if (useStream) {
2030
2109
  // Legacy SSE mode
2031
- const { results } = await streamAgent(baseUrl, headers, projectId, finalPrompt, { videoModel, videoResolution, preferredModel, agentModel });
2110
+ const { results } = await streamAgent(baseUrl, headers, projectId, finalPrompt, {
2111
+ videoModel,
2112
+ videoResolution,
2113
+ preferredModel,
2114
+ agentModel,
2115
+ uploadedVideoCount: uploadedTurnVideoCount,
2116
+ turnMediaCount: uploadedTurnMediaCount,
2117
+ });
2032
2118
  process.stderr.write('\n━━━ Results ━━━\n');
2033
2119
  for (const img of results.images) process.stderr.write(`🖼️ Image: ${img.imageUrl}\n`);
2034
2120
  for (const d of results.designs) process.stderr.write(`🎨 ${d.desc}\n`);
@@ -2037,7 +2123,15 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
2037
2123
  for (const task of results.musicTasks) await pollMusic(baseUrl, headers, task.taskId);
2038
2124
  } else {
2039
2125
  // Default: fire-and-forget + poll
2040
- const { runId } = await submitRun(baseUrl, headers, projectId, finalPrompt, { videoModel, videoResolution, preferredModel, agentModel, audioAttachments });
2126
+ const { runId } = await submitRun(baseUrl, headers, projectId, finalPrompt, {
2127
+ videoModel,
2128
+ videoResolution,
2129
+ preferredModel,
2130
+ agentModel,
2131
+ audioAttachments,
2132
+ uploadedVideoCount: uploadedTurnVideoCount,
2133
+ turnMediaCount: uploadedTurnMediaCount,
2134
+ });
2041
2135
  if (background) {
2042
2136
  // Just print runId and exit
2043
2137
  if (jsonOutput) {
@@ -2225,8 +2319,13 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
2225
2319
  const jsonOutput = args.includes('--json');
2226
2320
 
2227
2321
  if (sub === 'list') {
2228
- const skills = await fetchMarketplaceSkills(baseUrl);
2322
+ const builtIn = args.includes('--built-in');
2323
+ let skills = builtIn ? await fetchBuiltInSkills(baseUrl) : await fetchMarketplaceSkills(baseUrl);
2324
+ if (builtIn && args.includes('--openmontage')) {
2325
+ skills = skills.filter(skill => skill.sourceProject === 'openmontage');
2326
+ }
2229
2327
  if (jsonOutput) console.log(JSON.stringify({ skills }, null, 2));
2328
+ else if (builtIn) printBuiltInSkills(skills);
2230
2329
  else printMarketplaceSkills(skills);
2231
2330
  } else if (sub === 'search') {
2232
2331
  const query = args.filter((arg, index) => index > 1 && arg !== '--json').join(' ').trim();
@@ -2243,11 +2342,15 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
2243
2342
  else printMarketplaceSkills(skills);
2244
2343
  } else if (sub === 'show') {
2245
2344
  const identifier = args[2];
2246
- if (!identifier) { console.error('Usage: makaron skills show <marketplace-id|label> [--json]'); process.exit(1); }
2247
- const skills = await fetchMarketplaceSkills(baseUrl);
2248
- const skill = findMarketplaceSkill(skills, identifier);
2345
+ if (!identifier) { console.error('Usage: makaron skills show <id|label> [--built-in] [--json]'); process.exit(1); }
2346
+ const builtIn = args.includes('--built-in');
2347
+ const skills = builtIn ? await fetchBuiltInSkills(baseUrl) : await fetchMarketplaceSkills(baseUrl);
2348
+ const skill = builtIn
2349
+ ? skills.find(candidate => candidate.name === identifier || candidate.label?.toLowerCase() === identifier.toLowerCase())
2350
+ : findMarketplaceSkill(skills, identifier);
2249
2351
  if (!skill) { console.error(`Skill not found: ${identifier}`); process.exit(1); }
2250
2352
  if (jsonOutput) console.log(JSON.stringify(skill, null, 2));
2353
+ else if (builtIn) printBuiltInSkills([skill]);
2251
2354
  else printMarketplaceSkill(skill);
2252
2355
  } else if (sub === 'install') {
2253
2356
  const identifier = args[2];
@@ -2260,9 +2363,13 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
2260
2363
  if (jsonOutput) console.log(JSON.stringify({ ...data, marketplaceId: skill.id, label: skill.label }, null, 2));
2261
2364
  else console.log(data.skillName);
2262
2365
  } else {
2263
- console.log(`Skill marketplace commands:
2366
+ console.log(`Skill commands:
2367
+ skills list --built-in List all built-in Makaron skills and Studio Run recipes
2368
+ skills list --built-in --openmontage
2369
+ List OpenMontage-native adapters only
2264
2370
  skills list List marketplace skills
2265
2371
  skills search <query> Search marketplace skills
2372
+ skills show <id|label> --built-in Show a built-in skill
2266
2373
  skills show <id|label> Show a marketplace skill
2267
2374
  skills install <id|label> Install a marketplace skill to your workspace
2268
2375
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "makaron-cli",
3
- "version": "0.13.1",
3
+ "version": "0.13.2",
4
4
  "description": "Talk to Makaron Agent from the terminal — create projects, edit images, generate videos",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -44,6 +44,12 @@ export MAKARON_API_KEY=mk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
44
44
 
45
45
  Verify: `npx makaron-cli list` should show projects.
46
46
 
47
+ Check the current credit balance and subscription:
48
+ ```bash
49
+ npx makaron-cli credits
50
+ npx makaron-cli credits --json
51
+ ```
52
+
47
53
  ## Core Workflow
48
54
 
49
55
  ```bash