@koda-sl/baker-cli 0.393.0-dev.10b6da011 → 0.393.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 +255 -97
- package/canvas/static-ad-composition/index.html +253 -0
- package/canvas/static-ad-composition/meta.json +53 -0
- package/dist/{chunk-AOVFDDVW.js → chunk-53ATKV76.js} +13 -2
- package/dist/{chunk-AOVFDDVW.js.map → chunk-53ATKV76.js.map} +1 -1
- package/dist/{chunk-OKE2QJSJ.js → chunk-CEVZG46B.js} +3 -3
- package/dist/{chunk-NEZ3NAJV.js → chunk-F56XWRCY.js} +4 -4
- package/dist/{chunk-QIQ57EIA.js → chunk-S35KWGIQ.js} +2 -2
- package/dist/{chunk-FEJQJ64L.js → chunk-VTLHR3BB.js} +3997 -2418
- package/dist/chunk-VTLHR3BB.js.map +1 -0
- package/dist/{chunk-T2UNK5BW.js → chunk-ZPQ7L33H.js} +3 -3
- package/dist/cli.js +12327 -9257
- package/dist/cli.js.map +1 -1
- package/dist/{client-EQOSDYJL.js → client-ST4W3WRE.js} +4 -4
- package/dist/engine/index.d.ts +178 -117
- package/dist/engine/index.js +3 -3
- package/dist/{env-ANKM47IJ.js → env-HQWFIYZF.js} +2 -2
- package/dist/{output-NR4EJQDV.js → output-BTGDLTVL.js} +5 -5
- package/dist/{shared-SLCDVV45.js → shared-KLQWQWPJ.js} +6 -6
- package/package.json +2 -1
- package/dist/chunk-FEJQJ64L.js.map +0 -1
- /package/dist/{chunk-OKE2QJSJ.js.map → chunk-CEVZG46B.js.map} +0 -0
- /package/dist/{chunk-NEZ3NAJV.js.map → chunk-F56XWRCY.js.map} +0 -0
- /package/dist/{chunk-QIQ57EIA.js.map → chunk-S35KWGIQ.js.map} +0 -0
- /package/dist/{chunk-T2UNK5BW.js.map → chunk-ZPQ7L33H.js.map} +0 -0
- /package/dist/{client-EQOSDYJL.js.map → client-ST4W3WRE.js.map} +0 -0
- /package/dist/{env-ANKM47IJ.js.map → env-HQWFIYZF.js.map} +0 -0
- /package/dist/{output-NR4EJQDV.js.map → output-BTGDLTVL.js.map} +0 -0
- /package/dist/{shared-SLCDVV45.js.map → shared-KLQWQWPJ.js.map} +0 -0
package/README.md
CHANGED
|
@@ -106,7 +106,7 @@ offered, so a short option is never pointed at an unrelated one (`--from` at `--
|
|
|
106
106
|
Previously such an option was accepted and ignored, so a call that
|
|
107
107
|
looked like it had filtered or narrowed a result was in fact answering the unfiltered
|
|
108
108
|
question — and nothing in the output said so. Both spellings of an option work
|
|
109
|
-
(`--run-id` and `--
|
|
109
|
+
(`--run-id` and `--run-id`), as they always did. The error's `fix` names the exact call
|
|
110
110
|
that lists every option: `baker schema <command>` (`check_schema`) when the command
|
|
111
111
|
registers a schema, `baker <command> --help` (`check_help`) when it does not.
|
|
112
112
|
|
|
@@ -876,7 +876,7 @@ Read the company's ChatGPT Ads account (OpenAI's ads inside ChatGPT conversation
|
|
|
876
876
|
| `conversions` | Pixels (with the pixel id a landing page installs) and conversions, with the campaigns using each |
|
|
877
877
|
| `insights` | Impressions, clicks, spend, CTR, CPC, CPM, conversions, CPA, ROAS — per `--level account\|campaign\|ad_group\|ad`, over `--days N` or `--since/--until`, `--granularity`, `--segment country\|device\|platform\|product` |
|
|
878
878
|
| `campaigns create\|update\|pause\|resume\|archive` | Stage a campaign change: objective, daily or lifetime budget (currency units), schedule, countries and regions included and excluded, audiences, platforms, tracking template, status, the conversions it measures (`--conversion-event-ids`), a catalog (`--catalog-id`) |
|
|
879
|
-
| `ad-groups create\|update\|pause\|resume\|archive` | Stage an ad group change: context hints, bid strategy, max bid, tracking template, product filters, status |
|
|
879
|
+
| `ad-groups create\|update\|pause\|resume\|archive` | Stage an ad group change: context hints, bid strategy, max bid, audience bid adjustments, tracking template, product filters, status |
|
|
880
880
|
| `ads create\|update\|pause\|resume\|archive` | Stage a chat card: title, body, landing page, image and its crop, tracking template, status |
|
|
881
881
|
| `ads create-product` | Stage a catalog ad group's product ad: a title template the catalog's products fill in |
|
|
882
882
|
| `pixels create` | Stage a new website pixel |
|
|
@@ -3201,7 +3201,7 @@ Search the **ad-dna** corpus of scored "winning" competitor ads for reference cr
|
|
|
3201
3201
|
|
|
3202
3202
|
### `baker winning-ads search <query>`
|
|
3203
3203
|
|
|
3204
|
-
Semantic search (dense recall + BM25 + rerank) → `POST /api/ad-library/winners/search`. The CLI projects each result to a **lean, decision-focused** shape so the agent's context stays small — default fields: `advertiser`, `advertiser_id`, `platform`, `format`, `relevance`, `winner_score`, `summary` (what the ad is about), `media_url
|
|
3204
|
+
Semantic search (dense recall + BM25 + rerank) → `POST /api/ad-library/winners/search`. The CLI projects each result to a **lean, decision-focused** shape so the agent's context stays small — default fields: `advertiser`, `advertiser_id`, `platform`, `format`, `relevance`, `winner_score`, `summary` (what the ad is about), `media_url`, and `visual` (what the viewer sees) + `container` (a static's layout pattern) when known; plus top-level `pool_size`, `match_confidence` (`high|medium|low`), and `below_floor_count` (matches dropped under the relevance floor). `--full` adds DNA detail (`angle`, `target_persona`, `hook_archetype`, `mechanism`, `awareness_stage`, `industry`) + longevity (`days_active`, `reach`, `active`, `winner_category`, `media_kind`). `--output json` (default) returns the lean objects; `--output md` prints a table.
|
|
3205
3205
|
|
|
3206
3206
|
> `media_url` is the creative itself: for `static` it's the image, for `video` it's the video file. ad-dna stores **no separate poster** for videos, so a video result has only the video URL.
|
|
3207
3207
|
|
|
@@ -3214,6 +3214,10 @@ baker winning-ads search "B2B lead gen demo" --platform linkedin --format video
|
|
|
3214
3214
|
baker winning-ads search "fintech onboarding" --exclude-advertiser adv_ourbrand,adv_usedbefore --output md
|
|
3215
3215
|
# Similar-to a reference ad, recent only:
|
|
3216
3216
|
baker winning-ads search --ref-ad-id a_12345 --first-seen-after 2026-01-01T00:00:00Z --limit 5 --output md
|
|
3217
|
+
# One varied reference per concept (different brands, containers, hooks) in one call:
|
|
3218
|
+
baker winning-ads search "meal kit for busy parents" --scope followed-first --diverse --limit 5 --output md
|
|
3219
|
+
# The best proven ads in one container, from any category (no query needed):
|
|
3220
|
+
baker winning-ads search --format-pattern comparison_table --platform meta --output md
|
|
3217
3221
|
```
|
|
3218
3222
|
|
|
3219
3223
|
| Flag | Purpose |
|
|
@@ -3232,6 +3236,8 @@ baker winning-ads search --ref-ad-id a_12345 --first-seen-after 2026-01-01T00:00
|
|
|
3232
3236
|
| `--country <list>` / `--language <list>` | Filter by country / language codes |
|
|
3233
3237
|
| `--first-seen-after` / `--first-seen-before` | ISO datetime bounds (recency) |
|
|
3234
3238
|
| `--hook-archetype <list>` | Narrow to ads on a proven hook **seed** (from `winning-ads hooks`) |
|
|
3239
|
+
| `--format-pattern <list>` | Narrow to statics in these containers (from `winning-ads formats`, e.g. `comparison_table`) |
|
|
3240
|
+
| `--diverse` | One varied page (different brands, containers, hooks) instead of rank order; ignores paging |
|
|
3235
3241
|
| `--output json\|md\|files` | Output format (default json) |
|
|
3236
3242
|
| `--full` | Include DNA detail + longevity |
|
|
3237
3243
|
|
|
@@ -3240,16 +3246,17 @@ Reading the scores: **`relevance`** (0–1) = match of the creative to your quer
|
|
|
3240
3246
|
### `baker winning-ads hooks|mechanisms|formats`
|
|
3241
3247
|
|
|
3242
3248
|
Read the **winner-weighted rollup** of the ad-dna corpus → `GET /api/ad-library/seed-catalog` — the hook /
|
|
3243
|
-
mechanism / static-format patterns that actually win, ranked by `winner_score
|
|
3244
|
-
|
|
3245
|
-
|
|
3249
|
+
mechanism / static-format patterns that actually win, ranked by `winner_score` among the ads of the segment you
|
|
3250
|
+
pass (`--platform`, `--awareness`, `--industry`; an unset one means any), keyed on the closed seeds/enums the
|
|
3251
|
+
extractor emits (no clustering). Ground a creative in a **proven** pattern for the exact segment instead of
|
|
3252
|
+
guessing. The rollup is recomputed daily. (Distinct from `winning-ads patterns`, which contrasts two ad cohorts.)
|
|
3246
3253
|
|
|
3247
3254
|
```bash
|
|
3248
3255
|
# Top winning hook seeds for problem-aware SaaS on Meta:
|
|
3249
3256
|
baker winning-ads hooks --platform meta --awareness problem_aware --industry saas --output md
|
|
3250
3257
|
# Then narrow a reference search to that proven hook:
|
|
3251
3258
|
baker winning-ads search "onboarding demo" --hook-archetype callout --output md
|
|
3252
|
-
# Also: mechanisms (
|
|
3259
|
+
# Also: mechanisms (how the product claims to work, not the ad's device), formats (static ad layouts):
|
|
3253
3260
|
baker winning-ads mechanisms --platform linkedin --output md
|
|
3254
3261
|
baker winning-ads formats --awareness solution_aware --output md
|
|
3255
3262
|
```
|
|
@@ -3830,7 +3837,7 @@ Starting a batch returns 202 + a `generationId`; the CLI polls until the takes s
|
|
|
3830
3837
|
```bash
|
|
3831
3838
|
baker studio generate "a friendly golden retriever in a bright modern living room" --aspect-ratio 16:9
|
|
3832
3839
|
baker studio generate "hero shot of a matte black water bottle on wet marble, studio light" \
|
|
3833
|
-
--model google/gemini-3-pro-image
|
|
3840
|
+
--model google/gemini-3-pro-image --image-size 2K --aspect-ratio 4:5 --count 3
|
|
3834
3841
|
baker studio generate "home office hero, warm minimalist, natural light, 35mm photo" \
|
|
3835
3842
|
--reference "https://i.pinimg.com/…/pin1.jpg,./src/brand/logos/product.png,j57abc123def456ghi789"
|
|
3836
3843
|
baker studio generate "founder-style selfie, kitchen background" --skill ugc-selfie-hook
|
|
@@ -3838,13 +3845,13 @@ baker studio generate "flat geometric mascot, brand palette" \
|
|
|
3838
3845
|
--model recraft/recraft-v4.1-pro-vector --rgb-colors "[[10,10,10],[255,80,0]]" --bg-rgb "[255,255,255]"
|
|
3839
3846
|
```
|
|
3840
3847
|
|
|
3841
|
-
**Models** (`--model`, default `google/gemini-3.1-flash-image
|
|
3848
|
+
**Models** (`--model`, default `google/gemini-3.1-flash-image`):
|
|
3842
3849
|
|
|
3843
3850
|
| Model | Best for | Aspect ratios | Sizes |
|
|
3844
3851
|
|---|---|---|---|
|
|
3845
|
-
| `google/gemini-3.1-flash-image
|
|
3852
|
+
| `google/gemini-3.1-flash-image` **(default)** | Fast iteration, reference editing, extreme aspect ratios (Nano Banana flash) | standard **+** `1:4` `4:1` `1:8` `8:1` | `0.5K`–`4K` |
|
|
3846
3853
|
| `google/gemini-3.1-flash-lite-image` | Cheapest tier (Nano Banana 2 Lite) — half the flash price per output token. High-volume variants and any 1K-sufficient frame, portrait ad formats included | standard **+** `1:4` `4:1` `1:8` `8:1` | `1K` **only** |
|
|
3847
|
-
| `google/gemini-3-pro-image
|
|
3854
|
+
| `google/gemini-3-pro-image` | Highest fidelity (Nano Banana Pro) — strong prompt adherence and reference grounding | standard set | `1K` `2K` `4K` |
|
|
3848
3855
|
| `openai/gpt-image-2` | Photoreal + cleanest in-image text — ad/landing reproduction | standard set **minus** `4:5` `5:4` | *(none — derived from the ratio)* |
|
|
3849
3856
|
| `openai/gpt-5.4-image-2` | **Legacy** — accepted so canvases authored before the gpt-image-2 switch still re-run. Don't pick it for new work. | standard set | `1K` `2K` `4K` (ignored upstream) |
|
|
3850
3857
|
| `recraft/recraft-v4.1-pro-vector` | Vector/flat/SVG-style with palette control. Writes **SVG**, so its output cannot be fed back as a reference | `1:1` `4:3` `3:4` `16:9` `9:16` **only** | *(none — refuses `image_size`)* |
|
|
@@ -3853,7 +3860,7 @@ Standard aspect ratios: `1:1` `2:3` `3:2` `3:4` `4:3` `4:5` `5:4` `9:16` `16:9`
|
|
|
3853
3860
|
|
|
3854
3861
|
`openai/gpt-image-2` accepts neither `image_size` nor the `4:5` / `5:4` portrait ratios — OpenAI picks the pixel dimensions from the ratio. To ship a 4:5 Meta feed ad from it, render the hero at `3:4` and adapt with `image_aspect_adapt`; the scaffolders do this for you.
|
|
3855
3862
|
|
|
3856
|
-
`google/gemini-3.1-flash-lite-image` renders at `1K` and nothing else — `2K` / `4K` is a provider 400 (`resolution: not supported. Accepted: 1K`), not a soft clamp. It costs half of `google/gemini-3.1-flash-image
|
|
3863
|
+
`google/gemini-3.1-flash-lite-image` renders at `1K` and nothing else — `2K` / `4K` is a provider 400 (`resolution: not supported. Accepted: 1K`), not a soft clamp. It costs half of `google/gemini-3.1-flash-image` per output image token ($0.00003 vs $0.00006), so it is the cheap-iteration tier for images. Keep hero stills and reference sheets that need `2K`+ on the flash or pro tier.
|
|
3857
3864
|
|
|
3858
3865
|
`recraft/recraft-v4.1-pro-vector` is the narrowest of the roster in three ways, all read off its live endpoint schema: it renders **five** ratios (no `4:5` / `5:4` / `2:3` / `3:2` / `21:9`, so a portrait ad shape snaps to `3:4`), it has **no `image_size`** — the provider accepts one and ignores it, which is why it is refused here — and it takes **exactly one** reference image against 14-16 elsewhere. Its output is an SVG, and the image models read only JPG, PNG and WebP, so a Recraft take cannot be used as a reference for the next render.
|
|
3859
3866
|
|
|
@@ -3863,7 +3870,7 @@ A knob the chosen model does not advertise is **refused, not silently dropped**
|
|
|
3863
3870
|
|
|
3864
3871
|
| Flag | Description |
|
|
3865
3872
|
|---|---|
|
|
3866
|
-
| `--model` | Model id (default `google/gemini-3.1-flash-image
|
|
3873
|
+
| `--model` | Model id (default `google/gemini-3.1-flash-image`) |
|
|
3867
3874
|
| `--aspect-ratio` | Output aspect ratio (default `1:1`) |
|
|
3868
3875
|
| `--count` | Takes to render, 1–8 (default 1) |
|
|
3869
3876
|
| `--reference` | Comma-separated visual references, in order: **library image ids**, **local file paths**, and/or **image URLs**. Paths and URLs are added to the media library first and passed as ids — the studio takes library ids only, so the client can see what grounded the render and the reference stays reusable. The biggest quality lever there is. |
|
|
@@ -3927,7 +3934,7 @@ baker studio animate "she keeps walking, camera stays with her, then she stops a
|
|
|
3927
3934
|
- **480p / 720p only** — 1080p and 4K are real Seedance tiers but not on this route.
|
|
3928
3935
|
- **Sound costs nothing extra** here, unlike every other model that offers it.
|
|
3929
3936
|
- **No watermark** unless `--watermark` asks for one; it cannot be removed after the render.
|
|
3930
|
-
- **No people from an uploaded picture.** A privacy pre-check refuses any recognisable face in the input image — invented characters included, and images from ByteDance's own image model included.
|
|
3937
|
+
- **No people from an uploaded picture.** A privacy pre-check refuses any recognisable face in the input image — invented characters included, and images from ByteDance's own image model included. A reference-mode clip that hits it is re-rendered automatically on the same model through a second route that accepts faces (about twice the price); a frame-mode clip is reported as refused and never moved to another model — re-run it on Veo.
|
|
3931
3938
|
|
|
3932
3939
|
| Flag | Description |
|
|
3933
3940
|
|---|---|
|
|
@@ -3971,7 +3978,7 @@ Compact output per take is `{ slot, status, imageId | videoId, url, inLibrary, e
|
|
|
3971
3978
|
| `lifestyle-scene` | `openai/gpt-image-2` | `4:5` | 3 | **yes** |
|
|
3972
3979
|
| `cinematic-still` | `google/gemini-3-pro-image-preview` | `16:9` | 3 | no |
|
|
3973
3980
|
| `brand-icon` | `recraft/recraft-v4.1-pro-vector` | `1:1` | 4 | no |
|
|
3974
|
-
| `background-plate` | `google/gemini-3.1-flash-image
|
|
3981
|
+
| `background-plate` | `google/gemini-3.1-flash-image` | `4:5` | 3 | no |
|
|
3975
3982
|
|
|
3976
3983
|
A skill fills only what you did **not** pin — `--model`, `--aspect-ratio` and `--count` always win. Which means a skill can change the model, and the model decides what the rest of the flags may be: `--skill brand-icon` lands on Recraft, so it takes one reference image rather than fourteen, refuses `--image-size`, and writes SVG that cannot be fed back in as a reference. The CLI resolves the skill's model before checking the reference count, so an over-long `--reference` list is refused before anything is uploaded.
|
|
3977
3984
|
|
|
@@ -4067,7 +4074,7 @@ baker canvas set-prompt my-canvas.json s0_start --text-file ./hook-frame.txt
|
|
|
4067
4074
|
`baker canvas scaffold-ad` renders everything with a known, fixed appearance locally,
|
|
4068
4075
|
because a video model garbles a wordmark every time:
|
|
4069
4076
|
|
|
4070
|
-
- **`brand.logo`** — the repo path to the mark. It is copied into the
|
|
4077
|
+
- **`brand.logo`** — the repo path to the mark. It is copied into the caption composition
|
|
4071
4078
|
and drawn as a real `<img>`: small top-left for the whole ad, large on the closing card.
|
|
4072
4079
|
- **`end_card`** — on by default whenever the spec carries a brand. The last beat's PICTURE
|
|
4073
4080
|
becomes a flat brand colour plate (rendered by ffmpeg, not generated), with the mark and
|
|
@@ -4087,9 +4094,24 @@ because a video model garbles a wordmark every time:
|
|
|
4087
4094
|
alongside the music bed. Most beats should not have one: it belongs where the picture
|
|
4088
4095
|
shows a specific physical event (a drill, a door, rain on glass), not on a talking head
|
|
4089
4096
|
or a calm lifestyle shot. A bed plus two well-placed effects beats eight.
|
|
4090
|
-
- **
|
|
4091
|
-
|
|
4092
|
-
|
|
4097
|
+
- **The logo is optional.** With no mark in `src/brand/logos/` (and none confirmed for the
|
|
4098
|
+
company's own domain), the ad is built without the corner mark, and one hint says so and
|
|
4099
|
+
how to add one: put the client's logo file in `src/brand/logos/` and re-run. Meta shows
|
|
4100
|
+
the page name and picture above every ad either way. The closing card still carries the
|
|
4101
|
+
call to action when the spec has a `cta`; with neither a mark nor a `cta`, the last beat
|
|
4102
|
+
keeps its footage. A typed stand-in (an SVG wordmark in live `<text>`) is still refused,
|
|
4103
|
+
and `--no-brand` still means no brand layer at all: no mark and no closing card.
|
|
4104
|
+
- **Captions and the mark sit inside the safe area.** On 9:16 the caption cards end above
|
|
4105
|
+
the bottom 37% of the frame (Reels covers the bottom 35%) and the mark starts below the
|
|
4106
|
+
top 16% (the Stories and Reels header); 4:5 and 1:1 use modest feed margins. The closing
|
|
4107
|
+
card is centred in the same area. Every composition is sized to the spec's
|
|
4108
|
+
`format.aspect_ratio`, so a 4:5 ad lays its captions out on a 4:5 frame.
|
|
4109
|
+
- **Captions and the closing button are set in the brand's face**: a font file in
|
|
4110
|
+
`src/brand/fonts/`, else the typeface `tokens.json` or `BRAND.md` names, loaded as a web
|
|
4111
|
+
font. With neither, they keep the default heavy sans.
|
|
4112
|
+
- **A spoken ad's captions print the script.** The voice track is transcribed only to time
|
|
4113
|
+
the words: each caption word is the `say` line as written, at the moment it was said, so
|
|
4114
|
+
a word the recogniser mishears never reaches the screen.
|
|
4093
4115
|
- **An avatar with no pinned voice cannot speak on camera.** Their lines would be read by
|
|
4094
4116
|
a separately cast voice, which is dubbing and looks like it.
|
|
4095
4117
|
- **`voiceover: false`** — a music-led ad. No voice, no transcription: the `say` lines
|
|
@@ -4104,6 +4126,13 @@ because a video model garbles a wordmark every time:
|
|
|
4104
4126
|
|
|
4105
4127
|
Each of these also removes a generation: the closing beat costs no image and no clip.
|
|
4106
4128
|
|
|
4129
|
+
**The spec is kept, and the canvas is checked.** `scaffold-ad` writes the spec it built
|
|
4130
|
+
from beside the canvas as `ad.spec.json` (with the cast it resolved), so a later change
|
|
4131
|
+
edits that file and re-runs with `--spec` on it and the same `--slug`. With `--slug` it
|
|
4132
|
+
also writes the creative's `_definition.md` and syncs its workflow graph, as
|
|
4133
|
+
`scaffold-video` does. The canvas is validated before the command reports success: a
|
|
4134
|
+
blocking issue is recorded in the canvas and the command exits `2` with the issues.
|
|
4135
|
+
|
|
4107
4136
|
#### The finished video is reviewed
|
|
4108
4137
|
|
|
4109
4138
|
When a run's output is a video, `baker canvas run` samples twelve frames across it and
|
|
@@ -4163,7 +4192,7 @@ A re-run with no changes hits the cache for every node — total runtime drops t
|
|
|
4163
4192
|
"type": "text_generate",
|
|
4164
4193
|
"inputs": { "topic": "$ref:topic.text" },
|
|
4165
4194
|
"params": {
|
|
4166
|
-
"model": "google/gemini-3.1-flash-image
|
|
4195
|
+
"model": "google/gemini-3.1-flash-image",
|
|
4167
4196
|
"prompt": "Write a 6-word tagline for {{topic}}."
|
|
4168
4197
|
}
|
|
4169
4198
|
}
|
|
@@ -4194,6 +4223,8 @@ The top-level `output` declares the canvas's final result — the engine returns
|
|
|
4194
4223
|
|
|
4195
4224
|
Every node shares the envelope `{ id, type, inputs?, params? }`. `inputs` wires other nodes' output slots in; `params` configures the node. The validator enforces required input kinds and per-model param shape **before** anything is billed.
|
|
4196
4225
|
|
|
4226
|
+
A node may also carry `tags` (`{ scene?, role?, region?, on_camera? }`) stating what it is in the ad (scene 3, region 1, the presenter's clip, the spine). `scaffold-video` stamps them and `canvas validate` reads them in place of id conventions like `s3_r1_clip`. They are descriptive only: tags never enter the cache key, so adding or editing them never re-renders or re-bills a node. Canvases without tags validate as they always did.
|
|
4227
|
+
|
|
4197
4228
|
`baker canvas catalog` prints a one-line index of every node; `baker canvas catalog --node <id>` prints that node's always-current machine-readable schema (params, inputs, outputs, every model), and `--full` prints every node's. The tables below mirror the catalog at time of write.
|
|
4198
4229
|
|
|
4199
4230
|
In every table:
|
|
@@ -4248,6 +4279,44 @@ Local ffmpeg passthrough. Write the argv you'd type, declare outputs, the engine
|
|
|
4248
4279
|
|
|
4249
4280
|
---
|
|
4250
4281
|
|
|
4282
|
+
##### `video_concat`
|
|
4283
|
+
|
|
4284
|
+
Join clips and stills into one continuous video: the spine of a video ad. Every input is normalized onto one raster (size, frame rate, square pixels, `yuv420p`), cut or held to its timeline length, and joined with hard cuts or `xfade` transitions. Local ffmpeg, free. Because the length and size live in params, `canvas validate` sums the spine against the timeline (`VIDEO_TIMELINE_TOTAL_MISMATCH`) before anything renders.
|
|
4285
|
+
|
|
4286
|
+
**Inputs:** one video or image per clip, wired as `c0`, `c1`, … in timeline order (deep validation flags a missing or extra slot).
|
|
4287
|
+
|
|
4288
|
+
**Params:**
|
|
4289
|
+
|
|
4290
|
+
| Name | Type | Required | Notes |
|
|
4291
|
+
|---|---|---|---|
|
|
4292
|
+
| `width`, `height` | integer | yes | output raster; must be even (libx264 `yuv420p` rejects odd sizes) |
|
|
4293
|
+
| `fps` | integer | no | default `30` |
|
|
4294
|
+
| `seam_dedup` | `head` \| `tail` \| `off` | no | default `head`: which copy of a repeated splice frame survives on a `continues_frame` clip; the timeline length never changes |
|
|
4295
|
+
| `clips` | array | yes | per clip: `duration_s` (its timeline length), `length` (`source` default: play as rendered; `trim`: cut out of a longer input from `offset_s`; `still`: hold an image), `fit` (`fill` default crops, `pad` letterboxes a screenshot), `transition` (`{ type, duration_s }`, the xfade out of this clip; the clip carries that much extra footage), `continues_frame` |
|
|
4296
|
+
|
|
4297
|
+
The joined length is the sum of the clips' `duration_s` (a `source` clip counts what is wired into it). **Outputs:** `video` (mp4).
|
|
4298
|
+
|
|
4299
|
+
---
|
|
4300
|
+
|
|
4301
|
+
##### `av_mux`
|
|
4302
|
+
|
|
4303
|
+
Put the soundtrack under a finished picture: the last step of a video ad. The video is stream-copied, the audio loudness-normalized, upmixed to stereo and encoded to AAC. Local ffmpeg, free.
|
|
4304
|
+
|
|
4305
|
+
**Inputs:** `video`, `audio`.
|
|
4306
|
+
|
|
4307
|
+
**Params:**
|
|
4308
|
+
|
|
4309
|
+
| Name | Type | Required | Notes |
|
|
4310
|
+
|---|---|---|---|
|
|
4311
|
+
| `loudnorm` | `{ i, tp, lra }` \| `false` | no | default `{ i: -14, tp: -1.5, lra: 11 }` (the social target); `false` keeps the mix level |
|
|
4312
|
+
| `audio_bitrate` | string | no | default `"192k"` |
|
|
4313
|
+
| `sample_rate` | integer | no | default `48000` |
|
|
4314
|
+
| `length` | `"shortest"` \| `{ total_s }` | no | default `shortest`: end with the shorter stream; `{ total_s }` cuts both at that second |
|
|
4315
|
+
|
|
4316
|
+
**Outputs:** `video` (mp4).
|
|
4317
|
+
|
|
4318
|
+
---
|
|
4319
|
+
|
|
4251
4320
|
##### `imagemagick`
|
|
4252
4321
|
|
|
4253
4322
|
Local ImageMagick passthrough. Identical schema and safety contract to `ffmpeg`; uses `magick` (v7+) or `convert` (v6) on PATH.
|
|
@@ -4345,7 +4414,7 @@ Example pipeline:
|
|
|
4345
4414
|
{ "id": "font", "type": "ingest", "params": { "source": "url", "url": "https://cdn.example.com/brand.woff2", "expect": "font" } }
|
|
4346
4415
|
{ "id": "specimen", "type": "font_specimen", "inputs": { "font": "$ref:font.asset" }, "params": {} }
|
|
4347
4416
|
{ "id": "gen", "type": "image_generate", "inputs": { "reference": "$ref:specimen.image" },
|
|
4348
|
-
"params": { "model": "google/gemini-3.1-flash-image
|
|
4417
|
+
"params": { "model": "google/gemini-3.1-flash-image", "prompt": "Launch poster headlined 'SALE ENDS FRIDAY' using the exact typeface from the reference image" } }
|
|
4349
4418
|
```
|
|
4350
4419
|
|
|
4351
4420
|
---
|
|
@@ -4482,15 +4551,15 @@ No `image_size`: OpenAI derives the pixel dimensions from the ratio, so a size i
|
|
|
4482
4551
|
|
|
4483
4552
|
###### Model: `openai/gpt-5.4-image-2` (legacy)
|
|
4484
4553
|
|
|
4485
|
-
Kept only so canvases authored before the gpt-image-2 switch still validate and re-run unchanged — `baker canvas
|
|
4554
|
+
Kept only so canvases authored before the gpt-image-2 switch still validate and re-run unchanged — `baker canvas run --slug` replays a recorded canvas verbatim. It keeps the full standard ratio set (including `4:5` / `5:4`) and still accepts `image_size`, which the provider ignores. Prefer `openai/gpt-image-2` for anything new.
|
|
4486
4555
|
|
|
4487
|
-
###### Model: `google/gemini-3.1-flash-image-preview`
|
|
4556
|
+
###### Model: `google/gemini-3.1-flash-image` (legacy id `google/gemini-3.1-flash-image-preview` still accepted)
|
|
4488
4557
|
|
|
4489
4558
|
Gemini flash image model — the default. Fast iteration, reference editing, extreme aspect ratios, and the `0.5K` size for cheap previews.
|
|
4490
4559
|
|
|
4491
4560
|
| Name | Type | Required | Notes |
|
|
4492
4561
|
|---|---|---|---|
|
|
4493
|
-
| `model` | literal | yes | `"google/gemini-3.1-flash-image
|
|
4562
|
+
| `model` | literal | yes | `"google/gemini-3.1-flash-image"` |
|
|
4494
4563
|
| `prompt` | string | yes | non-empty |
|
|
4495
4564
|
| `aspect_ratio` | enum | no | EXTREME AR |
|
|
4496
4565
|
| `image_size` | enum | no | `0.5K \| 1K \| 2K \| 4K` |
|
|
@@ -4506,20 +4575,20 @@ Gemini flash image model — the default. Fast iteration, reference editing, ext
|
|
|
4506
4575
|
| `aspect_ratio` | enum | no | EXTREME AR — keeps `4:5` / `5:4`, so Meta/Instagram portrait formats come straight out of it |
|
|
4507
4576
|
| `image_size` | enum | no | `1K` only — `2K` / `4K` is a hard provider 400 (`resolution: not supported. Accepted: 1K`), so `validate` rejects it before the call costs anything |
|
|
4508
4577
|
|
|
4509
|
-
Reach for it for high-volume variants and any frame `1K` covers. Send hero stills and the `image_reference_sheet` subject sheet — anything that needs `2K`+ — to `google/gemini-3.1-flash-image
|
|
4578
|
+
Reach for it for high-volume variants and any frame `1K` covers. Send hero stills and the `image_reference_sheet` subject sheet — anything that needs `2K`+ — to `google/gemini-3.1-flash-image` or `google/gemini-3-pro-image`. Registered for `image_generate` only; `image_reference_sheet` and `image_aspect_adapt` do not accept it.
|
|
4510
4579
|
|
|
4511
4580
|
```json
|
|
4512
4581
|
{ "id": "variant_a", "type": "image_generate",
|
|
4513
4582
|
"params": { "model": "google/gemini-3.1-flash-lite-image", "prompt": "Portrait feed ad for a citrus energy drink …", "aspect_ratio": "4:5", "image_size": "1K" } }
|
|
4514
4583
|
```
|
|
4515
4584
|
|
|
4516
|
-
###### Model: `google/gemini-3-pro-image-preview`
|
|
4585
|
+
###### Model: `google/gemini-3-pro-image` (legacy id `google/gemini-3-pro-image-preview` still accepted)
|
|
4517
4586
|
|
|
4518
4587
|
"Nano Banana Pro" — heavier, higher-quality Gemini.
|
|
4519
4588
|
|
|
4520
4589
|
| Name | Type | Required | Notes |
|
|
4521
4590
|
|---|---|---|---|
|
|
4522
|
-
| `model` | literal | yes | `"google/gemini-3-pro-image
|
|
4591
|
+
| `model` | literal | yes | `"google/gemini-3-pro-image"` |
|
|
4523
4592
|
| `prompt` | string | yes | non-empty |
|
|
4524
4593
|
| `aspect_ratio` | enum | no | STD AR |
|
|
4525
4594
|
| `image_size` | enum | no | `1K \| 2K \| 4K` |
|
|
@@ -4563,7 +4632,7 @@ The sheet prompt is baked into the backend — there is no `prompt` param. `subj
|
|
|
4563
4632
|
|
|
4564
4633
|
| Name | Type | Required | Notes |
|
|
4565
4634
|
|---|---|---|---|
|
|
4566
|
-
| `model` | enum | yes | one of `google/gemini-3-pro-image
|
|
4635
|
+
| `model` | enum | yes | one of `google/gemini-3-pro-image`, `google/gemini-3.1-flash-image` (their `-preview` ids still accepted) |
|
|
4567
4636
|
| `subject_description` | string | yes | exact subject wording — reuse verbatim downstream |
|
|
4568
4637
|
| `subject_type` | enum | yes | `character \| person \| product` — picks the template + default views |
|
|
4569
4638
|
| `views` | string[] | no | 2–6 view labels; defaults per `subject_type` (below) |
|
|
@@ -4590,7 +4659,7 @@ Default views per `subject_type`:
|
|
|
4590
4659
|
|
|
4591
4660
|
Guidance:
|
|
4592
4661
|
- 3–4 full-body views at `16:9` is the sweet spot for characters; more views shrink each figure and lose face detail.
|
|
4593
|
-
- Use `google/gemini-3.1-flash-image
|
|
4662
|
+
- Use `google/gemini-3.1-flash-image` while iterating; switch to `google/gemini-3-pro-image` at `2K`+ for final 6-view sheets.
|
|
4594
4663
|
- Feed the whole sheet downstream as one `reference` input — don't crop individual views.
|
|
4595
4664
|
|
|
4596
4665
|
**Example** — two photos of a sneaker → product sheet → consistent hero shot:
|
|
@@ -4603,14 +4672,14 @@ Guidance:
|
|
|
4603
4672
|
{ "id": "b", "type": "ingest", "params": { "source": "url", "url": "https://example.com/sneaker-side.jpg", "expect": "image" } },
|
|
4604
4673
|
{ "id": "sheet", "type": "image_reference_sheet",
|
|
4605
4674
|
"inputs": { "references": ["$ref:a.asset", "$ref:b.asset"] },
|
|
4606
|
-
"params": { "model": "google/gemini-3-pro-image
|
|
4675
|
+
"params": { "model": "google/gemini-3-pro-image",
|
|
4607
4676
|
"subject_description": "navy leather low-top sneaker with white sole and gold logo",
|
|
4608
4677
|
"subject_type": "product",
|
|
4609
4678
|
"style": "matte black studio background, soft even key light",
|
|
4610
4679
|
"aspect_ratio": "16:9", "image_size": "2K" } },
|
|
4611
4680
|
{ "id": "hero", "type": "image_generate",
|
|
4612
4681
|
"inputs": { "reference": "$ref:sheet.sheet" },
|
|
4613
|
-
"params": { "model": "google/gemini-3-pro-image
|
|
4682
|
+
"params": { "model": "google/gemini-3-pro-image",
|
|
4614
4683
|
"prompt": "The navy leather low-top sneaker with white sole and gold logo from the reference sheet, hero shot on a rain-soaked neon street at night" } }
|
|
4615
4684
|
],
|
|
4616
4685
|
"output": { "node": "hero", "output": "images" }
|
|
@@ -4635,7 +4704,7 @@ The adaptation prompt is baked into the backend and is direction-aware (a wider
|
|
|
4635
4704
|
|
|
4636
4705
|
| Name | Type | Required | Notes |
|
|
4637
4706
|
|---|---|---|---|
|
|
4638
|
-
| `model` | enum | yes | one of `google/gemini-3-pro-image
|
|
4707
|
+
| `model` | enum | yes | one of `google/gemini-3-pro-image`, `google/gemini-3.1-flash-image` (their `-preview` ids still accepted) |
|
|
4639
4708
|
| `formats` | enum[] | one of `formats`/`platform` | 1–6 unique target ratios from `1:1 \| 2:3 \| 3:2 \| 3:4 \| 4:3 \| 4:5 \| 5:4 \| 9:16 \| 16:9 \| 21:9` — output order follows this list |
|
|
4640
4709
|
| `platform` | enum | one of `formats`/`platform` | a placement preset that expands to that platform's canonical on-grid set: `meta_feed` (4:5), `meta_feed_stories` (4:5, 9:16), `meta_all` (4:5, 9:16, 1:1), `linkedin` (1:1), `google_demandgen` (1:1, 4:5, 16:9), `tiktok` (9:16), `youtube` (16:9). Mutually exclusive with `formats`. Off-grid platform targets (e.g. Google 1.91:1) are not rendered here — crop them from the nearest rendered ratio with `baker images crop`. |
|
|
4641
4710
|
| `guidance` | string | no | hints appended to the baked prompt, e.g. `"keep the CTA button fully visible"` |
|
|
@@ -4652,7 +4721,7 @@ The adaptation prompt is baked into the backend and is direction-aware (a wider
|
|
|
4652
4721
|
Guidance:
|
|
4653
4722
|
- Reach for `platform` when you want a placement's whole set without naming ratios (e.g. `"platform": "meta_feed_stories"`); use `formats` when you need an exact, custom ratio list. Set one, never both.
|
|
4654
4723
|
- List the source's own ratio in `formats` too when you want the full set in one output — it passes through for free.
|
|
4655
|
-
- Use `google/gemini-3.1-flash-image
|
|
4724
|
+
- Use `google/gemini-3.1-flash-image` while iterating; switch to `google/gemini-3-pro-image` for final-quality adaptation.
|
|
4656
4725
|
- Indexes are positional: `$ref:adapt.images#1` is always the second entry of `formats`.
|
|
4657
4726
|
|
|
4658
4727
|
**Example** — one 9:16 story creative fanned out to feed and landscape:
|
|
@@ -4662,12 +4731,12 @@ Guidance:
|
|
|
4662
4731
|
"schema": "baker-canvas/1",
|
|
4663
4732
|
"nodes": [
|
|
4664
4733
|
{ "id": "hero", "type": "image_generate",
|
|
4665
|
-
"params": { "model": "google/gemini-3-pro-image
|
|
4734
|
+
"params": { "model": "google/gemini-3-pro-image",
|
|
4666
4735
|
"prompt": "Story-format ad for a citrus energy drink: can centered, bold OFFER text top, logo bottom",
|
|
4667
4736
|
"aspect_ratio": "9:16", "image_size": "2K" } },
|
|
4668
4737
|
{ "id": "adapt", "type": "image_aspect_adapt",
|
|
4669
4738
|
"inputs": { "source": "$ref:hero.images#0" },
|
|
4670
|
-
"params": { "model": "google/gemini-3-pro-image
|
|
4739
|
+
"params": { "model": "google/gemini-3-pro-image",
|
|
4671
4740
|
"formats": ["9:16", "1:1", "16:9"],
|
|
4672
4741
|
"guidance": "keep the offer text and logo fully visible" } }
|
|
4673
4742
|
],
|
|
@@ -4802,9 +4871,9 @@ Fail-fast: an invalid model selection (wrong count, duplicate or out-of-range pi
|
|
|
4802
4871
|
{
|
|
4803
4872
|
"schema": "baker-canvas/1",
|
|
4804
4873
|
"nodes": [
|
|
4805
|
-
{ "id": "a", "type": "image_generate", "params": { "model": "google/gemini-3.1-flash-image
|
|
4806
|
-
{ "id": "b", "type": "image_generate", "params": { "model": "google/gemini-3.1-flash-image
|
|
4807
|
-
{ "id": "c", "type": "image_generate", "params": { "model": "google/gemini-3.1-flash-image
|
|
4874
|
+
{ "id": "a", "type": "image_generate", "params": { "model": "google/gemini-3.1-flash-image", "prompt": "hero shot of a ceramic mug, warm morning light" } },
|
|
4875
|
+
{ "id": "b", "type": "image_generate", "params": { "model": "google/gemini-3.1-flash-image", "prompt": "hero shot of a ceramic mug, studio softbox" } },
|
|
4876
|
+
{ "id": "c", "type": "image_generate", "params": { "model": "google/gemini-3.1-flash-image", "prompt": "hero shot of a ceramic mug, dramatic side light" } },
|
|
4808
4877
|
{ "id": "best", "type": "image_select",
|
|
4809
4878
|
"inputs": { "images": ["$ref:a.images#0", "$ref:b.images#0", "$ref:c.images#0"] },
|
|
4810
4879
|
"params": { "model": "~google/gemini-pro-latest", "prompt": "most premium-feeling lighting, no harsh shadows" } }
|
|
@@ -4836,7 +4905,7 @@ Accepted ref-image MIMEs vary by model — see per-model sections below.
|
|
|
4836
4905
|
|---|---|---|
|
|
4837
4906
|
| `video` | video | `video/mp4` |
|
|
4838
4907
|
|
|
4839
|
-
**Cost** —
|
|
4908
|
+
**Cost** — priced per model from the shared contract table (duration x resolution x audio; the fal route and source-clip premium when they apply), the same table the backend charges from. One clip may not cost more than $20: the backend refuses a dearer one before it runs, and no clip the registry allows reaches it.
|
|
4840
4909
|
|
|
4841
4910
|
---
|
|
4842
4911
|
|
|
@@ -4900,7 +4969,7 @@ Ref-image MIMEs: `image/png`, `image/jpeg`, `image/webp`.
|
|
|
4900
4969
|
|---|---|---|---|
|
|
4901
4970
|
| `model` | literal | yes | `"bytedance/seedance-2.0"` |
|
|
4902
4971
|
| `prompt` | string | yes | non-empty |
|
|
4903
|
-
| `duration` | enum | no |
|
|
4972
|
+
| `duration` | enum | no | every whole second `4`-`15` |
|
|
4904
4973
|
| `resolution` | enum | no | `480p \| 720p \| 1080p` |
|
|
4905
4974
|
| `aspect_ratio` | enum | no | `1:1, 3:4, 9:16, 4:3, 16:9, 21:9, 9:21` |
|
|
4906
4975
|
| `generate_audio` | boolean | no | native audio track |
|
|
@@ -4943,18 +5012,18 @@ Ref-image MIMEs: `image/png`, `image/jpeg`, `image/webp`, `image/gif` (via OpenR
|
|
|
4943
5012
|
|
|
4944
5013
|
> **Switching a clip's model has cross-node constraints** — validate after every model edit. `validate` gates all of these before any billed run:
|
|
4945
5014
|
> - **One aspect_ratio for the whole canvas.** Every `video_generate` node must agree (`VIDEO_ASPECT_MISMATCH`), or the composite silently crops/letterboxes. Switching one clip to Veo (which only offers `16:9`/`9:16`) forces every other clip to a shared ratio too.
|
|
4946
|
-
> - **`duration` is a per-model hard enum.** Seedance `4
|
|
5015
|
+
> - **`duration` is a per-model hard enum.** Seedance 2.0 `4`-`15`, Seedance 2.5 `4`-`30`, Kling `3`-`15`, Omni `3`-`10`, Veo `4,6,8` only. The scaffolders snap clip lengths to the same registry sets. A value outside the model's set is rejected; a scene whose natural span exceeds the model's max is flagged (`VIDEO_SPAN_EXCEEDS_MODEL`, advisory) — a 9.2s scene can't fit Veo's 8s cap and would truncate; split the scene or keep it on a longer-max model.
|
|
4947
5016
|
> - **`person_generation` differs.** Veo **image-to-video** accepts only `allow_adult` (the sole legal value for keyframe-driven modes, and the only one permitted in the EU/UK); `allow_all` is text-to-video only. The scaffolder sets `allow_adult` on every Veo clip automatically (`VIDEO_PERSON_GENERATION_MISSING` warns if it's missing on a hand-edited canvas).
|
|
4948
5017
|
>
|
|
4949
5018
|
> A scaffolded canvas carries this table inline at `metadata.todo.model_constraints`.
|
|
4950
5019
|
|
|
4951
|
-
> **Per-model prompt & param profiles (applied automatically).** The scaffolder shapes each clip/frame prompt to the model's documented best practice and sets its request-param levers — see `packages/cli/src/engine/
|
|
5020
|
+
> **Per-model prompt & param profiles (applied automatically).** The scaffolder shapes each clip/frame prompt to the model's documented best practice and sets its request-param levers — see `packages/cli/src/engine/lib/prompt-profiles.ts`:
|
|
4952
5021
|
> - **Seedance 2.0** (default video): dialogue in quotes with delivery in **prose** (not `[brackets]`), a `Preserve the composition and colors` keyframe hold, and an **affirmative** stability tail — `Keep it clean and stable: crisp anatomically-correct hands, one consistent face, smooth steady motion` — instead of an `Avoid: …` list (Seedance has no `negative_prompt` param, and inline negation tends to summon the artifact it names). Prompt kept to the ~120-word quality budget (`VIDEO_PROMPT_OVER_BUDGET` warns past ~120).
|
|
4953
5022
|
> - **Veo 3.1** (fallback): `person_generation=allow_adult` + a caption-suppressing `negative_prompt` set as params; durations snapped to `{4,6,8}`.
|
|
4954
5023
|
> - **gpt-image** (default frames): clean-plate exclusions placed **LAST** (OpenAI weights early text as composition), an explicit `photorealistic` cue, identity-critical reference **first**, and `quality=high` (gpt-image-2 forces high input fidelity automatically — the `input_fidelity` param is deliberately **not** sent). Gemini keeps exclusions inline; Recraft drops the photoreal cue.
|
|
4955
5024
|
> - New advisories: `VIDEO_RAW_FACE_KEYFRAME` (a Seedance clip animating a RAW ingested image — real-person 422 risk), `IMAGE_PROMPT_PROFILE_MISSING`, `VIDEO_PROMPT_PROFILE_MISSING`.
|
|
4956
5025
|
|
|
4957
|
-
> **Content-policy blocks are deterministic, not flaky
|
|
5026
|
+
> **Content-policy blocks are deterministic, not flaky.** ByteDance's Seedance rejects a first/last frame or reference that reads as a real-person likeness (even an AI-generated face), surfaced as `content_policy_blocked` (HTTP 422/`E005` "flagged as sensitive", **non-retryable on the same route**). A blocked clip is **not** moved to another model: one guided only by reference images is re-rendered on the same Seedance model through the fal route, which accepts faces (priced at that route, about twice the rate); one that starts from a frame fails with `content_policy_blocked`, nothing billed, and is re-run on `google/veo-3.1` (`person_generation: allow_adult`). To skip the wasted attempt, scaffold a photoreal-cast ad on Veo up front (`--real-face`, which also covers AI-generated photoreal faces), or make the source frame less photorealistic.
|
|
4958
5027
|
|
|
4959
5028
|
---
|
|
4960
5029
|
|
|
@@ -4973,7 +5042,7 @@ None.
|
|
|
4973
5042
|
| `audio` | audio | provider-determined (mp3 by default) |
|
|
4974
5043
|
| `timestamps` | json | `application/json` — present only when `with_timestamps: true` |
|
|
4975
5044
|
|
|
4976
|
-
**Cost** —
|
|
5045
|
+
**Cost** — ElevenLabs characters x $0.00022 (0.022 credits a character), rounded up to whole credits; the backend charges the same rate on the character count ElevenLabs reports.
|
|
4977
5046
|
|
|
4978
5047
|
###### Model: `elevenlabs/eleven_v3`
|
|
4979
5048
|
|
|
@@ -5005,7 +5074,7 @@ None.
|
|
|
5005
5074
|
|
|
5006
5075
|
##### `video_lipsync`
|
|
5007
5076
|
|
|
5008
|
-
Lip-sync a video to an audio track
|
|
5077
|
+
Lip-sync a video to an audio track. Model `replicate/sync-lipsync-2`, which runs Sync Labs `sync/lipsync-2` on Replicate.
|
|
5009
5078
|
|
|
5010
5079
|
**Inputs**
|
|
5011
5080
|
|
|
@@ -5018,7 +5087,7 @@ Lip-sync a video to an audio track via Sync Labs `sync/lipsync-2` (Replicate).
|
|
|
5018
5087
|
|
|
5019
5088
|
| Name | Type | Required | Notes |
|
|
5020
5089
|
|---|---|---|---|
|
|
5021
|
-
| `model` |
|
|
5090
|
+
| `model` | enum | yes | `"replicate/sync-lipsync-2"`. The legacy id `"fal/veed-lipsync"` is still accepted and runs the same model. |
|
|
5022
5091
|
|
|
5023
5092
|
**Outputs**
|
|
5024
5093
|
|
|
@@ -5026,7 +5095,7 @@ Lip-sync a video to an audio track via Sync Labs `sync/lipsync-2` (Replicate).
|
|
|
5026
5095
|
|---|---|---|
|
|
5027
5096
|
| `video` | video | `video/mp4` |
|
|
5028
5097
|
|
|
5029
|
-
**Cost** —
|
|
5098
|
+
**Cost** — $0.40 per minute of the longer input (40 credits for a minute, the quote's assumption when the clips carry no duration). Runs as a background job.
|
|
5030
5099
|
|
|
5031
5100
|
---
|
|
5032
5101
|
|
|
@@ -5044,8 +5113,8 @@ Strip a video's background → transparent alpha WebM (VP9) or MOV (ProRes 4444)
|
|
|
5044
5113
|
|
|
5045
5114
|
| Name | Type | Required | Default | Notes |
|
|
5046
5115
|
|---|---|---|---|---|
|
|
5047
|
-
| `model` |
|
|
5048
|
-
| `edge_refinement` | boolean | no | `true` |
|
|
5116
|
+
| `model` | enum | no | `"replicate/birefnet-video"` | The legacy id `fal/veed-video-background-removal` is still accepted and runs the same model. |
|
|
5117
|
+
| `edge_refinement` | boolean | no | `true` | Refines soft edges against background bleed (the model's `refine_fg`). Off bills the lower rate. |
|
|
5049
5118
|
| `output_codec` | enum | no | `"vp9"` | `vp9 \| h264` |
|
|
5050
5119
|
|
|
5051
5120
|
**Outputs**
|
|
@@ -5054,7 +5123,7 @@ Strip a video's background → transparent alpha WebM (VP9) or MOV (ProRes 4444)
|
|
|
5054
5123
|
|---|---|---|
|
|
5055
5124
|
| `video` | video | `video/webm` (VP9 + alpha) or `video/mp4` (H264 RGB + alpha) |
|
|
5056
5125
|
|
|
5057
|
-
**Cost** —
|
|
5126
|
+
**Cost** — $0.012 per 30 frames with edge refinement, $0.008 without, at 30fps (36 credits for the 30s the quote assumes). Runs as a background job.
|
|
5058
5127
|
|
|
5059
5128
|
---
|
|
5060
5129
|
|
|
@@ -5075,6 +5144,7 @@ Word-level transcription of a video OR a bare audio track (the `video` input acc
|
|
|
5075
5144
|
| `language` | string | no | auto | ISO 639-1, 2–8 chars (e.g. `en`, `es`) |
|
|
5076
5145
|
| `transcriber` | enum | no | `groq` | `groq` (Whisper) \| `deepgram` (Nova-3, adds the `rich` output) |
|
|
5077
5146
|
| `keyterms` | string[] | no | — | Up to 30 names the recogniser must not mangle — a product, a person, a phrase the script coins. The **company's own name is always applied server-side**, so it never needs listing here |
|
|
5147
|
+
| `script` | string | no | — | The words the track was asked to say. Given, the `transcript` output is the script's words, spelled as written, at the times the recogniser heard them: aligned locally (misheard words, fillers, accents and numbers said as words are all tolerated), never sent to the recogniser. `scaffold-ad` sets it on every spoken ad |
|
|
5078
5148
|
|
|
5079
5149
|
**Outputs**
|
|
5080
5150
|
|
|
@@ -5085,7 +5155,7 @@ Word-level transcription of a video OR a bare audio track (the `video` input acc
|
|
|
5085
5155
|
|
|
5086
5156
|
**Known names are spelled the way the brand spells them.** Every term (the company's own name, plus `keyterms`) is biased into the request — Deepgram `keyterm`, Whisper `prompt` — and its spelling is then enforced on the words that come back, because bias is a probability and a caption prints the mistake at full size over the ad. Verified against the take that shipped wrong: the same Spanish clip returns "Cubicap" bare and "Cubicup" with the name applied. Corrections are listed on the output's `brand_spelling_corrections` metadata, so a rewrite is visible rather than silent. A short name (under 6 characters) gets the bias only — at that length ordinary words sit one edit away, and rewriting those to protect a brand would corrupt the line. **Never patch a mishearing inside a caption composition**: that fix dies with that one creative.
|
|
5087
5157
|
|
|
5088
|
-
**Cost** —
|
|
5158
|
+
**Cost** — Groq: $0.04/hr, 10s minimum; Deepgram Nova-3: $0.0043/min. Quoted on one minute of audio; the backend charges the transcribed duration.
|
|
5089
5159
|
|
|
5090
5160
|
**Requirements** — `ffmpeg` on PATH for audio extraction (already in the E2B sandbox image). Without it the node still works but uploads the full video and Groq's 100 MB cap applies.
|
|
5091
5161
|
|
|
@@ -5216,7 +5286,7 @@ Place and mix several audio clips onto one timeline — a music bed plus timed v
|
|
|
5216
5286
|
|
|
5217
5287
|
##### `image_background_remove`
|
|
5218
5288
|
|
|
5219
|
-
Strip background → transparent PNG.
|
|
5289
|
+
Strip background → transparent PNG. Model `replicate/birefnet`, which runs `men1scus/birefnet` on Replicate. The model takes an image and a resolution only: it has no variants and returns no mask.
|
|
5220
5290
|
|
|
5221
5291
|
**Inputs**
|
|
5222
5292
|
|
|
@@ -5228,19 +5298,18 @@ Strip background → transparent PNG. Powered by `men1scus/birefnet` (Replicate)
|
|
|
5228
5298
|
|
|
5229
5299
|
| Name | Type | Required | Default | Notes |
|
|
5230
5300
|
|---|---|---|---|---|
|
|
5231
|
-
| `model` |
|
|
5232
|
-
| `model_variant` | enum | no | `"General Use (Light)"` | `General Use (Light) \| General Use (Heavy) \| Matting \| Portrait \| DIS \| HRSOD \| COD` |
|
|
5301
|
+
| `model` | enum | no | `"replicate/birefnet"` | The legacy id `fal/birefnet-v2` is still accepted and runs the same model. |
|
|
5233
5302
|
| `operating_resolution` | enum | no | — | `1024x1024 \| 2048x2048 \| 2304x2304` |
|
|
5234
|
-
| `
|
|
5303
|
+
| `model_variant` | enum | no | — | **Deprecated, ignored.** Accepted so older canvases still parse. |
|
|
5304
|
+
| `mask_only` | boolean | no | — | **Deprecated, ignored.** The cutout is always returned; there is no mask. |
|
|
5235
5305
|
|
|
5236
5306
|
**Outputs**
|
|
5237
5307
|
|
|
5238
5308
|
| Slot | Kind | MIME |
|
|
5239
5309
|
|---|---|---|
|
|
5240
5310
|
| `image` | image | `image/png` (with alpha) |
|
|
5241
|
-
| `mask` | image | `image/png` (only when `mask_only: true`) |
|
|
5242
5311
|
|
|
5243
|
-
**Cost** — 1 credit per call.
|
|
5312
|
+
**Cost** — 1 credit per call. Runs as a background job.
|
|
5244
5313
|
|
|
5245
5314
|
---
|
|
5246
5315
|
|
|
@@ -5263,7 +5332,7 @@ Generate music from prompt OR score an existing video clip.
|
|
|
5263
5332
|
| `audio` | audio | provider-determined |
|
|
5264
5333
|
| `timestamps` | json | `application/json` (optional) |
|
|
5265
5334
|
|
|
5266
|
-
**Cost** —
|
|
5335
|
+
**Cost** — $0.022 per second of audio (2.2 credits a second), 30s when `music_length_ms` is unset; rounded up to whole credits once, not per second. `elevenlabs/video-background-music-v1` runs as a background job.
|
|
5267
5336
|
|
|
5268
5337
|
###### Model: `elevenlabs/music-v1`
|
|
5269
5338
|
|
|
@@ -5321,7 +5390,7 @@ None.
|
|
|
5321
5390
|
|
|
5322
5391
|
Same as `tts`.
|
|
5323
5392
|
|
|
5324
|
-
**Cost** —
|
|
5393
|
+
**Cost** — total characters x $0.00022 (0.022 credits a character), rounded up to whole credits.
|
|
5325
5394
|
|
|
5326
5395
|
---
|
|
5327
5396
|
|
|
@@ -5350,7 +5419,7 @@ None.
|
|
|
5350
5419
|
|---|---|---|
|
|
5351
5420
|
| `audio` | audio | provider-determined |
|
|
5352
5421
|
|
|
5353
|
-
**Cost** —
|
|
5422
|
+
**Cost** — $0.022 per second of audio (2.2 credits a second), 5s when `duration_seconds` is unset.
|
|
5354
5423
|
|
|
5355
5424
|
---
|
|
5356
5425
|
|
|
@@ -5425,7 +5494,7 @@ Cache policies (via `--cache-policy`):
|
|
|
5425
5494
|
### Safety limits
|
|
5426
5495
|
|
|
5427
5496
|
- **Asset size cap: 2 GiB per asset.** Applies to every download (remote node outputs, `ingest` fetches, yt-dlp output) and to local files ingested via `source: "path"`. Oversized assets fail the node with a `file_too_large` / byte-limit error.
|
|
5428
|
-
- **Per-request cost cap: $
|
|
5497
|
+
- **Per-request cost cap: $20 of estimated provider cost for a `video_generate` clip, $10 for every other node.** The backend pre-estimates cost from the shared contract table (for a Seedance clip, on the dearest route it may take) and rejects a single execution above the cap with `cost_limit_exceeded`. No clip the registry allows exceeds $20; split long audio or long scripts into smaller nodes.
|
|
5429
5498
|
- **Retries never double-charge.** Every remote node execution carries a run-scoped idempotency key; if a request is retried after a transient failure, the backend replays the stored response instead of dispatching (and billing) the provider again. A fresh run (including `--cache-policy bypass`) always generates fresh.
|
|
5430
5499
|
|
|
5431
5500
|
---
|
|
@@ -5436,6 +5505,8 @@ Cache policies (via `--cache-policy`):
|
|
|
5436
5505
|
|
|
5437
5506
|
Parse + schema-check + per-node deep validation (composition `meta.json` is consulted for `hyperframe_*`). Prints a structured result with a per-node cost preview. Exits non-zero on issues. Never calls Convex, never spends credits.
|
|
5438
5507
|
|
|
5508
|
+
**The canvas file is brought up to date first.** Parts of a creative's canvas are projections of its blueprint: each video scene's frame and clip prompts (from `scenes/sNN.json`), a static ad's copy block and type specimen (from `prompt.json`), and the derived spec files the frames read (`prompt.style.json`, `prompt.plate.json`, `prompt.compose.json`). `validate`, `run`, `critique` and `set-prompt` share one preflight that applies every projection and **writes the result back to the canvas file** (only when something changed; `canvas_updated` says so; `sync-definition`, which the file watcher runs over every creative, applies it without writing), so the file on disk, the graph the dashboard shows, the graph the critic scores and the graph that renders are the same. A scene edit that changes timing or the number of scenes is structural and still asks for a re-scaffold. Every preflight failure prints `{ ok: false, error: { code, message, fix? } }` and exits 2.
|
|
5509
|
+
|
|
5439
5510
|
```bash
|
|
5440
5511
|
$ baker canvas validate my-canvas.json
|
|
5441
5512
|
{
|
|
@@ -5449,7 +5520,7 @@ $ baker canvas validate my-canvas.json
|
|
|
5449
5520
|
}
|
|
5450
5521
|
```
|
|
5451
5522
|
|
|
5452
|
-
On error you get
|
|
5523
|
+
On error you get `{ ok: false, error: { code: "validation", issues } }`, each issue with `path`, `code`, `message`, and optional `did_you_mean`.
|
|
5453
5524
|
|
|
5454
5525
|
**Absolute asset paths are healed, not reported.** A `source: "path"` ingest (or a `hyperframe_render`
|
|
5455
5526
|
composition) written as an absolute path inside the workspace is rewritten to its canvas-relative form
|
|
@@ -5480,7 +5551,8 @@ Pre-spend creative critic — **advisory, never blocks**. Scores a scaffolded cr
|
|
|
5480
5551
|
so a weak spot gets fixed for free. It branches on modality: a **video** creative is scored on **hook strength**,
|
|
5481
5552
|
**sound-off first-frame legibility**, **retention risk**, and **mechanism clarity**; a **static ad** on **baked-text
|
|
5482
5553
|
legibility risk**, **identity grounding** (Rule 1 — every subject on a real reference), **brand-type fidelity** (a
|
|
5483
|
-
font specimen wired), and **mechanism clarity**.
|
|
5554
|
+
font specimen wired), and **mechanism clarity**. Scores the canvas after the shared preflight (so scene and copy
|
|
5555
|
+
edits are what gets scored); no generation, no Convex. Ground a video
|
|
5484
5556
|
hook against a proven pattern with `baker winning-ads hooks`.
|
|
5485
5557
|
|
|
5486
5558
|
```bash
|
|
@@ -5493,20 +5565,28 @@ $ baker canvas critique my-static-ad.json
|
|
|
5493
5565
|
"dimensions": [ { "dimension": "text_legibility_risk", "score": 1, "note": "2 copy block(s) — reads cleanly" }, … ] }
|
|
5494
5566
|
```
|
|
5495
5567
|
|
|
5496
|
-
#### `baker canvas run <file.json> [flags]`
|
|
5568
|
+
#### `baker canvas run <file.json> [flags]` / `baker canvas run --slug <slug> [flags]`
|
|
5497
5569
|
|
|
5498
5570
|
Validate, then execute the graph. Blocks until done. Logs one line per node. Returns `{ ok, run_id, outputs_dir, output, stats }` on stdout.
|
|
5499
5571
|
|
|
5572
|
+
`--slug <slug>` runs a creative by name instead of by file: its `src/creatives/<slug>/<slug>.canvas.json` when this workspace has it, else its files restored from the newest run that recorded a portable snapshot (sha-verified, into `src/creatives/<slug>/`), then the normal run flow. Local files are never overwritten unless `--force-remote` is passed. This replaces `baker canvas rerun <slug>`, which still works as a deprecated alias.
|
|
5573
|
+
|
|
5574
|
+
A completed render the review calls ready (`verdict` `ready` or `ready_with_notes` for a static ad, no blocking finding for a video) sets `status: rendered` in the creative's `_definition.md` (never moving a `published` or `archived` creative back); the output then carries `definition_status: "rendered"`.
|
|
5575
|
+
|
|
5500
5576
|
| Flag | Default | Effect |
|
|
5501
5577
|
|---|---|---|
|
|
5502
|
-
| `--
|
|
5503
|
-
| `--
|
|
5578
|
+
| `--slug <slug>` | none | Run a creative by name (see above). |
|
|
5579
|
+
| `--force-remote` | off | With `--slug`: restore the recorded files even over local ones. |
|
|
5504
5580
|
| `--run-id <id>` | auto ULID | Override the generated run id. |
|
|
5505
5581
|
| `--cache-policy <policy>` | `read_write` | `read_write`, `bypass`, or `read_only`. |
|
|
5506
5582
|
| `--concurrency <n>` | `5` (or `BAKER_CANVAS_CONCURRENCY`) | Max nodes executing at once within a layer. |
|
|
5507
|
-
| `--remote-cache <on\|off>` | `on` (or `BAKER_CANVAS_REMOTE_CACHE`) | Company-scoped remote cache + durable asset persistence. |
|
|
5583
|
+
| `--remote-cache <on\|off>` | `on` (or `BAKER_CANVAS_REMOTE_CACHE`) | Operator only. Company-scoped remote cache + durable asset persistence. |
|
|
5508
5584
|
| `--max-credits <n>` | uncapped (or `BAKER_CANVAS_MAX_CREDITS`) | Credit ceiling: aborts before billing when the estimate exceeds it, and at layer boundaries once actual spend does. Completed nodes stay cached, so retrying with a higher cap loses nothing. |
|
|
5509
5585
|
| `--no-record` | records | Skip posting the durable run-history record (and its live progress). |
|
|
5586
|
+
| `--until frames` | none | Stop before the clips with the frames settled; the run is recorded as `awaiting_approval` (not failed, not interrupted) and resumes with `--run-id`. |
|
|
5587
|
+
| `--cache-dir <path>` | `<workspace root>/canvas/.cache` | Operator only. Where content-addressed bytes + cache index live. |
|
|
5588
|
+
| `--outputs-dir <path>` | `<workspace root>/canvas` | Operator only. Root for per-run output directories. |
|
|
5589
|
+
| `--keep-runs <n>` | off | Operator only. Prune old `r_*` run dirs after the run, keeping the N newest. |
|
|
5510
5590
|
|
|
5511
5591
|
**Run history streams live.** The run posts its plan (every node + its dependency edges) the moment validation passes, then re-posts a progress snapshot as each node starts and settles — the dashboard's creative workflow graph shows nodes flipping pending → running → done in real time, with each node's outputs attached as they land. A failed run keeps its per-node trail (what completed, what died). All best-effort: an unreachable backend never changes the run's outcome.
|
|
5512
5592
|
|
|
@@ -5553,7 +5633,7 @@ It then scaffolds the full pipeline like an **editing timeline**: each clip gets
|
|
|
5553
5633
|
|
|
5554
5634
|
**Timing-faithful clip + extract (no overlap).** Each phrase clip is generated to its **coverage window** (the deconstruct's real scene/line timing, capped at the gateway-safe ~10s ceiling) and its converted voice is extracted to the **spoken window** (pause to pause) — *not* padded to a word-count estimate. Padding past the window was what ran the voice the clip's whole length and overlapped the next phrase; trusting the deconstruct's timing keeps consecutive phrases back-to-back and lets Seedance pace the quoted text to fit. `metadata.video.talking_scenes` records each phrase's `scene_s` vs `est_speech_s`; on top of that the scaffold flags any scene whose estimated speech overruns its window by more than ~1.3× as **`metadata.todo.overstuffed_scenes`** (also in the stdout checklist) — a loud advisory to shorten the copy or lengthen the scene before rendering, since an over-stuffed line pushes the picture off the audio timeline. It similarly flags **`oversize_scenes`** — a single scene whose own footage exceeds the gateway-safe ~10s clip ceiling (a b-roll shot or one-shot monologue). The phrase splitter only breaks at scene boundaries, so it can't shrink a single over-long scene; its clip would 524 at the gateway, so the advisory tells you to split that scene into two before rendering.
|
|
5555
5635
|
|
|
5556
|
-
**Timeline-accurate picture — ONE clock.** Seedance can't render under 4s, so each clip is generated at the smallest allowed duration ≥ the scene length and **cut back to the exact scene duration inside the spine's own per-input filter chain** (no standalone trim nodes — one fewer re-encode per scene, and the graph stays a third smaller). Real screenshots/graphics loop fit-padded, generated stills loop fill-cropped, on the same chains. The spine is the ONE clock: every voice, SFX, music, and overlay placement is mapped onto where each scene's picture actually sits in the butted concat (not the reference video's own timestamps, which can carry dead air the picture doesn't reproduce), the audio mix's `total_ms` is pinned to the spine length, and every concat input is normalized to one raster clock (`yuv420p`, 30fps, square pixels, shared timebase) so a generated clip's unadvertised frame rate can never stretch the picture off the audio. The contract is stamped as `metadata.video.timeline` and `baker canvas validate` re-proves it from the live node params after any hand edit. Frames are also prompted as **clean text-free plates** (no baked captions/lower-thirds/tickers/logos-as-text) so the overlay layer is the single source of on-screen text.
|
|
5636
|
+
**Timeline-accurate picture — ONE clock.** Seedance can't render under 4s, so each clip is generated at the smallest allowed duration ≥ the scene length and **cut back to the exact scene duration inside the spine's own per-input filter chain** (no standalone trim nodes — one fewer re-encode per scene, and the graph stays a third smaller). Real screenshots/graphics loop fit-padded, generated stills loop fill-cropped, on the same chains. The spine is the ONE clock: every voice, SFX, music, and overlay placement is mapped onto where each scene's picture actually sits in the butted concat (not the reference video's own timestamps, which can carry dead air the picture doesn't reproduce), the audio mix's `total_ms` is pinned to the spine length, and every concat input is normalized to one raster clock (`yuv420p`, 30fps, square pixels, shared timebase) so a generated clip's unadvertised frame rate can never stretch the picture off the audio. The contract is stamped as `metadata.video.timeline` and `baker canvas validate` re-proves it from the live node params after any hand edit. The spine is a `video_concat` node (each clip's `duration_s`, trim or hold is a param, never argv) and the final mux an `av_mux`, and every scene node carries `tags` (its scene, region and role), which is what the validator reads. Frames are also prompted as **clean text-free plates** (no baked captions/lower-thirds/tickers/logos-as-text) so the overlay layer is the single source of on-screen text.
|
|
5557
5637
|
|
|
5558
5638
|
**Scene transitions.** When the deconstruct flags a boundary as `fade`/`whip`/`zoom`/`dissolve`/`swipe` (`scene.transition_out`), the spine reproduces it as an ffmpeg **`xfade`** instead of a hard cut; plain `cut`/`match_cut` stay hard cuts. The overlap is consumed from **extra generated footage** (each transitioning clip is trimmed to `scene_s + transition` and the xfade `offset` lands on the cumulative scene start), so the total length still equals the sum of the scene lengths — the picture stays exactly on the audio timeline.
|
|
5559
5639
|
|
|
@@ -5585,12 +5665,14 @@ baker canvas run ./reference-ad.video.canvas.json
|
|
|
5585
5665
|
| `--max-scenes <n>` | all source scenes | **Cost lever that reduces fidelity** — caps the deconstruct, MERGING away every scene beyond the cap (fewer cuts, lost beats). Prints a warning when set; omit it to reproduce every scene. |
|
|
5586
5666
|
| `--language <code>` | auto | Transcript/dialogue language hint (e.g. `fr`, `en`). |
|
|
5587
5667
|
| `--focus <text>` | — | Known provenance/emphasis to ground the deconstruct. |
|
|
5588
|
-
| `--deconstruct-model <id>` | `~google/gemini-
|
|
5668
|
+
| `--deconstruct-model <id>` | `~google/gemini-flash-latest` | Override the `video_deconstruct` model. |
|
|
5589
5669
|
| `--select-model <id>` | `~google/gemini-flash-latest` | Override the element-selection `text_generate` model. |
|
|
5590
|
-
| `--image-model <id>` | `openai/gpt-image-2` | Override the per-frame `image_generate` model (defaults to the strongest, matching `scaffold-static-ad`). |
|
|
5670
|
+
| `--image-model <id>` | `openai/gpt-image-2.5-sunburst` | Override the per-frame `image_generate` model (defaults to the strongest, matching `scaffold-static-ad`). |
|
|
5591
5671
|
| `--video-model <id>` | scored router | Override the `video_generate` model **and skip the router**. Curated roster: `google/gemini-omni-1.1-flash` (**the default video model** — native audio + reference images, `360p`\|`720p`\|`1080p`\|`4k` at ~$0.034\|$0.101\|$0.152\|$0.304 per second, `16:9`\|`9:16`, `duration` 3–10s and 10s when unset), `bytedance/seedance-2.0` (workhorse), `google/veo-3.1` (cine ceiling + real-face), `google/veo-3.1-fast` (cheap Veo), `kwaivgi/kling-v3.0-pro` (motion-transfer). |
|
|
5592
5672
|
| `--real-face` | auto-detected | Force the Veo route for a photoreal human presenter (dodges the ByteDance real-person filter). **Usually unnecessary** — the scaffold auto-detects a photoreal on-camera person/animal cast from the deconstruct and routes it to Veo on its own; set this only to force it when the cast isn't detected. Covers AI-generated photoreal faces too, not just real ones. |
|
|
5593
5673
|
| `--motion-transfer` | off | Motion driven from a reference / hyper-dynamic → the router picks Kling. |
|
|
5674
|
+
|
|
5675
|
+
A model flag naming a model the registry does not have for that node fails the command (`unknown_model`, listing the models it does have) before anything is billed; it no longer falls back silently to another model.
|
|
5594
5676
|
| `--identity` | off | Same character/product across clips → the router picks the Seedance workhorse. |
|
|
5595
5677
|
| `--budget <tier>` | `standard` | Router cost posture: `economy` (cheap Veo-fast tier) / `standard` / `premium`. |
|
|
5596
5678
|
| `--avatar <handles>` | — | Cast company avatars as the reference's people, comma-separated in the order they appear (`marta` or `marta,joan`). Each recurring person grounds on that avatar's identity sheet and takes its subject description; a `same_as` look of them uses the same sheet. Resolved before anything is billed — a handle that is not `ready` fails the command. |
|
|
@@ -5621,13 +5703,22 @@ Turn a source/inspiration image into a **runnable, self-validated static-ad canv
|
|
|
5621
5703
|
2. **element selection** (`~google/gemini-flash-latest`) — picks the **main, identity-critical** elements (the brand logo, a showcased product, a trust badge) **plus any foreground/hero person or animal** — the emotional focal point — even a generic one, because a free-generated face/muzzle reads as AI and grows artifacts; the emotional hero always gets a real-reference slot. When the advertiser's logo appears in **more than one lockup** (a square/icon **mark** and a horizontal **wordmark**), each is emitted as its **own** element (e.g. `LOGO_MARK`, `LOGO_WORDMARK`) so you drop the right file in each slot instead of stretching one logo to cover both. The describe pass also records the ad's **typography** under a `fonts` block (each typeface's classification, a best-guess family, and its weight/case) so you know exactly what to drop at the brand-font slot. Background extras are dropped. Each element is stamped back onto its blueprint entry as a `reference_image` label so the JSON self-documents which slot grounds which subject.
|
|
5622
5704
|
3. **global layout** (`~google/gemini-flash-latest`) — produces a structured `layout` block in `prompt.json`: the column/row grid, each region's `x_pct`/`y_pct` bounds, panel splits, background/shape, and every text block's relative size/weight/case/alignment. This is what gives the generator a precise composition to rebuild.
|
|
5623
5705
|
|
|
5624
|
-
It then scaffolds a canvas that
|
|
5706
|
+
It then scaffolds a canvas that draws **only what `prompt.json` holds**; no element is mandatory. The report's `mode` names which of four the ad is:
|
|
5707
|
+
|
|
5708
|
+
- **`composed`**: the image model (`openai/gpt-image-2.5-sunburst`, quality `xhigh`) paints up to **three candidate plates** (`--takes`, default 3; the picture with no overlay text and no logo, keeping calm the areas the copy will occupy), an `image_select` judge scores them on one dominant idea, thumbnail legibility, mechanism kept, space for what is drawn, brand with the logo covered, artifacts, Stories safe zones and text, and every delivered format is drawn by an HTML composition (`layout-4x5/`, `layout-9x16/`, one per format, snapshotted by `hyperframe_snapshot`). Each line sits at its layout region, carried through the format's crop or extension into its safe area (a band only when the layout gives it no position), and is set the way the reference set it: plain, underlined, followed by an arrow, a button (in the brand CTA colour), a sticker or a highlight. A call to action is never forced into a pill.
|
|
5709
|
+
- **`picture_only`**: the concept has no copy, so the plate is the ad and nothing is drawn on it.
|
|
5710
|
+
- **`text_only`**: no picture is generated. The copy is drawn on the brand's lead colour in its fonts, for near-zero credits. On by default when the reference pictures nothing but copy (no subjects, no people); `--text-only` forces it, `--no-text-only` refuses it.
|
|
5711
|
+
- **`baked`**: the copy is painted into the plates, only with `--skip-font` or when no font can load; every other shape is re-framed by `image_aspect_adapt` on `google/gemini-3-pro-image`, never cropped.
|
|
5625
5712
|
|
|
5626
|
-
**
|
|
5713
|
+
**Fonts** come from the first source that has one: a file in `src/brand/fonts/` (via `src/brand/tokens.json` when present), else the heading and body families `tokens.json` or `BRAND.md`'s typography section name, fetched as web font files from Google Fonts or Fontsource and ingested by URL, else Inter (800 and 400). **The logo is optional**: it is drawn from `src/brand/logos/` only when `prompt.json` keeps a logo (a `logo` layout region, or an `advertiser_brand` entry in `brands_logos`) and a real file exists. Meta shows the page name and picture on every ad, so no logo is never an error, never a slot to fill and never a stand-in; a canvas scaffolded with an unfilled logo slot runs without it. Text that belongs to something in the scene (a phone note, a screenshot, a sign, packaging: `surface: "in_scene"` in `text_content`) is painted by the plate, not overlaid, and listed in the report's `in_scene_text`. The plates read `prompt.plate.json` (the scene, the reserved areas, the visual mechanisms and the in-scene text; no overlay copy, no brand names) and the compositions read `prompt.compose.json`; both are re-derived from `prompt.json` by `validate` and `run`, so a copy edit re-draws the formats from the cached plates without paying for a picture again. A format within 10% of the plate's shape is a free crop; a farther one extends the chosen plate once on the same model. Detected elements get **one `[TODO]` ingest slot** each (never the logo), and the original image is wired in for composition only, left out of the render when it shows a person, because the generator copies that face whatever the prompt says (the blueprint's layout carries the composition instead). Each **animal hero** is additionally fused into a generated **multi-view reference sheet** (`image_reference_sheet`, a turnaround built from the one dropped photo) that the render grounds on instead of the lone flat snapshot. Pass `--skip-actor-sheets` to ground straight on the dropped photo. A **person hero** is cast from a company Avatar with `--avatar <handle>`: the slot ingests its identity sheet and `prompt.json` describes that person in the Avatar's own words. The render keeps the face it is given, so it is never a found photo of a real person. The canvas is validated before it's written. stdout returns `{ ok, canvas_path, prompt_path, models, aspect_ratio, placements, output_formats, layout_regions, stats, checklist }`; the **checklist** carries `mode`, `summary` (what a run does: pictures, judge, fonts, logo), `fonts`, `logo`, `in_scene_text` and every real asset to drop in.
|
|
5627
5714
|
|
|
5628
|
-
The
|
|
5715
|
+
**Placement formats.** The canvas delivers the platform's full placement set, each format at its exact size, collected under `final` with the format as its label. The set is derived from `--platform` (`meta` → 4:5 + 9:16, `google` → 1:1 + 4:5 + 16:9, `linkedin` → 1:1, `tiktok` → 9:16, `youtube` → 16:9); override it with `--placements <preset>` or `--placements none` for a single format. The report's `aspect_ratio` is the ratio the plates really render at (the model's nearest to the lead format), and `format_plan` says how each format is made.
|
|
5629
5716
|
|
|
5630
|
-
|
|
5717
|
+
**After a run** the CLI reads the finished ad back on what makes a static win, not a checklist of elements: every delivered image's pixel shape against its format, and one vision pass that reads each word against the copy `prompt.json` holds and checks one dominant idea, clipping, artifacts, the brand with the logo covered and the concept's mechanisms, with every format shown a second time at phone-feed thumbnail size (240px wide, same call): a lead message unreadable at that size is blocking. A missing logo, CTA or copy is never a finding; a logo that is drawn and broken is. stdout carries `verdict` (`ready` | `ready_with_notes` | `needs_fixes` | `not_reviewed`), `judge` (the per-plate scores) and hints that end with what the run made; the findings are stored on the run as its `review`. `needs_fixes` means the ad is not done even though every node completed; its hints name the cheapest fix (overlapping or unreadable copy is fixed with a smaller `size`, a `highlight` or `sticker` treatment or shorter words, which re-draw the formats from the cached plates). **Render budget:** a static ad gets 3 renders per creative in a chat before the person reacts; the fourth exits 2 with `RENDER_BUDGET` without rendering, and `baker creatives feedback record` or `baker creatives select` on that creative opens a new round. Resuming an interrupted run does not count again, and a run that died only on an outage (timeout, network, a provider that says retry) gives its render back.
|
|
5718
|
+
|
|
5719
|
+
The logo is never a slot to fill: use the company's committed logo in `src/brand/logos/` (per `BRAND.md`), never `baker images logo <domain>`, whose Brandfetch CDN often serves an outdated mark.
|
|
5720
|
+
|
|
5721
|
+
The plate prompt is **model-aware**: for **gpt-image** the exclusions (no invented badges/watermark) are hoisted to the very *end* of the prompt where the model reads them as pure exclusions rather than composition. In the baked-copy fallback it quotes every line of `prompt.json`'s copy (never the logo's text) with an instruction to render each **once, verbatim**; that block is re-read from `prompt.json` on every `validate` and `run`, which also upgrades canvases scaffolded before this change. Run `baker canvas critique` on the scaffolded canvas for an advisory read before you spend.
|
|
5631
5722
|
|
|
5632
5723
|
```bash
|
|
5633
5724
|
baker canvas scaffold-static-ad ./reference-ad.png --context "competitor ad for <brand>, <category>, <market>"
|
|
@@ -5645,39 +5736,36 @@ baker canvas run ./static-ad.canvas.json
|
|
|
5645
5736
|
| `--describe-model <id>` | registry default (`~google/gemini-pro-latest`) | Override the `image_describe` model. |
|
|
5646
5737
|
| `--select-model <id>` | registry default (`~google/gemini-flash-latest`) | Override the element-selection `text_generate` model. |
|
|
5647
5738
|
| `--layout-model <id>` | registry default (`~google/gemini-flash-latest`) | Override the global-layout `text_generate` model. |
|
|
5648
|
-
| `--gen-model <id>` |
|
|
5739
|
+
| `--gen-model <id>` | `openai/gpt-image-2.5-sunburst` | Override the plate model. An unknown model id in any `--*-model` flag fails the command (`unknown_model`) before anything is billed. |
|
|
5740
|
+
| `--takes <n>` | `3` | Plates rendered and judged (1-3); `1` skips the judge. A text-only ad renders none. |
|
|
5741
|
+
| `--text-only` | on when the reference pictures nothing but copy | Draw a typographic ad: no picture generated, the copy on the brand colours in its fonts. `--no-text-only` renders a picture anyway. |
|
|
5649
5742
|
| `--platform <channel>` | `meta` | `_definition.md` channel **and** the default placement set the hero fans out to (`meta`\|`google`\|`linkedin`\|`tiktok`\|`youtube`\|`x`\|`other`; `x`/`other` fan out to nothing). |
|
|
5650
5743
|
| `--placements <preset\|none>` | derived from `--platform` | Override the fan-out set: `meta_feed`\|`meta_feed_stories`\|`meta_all`\|`linkedin`\|`google_demandgen`\|`tiktok`\|`youtube`, or `none` for a single base ad. |
|
|
5651
|
-
| `--aspect <ratio>` | inferred from the image, else `9:16` | Force the
|
|
5652
|
-
| `--skip-font` | off |
|
|
5744
|
+
| `--aspect <ratio>` | inferred from the image, else `9:16` | Force the lead format (it leads the placement set when the set has it). |
|
|
5745
|
+
| `--skip-font` | off | Bake the copy into the plates instead of drawing it in a real font (a font file, else the typeface the brand names, else Inter). |
|
|
5653
5746
|
| `--skip-actor-sheets` | off | Ground each animal hero on its lone dropped photo instead of a generated multi-view reference sheet. |
|
|
5654
5747
|
| `--avatar <handles>` | — | Cast company avatars as the image's people, comma-separated in the order they were detected (`marta` or `marta,joan`). Each person slot grounds on that avatar's identity sheet and takes its subject description. Checked before any billed pass; an unknown, not-ready or sheetless avatar fails with code `avatar`. A handle with no person to play is reported in `hints` as `NOT CAST`. |
|
|
5655
5748
|
|
|
5656
5749
|
Scaffolding runs (and bills) the two vision passes; **running** the result generates a billed image. `baker canvas validate` does not check that the `[TODO]` paths exist — supply the real files before `run`.
|
|
5657
5750
|
|
|
5658
|
-
**Resuming an interrupted run.** A long `baker canvas run` (multi-clip video) that is killed mid-render — session end, sandbox pause — leaves a marker under the outputs dir. The next `baker canvas run` of the same canvas automatically **resumes** that run: it reuses the run id so still-running billed jobs re-attach instead of being abandoned and re-billed, and completed nodes come from the cache. Resume also works from a **different workspace or a fresh sandbox**: when no local marker survives, the run history is consulted
|
|
5751
|
+
**Resuming an interrupted run.** A long `baker canvas run` (multi-clip video) that is killed mid-render — session end, sandbox pause — leaves a marker under the outputs dir. The next `baker canvas run` of the same canvas automatically **resumes** that run: it reuses the run id so still-running billed jobs re-attach instead of being abandoned and re-billed, and completed nodes come from the cache. A live render keeps its marker's heartbeat fresh, so a second `run` of the same canvas is refused while it is alive (checked with `kill(pid, 0)` plus the heartbeat, on Linux and macOS alike). Resume also works from a **different workspace or a fresh sandbox**: when no local marker survives, the run history is consulted for a resumable run of the same source (canvas, `prompt.json`, scenes, compositions and formats), and it is adopted when the backend says it may be: interrupted, stopped at a checkpoint, or a `running` row whose keepalive is older than the backend's staleness threshold. Ctrl-C / SIGTERM aborts gracefully — no new nodes dispatch, a resumable snapshot is flushed, and the marker survives. A clean completion (or a handled failure) clears the marker, so a normal re-run starts a fresh generation. Force a new run with `--fresh`, or pin a specific run with `--run-id <id>` (also the escape hatch to adopt a run that is reported as concurrently live). Independent same-layer nodes (e.g. video clips) fan out in parallel up to `--parallel`/`--concurrency` (default 8; env `BAKER_CANVAS_CONCURRENCY`).
|
|
5659
5752
|
|
|
5660
|
-
#### `baker canvas rerun <slug> [flags]`
|
|
5753
|
+
#### `baker canvas rerun <slug> [flags]` (deprecated)
|
|
5661
5754
|
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
| Flag | Default | Meaning |
|
|
5665
|
-
| --- | --- | --- |
|
|
5666
|
-
| `--force-remote` | off | Overwrite local files whose content differs from the snapshot (otherwise a conflict aborts with the differing paths). |
|
|
5667
|
-
| `--fresh` | off | Start a new run id instead of resuming an interrupted one. |
|
|
5668
|
-
| `--regenerate <ids>` | — | Same as `canvas run --regenerate`. |
|
|
5669
|
-
| `--concurrency <n>` | — | Same as `canvas run --concurrency`. |
|
|
5755
|
+
Now `baker canvas run --slug <slug>`; `rerun` stays as a hidden alias for one release and prints a deprecation note. Every `canvas run` of a creative records a portable snapshot (the canvas JSON plus the local files it ingests: prompt blueprints, composition dirs, reference images) in durable storage, so `run --slug` can rebuild `src/creatives/<slug>/` in a workspace that never had it. A snapshot whose files are exactly the last one uploaded for the creative is reused without a request, and a file the store already holds is not uploaded again. Files the snapshot could not include (missing at run time, or oversized) are listed as warnings.
|
|
5670
5756
|
|
|
5671
5757
|
```bash
|
|
5672
|
-
baker canvas
|
|
5673
|
-
baker canvas
|
|
5758
|
+
baker canvas run --slug spring-offer
|
|
5759
|
+
baker canvas run --slug spring-offer --force-remote --regenerate gen
|
|
5674
5760
|
```
|
|
5675
5761
|
|
|
5676
|
-
|
|
5762
|
+
#### `baker canvas stale`
|
|
5763
|
+
|
|
5764
|
+
Lists the creatives edited since their last render in this workspace, each with the command to re-render it: `{ ok, data: { stale: [{ slug, canvasPath, renderedRunId }] }, hints? }`. "Edited" is one content fingerprint of the creative's source: the canvas, `prompt.json`, every `scenes/sNN.json`, every local composition directory the canvas renders, and the `formats` in `_definition.md`, so a change counts however it was made. A completed `run` records the fingerprint it drew in `canvas/.rendered/<slug>.json`, and a render attempt of the current source is not asked for again until the source moves. Creatives never rendered in this workspace, and ones with an unfilled `[TODO]` asset slot, are not listed. Read-only, offline, free. The scaffold's turn-end reminder and its `validate-creative-renders` check read this list; the same fingerprint is what `sync-definition` and `run` post as `sourceSha`.
|
|
5677
5765
|
|
|
5678
5766
|
#### `baker canvas inspect <run_id> [--thumbnails]`
|
|
5679
5767
|
|
|
5680
|
-
One-page summary of a
|
|
5768
|
+
One-page summary of a run: per-node duration + cache status, list of files in the run dir, optional video thumbnails (start/middle/end frames extracted via ffmpeg). When the run has no folder in this workspace (an older version, or a run from another chat), it is read from the company's run history instead (`source: "run_history"`): status, stats, per-node results and the durable URL of every output.
|
|
5681
5769
|
|
|
5682
5770
|
```bash
|
|
5683
5771
|
baker canvas inspect r_01JXYZ...
|
|
@@ -6147,23 +6235,93 @@ import {
|
|
|
6147
6235
|
|
|
6148
6236
|
## Creatives
|
|
6149
6237
|
|
|
6150
|
-
Publish an approved canvas render as a first-class Baker creative.
|
|
6238
|
+
Publish an approved canvas render as a first-class Baker creative: a static image (PNG, JPG, WebP) or a finished video (MP4, MOV, WebM). An image uploads to the Baker image library (tagged `creative`); a video uploads to the Baker video library and is saved once it plays (bounded at 3 minutes). A creative record is created/updated, and the command prints the creative reference JSON the dashboard renders in chat.
|
|
6151
6239
|
|
|
6152
6240
|
```bash
|
|
6153
|
-
baker creatives publish
|
|
6154
|
-
--slug spring-offer-4x5 --run-id r_01JXYZ... \
|
|
6241
|
+
baker creatives publish --title "Spring Offer 4x5" \
|
|
6242
|
+
--slug spring-offer-4x5 --run-id r_01JXYZ... --output 4:5 \
|
|
6155
6243
|
--source-reference-url "https://www.facebook.com/ads/library/?id=..."
|
|
6244
|
+
# or from a local file
|
|
6245
|
+
baker creatives publish ./canvas/<run_id>/<final>.png --title "Spring Offer 4x5" --slug spring-offer-4x5 --run-id r_01JXYZ...
|
|
6246
|
+
# a finished video render (needs the file and --slug)
|
|
6247
|
+
baker creatives publish ./canvas/<run_id>/final.mp4 --title "Spring Offer Reel" \
|
|
6248
|
+
--slug spring-offer-reel --run-id r_01JXYZ...
|
|
6156
6249
|
```
|
|
6157
6250
|
|
|
6158
6251
|
| Flag | Effect |
|
|
6159
6252
|
|---|---|
|
|
6253
|
+
| `<file>` | The image or video. For an image it is optional with `--run-id`: the run's own saved output (from its run record, sha-verified) is used instead, so a workspace that never had the run's folder can publish it. |
|
|
6160
6254
|
| `--title <text>` | Required. Human title for the creative. |
|
|
6161
|
-
| `--slug <slug>` | Creative slug (`src/creatives/<slug>/`)
|
|
6255
|
+
| `--slug <slug>` | Creative slug (`src/creatives/<slug>/`): attaches the image or video to that creative's row, marks it `published`. Required for a video. With `--run-id` an image publish also sets `status: published` in the creative's `_definition.md` (`definitionStatus` in the output). |
|
|
6162
6256
|
| `--run-id <r_…>` | Pins the approved generation from the creative's run history as the published one. |
|
|
6257
|
+
| `--output <label\|slot\|n>` | With `--run-id` and no file: which of the run's images to save when it made several (a label such as `4:5`, a slot, or a 1-based number). |
|
|
6163
6258
|
| `--source-reference-url <url>` | Original reference ad URL, recorded on the creative. |
|
|
6164
|
-
| `--context <text>` | Optional describe context for the uploaded image
|
|
6259
|
+
| `--context <text>` | Optional describe context for the uploaded image or video. |
|
|
6260
|
+
|
|
6261
|
+
An image prints `{ ok, data: { creativeId, imageId, reference } }`. A video prints `{ ok, data: { creativeId, videoId, reference }, hints }`, where `reference.asset.kind` is `video` (with `videoId`, `muxPlaybackId`, the poster as `thumbnailUrl`, size and duration) and `hints` names the next step to put it in a Meta ad: `baker ads meta media upload --kind video --video-id <videoId>`.
|
|
6262
|
+
|
|
6263
|
+
Without `--slug` an image behaves as before (one creative record per published image). With `--slug` it upserts the repo-convention row — the same one the dashboard's Creatives tab and the `src/creatives/{slug}/` folder describe — so publish, repo sync, and run history all land on a single record regardless of order.
|
|
6264
|
+
|
|
6265
|
+
### `baker creatives performance`
|
|
6266
|
+
|
|
6267
|
+
How each published creative is doing in the live Meta ads that show it. A creative is linked to an ad when a chat's publish uploads its image or video to Meta and creates an ad with it (or swaps an existing ad onto it); the results are read live from Meta.
|
|
6268
|
+
|
|
6269
|
+
```bash
|
|
6270
|
+
baker creatives performance # every creative in an ad, last 30 days
|
|
6271
|
+
baker creatives performance --slug spring-offer # one creative
|
|
6272
|
+
baker creatives performance --days 14 --full # + ids, last-7-day figures, one row per ad
|
|
6273
|
+
```
|
|
6274
|
+
|
|
6275
|
+
| Flag | Effect |
|
|
6276
|
+
|---|---|
|
|
6277
|
+
| `--slug <slug>` | Only this creative. |
|
|
6278
|
+
| `--days <n>` | Window ending today, 1-90 (default 30). |
|
|
6279
|
+
| `--full` | Adds creative, image and video ids, result type, days live, last-7-day CTR and cost per result, a video's watch funnel (`video`: 3-second views, ThruPlays, 25/50/75/100% watched), and one row per ad. |
|
|
6280
|
+
|
|
6281
|
+
Each creative reports `spend`, `impressions`, `ctr` (percent), `cpc`, `results`, `costPerResult`, `frequency`, a `fatigue` verdict (`fresh`, `fading`, `fatigued`, `dead`, `too_early`, `no_delivery`) with its reason, and `definition`: the angle, hook, mechanism, dominantSlot, persona, awareness, hypothesis and reference fields read from the local `src/creatives/<slug>/_definition.md`. A video creative also reports `hookRate` (percent of impressions that watched 3 seconds) and `holdRate` (percent of those that reached a ThruPlay); a static one carries neither. `hints` name worn-out creatives, videos whose hook rate is under 25% (the first 3 seconds need new hooks) or whose hold rate is under 30% behind a working hook (the body loses people), the angle, mechanism and dominant slot with the lowest cost per result (each ranked once two or more values have results), and unreadable ad accounts.
|
|
6282
|
+
|
|
6283
|
+
### `baker creatives feedback [--slug <slug>] [--full]`
|
|
6284
|
+
|
|
6285
|
+
What reviewers said about creative versions in Baker (Approve, or Not this one with reasons and a note; choosing a version to keep counts as an approval). Compact by default: approved and rejected counts, the top rejection reasons, what approved versions share (format, platform, option) and the 8 latest verdicts, plus `hints[]` saying what to do differently. `--slug` narrows to one creative; `--full` returns every verdict (up to 200) with whole notes.
|
|
6286
|
+
|
|
6287
|
+
```bash
|
|
6288
|
+
baker creatives feedback
|
|
6289
|
+
baker creatives feedback --slug spring-offer --full
|
|
6290
|
+
```
|
|
6291
|
+
|
|
6292
|
+
Each entry carries its `source`: `review` (the buttons in Baker), `selection` (choosing or saving the version) or `chat` (what the person wrote in a chat, recorded with `feedback record`).
|
|
6293
|
+
|
|
6294
|
+
### `baker creatives feedback record`
|
|
6295
|
+
|
|
6296
|
+
Record what the person said about one version in a chat: an approval, or a rejection with reasons and their own words. It is stored like the Approve / Not this one buttons, as the verdict of the person who wrote the chat's latest message (`recordedFor: "sender"`), or of the chat itself when nobody has written in it (`"chat"`). One verdict per person per version or option: a later one replaces theirs. Must run from a chat.
|
|
6297
|
+
|
|
6298
|
+
```bash
|
|
6299
|
+
baker creatives feedback record --slug spring-offer --run r_01K… --verdict rejected \
|
|
6300
|
+
--reasons text_mistakes,weak_hook --note "the price is 49 not 59"
|
|
6301
|
+
baker creatives feedback record --slug spring-offer --run r_01K… --option 2 --verdict approved
|
|
6302
|
+
```
|
|
6303
|
+
|
|
6304
|
+
| Flag | Effect |
|
|
6305
|
+
|---|---|
|
|
6306
|
+
| `--slug <slug>` | Required. The creative (`src/creatives/<slug>/`). |
|
|
6307
|
+
| `--run <r_…>` | Required. The version. |
|
|
6308
|
+
| `--verdict approved\|rejected` | Required. |
|
|
6309
|
+
| `--option <o>` | One option of a version that made several: its label, its number (`2`) or its slot. |
|
|
6310
|
+
| `--reasons <a,b>` | Comma-separated: `text_mistakes`, `logo_or_brand_wrong`, `weak_hook`, `wrong_message`, `looks_fake`, `not_our_style`, `voice_or_sound`, `pacing`, `other`. The two video reasons: `voice_or_sound` (the voiceover, music or mix) and `pacing` (too slow, too fast, the hook lands late). A rejection needs reasons or a note. |
|
|
6311
|
+
| `--note <text>` | The person's own words (stored up to 1000 characters). |
|
|
6312
|
+
|
|
6313
|
+
After a rejection, `hints[]` restates what to change, starting from that version.
|
|
6314
|
+
|
|
6315
|
+
### `baker creatives select`
|
|
6316
|
+
|
|
6317
|
+
Choose the version of a creative to keep, or only some of its options, the way choosing it in Baker does: the creative shows that version as the chosen one, and the choice records the chat sender's approval (`source: "selection"`). Naming options replaces the earlier pick. It does not add the image to the library; that is `baker creatives publish`, which, given `--run-id`, also counts as choosing that version.
|
|
6318
|
+
|
|
6319
|
+
```bash
|
|
6320
|
+
baker creatives select --slug spring-offer --run r_01K…
|
|
6321
|
+
baker creatives select --slug spring-offer --run r_01K… --options "beach,2"
|
|
6322
|
+
```
|
|
6165
6323
|
|
|
6166
|
-
|
|
6324
|
+
Prints `{ ok, data: { creative, runId, kept?, options, approvalRecorded }, hints }`, where `options` lists every option the version offers.
|
|
6167
6325
|
|
|
6168
6326
|
## Landing Quality (`baker landing`)
|
|
6169
6327
|
|