@koda-sl/baker-cli 0.133.0-dev.42a8955b3 → 0.133.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -31
- package/dist/{chunk-O3TH7JW5.js → chunk-Q4UI5JZN.js} +107 -28
- package/dist/chunk-Q4UI5JZN.js.map +1 -0
- package/dist/cli.js +1388 -1476
- 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
|
}
|
|
@@ -2451,6 +2488,11 @@ import { readFile as readFile2 } from "fs/promises";
|
|
|
2451
2488
|
import path3 from "path";
|
|
2452
2489
|
import { z as z2 } from "zod";
|
|
2453
2490
|
|
|
2491
|
+
// src/engine/lib/url.ts
|
|
2492
|
+
function looksLikeHttpUrl(value) {
|
|
2493
|
+
return typeof value === "string" && /^https?:\/\//i.test(value.trim());
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2454
2496
|
// src/engine/scaffold/lib/prompt-profiles.ts
|
|
2455
2497
|
var VEO_PERSON_GENERATION = "allow_adult";
|
|
2456
2498
|
var VEO_NEGATIVE_PROMPT = "subtitles, captions, on-screen text, watermark, logo, warped face, distorted hands, extra fingers, low quality";
|
|
@@ -2683,7 +2725,8 @@ var STAGE_CODES = {
|
|
|
2683
2725
|
ODD_DIMENSIONS: "VIDEO_ODD_DIMENSIONS",
|
|
2684
2726
|
REGION_DROPPED: "VIDEO_REGION_DROPPED",
|
|
2685
2727
|
OVERLAY_OUT_OF_BOUNDS: "VIDEO_OVERLAY_OUT_OF_BOUNDS",
|
|
2686
|
-
ORPHAN_NODE: "ORPHAN_NODE"
|
|
2728
|
+
ORPHAN_NODE: "ORPHAN_NODE",
|
|
2729
|
+
INGEST_PATH_URL_HEALED: "INGEST_PATH_URL_HEALED"
|
|
2687
2730
|
};
|
|
2688
2731
|
var SPAN_MODEL_SLACK_S = 0.25;
|
|
2689
2732
|
var VIDEO_TIME_SLACK_S = 0.75;
|
|
@@ -2740,6 +2783,7 @@ function validateCanvas(input, registry) {
|
|
|
2740
2783
|
checkUnknownNodeTypes(ctx);
|
|
2741
2784
|
if (issues.some((i) => i.code === STAGE_CODES.UNKNOWN_NODE)) return { ok: false, issues };
|
|
2742
2785
|
checkAllParams(ctx);
|
|
2786
|
+
checkIngestPathUrlHeals(ctx);
|
|
2743
2787
|
checkAllModelParams(ctx);
|
|
2744
2788
|
checkAllRefs(ctx);
|
|
2745
2789
|
checkAllModelInputs(ctx);
|
|
@@ -2862,6 +2906,22 @@ function checkAllParams(ctx) {
|
|
|
2862
2906
|
pushZodIssues(ctx.issues, parsed.error, `nodes[${i}].params`, STAGE_CODES.PARAMS, n.id, n.type);
|
|
2863
2907
|
}
|
|
2864
2908
|
}
|
|
2909
|
+
function checkIngestPathUrlHeals(ctx) {
|
|
2910
|
+
for (let i = 0; i < ctx.canvas.nodes.length; i++) {
|
|
2911
|
+
const n = ctx.canvas.nodes[i];
|
|
2912
|
+
if (!n || n.type !== "ingest") continue;
|
|
2913
|
+
const params = n.params ?? {};
|
|
2914
|
+
if (params.source !== "path" || !looksLikeHttpUrl(params.path)) continue;
|
|
2915
|
+
ctx.issues.push({
|
|
2916
|
+
path: `nodes[${i}].params.source`,
|
|
2917
|
+
code: STAGE_CODES.INGEST_PATH_URL_HEALED,
|
|
2918
|
+
message: `ingest "${n.id}" declares source:"path" but its value is a URL \u2014 running it as source:"url". Update the canvas JSON to source:"url" so it matches what baker runs.`,
|
|
2919
|
+
severity: "warning",
|
|
2920
|
+
node_id: n.id,
|
|
2921
|
+
node_type: n.type
|
|
2922
|
+
});
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2865
2925
|
function checkAllModelParams(ctx) {
|
|
2866
2926
|
for (let i = 0; i < ctx.canvas.nodes.length; i++) {
|
|
2867
2927
|
const n = ctx.canvas.nodes[i];
|
|
@@ -4497,18 +4557,30 @@ function mapClientError(ctx, e) {
|
|
|
4497
4557
|
// src/engine/nodes/ingest.ts
|
|
4498
4558
|
var execFile = promisify(execFileCb);
|
|
4499
4559
|
var ExpectEnum = z5.enum(["image", "video", "audio", "text", "json", "font"]);
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4560
|
+
function coercePathUrlToUrl(input) {
|
|
4561
|
+
if (!input || typeof input !== "object") return input;
|
|
4562
|
+
const obj = input;
|
|
4563
|
+
if (obj.source === "path" && looksLikeHttpUrl(obj.path)) {
|
|
4564
|
+
const { path: _path, ...rest } = obj;
|
|
4565
|
+
return { ...rest, source: "url", url: obj.path.trim() };
|
|
4566
|
+
}
|
|
4567
|
+
return input;
|
|
4568
|
+
}
|
|
4569
|
+
var IngestParams = z5.preprocess(
|
|
4570
|
+
coercePathUrlToUrl,
|
|
4571
|
+
z5.discriminatedUnion("source", [
|
|
4572
|
+
z5.object({
|
|
4573
|
+
source: z5.literal("url"),
|
|
4574
|
+
url: z5.string().url(),
|
|
4575
|
+
expect: ExpectEnum
|
|
4576
|
+
}).strict(),
|
|
4577
|
+
z5.object({
|
|
4578
|
+
source: z5.literal("path"),
|
|
4579
|
+
path: z5.string().min(1),
|
|
4580
|
+
expect: ExpectEnum
|
|
4581
|
+
}).strict()
|
|
4582
|
+
])
|
|
4583
|
+
);
|
|
4512
4584
|
var IngestInputs = z5.object({}).strict();
|
|
4513
4585
|
var IngestOutputs = z5.object({ asset: AssetRef }).strict();
|
|
4514
4586
|
var RAW_TEXT_EXTENSIONS = [".txt", ".md"];
|
|
@@ -6860,23 +6932,27 @@ var ASPECT_ADAPT_MODELS = ["google/gemini-3-pro-image-preview", "google/gemini-3
|
|
|
6860
6932
|
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
6933
|
var ImageAspectAdaptParams = z18.object({
|
|
6862
6934
|
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" }),
|
|
6935
|
+
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(),
|
|
6936
|
+
platform: z18.enum(AD_FORMAT_PLATFORMS).optional(),
|
|
6864
6937
|
guidance: z18.string().min(1).optional(),
|
|
6865
6938
|
image_size: z18.enum(["0.5K", "1K", "2K", "4K"]).optional()
|
|
6866
|
-
}).strict()
|
|
6939
|
+
}).strict().refine((p) => p.formats === void 0 !== (p.platform === void 0), {
|
|
6940
|
+
message: "provide exactly one of `formats` (explicit ratios) or `platform` (a placement preset)",
|
|
6941
|
+
path: ["formats"]
|
|
6942
|
+
});
|
|
6867
6943
|
var imageAspectAdaptNode = delegated({
|
|
6868
6944
|
id: "image_aspect_adapt",
|
|
6869
6945
|
version: "1.0.0",
|
|
6870
6946
|
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
|
|
6947
|
+
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.",
|
|
6948
|
+
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
6949
|
inputs: z18.object({ source: ImageRef }).loose(),
|
|
6874
6950
|
params: ImageAspectAdaptParams,
|
|
6875
6951
|
outputs: z18.object({ images: z18.array(ImageRef).min(1) }).strict(),
|
|
6876
6952
|
outputKinds: { images: "image" },
|
|
6877
6953
|
cost: ({ params }) => {
|
|
6878
6954
|
const p = params;
|
|
6879
|
-
const count =
|
|
6955
|
+
const count = Math.max(1, resolveAdaptFormats(p ?? {}).length);
|
|
6880
6956
|
return {
|
|
6881
6957
|
credits: count * (p?.model === "google/gemini-3-pro-image-preview" ? 20 : 5),
|
|
6882
6958
|
seconds_estimate: 30 + 30 * count
|
|
@@ -7689,6 +7765,8 @@ export {
|
|
|
7689
7765
|
RunAbortedError,
|
|
7690
7766
|
LayerExecutionError,
|
|
7691
7767
|
describeFailureReason,
|
|
7768
|
+
AD_FORMAT_PLATFORMS,
|
|
7769
|
+
platformFormats,
|
|
7692
7770
|
SEEDANCE_DURATIONS,
|
|
7693
7771
|
ELEVENLABS_MAX_MUSIC_LENGTH_MS,
|
|
7694
7772
|
IMAGE_GENERATE_MODELS,
|
|
@@ -7701,6 +7779,7 @@ export {
|
|
|
7701
7779
|
REF_PREFIX,
|
|
7702
7780
|
parseRefExpr,
|
|
7703
7781
|
sha256Hex,
|
|
7782
|
+
looksLikeHttpUrl,
|
|
7704
7783
|
SEEDANCE_PROFILE,
|
|
7705
7784
|
clipProfileFor,
|
|
7706
7785
|
clipParamRecipe,
|
|
@@ -7719,4 +7798,4 @@ export {
|
|
|
7719
7798
|
defaultRegistry,
|
|
7720
7799
|
createEngineFromEnv
|
|
7721
7800
|
};
|
|
7722
|
-
//# sourceMappingURL=chunk-
|
|
7801
|
+
//# sourceMappingURL=chunk-Q4UI5JZN.js.map
|