makaron-cli 0.4.0 → 0.5.1
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 +119 -204
- package/SKILL.md +201 -0
- package/bin/makaron.mjs +136 -16
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,287 +1,202 @@
|
|
|
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
|
+
# One-shot: create project + upload image + submit prompt — all in one command
|
|
20
|
+
RUN_ID=$(npx makaron-cli chat --project auto --image photo.jpg -b "make it cinematic and create a 5s video")
|
|
34
21
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
### `chat` — Send a message to Makaron Agent
|
|
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):**
|
|
22
|
+
# Watch until all artifacts are ready
|
|
23
|
+
npx makaron-cli responses watch $RUN_ID --jsonl
|
|
24
|
+
```
|
|
44
25
|
|
|
26
|
+
Or with an existing project:
|
|
45
27
|
```bash
|
|
46
|
-
npx makaron-cli chat --project
|
|
28
|
+
RUN_ID=$(npx makaron-cli chat --project $PROJECT_ID -b "make a 5s video")
|
|
29
|
+
npx makaron-cli responses watch $RUN_ID --jsonl
|
|
47
30
|
```
|
|
48
31
|
|
|
49
|
-
|
|
32
|
+
## Primary: `chat` (Agent-driven creative work)
|
|
33
|
+
|
|
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.
|
|
50
35
|
|
|
51
|
-
|
|
36
|
+
### Submit a request
|
|
52
37
|
|
|
53
38
|
```bash
|
|
54
|
-
|
|
55
|
-
|
|
39
|
+
# With existing project
|
|
40
|
+
npx makaron-cli chat --project <id> --json -b "<prompt>"
|
|
56
41
|
|
|
57
|
-
#
|
|
58
|
-
npx makaron-cli
|
|
42
|
+
# Auto-create project (with or without images)
|
|
43
|
+
npx makaron-cli chat --project auto --image photo.jpg --json -b "make it cinematic"
|
|
44
|
+
npx makaron-cli chat --project auto --image img1.jpg --image img2.jpg --json -b "combine these"
|
|
59
45
|
```
|
|
60
46
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
npx makaron-cli chat --project <id> --json --background "edit the photo"
|
|
65
|
-
# → {"runId":"...","projectId":"...","projectUrl":"...","status":"running"}
|
|
47
|
+
Returns immediately:
|
|
48
|
+
```json
|
|
49
|
+
{"runId": "xxx", "projectId": "...", "projectUrl": "https://www.makaron.app/projects/...", "status": "running"}
|
|
66
50
|
```
|
|
67
51
|
|
|
68
|
-
|
|
52
|
+
### With additional images (existing project)
|
|
69
53
|
|
|
70
54
|
```bash
|
|
71
|
-
npx makaron-cli chat --project <id> --
|
|
55
|
+
npx makaron-cli chat --project <id> --image ref1.jpg --image ref2.jpg -b "use these as style reference"
|
|
72
56
|
```
|
|
73
57
|
|
|
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
|
|
58
|
+
### Check status (single query)
|
|
84
59
|
|
|
85
60
|
```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>
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
**Result JSON structure:**
|
|
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
|
-
}
|
|
61
|
+
npx makaron-cli responses get <runId> --json
|
|
123
62
|
```
|
|
124
63
|
|
|
125
|
-
###
|
|
64
|
+
### Watch until done (streaming events)
|
|
126
65
|
|
|
127
66
|
```bash
|
|
128
|
-
npx makaron-cli
|
|
67
|
+
npx makaron-cli responses watch <runId> --jsonl
|
|
129
68
|
```
|
|
130
69
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
# From URL(s)
|
|
139
|
-
npx makaron-cli create --image-url https://example.com/photo.jpg
|
|
140
|
-
|
|
141
|
-
# Empty project (for text-to-image)
|
|
142
|
-
npx makaron-cli create --title "My New Project"
|
|
70
|
+
Outputs one JSON per line as artifacts appear:
|
|
71
|
+
```
|
|
72
|
+
{"event":"output.added","item":{"id":"out_1","type":"image","status":"completed","url":"https://..."}}
|
|
73
|
+
{"event":"output.added","item":{"id":"out_2","type":"video","status":"rendering","task_id":"xxx"}}
|
|
74
|
+
{"event":"output.updated","item":{"id":"out_2","type":"video","status":"completed","url":"https://..."}}
|
|
75
|
+
{"event":"done","status":"completed"}
|
|
143
76
|
```
|
|
144
77
|
|
|
145
|
-
###
|
|
78
|
+
### Extract specific results
|
|
146
79
|
|
|
147
80
|
```bash
|
|
148
|
-
npx makaron-cli
|
|
81
|
+
npx makaron-cli responses get <runId> --pick first_image_url
|
|
82
|
+
npx makaron-cli responses get <runId> --pick image_urls # all images (JSON array)
|
|
83
|
+
npx makaron-cli responses get <runId> --pick first_video_url
|
|
84
|
+
npx makaron-cli responses get <runId> --pick video_urls # all videos
|
|
85
|
+
npx makaron-cli responses get <runId> --pick project_url
|
|
86
|
+
npx makaron-cli responses get <runId> --pick text # agent's text reply
|
|
87
|
+
npx makaron-cli responses get <runId> --pick output # full output array
|
|
88
|
+
npx makaron-cli responses get <runId> --pick status
|
|
149
89
|
```
|
|
150
90
|
|
|
151
|
-
|
|
91
|
+
## Fallback: Direct tool calls (no project context)
|
|
152
92
|
|
|
153
|
-
|
|
93
|
+
Use these only when `chat` is unavailable or you need raw model access without project/conversation context.
|
|
154
94
|
|
|
155
|
-
|
|
156
|
-
# Text-to-image (no input image)
|
|
157
|
-
npx makaron-cli edit "a cyberpunk cityscape at night, neon reflections"
|
|
95
|
+
### `edit` — One-shot image editing
|
|
158
96
|
|
|
97
|
+
```bash
|
|
159
98
|
# Edit an existing image
|
|
160
99
|
npx makaron-cli edit --image photo.jpg "add cinematic warm lighting"
|
|
161
100
|
|
|
162
|
-
#
|
|
163
|
-
npx makaron-cli edit
|
|
101
|
+
# Text-to-image (no input)
|
|
102
|
+
npx makaron-cli edit "a cyberpunk cityscape at night"
|
|
164
103
|
|
|
165
|
-
# With reference
|
|
166
|
-
npx makaron-cli edit --image photo.jpg --
|
|
104
|
+
# With model/skill/reference
|
|
105
|
+
npx makaron-cli edit --image photo.jpg --model openai --skill captions "add title"
|
|
106
|
+
npx makaron-cli edit --image photo.jpg --ref style.jpg "match this style"
|
|
167
107
|
|
|
168
|
-
#
|
|
169
|
-
npx makaron-cli edit --
|
|
108
|
+
# Output to file
|
|
109
|
+
npx makaron-cli edit --image photo.jpg --out result.jpg "make it dramatic"
|
|
170
110
|
```
|
|
171
111
|
|
|
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`)
|
|
112
|
+
Options: `--image`, `--model gemini|qwen|openai|pony|wai`, `--skill enhance|creative|wild|captions`, `--ref <file>` (up to 3), `--aspect <ratio>`, `--out <path>`
|
|
179
113
|
|
|
180
|
-
### `video` — Video generation
|
|
114
|
+
### `video` — Video generation (3 steps)
|
|
181
115
|
|
|
182
116
|
```bash
|
|
183
|
-
# Write
|
|
184
|
-
npx makaron-cli video script --image img1.jpg
|
|
117
|
+
# 1. Write script from images
|
|
118
|
+
npx makaron-cli video script --image img1.jpg "cinematic story"
|
|
185
119
|
|
|
186
|
-
# Submit
|
|
187
|
-
npx makaron-cli video create --script "Shot 1..." --image https://...
|
|
120
|
+
# 2. Submit rendering (images must be public URLs from step 1 or uploaded)
|
|
121
|
+
npx makaron-cli video create --script "Shot 1 (5s): <<<image_1>>> ..." --image https://...jpg --duration 5 --model kling
|
|
188
122
|
|
|
189
|
-
# Check status
|
|
123
|
+
# 3. Check status
|
|
190
124
|
npx makaron-cli video status <taskId>
|
|
191
125
|
```
|
|
192
126
|
|
|
127
|
+
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`
|
|
128
|
+
|
|
193
129
|
### `music` — Music generation
|
|
194
130
|
|
|
195
131
|
```bash
|
|
196
|
-
# Generate instrumental music
|
|
197
132
|
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
|
|
133
|
+
npx makaron-cli music create --vocals --style "lo-fi" "rainy day vibes"
|
|
203
134
|
npx makaron-cli music status <taskId>
|
|
204
135
|
```
|
|
205
136
|
|
|
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
|
-
```
|
|
137
|
+
Options: `--vocals` (include vocals), `--style "genre"`
|
|
221
138
|
|
|
222
|
-
|
|
139
|
+
## Response Schema
|
|
223
140
|
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
141
|
+
```typescript
|
|
142
|
+
type MakaronRunResponse = {
|
|
143
|
+
id: string
|
|
144
|
+
status: "in_progress" | "completed" | "failed" | "aborted"
|
|
145
|
+
incomplete: boolean // true = keep polling
|
|
146
|
+
project_id: string
|
|
147
|
+
project_url: string
|
|
148
|
+
next_poll_after_ms?: number // suggested poll interval
|
|
149
|
+
output: MakaronOutput[]
|
|
150
|
+
}
|
|
228
151
|
|
|
229
|
-
|
|
230
|
-
|
|
152
|
+
type MakaronOutput =
|
|
153
|
+
| { id: string; type: "text"; status: "completed"; content: string }
|
|
154
|
+
| { id: string; type: "image"; status: "completed"; url: string; snapshot_id: string }
|
|
155
|
+
| { id: string; type: "design"; status: "completed"; url: string; width: number; height: number; animated: boolean; duration?: number }
|
|
156
|
+
| { id: string; type: "video"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
|
|
157
|
+
| { id: string; type: "music"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
|
|
231
158
|
```
|
|
232
159
|
|
|
233
|
-
|
|
160
|
+
## Polling Rules
|
|
234
161
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
162
|
+
1. Poll while `incomplete: true` or `status` is `"in_progress"`
|
|
163
|
+
2. Use `next_poll_after_ms` as interval (default 5000ms)
|
|
164
|
+
3. Stop when `status` is `"completed"`, `"failed"`, or `"aborted"`
|
|
165
|
+
4. Top-level `status: "completed"` means ALL artifacts are ready (including rendered videos)
|
|
239
166
|
|
|
240
|
-
|
|
241
|
-
npx makaron-cli chat --project $ID "now make a 5s video from this"
|
|
242
|
-
```
|
|
167
|
+
## Exit Codes
|
|
243
168
|
|
|
244
|
-
|
|
169
|
+
| Code | Meaning |
|
|
170
|
+
|------|---------|
|
|
171
|
+
| 0 | Success (completed) or valid in-progress response |
|
|
172
|
+
| 1 | Failed, aborted, or HTTP error |
|
|
173
|
+
| 2 | Timeout (partial response still printed to stdout) |
|
|
245
174
|
|
|
246
|
-
|
|
175
|
+
## What Makaron Agent Can Do
|
|
247
176
|
|
|
248
|
-
|
|
|
249
|
-
|
|
250
|
-
|
|
|
177
|
+
| Task | Example prompt |
|
|
178
|
+
|------|---------------|
|
|
179
|
+
| Edit photo | "make it cinematic with warm tones" |
|
|
251
180
|
| 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" |
|
|
181
|
+
| Add/remove elements | "add a cat on the table" / "remove background person" |
|
|
182
|
+
| Text-to-image | "generate a cyberpunk cityscape" |
|
|
183
|
+
| Video from image | "create a 5 second video of her walking" |
|
|
184
|
+
| Video with model | "用seedance模型做5秒视频" |
|
|
257
185
|
| 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
|
-
```
|
|
186
|
+
| Motion design | "create an Instagram story with animated text" |
|
|
187
|
+
| Multi-step | "edit the photo then make a video from it" |
|
|
188
|
+
|
|
189
|
+
## Important Notes
|
|
190
|
+
|
|
191
|
+
- One project = one conversation thread. All history is preserved.
|
|
192
|
+
- One run at a time per project. New message interrupts previous run.
|
|
193
|
+
- Multi-image: `create --image a.jpg --image b.jpg` or `chat --image ref.jpg`.
|
|
194
|
+
- Videos take 2-5 minutes to render. Use `watch` to get URL when ready.
|
|
195
|
+
- Music takes ~60 seconds. Appears in output when done.
|
|
196
|
+
- Images are typically ready in 15-30 seconds.
|
|
197
|
+
- stdout is always machine-readable JSON/text. Human-friendly logs go to stderr.
|
|
198
|
+
- Always use `chat` as the primary interface — even for single image edits.
|
|
199
|
+
- `edit`/`video`/`music` are fallback tools for when `chat` is unavailable or you need raw model access without project context.
|
|
285
200
|
|
|
286
201
|
## Admin: Skill Marketplace Operations
|
|
287
202
|
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
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
|
+
# One-shot: create project + upload image + submit prompt — all in one command
|
|
22
|
+
RUN_ID=$(npx makaron-cli chat --project auto --image photo.jpg -b "make it cinematic and create a 5s video")
|
|
23
|
+
|
|
24
|
+
# Watch until all artifacts are ready
|
|
25
|
+
npx makaron-cli responses watch $RUN_ID --jsonl
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or with an existing project:
|
|
29
|
+
```bash
|
|
30
|
+
RUN_ID=$(npx makaron-cli chat --project $PROJECT_ID -b "make a 5s video")
|
|
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
|
+
# With existing project
|
|
42
|
+
npx makaron-cli chat --project <id> --json -b "<prompt>"
|
|
43
|
+
|
|
44
|
+
# Auto-create project (with or without images)
|
|
45
|
+
npx makaron-cli chat --project auto --image photo.jpg --json -b "make it cinematic"
|
|
46
|
+
npx makaron-cli chat --project auto --image img1.jpg --image img2.jpg --json -b "combine these"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Returns immediately:
|
|
50
|
+
```json
|
|
51
|
+
{"runId": "xxx", "projectId": "...", "projectUrl": "https://www.makaron.app/projects/...", "status": "running"}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### With additional images (existing project)
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npx makaron-cli chat --project <id> --image ref1.jpg --image ref2.jpg -b "use these as style reference"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Check status (single query)
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npx makaron-cli responses get <runId> --json
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Watch until done (streaming events)
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npx makaron-cli responses watch <runId> --jsonl
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Outputs one JSON per line as artifacts appear:
|
|
73
|
+
```
|
|
74
|
+
{"event":"output.added","item":{"id":"out_1","type":"image","status":"completed","url":"https://..."}}
|
|
75
|
+
{"event":"output.added","item":{"id":"out_2","type":"video","status":"rendering","task_id":"xxx"}}
|
|
76
|
+
{"event":"output.updated","item":{"id":"out_2","type":"video","status":"completed","url":"https://..."}}
|
|
77
|
+
{"event":"done","status":"completed"}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Extract specific results
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npx makaron-cli responses get <runId> --pick first_image_url
|
|
84
|
+
npx makaron-cli responses get <runId> --pick image_urls # all images (JSON array)
|
|
85
|
+
npx makaron-cli responses get <runId> --pick first_video_url
|
|
86
|
+
npx makaron-cli responses get <runId> --pick video_urls # all videos
|
|
87
|
+
npx makaron-cli responses get <runId> --pick project_url
|
|
88
|
+
npx makaron-cli responses get <runId> --pick text # agent's text reply
|
|
89
|
+
npx makaron-cli responses get <runId> --pick output # full output array
|
|
90
|
+
npx makaron-cli responses get <runId> --pick status
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Fallback: Direct tool calls (no project context)
|
|
94
|
+
|
|
95
|
+
Use these only when `chat` is unavailable or you need raw model access without project/conversation context.
|
|
96
|
+
|
|
97
|
+
### `edit` — One-shot image editing
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Edit an existing image
|
|
101
|
+
npx makaron-cli edit --image photo.jpg "add cinematic warm lighting"
|
|
102
|
+
|
|
103
|
+
# Text-to-image (no input)
|
|
104
|
+
npx makaron-cli edit "a cyberpunk cityscape at night"
|
|
105
|
+
|
|
106
|
+
# With model/skill/reference
|
|
107
|
+
npx makaron-cli edit --image photo.jpg --model openai --skill captions "add title"
|
|
108
|
+
npx makaron-cli edit --image photo.jpg --ref style.jpg "match this style"
|
|
109
|
+
|
|
110
|
+
# Output to file
|
|
111
|
+
npx makaron-cli edit --image photo.jpg --out result.jpg "make it dramatic"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Options: `--image`, `--model gemini|qwen|openai|pony|wai`, `--skill enhance|creative|wild|captions`, `--ref <file>` (up to 3), `--aspect <ratio>`, `--out <path>`
|
|
115
|
+
|
|
116
|
+
### `video` — Video generation (3 steps)
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# 1. Write script from images
|
|
120
|
+
npx makaron-cli video script --image img1.jpg "cinematic story"
|
|
121
|
+
|
|
122
|
+
# 2. Submit rendering (images must be public URLs from step 1 or uploaded)
|
|
123
|
+
npx makaron-cli video create --script "Shot 1 (5s): <<<image_1>>> ..." --image https://...jpg --duration 5 --model kling
|
|
124
|
+
|
|
125
|
+
# 3. Check status
|
|
126
|
+
npx makaron-cli video status <taskId>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
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`
|
|
130
|
+
|
|
131
|
+
### `music` — Music generation
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npx makaron-cli music create "gentle piano, warm strings, cinematic"
|
|
135
|
+
npx makaron-cli music create --vocals --style "lo-fi" "rainy day vibes"
|
|
136
|
+
npx makaron-cli music status <taskId>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Options: `--vocals` (include vocals), `--style "genre"`
|
|
140
|
+
|
|
141
|
+
## Response Schema
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
type MakaronRunResponse = {
|
|
145
|
+
id: string
|
|
146
|
+
status: "in_progress" | "completed" | "failed" | "aborted"
|
|
147
|
+
incomplete: boolean // true = keep polling
|
|
148
|
+
project_id: string
|
|
149
|
+
project_url: string
|
|
150
|
+
next_poll_after_ms?: number // suggested poll interval
|
|
151
|
+
output: MakaronOutput[]
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
type MakaronOutput =
|
|
155
|
+
| { id: string; type: "text"; status: "completed"; content: string }
|
|
156
|
+
| { id: string; type: "image"; status: "completed"; url: string; snapshot_id: string }
|
|
157
|
+
| { id: string; type: "design"; status: "completed"; url: string; width: number; height: number; animated: boolean; duration?: number }
|
|
158
|
+
| { id: string; type: "video"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
|
|
159
|
+
| { id: string; type: "music"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Polling Rules
|
|
163
|
+
|
|
164
|
+
1. Poll while `incomplete: true` or `status` is `"in_progress"`
|
|
165
|
+
2. Use `next_poll_after_ms` as interval (default 5000ms)
|
|
166
|
+
3. Stop when `status` is `"completed"`, `"failed"`, or `"aborted"`
|
|
167
|
+
4. Top-level `status: "completed"` means ALL artifacts are ready (including rendered videos)
|
|
168
|
+
|
|
169
|
+
## Exit Codes
|
|
170
|
+
|
|
171
|
+
| Code | Meaning |
|
|
172
|
+
|------|---------|
|
|
173
|
+
| 0 | Success (completed) or valid in-progress response |
|
|
174
|
+
| 1 | Failed, aborted, or HTTP error |
|
|
175
|
+
| 2 | Timeout (partial response still printed to stdout) |
|
|
176
|
+
|
|
177
|
+
## What Makaron Agent Can Do
|
|
178
|
+
|
|
179
|
+
| Task | Example prompt |
|
|
180
|
+
|------|---------------|
|
|
181
|
+
| Edit photo | "make it cinematic with warm tones" |
|
|
182
|
+
| Style transfer | "convert to oil painting style" |
|
|
183
|
+
| Add/remove elements | "add a cat on the table" / "remove background person" |
|
|
184
|
+
| Text-to-image | "generate a cyberpunk cityscape" |
|
|
185
|
+
| Video from image | "create a 5 second video of her walking" |
|
|
186
|
+
| Video with model | "用seedance模型做5秒视频" |
|
|
187
|
+
| Background music | "add calm piano music" |
|
|
188
|
+
| Motion design | "create an Instagram story with animated text" |
|
|
189
|
+
| Multi-step | "edit the photo then make a video from it" |
|
|
190
|
+
|
|
191
|
+
## Important Notes
|
|
192
|
+
|
|
193
|
+
- One project = one conversation thread. All history is preserved.
|
|
194
|
+
- One run at a time per project. New message interrupts previous run.
|
|
195
|
+
- Multi-image: `create --image a.jpg --image b.jpg` or `chat --image ref.jpg`.
|
|
196
|
+
- Videos take 2-5 minutes to render. Use `watch` to get URL when ready.
|
|
197
|
+
- Music takes ~60 seconds. Appears in output when done.
|
|
198
|
+
- Images are typically ready in 15-30 seconds.
|
|
199
|
+
- stdout is always machine-readable JSON/text. Human-friendly logs go to stderr.
|
|
200
|
+
- Always use `chat` as the primary interface — even for single image edits.
|
|
201
|
+
- `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) {
|
|
@@ -551,11 +624,44 @@ if (command === 'login') {
|
|
|
551
624
|
else promptParts.push(args[i]);
|
|
552
625
|
}
|
|
553
626
|
const prompt = promptParts.join(' ');
|
|
554
|
-
if (!
|
|
555
|
-
console.error('Usage: makaron chat --project <id> [--image <file>] [--stream] [--background|-b] [--json] "your message"');
|
|
627
|
+
if (!prompt) {
|
|
628
|
+
console.error('Usage: makaron chat --project <id|auto> [--image <file>] [--stream] [--background|-b] [--json] "your message"');
|
|
556
629
|
process.exit(1);
|
|
557
630
|
}
|
|
558
|
-
//
|
|
631
|
+
// --project auto: create a new project (with images if provided)
|
|
632
|
+
if (!projectId || projectId === 'auto') {
|
|
633
|
+
if (chatImages.length === 0) {
|
|
634
|
+
// Create empty project
|
|
635
|
+
process.stderr.write(`📦 Creating new project...\n`);
|
|
636
|
+
const res = await fetch(`${baseUrl}/api/projects/create`, {
|
|
637
|
+
method: 'POST',
|
|
638
|
+
headers: { 'Content-Type': 'application/json', ...headers },
|
|
639
|
+
body: JSON.stringify({ title: prompt.slice(0, 50) }),
|
|
640
|
+
});
|
|
641
|
+
if (!res.ok) { process.stderr.write(`❌ Failed to create project: ${await res.text()}\n`); process.exit(1); }
|
|
642
|
+
const data = await res.json();
|
|
643
|
+
projectId = data.projectId;
|
|
644
|
+
process.stderr.write(`📦 Project created: ${projectId}\n`);
|
|
645
|
+
} else {
|
|
646
|
+
// Create project with images
|
|
647
|
+
const base64s = chatImages.map(imgPath => {
|
|
648
|
+
process.stderr.write(`📤 Uploading ${path.basename(imgPath)}...\n`);
|
|
649
|
+
const buf = fs.readFileSync(imgPath);
|
|
650
|
+
return `data:image/jpeg;base64,${buf.toString('base64')}`;
|
|
651
|
+
});
|
|
652
|
+
const res = await fetch(`${baseUrl}/api/projects/create`, {
|
|
653
|
+
method: 'POST',
|
|
654
|
+
headers: { 'Content-Type': 'application/json', ...headers },
|
|
655
|
+
body: JSON.stringify({ imageBase64s: base64s }),
|
|
656
|
+
});
|
|
657
|
+
if (!res.ok) { process.stderr.write(`❌ Failed to create project: ${await res.text()}\n`); process.exit(1); }
|
|
658
|
+
const data = await res.json();
|
|
659
|
+
projectId = data.projectId;
|
|
660
|
+
process.stderr.write(`📦 Project created: ${projectId} (${data.snapshots?.length || 0} images)\n`);
|
|
661
|
+
}
|
|
662
|
+
chatImages.length = 0; // already uploaded
|
|
663
|
+
}
|
|
664
|
+
// Upload additional images to existing project
|
|
559
665
|
if (chatImages.length > 0) {
|
|
560
666
|
const base64s = chatImages.map(imgPath => {
|
|
561
667
|
process.stderr.write(`📤 Uploading ${path.basename(imgPath)}...\n`);
|
|
@@ -605,34 +711,47 @@ if (command === 'login') {
|
|
|
605
711
|
|
|
606
712
|
if (sub === 'get') {
|
|
607
713
|
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,
|
|
714
|
+
if (!runId) { console.error('Usage: makaron responses get <runId> [--wait] [--json] [--pick <field>]'); process.exit(1); }
|
|
715
|
+
let wait = false, jsonOutput = false, pick = null;
|
|
610
716
|
for (let i = 3; i < args.length; i++) {
|
|
611
717
|
if (args[i] === '--wait') wait = true;
|
|
612
718
|
if (args[i] === '--json') jsonOutput = true;
|
|
613
|
-
if (args[i] === '--
|
|
719
|
+
if (args[i] === '--pick' && args[i + 1]) pick = args[++i];
|
|
614
720
|
}
|
|
615
721
|
|
|
616
722
|
if (wait) {
|
|
617
|
-
await pollRun(baseUrl, headers, runId, { json:
|
|
723
|
+
await pollRun(baseUrl, headers, runId, { json: true });
|
|
618
724
|
} 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); }
|
|
725
|
+
const res = await fetch(`${baseUrl}/api/agent/run/${runId}`, { headers });
|
|
726
|
+
if (!res.ok) { process.stderr.write(`Error ${res.status}: ${await res.text()}\n`); process.exit(1); }
|
|
623
727
|
const data = await res.json();
|
|
624
|
-
|
|
728
|
+
if (pick) {
|
|
729
|
+
const picked = applyPick(data, pick);
|
|
730
|
+
if (picked !== undefined) console.log(typeof picked === 'string' ? picked : JSON.stringify(picked));
|
|
731
|
+
} else {
|
|
732
|
+
console.log(JSON.stringify(data, null, 2));
|
|
733
|
+
}
|
|
734
|
+
if (data.status === 'failed' || data.status === 'aborted') process.exit(1);
|
|
625
735
|
}
|
|
626
736
|
|
|
737
|
+
} else if (sub === 'watch') {
|
|
738
|
+
const runId = args[2];
|
|
739
|
+
if (!runId) { console.error('Usage: makaron responses watch <runId> [--jsonl] [--interval <ms>]'); process.exit(1); }
|
|
740
|
+
let interval = 5000, jsonl = false;
|
|
741
|
+
for (let i = 3; i < args.length; i++) {
|
|
742
|
+
if (args[i] === '--jsonl') jsonl = true;
|
|
743
|
+
if (args[i] === '--interval' && args[i + 1]) interval = parseInt(args[++i]);
|
|
744
|
+
}
|
|
745
|
+
await watchRun(baseUrl, headers, runId, { interval, jsonl });
|
|
746
|
+
|
|
627
747
|
} else if (sub === 'list') {
|
|
628
748
|
let projectId = null;
|
|
629
749
|
for (let i = 2; i < args.length; i++) {
|
|
630
750
|
if (args[i] === '--project' && args[i + 1]) projectId = args[++i];
|
|
631
751
|
}
|
|
632
752
|
if (!projectId) { console.error('Usage: makaron responses list --project <id>'); process.exit(1); }
|
|
633
|
-
// Query runs for project
|
|
634
753
|
const res = await fetch(`${baseUrl}/api/agent/run?projectId=${projectId}`, { headers });
|
|
635
|
-
if (!res.ok) {
|
|
754
|
+
if (!res.ok) { process.stderr.write(`Error ${res.status}: ${await res.text()}\n`); process.exit(1); }
|
|
636
755
|
const data = await res.json();
|
|
637
756
|
if (data.runs?.length) {
|
|
638
757
|
for (const r of data.runs) {
|
|
@@ -645,9 +764,10 @@ if (command === 'login') {
|
|
|
645
764
|
|
|
646
765
|
} else {
|
|
647
766
|
console.log(`Responses commands:
|
|
648
|
-
responses get <runId> Get
|
|
767
|
+
responses get <runId> Get status and output (JSON)
|
|
649
768
|
responses get <runId> --wait Poll until completed
|
|
650
|
-
responses get <runId> --
|
|
769
|
+
responses get <runId> --pick <field> Extract: first_image_url, first_video_url, project_url, output
|
|
770
|
+
responses watch <runId> --jsonl Watch until done (incremental events)
|
|
651
771
|
responses list --project <id> List runs for a project
|
|
652
772
|
`);
|
|
653
773
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "makaron-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
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",
|