makaron-cli 0.4.0 → 0.5.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/README.md +115 -204
- package/SKILL.md +197 -0
- package/bin/makaron.mjs +100 -13
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,287 +1,198 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Makaron CLI — Agent Integration Skill
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
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.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Install
|
|
5
|
+
## Setup
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
|
-
npx makaron-cli # run directly (always latest)
|
|
11
|
-
# or
|
|
12
8
|
npm install -g makaron-cli
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
## Authentication
|
|
16
|
-
|
|
17
|
-
### API Key (recommended for agents)
|
|
9
|
+
# or use directly: npx makaron-cli
|
|
18
10
|
|
|
19
|
-
Set the `MAKARON_API_KEY` environment variable. That's it — no login step needed.
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
11
|
export MAKARON_API_KEY=mk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
23
|
-
npx makaron-cli list
|
|
24
12
|
```
|
|
25
13
|
|
|
26
|
-
|
|
14
|
+
Verify: `npx makaron-cli list` should show projects.
|
|
27
15
|
|
|
28
|
-
|
|
16
|
+
## Core Workflow
|
|
29
17
|
|
|
30
18
|
```bash
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
```
|
|
19
|
+
# 1. Create project with image(s)
|
|
20
|
+
PROJECT_ID=$(npx makaron-cli create --image photo.jpg 2>/dev/null | grep "ID:" | awk '{print $2}')
|
|
34
21
|
|
|
35
|
-
|
|
22
|
+
# Multi-image project:
|
|
23
|
+
PROJECT_ID=$(npx makaron-cli create --image img1.jpg --image img2.jpg 2>/dev/null | grep "ID:" | awk '{print $2}')
|
|
36
24
|
|
|
37
|
-
|
|
25
|
+
# 2. Submit creative request (non-blocking)
|
|
26
|
+
RUN_ID=$(npx makaron-cli chat --project $PROJECT_ID -b "make it cinematic and create a 5s video")
|
|
38
27
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
This is the main command. The Agent can edit images, generate videos, compose music, and create designs.
|
|
42
|
-
|
|
43
|
-
**Default mode (non-blocking poll):**
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
npx makaron-cli chat --project <id> "make it look cinematic"
|
|
28
|
+
# 3. Watch until all artifacts are ready
|
|
29
|
+
npx makaron-cli responses watch $RUN_ID --jsonl
|
|
47
30
|
```
|
|
48
31
|
|
|
49
|
-
|
|
32
|
+
## Primary: `chat` (Agent-driven creative work)
|
|
50
33
|
|
|
51
|
-
|
|
34
|
+
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.
|
|
35
|
+
|
|
36
|
+
### Submit a request
|
|
52
37
|
|
|
53
38
|
```bash
|
|
54
|
-
npx makaron-cli chat --project <id> --
|
|
55
|
-
|
|
39
|
+
npx makaron-cli chat --project <id> --json -b "<prompt>"
|
|
40
|
+
```
|
|
56
41
|
|
|
57
|
-
|
|
58
|
-
|
|
42
|
+
Returns immediately:
|
|
43
|
+
```json
|
|
44
|
+
{"runId": "xxx", "projectId": "...", "projectUrl": "https://www.makaron.app/projects/...", "status": "running"}
|
|
59
45
|
```
|
|
60
46
|
|
|
61
|
-
|
|
47
|
+
### With additional images
|
|
62
48
|
|
|
63
49
|
```bash
|
|
64
|
-
|
|
65
|
-
|
|
50
|
+
# Add reference images to project before chatting
|
|
51
|
+
npx makaron-cli chat --project <id> --image ref1.jpg --image ref2.jpg -b "use these as style reference"
|
|
66
52
|
```
|
|
67
53
|
|
|
68
|
-
|
|
54
|
+
### Check status (single query)
|
|
69
55
|
|
|
70
56
|
```bash
|
|
71
|
-
npx makaron-cli
|
|
57
|
+
npx makaron-cli responses get <runId> --json
|
|
72
58
|
```
|
|
73
59
|
|
|
74
|
-
|
|
75
|
-
- `--project <id>` — target project (required)
|
|
76
|
-
- `--image <file>` — upload image to project before chatting
|
|
77
|
-
- `--background` / `-b` — submit and exit, print runId
|
|
78
|
-
- `--json` — structured JSON output
|
|
79
|
-
- `--stream` — legacy real-time SSE mode
|
|
80
|
-
- `--video-model kling|seedance` — preferred video model
|
|
81
|
-
- `--model <name>` — preferred image model
|
|
82
|
-
|
|
83
|
-
### `responses` — Query run status and results
|
|
60
|
+
### Watch until done (streaming events)
|
|
84
61
|
|
|
85
62
|
```bash
|
|
86
|
-
|
|
87
|
-
npx makaron-cli responses get <runId>
|
|
88
|
-
|
|
89
|
-
# Poll until completed
|
|
90
|
-
npx makaron-cli responses get <runId> --wait
|
|
91
|
-
|
|
92
|
-
# Wait for video/music rendering too (not just Agent completion)
|
|
93
|
-
npx makaron-cli responses get <runId> --wait --wait-artifacts
|
|
94
|
-
|
|
95
|
-
# List runs for a project
|
|
96
|
-
npx makaron-cli responses list --project <id>
|
|
63
|
+
npx makaron-cli responses watch <runId> --jsonl
|
|
97
64
|
```
|
|
98
65
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
```json
|
|
102
|
-
{
|
|
103
|
-
"runId": "...",
|
|
104
|
-
"projectId": "...",
|
|
105
|
-
"status": "completed",
|
|
106
|
-
"eventCount": 31,
|
|
107
|
-
"result": {
|
|
108
|
-
"text": "Agent's text response...",
|
|
109
|
-
"images": [{ "snapshotId": "...", "imageUrl": "https://..." }],
|
|
110
|
-
"designs": [{
|
|
111
|
-
"snapshotId": "...",
|
|
112
|
-
"imageUrl": "https://...",
|
|
113
|
-
"width": 1080,
|
|
114
|
-
"height": 1920,
|
|
115
|
-
"animation": { "fps": 30, "durationInSeconds": 3 },
|
|
116
|
-
"props": { "title": "..." },
|
|
117
|
-
"code": "function Design(props) { ... }"
|
|
118
|
-
}],
|
|
119
|
-
"videos": [{ "taskId": "...", "status": "completed", "videoUrl": "https://..." }],
|
|
120
|
-
"music": [{ "taskId": "...", "status": "completed", "audioUrl": "https://..." }]
|
|
121
|
-
}
|
|
122
|
-
}
|
|
66
|
+
Outputs one JSON per line as artifacts appear:
|
|
123
67
|
```
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
npx makaron-cli list
|
|
68
|
+
{"event":"output.added","item":{"id":"out_1","type":"image","status":"completed","url":"https://..."}}
|
|
69
|
+
{"event":"output.added","item":{"id":"out_2","type":"video","status":"rendering","task_id":"xxx"}}
|
|
70
|
+
{"event":"output.updated","item":{"id":"out_2","type":"video","status":"completed","url":"https://..."}}
|
|
71
|
+
{"event":"done","status":"completed"}
|
|
129
72
|
```
|
|
130
73
|
|
|
131
|
-
###
|
|
74
|
+
### Extract specific results
|
|
132
75
|
|
|
133
76
|
```bash
|
|
134
|
-
|
|
135
|
-
npx makaron-cli
|
|
136
|
-
npx makaron-cli
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
npx makaron-cli
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
npx makaron-cli create --title "My New Project"
|
|
77
|
+
npx makaron-cli responses get <runId> --pick first_image_url
|
|
78
|
+
npx makaron-cli responses get <runId> --pick image_urls # all images (JSON array)
|
|
79
|
+
npx makaron-cli responses get <runId> --pick first_video_url
|
|
80
|
+
npx makaron-cli responses get <runId> --pick video_urls # all videos
|
|
81
|
+
npx makaron-cli responses get <runId> --pick project_url
|
|
82
|
+
npx makaron-cli responses get <runId> --pick text # agent's text reply
|
|
83
|
+
npx makaron-cli responses get <runId> --pick output # full output array
|
|
84
|
+
npx makaron-cli responses get <runId> --pick status
|
|
143
85
|
```
|
|
144
86
|
|
|
145
|
-
|
|
87
|
+
## Fallback: Direct tool calls (no project context)
|
|
146
88
|
|
|
147
|
-
|
|
148
|
-
npx makaron-cli abort <runId>
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### `edit` — AI image editing (direct MCP tool call)
|
|
89
|
+
Use these only when `chat` is unavailable or you need raw model access without project/conversation context.
|
|
152
90
|
|
|
153
|
-
|
|
91
|
+
### `edit` — One-shot image editing
|
|
154
92
|
|
|
155
93
|
```bash
|
|
156
|
-
# Text-to-image (no input image)
|
|
157
|
-
npx makaron-cli edit "a cyberpunk cityscape at night, neon reflections"
|
|
158
|
-
|
|
159
94
|
# Edit an existing image
|
|
160
95
|
npx makaron-cli edit --image photo.jpg "add cinematic warm lighting"
|
|
161
96
|
|
|
162
|
-
#
|
|
163
|
-
npx makaron-cli edit
|
|
97
|
+
# Text-to-image (no input)
|
|
98
|
+
npx makaron-cli edit "a cyberpunk cityscape at night"
|
|
164
99
|
|
|
165
|
-
# With reference
|
|
166
|
-
npx makaron-cli edit --image photo.jpg --
|
|
100
|
+
# With model/skill/reference
|
|
101
|
+
npx makaron-cli edit --image photo.jpg --model openai --skill captions "add title"
|
|
102
|
+
npx makaron-cli edit --image photo.jpg --ref style.jpg "match this style"
|
|
167
103
|
|
|
168
|
-
#
|
|
169
|
-
npx makaron-cli edit --
|
|
104
|
+
# Output to file
|
|
105
|
+
npx makaron-cli edit --image photo.jpg --out result.jpg "make it dramatic"
|
|
170
106
|
```
|
|
171
107
|
|
|
172
|
-
Options:
|
|
173
|
-
- `--image <file|url>` — input image (omit for text-to-image)
|
|
174
|
-
- `--model gemini|qwen|openai|pony|wai` — model selection (default: auto)
|
|
175
|
-
- `--skill enhance|creative|wild|captions` — activate skill template
|
|
176
|
-
- `--ref <file|url>` — reference image (repeatable, up to 3)
|
|
177
|
-
- `--aspect <ratio>` — target aspect ratio (e.g. `4:5`, `1:1`, `16:9`)
|
|
178
|
-
- `--out <path>` — output file path (default: `makaron-output-{timestamp}.jpg`)
|
|
108
|
+
Options: `--image`, `--model gemini|qwen|openai|pony|wai`, `--skill enhance|creative|wild|captions`, `--ref <file>` (up to 3), `--aspect <ratio>`, `--out <path>`
|
|
179
109
|
|
|
180
|
-
### `video` — Video generation
|
|
110
|
+
### `video` — Video generation (3 steps)
|
|
181
111
|
|
|
182
112
|
```bash
|
|
183
|
-
# Write
|
|
184
|
-
npx makaron-cli video script --image img1.jpg
|
|
113
|
+
# 1. Write script from images
|
|
114
|
+
npx makaron-cli video script --image img1.jpg "cinematic story"
|
|
185
115
|
|
|
186
|
-
# Submit
|
|
187
|
-
npx makaron-cli video create --script "Shot 1..." --image https://...
|
|
116
|
+
# 2. Submit rendering (images must be public URLs from step 1 or uploaded)
|
|
117
|
+
npx makaron-cli video create --script "Shot 1 (5s): <<<image_1>>> ..." --image https://...jpg --duration 5 --model kling
|
|
188
118
|
|
|
189
|
-
# Check status
|
|
119
|
+
# 3. Check status
|
|
190
120
|
npx makaron-cli video status <taskId>
|
|
191
121
|
```
|
|
192
122
|
|
|
123
|
+
Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to 7), `--duration 3|5|7|10|15`, `--aspect 9:16|16:9|1:1`, `--model kling|seedance`
|
|
124
|
+
|
|
193
125
|
### `music` — Music generation
|
|
194
126
|
|
|
195
127
|
```bash
|
|
196
|
-
# Generate instrumental music
|
|
197
128
|
npx makaron-cli music create "gentle piano, warm strings, cinematic"
|
|
198
|
-
|
|
199
|
-
# With vocals and style
|
|
200
|
-
npx makaron-cli music create --vocals --style "lo-fi, ambient" "rainy day vibes"
|
|
201
|
-
|
|
202
|
-
# Check status
|
|
129
|
+
npx makaron-cli music create --vocals --style "lo-fi" "rainy day vibes"
|
|
203
130
|
npx makaron-cli music status <taskId>
|
|
204
131
|
```
|
|
205
132
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
For AI agents (Claude Code, OpenClaw, etc.) calling this CLI programmatically:
|
|
209
|
-
|
|
210
|
-
### Non-blocking workflow (recommended)
|
|
211
|
-
|
|
212
|
-
```bash
|
|
213
|
-
# 1. Submit task — returns immediately
|
|
214
|
-
RUN_ID=$(npx makaron-cli chat --project $PROJECT_ID --background "make a 5s video")
|
|
215
|
-
|
|
216
|
-
# 2. Do other work while Makaron processes...
|
|
217
|
-
|
|
218
|
-
# 3. Poll for result when ready
|
|
219
|
-
npx makaron-cli responses get $RUN_ID --wait
|
|
220
|
-
```
|
|
133
|
+
Options: `--vocals` (include vocals), `--style "genre"`
|
|
221
134
|
|
|
222
|
-
|
|
135
|
+
## Response Schema
|
|
223
136
|
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
137
|
+
```typescript
|
|
138
|
+
type MakaronRunResponse = {
|
|
139
|
+
id: string
|
|
140
|
+
status: "in_progress" | "completed" | "failed" | "aborted"
|
|
141
|
+
incomplete: boolean // true = keep polling
|
|
142
|
+
project_id: string
|
|
143
|
+
project_url: string
|
|
144
|
+
next_poll_after_ms?: number // suggested poll interval
|
|
145
|
+
output: MakaronOutput[]
|
|
146
|
+
}
|
|
228
147
|
|
|
229
|
-
|
|
230
|
-
|
|
148
|
+
type MakaronOutput =
|
|
149
|
+
| { id: string; type: "text"; status: "completed"; content: string }
|
|
150
|
+
| { id: string; type: "image"; status: "completed"; url: string; snapshot_id: string }
|
|
151
|
+
| { id: string; type: "design"; status: "completed"; url: string; width: number; height: number; animated: boolean; duration?: number }
|
|
152
|
+
| { id: string; type: "video"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
|
|
153
|
+
| { id: string; type: "music"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
|
|
231
154
|
```
|
|
232
155
|
|
|
233
|
-
|
|
156
|
+
## Polling Rules
|
|
234
157
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
158
|
+
1. Poll while `incomplete: true` or `status` is `"in_progress"`
|
|
159
|
+
2. Use `next_poll_after_ms` as interval (default 5000ms)
|
|
160
|
+
3. Stop when `status` is `"completed"`, `"failed"`, or `"aborted"`
|
|
161
|
+
4. Top-level `status: "completed"` means ALL artifacts are ready (including rendered videos)
|
|
239
162
|
|
|
240
|
-
|
|
241
|
-
npx makaron-cli chat --project $ID "now make a 5s video from this"
|
|
242
|
-
```
|
|
163
|
+
## Exit Codes
|
|
243
164
|
|
|
244
|
-
|
|
165
|
+
| Code | Meaning |
|
|
166
|
+
|------|---------|
|
|
167
|
+
| 0 | Success (completed) or valid in-progress response |
|
|
168
|
+
| 1 | Failed, aborted, or HTTP error |
|
|
169
|
+
| 2 | Timeout (partial response still printed to stdout) |
|
|
245
170
|
|
|
246
|
-
|
|
171
|
+
## What Makaron Agent Can Do
|
|
247
172
|
|
|
248
|
-
|
|
|
249
|
-
|
|
250
|
-
|
|
|
173
|
+
| Task | Example prompt |
|
|
174
|
+
|------|---------------|
|
|
175
|
+
| Edit photo | "make it cinematic with warm tones" |
|
|
251
176
|
| Style transfer | "convert to oil painting style" |
|
|
252
|
-
| Add elements | "add a cat
|
|
253
|
-
|
|
|
254
|
-
|
|
|
255
|
-
| Video
|
|
256
|
-
| Video with script | "make a video: camera slowly zooms in while leaves fall" |
|
|
177
|
+
| Add/remove elements | "add a cat on the table" / "remove background person" |
|
|
178
|
+
| Text-to-image | "generate a cyberpunk cityscape" |
|
|
179
|
+
| Video from image | "create a 5 second video of her walking" |
|
|
180
|
+
| Video with model | "用seedance模型做5秒视频" |
|
|
257
181
|
| Background music | "add calm piano music" |
|
|
258
|
-
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
| Variable | Purpose | Default |
|
|
274
|
-
|----------|---------|---------|
|
|
275
|
-
| `MAKARON_API_KEY` | API key (`mk_live_xxx`) | — |
|
|
276
|
-
| `MAKARON_URL` | API endpoint override | `https://www.makaron.app` |
|
|
277
|
-
|
|
278
|
-
## Viewing results
|
|
279
|
-
|
|
280
|
-
All generated content (images, videos, designs) is saved to the project and visible at:
|
|
281
|
-
|
|
282
|
-
```
|
|
283
|
-
https://www.makaron.app/projects/<project-id>
|
|
284
|
-
```
|
|
182
|
+
| Motion design | "create an Instagram story with animated text" |
|
|
183
|
+
| Multi-step | "edit the photo then make a video from it" |
|
|
184
|
+
|
|
185
|
+
## Important Notes
|
|
186
|
+
|
|
187
|
+
- One project = one conversation thread. All history is preserved.
|
|
188
|
+
- One run at a time per project. New message interrupts previous run.
|
|
189
|
+
- Multi-image: `create --image a.jpg --image b.jpg` or `chat --image ref.jpg`.
|
|
190
|
+
- Videos take 2-5 minutes to render. Use `watch` to get URL when ready.
|
|
191
|
+
- Music takes ~60 seconds. Appears in output when done.
|
|
192
|
+
- Images are typically ready in 15-30 seconds.
|
|
193
|
+
- stdout is always machine-readable JSON/text. Human-friendly logs go to stderr.
|
|
194
|
+
- Always use `chat` as the primary interface — even for single image edits.
|
|
195
|
+
- `edit`/`video`/`music` are fallback tools for when `chat` is unavailable or you need raw model access without project context.
|
|
285
196
|
|
|
286
197
|
## Admin: Skill Marketplace Operations
|
|
287
198
|
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
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 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.
|
|
9
|
+
|
|
10
|
+
## Setup
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
export MAKARON_API_KEY=mk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Verify: `npx makaron-cli list` should show projects.
|
|
17
|
+
|
|
18
|
+
## Core Workflow
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# 1. Create project with image(s)
|
|
22
|
+
PROJECT_ID=$(npx makaron-cli create --image photo.jpg 2>/dev/null | grep "ID:" | awk '{print $2}')
|
|
23
|
+
|
|
24
|
+
# Multi-image project:
|
|
25
|
+
PROJECT_ID=$(npx makaron-cli create --image img1.jpg --image img2.jpg 2>/dev/null | grep "ID:" | awk '{print $2}')
|
|
26
|
+
|
|
27
|
+
# 2. Submit creative request (non-blocking)
|
|
28
|
+
RUN_ID=$(npx makaron-cli chat --project $PROJECT_ID -b "make it cinematic and create a 5s video")
|
|
29
|
+
|
|
30
|
+
# 3. Watch until all artifacts are ready
|
|
31
|
+
npx makaron-cli responses watch $RUN_ID --jsonl
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Primary: `chat` (Agent-driven creative work)
|
|
35
|
+
|
|
36
|
+
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.
|
|
37
|
+
|
|
38
|
+
### Submit a request
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx makaron-cli chat --project <id> --json -b "<prompt>"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Returns immediately:
|
|
45
|
+
```json
|
|
46
|
+
{"runId": "xxx", "projectId": "...", "projectUrl": "https://www.makaron.app/projects/...", "status": "running"}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### With additional images
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Add reference images to project before chatting
|
|
53
|
+
npx makaron-cli chat --project <id> --image ref1.jpg --image ref2.jpg -b "use these as style reference"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Check status (single query)
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npx makaron-cli responses get <runId> --json
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Watch until done (streaming events)
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npx makaron-cli responses watch <runId> --jsonl
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Outputs one JSON per line as artifacts appear:
|
|
69
|
+
```
|
|
70
|
+
{"event":"output.added","item":{"id":"out_1","type":"image","status":"completed","url":"https://..."}}
|
|
71
|
+
{"event":"output.added","item":{"id":"out_2","type":"video","status":"rendering","task_id":"xxx"}}
|
|
72
|
+
{"event":"output.updated","item":{"id":"out_2","type":"video","status":"completed","url":"https://..."}}
|
|
73
|
+
{"event":"done","status":"completed"}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Extract specific results
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npx makaron-cli responses get <runId> --pick first_image_url
|
|
80
|
+
npx makaron-cli responses get <runId> --pick image_urls # all images (JSON array)
|
|
81
|
+
npx makaron-cli responses get <runId> --pick first_video_url
|
|
82
|
+
npx makaron-cli responses get <runId> --pick video_urls # all videos
|
|
83
|
+
npx makaron-cli responses get <runId> --pick project_url
|
|
84
|
+
npx makaron-cli responses get <runId> --pick text # agent's text reply
|
|
85
|
+
npx makaron-cli responses get <runId> --pick output # full output array
|
|
86
|
+
npx makaron-cli responses get <runId> --pick status
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Fallback: Direct tool calls (no project context)
|
|
90
|
+
|
|
91
|
+
Use these only when `chat` is unavailable or you need raw model access without project/conversation context.
|
|
92
|
+
|
|
93
|
+
### `edit` — One-shot image editing
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# Edit an existing image
|
|
97
|
+
npx makaron-cli edit --image photo.jpg "add cinematic warm lighting"
|
|
98
|
+
|
|
99
|
+
# Text-to-image (no input)
|
|
100
|
+
npx makaron-cli edit "a cyberpunk cityscape at night"
|
|
101
|
+
|
|
102
|
+
# With model/skill/reference
|
|
103
|
+
npx makaron-cli edit --image photo.jpg --model openai --skill captions "add title"
|
|
104
|
+
npx makaron-cli edit --image photo.jpg --ref style.jpg "match this style"
|
|
105
|
+
|
|
106
|
+
# Output to file
|
|
107
|
+
npx makaron-cli edit --image photo.jpg --out result.jpg "make it dramatic"
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Options: `--image`, `--model gemini|qwen|openai|pony|wai`, `--skill enhance|creative|wild|captions`, `--ref <file>` (up to 3), `--aspect <ratio>`, `--out <path>`
|
|
111
|
+
|
|
112
|
+
### `video` — Video generation (3 steps)
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# 1. Write script from images
|
|
116
|
+
npx makaron-cli video script --image img1.jpg "cinematic story"
|
|
117
|
+
|
|
118
|
+
# 2. Submit rendering (images must be public URLs from step 1 or uploaded)
|
|
119
|
+
npx makaron-cli video create --script "Shot 1 (5s): <<<image_1>>> ..." --image https://...jpg --duration 5 --model kling
|
|
120
|
+
|
|
121
|
+
# 3. Check status
|
|
122
|
+
npx makaron-cli video status <taskId>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to 7), `--duration 3|5|7|10|15`, `--aspect 9:16|16:9|1:1`, `--model kling|seedance`
|
|
126
|
+
|
|
127
|
+
### `music` — Music generation
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npx makaron-cli music create "gentle piano, warm strings, cinematic"
|
|
131
|
+
npx makaron-cli music create --vocals --style "lo-fi" "rainy day vibes"
|
|
132
|
+
npx makaron-cli music status <taskId>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Options: `--vocals` (include vocals), `--style "genre"`
|
|
136
|
+
|
|
137
|
+
## Response Schema
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
type MakaronRunResponse = {
|
|
141
|
+
id: string
|
|
142
|
+
status: "in_progress" | "completed" | "failed" | "aborted"
|
|
143
|
+
incomplete: boolean // true = keep polling
|
|
144
|
+
project_id: string
|
|
145
|
+
project_url: string
|
|
146
|
+
next_poll_after_ms?: number // suggested poll interval
|
|
147
|
+
output: MakaronOutput[]
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
type MakaronOutput =
|
|
151
|
+
| { id: string; type: "text"; status: "completed"; content: string }
|
|
152
|
+
| { id: string; type: "image"; status: "completed"; url: string; snapshot_id: string }
|
|
153
|
+
| { id: string; type: "design"; status: "completed"; url: string; width: number; height: number; animated: boolean; duration?: number }
|
|
154
|
+
| { id: string; type: "video"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
|
|
155
|
+
| { id: string; type: "music"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Polling Rules
|
|
159
|
+
|
|
160
|
+
1. Poll while `incomplete: true` or `status` is `"in_progress"`
|
|
161
|
+
2. Use `next_poll_after_ms` as interval (default 5000ms)
|
|
162
|
+
3. Stop when `status` is `"completed"`, `"failed"`, or `"aborted"`
|
|
163
|
+
4. Top-level `status: "completed"` means ALL artifacts are ready (including rendered videos)
|
|
164
|
+
|
|
165
|
+
## Exit Codes
|
|
166
|
+
|
|
167
|
+
| Code | Meaning |
|
|
168
|
+
|------|---------|
|
|
169
|
+
| 0 | Success (completed) or valid in-progress response |
|
|
170
|
+
| 1 | Failed, aborted, or HTTP error |
|
|
171
|
+
| 2 | Timeout (partial response still printed to stdout) |
|
|
172
|
+
|
|
173
|
+
## What Makaron Agent Can Do
|
|
174
|
+
|
|
175
|
+
| Task | Example prompt |
|
|
176
|
+
|------|---------------|
|
|
177
|
+
| Edit photo | "make it cinematic with warm tones" |
|
|
178
|
+
| Style transfer | "convert to oil painting style" |
|
|
179
|
+
| Add/remove elements | "add a cat on the table" / "remove background person" |
|
|
180
|
+
| Text-to-image | "generate a cyberpunk cityscape" |
|
|
181
|
+
| Video from image | "create a 5 second video of her walking" |
|
|
182
|
+
| Video with model | "用seedance模型做5秒视频" |
|
|
183
|
+
| Background music | "add calm piano music" |
|
|
184
|
+
| Motion design | "create an Instagram story with animated text" |
|
|
185
|
+
| Multi-step | "edit the photo then make a video from it" |
|
|
186
|
+
|
|
187
|
+
## Important Notes
|
|
188
|
+
|
|
189
|
+
- One project = one conversation thread. All history is preserved.
|
|
190
|
+
- One run at a time per project. New message interrupts previous run.
|
|
191
|
+
- Multi-image: `create --image a.jpg --image b.jpg` or `chat --image ref.jpg`.
|
|
192
|
+
- Videos take 2-5 minutes to render. Use `watch` to get URL when ready.
|
|
193
|
+
- Music takes ~60 seconds. Appears in output when done.
|
|
194
|
+
- Images are typically ready in 15-30 seconds.
|
|
195
|
+
- stdout is always machine-readable JSON/text. Human-friendly logs go to stderr.
|
|
196
|
+
- Always use `chat` as the primary interface — even for single image edits.
|
|
197
|
+
- `edit`/`video`/`music` are fallback tools for when `chat` is unavailable or you need raw model access without project context.
|
package/bin/makaron.mjs
CHANGED
|
@@ -360,6 +360,79 @@ async function pollRun(baseUrl, headers, runId, opts = {}) {
|
|
|
360
360
|
}
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
+
// ─── Pick Helper ────────────────────────────────────────────────────────────
|
|
364
|
+
|
|
365
|
+
function applyPick(data, field) {
|
|
366
|
+
switch (field) {
|
|
367
|
+
case 'first_image_url': return data.output?.find(o => o.type === 'image')?.url || null;
|
|
368
|
+
case 'image_urls': return (data.output || []).filter(o => o.type === 'image' && o.url).map(o => o.url);
|
|
369
|
+
case 'first_video_url': return data.output?.find(o => o.type === 'video' && o.url)?.url || null;
|
|
370
|
+
case 'video_urls': return (data.output || []).filter(o => o.type === 'video' && o.url).map(o => o.url);
|
|
371
|
+
case 'first_design_url': return data.output?.find(o => o.type === 'design')?.url || null;
|
|
372
|
+
case 'design_urls': return (data.output || []).filter(o => o.type === 'design' && o.url).map(o => o.url);
|
|
373
|
+
case 'first_music_url': return data.output?.find(o => o.type === 'music' && o.url)?.url || null;
|
|
374
|
+
case 'music_urls': return (data.output || []).filter(o => o.type === 'music' && o.url).map(o => o.url);
|
|
375
|
+
case 'project_url': return data.project_url || null;
|
|
376
|
+
case 'output': return data.output || [];
|
|
377
|
+
case 'text': return data.output?.find(o => o.type === 'text')?.content || null;
|
|
378
|
+
case 'status': return data.status;
|
|
379
|
+
default: return data[field] !== undefined ? data[field] : null;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// ─── Watch (incremental event stream) ───────────────────────────────────────
|
|
384
|
+
|
|
385
|
+
async function watchRun(baseUrl, headers, runId, opts = {}) {
|
|
386
|
+
const { interval = 5000, jsonl = true } = opts;
|
|
387
|
+
let lastOutput = [];
|
|
388
|
+
const start = Date.now();
|
|
389
|
+
|
|
390
|
+
while (true) {
|
|
391
|
+
const elapsed = Math.round((Date.now() - start) / 1000);
|
|
392
|
+
let data;
|
|
393
|
+
try {
|
|
394
|
+
const res = await fetch(`${baseUrl}/api/agent/run/${runId}`, { headers });
|
|
395
|
+
if (!res.ok) {
|
|
396
|
+
if (elapsed > 800) { process.stderr.write(`Timeout after ${elapsed}s\n`); process.exit(2); }
|
|
397
|
+
await new Promise(r => setTimeout(r, interval));
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
data = await res.json();
|
|
401
|
+
} catch {
|
|
402
|
+
await new Promise(r => setTimeout(r, interval));
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const currentOutput = data.output || [];
|
|
407
|
+
|
|
408
|
+
// Diff: find new or updated items
|
|
409
|
+
for (const item of currentOutput) {
|
|
410
|
+
const prev = lastOutput.find(o => o.id === item.id);
|
|
411
|
+
if (!prev) {
|
|
412
|
+
// New item
|
|
413
|
+
if (jsonl) console.log(JSON.stringify({ event: 'output.added', item }));
|
|
414
|
+
else process.stderr.write(`+ [${item.type}] ${item.url || item.content?.slice(0, 60) || item.status}\n`);
|
|
415
|
+
} else if (JSON.stringify(prev) !== JSON.stringify(item)) {
|
|
416
|
+
// Updated item (e.g. video status changed)
|
|
417
|
+
if (jsonl) console.log(JSON.stringify({ event: 'output.updated', item }));
|
|
418
|
+
else process.stderr.write(`~ [${item.type}] ${item.status} ${item.url || ''}\n`);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
lastOutput = currentOutput;
|
|
423
|
+
|
|
424
|
+
// Check terminal status
|
|
425
|
+
if (!data.incomplete && (data.status === 'completed' || data.status === 'failed' || data.status === 'aborted')) {
|
|
426
|
+
if (jsonl) console.log(JSON.stringify({ event: 'done', status: data.status }));
|
|
427
|
+
if (data.status === 'failed' || data.status === 'aborted') process.exit(1);
|
|
428
|
+
process.exit(0);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const waitMs = data.next_poll_after_ms || interval;
|
|
432
|
+
await new Promise(r => setTimeout(r, waitMs));
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
363
436
|
// ─── Async Task Polling ──────────────────────────────────────────────────────
|
|
364
437
|
|
|
365
438
|
async function pollVideo(baseUrl, headers, taskId) {
|
|
@@ -605,34 +678,47 @@ if (command === 'login') {
|
|
|
605
678
|
|
|
606
679
|
if (sub === 'get') {
|
|
607
680
|
const runId = args[2];
|
|
608
|
-
if (!runId) { console.error('Usage: makaron responses get <runId> [--wait] [--json]'); process.exit(1); }
|
|
609
|
-
let wait = false, jsonOutput = false,
|
|
681
|
+
if (!runId) { console.error('Usage: makaron responses get <runId> [--wait] [--json] [--pick <field>]'); process.exit(1); }
|
|
682
|
+
let wait = false, jsonOutput = false, pick = null;
|
|
610
683
|
for (let i = 3; i < args.length; i++) {
|
|
611
684
|
if (args[i] === '--wait') wait = true;
|
|
612
685
|
if (args[i] === '--json') jsonOutput = true;
|
|
613
|
-
if (args[i] === '--
|
|
686
|
+
if (args[i] === '--pick' && args[i + 1]) pick = args[++i];
|
|
614
687
|
}
|
|
615
688
|
|
|
616
689
|
if (wait) {
|
|
617
|
-
await pollRun(baseUrl, headers, runId, { json:
|
|
690
|
+
await pollRun(baseUrl, headers, runId, { json: true });
|
|
618
691
|
} else {
|
|
619
|
-
const
|
|
620
|
-
if (
|
|
621
|
-
const res = await fetch(`${baseUrl}/api/agent/run/${runId}?${params}`, { headers });
|
|
622
|
-
if (!res.ok) { console.error(`Error ${res.status}:`, await res.text()); process.exit(1); }
|
|
692
|
+
const res = await fetch(`${baseUrl}/api/agent/run/${runId}`, { headers });
|
|
693
|
+
if (!res.ok) { process.stderr.write(`Error ${res.status}: ${await res.text()}\n`); process.exit(1); }
|
|
623
694
|
const data = await res.json();
|
|
624
|
-
|
|
695
|
+
if (pick) {
|
|
696
|
+
const picked = applyPick(data, pick);
|
|
697
|
+
if (picked !== undefined) console.log(typeof picked === 'string' ? picked : JSON.stringify(picked));
|
|
698
|
+
} else {
|
|
699
|
+
console.log(JSON.stringify(data, null, 2));
|
|
700
|
+
}
|
|
701
|
+
if (data.status === 'failed' || data.status === 'aborted') process.exit(1);
|
|
625
702
|
}
|
|
626
703
|
|
|
704
|
+
} else if (sub === 'watch') {
|
|
705
|
+
const runId = args[2];
|
|
706
|
+
if (!runId) { console.error('Usage: makaron responses watch <runId> [--jsonl] [--interval <ms>]'); process.exit(1); }
|
|
707
|
+
let interval = 5000, jsonl = false;
|
|
708
|
+
for (let i = 3; i < args.length; i++) {
|
|
709
|
+
if (args[i] === '--jsonl') jsonl = true;
|
|
710
|
+
if (args[i] === '--interval' && args[i + 1]) interval = parseInt(args[++i]);
|
|
711
|
+
}
|
|
712
|
+
await watchRun(baseUrl, headers, runId, { interval, jsonl });
|
|
713
|
+
|
|
627
714
|
} else if (sub === 'list') {
|
|
628
715
|
let projectId = null;
|
|
629
716
|
for (let i = 2; i < args.length; i++) {
|
|
630
717
|
if (args[i] === '--project' && args[i + 1]) projectId = args[++i];
|
|
631
718
|
}
|
|
632
719
|
if (!projectId) { console.error('Usage: makaron responses list --project <id>'); process.exit(1); }
|
|
633
|
-
// Query runs for project
|
|
634
720
|
const res = await fetch(`${baseUrl}/api/agent/run?projectId=${projectId}`, { headers });
|
|
635
|
-
if (!res.ok) {
|
|
721
|
+
if (!res.ok) { process.stderr.write(`Error ${res.status}: ${await res.text()}\n`); process.exit(1); }
|
|
636
722
|
const data = await res.json();
|
|
637
723
|
if (data.runs?.length) {
|
|
638
724
|
for (const r of data.runs) {
|
|
@@ -645,9 +731,10 @@ if (command === 'login') {
|
|
|
645
731
|
|
|
646
732
|
} else {
|
|
647
733
|
console.log(`Responses commands:
|
|
648
|
-
responses get <runId> Get
|
|
734
|
+
responses get <runId> Get status and output (JSON)
|
|
649
735
|
responses get <runId> --wait Poll until completed
|
|
650
|
-
responses get <runId> --
|
|
736
|
+
responses get <runId> --pick <field> Extract: first_image_url, first_video_url, project_url, output
|
|
737
|
+
responses watch <runId> --jsonl Watch until done (incremental events)
|
|
651
738
|
responses list --project <id> List runs for a project
|
|
652
739
|
`);
|
|
653
740
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "makaron-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Talk to Makaron Agent from the terminal — create projects, edit images, generate videos",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"makaron": "./bin/makaron.mjs"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
|
-
"bin/"
|
|
10
|
+
"bin/",
|
|
11
|
+
"SKILL.md",
|
|
12
|
+
"README.md"
|
|
11
13
|
],
|
|
12
14
|
"keywords": [
|
|
13
15
|
"makaron",
|