makaron-cli 0.8.3 → 0.9.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 +43 -6
- package/bin/makaron.mjs +334 -48
- package/package.json +3 -4
- package/skills/makaron/SKILL.md +43 -7
- package/SKILL.md +0 -311
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "makaron-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "AI image editing, video generation, and music creation via CLI. Agents can self-register, 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
|
@@ -75,6 +75,10 @@ npx makaron-cli responses get $RUN_ID --wait --json
|
|
|
75
75
|
|
|
76
76
|
Use `chat` for all creative tasks. Makaron Agent decides how to execute — it can edit images, generate videos, compose music, and create designs in a single conversation.
|
|
77
77
|
|
|
78
|
+
```bash
|
|
79
|
+
npx makaron-cli chat --help
|
|
80
|
+
```
|
|
81
|
+
|
|
78
82
|
### Submit a request
|
|
79
83
|
|
|
80
84
|
```bash
|
|
@@ -91,6 +95,18 @@ Returns immediately:
|
|
|
91
95
|
{"runId": "xxx", "projectId": "...", "projectUrl": "https://www.makaron.app/projects/...", "status": "running"}
|
|
92
96
|
```
|
|
93
97
|
|
|
98
|
+
### Common workflows
|
|
99
|
+
|
|
100
|
+
| What you want | Example |
|
|
101
|
+
|--------------|---------|
|
|
102
|
+
| Edit an image | `npx makaron-cli chat --project <id> --image photo.jpg "remove the person in the background"` |
|
|
103
|
+
| Generate an image | `npx makaron-cli chat --project auto "generate a cinematic poster of a rainy Tokyo alley"` |
|
|
104
|
+
| Make a video from the current project | `npx makaron-cli chat --project <id> "make this into a 5 second cinematic video"` |
|
|
105
|
+
| Fix one moment in a video from a screenshot | `npx makaron-cli chat --project <id> --image screenshot.png "@4 this frame should be Paris; only fix this moment"` |
|
|
106
|
+
| Cut or assemble video | `npx makaron-cli chat --project <id> --video clip.mp4 "cut out the dead air and keep the best 20 seconds"` |
|
|
107
|
+
| Add music | `npx makaron-cli chat --project <id> "add calm piano background music"` |
|
|
108
|
+
| Create motion design | `npx makaron-cli chat --project <id> "make an animated Instagram story with this image"` |
|
|
109
|
+
|
|
94
110
|
### With additional images (existing project)
|
|
95
111
|
|
|
96
112
|
```bash
|
|
@@ -124,6 +140,18 @@ Video files are uploaded via signed URL. CLI local video uploads support `.mp4`,
|
|
|
124
140
|
The agent understands video content natively — it can analyze scenes, edit, extend, and compose videos. Seedance video-reference editing is still limited to ~15s provider references, so longer uploaded videos should be split/prepared by the agent before model submission; Kling remains the base/direct edit path.
|
|
125
141
|
Use `chat --project <id|auto> --video ...` for any project/timeline video work. Direct `video create` is standalone and does not write timeline entries.
|
|
126
142
|
|
|
143
|
+
### Fix one video moment from a screenshot
|
|
144
|
+
|
|
145
|
+
When a video is mostly good but one moment needs a local fix, attach a screenshot of the problem frame and describe the correction in normal language:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
npx makaron-cli chat --project <id> \
|
|
149
|
+
--image screenshot.png \
|
|
150
|
+
"@4 this frame should be Paris, keep the same style and only fix this moment"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Makaron can locate the screenshot in the video, regenerate only the nearby segment, and then print a `Next steps` command when the new clip should be stitched back into the full MP4.
|
|
154
|
+
|
|
127
155
|
### Check status (single query)
|
|
128
156
|
|
|
129
157
|
```bash
|
|
@@ -170,15 +198,14 @@ npx makaron-cli edit --image photo.jpg "add cinematic warm lighting"
|
|
|
170
198
|
# Text-to-image (no input)
|
|
171
199
|
npx makaron-cli edit "a cyberpunk cityscape at night"
|
|
172
200
|
|
|
173
|
-
# With model/
|
|
174
|
-
npx makaron-cli edit --image photo.jpg --model openai --skill captions "add title"
|
|
201
|
+
# With model/reference
|
|
175
202
|
npx makaron-cli edit --image photo.jpg --ref style.jpg "match this style"
|
|
176
203
|
|
|
177
204
|
# Output to file
|
|
178
205
|
npx makaron-cli edit --image photo.jpg --out result.jpg "make it dramatic"
|
|
179
206
|
```
|
|
180
207
|
|
|
181
|
-
Options: `--image`, `--model gemini|qwen|openai|pony|wai`, `--
|
|
208
|
+
Options: `--image`, `--model gemini|qwen|openai|pony|wai`, `--ref <file>` (up to 3), `--aspect <ratio>`, `--out <path>`
|
|
182
209
|
|
|
183
210
|
### `video` — Standalone video tools (no project timeline)
|
|
184
211
|
|
|
@@ -206,9 +233,9 @@ For project/timeline video editing, use:
|
|
|
206
233
|
npx makaron-cli chat --project <id|auto> --video input.mp4 -b "make it funny"
|
|
207
234
|
```
|
|
208
235
|
|
|
209
|
-
Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to 7), `--video <file|url>`, `--duration <seconds>`, `--aspect 9:16|16:9|1:1`, `--model kling|seedance`. SeeDance accepts integer output duration 4-15s (default 5s); Kling supports 5-
|
|
236
|
+
Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to 7), `--video <file|url>`, `--duration <seconds>`, `--aspect 9:16|16:9|1:1`, `--model kling|seedance`. SeeDance accepts integer output duration 4-15s (default 5s); Kling supports 5-15s.
|
|
210
237
|
|
|
211
|
-
Video edit model behavior: `--model kling --video` uses Kling base/direct edit internally; `--model seedance --video` uses the Seedance video-reference path and requires target <=15s, <=
|
|
238
|
+
Video edit model behavior: `--model kling --video` uses Kling base/direct edit internally; `--model seedance --video` uses the Seedance video-reference path and requires target <=15s, <=50MB, width/height 300-6000px, aspect ratio 0.4-2.5, and frame pixels 409,600-2,086,876. Tiny metadata padding up to 15.5s is accepted and output duration is clamped to 15s.
|
|
212
239
|
|
|
213
240
|
### `music` — Music generation
|
|
214
241
|
|
|
@@ -237,8 +264,15 @@ type MakaronOutput =
|
|
|
237
264
|
| { id: string; type: "text"; status: "completed"; content: string }
|
|
238
265
|
| { id: string; type: "image"; status: "completed"; url: string; snapshot_id: string }
|
|
239
266
|
| { id: string; type: "design"; status: "completed"; url: string; width: number; height: number; animated: boolean; duration?: number }
|
|
240
|
-
| { id: string; type: "video"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
|
|
267
|
+
| { id: string; type: "video"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; snapshot_id?: string; url?: string; elapsed_seconds?: number; width?: number; height?: number; error?: string; completion_actions?: CompletionAction[] }
|
|
241
268
|
| { id: string; type: "music"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
|
|
269
|
+
|
|
270
|
+
type CompletionAction = {
|
|
271
|
+
label: string
|
|
272
|
+
prompt: string
|
|
273
|
+
description?: string
|
|
274
|
+
policy?: "confirm" | "auto"
|
|
275
|
+
}
|
|
242
276
|
```
|
|
243
277
|
|
|
244
278
|
## Polling Rules
|
|
@@ -247,6 +281,7 @@ type MakaronOutput =
|
|
|
247
281
|
2. Use `next_poll_after_ms` as interval (default 5000ms)
|
|
248
282
|
3. Stop when `status` is `"completed"`, `"failed"`, or `"aborted"`
|
|
249
283
|
4. Top-level `status: "completed"` means ALL artifacts are ready (including rendered videos)
|
|
284
|
+
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.
|
|
250
285
|
|
|
251
286
|
## Exit Codes
|
|
252
287
|
|
|
@@ -266,6 +301,7 @@ type MakaronOutput =
|
|
|
266
301
|
| Text-to-image | "generate a cyberpunk cityscape" |
|
|
267
302
|
| Video from image | "create a 5 second video of her walking" |
|
|
268
303
|
| Video with model | "use seedance model, make a 5s video" |
|
|
304
|
+
| Real MP4 edits | `--video clip.mp4 "trim this to the best 20 seconds and preserve audio"` |
|
|
269
305
|
| **Edit video** | **"put Iron Man armor on me in this video"** |
|
|
270
306
|
| **Compose videos** | **"combine @1 and @2 into one party video"** |
|
|
271
307
|
| **Extend video** | **"continue the story for 10 more seconds"** |
|
|
@@ -320,6 +356,7 @@ send_message "All done!"
|
|
|
320
356
|
- stdout is always machine-readable JSON/text. Human-friendly logs go to stderr.
|
|
321
357
|
- Always use `chat` as the primary interface — even for single image edits.
|
|
322
358
|
- `edit`/`video`/`music` are fallback tools for when `chat` is unavailable or you need raw model access without project context.
|
|
359
|
+
- The CLI checks npm for updates at most once per day and prints update notices to stderr. Set `MAKARON_DISABLE_UPDATE_CHECK=1` to disable it.
|
|
323
360
|
|
|
324
361
|
## Admin: Skill Marketplace Operations
|
|
325
362
|
|
package/bin/makaron.mjs
CHANGED
|
@@ -19,9 +19,13 @@ import { execFileSync } from 'child_process';
|
|
|
19
19
|
// ─── Config ──────────────────────────────────────────────────────────────────
|
|
20
20
|
|
|
21
21
|
const AUTH_FILE = path.join(process.env.HOME || '~', '.makaron', 'auth.json');
|
|
22
|
+
const UPDATE_CHECK_FILE = path.join(process.env.HOME || '~', '.makaron', 'update-check.json');
|
|
22
23
|
const DEFAULT_URL = 'https://www.makaron.app';
|
|
23
24
|
const BASE_URL = process.env.MAKARON_URL || DEFAULT_URL;
|
|
24
25
|
const APP_URL = process.env.MAKARON_APP_URL || DEFAULT_URL;
|
|
26
|
+
const NPM_PACKAGE_NAME = 'makaron-cli';
|
|
27
|
+
const UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
28
|
+
const UPDATE_CHECK_TIMEOUT_MS = 400;
|
|
25
29
|
|
|
26
30
|
// Public anon key (safe to embed — only enables auth, not data access)
|
|
27
31
|
const SUPABASE_URL = 'https://sdyrtztrjgmmpnirswxt.supabase.co';
|
|
@@ -34,6 +38,11 @@ const MAX_VIDEO_UPLOAD_DURATION_TOLERANCE = 1;
|
|
|
34
38
|
const MAX_VIDEO_PROVIDER_REFERENCE_DURATION = 15;
|
|
35
39
|
const MAX_VIDEO_PROVIDER_REFERENCE_DURATION_TOLERANCE = 0.5;
|
|
36
40
|
const MAX_VIDEO_FRAME_PIXELS = 2_086_876;
|
|
41
|
+
const SEEDANCE_MIN_VIDEO_FRAME_PIXELS = 409_600;
|
|
42
|
+
const SEEDANCE_MIN_VIDEO_SIDE = 300;
|
|
43
|
+
const SEEDANCE_MAX_VIDEO_SIDE = 6000;
|
|
44
|
+
const SEEDANCE_MIN_VIDEO_ASPECT = 0.4;
|
|
45
|
+
const SEEDANCE_MAX_VIDEO_ASPECT = 2.5;
|
|
37
46
|
|
|
38
47
|
function getCliVersion() {
|
|
39
48
|
try {
|
|
@@ -44,6 +53,84 @@ function getCliVersion() {
|
|
|
44
53
|
}
|
|
45
54
|
}
|
|
46
55
|
|
|
56
|
+
function compareVersions(a, b) {
|
|
57
|
+
const parse = (version) => String(version || '')
|
|
58
|
+
.split('-')[0]
|
|
59
|
+
.split('.')
|
|
60
|
+
.map(part => Number.parseInt(part, 10) || 0);
|
|
61
|
+
const left = parse(a);
|
|
62
|
+
const right = parse(b);
|
|
63
|
+
for (let i = 0; i < Math.max(left.length, right.length); i++) {
|
|
64
|
+
const diff = (left[i] || 0) - (right[i] || 0);
|
|
65
|
+
if (diff !== 0) return diff > 0 ? 1 : -1;
|
|
66
|
+
}
|
|
67
|
+
return 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function readUpdateCache() {
|
|
71
|
+
try {
|
|
72
|
+
return JSON.parse(fs.readFileSync(UPDATE_CHECK_FILE, 'utf-8'));
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function writeUpdateCache(data) {
|
|
79
|
+
try {
|
|
80
|
+
const dir = path.dirname(UPDATE_CHECK_FILE);
|
|
81
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
82
|
+
fs.writeFileSync(UPDATE_CHECK_FILE, JSON.stringify(data, null, 2));
|
|
83
|
+
} catch { /* best effort */ }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function shouldCheckForUpdates(command, args) {
|
|
87
|
+
if (!command || command === '--version' || command === '-v' || command === 'version') return false;
|
|
88
|
+
if (args.includes('--help') || args.includes('-h')) return false;
|
|
89
|
+
if (args.includes('--json') || args.includes('--jsonl') || args.includes('--pick')) return false;
|
|
90
|
+
if (process.env.CI || process.env.NO_UPDATE_NOTIFIER || process.env.MAKARON_DISABLE_UPDATE_CHECK) return false;
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function maybeNotifyUpdate(command, args) {
|
|
95
|
+
if (!shouldCheckForUpdates(command, args)) return;
|
|
96
|
+
const currentVersion = getCliVersion();
|
|
97
|
+
const now = Date.now();
|
|
98
|
+
const cache = readUpdateCache();
|
|
99
|
+
if (cache?.checkedAt && now - cache.checkedAt < UPDATE_CHECK_INTERVAL_MS) {
|
|
100
|
+
if (cache.latestVersion && compareVersions(cache.latestVersion, currentVersion) > 0) {
|
|
101
|
+
printUpdateNotice(currentVersion, cache.latestVersion);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const controller = new AbortController();
|
|
107
|
+
const timer = setTimeout(() => controller.abort(), UPDATE_CHECK_TIMEOUT_MS);
|
|
108
|
+
try {
|
|
109
|
+
const res = await fetch(`https://registry.npmjs.org/${NPM_PACKAGE_NAME}/latest`, {
|
|
110
|
+
signal: controller.signal,
|
|
111
|
+
headers: { 'Accept': 'application/json' },
|
|
112
|
+
});
|
|
113
|
+
if (!res.ok) return;
|
|
114
|
+
const data = await res.json();
|
|
115
|
+
const latestVersion = data?.version;
|
|
116
|
+
if (!latestVersion) return;
|
|
117
|
+
writeUpdateCache({ checkedAt: now, latestVersion });
|
|
118
|
+
if (compareVersions(latestVersion, currentVersion) > 0) {
|
|
119
|
+
printUpdateNotice(currentVersion, latestVersion);
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
writeUpdateCache({ checkedAt: now, latestVersion: cache?.latestVersion || currentVersion });
|
|
123
|
+
} finally {
|
|
124
|
+
clearTimeout(timer);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function printUpdateNotice(currentVersion, latestVersion) {
|
|
129
|
+
process.stderr.write(`\nUpdate available: makaron-cli ${currentVersion} -> ${latestVersion}\n`);
|
|
130
|
+
process.stderr.write('Run: npm install -g makaron-cli@latest\n');
|
|
131
|
+
process.stderr.write('Or: npx makaron-cli@latest ...\n\n');
|
|
132
|
+
}
|
|
133
|
+
|
|
47
134
|
function formatSeconds(seconds) {
|
|
48
135
|
if (!Number.isFinite(seconds)) return String(seconds);
|
|
49
136
|
return Number.isInteger(seconds) ? String(seconds) : seconds.toFixed(1).replace(/\.0$/, '');
|
|
@@ -148,6 +235,80 @@ function normalizeRunResponse(data) {
|
|
|
148
235
|
return data;
|
|
149
236
|
}
|
|
150
237
|
|
|
238
|
+
function collectCompletionActions(data) {
|
|
239
|
+
const items = [];
|
|
240
|
+
const add = (action, source) => {
|
|
241
|
+
if (!action?.label || !action?.prompt) return;
|
|
242
|
+
const key = `${action.label}\n${action.prompt}`;
|
|
243
|
+
if (items.some(i => i.key === key)) return;
|
|
244
|
+
items.push({ key, label: action.label, prompt: action.prompt, description: action.description, source });
|
|
245
|
+
};
|
|
246
|
+
for (const out of data.output || []) {
|
|
247
|
+
for (const action of out.completion_actions || out.completionActions || []) add(action, out.id || out.task_id);
|
|
248
|
+
}
|
|
249
|
+
for (const video of data.result?.videos || []) {
|
|
250
|
+
for (const action of video.completion_actions || video.completionActions || []) add(action, video.taskId);
|
|
251
|
+
}
|
|
252
|
+
return items;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function printCompletionActions(data) {
|
|
256
|
+
const projectId = data.projectId || data.project_id;
|
|
257
|
+
const actions = collectCompletionActions(data);
|
|
258
|
+
if (!projectId || actions.length === 0) return;
|
|
259
|
+
process.stderr.write('\nNext steps:\n');
|
|
260
|
+
for (const action of actions) {
|
|
261
|
+
process.stderr.write(`• ${action.label}${action.description ? ` — ${action.description}` : ''}\n`);
|
|
262
|
+
process.stderr.write(` makaron chat --project ${projectId} ${JSON.stringify(action.prompt)}\n`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function printChatHelp() {
|
|
267
|
+
console.log(`Makaron chat — create and edit with Makaron Agent
|
|
268
|
+
|
|
269
|
+
Usage:
|
|
270
|
+
makaron chat --project <id|auto> [options] "your message"
|
|
271
|
+
|
|
272
|
+
Options:
|
|
273
|
+
--project <id|auto> Project to work in. Use "auto" to create one.
|
|
274
|
+
--image <file|url> Attach a reference image or screenshot. Repeatable.
|
|
275
|
+
--video <file|url> Attach a video to the project timeline. Repeatable.
|
|
276
|
+
--model <name> Preferred image/model route.
|
|
277
|
+
--video-model <name> Preferred video model.
|
|
278
|
+
--background, -b Submit and print a runId.
|
|
279
|
+
--json Output structured JSON.
|
|
280
|
+
--stream Legacy live SSE stream.
|
|
281
|
+
--help, -h Show this help.
|
|
282
|
+
|
|
283
|
+
What you can ask:
|
|
284
|
+
Image edit
|
|
285
|
+
makaron chat --project <id> --image photo.jpg "remove the person in the background"
|
|
286
|
+
|
|
287
|
+
Image generation
|
|
288
|
+
makaron chat --project auto "generate a cinematic poster of a rainy Tokyo alley"
|
|
289
|
+
|
|
290
|
+
Video from image or timeline
|
|
291
|
+
makaron chat --project <id> "make this into a 5 second cinematic video"
|
|
292
|
+
|
|
293
|
+
Fix one video moment from a screenshot
|
|
294
|
+
makaron chat --project <id> --image screenshot.png "@4 this frame should be Paris; only fix this moment"
|
|
295
|
+
|
|
296
|
+
Video cuts and assembly
|
|
297
|
+
makaron chat --project <id> --video clip.mp4 "cut out the dead air and keep the best 20 seconds"
|
|
298
|
+
|
|
299
|
+
Music
|
|
300
|
+
makaron chat --project <id> "add calm piano background music"
|
|
301
|
+
|
|
302
|
+
Motion design
|
|
303
|
+
makaron chat --project <id> "make an animated Instagram story with this image"
|
|
304
|
+
|
|
305
|
+
After async generation:
|
|
306
|
+
The CLI waits for video/music tasks. If the result has a natural next step, it prints:
|
|
307
|
+
Next steps:
|
|
308
|
+
makaron chat --project <id> "..."
|
|
309
|
+
`);
|
|
310
|
+
}
|
|
311
|
+
|
|
151
312
|
// ─── SSE Consumer ────────────────────────────────────────────────────────────
|
|
152
313
|
|
|
153
314
|
async function abortRun(baseUrl, headers, runId) {
|
|
@@ -394,6 +555,7 @@ async function pollRun(baseUrl, headers, runId, opts = {}) {
|
|
|
394
555
|
else if (v.status === 'failed') process.stderr.write(`🎬 Video ${v.taskId}: failed${v.error ? ` — ${v.error}` : ''}\n`);
|
|
395
556
|
else process.stderr.write(`🎬 Video ${v.taskId}: ${v.status || 'submitted'}\n`);
|
|
396
557
|
}
|
|
558
|
+
printCompletionActions(data);
|
|
397
559
|
for (const m of data.result.music || []) {
|
|
398
560
|
if (m.audioUrl) process.stderr.write(`🎵 Music: ${m.audioUrl}\n`);
|
|
399
561
|
else process.stderr.write(`🎵 Music ${m.taskId}: ${m.status || 'submitted'}\n`);
|
|
@@ -421,6 +583,12 @@ function applyPick(data, field) {
|
|
|
421
583
|
case 'design_urls': return (data.output || []).filter(o => o.type === 'design' && o.url).map(o => o.url);
|
|
422
584
|
case 'first_music_url': return data.output?.find(o => o.type === 'music' && o.url)?.url || null;
|
|
423
585
|
case 'music_urls': return (data.output || []).filter(o => o.type === 'music' && o.url).map(o => o.url);
|
|
586
|
+
case 'next_steps': return collectCompletionActions(data).map(action => ({
|
|
587
|
+
label: action.label,
|
|
588
|
+
prompt: action.prompt,
|
|
589
|
+
description: action.description,
|
|
590
|
+
source: action.source,
|
|
591
|
+
}));
|
|
424
592
|
case 'project_url': return data.project_url || data.projectUrl || null;
|
|
425
593
|
case 'output': return data.output || [];
|
|
426
594
|
case 'text': return data.output?.find(o => o.type === 'text')?.content || null;
|
|
@@ -768,6 +936,11 @@ function probeLocalVideo(videoPath) {
|
|
|
768
936
|
function validateVideoFile(videoPath, options = {}) {
|
|
769
937
|
const maxDuration = options.maxDuration ?? MAX_VIDEO_UPLOAD_DURATION;
|
|
770
938
|
const durationTolerance = options.durationTolerance ?? MAX_VIDEO_UPLOAD_DURATION_TOLERANCE;
|
|
939
|
+
const minFramePixels = options.minFramePixels ?? 0;
|
|
940
|
+
const minSide = options.minSide ?? 0;
|
|
941
|
+
const maxSide = options.maxSide ?? Infinity;
|
|
942
|
+
const minAspect = options.minAspect ?? 0;
|
|
943
|
+
const maxAspect = options.maxAspect ?? Infinity;
|
|
771
944
|
if (!fs.existsSync(videoPath)) {
|
|
772
945
|
return { ok: false, error: `Video file not found: ${videoPath}` };
|
|
773
946
|
}
|
|
@@ -789,6 +962,19 @@ function validateVideoFile(videoPath, options = {}) {
|
|
|
789
962
|
if (meta.width * meta.height > MAX_VIDEO_FRAME_PIXELS) {
|
|
790
963
|
return { ok: false, error: `Video resolution too high: ${meta.width}x${meta.height} (${meta.width * meta.height} px). Max is <=1080p (${MAX_VIDEO_FRAME_PIXELS} px). Re-upload through the frontend to transcode, or export a smaller video.` };
|
|
791
964
|
}
|
|
965
|
+
const framePixels = meta.width * meta.height;
|
|
966
|
+
const aspect = meta.width / meta.height;
|
|
967
|
+
if (
|
|
968
|
+
framePixels < minFramePixels ||
|
|
969
|
+
meta.width < minSide ||
|
|
970
|
+
meta.height < minSide ||
|
|
971
|
+
meta.width > maxSide ||
|
|
972
|
+
meta.height > maxSide ||
|
|
973
|
+
aspect < minAspect ||
|
|
974
|
+
aspect > maxAspect
|
|
975
|
+
) {
|
|
976
|
+
return { ok: false, error: `Video size does not meet provider limits: ${meta.width}x${meta.height} (${framePixels} px, aspect ${aspect.toFixed(2)}). Required: frame pixels >=${minFramePixels}, sides ${minSide}-${Number.isFinite(maxSide) ? maxSide : '∞'}px, aspect ${minAspect}-${Number.isFinite(maxAspect) ? maxAspect : '∞'}. Resize/pad with FFmpeg before submitting.` };
|
|
977
|
+
}
|
|
792
978
|
const mime = ext === 'mov' ? 'video/quicktime' : ext === 'webm' ? 'video/webm' : 'video/mp4';
|
|
793
979
|
return { ok: true, mime, meta };
|
|
794
980
|
}
|
|
@@ -850,12 +1036,135 @@ async function analyzeVideoCli(baseUrl, headers, rawVideo, questionParts) {
|
|
|
850
1036
|
if (text) console.log(text);
|
|
851
1037
|
}
|
|
852
1038
|
|
|
1039
|
+
// ─── Help ───────────────────────────────────────────────────────────────────
|
|
1040
|
+
|
|
1041
|
+
function hasHelpFlag(values) {
|
|
1042
|
+
return values.includes('--help') || values.includes('-h');
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
function printRootHelp() {
|
|
1046
|
+
console.log(`Makaron CLI — Talk to Makaron Agent from the terminal
|
|
1047
|
+
|
|
1048
|
+
Commands:
|
|
1049
|
+
register --json Get challenge for agent self-registration
|
|
1050
|
+
register --verify --challenge-id <id> --answer <n> Verify and save API key
|
|
1051
|
+
claim Get claim URL for human to link account
|
|
1052
|
+
login Log in to Makaron (human interactive)
|
|
1053
|
+
list (ls) List all projects
|
|
1054
|
+
project media <projectId> --json List timeline media for a project
|
|
1055
|
+
create --image <file> Create project from local image
|
|
1056
|
+
create --image-url <url> Create project from URL
|
|
1057
|
+
create --title "name" Create empty project (text-to-image)
|
|
1058
|
+
|
|
1059
|
+
chat --project <id> "message" Chat (non-blocking, polls for result)
|
|
1060
|
+
chat --project <id> --video <file> Attach video to conversation
|
|
1061
|
+
chat --project <id> -b "message" Background: submit and print runId
|
|
1062
|
+
chat --project <id> --stream "msg" Legacy: stream SSE in real-time
|
|
1063
|
+
chat --project <id> --json "msg" Output structured JSON result
|
|
1064
|
+
|
|
1065
|
+
responses get <runId> Get run status and results
|
|
1066
|
+
responses get <runId> --wait Poll until completed
|
|
1067
|
+
responses list --project <id> List runs for a project
|
|
1068
|
+
abort <runId> Abort a running Agent
|
|
1069
|
+
|
|
1070
|
+
edit [--image <file>] "prompt" AI image edit / text-to-image
|
|
1071
|
+
analyze --video <file|url> Analyze video content
|
|
1072
|
+
video script|create|status Video generation
|
|
1073
|
+
music create|status Music generation
|
|
1074
|
+
|
|
1075
|
+
admin Admin commands (skills, upload, set-admin)
|
|
1076
|
+
|
|
1077
|
+
Environment:
|
|
1078
|
+
MAKARON_API_KEY API key (mk_live_xxx) — recommended for agents
|
|
1079
|
+
MAKARON_URL API base (default: ${DEFAULT_URL})
|
|
1080
|
+
`);
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
function printHelp(topic, subtopic) {
|
|
1084
|
+
if (topic === 'login') {
|
|
1085
|
+
console.log('Usage: makaron login');
|
|
1086
|
+
} else if (topic === 'create') {
|
|
1087
|
+
console.log('Usage: makaron create --image <file> [--image <file2>] | --image-url <url> | --title "name"');
|
|
1088
|
+
} else if (topic === 'chat') {
|
|
1089
|
+
console.log('Usage: makaron chat --project <id|auto> [--image <file>] [--video <file|url>] [--stream] [--background|-b] [--json] "your message"');
|
|
1090
|
+
} else if (topic === 'responses' || topic === 'run') {
|
|
1091
|
+
if (subtopic === 'get') console.log('Usage: makaron responses get <runId> [--wait] [--json] [--pick <field>]');
|
|
1092
|
+
else if (subtopic === 'watch') console.log('Usage: makaron responses watch <runId> [--jsonl] [--interval <ms>]');
|
|
1093
|
+
else if (subtopic === 'list') console.log('Usage: makaron responses list --project <id>');
|
|
1094
|
+
else console.log(`Responses commands:
|
|
1095
|
+
responses get <runId> Get status and output (JSON)
|
|
1096
|
+
responses get <runId> --wait Poll until completed
|
|
1097
|
+
responses get <runId> --pick <field> Extract: first_image_url, first_video_url, project_url, output
|
|
1098
|
+
responses watch <runId> --jsonl Watch until done (incremental events)
|
|
1099
|
+
responses list --project <id> List runs for a project
|
|
1100
|
+
`);
|
|
1101
|
+
} else if (topic === 'list' || topic === 'ls') {
|
|
1102
|
+
console.log('Usage: makaron list');
|
|
1103
|
+
} else if (topic === 'project' || topic === 'projects') {
|
|
1104
|
+
if (subtopic === 'media') console.log('Usage: makaron project media <projectId> [--json]');
|
|
1105
|
+
else console.log(`Project commands:
|
|
1106
|
+
project media <projectId> --json List timeline media for a project
|
|
1107
|
+
`);
|
|
1108
|
+
} else if (topic === 'abort') {
|
|
1109
|
+
console.log('Usage: makaron abort <runId>');
|
|
1110
|
+
} else if (topic === 'edit') {
|
|
1111
|
+
console.log('Usage: makaron edit [--image <file|url>] [--model gemini|qwen|openai] [--skill enhance|creative|wild|captions] [--ref <file>] [--out <file>] "prompt"');
|
|
1112
|
+
} else if (topic === 'analyze') {
|
|
1113
|
+
console.log('Usage: makaron analyze --video <file|url> ["question"]');
|
|
1114
|
+
} else if (topic === 'video') {
|
|
1115
|
+
if (subtopic === 'script') console.log('Usage: makaron video script --image <file> [--image <file>] [--lang en|zh] "direction"');
|
|
1116
|
+
else if (subtopic === 'create') console.log('Usage: makaron video create --script "..." (--image <url> | --video <public-url>) [--duration 10] [--aspect 9:16] [--model kling|seedance] [--keep-original-sound]');
|
|
1117
|
+
else if (subtopic === 'status') console.log('Usage: makaron video status <taskId> | --snapshot <snapshotId> [--wait]');
|
|
1118
|
+
else console.log(`Video commands:
|
|
1119
|
+
video script --image <file> [--image <file>] "direction" Write video script
|
|
1120
|
+
video create --script "..." --image <url> [--duration 10] Submit video task
|
|
1121
|
+
video create --script "..." --video <public-url> [--model kling|seedance] Edit a video (standalone)
|
|
1122
|
+
video status <taskId> Check video status
|
|
1123
|
+
video status --snapshot <snapshotId> [--wait] Check v2 video snapshot
|
|
1124
|
+
`);
|
|
1125
|
+
} else if (topic === 'music') {
|
|
1126
|
+
if (subtopic === 'create') console.log('Usage: makaron music create [--vocals] [--style "genre"] "description"');
|
|
1127
|
+
else if (subtopic === 'status') console.log('Usage: makaron music status <taskId>');
|
|
1128
|
+
else console.log(`Music commands:
|
|
1129
|
+
music create [--vocals] [--style "genre"] "description" Generate music
|
|
1130
|
+
music status <taskId> Check music status
|
|
1131
|
+
`);
|
|
1132
|
+
} else if (topic === 'admin') {
|
|
1133
|
+
if (subtopic === 'skills') console.log('Usage: makaron admin skills [add|update|delete] ...');
|
|
1134
|
+
else if (subtopic === 'upload') console.log('Usage: makaron admin upload <local-file> <storage-path>');
|
|
1135
|
+
else if (subtopic === 'fetch-skill') console.log('Usage: makaron admin fetch-skill <share-code|url>');
|
|
1136
|
+
else if (subtopic === 'set-admin') console.log('Usage: makaron admin set-admin <email>');
|
|
1137
|
+
else console.log(`Admin commands:
|
|
1138
|
+
admin skills List all marketplace skills
|
|
1139
|
+
admin skills add '<json>' Add a new skill
|
|
1140
|
+
admin skills update <id> '<json>' Update a skill
|
|
1141
|
+
admin skills delete <id> Delete a skill
|
|
1142
|
+
admin upload <file> <storage-path> Upload file to Storage
|
|
1143
|
+
admin fetch-skill <code|url> Download skill from share link
|
|
1144
|
+
admin set-admin <email> Grant admin access to a user
|
|
1145
|
+
`);
|
|
1146
|
+
} else if (topic === 'register') {
|
|
1147
|
+
if (subtopic === '--verify') console.log('Usage: makaron register --verify --challenge-id <id> --answer <number>');
|
|
1148
|
+
else console.log('Usage: makaron register --json | makaron register --verify --challenge-id <id> --answer <number>');
|
|
1149
|
+
} else if (topic === 'claim') {
|
|
1150
|
+
console.log('Usage: makaron claim');
|
|
1151
|
+
} else {
|
|
1152
|
+
printRootHelp();
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
|
|
853
1156
|
// ─── Main ────────────────────────────────────────────────────────────────────
|
|
854
1157
|
|
|
855
1158
|
const args = process.argv.slice(2);
|
|
856
1159
|
const command = args[0];
|
|
857
1160
|
|
|
858
|
-
|
|
1161
|
+
await maybeNotifyUpdate(command, args);
|
|
1162
|
+
|
|
1163
|
+
if (!command || command === '--help' || command === '-h' || command === 'help') {
|
|
1164
|
+
printRootHelp();
|
|
1165
|
+
} else if (hasHelpFlag(args)) {
|
|
1166
|
+
printHelp(command, args[1]);
|
|
1167
|
+
} else if (command === '--version' || command === '-v' || command === 'version') {
|
|
859
1168
|
console.log(getCliVersion());
|
|
860
1169
|
} else if (command === 'login') {
|
|
861
1170
|
await login();
|
|
@@ -875,7 +1184,10 @@ if (command === '--version' || command === '-v' || command === 'version') {
|
|
|
875
1184
|
}
|
|
876
1185
|
await createProject(baseUrl, headers, opts);
|
|
877
1186
|
} else if (command === 'chat') {
|
|
878
|
-
|
|
1187
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
1188
|
+
printChatHelp();
|
|
1189
|
+
process.exit(0);
|
|
1190
|
+
}
|
|
879
1191
|
let projectId = null;
|
|
880
1192
|
const chatImages = [];
|
|
881
1193
|
const chatVideos = [];
|
|
@@ -883,16 +1195,14 @@ if (command === '--version' || command === '-v' || command === 'version') {
|
|
|
883
1195
|
let useStream = false;
|
|
884
1196
|
let background = false;
|
|
885
1197
|
let jsonOutput = false;
|
|
1198
|
+
let activeSkill = undefined;
|
|
886
1199
|
let videoModel = undefined;
|
|
887
1200
|
let preferredModel = undefined;
|
|
888
1201
|
for (let i = 1; i < args.length; i++) {
|
|
889
|
-
if (args[i] === '--
|
|
890
|
-
console.error('Usage: makaron chat --project <id|auto> [--image <file>] [--video <file|url>] [--stream] [--background|-b] [--json] "your message"');
|
|
891
|
-
process.exit(0);
|
|
892
|
-
}
|
|
893
|
-
else if (args[i] === '--project' && args[i + 1]) projectId = args[++i];
|
|
1202
|
+
if (args[i] === '--project' && args[i + 1]) projectId = args[++i];
|
|
894
1203
|
else if (args[i] === '--image' && args[i + 1]) chatImages.push(args[++i]);
|
|
895
1204
|
else if (args[i] === '--video' && args[i + 1]) chatVideos.push(args[++i]);
|
|
1205
|
+
else if (args[i] === '--skill' && args[i + 1]) activeSkill = args[++i];
|
|
896
1206
|
else if (args[i] === '--stream') useStream = true;
|
|
897
1207
|
else if (args[i] === '--background' || args[i] === '-b') background = true;
|
|
898
1208
|
else if (args[i] === '--json') jsonOutput = true;
|
|
@@ -902,9 +1212,11 @@ if (command === '--version' || command === '-v' || command === 'version') {
|
|
|
902
1212
|
}
|
|
903
1213
|
const prompt = promptParts.join(' ');
|
|
904
1214
|
if (!prompt) {
|
|
905
|
-
console.error('Usage: makaron chat --project <id|auto> [
|
|
1215
|
+
console.error('Usage: makaron chat --project <id|auto> [options] "your message"');
|
|
1216
|
+
console.error('Run: makaron chat --help');
|
|
906
1217
|
process.exit(1);
|
|
907
1218
|
}
|
|
1219
|
+
const { headers, baseUrl } = getAuth();
|
|
908
1220
|
// Split images into URLs vs local files
|
|
909
1221
|
const imageUrlList = chatImages.filter(p => p.startsWith('http://') || p.startsWith('https://'));
|
|
910
1222
|
const imageFileList = chatImages.filter(p => !p.startsWith('http://') && !p.startsWith('https://'));
|
|
@@ -982,7 +1294,7 @@ if (command === '--version' || command === '-v' || command === 'version') {
|
|
|
982
1294
|
}
|
|
983
1295
|
|
|
984
1296
|
// Upload videos to project timeline (via /api/projects/create with videoUrls)
|
|
985
|
-
let finalPrompt = prompt;
|
|
1297
|
+
let finalPrompt = activeSkill ? `[Active skill: ${activeSkill}]\n${prompt}` : prompt;
|
|
986
1298
|
if (chatVideos.length > 0) {
|
|
987
1299
|
// Upload local files via signed URL (no size limit, works with API key auth)
|
|
988
1300
|
const uploadedVideoUrls = [...prevalidatedVideoUrlList];
|
|
@@ -1026,7 +1338,7 @@ if (command === '--version' || command === '-v' || command === 'version') {
|
|
|
1026
1338
|
|
|
1027
1339
|
// Inject hint so Agent knows videos are available
|
|
1028
1340
|
const hint = `[User uploaded ${chatVideos.length === 1 ? 'a video' : `${chatVideos.length} videos`}. Use analyze_video to understand the content.]`;
|
|
1029
|
-
finalPrompt = `${
|
|
1341
|
+
finalPrompt = `${finalPrompt}\n\n${hint}`;
|
|
1030
1342
|
}
|
|
1031
1343
|
|
|
1032
1344
|
if (useStream) {
|
|
@@ -1171,7 +1483,7 @@ if (command === '--version' || command === '-v' || command === 'version') {
|
|
|
1171
1483
|
else promptParts.push(args[i]);
|
|
1172
1484
|
}
|
|
1173
1485
|
editArgs.editPrompt = promptParts.join(' ');
|
|
1174
|
-
if (!editArgs.editPrompt) { console.error('Usage: makaron edit [--image <file|url>] [--model gemini|qwen|openai] [--
|
|
1486
|
+
if (!editArgs.editPrompt) { console.error('Usage: makaron edit [--image <file|url>] [--model gemini|qwen|openai] [--ref <file>] [--out <file>] "prompt"'); process.exit(1); }
|
|
1175
1487
|
process.stderr.write('🎨 Generating...\n');
|
|
1176
1488
|
const result = await callMcpTool(baseUrl, headers, 'makaron_edit_image', editArgs);
|
|
1177
1489
|
saveMcpImage(result, outputPath);
|
|
@@ -1236,13 +1548,21 @@ if (command === '--version' || command === '-v' || command === 'version') {
|
|
|
1236
1548
|
|
|
1237
1549
|
let videoUrl = isHttpUrl(video) ? video : null;
|
|
1238
1550
|
let inputVideoMeta = null;
|
|
1551
|
+
const selectedVideoModel = videoModel || 'kling';
|
|
1239
1552
|
if (videoUrl) {
|
|
1240
|
-
process.stderr.write(`📹 Assuming public video URL already matches provider reference limits
|
|
1553
|
+
process.stderr.write(`📹 Assuming public video URL already matches provider reference limits. Seedance requires ≤${MAX_VIDEO_PROVIDER_REFERENCE_DURATION}s, ≤50MB, sides 300-6000px, frame pixels 409,600-${MAX_VIDEO_FRAME_PIXELS}; Kling requires ≤200MB and ≤2K.\n`);
|
|
1241
1554
|
}
|
|
1242
1555
|
if (video && !videoUrl) {
|
|
1243
1556
|
const valid = validateVideoFile(video, {
|
|
1244
1557
|
maxDuration: MAX_VIDEO_PROVIDER_REFERENCE_DURATION,
|
|
1245
1558
|
durationTolerance: MAX_VIDEO_PROVIDER_REFERENCE_DURATION_TOLERANCE,
|
|
1559
|
+
...(selectedVideoModel === 'seedance' ? {
|
|
1560
|
+
minFramePixels: SEEDANCE_MIN_VIDEO_FRAME_PIXELS,
|
|
1561
|
+
minSide: SEEDANCE_MIN_VIDEO_SIDE,
|
|
1562
|
+
maxSide: SEEDANCE_MAX_VIDEO_SIDE,
|
|
1563
|
+
minAspect: SEEDANCE_MIN_VIDEO_ASPECT,
|
|
1564
|
+
maxAspect: SEEDANCE_MAX_VIDEO_ASPECT,
|
|
1565
|
+
} : {}),
|
|
1246
1566
|
});
|
|
1247
1567
|
if (!valid.ok) { console.error(`❌ ${valid.error}`); process.exit(1); }
|
|
1248
1568
|
inputVideoMeta = valid.meta;
|
|
@@ -1254,7 +1574,7 @@ if (command === '--version' || command === '-v' || command === 'version') {
|
|
|
1254
1574
|
// Standalone MCP tool (no project timeline write)
|
|
1255
1575
|
process.stderr.write('🎬 Submitting video...\n');
|
|
1256
1576
|
const vArgs = videoUrl
|
|
1257
|
-
? { videoUrl, editPrompt: script, images, videoModel:
|
|
1577
|
+
? { videoUrl, editPrompt: script, images, videoModel: selectedVideoModel, referType: selectedVideoModel === 'seedance' ? 'feature' : 'base' }
|
|
1258
1578
|
: { script, images };
|
|
1259
1579
|
const effectiveDuration = duration || (inputVideoMeta?.duration ? Math.min(MAX_VIDEO_PROVIDER_REFERENCE_DURATION, Math.round(inputVideoMeta.duration)) : undefined);
|
|
1260
1580
|
if (effectiveDuration) vArgs.duration = effectiveDuration;
|
|
@@ -1549,39 +1869,5 @@ if (command === '--version' || command === '-v' || command === 'version') {
|
|
|
1549
1869
|
console.log(JSON.stringify(data));
|
|
1550
1870
|
console.error(`🔗 Share this link with a human: ${data.claim_url}`);
|
|
1551
1871
|
} else {
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
Commands:
|
|
1555
|
-
register --json Get challenge for agent self-registration
|
|
1556
|
-
register --verify --challenge-id <id> --answer <n> Verify and save API key
|
|
1557
|
-
claim Get claim URL for human to link account
|
|
1558
|
-
login Log in to Makaron (human interactive)
|
|
1559
|
-
list (ls) List all projects
|
|
1560
|
-
project media <projectId> --json List timeline media for a project
|
|
1561
|
-
create --image <file> Create project from local image
|
|
1562
|
-
create --image-url <url> Create project from URL
|
|
1563
|
-
create --title "name" Create empty project (text-to-image)
|
|
1564
|
-
|
|
1565
|
-
chat --project <id> "message" Chat (non-blocking, polls for result)
|
|
1566
|
-
chat --project <id> --video <file> Attach video to conversation
|
|
1567
|
-
chat --project <id> -b "message" Background: submit and print runId
|
|
1568
|
-
chat --project <id> --stream "msg" Legacy: stream SSE in real-time
|
|
1569
|
-
chat --project <id> --json "msg" Output structured JSON result
|
|
1570
|
-
|
|
1571
|
-
responses get <runId> Get run status and results
|
|
1572
|
-
responses get <runId> --wait Poll until completed
|
|
1573
|
-
responses list --project <id> List runs for a project
|
|
1574
|
-
abort <runId> Abort a running Agent
|
|
1575
|
-
|
|
1576
|
-
edit [--image <file>] "prompt" AI image edit / text-to-image
|
|
1577
|
-
analyze --video <file|url> Analyze video content
|
|
1578
|
-
video script|create|status Video generation
|
|
1579
|
-
music create|status Music generation
|
|
1580
|
-
|
|
1581
|
-
admin Admin commands (skills, upload, set-admin)
|
|
1582
|
-
|
|
1583
|
-
Environment:
|
|
1584
|
-
MAKARON_API_KEY API key (mk_live_xxx) — recommended for agents
|
|
1585
|
-
MAKARON_URL API base (default: ${DEFAULT_URL})
|
|
1586
|
-
`);
|
|
1872
|
+
printRootHelp();
|
|
1587
1873
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "makaron-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Talk to Makaron Agent from the terminal — create projects, edit images, generate videos",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -9,14 +9,13 @@
|
|
|
9
9
|
"test:live": "MAKARON_LIVE=1 node test/live-smoke.mjs"
|
|
10
10
|
},
|
|
11
11
|
"bin": {
|
|
12
|
-
"makaron": "
|
|
12
|
+
"makaron": "bin/makaron.mjs"
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
15
|
"bin/",
|
|
16
16
|
"skills/",
|
|
17
17
|
".codex-plugin/",
|
|
18
18
|
".claude-plugin/",
|
|
19
|
-
"SKILL.md",
|
|
20
19
|
"README.md"
|
|
21
20
|
],
|
|
22
21
|
"keywords": [
|
|
@@ -30,6 +29,6 @@
|
|
|
30
29
|
"license": "MIT",
|
|
31
30
|
"repository": {
|
|
32
31
|
"type": "git",
|
|
33
|
-
"url": "https://github.com/vegekyd/ai-image-editor"
|
|
32
|
+
"url": "git+https://github.com/vegekyd/ai-image-editor.git"
|
|
34
33
|
}
|
|
35
34
|
}
|
package/skills/makaron/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: makaron
|
|
3
|
-
description: Use Makaron CLI to generate AI images, videos, music, and motion designs. Trigger when user needs creative media production — photo editing, video generation, music composition, or design creation. Requires `npx makaron-cli` and MAKARON_API_KEY env var.
|
|
3
|
+
description: Use Makaron CLI to generate AI images, videos, music, and motion designs. Trigger when user needs creative media production — photo editing, video generation, video editing, music composition, or design creation. Requires `npx makaron-cli` and MAKARON_API_KEY env var.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Makaron CLI — Agent Integration Skill
|
|
@@ -64,6 +64,10 @@ npx makaron-cli responses get $RUN_ID --wait --json
|
|
|
64
64
|
|
|
65
65
|
Use `chat` for all creative tasks. Makaron Agent decides how to execute — it can edit images, generate videos, compose music, and create designs in a single conversation.
|
|
66
66
|
|
|
67
|
+
```bash
|
|
68
|
+
npx makaron-cli chat --help
|
|
69
|
+
```
|
|
70
|
+
|
|
67
71
|
### Submit a request
|
|
68
72
|
|
|
69
73
|
```bash
|
|
@@ -80,6 +84,18 @@ Returns immediately:
|
|
|
80
84
|
{"runId": "xxx", "projectId": "...", "projectUrl": "https://www.makaron.app/projects/...", "status": "running"}
|
|
81
85
|
```
|
|
82
86
|
|
|
87
|
+
### Common workflows
|
|
88
|
+
|
|
89
|
+
| What you want | Example |
|
|
90
|
+
|--------------|---------|
|
|
91
|
+
| Edit an image | `npx makaron-cli chat --project <id> --image photo.jpg "remove the person in the background"` |
|
|
92
|
+
| Generate an image | `npx makaron-cli chat --project auto "generate a cinematic poster of a rainy Tokyo alley"` |
|
|
93
|
+
| Make a video from the current project | `npx makaron-cli chat --project <id> "make this into a 5 second cinematic video"` |
|
|
94
|
+
| Fix one moment in a video from a screenshot | `npx makaron-cli chat --project <id> --image screenshot.png "@4 this frame should be Paris; only fix this moment"` |
|
|
95
|
+
| Cut or assemble video | `npx makaron-cli chat --project <id> --video clip.mp4 "cut out the dead air and keep the best 20 seconds"` |
|
|
96
|
+
| Add music | `npx makaron-cli chat --project <id> "add calm piano background music"` |
|
|
97
|
+
| Create motion design | `npx makaron-cli chat --project <id> "make an animated Instagram story with this image"` |
|
|
98
|
+
|
|
83
99
|
### With additional images (existing project)
|
|
84
100
|
|
|
85
101
|
```bash
|
|
@@ -116,6 +132,18 @@ Supported formats: MP4, MOV, WebM. CLI local video uploads support max 50MB, max
|
|
|
116
132
|
|
|
117
133
|
Use `chat --project <id|auto> --video ...` for any project/timeline video work. Direct video commands are standalone raw-tool calls.
|
|
118
134
|
|
|
135
|
+
### Fix one video moment from a screenshot
|
|
136
|
+
|
|
137
|
+
When a video is mostly good but one moment needs a local fix, attach a screenshot of the problem frame and describe the correction in normal language:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npx makaron-cli chat --project <id> \
|
|
141
|
+
--image screenshot.png \
|
|
142
|
+
"@4 this frame should be Paris, keep the same style and only fix this moment"
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Makaron can locate the screenshot in the video, regenerate only the nearby segment, and then print a `Next steps` command when the new clip should be stitched back into the full MP4.
|
|
146
|
+
|
|
119
147
|
### Check status (single query)
|
|
120
148
|
|
|
121
149
|
```bash
|
|
@@ -162,15 +190,14 @@ npx makaron-cli edit --image photo.jpg "add cinematic warm lighting"
|
|
|
162
190
|
# Text-to-image (no input)
|
|
163
191
|
npx makaron-cli edit "a cyberpunk cityscape at night"
|
|
164
192
|
|
|
165
|
-
# With model/
|
|
166
|
-
npx makaron-cli edit --image photo.jpg --model openai --skill captions "add title"
|
|
193
|
+
# With model/reference
|
|
167
194
|
npx makaron-cli edit --image photo.jpg --ref style.jpg "match this style"
|
|
168
195
|
|
|
169
196
|
# Output to file
|
|
170
197
|
npx makaron-cli edit --image photo.jpg --out result.jpg "make it dramatic"
|
|
171
198
|
```
|
|
172
199
|
|
|
173
|
-
Options: `--image`, `--model gemini|qwen|openai|pony|wai`, `--
|
|
200
|
+
Options: `--image`, `--model gemini|qwen|openai|pony|wai`, `--ref <file>` (up to 3), `--aspect <ratio>`, `--out <path>`
|
|
174
201
|
|
|
175
202
|
### `video` — Standalone video tools (no project timeline)
|
|
176
203
|
|
|
@@ -198,9 +225,9 @@ npx makaron-cli video status <taskId>
|
|
|
198
225
|
npx makaron-cli chat --project <id|auto> --video input.mp4 -b "make it funny"
|
|
199
226
|
```
|
|
200
227
|
|
|
201
|
-
Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to 7), `--video <file|url>`, `--duration <seconds>`, `--aspect 9:16|16:9|1:1`, `--model kling|seedance`. SeeDance accepts integer output duration 4-15s (default 5s); Kling supports 5-
|
|
228
|
+
Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to 7), `--video <file|url>`, `--duration <seconds>`, `--aspect 9:16|16:9|1:1`, `--model kling|seedance`. SeeDance accepts integer output duration 4-15s (default 5s); Kling supports 5-15s.
|
|
202
229
|
|
|
203
|
-
Video edit model behavior: `--model kling --video` uses Kling base/direct edit internally; `--model seedance --video` uses the Seedance video-reference path and requires target <=15s, <=
|
|
230
|
+
Video edit model behavior: `--model kling --video` uses Kling base/direct edit internally; `--model seedance --video` uses the Seedance video-reference path and requires target <=15s, <=50MB, width/height 300-6000px, aspect ratio 0.4-2.5, and frame pixels 409,600-2,086,876. Tiny metadata padding up to 15.5s is accepted and output duration is clamped to 15s.
|
|
204
231
|
|
|
205
232
|
### `music` — Music generation
|
|
206
233
|
|
|
@@ -229,8 +256,15 @@ type MakaronOutput =
|
|
|
229
256
|
| { id: string; type: "text"; status: "completed"; content: string }
|
|
230
257
|
| { id: string; type: "image"; status: "completed"; url: string; snapshot_id: string }
|
|
231
258
|
| { id: string; type: "design"; status: "completed"; url: string; width: number; height: number; animated: boolean; duration?: number }
|
|
232
|
-
| { id: string; type: "video"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; snapshot_id?: string; url?: string; elapsed_seconds?: number; width?: number; height?: number }
|
|
259
|
+
| { id: string; type: "video"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; snapshot_id?: string; url?: string; elapsed_seconds?: number; width?: number; height?: number; error?: string; completion_actions?: CompletionAction[] }
|
|
233
260
|
| { id: string; type: "music"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
|
|
261
|
+
|
|
262
|
+
type CompletionAction = {
|
|
263
|
+
label: string
|
|
264
|
+
prompt: string
|
|
265
|
+
description?: string
|
|
266
|
+
policy?: "confirm" | "auto"
|
|
267
|
+
}
|
|
234
268
|
```
|
|
235
269
|
|
|
236
270
|
## Polling Rules
|
|
@@ -239,6 +273,7 @@ type MakaronOutput =
|
|
|
239
273
|
2. Use `next_poll_after_ms` as interval (default 5000ms)
|
|
240
274
|
3. Stop when `status` is `"completed"`, `"failed"`, or `"aborted"`
|
|
241
275
|
4. Top-level `status: "completed"` means ALL artifacts are ready (including rendered videos)
|
|
276
|
+
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.
|
|
242
277
|
|
|
243
278
|
## Exit Codes
|
|
244
279
|
|
|
@@ -258,6 +293,7 @@ type MakaronOutput =
|
|
|
258
293
|
| Text-to-image | "generate a cyberpunk cityscape" |
|
|
259
294
|
| Video from image | "create a 5 second video of her walking" |
|
|
260
295
|
| Video with model | "use seedance model, make a 5s video" |
|
|
296
|
+
| Real MP4 edits | `--video clip.mp4 "trim this to the best 20 seconds and preserve audio"` |
|
|
261
297
|
| Background music | "add calm piano music" |
|
|
262
298
|
| Motion design | "create an Instagram story with animated text" |
|
|
263
299
|
| Multi-step | "edit the photo then make a video from it" |
|
package/SKILL.md
DELETED
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: makaron
|
|
3
|
-
description: Use Makaron CLI to generate AI images, videos, music, and motion designs. Trigger when user needs creative media production — photo editing, video generation, music composition, or design creation. Requires `npx makaron-cli` and MAKARON_API_KEY env var.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Makaron CLI — Agent Integration Skill
|
|
7
|
-
|
|
8
|
-
> **makaron.app** is for humans. **makaron-cli** is for AI agents.
|
|
9
|
-
|
|
10
|
-
Makaron is a multimodal AI creative agent. You talk to it via `makaron chat`, and it produces images, videos, music, and animated designs — all saved to a persistent project.
|
|
11
|
-
|
|
12
|
-
## Setup
|
|
13
|
-
|
|
14
|
-
### Get your API key
|
|
15
|
-
|
|
16
|
-
**Option A: Human login**
|
|
17
|
-
1. Go to [makaron.app](https://makaron.app) and log in
|
|
18
|
-
2. Open the menu (top-right) → **Get API Key**
|
|
19
|
-
3. Copy your `mk_live_...` key
|
|
20
|
-
|
|
21
|
-
**Option B: Self-Registration (no human required)**
|
|
22
|
-
```bash
|
|
23
|
-
# Step 1: Get challenge
|
|
24
|
-
npx makaron-cli register --json
|
|
25
|
-
# → { "challenge_id": "...", "challenge": "...", "expected_format": "numeric, round to 2 decimal places" }
|
|
26
|
-
|
|
27
|
-
# Step 2: Solve and verify
|
|
28
|
-
npx makaron-cli register --verify --challenge-id <id> --answer 34.5
|
|
29
|
-
# → Key saved to ~/.makaron/auth.json
|
|
30
|
-
# → { "api_key": "mk_live_...", "credits": N, "claim_url": "..." }
|
|
31
|
-
|
|
32
|
-
# (Optional) Let a human claim this account
|
|
33
|
-
npx makaron-cli claim
|
|
34
|
-
# → { "claim_url": "..." } — share with human to link key to their account (valid 7 days)
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Discovery endpoint: `GET https://www.makaron.app/api/agent/register` — returns full registration flow + CLI usage as JSON.
|
|
38
|
-
|
|
39
|
-
After self-registration the key is saved locally — no need to export `MAKARON_API_KEY`.
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
export MAKARON_API_KEY=mk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Verify: `npx makaron-cli list` should show projects.
|
|
46
|
-
|
|
47
|
-
## Core Workflow
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
# One-shot: create project + upload image + submit prompt — all in one command
|
|
51
|
-
RUN_ID=$(npx makaron-cli chat --project auto --image photo.jpg -b "make it cinematic and create a 5s video")
|
|
52
|
-
|
|
53
|
-
# Wait for the final customer-ready result
|
|
54
|
-
npx makaron-cli responses get $RUN_ID --wait --json
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Or with an existing project:
|
|
58
|
-
```bash
|
|
59
|
-
RUN_ID=$(npx makaron-cli chat --project $PROJECT_ID -b "make a 5s video")
|
|
60
|
-
npx makaron-cli responses get $RUN_ID --wait --json
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
## Primary: `chat` (Agent-driven creative work)
|
|
64
|
-
|
|
65
|
-
Use `chat` for all creative tasks. Makaron Agent decides how to execute — it can edit images, generate videos, compose music, and create designs in a single conversation.
|
|
66
|
-
|
|
67
|
-
### Submit a request
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
# With existing project
|
|
71
|
-
npx makaron-cli chat --project <id> --json -b "<prompt>"
|
|
72
|
-
|
|
73
|
-
# Auto-create project (with or without images)
|
|
74
|
-
npx makaron-cli chat --project auto --image photo.jpg --json -b "make it cinematic"
|
|
75
|
-
npx makaron-cli chat --project auto --image img1.jpg --image img2.jpg --json -b "combine these"
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
Returns immediately:
|
|
79
|
-
```json
|
|
80
|
-
{"runId": "xxx", "projectId": "...", "projectUrl": "https://www.makaron.app/projects/...", "status": "running"}
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### With additional images (existing project)
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
npx makaron-cli chat --project <id> --image ref1.jpg --image ref2.jpg -b "use these as style reference"
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### Inspect existing timeline media
|
|
90
|
-
|
|
91
|
-
Before starting a follow-up run on an existing project, list the current timeline media so you know what assets are available and which `<<<media_N>>>` references to use:
|
|
92
|
-
|
|
93
|
-
```bash
|
|
94
|
-
npx makaron-cli project media <projectId> --json
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
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.
|
|
98
|
-
|
|
99
|
-
### With video input (edit, compose, extend)
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
# Upload a video and transform it — Agent understands video content natively
|
|
103
|
-
npx makaron-cli chat --project auto --video selfie.mp4 -b "put Iron Man armor on me"
|
|
104
|
-
|
|
105
|
-
# Combine a person's photo with a video scene
|
|
106
|
-
npx makaron-cli chat --project <id> --video party.mp4 --image kid.jpg -b "make this kid join the party"
|
|
107
|
-
|
|
108
|
-
# Multiple videos — compose or splice
|
|
109
|
-
npx makaron-cli chat --project <id> --video clip1.mp4 --video clip2.mp4 -b "combine into one seamless video"
|
|
110
|
-
|
|
111
|
-
# Video URL (public, downloadable)
|
|
112
|
-
npx makaron-cli chat --project auto --video https://example.com/dance.mp4 -b "extend this to 15 seconds"
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
Supported formats: MP4, MOV, WebM. CLI local video uploads support max 50MB, max 120s with 1s metadata tolerance, and <=1080p / 2,086,876 frame pixels. The frontend can transcode larger videos before upload; the CLI uploads directly to Storage and rejects videos above those limits. Videos are uploaded to the project timeline. The Agent can analyze scenes, edit content, compose multiple clips, extend duration, and add effects — all via natural language. Seedance video-reference editing is still limited to ~15s provider references, so longer uploaded videos should be split/prepared by the agent before model submission; Kling remains the base/direct edit path.
|
|
116
|
-
|
|
117
|
-
Use `chat --project <id|auto> --video ...` for any project/timeline video work. Direct video commands are standalone raw-tool calls.
|
|
118
|
-
|
|
119
|
-
### Check status (single query)
|
|
120
|
-
|
|
121
|
-
```bash
|
|
122
|
-
npx makaron-cli responses get <runId> --json
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
### Advanced: stream incremental events
|
|
126
|
-
|
|
127
|
-
```bash
|
|
128
|
-
npx makaron-cli responses watch <runId> --jsonl
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
Outputs one JSON per line as artifacts appear:
|
|
132
|
-
```
|
|
133
|
-
{"event":"output.added","item":{"id":"out_1","type":"image","status":"completed","url":"https://..."}}
|
|
134
|
-
{"event":"output.added","item":{"id":"out_2","type":"video","status":"rendering","task_id":"xxx"}}
|
|
135
|
-
{"event":"output.updated","item":{"id":"out_2","type":"video","status":"completed","url":"https://..."}}
|
|
136
|
-
{"event":"done","status":"completed"}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
### Extract specific results
|
|
140
|
-
|
|
141
|
-
```bash
|
|
142
|
-
npx makaron-cli responses get <runId> --pick first_image_url
|
|
143
|
-
npx makaron-cli responses get <runId> --pick image_urls # all images (JSON array)
|
|
144
|
-
npx makaron-cli responses get <runId> --pick first_video_url
|
|
145
|
-
npx makaron-cli responses get <runId> --pick video_urls # all videos
|
|
146
|
-
npx makaron-cli responses get <runId> --pick project_url
|
|
147
|
-
npx makaron-cli responses get <runId> --pick text # agent's text reply
|
|
148
|
-
npx makaron-cli responses get <runId> --pick output # full output array
|
|
149
|
-
npx makaron-cli responses get <runId> --pick status
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
## Fallback: Direct tool calls (no project context)
|
|
153
|
-
|
|
154
|
-
Use these only when `chat` is unavailable or you need raw model access without project/conversation context.
|
|
155
|
-
|
|
156
|
-
### `edit` — One-shot image editing
|
|
157
|
-
|
|
158
|
-
```bash
|
|
159
|
-
# Edit an existing image
|
|
160
|
-
npx makaron-cli edit --image photo.jpg "add cinematic warm lighting"
|
|
161
|
-
|
|
162
|
-
# Text-to-image (no input)
|
|
163
|
-
npx makaron-cli edit "a cyberpunk cityscape at night"
|
|
164
|
-
|
|
165
|
-
# With model/skill/reference
|
|
166
|
-
npx makaron-cli edit --image photo.jpg --model openai --skill captions "add title"
|
|
167
|
-
npx makaron-cli edit --image photo.jpg --ref style.jpg "match this style"
|
|
168
|
-
|
|
169
|
-
# Output to file
|
|
170
|
-
npx makaron-cli edit --image photo.jpg --out result.jpg "make it dramatic"
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
Options: `--image`, `--model gemini|qwen|openai|pony|wai`, `--skill enhance|creative|wild|captions`, `--ref <file>` (up to 3), `--aspect <ratio>`, `--out <path>`
|
|
174
|
-
|
|
175
|
-
### `video` — Standalone video tools (no project timeline)
|
|
176
|
-
|
|
177
|
-
```bash
|
|
178
|
-
# 1. Write script from images
|
|
179
|
-
npx makaron-cli video script --image img1.jpg "cinematic story"
|
|
180
|
-
|
|
181
|
-
# 2. Analyze a video (standalone, no timeline write)
|
|
182
|
-
npx makaron-cli analyze --video input.mp4 "describe the key actions and pacing"
|
|
183
|
-
|
|
184
|
-
# 3a. Submit image-to-video rendering (images must be public URLs from step 1 or uploaded)
|
|
185
|
-
npx makaron-cli video create --script "Shot 1 (5s): <<<image_1>>> ..." --image https://...jpg --duration 5 --model kling
|
|
186
|
-
|
|
187
|
-
# 3b. Edit a video from a local file or public URL
|
|
188
|
-
npx makaron-cli video create --script "make it funny" --video input.mp4 --duration 5 --model seedance
|
|
189
|
-
npx makaron-cli video create --script "make it warmer and cinematic" --video https://example.com/input.mp4 --duration 5 --model seedance
|
|
190
|
-
|
|
191
|
-
# 4. Check status
|
|
192
|
-
npx makaron-cli video status <taskId>
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
`video create` returns a provider task id and does not create or update a Makaron project timeline. For project/timeline video editing, use:
|
|
196
|
-
|
|
197
|
-
```bash
|
|
198
|
-
npx makaron-cli chat --project <id|auto> --video input.mp4 -b "make it funny"
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to 7), `--video <file|url>`, `--duration <seconds>`, `--aspect 9:16|16:9|1:1`, `--model kling|seedance`. SeeDance accepts integer output duration 4-15s (default 5s); Kling supports 5-10s.
|
|
202
|
-
|
|
203
|
-
Video edit model behavior: `--model kling --video` uses Kling base/direct edit internally; `--model seedance --video` uses the Seedance video-reference path and requires target <=15s, <=1080p input. Tiny metadata padding up to 15.5s is accepted and output duration is clamped to 15s.
|
|
204
|
-
|
|
205
|
-
### `music` — Music generation
|
|
206
|
-
|
|
207
|
-
```bash
|
|
208
|
-
npx makaron-cli music create "gentle piano, warm strings, cinematic"
|
|
209
|
-
npx makaron-cli music create --vocals --style "lo-fi" "rainy day vibes"
|
|
210
|
-
npx makaron-cli music status <taskId>
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
Options: `--vocals` (include vocals), `--style "genre"`
|
|
214
|
-
|
|
215
|
-
## Response Schema
|
|
216
|
-
|
|
217
|
-
```typescript
|
|
218
|
-
type MakaronRunResponse = {
|
|
219
|
-
id: string
|
|
220
|
-
status: "in_progress" | "completed" | "failed" | "aborted"
|
|
221
|
-
incomplete: boolean // true = keep polling
|
|
222
|
-
project_id: string
|
|
223
|
-
project_url: string
|
|
224
|
-
next_poll_after_ms?: number // suggested poll interval
|
|
225
|
-
output: MakaronOutput[]
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
type MakaronOutput =
|
|
229
|
-
| { id: string; type: "text"; status: "completed"; content: string }
|
|
230
|
-
| { id: string; type: "image"; status: "completed"; url: string; snapshot_id: string }
|
|
231
|
-
| { id: string; type: "design"; status: "completed"; url: string; width: number; height: number; animated: boolean; duration?: number }
|
|
232
|
-
| { id: string; type: "video"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; snapshot_id?: string; url?: string; elapsed_seconds?: number; width?: number; height?: number }
|
|
233
|
-
| { id: string; type: "music"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
## Polling Rules
|
|
237
|
-
|
|
238
|
-
1. Poll while `incomplete: true` or `status` is `"in_progress"`
|
|
239
|
-
2. Use `next_poll_after_ms` as interval (default 5000ms)
|
|
240
|
-
3. Stop when `status` is `"completed"`, `"failed"`, or `"aborted"`
|
|
241
|
-
4. Top-level `status: "completed"` means ALL artifacts are ready (including rendered videos)
|
|
242
|
-
|
|
243
|
-
## Exit Codes
|
|
244
|
-
|
|
245
|
-
| Code | Meaning |
|
|
246
|
-
|------|---------|
|
|
247
|
-
| 0 | Success (completed) or valid in-progress response |
|
|
248
|
-
| 1 | Failed, aborted, or HTTP error |
|
|
249
|
-
| 2 | Timeout (partial response still printed to stdout) |
|
|
250
|
-
|
|
251
|
-
## What Makaron Agent Can Do
|
|
252
|
-
|
|
253
|
-
| Task | Example prompt |
|
|
254
|
-
|------|---------------|
|
|
255
|
-
| Edit photo | "make it cinematic with warm tones" |
|
|
256
|
-
| Style transfer | "convert to oil painting style" |
|
|
257
|
-
| Add/remove elements | "add a cat on the table" / "remove background person" |
|
|
258
|
-
| Text-to-image | "generate a cyberpunk cityscape" |
|
|
259
|
-
| Video from image | "create a 5 second video of her walking" |
|
|
260
|
-
| Video with model | "use seedance model, make a 5s video" |
|
|
261
|
-
| Background music | "add calm piano music" |
|
|
262
|
-
| Motion design | "create an Instagram story with animated text" |
|
|
263
|
-
| Multi-step | "edit the photo then make a video from it" |
|
|
264
|
-
|
|
265
|
-
## Recommended Pattern: Service Flow (Feishu/OpenClaw/Group Chat)
|
|
266
|
-
|
|
267
|
-
When serving end-users in a chat environment (Feishu, Slack, Discord), use this proactive message pattern:
|
|
268
|
-
|
|
269
|
-
```bash
|
|
270
|
-
# 1. Immediately acknowledge the user
|
|
271
|
-
send_message "Got it! Working on it now..."
|
|
272
|
-
|
|
273
|
-
# 2. Create project + submit (one command)
|
|
274
|
-
RUN_ID=$(npx makaron-cli chat --project auto --image photo.jpg -b "make it cinematic and create a 5s video")
|
|
275
|
-
|
|
276
|
-
# 3. Send project link proactively
|
|
277
|
-
PROJECT_URL=$(npx makaron-cli responses get $RUN_ID --pick project_url)
|
|
278
|
-
send_message "Project created: $PROJECT_URL"
|
|
279
|
-
|
|
280
|
-
# 4. Wait for the final customer-ready result
|
|
281
|
-
RESULT=$(npx makaron-cli responses get $RUN_ID --wait --json)
|
|
282
|
-
IMAGE_URLS=$(echo "$RESULT" | jq -r '[.result.images[]?.imageUrl, .output[]? | select(.type == "image") | .url] | map(select(. != null)) | unique | .[]')
|
|
283
|
-
VIDEO_URLS=$(echo "$RESULT" | jq -r '[.result.videos[]?.videoUrl, .output[]? | select(.type == "video") | .url] | map(select(. != null)) | unique | .[]')
|
|
284
|
-
|
|
285
|
-
for URL in $IMAGE_URLS; do
|
|
286
|
-
send_image "$URL"
|
|
287
|
-
done
|
|
288
|
-
for URL in $VIDEO_URLS; do
|
|
289
|
-
send_video "$URL"
|
|
290
|
-
done
|
|
291
|
-
send_message "All done!"
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
**Key principles for service agents:**
|
|
295
|
-
- **Proactive, not silent**: Acknowledge immediately, send the project link early, then send the final customer-ready media when the run completes.
|
|
296
|
-
- **Media over links**: When possible, send images/videos as native media in the chat (download URL and upload as attachment), not just paste the URL.
|
|
297
|
-
- **Immediate acknowledgment**: Reply within 1 second of receiving user request. Don't make users wait for project creation.
|
|
298
|
-
- **Project link early**: Send the project URL right after creation so users can check anytime.
|
|
299
|
-
- **Use `get --wait --json` as the default service path**: reserve `watch --jsonl` for advanced streaming or debugging integrations that explicitly need incremental events.
|
|
300
|
-
|
|
301
|
-
## Important Notes
|
|
302
|
-
|
|
303
|
-
- One project = one conversation thread. All history is preserved.
|
|
304
|
-
- One run at a time per project. New message interrupts previous run.
|
|
305
|
-
- Multi-image: `create --image a.jpg --image b.jpg` or `chat --image ref.jpg`.
|
|
306
|
-
- Videos take 2-5 minutes to render. Use `responses get <runId> --wait --json` for the default customer-service path.
|
|
307
|
-
- Music takes ~60 seconds. Appears in output when done.
|
|
308
|
-
- Images are typically ready in 15-30 seconds.
|
|
309
|
-
- stdout is always machine-readable JSON/text. Human-friendly logs go to stderr.
|
|
310
|
-
- Always use `chat` as the primary interface — even for single image edits.
|
|
311
|
-
- `edit`/`video`/`music` are fallback tools for when `chat` is unavailable or you need raw model access without project context.
|