@koda-sl/baker-cli 0.133.0-dev.42a8955b3 → 0.133.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 +10 -31
- package/dist/{chunk-O3TH7JW5.js → chunk-UBFU3E7L.js} +58 -15
- package/dist/chunk-UBFU3E7L.js.map +1 -0
- package/dist/cli.js +1386 -1475
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-O3TH7JW5.js.map +0 -1
package/README.md
CHANGED
|
@@ -954,33 +954,6 @@ Each finding: `{id, area, check, status, severity, evidence, fix: {explanation,
|
|
|
954
954
|
|
|
955
955
|
---
|
|
956
956
|
|
|
957
|
-
### Landing Analytics (`baker analytics`)
|
|
958
|
-
|
|
959
|
-
First-party analytics for every published landing page — pageviews, unique visitors, sessions, and form submits, collected by a built-in consent-aware beacon (no setup, no third-party script) and stored in Tinybird. Read-only; scoped to the API key's company.
|
|
960
|
-
|
|
961
|
-
```bash
|
|
962
|
-
baker analytics # = overview, last 28 days
|
|
963
|
-
baker analytics overview --period 7d
|
|
964
|
-
baker analytics timeseries --period 28d --output md
|
|
965
|
-
baker analytics pages --period 28d --limit 20
|
|
966
|
-
baker analytics sources --period 90d
|
|
967
|
-
baker analytics events --period 7d
|
|
968
|
-
```
|
|
969
|
-
|
|
970
|
-
| Subcommand | Returns |
|
|
971
|
-
|---|---|
|
|
972
|
-
| `overview` | pageviews, visitors, sessions, form_submits |
|
|
973
|
-
| `timeseries` | daily pageviews/visitors/form_submits |
|
|
974
|
-
| `pages` | top pages by pageviews |
|
|
975
|
-
| `sources` | top traffic sources (utm_source → referrer domain → direct) |
|
|
976
|
-
| `events` | non-pageview events (form_submit + custom) |
|
|
977
|
-
|
|
978
|
-
Flags: `--period today|7d|28d|90d` (default `28d`), `--limit 1-100` (list reports), `--output json|files|md`, `--fields a,b`.
|
|
979
|
-
|
|
980
|
-
Backend env: `TINYBIRD_TOKEN` (read token) and optional `TINYBIRD_HOST` on the Convex deployment — unset, commands return a clean "not configured" error. See `tinybird/README.md`.
|
|
981
|
-
|
|
982
|
-
---
|
|
983
|
-
|
|
984
957
|
### Google Analytics 4 (`baker ga4`)
|
|
985
958
|
|
|
986
959
|
GA4 commands for multi-channel audits. Playbook-aligned presets, property health audits, and free-form Data API queries.
|
|
@@ -3121,7 +3094,8 @@ The adaptation prompt is baked into the backend and is direction-aware (a wider
|
|
|
3121
3094
|
| Name | Type | Required | Notes |
|
|
3122
3095
|
|---|---|---|---|
|
|
3123
3096
|
| `model` | enum | yes | one of `google/gemini-3-pro-image-preview`, `google/gemini-3.1-flash-image-preview` |
|
|
3124
|
-
| `formats` | enum[] |
|
|
3097
|
+
| `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 |
|
|
3098
|
+
| `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`. |
|
|
3125
3099
|
| `guidance` | string | no | hints appended to the baked prompt, e.g. `"keep the CTA button fully visible"` |
|
|
3126
3100
|
| `image_size` | enum | no | `1K \| 2K \| 4K` on `3-pro`, `0.5K \| 1K \| 2K \| 4K` on `3.1-flash` |
|
|
3127
3101
|
|
|
@@ -3131,9 +3105,10 @@ The adaptation prompt is baked into the backend and is direction-aware (a wider
|
|
|
3131
3105
|
|---|---|---|
|
|
3132
3106
|
| `images` | image[] | one per requested format, in `formats` order; each carries `metadata.aspect_ratio` and `metadata.adapted` (`false` = source passed through) |
|
|
3133
3107
|
|
|
3134
|
-
**Cost** — per-format estimate: 20 credits on `3-pro`, 5 on `3.1-flash`, × `
|
|
3108
|
+
**Cost** — per-format estimate: 20 credits on `3-pro`, 5 on `3.1-flash`, × the resolved format count (a `platform` preset expands first; backend charges actual OpenRouter usage; pass-through formats are free).
|
|
3135
3109
|
|
|
3136
3110
|
Guidance:
|
|
3111
|
+
- 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.
|
|
3137
3112
|
- List the source's own ratio in `formats` too when you want the full set in one output — it passes through for free.
|
|
3138
3113
|
- Use `google/gemini-3.1-flash-image-preview` while iterating; switch to `google/gemini-3-pro-image-preview` for final-quality adaptation.
|
|
3139
3114
|
- Indexes are positional: `$ref:adapt.images#1` is always the second entry of `formats`.
|
|
@@ -4019,7 +3994,9 @@ Turn a source/inspiration image into a **runnable, self-validated static-ad canv
|
|
|
4019
3994
|
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.
|
|
4020
3995
|
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.
|
|
4021
3996
|
|
|
4022
|
-
It then scaffolds a canvas that ingests `prompt.json`, wires **one `[TODO]` ingest slot per detected element** (plus an optional brand-font → type-specimen) into `image_generate`, and wires the original image in for composition only. Each **person/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 — the same identity lock the video scaffold uses, so the face/muzzle stays consistent and artifact-free from a single reference. Pass `--skip-actor-sheets` to ground straight on the dropped photo. The canvas is validated before it's written. stdout returns `{ ok, canvas_path, prompt_path, models, layout_regions, stats, checklist }` — the **checklist** lists every real asset to drop in (and which heroes get a sheet).
|
|
3997
|
+
It then scaffolds a canvas that ingests `prompt.json`, wires **one `[TODO]` ingest slot per detected element** (plus an optional brand-font → type-specimen) into `image_generate`, and wires the original image in for composition only. Each **person/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 — the same identity lock the video scaffold uses, so the face/muzzle stays consistent and artifact-free from a single reference. Pass `--skip-actor-sheets` to ground straight on the dropped photo. 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** lists every real asset to drop in (and which heroes get a sheet).
|
|
3998
|
+
|
|
3999
|
+
**Automatic placement fan-out.** By default the canvas doesn't stop at one ratio: the hero renders once at the platform's primary ratio, then an `image_aspect_adapt` node recomposes it into that platform's full placement set, and the canvas `output` points at the adapted images. The placement 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 disable it with `--placements none` for a single base ad. The hero ratio passes through the adapt node **free**; each other ratio is a billed recomposition — and because adaptation re-generates, every ratio should be eyeballed for text and brand-color fidelity before publish (`ad-formats.md` in the scaffold skill covers this).
|
|
4023
4000
|
|
|
4024
4001
|
The generation prompt is **model-aware and typography-hardened** (a static ad is the only path that bakes text *into* the image — video frames are clean plates). It resolves the image model's profile, so 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, and it quotes every blueprint string with an instruction to render each **once, verbatim**, lock brand names letter-by-letter, and keep type crisp and legible — the fix for the #1 static-ad defect (garbled/duplicated/dropped copy). Run `baker canvas critique` on the scaffolded canvas for an advisory read on text-legibility risk, identity grounding, and brand-type fidelity before you spend.
|
|
4025
4002
|
|
|
@@ -4040,7 +4017,9 @@ baker canvas run ./static-ad.canvas.json
|
|
|
4040
4017
|
| `--select-model <id>` | registry default (`~google/gemini-flash-latest`) | Override the element-selection `text_generate` model. |
|
|
4041
4018
|
| `--layout-model <id>` | registry default (`~google/gemini-flash-latest`) | Override the global-layout `text_generate` model. |
|
|
4042
4019
|
| `--gen-model <id>` | registry default (`openai/gpt-5.4-image-2`) | Override the `image_generate` model. |
|
|
4043
|
-
| `--
|
|
4020
|
+
| `--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). |
|
|
4021
|
+
| `--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. |
|
|
4022
|
+
| `--aspect <ratio>` | inferred from the image, else `9:16` | Force the hero's aspect ratio (clamped into the placement set so it stays a free pass-through). |
|
|
4044
4023
|
| `--skip-font` | off | Skip the brand-font → type-specimen slot. |
|
|
4045
4024
|
| `--skip-actor-sheets` | off | Ground each person/animal on its lone dropped photo instead of a generated multi-view reference sheet. |
|
|
4046
4025
|
|
|
@@ -7,9 +7,9 @@ import {
|
|
|
7
7
|
__toESM
|
|
8
8
|
} from "./chunk-RK67WL4O.js";
|
|
9
9
|
|
|
10
|
-
//
|
|
10
|
+
// ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/index.js
|
|
11
11
|
var require_safe_stable_stringify = __commonJS({
|
|
12
|
-
"
|
|
12
|
+
"../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/index.js"(exports, module) {
|
|
13
13
|
"use strict";
|
|
14
14
|
var { hasOwnProperty } = Object.prototype;
|
|
15
15
|
var stringify = configure2();
|
|
@@ -1002,7 +1002,39 @@ function describeCause(c) {
|
|
|
1002
1002
|
}
|
|
1003
1003
|
}
|
|
1004
1004
|
|
|
1005
|
-
//
|
|
1005
|
+
// ../canvas-contract/src/adPlatformFormats.ts
|
|
1006
|
+
var AD_FORMAT_PLATFORMS = [
|
|
1007
|
+
"meta_feed",
|
|
1008
|
+
"meta_feed_stories",
|
|
1009
|
+
"meta_all",
|
|
1010
|
+
"linkedin",
|
|
1011
|
+
"google_demandgen",
|
|
1012
|
+
"tiktok",
|
|
1013
|
+
"youtube"
|
|
1014
|
+
];
|
|
1015
|
+
var AD_PLATFORM_FORMAT_PRESETS = {
|
|
1016
|
+
meta_feed: { formats: ["4:5"], note: "Meta/Instagram feed static." },
|
|
1017
|
+
meta_feed_stories: { formats: ["4:5", "9:16"], note: "Meta feed + Stories/Reels." },
|
|
1018
|
+
meta_all: { formats: ["4:5", "9:16", "1:1"], note: "Meta feed + Stories/Reels + square." },
|
|
1019
|
+
linkedin: { formats: ["1:1"], note: "LinkedIn single-image feed (square)." },
|
|
1020
|
+
google_demandgen: {
|
|
1021
|
+
// Renders 16:9 too so the off-grid 1.91:1 landscape has a native source to crop from.
|
|
1022
|
+
formats: ["1:1", "4:5", "16:9"],
|
|
1023
|
+
offGrid: ["1.91:1"],
|
|
1024
|
+
note: "Google Demand Gen; also wants 1.91:1 landscape \u2014 crop that from the rendered 16:9."
|
|
1025
|
+
},
|
|
1026
|
+
tiktok: { formats: ["9:16"], note: "TikTok full-screen vertical." },
|
|
1027
|
+
youtube: { formats: ["16:9"], note: "YouTube in-stream / companion display." }
|
|
1028
|
+
};
|
|
1029
|
+
function platformFormats(platform) {
|
|
1030
|
+
return AD_PLATFORM_FORMAT_PRESETS[platform].formats;
|
|
1031
|
+
}
|
|
1032
|
+
function resolveAdaptFormats(params) {
|
|
1033
|
+
if (params.platform) return platformFormats(params.platform);
|
|
1034
|
+
return params.formats ?? [];
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/esm/wrapper.js
|
|
1006
1038
|
var import__ = __toESM(require_safe_stable_stringify(), 1);
|
|
1007
1039
|
var configure = import__.default.configure;
|
|
1008
1040
|
var wrapper_default = import__.default;
|
|
@@ -1265,16 +1297,20 @@ var MODEL_REGISTRY = {
|
|
|
1265
1297
|
},
|
|
1266
1298
|
image_aspect_adapt: {
|
|
1267
1299
|
// Multi-format adaptation of one creative. The recomposition prompt is
|
|
1268
|
-
// baked into the backend handler
|
|
1269
|
-
//
|
|
1270
|
-
//
|
|
1271
|
-
//
|
|
1300
|
+
// baked into the backend handler. The target ratios come from EITHER an
|
|
1301
|
+
// explicit `formats` list OR a `platform` preset that expands to the
|
|
1302
|
+
// canonical set; exactly one of the two is required — that "one of" gate,
|
|
1303
|
+
// and per-element ratio validation, live in the node's Zod schema (the
|
|
1304
|
+
// registry can't express "one of" or gate array elements), so `formats`
|
|
1305
|
+
// is not listed as registry-required. The source creative is a required
|
|
1306
|
+
// input slot.
|
|
1272
1307
|
"google/gemini-3-pro-image-preview": {
|
|
1273
1308
|
label: "Google Gemini 3 Pro Image (Nano Banana Pro)",
|
|
1274
1309
|
inputs: [{ kind: "image", mimes: OPENROUTER_IMAGE_MIMES }],
|
|
1275
|
-
required: [
|
|
1310
|
+
required: [],
|
|
1276
1311
|
params: {
|
|
1277
1312
|
formats: { kind: "json" },
|
|
1313
|
+
platform: { kind: "string", enum: AD_FORMAT_PLATFORMS },
|
|
1278
1314
|
guidance: { kind: "string" },
|
|
1279
1315
|
image_size: { kind: "string", enum: OPENROUTER_IMAGE_SIZE }
|
|
1280
1316
|
}
|
|
@@ -1282,9 +1318,10 @@ var MODEL_REGISTRY = {
|
|
|
1282
1318
|
"google/gemini-3.1-flash-image-preview": {
|
|
1283
1319
|
label: "Google Gemini 3.1 Flash Image (Preview)",
|
|
1284
1320
|
inputs: [{ kind: "image", mimes: OPENROUTER_IMAGE_MIMES }],
|
|
1285
|
-
required: [
|
|
1321
|
+
required: [],
|
|
1286
1322
|
params: {
|
|
1287
1323
|
formats: { kind: "json" },
|
|
1324
|
+
platform: { kind: "string", enum: AD_FORMAT_PLATFORMS },
|
|
1288
1325
|
guidance: { kind: "string" },
|
|
1289
1326
|
image_size: { kind: "string", enum: OPENROUTER_IMAGE_SIZE_EXTENDED }
|
|
1290
1327
|
}
|
|
@@ -6860,23 +6897,27 @@ var ASPECT_ADAPT_MODELS = ["google/gemini-3-pro-image-preview", "google/gemini-3
|
|
|
6860
6897
|
var ASPECT_ADAPT_FORMATS = ["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"];
|
|
6861
6898
|
var ImageAspectAdaptParams = z18.object({
|
|
6862
6899
|
model: z18.enum(ASPECT_ADAPT_MODELS),
|
|
6863
|
-
formats: z18.array(z18.enum(ASPECT_ADAPT_FORMATS)).min(1).max(6).refine((formats) => new Set(formats).size === formats.length, { message: "formats must be unique" }),
|
|
6900
|
+
formats: z18.array(z18.enum(ASPECT_ADAPT_FORMATS)).min(1).max(6).refine((formats) => new Set(formats).size === formats.length, { message: "formats must be unique" }).optional(),
|
|
6901
|
+
platform: z18.enum(AD_FORMAT_PLATFORMS).optional(),
|
|
6864
6902
|
guidance: z18.string().min(1).optional(),
|
|
6865
6903
|
image_size: z18.enum(["0.5K", "1K", "2K", "4K"]).optional()
|
|
6866
|
-
}).strict()
|
|
6904
|
+
}).strict().refine((p) => p.formats === void 0 !== (p.platform === void 0), {
|
|
6905
|
+
message: "provide exactly one of `formats` (explicit ratios) or `platform` (a placement preset)",
|
|
6906
|
+
path: ["formats"]
|
|
6907
|
+
});
|
|
6867
6908
|
var imageAspectAdaptNode = delegated({
|
|
6868
6909
|
id: "image_aspect_adapt",
|
|
6869
6910
|
version: "1.0.0",
|
|
6870
6911
|
category: "image",
|
|
6871
|
-
summary: "Adapt ONE creative into multiple aspect ratios (Meta: 9:16 stories, 1:1 feed, 4:5, 16:9\u2026) in a single step. AI recomposes the layout per format \u2014 identical subject, text, logos, colors, and style; the scene is extended/restructured, never stretched or cropped. Formats that already match the source ratio pass through unchanged at zero cost. Outputs are ordered exactly as
|
|
6872
|
-
when_to_use: "Use after a hero creative exists (image_generate, ingest, image_search) to fan it out to every placement format \u2014 wire the creative into `source` and
|
|
6912
|
+
summary: "Adapt ONE creative into multiple aspect ratios (Meta: 9:16 stories, 1:1 feed, 4:5, 16:9\u2026) in a single step. Give it either an explicit `formats` list OR a `platform` preset (meta_feed, meta_feed_stories, meta_all, linkedin, google_demandgen, tiktok, youtube) that expands to that platform's canonical set. AI recomposes the layout per format \u2014 identical subject, text, logos, colors, and style; the scene is extended/restructured, never stretched or cropped. Formats that already match the source ratio pass through unchanged at zero cost. Outputs are ordered exactly as the resolved format list.",
|
|
6913
|
+
when_to_use: "Use after a hero creative exists (image_generate, ingest, image_search) to fan it out to every placement format \u2014 wire the creative into `source` and set EITHER `formats` (exact ratios) OR `platform` (e.g. `meta_feed_stories`). Cost is estimated per format; formats matching the source ratio are free pass-throughs. Off-grid platform targets (e.g. Google 1.91:1) are not rendered here \u2014 crop them from the nearest ratio with `baker images crop`. Pick `google/gemini-3.1-flash-image-preview` (Nano Banana flash) while iterating, `google/gemini-3-pro-image-preview` (Nano Banana Pro) for final-quality adaptation.",
|
|
6873
6914
|
inputs: z18.object({ source: ImageRef }).loose(),
|
|
6874
6915
|
params: ImageAspectAdaptParams,
|
|
6875
6916
|
outputs: z18.object({ images: z18.array(ImageRef).min(1) }).strict(),
|
|
6876
6917
|
outputKinds: { images: "image" },
|
|
6877
6918
|
cost: ({ params }) => {
|
|
6878
6919
|
const p = params;
|
|
6879
|
-
const count =
|
|
6920
|
+
const count = Math.max(1, resolveAdaptFormats(p ?? {}).length);
|
|
6880
6921
|
return {
|
|
6881
6922
|
credits: count * (p?.model === "google/gemini-3-pro-image-preview" ? 20 : 5),
|
|
6882
6923
|
seconds_estimate: 30 + 30 * count
|
|
@@ -7689,6 +7730,8 @@ export {
|
|
|
7689
7730
|
RunAbortedError,
|
|
7690
7731
|
LayerExecutionError,
|
|
7691
7732
|
describeFailureReason,
|
|
7733
|
+
AD_FORMAT_PLATFORMS,
|
|
7734
|
+
platformFormats,
|
|
7692
7735
|
SEEDANCE_DURATIONS,
|
|
7693
7736
|
ELEVENLABS_MAX_MUSIC_LENGTH_MS,
|
|
7694
7737
|
IMAGE_GENERATE_MODELS,
|
|
@@ -7719,4 +7762,4 @@ export {
|
|
|
7719
7762
|
defaultRegistry,
|
|
7720
7763
|
createEngineFromEnv
|
|
7721
7764
|
};
|
|
7722
|
-
//# sourceMappingURL=chunk-
|
|
7765
|
+
//# sourceMappingURL=chunk-UBFU3E7L.js.map
|