makaron-cli 0.13.8 → 0.14.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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +33 -9
- package/bin/makaron.mjs +213 -56
- package/package.json +1 -1
- package/skills/makaron/SKILL.md +13 -8
package/README.md
CHANGED
|
@@ -170,26 +170,37 @@ npx makaron-cli project media <projectId> --json
|
|
|
170
170
|
|
|
171
171
|
This is project-scoped. `responses get <runId> --pick output` only returns artifacts from one run; `project media` returns the whole project timeline: original uploads, references, generated images, video snapshots, and editable compositions.
|
|
172
172
|
|
|
173
|
-
|
|
173
|
+
Typed external images and video ranges can be added without uploading the original media:
|
|
174
174
|
|
|
175
175
|
```bash
|
|
176
176
|
npx makaron-cli project media add <projectId> \
|
|
177
|
+
--type image \
|
|
178
|
+
--source-url "https://cdn.example.com/product.jpg" \
|
|
179
|
+
--description "Hero product image"
|
|
180
|
+
|
|
181
|
+
npx makaron-cli project media add <projectId> \
|
|
182
|
+
--type video \
|
|
177
183
|
--source-url "https://cdn.example.com/source.mp4" \
|
|
178
184
|
--start 12.5 --end 19 \
|
|
179
185
|
--description "Racket frame molding"
|
|
180
186
|
|
|
181
187
|
# Batch form: a JSON array or {"clips": [...]}
|
|
182
|
-
npx makaron-cli project media add <projectId> --input
|
|
188
|
+
npx makaron-cli project media add <projectId> --input media.json --json
|
|
183
189
|
```
|
|
184
190
|
|
|
185
|
-
|
|
191
|
+
Every item must declare `type` as `image` or `video`. Images contain
|
|
192
|
+
`source_url + type + description` and do not have a time range. Videos contain
|
|
193
|
+
`source_url + type + start + end + description`; their ranges remain
|
|
194
|
+
non-destructive metadata, and Remotion must trim the original URL to those exact
|
|
195
|
+
source bounds. The returned `<<<media_N>>>` is immediately usable by a later
|
|
196
|
+
Agent run.
|
|
186
197
|
Use `description` as the provider-neutral media-understanding field. Put any
|
|
187
198
|
already-known summary, editorial purpose, concrete scene evidence, confidence,
|
|
188
199
|
and limitations there. Makaron exposes the full description in Media List
|
|
189
200
|
context so the Agent can edit from it without repeating image/video analysis
|
|
190
201
|
unless a required detail is missing or uncertain.
|
|
191
202
|
|
|
192
|
-
For an agent-to-agent handoff, create the project, import the external
|
|
203
|
+
For an agent-to-agent handoff, create the project, import the typed external media,
|
|
193
204
|
and start the Agent in one command:
|
|
194
205
|
|
|
195
206
|
```bash
|
|
@@ -200,9 +211,9 @@ npx makaron-cli chat --project auto \
|
|
|
200
211
|
```
|
|
201
212
|
|
|
202
213
|
The manifest is a JSON array or `{ "title": "...", "clips": [...] }`. Every
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
214
|
+
item declares `type`. Images omit `start` and `end`; videos require both values
|
|
215
|
+
in seconds. Array order is edit order. It is validated before project creation
|
|
216
|
+
and supports up to 20 media items for one Makaron task. Batch planning remains
|
|
206
217
|
the upstream orchestrator's responsibility: convert each plan into one manifest
|
|
207
218
|
and start one independent Makaron task.
|
|
208
219
|
|
|
@@ -341,9 +352,12 @@ npx makaron-cli edit --image photo.jpg --ref style.jpg "match this style"
|
|
|
341
352
|
|
|
342
353
|
# Output to file
|
|
343
354
|
npx makaron-cli edit --image photo.jpg --out result.jpg "make it dramatic"
|
|
355
|
+
|
|
356
|
+
# Strict transparent PNG/WebP output through GPT Image 2 (fails rather than returning opaque)
|
|
357
|
+
npx makaron-cli edit --image-model openai --background transparent --out sticker.png "a magenta star sticker"
|
|
344
358
|
```
|
|
345
359
|
|
|
346
|
-
Options: `--image`, `--image-model gemini|gemini-lite|qwen|openai|pony|wai`, `--ref <file>` (up to 3), `--aspect <ratio>`, `--out <path
|
|
360
|
+
Options: `--image`, `--image-model gemini|gemini-lite|qwen|openai|pony|wai`, `--ref <file>` (up to 3), `--aspect <ratio>`, `--background auto|opaque|transparent`, `--out <path>`. Transparent output routes strictly to GPT Image 2 and is returned only when the provider supplies real PNG/WebP alpha.
|
|
347
361
|
|
|
348
362
|
### `video` — Standalone video tools (no project timeline)
|
|
349
363
|
|
|
@@ -377,7 +391,7 @@ For project/timeline video editing, use:
|
|
|
377
391
|
npx makaron-cli chat --project <id|auto> --video input.mp4 -b "make it funny"
|
|
378
392
|
```
|
|
379
393
|
|
|
380
|
-
Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to the selected model limit), `--video <file|url>` and `--audio <file|url>` (repeatable where supported), `--duration <seconds>`, `--aspect 9:16|16:9|1:1`, `--video-model seedance-fast|seedance-mini|seedance|seedance-2.5|kling|grok|google-omni|minimax-h3`, `--video-resolution auto|480p|720p|768p|1080p|2k|4k`. Default model is `seedance-fast`. SeeDance accepts native text-to-video with no image and integer output duration 4-15s (default 5s); `seedance-mini` supports 480p/720p and is best for cheaper drafts/multi-size tests; MiniMax H3 accepts native text-to-video, 4-15s output, public 768p/2k resolution, and up to 9 image, up to 3 video, and up to 3 audio references through Makaron Agent/chat. H3 defaults to 768p; request 2k explicitly for maximum/final quality. Kling supports 5-15s; Grok 1.5 supports 1-15s single-image-to-video only; Gemini Omni supports 3-10s fast 720p image/video generation and editing with native generated audio, including up to 6 image references when no video reference is provided. For `--video-model grok`, forced `--aspect` is ignored to avoid xAI stretching the source image; pad/create the image at the target shape first or use another model.
|
|
394
|
+
Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to the selected model limit), `--video <file|url>` and `--audio <file|url>` (repeatable where supported), `--duration <seconds>`, `--aspect 9:16|16:9|1:1`, `--video-model seedance-fast|seedance-mini|seedance|seedance-2.5|kling|grok|google-omni|minimax-h3|sync-lipsync-v3`, `--video-resolution auto|480p|720p|768p|1080p|2k|4k`. Default model is `seedance-fast`. SeeDance accepts native text-to-video with no image and integer output duration 4-15s (default 5s); `seedance-mini` supports 480p/720p and is best for cheaper drafts/multi-size tests; MiniMax H3 accepts native text-to-video, 4-15s output, public 768p/2k resolution, and up to 9 image, up to 3 video, and up to 3 audio references through Makaron Agent/chat. `sync-lipsync-v3` requires exactly one video plus one MP3/WAV and preserves that replacement audio while aligning the mouth. H3 defaults to 768p; request 2k explicitly for maximum/final quality. Kling supports 5-15s; Grok 1.5 supports 1-15s single-image-to-video only; Gemini Omni supports 3-10s fast 720p image/video generation and editing with native generated audio, including up to 6 image references when no video reference is provided. For `--video-model grok`, forced `--aspect` is ignored to avoid xAI stretching the source image; pad/create the image at the target shape first or use another model.
|
|
381
395
|
|
|
382
396
|
Seedance 2.5: use `--video-model seedance-2.5` for 4-30 second output at 480p/720p. `--image` accepts local files or URLs (up to 30), while repeatable `--video` and `--audio` accept up to 10 each. Use `--video-operation generate|edit|extend`, `--extend-direction forward|backward`, `--output-format mp4|mov`, `--web-search`, `--generated-audio` / `--no-generated-audio`, and `--relaxed-content-filter`. Edit/extend require a video reference. The Evolink route does not currently expose 4K output.
|
|
383
397
|
|
|
@@ -428,6 +442,7 @@ type CompletionAction = {
|
|
|
428
442
|
3. Stop when `status` is `"completed"`, `"failed"`, or `"aborted"`
|
|
429
443
|
4. Top-level `status: "completed"` means ALL artifacts are ready (including rendered videos)
|
|
430
444
|
5. If an async video fails, top-level `status` is `"failed"` and the failed video may include `completion_actions` for a safe retry or diagnosis. Agents can surface these as the next user-confirmed step.
|
|
445
|
+
6. `responses get --wait` reconciles pending video output against completed Project Media with the same `snapshot_id` or `task_id`. A lagging Run row therefore does not block delivery after the durable project video is ready.
|
|
431
446
|
|
|
432
447
|
## Exit Codes
|
|
433
448
|
|
|
@@ -508,6 +523,15 @@ send_message "All done!"
|
|
|
508
523
|
|
|
509
524
|
Admin commands require an API key with admin privileges. Ask your admin to run `makaron admin set-admin <your-email>` to grant access.
|
|
510
525
|
|
|
526
|
+
### Add credits to a user
|
|
527
|
+
|
|
528
|
+
Use either the account email address or the Supabase user UUID. Credits must be a positive integer.
|
|
529
|
+
|
|
530
|
+
```bash
|
|
531
|
+
npx makaron-cli admin add-credits user@example.com 1000
|
|
532
|
+
npx makaron-cli admin add-credits <user-id> 1000 --json
|
|
533
|
+
```
|
|
534
|
+
|
|
511
535
|
### List all marketplace skills
|
|
512
536
|
|
|
513
537
|
```bash
|
package/bin/makaron.mjs
CHANGED
|
@@ -174,25 +174,19 @@ function readJsonInput(filePath) {
|
|
|
174
174
|
return JSON.parse(raw);
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
const
|
|
177
|
+
const MAX_MEDIA_MANIFEST_ITEMS = 20;
|
|
178
178
|
|
|
179
179
|
function normalizeMediaManifest(input) {
|
|
180
180
|
const manifest = Array.isArray(input) ? { clips: input } : input;
|
|
181
181
|
if (!manifest || typeof manifest !== 'object') {
|
|
182
|
-
throw new Error('Media manifest must be a JSON object or an array of
|
|
183
|
-
}
|
|
184
|
-
const rawRanges = Array.isArray(manifest.clips)
|
|
185
|
-
? manifest.clips
|
|
186
|
-
: Array.isArray(manifest.source_ranges)
|
|
187
|
-
? manifest.source_ranges
|
|
188
|
-
: Array.isArray(manifest.sourceRanges)
|
|
189
|
-
? manifest.sourceRanges
|
|
190
|
-
: null;
|
|
182
|
+
throw new Error('Media manifest must be a JSON object or an array of media items.');
|
|
183
|
+
}
|
|
184
|
+
const rawRanges = Array.isArray(manifest.clips) ? manifest.clips : null;
|
|
191
185
|
if (!rawRanges?.length) {
|
|
192
186
|
throw new Error('Media manifest must contain a non-empty clips array.');
|
|
193
187
|
}
|
|
194
|
-
if (rawRanges.length >
|
|
195
|
-
throw new Error(`Media manifest supports at most ${
|
|
188
|
+
if (rawRanges.length > MAX_MEDIA_MANIFEST_ITEMS) {
|
|
189
|
+
throw new Error(`Media manifest supports at most ${MAX_MEDIA_MANIFEST_ITEMS} media items per Makaron task.`);
|
|
196
190
|
}
|
|
197
191
|
|
|
198
192
|
const sourceRanges = rawRanges.map((raw, index) => {
|
|
@@ -207,8 +201,19 @@ function normalizeMediaManifest(input) {
|
|
|
207
201
|
if (!['http:', 'https:'].includes(parsed.protocol)) {
|
|
208
202
|
throw new Error(`clips[${index}].source_url must use HTTP or HTTPS.`);
|
|
209
203
|
}
|
|
210
|
-
const
|
|
211
|
-
|
|
204
|
+
const declaredType = raw.type;
|
|
205
|
+
if (declaredType !== 'image' && declaredType !== 'video') {
|
|
206
|
+
throw new Error(`clips[${index}].type must be image or video.`);
|
|
207
|
+
}
|
|
208
|
+
const description = typeof raw.description === 'string' ? raw.description.trim() : '';
|
|
209
|
+
if (declaredType === 'image') {
|
|
210
|
+
if (raw.start !== undefined || raw.end !== undefined || raw.start_sec !== undefined || raw.end_sec !== undefined) {
|
|
211
|
+
throw new Error(`clips[${index}] image items must not include start or end.`);
|
|
212
|
+
}
|
|
213
|
+
return { source_url: sourceUrl, type: 'image', description };
|
|
214
|
+
}
|
|
215
|
+
const start = Number(raw.start);
|
|
216
|
+
const end = Number(raw.end);
|
|
212
217
|
if (!Number.isFinite(start) || start < 0) {
|
|
213
218
|
throw new Error(`clips[${index}].start must be a finite number >= 0.`);
|
|
214
219
|
}
|
|
@@ -217,9 +222,10 @@ function normalizeMediaManifest(input) {
|
|
|
217
222
|
}
|
|
218
223
|
return {
|
|
219
224
|
source_url: sourceUrl,
|
|
225
|
+
type: declaredType,
|
|
220
226
|
start,
|
|
221
227
|
end,
|
|
222
|
-
description
|
|
228
|
+
description,
|
|
223
229
|
};
|
|
224
230
|
});
|
|
225
231
|
|
|
@@ -328,6 +334,86 @@ function normalizeRunResponse(data) {
|
|
|
328
334
|
return data;
|
|
329
335
|
}
|
|
330
336
|
|
|
337
|
+
function projectMediaIdentity(item) {
|
|
338
|
+
const snapshotId = item?.snapshot_id || item?.snapshotId;
|
|
339
|
+
const taskId = item?.task_id || item?.taskId;
|
|
340
|
+
return {
|
|
341
|
+
snapshotId: typeof snapshotId === 'string' && snapshotId ? snapshotId : null,
|
|
342
|
+
taskId: typeof taskId === 'string' && taskId ? taskId : null,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function findMatchingCompletedProjectVideo(item, media) {
|
|
347
|
+
const identity = projectMediaIdentity(item);
|
|
348
|
+
if (!identity.snapshotId && !identity.taskId) return null;
|
|
349
|
+
return (media || []).find(candidate => {
|
|
350
|
+
if (candidate?.type !== 'video' || candidate?.status !== 'completed' || !candidate?.url) return false;
|
|
351
|
+
const candidateIdentity = projectMediaIdentity(candidate);
|
|
352
|
+
return (identity.snapshotId && candidateIdentity.snapshotId === identity.snapshotId)
|
|
353
|
+
|| (identity.taskId && candidateIdentity.taskId === identity.taskId);
|
|
354
|
+
}) || null;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async function reconcileRunWithProjectMedia(baseUrl, headers, data) {
|
|
358
|
+
normalizeRunResponse(data);
|
|
359
|
+
const output = Array.isArray(data.output) ? data.output : [];
|
|
360
|
+
const pendingVideos = output.filter(item =>
|
|
361
|
+
item?.type === 'video' && (item.status === 'queued' || item.status === 'rendering')
|
|
362
|
+
);
|
|
363
|
+
const agentDone = data.agent_status === 'completed'
|
|
364
|
+
|| (data.status === 'in_progress' && data.incomplete === true);
|
|
365
|
+
if (!agentDone || pendingVideos.length === 0 || !data.projectId) return data;
|
|
366
|
+
|
|
367
|
+
let projectMedia;
|
|
368
|
+
try {
|
|
369
|
+
const res = await fetch(`${baseUrl}/api/projects/${data.projectId}/media`, { headers });
|
|
370
|
+
if (!res.ok) return data;
|
|
371
|
+
projectMedia = await res.json();
|
|
372
|
+
} catch {
|
|
373
|
+
return data;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
let reconciled = false;
|
|
377
|
+
for (const item of pendingVideos) {
|
|
378
|
+
const completed = findMatchingCompletedProjectVideo(item, projectMedia.media);
|
|
379
|
+
if (!completed) continue;
|
|
380
|
+
const identity = projectMediaIdentity(completed);
|
|
381
|
+
item.status = 'completed';
|
|
382
|
+
item.url = completed.url;
|
|
383
|
+
if (identity.snapshotId) item.snapshot_id = identity.snapshotId;
|
|
384
|
+
if (identity.taskId) item.task_id = identity.taskId;
|
|
385
|
+
if (completed.posterUrl) item.poster_url = completed.posterUrl;
|
|
386
|
+
for (const field of ['duration', 'width', 'height']) {
|
|
387
|
+
if (typeof completed[field] === 'number') item[field] = completed[field];
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const legacyVideos = Array.isArray(data.result?.videos) ? data.result.videos : [];
|
|
391
|
+
const legacy = legacyVideos.find(video => {
|
|
392
|
+
const legacyIdentity = projectMediaIdentity(video);
|
|
393
|
+
return (identity.snapshotId && legacyIdentity.snapshotId === identity.snapshotId)
|
|
394
|
+
|| (identity.taskId && legacyIdentity.taskId === identity.taskId);
|
|
395
|
+
});
|
|
396
|
+
if (legacy) {
|
|
397
|
+
legacy.status = 'completed';
|
|
398
|
+
legacy.videoUrl = completed.url;
|
|
399
|
+
if (identity.snapshotId && !legacy.snapshotId) legacy.snapshotId = identity.snapshotId;
|
|
400
|
+
}
|
|
401
|
+
reconciled = true;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (!reconciled) return data;
|
|
405
|
+
const pendingArtifacts = output.some(item =>
|
|
406
|
+
(item?.type === 'video' || item?.type === 'music')
|
|
407
|
+
&& (item.status === 'queued' || item.status === 'rendering')
|
|
408
|
+
);
|
|
409
|
+
if (!pendingArtifacts) {
|
|
410
|
+
data.status = 'completed';
|
|
411
|
+
data.incomplete = false;
|
|
412
|
+
delete data.next_poll_after_ms;
|
|
413
|
+
}
|
|
414
|
+
return data;
|
|
415
|
+
}
|
|
416
|
+
|
|
331
417
|
function collectCompletionActions(data) {
|
|
332
418
|
const items = [];
|
|
333
419
|
const add = (action, source) => {
|
|
@@ -367,7 +453,7 @@ Options:
|
|
|
367
453
|
--image <file|url> Attach a reference image or screenshot. Repeatable.
|
|
368
454
|
--video <file|url> Attach a video to the project timeline. Repeatable.
|
|
369
455
|
--audio <file|url> Attach a song, beat, or voice reference. MP3/WAV, repeatable.
|
|
370
|
-
--media-manifest <file|-> Import
|
|
456
|
+
--media-manifest <file|-> Import typed image/video media before this run.
|
|
371
457
|
--skill <id|label|name> Use an installed skill or auto-install a matched marketplace skill.
|
|
372
458
|
--agent-model <id> Agent LLM only: auto, gpt-5.6-terra, gpt-5.6-sol,
|
|
373
459
|
gpt-5.6-luna, grok-4.5, or deepseek-v4-pro.
|
|
@@ -612,6 +698,7 @@ async function pollRun(baseUrl, headers, runId, opts = {}) {
|
|
|
612
698
|
continue;
|
|
613
699
|
}
|
|
614
700
|
data = await res.json();
|
|
701
|
+
data = await reconcileRunWithProjectMedia(baseUrl, headers, data);
|
|
615
702
|
} catch {
|
|
616
703
|
continue;
|
|
617
704
|
}
|
|
@@ -764,6 +851,7 @@ async function watchRun(baseUrl, headers, runId, opts = {}) {
|
|
|
764
851
|
continue;
|
|
765
852
|
}
|
|
766
853
|
data = await res.json();
|
|
854
|
+
data = await reconcileRunWithProjectMedia(baseUrl, headers, data);
|
|
767
855
|
} catch {
|
|
768
856
|
await new Promise(r => setTimeout(r, interval));
|
|
769
857
|
continue;
|
|
@@ -949,7 +1037,10 @@ async function addProjectMediaSourceRanges(baseUrl, headers, projectId, ranges,
|
|
|
949
1037
|
}
|
|
950
1038
|
if (!opts.silent) {
|
|
951
1039
|
for (const item of data.media || []) {
|
|
952
|
-
|
|
1040
|
+
const range = item.type === 'video' && Number.isFinite(item.start_sec) && Number.isFinite(item.end_sec)
|
|
1041
|
+
? ` ${formatSeconds(item.start_sec)}-${formatSeconds(item.end_sec)}s`
|
|
1042
|
+
: '';
|
|
1043
|
+
console.log(`${item.ref} [${item.type || 'media'}] ${item.source_url}${range}${item.created ? '' : ' (existing)'}`);
|
|
953
1044
|
}
|
|
954
1045
|
}
|
|
955
1046
|
return data;
|
|
@@ -1705,7 +1796,22 @@ function saveMcpImage(result, outputPath) {
|
|
|
1705
1796
|
const imageBlock = content.find(c => c.type === 'image');
|
|
1706
1797
|
if (textBlock) process.stderr.write(`${textBlock.text}\n`);
|
|
1707
1798
|
if (imageBlock) {
|
|
1708
|
-
const
|
|
1799
|
+
const extension = imageBlock.mimeType === 'image/png'
|
|
1800
|
+
? 'png'
|
|
1801
|
+
: imageBlock.mimeType === 'image/webp'
|
|
1802
|
+
? 'webp'
|
|
1803
|
+
: 'jpg';
|
|
1804
|
+
let out = outputPath || `makaron-output-${Date.now()}.${extension}`;
|
|
1805
|
+
if (outputPath) {
|
|
1806
|
+
const requestedExtension = path.extname(outputPath).slice(1).toLowerCase();
|
|
1807
|
+
const compatible = extension === 'jpg'
|
|
1808
|
+
? ['jpg', 'jpeg'].includes(requestedExtension)
|
|
1809
|
+
: requestedExtension === extension;
|
|
1810
|
+
if (!compatible) {
|
|
1811
|
+
out = `${outputPath.slice(0, outputPath.length - path.extname(outputPath).length)}.${extension}`;
|
|
1812
|
+
process.stderr.write(`Output is ${imageBlock.mimeType}; saving as ${out} so bytes and filename agree.\n`);
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1709
1815
|
fs.writeFileSync(out, Buffer.from(imageBlock.data, 'base64'));
|
|
1710
1816
|
console.log(out);
|
|
1711
1817
|
return out;
|
|
@@ -1756,8 +1862,10 @@ Commands:
|
|
|
1756
1862
|
credits Show current credit balance
|
|
1757
1863
|
list (ls) List all projects
|
|
1758
1864
|
project media <projectId> --json List timeline media for a project
|
|
1759
|
-
project media add <projectId> --source-url <url>
|
|
1760
|
-
Add an external
|
|
1865
|
+
project media add <projectId> --type image --source-url <url>
|
|
1866
|
+
Add an external image without uploading it
|
|
1867
|
+
project media add <projectId> --type video --source-url <url> --start <n> --end <n>
|
|
1868
|
+
Add an external video range without uploading it
|
|
1761
1869
|
create --image <file> Create project from local image
|
|
1762
1870
|
create --image-url <url> Create project from URL
|
|
1763
1871
|
create --title "name" Create empty project (text-to-image)
|
|
@@ -1769,7 +1877,7 @@ Commands:
|
|
|
1769
1877
|
chat --project <id> --video <file> Attach video to conversation
|
|
1770
1878
|
chat --project <id> --audio <file> Attach song/beat/voice reference
|
|
1771
1879
|
chat --project auto --media-manifest <file> "message"
|
|
1772
|
-
Create, import external
|
|
1880
|
+
Create, import typed external media, and run Agent
|
|
1773
1881
|
chat --project <id> -b "message" Background: submit and print runId
|
|
1774
1882
|
chat --project <id> --stream "msg" Legacy: stream SSE in real-time
|
|
1775
1883
|
chat --project <id> --json "msg" Output structured JSON result
|
|
@@ -1791,7 +1899,7 @@ Commands:
|
|
|
1791
1899
|
video script|create|status Video generation
|
|
1792
1900
|
music create|status Music generation
|
|
1793
1901
|
|
|
1794
|
-
admin Admin commands (skills, upload, set-admin)
|
|
1902
|
+
admin Admin commands (skills, credits, upload, set-admin)
|
|
1795
1903
|
|
|
1796
1904
|
Examples:
|
|
1797
1905
|
makaron chat --project auto "plan a launch poster"
|
|
@@ -1879,11 +1987,12 @@ function printHelp(topic, subtopic) {
|
|
|
1879
1987
|
console.log('Usage: makaron credits [--json]');
|
|
1880
1988
|
} else if (topic === 'project' || topic === 'projects') {
|
|
1881
1989
|
if (subtopic === 'media') console.log(`Usage: makaron project media <projectId> [--json]
|
|
1882
|
-
makaron project media add <projectId> --source-url <url>
|
|
1883
|
-
makaron project media add <projectId> --
|
|
1990
|
+
makaron project media add <projectId> --type image --source-url <url> [--description <text>] [--json]
|
|
1991
|
+
makaron project media add <projectId> --type video --source-url <url> --start <n> --end <n> [--description <text>] [--json]
|
|
1992
|
+
makaron project media add <projectId> --input <media.json> [--json]`);
|
|
1884
1993
|
else console.log(`Project commands:
|
|
1885
1994
|
project media <projectId> --json List timeline media for a project
|
|
1886
|
-
project media add <projectId> ... Add external
|
|
1995
|
+
project media add <projectId> ... Add typed external image/video media
|
|
1887
1996
|
`);
|
|
1888
1997
|
} else if (topic === 'abort') {
|
|
1889
1998
|
console.log('Usage: makaron abort <runId>');
|
|
@@ -1923,12 +2032,12 @@ Not sure which built-in skill to use? Start with:
|
|
|
1923
2032
|
composition status <jobId> [--wait] [--json]
|
|
1924
2033
|
`);
|
|
1925
2034
|
} else if (topic === 'edit') {
|
|
1926
|
-
console.log('Usage: makaron edit [--image <file|url>] [--image-model gemini|gemini-lite|qwen|openai|pony|wai] [--skill enhance|creative|wild|captions] [--ref <file>] [--out <file>] "prompt"');
|
|
2035
|
+
console.log('Usage: makaron edit [--image <file|url>] [--image-model gemini|gemini-lite|qwen|openai|pony|wai] [--skill enhance|creative|wild|captions] [--ref <file>] [--aspect <ratio>] [--background auto|opaque|transparent] [--out <file>] "prompt"');
|
|
1927
2036
|
} else if (topic === 'analyze') {
|
|
1928
2037
|
console.log('Usage: makaron analyze --video <file|url> ["question"]');
|
|
1929
2038
|
} else if (topic === 'video') {
|
|
1930
2039
|
if (subtopic === 'script') console.log('Usage: makaron video script --image <file> [--image <file>] [--lang en|zh] "direction"');
|
|
1931
|
-
else if (subtopic === 'create') console.log('Usage: makaron video create --script "..." [--image <url> ...] [--video <url> ...] [--audio <url> ...] [--duration 10] [--aspect 9:16] [--video-model seedance-fast|seedance-mini|seedance|seedance-2.5|kling|grok|google-omni|minimax-h3] [--operation generate|edit|extend] [--video-resolution auto|480p|720p|768p|1080p|2k|4k] [--keep-original-sound]');
|
|
2040
|
+
else if (subtopic === 'create') console.log('Usage: makaron video create --script "..." [--image <url> ...] [--video <url> ...] [--audio <url> ...] [--duration 10] [--aspect 9:16] [--video-model seedance-fast|seedance-mini|seedance|seedance-2.5|kling|grok|google-omni|minimax-h3|sync-lipsync-v3] [--operation generate|edit|extend] [--video-resolution auto|480p|720p|768p|1080p|2k|4k] [--keep-original-sound]');
|
|
1932
2041
|
else if (subtopic === 'status') console.log('Usage: makaron video status <taskId> | --snapshot <snapshotId> [--wait]');
|
|
1933
2042
|
else console.log(`Video commands:
|
|
1934
2043
|
video script --image <file> [--image <file>] "direction" Write video script
|
|
@@ -1936,6 +2045,7 @@ Not sure which built-in skill to use? Start with:
|
|
|
1936
2045
|
video create --script "..." --video-model minimax-h3 MiniMax H3 text-to-video (default 768P)
|
|
1937
2046
|
video create --script "..." --image <url> [--duration 10] Submit video task
|
|
1938
2047
|
video create --script "..." --video <public-url> [--video-model seedance-fast|seedance-mini|seedance|seedance-2.5|kling|google-omni|minimax-h3] Edit/reference a video (Grok does not support video refs)
|
|
2048
|
+
video create --script "Use the supplied audio" --video <url> --audio <url> --video-model sync-lipsync-v3 Lip-sync exact replacement audio
|
|
1939
2049
|
video status <taskId> Check video status
|
|
1940
2050
|
video status --snapshot <snapshotId> [--wait] Check v2 video snapshot
|
|
1941
2051
|
`);
|
|
@@ -1951,6 +2061,7 @@ Not sure which built-in skill to use? Start with:
|
|
|
1951
2061
|
else if (subtopic === 'skill-categories') console.log('Usage: makaron admin skill-categories [--json|add|update|delete] ...');
|
|
1952
2062
|
else if (subtopic === 'upload') console.log('Usage: makaron admin upload <local-file> <storage-path>');
|
|
1953
2063
|
else if (subtopic === 'fetch-skill') console.log('Usage: makaron admin fetch-skill <share-code|url>');
|
|
2064
|
+
else if (subtopic === 'add-credits') console.log('Usage: makaron admin add-credits <email-or-user-id> <credits> [--json]');
|
|
1954
2065
|
else if (subtopic === 'set-admin') console.log('Usage: makaron admin set-admin <email>');
|
|
1955
2066
|
else console.log(`Admin commands:
|
|
1956
2067
|
admin skills List all marketplace skills
|
|
@@ -1963,6 +2074,7 @@ Not sure which built-in skill to use? Start with:
|
|
|
1963
2074
|
admin skill-categories delete <id> Delete a category
|
|
1964
2075
|
admin upload <file> <storage-path> Upload file to Storage
|
|
1965
2076
|
admin fetch-skill <code|url> Download skill from share link
|
|
2077
|
+
admin add-credits <user> <credits> Add credits by email or user ID
|
|
1966
2078
|
admin set-admin <email> Grant admin access to a user
|
|
1967
2079
|
`);
|
|
1968
2080
|
} else if (topic === 'register') {
|
|
@@ -2184,12 +2296,12 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2184
2296
|
);
|
|
2185
2297
|
importedManifestMedia = imported.media || [];
|
|
2186
2298
|
if (importedManifestMedia.length !== mediaManifest.sourceRanges.length) {
|
|
2187
|
-
process.stderr.write(`❌ Imported ${importedManifestMedia.length}/${mediaManifest.sourceRanges.length}
|
|
2299
|
+
process.stderr.write(`❌ Imported ${importedManifestMedia.length}/${mediaManifest.sourceRanges.length} media item(s); aborting run.\n`);
|
|
2188
2300
|
process.exit(1);
|
|
2189
2301
|
}
|
|
2190
2302
|
uploadedTurnMediaCount += importedManifestMedia.length;
|
|
2191
|
-
uploadedTurnVideoCount += importedManifestMedia.length;
|
|
2192
|
-
process.stderr.write(`📎 Imported ${importedManifestMedia.length} external
|
|
2303
|
+
uploadedTurnVideoCount += importedManifestMedia.filter(item => item.type === 'video').length;
|
|
2304
|
+
process.stderr.write(`📎 Imported ${importedManifestMedia.length} typed external media item(s) from media manifest\n`);
|
|
2193
2305
|
}
|
|
2194
2306
|
// Upload additional images to existing project
|
|
2195
2307
|
if (imageFileList.length > 0 || imageUrlList.length > 0) {
|
|
@@ -2347,9 +2459,12 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2347
2459
|
...(importedManifestMedia.length ? {
|
|
2348
2460
|
importedMedia: importedManifestMedia.map(item => ({
|
|
2349
2461
|
ref: item.ref,
|
|
2462
|
+
type: item.type,
|
|
2350
2463
|
source_url: item.source_url,
|
|
2351
|
-
|
|
2352
|
-
|
|
2464
|
+
...(item.type === 'video' ? {
|
|
2465
|
+
start_sec: item.start_sec,
|
|
2466
|
+
end_sec: item.end_sec,
|
|
2467
|
+
} : {}),
|
|
2353
2468
|
})),
|
|
2354
2469
|
} : {}),
|
|
2355
2470
|
}));
|
|
@@ -2398,7 +2513,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2398
2513
|
const res = await fetch(`${baseUrl}/api/agent/run/${runId}`, { headers });
|
|
2399
2514
|
if (!res.ok) { process.stderr.write(`Error ${res.status}: ${await res.text()}\n`); process.exit(1); }
|
|
2400
2515
|
let data = await res.json();
|
|
2401
|
-
|
|
2516
|
+
data = await reconcileRunWithProjectMedia(baseUrl, headers, data);
|
|
2402
2517
|
if (exportCompositions && data.status === 'completed') {
|
|
2403
2518
|
data = await exportAnimatedCompositionsFromRun(baseUrl, headers, data, { publish: publishExports, quiet: jsonOutput || !!pick });
|
|
2404
2519
|
}
|
|
@@ -2614,7 +2729,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2614
2729
|
const jsonOutput = args.includes('--json');
|
|
2615
2730
|
if (args[2] === 'add') {
|
|
2616
2731
|
const projectId = args[3];
|
|
2617
|
-
if (!projectId) { console.error('Usage: makaron project media add <projectId> --source-url <url> --start <n> --end <n>'); process.exit(1); }
|
|
2732
|
+
if (!projectId) { console.error('Usage: makaron project media add <projectId> --type <image|video> --source-url <url> [--start <n> --end <n>]'); process.exit(1); }
|
|
2618
2733
|
const readOption = (name) => {
|
|
2619
2734
|
const index = args.indexOf(name);
|
|
2620
2735
|
return index >= 0 ? args[index + 1] : undefined;
|
|
@@ -2622,26 +2737,25 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2622
2737
|
const inputPath = readOption('--input');
|
|
2623
2738
|
let ranges;
|
|
2624
2739
|
if (inputPath) {
|
|
2625
|
-
const
|
|
2626
|
-
ranges =
|
|
2740
|
+
const normalized = normalizeMediaManifest(readJsonInput(inputPath));
|
|
2741
|
+
ranges = normalized.sourceRanges;
|
|
2627
2742
|
} else {
|
|
2628
2743
|
const sourceUrl = readOption('--source-url');
|
|
2629
|
-
const
|
|
2630
|
-
const
|
|
2631
|
-
|
|
2632
|
-
|
|
2744
|
+
const type = readOption('--type');
|
|
2745
|
+
const start = readOption('--start');
|
|
2746
|
+
const end = readOption('--end');
|
|
2747
|
+
if (!sourceUrl || (type !== 'image' && type !== 'video')) {
|
|
2748
|
+
console.error('Provide --type <image|video> and --source-url <url>, or --input <manifest.json>.');
|
|
2633
2749
|
process.exit(1);
|
|
2634
2750
|
}
|
|
2635
|
-
|
|
2751
|
+
const normalized = normalizeMediaManifest([{
|
|
2636
2752
|
source_url: sourceUrl,
|
|
2637
|
-
|
|
2638
|
-
|
|
2753
|
+
type,
|
|
2754
|
+
...(start !== undefined ? { start } : {}),
|
|
2755
|
+
...(end !== undefined ? { end } : {}),
|
|
2639
2756
|
...(readOption('--description') ? { description: readOption('--description') } : {}),
|
|
2640
|
-
}];
|
|
2641
|
-
|
|
2642
|
-
if (!Array.isArray(ranges) || !ranges.length) {
|
|
2643
|
-
console.error('Input must contain a non-empty clips array.');
|
|
2644
|
-
process.exit(1);
|
|
2757
|
+
}]);
|
|
2758
|
+
ranges = normalized.sourceRanges;
|
|
2645
2759
|
}
|
|
2646
2760
|
await addProjectMediaSourceRanges(baseUrl, headers, projectId, ranges, { json: jsonOutput });
|
|
2647
2761
|
} else {
|
|
@@ -2652,7 +2766,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2652
2766
|
} else {
|
|
2653
2767
|
console.log(`Project commands:
|
|
2654
2768
|
project media <projectId> --json List timeline media for a project
|
|
2655
|
-
project media add <projectId> ... Add external
|
|
2769
|
+
project media add <projectId> ... Add typed external image/video media
|
|
2656
2770
|
`);
|
|
2657
2771
|
}
|
|
2658
2772
|
} else if (command === 'abort') {
|
|
@@ -2690,11 +2804,19 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2690
2804
|
editArgs.referenceImages.push(imageToArg(args[++i]));
|
|
2691
2805
|
}
|
|
2692
2806
|
else if (args[i] === '--aspect' && args[i + 1]) editArgs.aspectRatio = args[++i];
|
|
2807
|
+
else if (args[i] === '--background' && args[i + 1]) {
|
|
2808
|
+
const background = args[++i];
|
|
2809
|
+
if (!['auto', 'opaque', 'transparent'].includes(background)) {
|
|
2810
|
+
console.error('Invalid --background. Use auto, opaque, or transparent.');
|
|
2811
|
+
process.exit(1);
|
|
2812
|
+
}
|
|
2813
|
+
editArgs.background = background;
|
|
2814
|
+
}
|
|
2693
2815
|
else if (args[i] === '--out' && args[i + 1]) outputPath = args[++i];
|
|
2694
2816
|
else promptParts.push(args[i]);
|
|
2695
2817
|
}
|
|
2696
2818
|
editArgs.editPrompt = promptParts.join(' ');
|
|
2697
|
-
if (!editArgs.editPrompt) { console.error('Usage: makaron edit [--image <file|url>] [--image-model gemini|gemini-lite|qwen|openai|pony|wai] [--ref <file>] [--out <file>] "prompt"'); process.exit(1); }
|
|
2819
|
+
if (!editArgs.editPrompt) { console.error('Usage: makaron edit [--image <file|url>] [--image-model gemini|gemini-lite|qwen|openai|pony|wai] [--ref <file>] [--aspect <ratio>] [--background auto|opaque|transparent] [--out <file>] "prompt"'); process.exit(1); }
|
|
2698
2820
|
process.stderr.write('🎨 Generating...\n');
|
|
2699
2821
|
const result = await callMcpTool(baseUrl, headers, 'makaron_edit_image', editArgs);
|
|
2700
2822
|
saveMcpImage(result, outputPath);
|
|
@@ -2768,6 +2890,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2768
2890
|
const isSeedance25 = selectedVideoModel === 'seedance-2.5';
|
|
2769
2891
|
const isSeedanceModel = selectedVideoModel === 'seedance-fast' || selectedVideoModel === 'seedance-mini' || selectedVideoModel === 'seedance' || isSeedance25;
|
|
2770
2892
|
const isMinimaxH3 = selectedVideoModel === 'minimax-h3';
|
|
2893
|
+
const isSyncLipsync = selectedVideoModel === 'sync-lipsync-v3';
|
|
2771
2894
|
const supportsNativeTextToVideo = isSeedanceModel || isMinimaxH3;
|
|
2772
2895
|
if (!script || (!images.length && !videos.length && !audios.length && !supportsNativeTextToVideo)) {
|
|
2773
2896
|
console.error('Usage: makaron video create --script "..." [--image <url>] [--video <file|url>] [--audio <file|url>] [--duration 30] [--video-model seedance-2.5|minimax-h3]');
|
|
@@ -2779,6 +2902,10 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2779
2902
|
if (isMinimaxH3 && images.length > 9) { console.error('MiniMax H3 supports at most 9 image references.'); process.exit(1); }
|
|
2780
2903
|
if (isMinimaxH3 && videos.length > 3) { console.error('MiniMax H3 supports at most 3 video references.'); process.exit(1); }
|
|
2781
2904
|
if (isMinimaxH3 && audios.length > 3) { console.error('MiniMax H3 supports at most 3 audio references.'); process.exit(1); }
|
|
2905
|
+
if (isSyncLipsync && (images.length !== 0 || videos.length !== 1 || audios.length !== 1)) { console.error('Sync Lipsync v3 requires exactly one --video and one --audio, with no --image.'); process.exit(1); }
|
|
2906
|
+
if (isSyncLipsync && !/<<<audio_1>>>/i.test(script)) {
|
|
2907
|
+
script += '\nUse <<<audio_1>>> as the exact replacement soundtrack.';
|
|
2908
|
+
}
|
|
2782
2909
|
if (videoOperation && !['generate', 'edit', 'extend'].includes(videoOperation)) { console.error('--video-operation must be generate, edit, or extend.'); process.exit(1); }
|
|
2783
2910
|
if (extendDirection && !['forward', 'backward'].includes(extendDirection)) { console.error('--extend-direction must be forward or backward.'); process.exit(1); }
|
|
2784
2911
|
if (outputFormat && !['mp4', 'mov'].includes(outputFormat)) { console.error('--output-format must be mp4 or mov.'); process.exit(1); }
|
|
@@ -2788,8 +2915,9 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2788
2915
|
process.exit(1);
|
|
2789
2916
|
}
|
|
2790
2917
|
|
|
2791
|
-
|
|
2792
|
-
|
|
2918
|
+
let providerMaxDuration = isSeedance25 ? SEEDANCE25_MAX_VIDEO_REFERENCE_DURATION : MAX_VIDEO_PROVIDER_REFERENCE_DURATION;
|
|
2919
|
+
if (isSyncLipsync) providerMaxDuration = 60;
|
|
2920
|
+
const providerMaxPixels = isSyncLipsync || isMinimaxH3 ? Infinity : isSeedance25 ? SEEDANCE25_MAX_VIDEO_FRAME_PIXELS : MAX_VIDEO_FRAME_PIXELS;
|
|
2793
2921
|
const localImages = images.filter(image => !isHttpUrl(image));
|
|
2794
2922
|
if (localImages.length) {
|
|
2795
2923
|
const uploadedImages = await uploadImageFilesViaSignedUrl(baseUrl, headers, undefined, localImages);
|
|
@@ -2804,7 +2932,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2804
2932
|
maxDuration: providerMaxDuration,
|
|
2805
2933
|
durationTolerance: MAX_VIDEO_PROVIDER_REFERENCE_DURATION_TOLERANCE,
|
|
2806
2934
|
maxFramePixels: providerMaxPixels,
|
|
2807
|
-
maxFileSize: isSeedance25 ? 200 * 1024 * 1024 : MAX_VIDEO_UPLOAD_FILE_SIZE,
|
|
2935
|
+
maxFileSize: isSeedance25 || isSyncLipsync ? 200 * 1024 * 1024 : MAX_VIDEO_UPLOAD_FILE_SIZE,
|
|
2808
2936
|
...(selectedVideoModel === 'minimax-h3' ? {
|
|
2809
2937
|
allowedExtensions: ['mp4', 'mov'],
|
|
2810
2938
|
minSide: MINIMAX_H3_MIN_VIDEO_SIDE,
|
|
@@ -2830,7 +2958,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2830
2958
|
const audioUrls = [];
|
|
2831
2959
|
for (const audio of audios) {
|
|
2832
2960
|
if (isHttpUrl(audio)) { audioUrls.push(audio); continue; }
|
|
2833
|
-
const valid = validateAudioReferenceFile(audio, { maxDuration: isSeedance25 ? 30 : MAX_AUDIO_REFERENCE_DURATION });
|
|
2961
|
+
const valid = validateAudioReferenceFile(audio, { maxDuration: isSyncLipsync ? 60 : isSeedance25 ? 30 : MAX_AUDIO_REFERENCE_DURATION });
|
|
2834
2962
|
if (!valid.ok) { console.error(`❌ ${valid.error}`); process.exit(1); }
|
|
2835
2963
|
process.stderr.write(`🎵 Uploading ${path.basename(audio)}...\n`);
|
|
2836
2964
|
const uploaded = await uploadFileViaSignedUrl(baseUrl, headers, undefined, audio, valid.mime, { uploadKind: 'audio' });
|
|
@@ -2840,7 +2968,9 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2840
2968
|
// Standalone MCP tool (no project timeline write)
|
|
2841
2969
|
process.stderr.write('🎬 Submitting video...\n');
|
|
2842
2970
|
const resolvedOperation = videoOperation || (isSeedance25 && videoUrls.length ? 'edit' : 'generate');
|
|
2843
|
-
const vArgs =
|
|
2971
|
+
const vArgs = isSyncLipsync
|
|
2972
|
+
? { script, images, videoUrls, audioUrls, videoModel: selectedVideoModel, videoResolution }
|
|
2973
|
+
: isSeedance25
|
|
2844
2974
|
? { script, images, videoUrls, audioUrls, videoModel: selectedVideoModel, videoResolution, operation: resolvedOperation, extendDirection, outputFormat, generateAudio, contentFilter, webSearch }
|
|
2845
2975
|
: isMinimaxH3
|
|
2846
2976
|
? { script, images, videoUrls, audioUrls, videoModel: selectedVideoModel, videoResolution }
|
|
@@ -2851,7 +2981,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2851
2981
|
if (effectiveDuration) vArgs.duration = effectiveDuration;
|
|
2852
2982
|
if (aspectRatio) vArgs.aspectRatio = aspectRatio;
|
|
2853
2983
|
if (keepOriginalSound && videoUrls.length && !isSeedance25) vArgs.keepOriginalSound = true;
|
|
2854
|
-
const result = await callMcpTool(baseUrl, headers, videoUrls.length && !isSeedance25 && !isMinimaxH3 ? 'makaron_edit_video' : 'makaron_create_video', vArgs);
|
|
2984
|
+
const result = await callMcpTool(baseUrl, headers, videoUrls.length && !isSeedance25 && !isMinimaxH3 && !isSyncLipsync ? 'makaron_edit_video' : 'makaron_create_video', vArgs);
|
|
2855
2985
|
const text = result?.content?.find(c => c.type === 'text')?.text;
|
|
2856
2986
|
if (text) {
|
|
2857
2987
|
console.log(text);
|
|
@@ -2898,6 +3028,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2898
3028
|
video create --script "..." --video-model minimax-h3 MiniMax H3 text-to-video (default 768P)
|
|
2899
3029
|
video create --script "..." --image <url> [--duration 10] Submit video task
|
|
2900
3030
|
video create --script "..." --video <public-url> [--video-model seedance-fast|seedance-mini|seedance|seedance-2.5|kling|google-omni|minimax-h3] Edit/reference a video (Grok does not support video refs)
|
|
3031
|
+
video create --script "Use the supplied audio" --video <url> --audio <url> --video-model sync-lipsync-v3 Lip-sync exact replacement audio
|
|
2901
3032
|
video status <taskId> Check video status
|
|
2902
3033
|
video status --snapshot <snapshotId> [--wait] Check v2 video snapshot
|
|
2903
3034
|
`);
|
|
@@ -3103,6 +3234,31 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
3103
3234
|
if (!res.ok) { console.error(`Error ${res.status}:`, await res.text()); process.exit(1); }
|
|
3104
3235
|
console.log(`✅ ${email} is now admin`);
|
|
3105
3236
|
|
|
3237
|
+
} else if (sub === 'add-credits') {
|
|
3238
|
+
const user = args[2]?.trim();
|
|
3239
|
+
const credits = Number(args[3]);
|
|
3240
|
+
if (!user || !Number.isSafeInteger(credits) || credits <= 0) {
|
|
3241
|
+
console.error('Usage: makaron admin add-credits <email-or-user-id> <credits> [--json]');
|
|
3242
|
+
console.error('Credits must be a positive integer.');
|
|
3243
|
+
process.exit(1);
|
|
3244
|
+
}
|
|
3245
|
+
|
|
3246
|
+
const res = await fetch(`${baseUrl}/api/admin/add-credits`, {
|
|
3247
|
+
method: 'POST',
|
|
3248
|
+
headers: { 'Content-Type': 'application/json', ...headers },
|
|
3249
|
+
body: JSON.stringify({ email: user, credits }),
|
|
3250
|
+
});
|
|
3251
|
+
const data = await res.json().catch(() => null);
|
|
3252
|
+
if (!res.ok) {
|
|
3253
|
+
console.error(`Error ${res.status}:`, data?.error || 'Failed to add credits');
|
|
3254
|
+
process.exit(1);
|
|
3255
|
+
}
|
|
3256
|
+
if (args.includes('--json')) {
|
|
3257
|
+
console.log(JSON.stringify(data));
|
|
3258
|
+
} else {
|
|
3259
|
+
console.log(`✅ Added ${credits} credits to ${user}. New balance: ${data.newBalance}`);
|
|
3260
|
+
}
|
|
3261
|
+
|
|
3106
3262
|
} else {
|
|
3107
3263
|
console.log(`Admin commands:
|
|
3108
3264
|
admin skills List all marketplace skills
|
|
@@ -3115,6 +3271,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
3115
3271
|
admin skill-categories delete <id> Delete a category
|
|
3116
3272
|
admin upload <file> <storage-path> Upload file to Storage
|
|
3117
3273
|
admin fetch-skill <code|url> Download skill from share link
|
|
3274
|
+
admin add-credits <user> <credits> Add credits by email or user ID
|
|
3118
3275
|
admin set-admin <email> Grant admin access to a user
|
|
3119
3276
|
`);
|
|
3120
3277
|
}
|
package/package.json
CHANGED
package/skills/makaron/SKILL.md
CHANGED
|
@@ -162,7 +162,7 @@ npx makaron-cli chat --project auto \
|
|
|
162
162
|
|
|
163
163
|
`--skill` accepts an installed skill name, a marketplace UUID, or a unique marketplace label. Marketplace search matches every localized title, every localized default prompt, and category ids. If a marketplace skill is matched, the CLI installs or reuses it and sends `[Active skill: <installed-skill-name>]` to Makaron Agent. Do not call admin skill commands for ordinary users. There is intentionally no user-facing CLI delete command for marketplace skills.
|
|
164
164
|
|
|
165
|
-
For an explicitly authorized
|
|
165
|
+
For an explicitly authorized administrator, `admin add-credits <email-or-user-id> <positive-integer> [--json]` adds credits to a user. Marketplace administrators can use `admin skills` to see category assignment and 4-locale title/prompt completeness, while `admin skill-categories` supports list/add/update/delete. New skills must include `en`, `zh`, `zh-Hant`, and `ja` in both `labels` and `prompts`, plus at least one existing `categories` id. Partial localized updates are safely merged server-side.
|
|
166
166
|
|
|
167
167
|
### With additional images (existing project)
|
|
168
168
|
|
|
@@ -180,16 +180,17 @@ npx makaron-cli project media <projectId> --json
|
|
|
180
180
|
|
|
181
181
|
This is project-scoped. `responses get <runId> --pick output` only returns artifacts from one run; `project media` returns the whole project timeline: original uploads, references, generated images, video snapshots, and editable compositions.
|
|
182
182
|
|
|
183
|
-
Publish
|
|
183
|
+
Publish typed external images and video intervals directly into that Media List without uploading the original media:
|
|
184
184
|
|
|
185
185
|
```bash
|
|
186
|
-
npx makaron-cli project media add <projectId> --source-url "https://cdn.example.com/
|
|
187
|
-
npx makaron-cli project media add <projectId> --
|
|
186
|
+
npx makaron-cli project media add <projectId> --type image --source-url "https://cdn.example.com/product.jpg" --description "Hero product image"
|
|
187
|
+
npx makaron-cli project media add <projectId> --type video --source-url "https://cdn.example.com/source.mp4" --start 12.5 --end 19 --description "Racket frame molding"
|
|
188
|
+
npx makaron-cli project media add <projectId> --input media.json --json
|
|
188
189
|
```
|
|
189
190
|
|
|
190
|
-
The JSON input may be an array or `{ "clips": [...] }`.
|
|
191
|
+
The JSON input may be an array or `{ "clips": [...] }`. Every item declares `type` as `image` or `video`. Images have `source_url + type + description` and no time range. Videos have `source_url + type + start + end + description`; `start` and `end` are seconds. Array order is edit order and `source_url` is opaque. Do not add or request provider-specific identity fields. Put existing media understanding (summary, editorial purpose, scene evidence, confidence, and limitations) in `description`. Makaron reads that provider-neutral Media List field before deciding whether any additional image/video analysis is needed.
|
|
191
192
|
|
|
192
|
-
For one-call orchestration, use `chat --project auto --media-manifest plan.json`. Makaron validates the manifest, creates the project, imports its
|
|
193
|
+
For one-call orchestration, use `chat --project auto --media-manifest plan.json`. Makaron validates the manifest, creates the project, imports its media, and starts the Agent. If an upstream service returns multiple plans, the caller should start one independent Makaron task per plan instead of passing the provider-specific batch response into Makaron.
|
|
193
194
|
|
|
194
195
|
```bash
|
|
195
196
|
npx makaron-cli chat --project auto --media-manifest set-01.json --json -b "Make a 30-second 9:16 TikTok with English VO and captions"
|
|
@@ -292,9 +293,12 @@ npx makaron-cli edit --image photo.jpg --ref style.jpg "match this style"
|
|
|
292
293
|
|
|
293
294
|
# Output to file
|
|
294
295
|
npx makaron-cli edit --image photo.jpg --out result.jpg "make it dramatic"
|
|
296
|
+
|
|
297
|
+
# Strict transparent output through GPT Image 2
|
|
298
|
+
npx makaron-cli edit --image-model openai --background transparent --out sticker.png "a magenta star sticker"
|
|
295
299
|
```
|
|
296
300
|
|
|
297
|
-
Options: `--image`, `--image-model gemini|gemini-lite|qwen|openai|pony|wai`, `--ref <file>` (up to 3), `--aspect <ratio>`, `--out <path
|
|
301
|
+
Options: `--image`, `--image-model gemini|gemini-lite|qwen|openai|pony|wai`, `--ref <file>` (up to 3), `--aspect <ratio>`, `--background auto|opaque|transparent`, `--out <path>`. Transparent output routes strictly to GPT Image 2 and fails instead of returning an opaque fallback.
|
|
298
302
|
|
|
299
303
|
### `video` — Standalone video tools (no project timeline)
|
|
300
304
|
|
|
@@ -327,7 +331,7 @@ npx makaron-cli video status <taskId>
|
|
|
327
331
|
npx makaron-cli chat --project <id|auto> --video input.mp4 -b "make it funny"
|
|
328
332
|
```
|
|
329
333
|
|
|
330
|
-
Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to the selected model limit), `--video <file|url>` and `--audio <file|url>` (repeatable where supported), `--duration <seconds>`, `--aspect 9:16|16:9|1:1`, `--video-model seedance-fast|seedance-mini|seedance|seedance-2.5|kling|grok|google-omni|minimax-h3`, `--video-resolution auto|480p|720p|768p|1080p|2k|4k`. Default model is `seedance-fast`. SeeDance accepts native text-to-video with no image and integer output duration 4-15s (default 5s); `seedance-mini` supports 480p/720p and is best for cheaper drafts/multi-size tests; MiniMax H3 accepts native text-to-video, 4-15s output, public 768p/2k resolution, and up to 9 image, up to 3 video, and up to 3 audio references through Makaron Agent/chat. H3 defaults to 768p; request 2k explicitly for maximum/final quality. Kling supports 5-15s; Grok 1.5 supports 1-15s single-image-to-video only; Gemini Omni supports 3-10s fast 720p image/video generation and editing with native generated audio, including up to 6 image references when no video reference is provided. For `--video-model grok`, forced `--aspect` is ignored to avoid xAI stretching the source image; pad/create the image at the target shape first or use another model.
|
|
334
|
+
Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to the selected model limit), `--video <file|url>` and `--audio <file|url>` (repeatable where supported), `--duration <seconds>`, `--aspect 9:16|16:9|1:1`, `--video-model seedance-fast|seedance-mini|seedance|seedance-2.5|kling|grok|google-omni|minimax-h3|sync-lipsync-v3`, `--video-resolution auto|480p|720p|768p|1080p|2k|4k`. Default model is `seedance-fast`. SeeDance accepts native text-to-video with no image and integer output duration 4-15s (default 5s); `seedance-mini` supports 480p/720p and is best for cheaper drafts/multi-size tests; MiniMax H3 accepts native text-to-video, 4-15s output, public 768p/2k resolution, and up to 9 image, up to 3 video, and up to 3 audio references through Makaron Agent/chat. `sync-lipsync-v3` requires exactly one video plus one MP3/WAV and preserves that replacement audio while aligning the mouth. H3 defaults to 768p; request 2k explicitly for maximum/final quality. Kling supports 5-15s; Grok 1.5 supports 1-15s single-image-to-video only; Gemini Omni supports 3-10s fast 720p image/video generation and editing with native generated audio, including up to 6 image references when no video reference is provided. For `--video-model grok`, forced `--aspect` is ignored to avoid xAI stretching the source image; pad/create the image at the target shape first or use another model.
|
|
331
335
|
|
|
332
336
|
Seedance 2.5 uses `--video-model seedance-2.5` and supports 4-30s at 480p/720p, up to 30 images + 10 videos + 10 audios, repeatable local/URL references, `--video-operation generate|edit|extend`, `--extend-direction`, `--output-format mp4|mov`, and `--web-search`. The Evolink route does not currently expose 4K output.
|
|
333
337
|
|
|
@@ -378,6 +382,7 @@ type CompletionAction = {
|
|
|
378
382
|
3. Stop when `status` is `"completed"`, `"failed"`, or `"aborted"`
|
|
379
383
|
4. Top-level `status: "completed"` means ALL artifacts are ready (including rendered videos)
|
|
380
384
|
5. If an async video fails, top-level `status` is `"failed"` and the failed video may include `completion_actions` for a safe retry or diagnosis. Agents can surface these as the next user-confirmed step.
|
|
385
|
+
6. `responses get --wait` reconciles pending video output against completed Project Media with the same `snapshot_id` or `task_id`. A lagging Run row therefore does not block delivery after the durable project video is ready.
|
|
381
386
|
|
|
382
387
|
## Exit Codes
|
|
383
388
|
|