makaron-cli 0.13.4 → 0.13.6
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 +4 -4
- package/.codex-plugin/plugin.json +21 -12
- package/README.md +36 -0
- package/bin/makaron.mjs +191 -8
- package/package.json +1 -1
- package/skills/makaron/SKILL.md +15 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "makaron-cli",
|
|
3
|
-
"version": "0.13.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.13.6",
|
|
4
|
+
"description": "Give Claude Code a creative agent. Pass complete creative requests and source media to Makaron Chat.",
|
|
5
5
|
"author": {
|
|
6
|
-
"name": "
|
|
6
|
+
"name": "Versa AI",
|
|
7
|
+
"email": "tianyi@versa-ai.com",
|
|
7
8
|
"url": "https://www.makaron.app"
|
|
8
9
|
},
|
|
9
10
|
"homepage": "https://www.makaron.app/agent",
|
|
10
|
-
"repository": "https://github.com/vegekyd/ai-image-editor",
|
|
11
11
|
"skills": "./skills/"
|
|
12
12
|
}
|
|
@@ -1,21 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "makaron-cli",
|
|
3
|
-
"version": "0.13.
|
|
4
|
-
"description": "
|
|
5
|
-
"displayName": "Makaron",
|
|
6
|
-
"shortDescription": "AI image/video/music creation from the terminal",
|
|
7
|
-
"longDescription": "makaron.app is for humans. makaron-cli is for AI agents. Talk to Makaron Agent from the terminal — list, search, show, and install marketplace skills; create projects; edit images; generate videos; and compose music. Zero dependencies, single file, works with npx. Agents can self-register to get an API key without human intervention.",
|
|
3
|
+
"version": "0.13.6",
|
|
4
|
+
"description": "Give Codex a creative agent. Pass complete creative requests and source media to Makaron Chat.",
|
|
8
5
|
"author": {
|
|
9
|
-
"name": "
|
|
6
|
+
"name": "Versa AI",
|
|
7
|
+
"email": "tianyi@versa-ai.com",
|
|
10
8
|
"url": "https://www.makaron.app"
|
|
11
9
|
},
|
|
12
10
|
"homepage": "https://www.makaron.app/agent",
|
|
13
|
-
"repository": "https://github.com/vegekyd/ai-image-editor",
|
|
14
11
|
"license": "MIT",
|
|
15
|
-
"keywords": ["
|
|
16
|
-
"category": "Creative Tools",
|
|
17
|
-
"capabilities": ["image-editing", "video-generation", "music-creation", "text-to-image", "marketplace-skills"],
|
|
12
|
+
"keywords": ["creative-production", "image-editing", "video-generation", "music", "agent", "cli"],
|
|
18
13
|
"skills": "./skills/",
|
|
19
|
-
"
|
|
20
|
-
|
|
14
|
+
"interface": {
|
|
15
|
+
"displayName": "Makaron Creative Studio",
|
|
16
|
+
"shortDescription": "Give Codex a creative agent",
|
|
17
|
+
"longDescription": "Pass any complete creative request and source media to Makaron Chat. Makaron plans and produces images, video, music, and motion design in a persistent project that a human can continue editing in makaron.app.",
|
|
18
|
+
"developerName": "Versa AI",
|
|
19
|
+
"category": "Creative Tools",
|
|
20
|
+
"capabilities": ["Read", "Write"],
|
|
21
|
+
"websiteURL": "https://www.makaron.app/agent",
|
|
22
|
+
"privacyPolicyURL": "https://www.makaron.app/privacy",
|
|
23
|
+
"defaultPrompt": [
|
|
24
|
+
"Pass this complete creative brief and its source files to Makaron Chat.",
|
|
25
|
+
"Ask Makaron Chat to turn this image into the finished media described in my request.",
|
|
26
|
+
"Continue this Makaron project with my complete follow-up request."
|
|
27
|
+
],
|
|
28
|
+
"brandColor": "#d946ef"
|
|
29
|
+
}
|
|
21
30
|
}
|
package/README.md
CHANGED
|
@@ -151,6 +151,42 @@ npx makaron-cli project media <projectId> --json
|
|
|
151
151
|
|
|
152
152
|
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.
|
|
153
153
|
|
|
154
|
+
External source ranges can be added without uploading either the original video or a derivative clip:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
npx makaron-cli project media add <projectId> \
|
|
158
|
+
--source-url "https://cdn.example.com/source.mp4" \
|
|
159
|
+
--start 12.5 --end 19 \
|
|
160
|
+
--description "Racket frame molding"
|
|
161
|
+
|
|
162
|
+
# Batch form: a JSON array or {"clips": [...]}
|
|
163
|
+
npx makaron-cli project media add <projectId> --input ranges.json --json
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The returned `<<<media_N>>>` is immediately usable by a later Agent run. Its range remains non-destructive metadata; Remotion must trim the original URL to these exact source bounds.
|
|
167
|
+
Use `description` as the provider-neutral media-understanding field. Put any
|
|
168
|
+
already-known summary, editorial purpose, concrete scene evidence, confidence,
|
|
169
|
+
and limitations there. Makaron exposes the full description in Media List
|
|
170
|
+
context so the Agent can edit from it without repeating image/video analysis
|
|
171
|
+
unless a required detail is missing or uncertain.
|
|
172
|
+
|
|
173
|
+
For an agent-to-agent handoff, create the project, import the external ranges,
|
|
174
|
+
and start the Agent in one command:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
npx makaron-cli chat --project auto \
|
|
178
|
+
--media-manifest set-01.json \
|
|
179
|
+
--json -b \
|
|
180
|
+
"Make a 30-second 9:16 TikTok with English VO and burned-in captions"
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The manifest is a JSON array or `{ "title": "...", "clips": [...] }`. Every
|
|
184
|
+
clip contains exactly `source_url`, `start`, `end`, and `description`; the time
|
|
185
|
+
values are seconds and array order is edit order. It is validated before project
|
|
186
|
+
creation and supports up to 20 clips for one Makaron task. Batch planning remains
|
|
187
|
+
the upstream orchestrator's responsibility: convert each plan into one manifest
|
|
188
|
+
and start one independent Makaron task.
|
|
189
|
+
|
|
154
190
|
### Export editable Remotion compositions
|
|
155
191
|
|
|
156
192
|
Animated Remotion compositions are saved as editable timeline/code artifacts first. To materialize one into an MP4 that CLI, V, or another service can read, call the backend export worker:
|
package/bin/makaron.mjs
CHANGED
|
@@ -156,6 +156,70 @@ function readJsonInput(filePath) {
|
|
|
156
156
|
return JSON.parse(raw);
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
const MAX_MEDIA_MANIFEST_RANGES = 20;
|
|
160
|
+
|
|
161
|
+
function normalizeMediaManifest(input) {
|
|
162
|
+
const manifest = Array.isArray(input) ? { clips: input } : input;
|
|
163
|
+
if (!manifest || typeof manifest !== 'object') {
|
|
164
|
+
throw new Error('Media manifest must be a JSON object or an array of source ranges.');
|
|
165
|
+
}
|
|
166
|
+
const rawRanges = Array.isArray(manifest.clips)
|
|
167
|
+
? manifest.clips
|
|
168
|
+
: Array.isArray(manifest.source_ranges)
|
|
169
|
+
? manifest.source_ranges
|
|
170
|
+
: Array.isArray(manifest.sourceRanges)
|
|
171
|
+
? manifest.sourceRanges
|
|
172
|
+
: null;
|
|
173
|
+
if (!rawRanges?.length) {
|
|
174
|
+
throw new Error('Media manifest must contain a non-empty clips array.');
|
|
175
|
+
}
|
|
176
|
+
if (rawRanges.length > MAX_MEDIA_MANIFEST_RANGES) {
|
|
177
|
+
throw new Error(`Media manifest supports at most ${MAX_MEDIA_MANIFEST_RANGES} source ranges per Makaron task.`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const sourceRanges = rawRanges.map((raw, index) => {
|
|
181
|
+
if (!raw || typeof raw !== 'object') throw new Error(`clips[${index}] must be an object.`);
|
|
182
|
+
const sourceUrl = typeof raw.source_url === 'string' ? raw.source_url.trim() : '';
|
|
183
|
+
let parsed;
|
|
184
|
+
try {
|
|
185
|
+
parsed = new URL(sourceUrl);
|
|
186
|
+
} catch {
|
|
187
|
+
throw new Error(`clips[${index}].source_url must be a valid HTTP(S) URL.`);
|
|
188
|
+
}
|
|
189
|
+
if (!['http:', 'https:'].includes(parsed.protocol)) {
|
|
190
|
+
throw new Error(`clips[${index}].source_url must use HTTP or HTTPS.`);
|
|
191
|
+
}
|
|
192
|
+
const start = Number(raw.start ?? raw.start_sec);
|
|
193
|
+
const end = Number(raw.end ?? raw.end_sec);
|
|
194
|
+
if (!Number.isFinite(start) || start < 0) {
|
|
195
|
+
throw new Error(`clips[${index}].start must be a finite number >= 0.`);
|
|
196
|
+
}
|
|
197
|
+
if (!Number.isFinite(end) || end <= start) {
|
|
198
|
+
throw new Error(`clips[${index}].end must be greater than start.`);
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
source_url: sourceUrl,
|
|
202
|
+
start,
|
|
203
|
+
end,
|
|
204
|
+
description: typeof raw.description === 'string' ? raw.description.trim() : '',
|
|
205
|
+
};
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
sourceRanges,
|
|
210
|
+
title: typeof manifest.title === 'string' && manifest.title.trim() ? manifest.title.trim() : undefined,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function readMediaManifest(filePath) {
|
|
215
|
+
try {
|
|
216
|
+
return normalizeMediaManifest(readJsonInput(filePath));
|
|
217
|
+
} catch (error) {
|
|
218
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
219
|
+
throw new Error(`Invalid media manifest: ${message}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
159
223
|
// ─── Auth ────────────────────────────────────────────────────────────────────
|
|
160
224
|
|
|
161
225
|
function loadAuth() {
|
|
@@ -285,6 +349,7 @@ Options:
|
|
|
285
349
|
--image <file|url> Attach a reference image or screenshot. Repeatable.
|
|
286
350
|
--video <file|url> Attach a video to the project timeline. Repeatable.
|
|
287
351
|
--audio <file|url> Attach a song, beat, or voice reference. MP3/WAV, repeatable.
|
|
352
|
+
--media-manifest <file|-> Import source_url + start + end + description clips before this run.
|
|
288
353
|
--skill <id|label|name> Use an installed skill or auto-install a matched marketplace skill.
|
|
289
354
|
--background, -b Submit and print a runId.
|
|
290
355
|
--json Output structured JSON.
|
|
@@ -313,6 +378,9 @@ What you can ask:
|
|
|
313
378
|
Video cuts and assembly
|
|
314
379
|
makaron chat --project <id> --video clip.mp4 "cut out the dead air and keep the best 20 seconds"
|
|
315
380
|
|
|
381
|
+
Agent-to-agent source-range handoff
|
|
382
|
+
makaron chat --project auto --media-manifest set-01.json -b --json "make a 30s vertical video"
|
|
383
|
+
|
|
316
384
|
Music
|
|
317
385
|
makaron chat --project <id> "add calm piano background music"
|
|
318
386
|
|
|
@@ -826,9 +894,33 @@ async function listProjectMedia(baseUrl, headers, projectId, opts = {}) {
|
|
|
826
894
|
const status = item.status && item.status !== 'completed' ? ` ${item.status}` : '';
|
|
827
895
|
const duration = typeof item.duration === 'number' ? ` ${item.duration}s` : '';
|
|
828
896
|
const dimensions = item.width && item.height ? ` ${item.width}x${item.height}` : '';
|
|
829
|
-
const description = item.description ? ` — ${item.description}` : '';
|
|
897
|
+
const description = item.description ? ` — ${String(item.description).replace(/\s*\n\s*/g, ' | ')}` : '';
|
|
898
|
+
const sourceRange = item.source_range || item.sourceRange;
|
|
899
|
+
const range = sourceRange
|
|
900
|
+
? ` source ${formatSeconds(sourceRange.start_sec)}-${formatSeconds(sourceRange.end_sec)}s`
|
|
901
|
+
: '';
|
|
830
902
|
const url = item.url ? `\n ${item.url}` : '';
|
|
831
|
-
console.log(` ${String(item.index).padStart(2)}. ${ref} [${item.type}${status}${duration}${dimensions}]${description}${url}`);
|
|
903
|
+
console.log(` ${String(item.index).padStart(2)}. ${ref} [${item.type}${status}${duration}${dimensions}${range}]${description}${url}`);
|
|
904
|
+
}
|
|
905
|
+
return data;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
async function addProjectMediaSourceRanges(baseUrl, headers, projectId, ranges, opts = {}) {
|
|
909
|
+
const res = await fetch(`${baseUrl}/api/projects/${projectId}/media`, {
|
|
910
|
+
method: 'POST',
|
|
911
|
+
headers: { 'Content-Type': 'application/json', ...headers },
|
|
912
|
+
body: JSON.stringify({ clips: ranges }),
|
|
913
|
+
});
|
|
914
|
+
if (!res.ok) { console.error('Project media add failed:', await res.text()); process.exit(1); }
|
|
915
|
+
const data = await res.json();
|
|
916
|
+
if (opts.json && !opts.silent) {
|
|
917
|
+
console.log(JSON.stringify(data, null, 2));
|
|
918
|
+
return data;
|
|
919
|
+
}
|
|
920
|
+
if (!opts.silent) {
|
|
921
|
+
for (const item of data.media || []) {
|
|
922
|
+
console.log(`${item.ref} ${item.source_url} ${formatSeconds(item.start_sec)}-${formatSeconds(item.end_sec)}s${item.created ? '' : ' (existing)'}`);
|
|
923
|
+
}
|
|
832
924
|
}
|
|
833
925
|
return data;
|
|
834
926
|
}
|
|
@@ -1593,6 +1685,8 @@ Commands:
|
|
|
1593
1685
|
credits Show current credit balance
|
|
1594
1686
|
list (ls) List all projects
|
|
1595
1687
|
project media <projectId> --json List timeline media for a project
|
|
1688
|
+
project media add <projectId> --source-url <url> --start <n> --end <n>
|
|
1689
|
+
Add an external source range without uploading video
|
|
1596
1690
|
create --image <file> Create project from local image
|
|
1597
1691
|
create --image-url <url> Create project from URL
|
|
1598
1692
|
create --title "name" Create empty project (text-to-image)
|
|
@@ -1601,6 +1695,8 @@ Commands:
|
|
|
1601
1695
|
chat --project <id> --skill <id> Use a built-in or marketplace skill
|
|
1602
1696
|
chat --project <id> --video <file> Attach video to conversation
|
|
1603
1697
|
chat --project <id> --audio <file> Attach song/beat/voice reference
|
|
1698
|
+
chat --project auto --media-manifest <file> "message"
|
|
1699
|
+
Create, import external ranges, and run Agent
|
|
1604
1700
|
chat --project <id> -b "message" Background: submit and print runId
|
|
1605
1701
|
chat --project <id> --stream "msg" Legacy: stream SSE in real-time
|
|
1606
1702
|
chat --project <id> --json "msg" Output structured JSON result
|
|
@@ -1708,9 +1804,12 @@ function printHelp(topic, subtopic) {
|
|
|
1708
1804
|
} else if (topic === 'credits' || topic === 'credit' || topic === 'balance') {
|
|
1709
1805
|
console.log('Usage: makaron credits [--json]');
|
|
1710
1806
|
} else if (topic === 'project' || topic === 'projects') {
|
|
1711
|
-
if (subtopic === 'media') console.log(
|
|
1807
|
+
if (subtopic === 'media') console.log(`Usage: makaron project media <projectId> [--json]
|
|
1808
|
+
makaron project media add <projectId> --source-url <url> --start <n> --end <n> [--description <text>] [--json]
|
|
1809
|
+
makaron project media add <projectId> --input <ranges.json> [--json]`);
|
|
1712
1810
|
else console.log(`Project commands:
|
|
1713
1811
|
project media <projectId> --json List timeline media for a project
|
|
1812
|
+
project media add <projectId> ... Add external source_url + start + end media
|
|
1714
1813
|
`);
|
|
1715
1814
|
} else if (topic === 'abort') {
|
|
1716
1815
|
console.log('Usage: makaron abort <runId>');
|
|
@@ -1868,11 +1967,20 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
1868
1967
|
let background = false;
|
|
1869
1968
|
let jsonOutput = false;
|
|
1870
1969
|
let activeSkill = undefined;
|
|
1970
|
+
let mediaManifestPath = undefined;
|
|
1871
1971
|
for (let i = 1; i < args.length; i++) {
|
|
1872
1972
|
if (args[i] === '--project' && args[i + 1]) projectId = args[++i];
|
|
1873
1973
|
else if (args[i] === '--image' && args[i + 1]) chatImages.push(args[++i]);
|
|
1874
1974
|
else if (args[i] === '--video' && args[i + 1]) chatVideos.push(args[++i]);
|
|
1875
1975
|
else if (args[i] === '--audio' && args[i + 1]) chatAudios.push(args[++i]);
|
|
1976
|
+
else if (args[i] === '--media-manifest') {
|
|
1977
|
+
if (!args[i + 1] || args[i + 1].startsWith('--')) {
|
|
1978
|
+
process.stderr.write('❌ --media-manifest requires a JSON file path or -.\n');
|
|
1979
|
+
process.exit(1);
|
|
1980
|
+
}
|
|
1981
|
+
mediaManifestPath = args[++i];
|
|
1982
|
+
}
|
|
1983
|
+
else if (args[i].startsWith('--media-manifest=')) mediaManifestPath = args[i].slice('--media-manifest='.length);
|
|
1876
1984
|
else if (args[i] === '--skill' && args[i + 1]) activeSkill = args[++i];
|
|
1877
1985
|
else if (args[i].startsWith('--skill=')) activeSkill = args[i].slice('--skill='.length);
|
|
1878
1986
|
else if (args[i] === '--stream') useStream = true;
|
|
@@ -1898,6 +2006,15 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
1898
2006
|
console.error('Run: makaron chat --help');
|
|
1899
2007
|
process.exit(1);
|
|
1900
2008
|
}
|
|
2009
|
+
let mediaManifest;
|
|
2010
|
+
if (mediaManifestPath) {
|
|
2011
|
+
try {
|
|
2012
|
+
mediaManifest = readMediaManifest(mediaManifestPath);
|
|
2013
|
+
} catch (error) {
|
|
2014
|
+
process.stderr.write(`❌ ${error instanceof Error ? error.message : String(error)}\n`);
|
|
2015
|
+
process.exit(1);
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
1901
2018
|
const { headers, baseUrl } = getAuth();
|
|
1902
2019
|
// Split images into URLs vs local files
|
|
1903
2020
|
const imageUrlList = chatImages.filter(p => p.startsWith('http://') || p.startsWith('https://'));
|
|
@@ -1954,13 +2071,31 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
1954
2071
|
const res = await fetch(`${baseUrl}/api/projects/create`, {
|
|
1955
2072
|
method: 'POST',
|
|
1956
2073
|
headers: { 'Content-Type': 'application/json', ...headers },
|
|
1957
|
-
body: JSON.stringify({ title: prompt.slice(0, 50) }),
|
|
2074
|
+
body: JSON.stringify({ title: mediaManifest?.title || prompt.slice(0, 50) }),
|
|
1958
2075
|
});
|
|
1959
2076
|
if (!res.ok) { process.stderr.write(`❌ Failed to create project: ${await res.text()}\n`); process.exit(1); }
|
|
1960
2077
|
const data = await res.json();
|
|
1961
2078
|
projectId = data.projectId;
|
|
1962
2079
|
process.stderr.write(`📦 Project created: ${projectId}\n`);
|
|
1963
2080
|
}
|
|
2081
|
+
let importedManifestMedia = [];
|
|
2082
|
+
if (mediaManifest) {
|
|
2083
|
+
const imported = await addProjectMediaSourceRanges(
|
|
2084
|
+
baseUrl,
|
|
2085
|
+
headers,
|
|
2086
|
+
projectId,
|
|
2087
|
+
mediaManifest.sourceRanges,
|
|
2088
|
+
{ silent: true },
|
|
2089
|
+
);
|
|
2090
|
+
importedManifestMedia = imported.media || [];
|
|
2091
|
+
if (importedManifestMedia.length !== mediaManifest.sourceRanges.length) {
|
|
2092
|
+
process.stderr.write(`❌ Imported ${importedManifestMedia.length}/${mediaManifest.sourceRanges.length} source range(s); aborting run.\n`);
|
|
2093
|
+
process.exit(1);
|
|
2094
|
+
}
|
|
2095
|
+
uploadedTurnMediaCount += importedManifestMedia.length;
|
|
2096
|
+
uploadedTurnVideoCount += importedManifestMedia.length;
|
|
2097
|
+
process.stderr.write(`📎 Imported ${importedManifestMedia.length} external source range(s) from media manifest\n`);
|
|
2098
|
+
}
|
|
1964
2099
|
// Upload additional images to existing project
|
|
1965
2100
|
if (imageFileList.length > 0 || imageUrlList.length > 0) {
|
|
1966
2101
|
const uploadedImageUrls = imageFileList.length
|
|
@@ -2106,7 +2241,20 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2106
2241
|
if (background) {
|
|
2107
2242
|
// Just print runId and exit
|
|
2108
2243
|
if (jsonOutput) {
|
|
2109
|
-
console.log(JSON.stringify({
|
|
2244
|
+
console.log(JSON.stringify({
|
|
2245
|
+
runId,
|
|
2246
|
+
projectId,
|
|
2247
|
+
projectUrl: `${APP_URL}/projects/${projectId}`,
|
|
2248
|
+
status: 'running',
|
|
2249
|
+
...(importedManifestMedia.length ? {
|
|
2250
|
+
importedMedia: importedManifestMedia.map(item => ({
|
|
2251
|
+
ref: item.ref,
|
|
2252
|
+
source_url: item.source_url,
|
|
2253
|
+
start_sec: item.start_sec,
|
|
2254
|
+
end_sec: item.end_sec,
|
|
2255
|
+
})),
|
|
2256
|
+
} : {}),
|
|
2257
|
+
}));
|
|
2110
2258
|
} else {
|
|
2111
2259
|
console.log(runId);
|
|
2112
2260
|
}
|
|
@@ -2344,13 +2492,48 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
|
|
|
2344
2492
|
const { headers, baseUrl } = getAuth();
|
|
2345
2493
|
const sub = args[1];
|
|
2346
2494
|
if (sub === 'media') {
|
|
2347
|
-
const projectId = args[2];
|
|
2348
|
-
if (!projectId) { console.error('Usage: makaron project media <projectId> [--json]'); process.exit(1); }
|
|
2349
2495
|
const jsonOutput = args.includes('--json');
|
|
2350
|
-
|
|
2496
|
+
if (args[2] === 'add') {
|
|
2497
|
+
const projectId = args[3];
|
|
2498
|
+
if (!projectId) { console.error('Usage: makaron project media add <projectId> --source-url <url> --start <n> --end <n>'); process.exit(1); }
|
|
2499
|
+
const readOption = (name) => {
|
|
2500
|
+
const index = args.indexOf(name);
|
|
2501
|
+
return index >= 0 ? args[index + 1] : undefined;
|
|
2502
|
+
};
|
|
2503
|
+
const inputPath = readOption('--input');
|
|
2504
|
+
let ranges;
|
|
2505
|
+
if (inputPath) {
|
|
2506
|
+
const input = readJsonInput(inputPath);
|
|
2507
|
+
ranges = Array.isArray(input) ? input : input.clips || input.source_ranges || input.sourceRanges;
|
|
2508
|
+
} else {
|
|
2509
|
+
const sourceUrl = readOption('--source-url');
|
|
2510
|
+
const start = Number(readOption('--start') ?? readOption('--start-sec'));
|
|
2511
|
+
const end = Number(readOption('--end') ?? readOption('--end-sec'));
|
|
2512
|
+
if (!sourceUrl || !Number.isFinite(start) || !Number.isFinite(end)) {
|
|
2513
|
+
console.error('Provide --source-url, --start, and --end, or --input <manifest.json>.');
|
|
2514
|
+
process.exit(1);
|
|
2515
|
+
}
|
|
2516
|
+
ranges = [{
|
|
2517
|
+
source_url: sourceUrl,
|
|
2518
|
+
start,
|
|
2519
|
+
end,
|
|
2520
|
+
...(readOption('--description') ? { description: readOption('--description') } : {}),
|
|
2521
|
+
}];
|
|
2522
|
+
}
|
|
2523
|
+
if (!Array.isArray(ranges) || !ranges.length) {
|
|
2524
|
+
console.error('Input must contain a non-empty clips array.');
|
|
2525
|
+
process.exit(1);
|
|
2526
|
+
}
|
|
2527
|
+
await addProjectMediaSourceRanges(baseUrl, headers, projectId, ranges, { json: jsonOutput });
|
|
2528
|
+
} else {
|
|
2529
|
+
const projectId = args[2];
|
|
2530
|
+
if (!projectId) { console.error('Usage: makaron project media <projectId> [--json]'); process.exit(1); }
|
|
2531
|
+
await listProjectMedia(baseUrl, headers, projectId, { json: jsonOutput });
|
|
2532
|
+
}
|
|
2351
2533
|
} else {
|
|
2352
2534
|
console.log(`Project commands:
|
|
2353
2535
|
project media <projectId> --json List timeline media for a project
|
|
2536
|
+
project media add <projectId> ... Add external source_url + start + end media
|
|
2354
2537
|
`);
|
|
2355
2538
|
}
|
|
2356
2539
|
} else if (command === 'abort') {
|
package/package.json
CHANGED
package/skills/makaron/SKILL.md
CHANGED
|
@@ -149,6 +149,21 @@ npx makaron-cli project media <projectId> --json
|
|
|
149
149
|
|
|
150
150
|
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.
|
|
151
151
|
|
|
152
|
+
Publish an external video interval directly into that Media List without uploading the original or a derivative MP4:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
npx makaron-cli project media add <projectId> --source-url "https://cdn.example.com/source.mp4" --start 12.5 --end 19 --description "Racket frame molding"
|
|
156
|
+
npx makaron-cli project media add <projectId> --input ranges.json --json
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The JSON input may be an array or `{ "clips": [...] }`. Each clip has exactly `source_url + 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.
|
|
160
|
+
|
|
161
|
+
For one-call orchestration, use `chat --project auto --media-manifest plan.json`. Makaron validates the manifest, creates the project, imports its ranges, 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.
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
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"
|
|
165
|
+
```
|
|
166
|
+
|
|
152
167
|
### With video input (edit, compose, extend)
|
|
153
168
|
|
|
154
169
|
```bash
|