@koda-sl/baker-cli 0.382.0-dev.a410b0488 → 0.383.0-dev.2caa8a15e
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 +3 -3
- package/dist/cli.js +27 -24
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -892,7 +892,7 @@ baker ads meta insights --object act_123 --level adset \
|
|
|
892
892
|
baker ads meta activities --account-id act_123 --days 7
|
|
893
893
|
|
|
894
894
|
# "Which Page should this ad run from?"
|
|
895
|
-
baker ads meta pages # Facebook Pages the
|
|
895
|
+
baker ads meta pages # Facebook Pages the selected ad accounts can promote
|
|
896
896
|
|
|
897
897
|
# "What is the lead form collecting, and how many came in?"
|
|
898
898
|
baker ads meta lead-forms --page-id 100635183117758 # instant forms on a Page + lead counts
|
|
@@ -5474,7 +5474,7 @@ Before the deconstruct it runs a **local shot-cut pass** on the source file with
|
|
|
5474
5474
|
|
|
5475
5475
|
A shot longer than the video model's per-clip ceiling (Seedance's 15s, passed as `video_deconstruct`'s `max_clip_s`) is split into equal **continuation sub-scenes** that share their splice boundary exactly — so a long shot is reproduced in **full** (no truncation) and joins seamlessly. Each sub-scene carries `continues_previous`.
|
|
5476
5476
|
|
|
5477
|
-
It then scaffolds the full pipeline like an **editing timeline**: each clip gets a **static-ad-grade start AND end keyframe** (`image_generate`, each with its **own `params.prompt`** regenerated from that scene's `scenes/sNN.json` — edit the scene file to change that frame, never the node prompt by hand; the slim `prompt.style.json` wired as the **shared `target_blueprint`** style reference, plus a per-element reference legend). Each keyframe is **fully recast** to the dropped `el_*` reference images. The original extracted frame is kept LAST as a **pure composition anchor** (framing / camera angle / shot size / pose) only in an ad with **nobody in it** — no deconstructed cast and no recurring person. In any ad with people, no generated frame takes a source frame as a reference: an image model copies the face of whoever is in its references, whatever the prompt says, and which scenes a person is in is itself a model's guess — so framing is carried by the frame prompt instead. **People are cast from company avatars** (`--avatar <handle>`): each recurring person grounds on that avatar's identity sheet, used as-is (no sheet is built from it); a person with no avatar gets a `[TODO]` asking for one, never for a found photo. Both keyframes feed `video_generate` (`first_frame`+`last_frame`, so Seedance interpolates real in-shot motion; ultra-detailed motion brief; duration snapped to the nearest allowed clip length). Every keyframe grounds **only on its own extracted frame + `el_*` slots** — no reference to any other generated frame — so all images render **in parallel** (no cascade). Source-frame URLs are **deduped** (each ingested once). `--frames reuse` wires the real source frame straight in.
|
|
5477
|
+
It then scaffolds the full pipeline like an **editing timeline**: each clip gets a **static-ad-grade start AND end keyframe** (`image_generate`, each with its **own `params.prompt`** regenerated from that scene's `scenes/sNN.json` — edit the scene file to change that frame, never the node prompt by hand; the slim `prompt.style.json` wired as the **shared `target_blueprint`** style reference, plus a per-element reference legend). Each keyframe is **fully recast** to the dropped `el_*` reference images. The original extracted frame is kept LAST as a **pure composition anchor** (framing / camera angle / shot size / pose) only in an ad with **nobody in it** — no deconstructed cast and no recurring person. In any ad with people, no generated frame takes a source frame as a reference: an image model copies the face of whoever is in its references, whatever the prompt says, and which scenes a person is in is itself a model's guess — so framing is carried by the frame prompt instead. **People are cast from company avatars** (`--avatar <handle>`): each recurring person grounds on that avatar's identity sheet, used as-is (no sheet is built from it); a person with no avatar gets a `[TODO]` asking for one, never for a found photo. Both keyframes feed `video_generate` (`first_frame`+`last_frame`, so Seedance interpolates real in-shot motion; ultra-detailed motion brief; duration snapped to the nearest allowed clip length). Every keyframe grounds **only on its own extracted frame + `el_*` slots** — no reference to any other generated frame — so all images render **in parallel** (no cascade). Source-frame URLs are **deduped** (each ingested once). `--frames reuse` wires the real source frame straight in — only on a video with nobody in it: when the deconstruct found a cast or a recurring person, reuse is refused (`frames_reuse_refused`) and every frame is generated, so the source's actors never reach the ad.
|
|
5478
5478
|
|
|
5479
5479
|
**Composited scenes (split-screen / picture-in-picture / keyed presenter).** Real ads aren't always one full-frame shot — a frame can be **persistently divided** (b-roll on top, a presenter talking on the bottom) or **layer a presenter** over background footage (boxed in a corner, or green-screen keyed). The deconstruct now reports this per scene as `scene.composition` (`layout: split_screen | pip | keyed_overlay`, with one `region` per stream — each its own clean-plate frame + motion brief, the talking-head region flagged `is_presenter`). The scaffold reproduces a composited scene by building **one clip per region** (`s<i>_r0_*`, `s<i>_r1_*`, …) and compositing them with ffmpeg: a split-screen `vstack`/`hstack` (stack direction read from the region **panels**, so a top/bottom split always stacks vertically), or a picture-in-picture `overlay` of the presenter inset at its corner. A **keyed** presenter is first cut to transparency by `video_background_remove` (`s<i>_key`), then overlaid. The presenter region carries the native lip-synced voice; b-roll/render panels stay silent. To change a layout, edit `composition` in that scene's `scenes/sNN.json` and re-scaffold, or hand-edit the `s<i>_composite` ffmpeg args. Plain full-frame scenes (the default) are unaffected.
|
|
5480
5480
|
|
|
@@ -5528,7 +5528,7 @@ baker canvas run ./reference-ad.video.canvas.json
|
|
|
5528
5528
|
|---|---|---|
|
|
5529
5529
|
| `--out <path>` | `<video-dir>/<name>.video.canvas.json` | Where to write the canvas (composition is copied alongside). |
|
|
5530
5530
|
| `--slug <slug>` | — | Creative slug (lowercase kebab): writes the canvas to `src/creatives/<slug>/<slug>.canvas.json` — the repo convention that attaches every run to the creative's dashboard generation history. `--out` wins over `--slug`. |
|
|
5531
|
-
| `--frames <mode>` | `generate` | `generate` emits ONE recast keyframe per scene (the original frame is dropped so the dropped `el_*` assets drive identity); `reuse` wires the real extracted first+last frames straight into the clips (faithful, cheaper, no recast). |
|
|
5531
|
+
| `--frames <mode>` | `generate` | `generate` emits ONE recast keyframe per scene (the original frame is dropped so the dropped `el_*` assets drive identity); `reuse` wires the real extracted first+last frames straight into the clips (faithful, cheaper, no recast) — only on a video with nobody in it; on one showing people it is refused and every frame is generated. |
|
|
5532
5532
|
| `--ambient` | off | Give silent **b-roll** scenes native diegetic ambient (Seedance `generate_audio`), mixed deep under the music bed. Talking scenes already carry voice; check levels don't muddy the mix before keeping it. |
|
|
5533
5533
|
| `--seam-dedup <mode>` | `head` | How to dedup the boundary frame two clips SHARE when a long shot is split for length (the second clip's first frame IS the first clip's last frame, so a plain concat freezes on it for a frame). `head` drops the second clip's first frame, `tail` drops the first clip's last frame, `off` keeps both. Only touches shared-frame continuation joins — a hard cut between two shots shares no frame. |
|
|
5534
5534
|
| `--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. |
|
package/dist/cli.js
CHANGED
|
@@ -299,6 +299,7 @@ var chatActionDraftOpViewSchema = z.object({
|
|
|
299
299
|
targetActionId: z.string().optional(),
|
|
300
300
|
tempId: z.string().optional(),
|
|
301
301
|
tags: z.array(z.string()).optional(),
|
|
302
|
+
priority: actionPrioritySchema.nullable().optional(),
|
|
302
303
|
reason: z.string().optional(),
|
|
303
304
|
note: z.string().optional(),
|
|
304
305
|
name: z.string().optional(),
|
|
@@ -28397,19 +28398,10 @@ var metaMediaCommand = defineCommand68({
|
|
|
28397
28398
|
|
|
28398
28399
|
// src/commands/ads/meta/pages.ts
|
|
28399
28400
|
import { defineCommand as defineCommand69 } from "citty";
|
|
28400
|
-
function advertisablePages(data) {
|
|
28401
|
-
return data.filter((p) => Array.isArray(p.tasks) && p.tasks.includes("ADVERTISE"));
|
|
28402
|
-
}
|
|
28403
28401
|
function pageSelectionHints(data) {
|
|
28404
|
-
|
|
28405
|
-
if (advertisable.length > 1) {
|
|
28402
|
+
if (data.length > 1) {
|
|
28406
28403
|
return [
|
|
28407
|
-
"MULTIPLE Pages can run ads \u2014 do NOT guess. Confirm with the user which Page an ad should run from before passing --page to `creatives create`; a wrong Page publishes the ad under the wrong brand identity and staging will not catch it."
|
|
28408
|
-
];
|
|
28409
|
-
}
|
|
28410
|
-
if (data.length > 0 && advertisable.length === 0) {
|
|
28411
|
-
return [
|
|
28412
|
-
"NO Page can run ads \u2014 none of the connected user's Pages carry the ADVERTISE task. Ask the user to grant Advertise access to the right Page before creating an ad; a readable-but-non-advertising Page still passes staging and would fail or run under the wrong identity."
|
|
28404
|
+
"MULTIPLE Pages can run ads from the selected Meta ad accounts \u2014 do NOT guess. Confirm with the user which Page an ad should run from before passing --page to `creatives create`; a wrong Page publishes the ad under the wrong brand identity and staging will not catch it."
|
|
28413
28405
|
];
|
|
28414
28406
|
}
|
|
28415
28407
|
return [];
|
|
@@ -28417,7 +28409,7 @@ function pageSelectionHints(data) {
|
|
|
28417
28409
|
var pagesCommand = defineCommand69({
|
|
28418
28410
|
meta: {
|
|
28419
28411
|
name: "pages",
|
|
28420
|
-
description: `List the Facebook Pages the
|
|
28412
|
+
description: `List the Facebook Pages that the selected Meta ad accounts can promote. Every Meta ad publishes from a Page, so use this to identify the Page an ad should run from. If more than one Page is listed, confirm with the user which Page to use \u2014 a wrong Page publishes under the wrong brand identity.
|
|
28421
28413
|
|
|
28422
28414
|
Examples:
|
|
28423
28415
|
baker ads meta pages`
|
|
@@ -28678,7 +28670,7 @@ Discovery:
|
|
|
28678
28670
|
baker ads meta accounts \u2014 accounts in this company's connected scope
|
|
28679
28671
|
baker ads meta accounts --include-all \u2014 every account the token can see
|
|
28680
28672
|
baker ads meta businesses \u2014 Business Manager IDs
|
|
28681
|
-
baker ads meta pages \u2014 Facebook Pages the
|
|
28673
|
+
baker ads meta pages \u2014 Facebook Pages the selected ad accounts can promote (an ad's identity)
|
|
28682
28674
|
baker ads meta lead-forms --page-id 100635183117758 \u2014 instant (lead) forms on a Page, with lead counts
|
|
28683
28675
|
baker ads meta leads --form-id 987654321 \u2014 the leads one form collected (contact details masked)
|
|
28684
28676
|
|
|
@@ -40790,10 +40782,18 @@ function dropUncastVoices(nodes) {
|
|
|
40790
40782
|
if (node && node.type === "voice_select" && !referenced.has(node.id)) nodes.splice(i, 1);
|
|
40791
40783
|
}
|
|
40792
40784
|
}
|
|
40793
|
-
function
|
|
40785
|
+
function framesReuseRefusal(blueprint, elements, requested) {
|
|
40786
|
+
if (requested !== "reuse") return void 0;
|
|
40787
|
+
const showsPeople = (blueprint.global?.cast ?? []).length > 0 || elements.some((e) => e.type.toLowerCase() === "person");
|
|
40788
|
+
if (!showsPeople) return void 0;
|
|
40789
|
+
return "--frames reuse was not applied: this video shows people, and reusing its frames would put the source's actors into our ad. Every frame is generated instead, recast to the el_* references. Reuse applies only to a video with nobody in it.";
|
|
40790
|
+
}
|
|
40791
|
+
function scaffoldVideoCanvas(input, elementsInput, requested) {
|
|
40794
40792
|
const blueprint = VideoBlueprint.parse(input);
|
|
40795
40793
|
injectHookPhysicality(blueprint);
|
|
40796
40794
|
const elements = RecurringElements.parse(elementsInput);
|
|
40795
|
+
const reuseRefused = framesReuseRefusal(blueprint, elements, requested.frames);
|
|
40796
|
+
const opts = reuseRefused ? { ...requested, frames: "generate" } : requested;
|
|
40797
40797
|
const clock = spineClockFor(blueprint);
|
|
40798
40798
|
const nodes = [];
|
|
40799
40799
|
emitBlueprintIngests(opts, nodes);
|
|
@@ -40896,6 +40896,7 @@ function scaffoldVideoCanvas(input, elementsInput, opts) {
|
|
|
40896
40896
|
}
|
|
40897
40897
|
const todo = {
|
|
40898
40898
|
...buildVideoTodo(videoReport(input, elementsInput), overlays.length, floating.length, opts, blueprint),
|
|
40899
|
+
...reuseRefused ? { frames_reuse_refused: reuseRefused } : {},
|
|
40899
40900
|
...aspectRemapTodo(resolveAspect(blueprint.source?.aspect_ratio, opts.aspect, genAspectsFor(opts.videoModel))),
|
|
40900
40901
|
model_constraints: buildModelConstraints()
|
|
40901
40902
|
};
|
|
@@ -42021,12 +42022,6 @@ async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
|
|
|
42021
42022
|
return { canvasPath: path14.resolve(resolvedTarget, manifest.canvas.path), restored, upToDate };
|
|
42022
42023
|
}
|
|
42023
42024
|
|
|
42024
|
-
// src/commands/canvas/unchanged-run.ts
|
|
42025
|
-
function unchangedRunHint(runId, stats) {
|
|
42026
|
-
if (stats.total_nodes === 0 || stats.cached_nodes < stats.total_nodes) return null;
|
|
42027
|
-
return `Nothing changed since the last render: every step came from the cache, so this run is no new version. To read what a render produced, use \`baker canvas inspect ${runId}\` instead of re-running; re-run a canvas only after editing it or the files it reads.`;
|
|
42028
|
-
}
|
|
42029
|
-
|
|
42030
42025
|
// src/engine/nodes/local/lib/render-review.ts
|
|
42031
42026
|
import { execFile as execFile2 } from "child_process";
|
|
42032
42027
|
import { mkdtemp, readdir as readdir6, readFile as readFile11, rm as rm5 } from "fs/promises";
|
|
@@ -42910,8 +42905,6 @@ ${describeRewrites(healed.rewrites)}
|
|
|
42910
42905
|
`Anything about the mark or the closing card is a composition fix, not a re-render: both are drawn as HTML by the caption composition, so editing it and re-running re-renders only that pass \u2014 the clips are cached and no generation is paid for again. Fix them, then re-run: edit the \`show\` of the scene files named above \u2014 a shot with a person handling equipment is where limbs and objects go wrong, so widen it, move the hands out of frame, or drop the person from that beat. Re-running re-renders only the scenes you edited; everything else comes from the cache (this run: ${result.stats?.cached_nodes ?? 0} of ${result.stats?.total_nodes ?? 0} nodes were cached).`
|
|
42911
42906
|
] : []
|
|
42912
42907
|
] : [];
|
|
42913
|
-
const unchanged = result.stats ? unchangedRunHint(result.run_id, result.stats) : null;
|
|
42914
|
-
if (unchanged) hints2.push(unchanged);
|
|
42915
42908
|
if (poster) {
|
|
42916
42909
|
await poster.flush(
|
|
42917
42910
|
// `review` is null only when nobody looked (no key, no ffmpeg, unreadable video);
|
|
@@ -45830,6 +45823,12 @@ function videoDefinitionDescription(blueprint) {
|
|
|
45830
45823
|
const product = g.branding?.product;
|
|
45831
45824
|
return typeof product === "string" && product.trim() ? product.trim() : void 0;
|
|
45832
45825
|
}
|
|
45826
|
+
function framesModeReport(canvas, requested) {
|
|
45827
|
+
const todo = canvas.metadata?.todo;
|
|
45828
|
+
const mode = typeof todo?.frames_mode === "string" ? todo.frames_mode : requested;
|
|
45829
|
+
const refused = typeof todo?.frames_reuse_refused === "string" ? todo.frames_reuse_refused : void 0;
|
|
45830
|
+
return { frames_mode: mode, ...refused ? { frames_reuse_refused: refused } : {} };
|
|
45831
|
+
}
|
|
45833
45832
|
async function materializeReferenceVideo(fileArg2) {
|
|
45834
45833
|
if (!/^https?:\/\//i.test(fileArg2)) return path26.resolve(fileArg2);
|
|
45835
45834
|
let bytes;
|
|
@@ -45987,7 +45986,10 @@ var scaffoldVideoCommand = defineCommand116({
|
|
|
45987
45986
|
type: "string",
|
|
45988
45987
|
description: "Creative slug \u2014 writes the canvas to src/creatives/<slug>/<slug>.canvas.json (repo convention)"
|
|
45989
45988
|
},
|
|
45990
|
-
frames: {
|
|
45989
|
+
frames: {
|
|
45990
|
+
type: "string",
|
|
45991
|
+
description: `"generate" (default, every frame regenerated for our ad) or "reuse" (wire the source's real frames in). Reuse applies only to a video with nobody in it \u2014 on a video showing people it is refused and every frame is generated, so the source's actors never reach our ad.`
|
|
45992
|
+
},
|
|
45991
45993
|
ambient: {
|
|
45992
45994
|
type: "boolean",
|
|
45993
45995
|
description: "Give silent b-roll scenes native diegetic ambient mixed deep under the music bed (off by default)"
|
|
@@ -46190,6 +46192,7 @@ var scaffoldVideoCommand = defineCommand116({
|
|
|
46190
46192
|
error: { code: "validation", issues: validation.issues },
|
|
46191
46193
|
canvas_path: outPath,
|
|
46192
46194
|
prompt_path: blueprintPath,
|
|
46195
|
+
...framesModeReport(canvas, frames),
|
|
46193
46196
|
note: "The canvas WAS written despite the blocking issue(s) \u2014 fix them in place, then `baker canvas validate` before running.",
|
|
46194
46197
|
...uncastHint
|
|
46195
46198
|
},
|
|
@@ -46241,7 +46244,7 @@ var scaffoldVideoCommand = defineCommand116({
|
|
|
46241
46244
|
composition_dir: compositionDest,
|
|
46242
46245
|
output: canvas.output,
|
|
46243
46246
|
...uncastHint,
|
|
46244
|
-
|
|
46247
|
+
...framesModeReport(canvas, frames),
|
|
46245
46248
|
models: {
|
|
46246
46249
|
deconstruct: deconstructModel,
|
|
46247
46250
|
select: selectModel,
|