makaron-cli 0.11.0 → 0.11.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.11.0",
3
+ "version": "0.11.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.11.0",
3
+ "version": "0.11.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
@@ -7,8 +7,10 @@ Makaron is a multimodal AI creative agent. You talk to it via `makaron chat`, an
7
7
  ## Setup
8
8
 
9
9
  ```bash
10
- npm install -g makaron-cli
11
- # or use directly: npx makaron-cli
10
+ # Install makaron-cli globally and add the Makaron Agent Skill.
11
+ npx makaron-cli setup
12
+
13
+ npx makaron-cli --help
12
14
  ```
13
15
 
14
16
  ### Get your API key
package/bin/makaron.mjs CHANGED
@@ -15,6 +15,7 @@ import fs from 'fs';
15
15
  import path from 'path';
16
16
  import { createInterface } from 'readline';
17
17
  import { execFileSync } from 'child_process';
18
+ import { fileURLToPath } from 'url';
18
19
 
19
20
  // ─── Config ──────────────────────────────────────────────────────────────────
20
21
 
@@ -267,12 +268,13 @@ function printChatHelp() {
267
268
  console.log(`Makaron chat — create and edit with Makaron Agent
268
269
 
269
270
  Usage:
270
- makaron chat --project <id|auto> [options] "your message"
271
+ makaron chat --project <id|auto> [options] [--skill <id|label|name>] "your message"
271
272
 
272
273
  Options:
273
274
  --project <id|auto> Project to work in. Use "auto" to create one.
274
275
  --image <file|url> Attach a reference image or screenshot. Repeatable.
275
276
  --video <file|url> Attach a video to the project timeline. Repeatable.
277
+ --skill <id|label|name> Use an installed skill or auto-install a matched marketplace skill.
276
278
  --model <name> Preferred image/model route.
277
279
  --video-model <name> Preferred video model: seedance-fast, seedance, kling, or grok.
278
280
  --video-resolution <res> Video resolution: auto, 480p, 720p, 1080p, or 4k.
@@ -291,6 +293,9 @@ What you can ask:
291
293
  Video from image or timeline
292
294
  makaron chat --project <id> "make this into a 5 second cinematic video"
293
295
 
296
+ Marketplace skill
297
+ makaron chat --project auto --image selfie.jpg --skill "Football Captain" "make this cinematic"
298
+
294
299
  Fix one video moment from a screenshot
295
300
  makaron chat --project <id> --image screenshot.png "@4 this frame should be Paris; only fix this moment"
296
301
 
@@ -1026,6 +1031,25 @@ async function uploadFileViaSignedUrl(baseUrl, headers, projectId, filePath, con
1026
1031
  return publicUrl;
1027
1032
  }
1028
1033
 
1034
+ async function uploadImageFilesViaSignedUrl(baseUrl, headers, projectId, imagePaths) {
1035
+ const urls = [];
1036
+ for (const imagePath of imagePaths) {
1037
+ const valid = validateImage(imagePath);
1038
+ if (!valid.ok) {
1039
+ process.stderr.write(`❌ Cannot upload: ${path.basename(imagePath)}\n ${valid.error}\n`);
1040
+ process.exit(1);
1041
+ }
1042
+ process.stderr.write(`📤 Uploading ${path.basename(imagePath)}...\n`);
1043
+ const url = await uploadFileViaSignedUrl(baseUrl, headers, projectId, imagePath, valid.mime);
1044
+ if (!url) {
1045
+ process.stderr.write(`❌ Failed to upload image: ${imagePath}\n`);
1046
+ process.exit(1);
1047
+ }
1048
+ urls.push(url);
1049
+ }
1050
+ return urls;
1051
+ }
1052
+
1029
1053
  function imageToArg(imgPath) {
1030
1054
  if (imgPath.startsWith('http://') || imgPath.startsWith('https://')) return imgPath;
1031
1055
  return readImageAsDataUrl(imgPath);
@@ -1197,6 +1221,8 @@ function printRootHelp() {
1197
1221
  console.log(`Makaron CLI — Talk to Makaron Agent from the terminal
1198
1222
 
1199
1223
  Commands:
1224
+ setup Install makaron-cli globally and add the Agent Skill
1225
+ install-skill Install Makaron Agent Skill into your coding agent
1200
1226
  register --json Get challenge for agent self-registration
1201
1227
  register --verify --challenge-id <id> --answer <n> Verify and save API key
1202
1228
  claim Get claim URL for human to link account
@@ -1208,6 +1234,7 @@ Commands:
1208
1234
  create --title "name" Create empty project (text-to-image)
1209
1235
 
1210
1236
  chat --project <id> "message" Chat (non-blocking, polls for result)
1237
+ chat --project <id> --skill <id> Use or auto-install a marketplace skill
1211
1238
  chat --project <id> --video <file> Attach video to conversation
1212
1239
  chat --project <id> -b "message" Background: submit and print runId
1213
1240
  chat --project <id> --stream "msg" Legacy: stream SSE in real-time
@@ -1232,13 +1259,56 @@ Environment:
1232
1259
  `);
1233
1260
  }
1234
1261
 
1262
+ function installAgentSkill(values = []) {
1263
+ if (hasHelpFlag(values)) {
1264
+ console.log('Usage: makaron install-skill [--global] [--agent <agent>] [--yes]');
1265
+ return;
1266
+ }
1267
+
1268
+ const skillDir = fileURLToPath(new URL('../skills/makaron', import.meta.url));
1269
+ const skillFile = path.join(skillDir, 'SKILL.md');
1270
+ if (!fs.existsSync(skillFile)) {
1271
+ console.error(`Makaron Agent Skill not found at ${skillFile}`);
1272
+ process.exit(1);
1273
+ }
1274
+
1275
+ execFileSync('npx', [
1276
+ '-y',
1277
+ 'skills',
1278
+ 'add',
1279
+ skillDir,
1280
+ '--skill',
1281
+ 'makaron',
1282
+ '--copy',
1283
+ ...values,
1284
+ ], { stdio: 'inherit' });
1285
+ }
1286
+
1287
+ function setupMakaron(values = []) {
1288
+ if (hasHelpFlag(values)) {
1289
+ console.log('Usage: makaron setup [--agent <agent>]');
1290
+ return;
1291
+ }
1292
+
1293
+ const version = getCliVersion();
1294
+ console.error(`Installing ${NPM_PACKAGE_NAME}@${version} globally...`);
1295
+ execFileSync('npm', ['install', '-g', `${NPM_PACKAGE_NAME}@${version}`], { stdio: 'inherit' });
1296
+
1297
+ const skillArgs = [...values];
1298
+ if (!skillArgs.includes('--global') && !skillArgs.includes('-g')) skillArgs.unshift('--global');
1299
+ if (!skillArgs.includes('--yes') && !skillArgs.includes('-y')) skillArgs.push('--yes');
1300
+
1301
+ console.error('Installing Makaron Agent Skill globally...');
1302
+ installAgentSkill(skillArgs);
1303
+ }
1304
+
1235
1305
  function printHelp(topic, subtopic) {
1236
1306
  if (topic === 'login') {
1237
1307
  console.log('Usage: makaron login');
1238
1308
  } else if (topic === 'create') {
1239
1309
  console.log('Usage: makaron create --image <file> [--image <file2>] | --image-url <url> | --title "name"');
1240
1310
  } else if (topic === 'chat') {
1241
- console.log('Usage: makaron chat --project <id|auto> [--image <file>] [--video <file|url>] [--stream] [--background|-b] [--json] "your message"');
1311
+ printChatHelp();
1242
1312
  } else if (topic === 'responses' || topic === 'run') {
1243
1313
  if (subtopic === 'get') console.log('Usage: makaron responses get <runId> [--wait] [--json] [--pick <field>]');
1244
1314
  else if (subtopic === 'watch') console.log('Usage: makaron responses watch <runId> [--jsonl] [--interval <ms>]');
@@ -1259,6 +1329,10 @@ function printHelp(topic, subtopic) {
1259
1329
  `);
1260
1330
  } else if (topic === 'abort') {
1261
1331
  console.log('Usage: makaron abort <runId>');
1332
+ } else if (topic === 'setup') {
1333
+ console.log('Usage: makaron setup [--agent <agent>]');
1334
+ } else if (topic === 'install-skill') {
1335
+ console.log('Usage: makaron install-skill [--global] [--agent <agent>] [--yes]');
1262
1336
  } else if (topic === 'skills') {
1263
1337
  if (subtopic === 'list') console.log('Usage: makaron skills list [--json]');
1264
1338
  else if (subtopic === 'search') console.log('Usage: makaron skills search <query> [--json]');
@@ -1332,6 +1406,10 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1332
1406
  printHelp(command, args[1]);
1333
1407
  } else if (command === '--version' || command === '-v' || command === 'version') {
1334
1408
  console.log(getCliVersion());
1409
+ } else if (command === 'setup') {
1410
+ setupMakaron(args.slice(1));
1411
+ } else if (command === 'install-skill') {
1412
+ installAgentSkill(args.slice(1));
1335
1413
  } else if (command === 'login') {
1336
1414
  await login();
1337
1415
  } else if (command === 'create') {
@@ -1403,52 +1481,28 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1403
1481
 
1404
1482
  // --project auto: create a new project (with images/videos if provided)
1405
1483
  if (!projectId || projectId === 'auto') {
1406
- if (chatImages.length === 0) {
1407
- // Create empty project (videos will be uploaded separately after)
1408
- process.stderr.write(`📦 Creating new project...\n`);
1409
- const res = await fetch(`${baseUrl}/api/projects/create`, {
1410
- method: 'POST',
1411
- headers: { 'Content-Type': 'application/json', ...headers },
1412
- body: JSON.stringify({ title: prompt.slice(0, 50) }),
1413
- });
1414
- if (!res.ok) { process.stderr.write(`❌ Failed to create project: ${await res.text()}\n`); process.exit(1); }
1415
- const data = await res.json();
1416
- projectId = data.projectId;
1417
- process.stderr.write(`📦 Project created: ${projectId}\n`);
1418
- } else {
1419
- // Create project with images (URLs and/or local files)
1420
- const base64s = imageFileList.map(imgPath => {
1421
- process.stderr.write(`📤 Uploading ${path.basename(imgPath)}...\n`);
1422
- return readImageAsDataUrl(imgPath);
1423
- });
1424
- if (imageUrlList.length) process.stderr.write(`📤 Attaching ${imageUrlList.length} URL image(s)...\n`);
1425
- const body = {};
1426
- if (base64s.length) body.imageBase64s = base64s;
1427
- if (imageUrlList.length) body.imageUrls = imageUrlList;
1428
- const res = await fetch(`${baseUrl}/api/projects/create`, {
1429
- method: 'POST',
1430
- headers: { 'Content-Type': 'application/json', ...headers },
1431
- body: JSON.stringify(body),
1432
- });
1433
- if (!res.ok) { process.stderr.write(`❌ Failed to create project: ${await res.text()}\n`); process.exit(1); }
1434
- const data = await res.json();
1435
- projectId = data.projectId;
1436
- process.stderr.write(`📦 Project created: ${projectId} (${data.snapshots?.length || 0} images)\n`);
1437
- }
1438
- chatImages.length = 0;
1439
- imageUrlList.length = 0;
1440
- imageFileList.length = 0;
1484
+ // Create an empty project first, then attach media by URL. Local images use
1485
+ // signed upload URLs so the agent never depends on the caller's filesystem.
1486
+ process.stderr.write(`📦 Creating new project...\n`);
1487
+ const res = await fetch(`${baseUrl}/api/projects/create`, {
1488
+ method: 'POST',
1489
+ headers: { 'Content-Type': 'application/json', ...headers },
1490
+ body: JSON.stringify({ title: prompt.slice(0, 50) }),
1491
+ });
1492
+ if (!res.ok) { process.stderr.write(`❌ Failed to create project: ${await res.text()}\n`); process.exit(1); }
1493
+ const data = await res.json();
1494
+ projectId = data.projectId;
1495
+ process.stderr.write(`📦 Project created: ${projectId}\n`);
1441
1496
  }
1442
1497
  // Upload additional images to existing project
1443
1498
  if (imageFileList.length > 0 || imageUrlList.length > 0) {
1444
- const base64s = imageFileList.map(imgPath => {
1445
- process.stderr.write(`📤 Uploading ${path.basename(imgPath)}...\n`);
1446
- return readImageAsDataUrl(imgPath);
1447
- });
1499
+ const uploadedImageUrls = imageFileList.length
1500
+ ? await uploadImageFilesViaSignedUrl(baseUrl, headers, projectId, imageFileList)
1501
+ : [];
1502
+ const allImageUrls = [...uploadedImageUrls, ...imageUrlList];
1448
1503
  if (imageUrlList.length) process.stderr.write(`📤 Attaching ${imageUrlList.length} URL image(s)...\n`);
1449
1504
  const body = { _addToProject: projectId };
1450
- if (base64s.length) body.imageBase64s = base64s;
1451
- if (imageUrlList.length) body.imageUrls = imageUrlList;
1505
+ if (allImageUrls.length) body.imageUrls = allImageUrls;
1452
1506
  const res = await fetch(`${baseUrl}/api/projects/create`, {
1453
1507
  method: 'POST',
1454
1508
  headers: { 'Content-Type': 'application/json', ...headers },
@@ -1456,10 +1510,19 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1456
1510
  });
1457
1511
  if (res.ok) {
1458
1512
  const data = await res.json();
1459
- process.stderr.write(`📤 Added ${data.snapshots?.length || 0} image(s) to project\n`);
1513
+ const addedCount = data.snapshots?.length || 0;
1514
+ if (addedCount < allImageUrls.length) {
1515
+ process.stderr.write(`❌ Added only ${addedCount}/${allImageUrls.length} image(s) to project; aborting run.\n`);
1516
+ process.exit(1);
1517
+ }
1518
+ process.stderr.write(`📤 Added ${addedCount} image(s) to project\n`);
1460
1519
  } else {
1461
- process.stderr.write(`⚠️ Failed to upload images: ${await res.text()}\n`);
1520
+ process.stderr.write(`❌ Failed to add images: ${await res.text()}\n`);
1521
+ process.exit(1);
1462
1522
  }
1523
+ chatImages.length = 0;
1524
+ imageUrlList.length = 0;
1525
+ imageFileList.length = 0;
1463
1526
  }
1464
1527
 
1465
1528
  const resolvedSkill = await resolveChatSkill(baseUrl, headers, activeSkill);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "makaron-cli",
3
- "version": "0.11.0",
3
+ "version": "0.11.2",
4
4
  "description": "Talk to Makaron Agent from the terminal — create projects, edit images, generate videos",
5
5
  "type": "module",
6
6
  "scripts": {