@koda-sl/baker-cli 0.191.0 → 0.192.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 +7 -3
- package/dist/{chunk-OWDTSPY4.js → chunk-ISZWNERZ.js} +9 -1
- package/dist/chunk-ISZWNERZ.js.map +1 -0
- package/dist/cli.js +89 -12
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-OWDTSPY4.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
toModelSafeImage,
|
|
48
48
|
ulid,
|
|
49
49
|
validateCanvasDeep
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-ISZWNERZ.js";
|
|
51
51
|
import {
|
|
52
52
|
csvOrJson,
|
|
53
53
|
daysAgoIso,
|
|
@@ -11142,7 +11142,7 @@ var assetsUpdateCommand = defineCommand30({
|
|
|
11142
11142
|
var assetsCommand = defineCommand30({
|
|
11143
11143
|
meta: {
|
|
11144
11144
|
name: "assets",
|
|
11145
|
-
description: "Stage asset create/update + asset-link attach/detach (via --file). For a high-performance Search campaign add \u22654 sitelinks, \u22653 callouts,
|
|
11145
|
+
description: "Stage asset create/update + asset-link attach/detach (via --file). For a high-performance Search campaign add \u22654 sitelinks, \u22653 callouts, \u22651 structured snippet, and \u22653 image assets per campaign. Build the images with the creative-canvas-ads flow (platform preset `google_search`) \u2014 product or team in action, never stock, and never a logo on a plain background."
|
|
11146
11146
|
},
|
|
11147
11147
|
subCommands: {
|
|
11148
11148
|
create: fileCreateCommand(
|
|
@@ -20906,7 +20906,7 @@ function todoPath(el, label) {
|
|
|
20906
20906
|
const t = el.type.toLowerCase();
|
|
20907
20907
|
const fresh = t === "person" || t === "animal" || t === "location" ? " [SOURCE FRESH \u2014 a DIFFERENT person/animal/set than the original ad; do not reuse the source's individual]" : "";
|
|
20908
20908
|
const same = el.same_as ? ` [SAME INDIVIDUAL as ${el.same_as} \u2014 a different wardrobe/look of the same person; reuse that cast person, change only the outfit]` : "";
|
|
20909
|
-
return `[TODO: drop one real source image for ${label} (${el.type})${desc}${expr} \u2014 reused across every frame it appears in${fresh}${same}]`;
|
|
20909
|
+
return `[TODO: drop one real source image for ${label} (${el.type})${desc}${expr} \u2014 reused across every frame it appears in${fresh}${same}; use a path relative to this canvas file, never an absolute path]`;
|
|
20910
20910
|
}
|
|
20911
20911
|
function buildElementSlots(elements) {
|
|
20912
20912
|
const usedIds = /* @__PURE__ */ new Set([
|
|
@@ -21753,7 +21753,7 @@ function emitScreenScene(i, scene, lengths, out, surfaceIngests, nodes, clips) {
|
|
|
21753
21753
|
type: "ingest",
|
|
21754
21754
|
params: {
|
|
21755
21755
|
source: "path",
|
|
21756
|
-
path: `[TODO: supply the REAL screen for "${label}" \u2014 NEVER AI-generate a UI. Capture a clean, text-free screenshot with \`baker images screenshot https://<brand-domain>/<path>\` (image-library skill); spoken/overlay text rides the overlay layer, not the screenshot]`,
|
|
21756
|
+
path: `[TODO: supply the REAL screen for "${label}" \u2014 NEVER AI-generate a UI. Capture a clean, text-free screenshot with \`baker images screenshot https://<brand-domain>/<path>\` (image-library skill); spoken/overlay text rides the overlay layer, not the screenshot. Wire it in as a path relative to this canvas file, never an absolute path]`,
|
|
21757
21757
|
expect: "image"
|
|
21758
21758
|
}
|
|
21759
21759
|
});
|
|
@@ -21805,7 +21805,7 @@ function emitGraphicScene(i, scene, lengths, out, surfaceIngests, nodes, clips)
|
|
|
21805
21805
|
type: "ingest",
|
|
21806
21806
|
params: {
|
|
21807
21807
|
source: "path",
|
|
21808
|
-
path: `[TODO: supply the REAL designed graphic for "${label}" \u2014 NEVER AI-generate a designed panel (its typography/wordmarks garble). Rebuild it as brand HTML in video-overlay-composition (the scene's text/logos are already seeded there), export the design from the brand kit, or source stock art; text rides the overlay layer, not this plate]`,
|
|
21808
|
+
path: `[TODO: supply the REAL designed graphic for "${label}" \u2014 NEVER AI-generate a designed panel (its typography/wordmarks garble). Rebuild it as brand HTML in video-overlay-composition (the scene's text/logos are already seeded there), export the design from the brand kit, or source stock art; text rides the overlay layer, not this plate. Wire it in as a path relative to this canvas file, never an absolute path]`,
|
|
21809
21809
|
expect: "image"
|
|
21810
21810
|
}
|
|
21811
21811
|
});
|
|
@@ -24082,6 +24082,10 @@ function mimeForFile(filePath) {
|
|
|
24082
24082
|
function toPosix(p) {
|
|
24083
24083
|
return p.split(path12.sep).join("/");
|
|
24084
24084
|
}
|
|
24085
|
+
function isInside(dir, target) {
|
|
24086
|
+
const rel = path12.relative(dir, target);
|
|
24087
|
+
return !rel.startsWith("..") && !path12.isAbsolute(rel);
|
|
24088
|
+
}
|
|
24085
24089
|
function localPathRefsFromCanvas(parsed) {
|
|
24086
24090
|
const nodes = parsed?.nodes;
|
|
24087
24091
|
if (!Array.isArray(nodes)) return [];
|
|
@@ -24120,6 +24124,13 @@ async function uploadRunSnapshot(client, opts) {
|
|
|
24120
24124
|
const skipped = [];
|
|
24121
24125
|
for (const refPath of await sourceRefsToSnapshot(canvasDir, opts.parsed)) {
|
|
24122
24126
|
const abs = path12.isAbsolute(refPath) ? refPath : path12.resolve(canvasDir, refPath);
|
|
24127
|
+
if (!isInside(canvasDir, abs)) {
|
|
24128
|
+
skipped.push({
|
|
24129
|
+
path: toPosix(path12.relative(canvasDir, abs)),
|
|
24130
|
+
reason: "outside the creative folder \u2014 read from the workspace on rerun"
|
|
24131
|
+
});
|
|
24132
|
+
continue;
|
|
24133
|
+
}
|
|
24123
24134
|
let st;
|
|
24124
24135
|
try {
|
|
24125
24136
|
st = await stat2(abs);
|
|
@@ -24776,7 +24787,7 @@ function todoPath2(el, label) {
|
|
|
24776
24787
|
const expr = el.expression ? `, with a ${el.expression} expression` : "";
|
|
24777
24788
|
const t = el.type.toLowerCase();
|
|
24778
24789
|
const lock = t === "person" || t === "animal" ? " \u2014 REQUIRED: this is the emotional hero; ground it in a real reference (image-library / video-library / Pinterest), do not delete this slot and free-generate" : "";
|
|
24779
|
-
return `[TODO: drop a real image for ${label} (${el.type})${desc}${expr}${lock}]`;
|
|
24790
|
+
return `[TODO: drop a real image for ${label} (${el.type})${desc}${expr}${lock} \u2014 replace with a path relative to this canvas file, never an absolute path]`;
|
|
24780
24791
|
}
|
|
24781
24792
|
function sheetSubjectDescription(el) {
|
|
24782
24793
|
const desc = el.description ?? `the ${el.type}`;
|
|
@@ -24867,7 +24878,11 @@ function scaffoldStaticAd(input, elementsInput, opts) {
|
|
|
24867
24878
|
nodes.push({
|
|
24868
24879
|
id: "brandfont",
|
|
24869
24880
|
type: "ingest",
|
|
24870
|
-
params: {
|
|
24881
|
+
params: {
|
|
24882
|
+
source: "path",
|
|
24883
|
+
path: "[TODO: brand font .otf/.ttf, as a path relative to this canvas file (e.g. ../../brand/fonts/Brand-Bold.otf) \u2014 never an absolute path]",
|
|
24884
|
+
expect: "font"
|
|
24885
|
+
}
|
|
24871
24886
|
});
|
|
24872
24887
|
nodes.push({
|
|
24873
24888
|
id: "type_ref",
|
|
@@ -35559,20 +35574,29 @@ async function captureMobileShots(args) {
|
|
|
35559
35574
|
await mobile.context.close();
|
|
35560
35575
|
}
|
|
35561
35576
|
}
|
|
35577
|
+
var MOTION_CONCURRENCY = 3;
|
|
35562
35578
|
async function captureMotionTakes(args) {
|
|
35563
35579
|
const { browser, sections, sectionsDir, outDir, pageUrl, log } = args;
|
|
35564
35580
|
const moving = sections.filter((section) => isWorthFilming(section.motion));
|
|
35565
35581
|
if (moving.length === 0) return;
|
|
35566
35582
|
log(`filming ${moving.length} moving sections`);
|
|
35567
|
-
|
|
35568
|
-
const take = await captureMotionTake(browser, pageUrl, section.selector);
|
|
35569
|
-
if (!take)
|
|
35583
|
+
const filmOne = async (section) => {
|
|
35584
|
+
const take = await captureMotionTake(browser, pageUrl, section.selector).catch(() => null);
|
|
35585
|
+
if (!take) return;
|
|
35570
35586
|
const dir = path31.join(sectionsDir, String(section.index).padStart(2, "0"));
|
|
35571
35587
|
const file = path31.join(dir, "motion-filmstrip.png");
|
|
35572
35588
|
await writeFile15(file, take.filmstrip);
|
|
35573
35589
|
section.motionFilmstrip = path31.relative(outDir, file);
|
|
35574
35590
|
log(` [${section.index}] ${section.motion.summary}`);
|
|
35575
|
-
}
|
|
35591
|
+
};
|
|
35592
|
+
const queue = [...moving];
|
|
35593
|
+
await Promise.all(
|
|
35594
|
+
Array.from({ length: Math.min(MOTION_CONCURRENCY, queue.length) }, async () => {
|
|
35595
|
+
for (let next = queue.shift(); next; next = queue.shift()) {
|
|
35596
|
+
await filmOne(next);
|
|
35597
|
+
}
|
|
35598
|
+
})
|
|
35599
|
+
);
|
|
35576
35600
|
}
|
|
35577
35601
|
async function scrapeLanding(options) {
|
|
35578
35602
|
const timeoutMs = options.timeoutMs ?? 45e3;
|
|
@@ -35666,6 +35690,54 @@ async function scrapeLanding(options) {
|
|
|
35666
35690
|
}
|
|
35667
35691
|
}
|
|
35668
35692
|
|
|
35693
|
+
// src/engine/landing-library/unreachable.ts
|
|
35694
|
+
var NET_ERROR_REASONS = [
|
|
35695
|
+
{
|
|
35696
|
+
prefixes: ["ERR_NAME_NOT_RESOLVED", "ERR_NAME_RESOLUTION_FAILED"],
|
|
35697
|
+
message: "We couldn't find this site \u2014 check the address is spelled right."
|
|
35698
|
+
},
|
|
35699
|
+
{
|
|
35700
|
+
prefixes: ["ERR_SSL_", "ERR_CERT_", "ERR_TLS_", "ERR_BAD_SSL_"],
|
|
35701
|
+
message: "This site's secure connection is broken, so no browser can open it \u2014 including ours. That's a fault on their side, not something you can fix here."
|
|
35702
|
+
},
|
|
35703
|
+
{
|
|
35704
|
+
prefixes: ["ERR_TOO_MANY_REDIRECTS"],
|
|
35705
|
+
message: "This site kept redirecting us and never settled on a page."
|
|
35706
|
+
},
|
|
35707
|
+
{
|
|
35708
|
+
prefixes: ["ERR_BLOCKED_BY_", "ERR_ACCESS_DENIED"],
|
|
35709
|
+
message: "This site refused our visit, so we couldn't read the page."
|
|
35710
|
+
},
|
|
35711
|
+
{
|
|
35712
|
+
prefixes: [
|
|
35713
|
+
"ERR_CONNECTION_",
|
|
35714
|
+
"ERR_ADDRESS_UNREACHABLE",
|
|
35715
|
+
"ERR_EMPTY_RESPONSE",
|
|
35716
|
+
"ERR_NETWORK_CHANGED",
|
|
35717
|
+
"ERR_INTERNET_DISCONNECTED",
|
|
35718
|
+
"ERR_TIMED_OUT"
|
|
35719
|
+
],
|
|
35720
|
+
message: "This site didn't respond, so we couldn't read the page. It may be down \u2014 try again later."
|
|
35721
|
+
}
|
|
35722
|
+
];
|
|
35723
|
+
var GENERIC_UNREACHABLE = "We couldn't open this site, so there was no page to study.";
|
|
35724
|
+
var TOO_SLOW = "This site took too long to load, so we stopped waiting. Try again later.";
|
|
35725
|
+
function describeUnreachableSite(error) {
|
|
35726
|
+
const raw = error instanceof Error ? error.message : typeof error === "string" ? error : "";
|
|
35727
|
+
if (!raw) return null;
|
|
35728
|
+
const netError = /net::(ERR_[A-Z0-9_]+)/.exec(raw)?.[1];
|
|
35729
|
+
if (netError) {
|
|
35730
|
+
const reason = NET_ERROR_REASONS.find(
|
|
35731
|
+
(candidate) => candidate.prefixes.some((prefix) => netError.startsWith(prefix))
|
|
35732
|
+
);
|
|
35733
|
+
return { code: "SITE_UNREACHABLE", message: reason?.message ?? GENERIC_UNREACHABLE };
|
|
35734
|
+
}
|
|
35735
|
+
if (/Timeout \d+ms exceeded/.test(raw) || raw.includes("page.goto: Timeout")) {
|
|
35736
|
+
return { code: "SITE_UNREACHABLE", message: TOO_SLOW };
|
|
35737
|
+
}
|
|
35738
|
+
return null;
|
|
35739
|
+
}
|
|
35740
|
+
|
|
35669
35741
|
// src/commands/landing/inspiration/scrape.ts
|
|
35670
35742
|
async function recordCapture(manifest, outDir) {
|
|
35671
35743
|
const consultedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -35701,7 +35773,7 @@ registerSchema({
|
|
|
35701
35773
|
},
|
|
35702
35774
|
motion: {
|
|
35703
35775
|
type: "boolean",
|
|
35704
|
-
description: "Film sections that move. `--no-motion` to skip \u2014
|
|
35776
|
+
description: "Film sections that move. `--no-motion` to skip \u2014 the single biggest lever on runtime",
|
|
35705
35777
|
required: false
|
|
35706
35778
|
},
|
|
35707
35779
|
report: { type: "boolean", description: "Write report.html. `--no-report` to skip", required: false }
|
|
@@ -35773,6 +35845,11 @@ var scrapeCommand = defineCommand147({
|
|
|
35773
35845
|
writeJson({ ok: false, error: { code: error.code, message: error.message } });
|
|
35774
35846
|
process.exit(1);
|
|
35775
35847
|
}
|
|
35848
|
+
const unreachable = describeUnreachableSite(error);
|
|
35849
|
+
if (unreachable) {
|
|
35850
|
+
writeJson({ ok: false, error: { code: unreachable.code, message: unreachable.message } });
|
|
35851
|
+
process.exit(1);
|
|
35852
|
+
}
|
|
35776
35853
|
reportError(error);
|
|
35777
35854
|
}
|
|
35778
35855
|
}
|