@koda-sl/baker-cli 0.132.1 → 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 -4
- package/dist/{chunk-MWR25ZMU.js → chunk-UBFU3E7L.js} +55 -12
- package/dist/chunk-UBFU3E7L.js.map +1 -0
- package/dist/cli.js +83 -12
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-MWR25ZMU.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
AD_FORMAT_PLATFORMS,
|
|
3
4
|
BackendClient,
|
|
4
5
|
BackendClient2,
|
|
5
6
|
DEFAULT_VIDEO_GENERATE_MODEL,
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
imageProfileFor,
|
|
24
25
|
isPersistedAssetRef,
|
|
25
26
|
parseRefExpr,
|
|
27
|
+
platformFormats,
|
|
26
28
|
requireCredentialsFromEnv,
|
|
27
29
|
resolveConcurrency,
|
|
28
30
|
sha256Hex,
|
|
@@ -31,7 +33,7 @@ import {
|
|
|
31
33
|
toModelSafeImage,
|
|
32
34
|
ulid,
|
|
33
35
|
validateCanvasDeep
|
|
34
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-UBFU3E7L.js";
|
|
35
37
|
import {
|
|
36
38
|
csvOrJson,
|
|
37
39
|
daysAgoIso,
|
|
@@ -19418,6 +19420,9 @@ var SHEET_SUBJECT_TYPE2 = {
|
|
|
19418
19420
|
};
|
|
19419
19421
|
var ACTOR_SHEET_MODEL2 = "google/gemini-3-pro-image-preview";
|
|
19420
19422
|
var ACTOR_SHEET_IMAGE_SIZE = "4K";
|
|
19423
|
+
var ADAPT_MODEL = "google/gemini-3-pro-image-preview";
|
|
19424
|
+
var ADAPT_IMAGE_SIZE = "2K";
|
|
19425
|
+
var ADAPT_GUIDANCE = "Keep the headline, logo, CTA, and hero subject fully visible in every ratio. Reproduce every text string verbatim \u2014 no dropped, added, or altered characters \u2014 and preserve the exact brand-color treatment (e.g. a black\u2192red word pivot), never flattening it.";
|
|
19421
19426
|
var Blueprint = z15.object({
|
|
19422
19427
|
meta: z15.object({ estimated_aspect_ratio: z15.string().optional() }).loose().optional(),
|
|
19423
19428
|
text_content: z15.array(z15.object({ text: z15.string().optional() }).loose()).optional()
|
|
@@ -19498,6 +19503,16 @@ function aspectRatio(blueprint, opts) {
|
|
|
19498
19503
|
const requested = opts.aspectRatio ?? blueprint.meta?.estimated_aspect_ratio;
|
|
19499
19504
|
return requested && GEN_ASPECT_RATIOS.has(requested) ? requested : DEFAULT_ASPECT_RATIO;
|
|
19500
19505
|
}
|
|
19506
|
+
function baseAspectRatio(blueprint, opts) {
|
|
19507
|
+
const requested = aspectRatio(blueprint, opts);
|
|
19508
|
+
if (!opts.placements) return requested;
|
|
19509
|
+
const formats = platformFormats(opts.placements);
|
|
19510
|
+
return formats.includes(requested) ? requested : formats[0] ?? requested;
|
|
19511
|
+
}
|
|
19512
|
+
function outputFormats(blueprint, opts) {
|
|
19513
|
+
if (!opts.placements) return [baseAspectRatio(blueprint, opts)];
|
|
19514
|
+
return [...platformFormats(opts.placements)];
|
|
19515
|
+
}
|
|
19501
19516
|
function blueprintTextStrings(blueprint) {
|
|
19502
19517
|
return (blueprint.text_content ?? []).map((t) => typeof t.text === "string" ? t.text.trim() : "").filter((t) => t.length > 0);
|
|
19503
19518
|
}
|
|
@@ -19615,11 +19630,25 @@ function scaffoldStaticAd(input, elementsInput, opts) {
|
|
|
19615
19630
|
inputs: { reference, target_blueprint: "$ref:prompt.asset" },
|
|
19616
19631
|
params: {
|
|
19617
19632
|
model: opts.genModel,
|
|
19618
|
-
aspect_ratio:
|
|
19633
|
+
aspect_ratio: baseAspectRatio(blueprint, opts),
|
|
19619
19634
|
image_size: "2K",
|
|
19620
19635
|
prompt
|
|
19621
19636
|
}
|
|
19622
19637
|
});
|
|
19638
|
+
const fanOut = opts.placements !== void 0;
|
|
19639
|
+
if (fanOut) {
|
|
19640
|
+
nodes.push({
|
|
19641
|
+
id: "adapt",
|
|
19642
|
+
type: "image_aspect_adapt",
|
|
19643
|
+
inputs: { source: "$ref:gen.images#0" },
|
|
19644
|
+
params: {
|
|
19645
|
+
model: ADAPT_MODEL,
|
|
19646
|
+
platform: opts.placements,
|
|
19647
|
+
guidance: ADAPT_GUIDANCE,
|
|
19648
|
+
image_size: ADAPT_IMAGE_SIZE
|
|
19649
|
+
}
|
|
19650
|
+
});
|
|
19651
|
+
}
|
|
19623
19652
|
return {
|
|
19624
19653
|
schema: "baker-canvas/1",
|
|
19625
19654
|
metadata: {
|
|
@@ -19627,7 +19656,7 @@ function scaffoldStaticAd(input, elementsInput, opts) {
|
|
|
19627
19656
|
description: "Scaffolded by `baker canvas scaffold-static-ad`. Edit `prompt.json` (the blueprint generated from the source image) into the ad you want, drop a real asset at each [TODO] ingest path (logo/product/subject/badge + brand font), then `baker canvas run`. The original is wired for composition only."
|
|
19628
19657
|
},
|
|
19629
19658
|
nodes,
|
|
19630
|
-
output: { node: "gen", output: "images" }
|
|
19659
|
+
output: fanOut ? { node: "adapt", output: "images" } : { node: "gen", output: "images" }
|
|
19631
19660
|
};
|
|
19632
19661
|
}
|
|
19633
19662
|
function staticAdReport(input, elementsInput, opts) {
|
|
@@ -19644,7 +19673,9 @@ function staticAdReport(input, elementsInput, opts) {
|
|
|
19644
19673
|
})),
|
|
19645
19674
|
includes_font: opts.includeFont !== false,
|
|
19646
19675
|
actor_sheets: includeActorSheets ? assignElementLabels2(elements).filter(({ el }) => SHEET_SUBJECT_TYPE2[el.type.toLowerCase()]).map(({ label }) => label) : [],
|
|
19647
|
-
aspect_ratio:
|
|
19676
|
+
aspect_ratio: baseAspectRatio(blueprint, opts),
|
|
19677
|
+
placements: opts.placements ?? null,
|
|
19678
|
+
output_formats: outputFormats(blueprint, opts)
|
|
19648
19679
|
};
|
|
19649
19680
|
}
|
|
19650
19681
|
|
|
@@ -19664,6 +19695,12 @@ function resolveFormats(aspect) {
|
|
|
19664
19695
|
const value = aspect?.trim();
|
|
19665
19696
|
return value && FORMAT_VALUES.includes(value) ? [value] : ["4:5"];
|
|
19666
19697
|
}
|
|
19698
|
+
function resolveFormatList(formats) {
|
|
19699
|
+
const valid = [...new Set(formats.map((f) => f.trim()))].filter(
|
|
19700
|
+
(f) => FORMAT_VALUES.includes(f)
|
|
19701
|
+
);
|
|
19702
|
+
return valid.length > 0 ? valid : ["4:5"];
|
|
19703
|
+
}
|
|
19667
19704
|
function describeBlueprintIntent(blueprint) {
|
|
19668
19705
|
const intent = blueprint?.ad_intent;
|
|
19669
19706
|
if (typeof intent === "string" && intent.trim()) return intent.trim();
|
|
@@ -19964,6 +20001,25 @@ function resolveModels(args) {
|
|
|
19964
20001
|
genModel: pick("gen-model", "image_generate", "openai/gpt-5.4-image-2")
|
|
19965
20002
|
};
|
|
19966
20003
|
}
|
|
20004
|
+
var PLATFORM_PLACEMENTS = {
|
|
20005
|
+
meta: "meta_feed_stories",
|
|
20006
|
+
google: "google_demandgen",
|
|
20007
|
+
linkedin: "linkedin",
|
|
20008
|
+
tiktok: "tiktok",
|
|
20009
|
+
youtube: "youtube",
|
|
20010
|
+
x: void 0,
|
|
20011
|
+
other: void 0
|
|
20012
|
+
};
|
|
20013
|
+
function resolvePlacements(placementsFlag, definitionPlatform) {
|
|
20014
|
+
const flag = placementsFlag?.trim();
|
|
20015
|
+
if (flag === void 0 || flag === "") return { ok: true, placements: PLATFORM_PLACEMENTS[definitionPlatform] };
|
|
20016
|
+
if (flag === "none") return { ok: true, placements: void 0 };
|
|
20017
|
+
if (AD_FORMAT_PLATFORMS.includes(flag)) return { ok: true, placements: flag };
|
|
20018
|
+
return {
|
|
20019
|
+
ok: false,
|
|
20020
|
+
message: `--placements "${flag}" is not a valid placement set. Use one of: ${AD_FORMAT_PLATFORMS.join(", ")}, or "none" for a single base ad.`
|
|
20021
|
+
};
|
|
20022
|
+
}
|
|
19967
20023
|
function regionCount(layout) {
|
|
19968
20024
|
const regions = layout?.regions;
|
|
19969
20025
|
return Array.isArray(regions) ? regions.length : 0;
|
|
@@ -20037,7 +20093,7 @@ async function runVisionPasses(canvas) {
|
|
|
20037
20093
|
var scaffoldStaticAdCommand = defineCommand91({
|
|
20038
20094
|
meta: {
|
|
20039
20095
|
name: "scaffold-static-ad",
|
|
20040
|
-
description: "Turn a source/inspiration image into a runnable static-ad canvas. Runs billed passes \u2014 image_describe (the blueprint, baked to prompt.json as the editable 'prompt'), an AI selection of the image's MAIN identity elements, and a structured global-layout pass (the column/row grid with per-region bounds and text sizes) \u2014 then scaffolds a canvas that wires one [TODO] ingest slot per element (logo/product/subject/badge + brand font) into image_generate. Edit prompt.json and drop the real assets, then `baker canvas run` it."
|
|
20096
|
+
description: "Turn a source/inspiration image into a runnable static-ad canvas. Runs billed passes \u2014 image_describe (the blueprint, baked to prompt.json as the editable 'prompt'), an AI selection of the image's MAIN identity elements, and a structured global-layout pass (the column/row grid with per-region bounds and text sizes) \u2014 then scaffolds a canvas that wires one [TODO] ingest slot per element (logo/product/subject/badge + brand font) into image_generate. By default it also fans the hero out to the platform's placement ratios (via image_aspect_adapt \u2014 the hero ratio is free, the rest are billed AI recompositions); pass --placements none for a single base ad, or a preset to pick the set. Edit prompt.json and drop the real assets, then `baker canvas run` it."
|
|
20041
20097
|
},
|
|
20042
20098
|
args: {
|
|
20043
20099
|
file: { type: "positional", required: true, description: "Path or http(s) URL to the source/inspiration image" },
|
|
@@ -20050,14 +20106,18 @@ var scaffoldStaticAdCommand = defineCommand91({
|
|
|
20050
20106
|
title: { type: "string", description: "Creative title for _definition.md (default: title-cased slug)" },
|
|
20051
20107
|
platform: {
|
|
20052
20108
|
type: "string",
|
|
20053
|
-
description: "Ad platform
|
|
20109
|
+
description: "Ad platform (meta|google|linkedin|tiktok|youtube|x|other; default meta). Sets _definition.md AND, unless --placements is given, the default placement set the hero auto-fans-out to (meta \u2192 4:5+9:16, google \u2192 1:1+4:5+16:9, linkedin \u2192 1:1, tiktok \u2192 9:16, youtube \u2192 16:9; x/other \u2192 none)."
|
|
20054
20110
|
},
|
|
20055
20111
|
advertiser: { type: "string", description: "Source advertiser recorded in _definition.md" },
|
|
20056
20112
|
"describe-model": { type: "string", description: "Override the image_describe model id" },
|
|
20057
20113
|
"select-model": { type: "string", description: "Override the text_generate model id for element selection" },
|
|
20058
20114
|
"layout-model": { type: "string", description: "Override the text_generate model id for the layout pass" },
|
|
20059
20115
|
"gen-model": { type: "string", description: "Override the image_generate model id" },
|
|
20060
|
-
aspect: { type: "string", description: "Force the
|
|
20116
|
+
aspect: { type: "string", description: "Force the hero's aspect ratio (else inferred from the image)" },
|
|
20117
|
+
placements: {
|
|
20118
|
+
type: "string",
|
|
20119
|
+
description: "Placement set the hero auto-fans-out to (image_aspect_adapt). One of meta_feed | meta_feed_stories | meta_all | linkedin | google_demandgen | tiktok | youtube, or `none` for a single base ad. Default: derived from --platform (meta \u2192 feed + stories). Each extra ratio is a billed render; the hero ratio passes through free."
|
|
20120
|
+
},
|
|
20061
20121
|
"skip-font": { type: "boolean", description: "Skip the brand-font \u2192 type-specimen slot" },
|
|
20062
20122
|
"skip-actor-sheets": {
|
|
20063
20123
|
type: "boolean",
|
|
@@ -20117,6 +20177,13 @@ var scaffoldStaticAdCommand = defineCommand91({
|
|
|
20117
20177
|
canvasImageIsUrl = true;
|
|
20118
20178
|
}
|
|
20119
20179
|
}
|
|
20180
|
+
const definitionPlatform = resolvePlatform(args.platform ? String(args.platform) : void 0);
|
|
20181
|
+
const placementsResult = resolvePlacements(
|
|
20182
|
+
args.placements ? String(args.placements) : void 0,
|
|
20183
|
+
definitionPlatform
|
|
20184
|
+
);
|
|
20185
|
+
if (!placementsResult.ok) return fail2("invalid_placements", placementsResult.message);
|
|
20186
|
+
const placements = placementsResult.placements;
|
|
20120
20187
|
const opts = {
|
|
20121
20188
|
genModel,
|
|
20122
20189
|
imagePath: canvasImagePath,
|
|
@@ -20124,7 +20191,8 @@ var scaffoldStaticAdCommand = defineCommand91({
|
|
|
20124
20191
|
blueprintPath: canvasBlueprintPath,
|
|
20125
20192
|
aspectRatio: args.aspect ? String(args.aspect) : void 0,
|
|
20126
20193
|
includeFont: !args["skip-font"],
|
|
20127
|
-
includeActorSheets: !args["skip-actor-sheets"]
|
|
20194
|
+
includeActorSheets: !args["skip-actor-sheets"],
|
|
20195
|
+
placements
|
|
20128
20196
|
};
|
|
20129
20197
|
let canvas;
|
|
20130
20198
|
let report;
|
|
@@ -20150,8 +20218,8 @@ var scaffoldStaticAdCommand = defineCommand91({
|
|
|
20150
20218
|
buildCreativeDefinition({
|
|
20151
20219
|
title: args.title ? String(args.title) : titleFromSlug(slug ?? ""),
|
|
20152
20220
|
kind: "static",
|
|
20153
|
-
platform:
|
|
20154
|
-
formats:
|
|
20221
|
+
platform: definitionPlatform,
|
|
20222
|
+
formats: resolveFormatList(report.output_formats),
|
|
20155
20223
|
sourceReferenceUrl: durableSourceUrl ?? (imageIsUrl ? imageSource : void 0),
|
|
20156
20224
|
sourceAdvertiser: args.advertiser ? String(args.advertiser) : args.context ? String(args.context) : void 0,
|
|
20157
20225
|
sourceKind: "image",
|
|
@@ -20164,8 +20232,8 @@ var scaffoldStaticAdCommand = defineCommand91({
|
|
|
20164
20232
|
await syncCreativeDefinitionBestEffort({
|
|
20165
20233
|
slug,
|
|
20166
20234
|
title: args.title ? String(args.title) : titleFromSlug(slug),
|
|
20167
|
-
platform:
|
|
20168
|
-
formats:
|
|
20235
|
+
platform: definitionPlatform,
|
|
20236
|
+
formats: resolveFormatList(report.output_formats),
|
|
20169
20237
|
sourceReferenceUrl: durableSourceUrl ?? (imageIsUrl ? imageSource : void 0),
|
|
20170
20238
|
canvas,
|
|
20171
20239
|
sourceSha: await computeSourceSha(outPath)
|
|
@@ -20182,6 +20250,8 @@ var scaffoldStaticAdCommand = defineCommand91({
|
|
|
20182
20250
|
output: canvas.output,
|
|
20183
20251
|
models: { describe: describeModel, select: selectModel, layout: layoutModel, gen: opts.genModel },
|
|
20184
20252
|
aspect_ratio: report.aspect_ratio,
|
|
20253
|
+
placements: report.placements,
|
|
20254
|
+
output_formats: report.output_formats,
|
|
20185
20255
|
layout_regions: regionCount(layout),
|
|
20186
20256
|
stats: {
|
|
20187
20257
|
total_nodes: canvas.nodes.length,
|
|
@@ -20193,6 +20263,7 @@ var scaffoldStaticAdCommand = defineCommand91({
|
|
|
20193
20263
|
assets_to_supply: report.elements,
|
|
20194
20264
|
font_slot: report.includes_font ? "Drop a brand font at the [TODO] brandfont path (the describe pass recorded the ad's typefaces under `fonts` in prompt.json \u2014 match those). The font is wired into the render as a TYPE SPECIMEN reference so generated text takes the brand letterforms. Delete the brandfont + type_ref nodes to skip it." : "skipped (--skip-font)",
|
|
20195
20265
|
actor_sheets: report.actor_sheets.length > 0 ? `Each living hero (${report.actor_sheets.join(", ")}) is fused into a generated multi-view reference sheet (image_reference_sheet) that the render grounds on \u2014 so drop ONE clean photo at that hero's ingest and the sheet builds the consistent turnaround. Pass --skip-actor-sheets to ground on the lone photo instead.` : "none (no person/animal heroes detected, or --skip-actor-sheets)",
|
|
20266
|
+
placements: report.placements === null ? `Single format (${report.aspect_ratio}). Pass --placements meta_feed_stories|meta_all|linkedin|\u2026 to auto-fan-out to a platform's set.` : `Auto-fans the approved hero out to ${report.placements} \u2192 ${report.output_formats.join(", ")}. The hero ratio (${report.aspect_ratio}) passes through free; each other ratio is a billed render, AI-recomposed (not cropped). Re-read the copy and confirm the brand-color treatment on EVERY ratio before publishing \u2014 adaptation re-generates and can silently mangle text or flatten a color device. Pass --placements none for a single base ad.`,
|
|
20196
20267
|
note: "Populate as you go: for each [TODO] ingest slot, source its real asset and wire it into the slot right away \u2014 one at a time, not all sourced first then reconciled at the end. When every slot is filled, `baker canvas validate` then `baker canvas run`. Running generates a billed image \u2014 it is not free."
|
|
20197
20268
|
}
|
|
20198
20269
|
},
|