@koda-sl/baker-cli 0.447.0-dev.397af7869 → 0.449.0-dev.b6179c7f5
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 +4 -4
- package/dist/{chunk-WMAHVZMY.js → chunk-Y33XAZ5M.js} +122 -41
- package/dist/chunk-Y33XAZ5M.js.map +1 -0
- package/dist/cli.js +110 -50
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-WMAHVZMY.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -201,7 +201,7 @@ import {
|
|
|
201
201
|
withoutCastName,
|
|
202
202
|
wordCount,
|
|
203
203
|
ytDlpBlockSignal
|
|
204
|
-
} from "./chunk-
|
|
204
|
+
} from "./chunk-Y33XAZ5M.js";
|
|
205
205
|
import {
|
|
206
206
|
NAME_CONTAINS_ARG,
|
|
207
207
|
csvOrJson,
|
|
@@ -71148,7 +71148,10 @@ function fieldProblems(f) {
|
|
|
71148
71148
|
"message is missing: the one promise a stranger should get, what is offered and what changes for them"
|
|
71149
71149
|
);
|
|
71150
71150
|
if (!f.why) problems.push("why is missing: the brief finding it answers");
|
|
71151
|
-
if (!f.awareness)
|
|
71151
|
+
if (!f.awareness) {
|
|
71152
|
+
const written = f.awarenessWritten ? `"${f.awarenessWritten}" ` : "";
|
|
71153
|
+
problems.push(`awareness ${written}must be one of ${AWARENESS_STAGES.join(", ")}`);
|
|
71154
|
+
}
|
|
71152
71155
|
if (!(f.angle && IDEA_ANGLES.includes(f.angle)))
|
|
71153
71156
|
problems.push(`angle ${f.angle ? `"${f.angle}" ` : ""}must be one of ${IDEA_ANGLES.join(", ")}`);
|
|
71154
71157
|
if (!(f.format && IDEA_FORMATS.includes(f.format)))
|
|
@@ -71165,7 +71168,8 @@ function parseIdea(id, markdown) {
|
|
|
71165
71168
|
why: text2(fm.why),
|
|
71166
71169
|
angle: text2(fm.angle),
|
|
71167
71170
|
format: text2(fm.format),
|
|
71168
|
-
awareness: oneOf(AWARENESS_STAGES, fm.awareness)
|
|
71171
|
+
awareness: oneOf(AWARENESS_STAGES, fm.awareness),
|
|
71172
|
+
awarenessWritten: text2(fm.awareness)
|
|
71169
71173
|
};
|
|
71170
71174
|
const problems = fieldProblems(f);
|
|
71171
71175
|
const { who, message, why, angle, format, awareness } = f;
|
|
@@ -71731,33 +71735,45 @@ async function staticSpecGate(canvas, canvasDir) {
|
|
|
71731
71735
|
fix: "Write the ad in prompt.json: the scene the viewer sees and each line of copy as it prints. Then re-run."
|
|
71732
71736
|
};
|
|
71733
71737
|
}
|
|
71734
|
-
const
|
|
71735
|
-
|
|
71736
|
-
|
|
71737
|
-
|
|
71738
|
-
|
|
71739
|
-
fix: "Edit prompt.json: every word or figure the picture shows is a `copy` line (`in_scene`, with `on`); describe any other surface without words. Then re-run."
|
|
71740
|
-
};
|
|
71741
|
-
}
|
|
71742
|
-
const concept = conceptProblems(spec);
|
|
71743
|
-
if (concept.length > 0) {
|
|
71744
|
-
return {
|
|
71745
|
-
code: "concept_does_not_fit",
|
|
71746
|
-
problems: concept,
|
|
71747
|
-
fix: "Edit prompt.json's `concept`: name what of ours carries each missing element (`ours`), or pick another reference with `baker winning-ads fit`. Never render a copy that has lost what the reference wins on."
|
|
71748
|
-
};
|
|
71749
|
-
}
|
|
71738
|
+
const concept = {
|
|
71739
|
+
code: "concept_does_not_fit",
|
|
71740
|
+
problems: conceptProblems(spec),
|
|
71741
|
+
fix: "Edit prompt.json's `concept`: name what of ours carries each missing element (`ours`), or pick another reference with `baker winning-ads fit`. Never render a copy that has lost what the reference wins on."
|
|
71742
|
+
};
|
|
71750
71743
|
const root = await findBrandRoot(canvasDir) ?? findWorkspaceRoot(canvasDir) ?? canvasDir;
|
|
71751
71744
|
const records = await readRecords(root);
|
|
71752
|
-
|
|
71753
|
-
|
|
71754
|
-
|
|
71755
|
-
|
|
71756
|
-
|
|
71757
|
-
|
|
71758
|
-
|
|
71759
|
-
|
|
71760
|
-
|
|
71745
|
+
const gates = spec.native ? [
|
|
71746
|
+
concept,
|
|
71747
|
+
{
|
|
71748
|
+
// A native screen's people are photos it describes, briefed as ordinary people of the market.
|
|
71749
|
+
code: "native_screen_invalid",
|
|
71750
|
+
problems: nativeGateProblems(spec.native, records, spec.brand?.name),
|
|
71751
|
+
fix: "Fix prompt.json's `native` block."
|
|
71752
|
+
}
|
|
71753
|
+
] : [
|
|
71754
|
+
{
|
|
71755
|
+
// Words the scene asks for that the copy does not hold: refused here, free, rather than erased by a paid round.
|
|
71756
|
+
code: "scene_words_not_in_copy",
|
|
71757
|
+
problems: sceneWordProblems(spec),
|
|
71758
|
+
fix: "Edit prompt.json: every word or figure the picture shows is a `copy` line (`in_scene`, with `on`); describe any other surface without words."
|
|
71759
|
+
},
|
|
71760
|
+
concept,
|
|
71761
|
+
{
|
|
71762
|
+
code: "proof_without_source",
|
|
71763
|
+
problems: proofProblems(spec, records),
|
|
71764
|
+
fix: "Wire the badge or drop the claim in prompt.json, everywhere it appears (copy and scene)."
|
|
71765
|
+
}
|
|
71766
|
+
];
|
|
71767
|
+
return oneRefusal(gates.filter((g) => g.problems.length > 0));
|
|
71768
|
+
}
|
|
71769
|
+
function oneRefusal(gates) {
|
|
71770
|
+
const first = gates[0];
|
|
71771
|
+
if (!first) return null;
|
|
71772
|
+
return {
|
|
71773
|
+
code: first.code,
|
|
71774
|
+
problems: [...new Set(gates.flatMap((g) => g.problems))],
|
|
71775
|
+
fix: `${gates.map((g) => g.fix).join(" ")} Then re-run.`
|
|
71776
|
+
};
|
|
71761
71777
|
}
|
|
71762
71778
|
async function readRecords(root) {
|
|
71763
71779
|
const parts = [await readFile12(path14.join(root, "COMPANY.md"), "utf8").catch(() => "")];
|
|
@@ -73566,6 +73582,7 @@ var PAST_BAND = /^(.+?) sits? (?:close to )?where (.+?) covers? (?:it|them) \((.
|
|
|
73566
73582
|
var OFF_EDGE = /^(.+?) runs? off the edge of the picture \((.+)\)\.?$/;
|
|
73567
73583
|
var OUTSIDE_SPACE = /^(.+?) sits? outside the space every size keeps clear \((.+)\)\.?$/;
|
|
73568
73584
|
var EXTRA_WORDS = /^the picture adds words that aren't in the copy: (.+?)\. Remove them, or add them to the copy\.?$/;
|
|
73585
|
+
var SCENE_PRICES = /^the picture shows prices the copy does not hold: (.+?)\. Make them unreadable\.?$/;
|
|
73569
73586
|
var PLACED = [PAST_BAND, OFF_EDGE, OUTSIDE_SPACE];
|
|
73570
73587
|
var MISREAD = /^Text: (".+") reads (".*")\.?$/;
|
|
73571
73588
|
var MISSING = /^Text: (".+") is missing\.?$/;
|
|
@@ -73601,6 +73618,10 @@ function correctionOf(finding, band) {
|
|
|
73601
73618
|
if (extra) {
|
|
73602
73619
|
return `Erase these words wherever they appear, leaving the surface they are on blank or with plain lines: ${extra[1]}.`;
|
|
73603
73620
|
}
|
|
73621
|
+
const prices = what.match(SCENE_PRICES);
|
|
73622
|
+
if (prices) {
|
|
73623
|
+
return `Blur these figures until no digit can be read, keeping the document or screen they are on as it is: ${prices[1]}.`;
|
|
73624
|
+
}
|
|
73604
73625
|
const misread2 = what.match(MISREAD);
|
|
73605
73626
|
if (misread2) return `Re-letter ${misread2[1]} exactly as written, letter for letter (it now reads ${misread2[2]}).`;
|
|
73606
73627
|
const missing = what.match(MISSING);
|
|
@@ -73911,7 +73932,7 @@ var CHOSEN = [
|
|
|
73911
73932
|
(id, out) => id === "ad" && out.role === void 0
|
|
73912
73933
|
];
|
|
73913
73934
|
function urlOf(out) {
|
|
73914
|
-
const url = out.images?.find((o) => typeof o?.url === "string")?.url ?? out.image?.url;
|
|
73935
|
+
const url = out.images?.find((o) => typeof o?.url === "string")?.url ?? out.image?.url ?? out.asset?.url;
|
|
73915
73936
|
return typeof url === "string" ? url : null;
|
|
73916
73937
|
}
|
|
73917
73938
|
function chosenUrl(byNode) {
|
|
@@ -74155,7 +74176,7 @@ function turnRenderRefusal(ledger, turnId, slug) {
|
|
|
74155
74176
|
return {
|
|
74156
74177
|
code: "CONFLICT",
|
|
74157
74178
|
message: `This ad was rendered twice in this turn, its automatic corrections included, and is still not ready.`,
|
|
74158
|
-
fix:
|
|
74179
|
+
fix: `Show the best version as a version ("Version 3"), never a file path or a run id, and say in one plain sentence what is left, in the person's words. End there: no question and no options; their next message says what changes.`
|
|
74159
74180
|
};
|
|
74160
74181
|
}
|
|
74161
74182
|
return null;
|
|
@@ -75608,7 +75629,8 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75608
75629
|
);
|
|
75609
75630
|
process.exit(2);
|
|
75610
75631
|
}
|
|
75611
|
-
|
|
75632
|
+
const advisories = (validation.warnings ?? []).filter((w) => w.code !== "ORPHAN_NODE");
|
|
75633
|
+
for (const w of [...advisories, ...await outputRatioWarningsFor(parsed, filePath)]) {
|
|
75612
75634
|
process.stdout.write(`[warn] ${w.code}: ${w.message}
|
|
75613
75635
|
`);
|
|
75614
75636
|
}
|
|
@@ -81359,6 +81381,7 @@ async function readViewer(root, adSet) {
|
|
|
81359
81381
|
}
|
|
81360
81382
|
var MARKET_ADS = 40;
|
|
81361
81383
|
var MARKET_FALLBACK_ADS = 15;
|
|
81384
|
+
var MARKET_SEARCH_MS = 1e4;
|
|
81362
81385
|
function marketQuery(companyDoc) {
|
|
81363
81386
|
const prose = companyDoc.split("\n").filter((line) => line.trim() && !line.startsWith("#")).join(" ").replace(/\s+/g, " ");
|
|
81364
81387
|
return prose.slice(0, 240);
|
|
@@ -81366,7 +81389,14 @@ function marketQuery(companyDoc) {
|
|
|
81366
81389
|
async function readMarket(root) {
|
|
81367
81390
|
const query = marketQuery(await readFile36(path44.join(root, "COMPANY.md"), "utf8").catch(() => ""));
|
|
81368
81391
|
if (!query) return null;
|
|
81369
|
-
const search = (body) => apiPost("/api/ad-library/winners/search", body
|
|
81392
|
+
const search = (body) => apiPost("/api/ad-library/winners/search", body, {
|
|
81393
|
+
timeoutMs: MARKET_SEARCH_MS
|
|
81394
|
+
}).catch((err) => {
|
|
81395
|
+
if (err instanceof ApiError && err.code === "TIMEOUT") {
|
|
81396
|
+
throw new Error("the ad library did not answer in time, so competitors' claims are left out");
|
|
81397
|
+
}
|
|
81398
|
+
throw err;
|
|
81399
|
+
});
|
|
81370
81400
|
const followed = await search(
|
|
81371
81401
|
buildSearchBody({ scope: "followed", platform: "meta", limit: MARKET_ADS, maxPerAdvertiser: 8 })
|
|
81372
81402
|
);
|
|
@@ -81464,7 +81494,7 @@ function briefHints(parts) {
|
|
|
81464
81494
|
hints2.push(`Ideas ready to build: ${parts.buildNext.join(", ")} (\`baker creatives make <idea>\`).`);
|
|
81465
81495
|
} else {
|
|
81466
81496
|
hints2.push(
|
|
81467
|
-
|
|
81497
|
+
`Write 4-8 ideas in src/creatives/_ideas/<idea>.md (who, message, evidence, awareness, angle, format, job, why), then \`baker creatives ideas\`. awareness: ${AWARENESS_STAGES.join(" | ")}. angle: ${IDEA_ANGLES.join(", ")}. format: ${IDEA_FORMATS.join(", ")}.`
|
|
81468
81498
|
);
|
|
81469
81499
|
}
|
|
81470
81500
|
return hints2;
|
|
@@ -82027,11 +82057,11 @@ function lastJsonObject(stdout) {
|
|
|
82027
82057
|
}
|
|
82028
82058
|
return null;
|
|
82029
82059
|
}
|
|
82030
|
-
async function baker(args8) {
|
|
82060
|
+
async function baker(args8, signal) {
|
|
82031
82061
|
const entry = process.argv[1];
|
|
82032
82062
|
const [file, argv] = entry ? [process.execPath, [...process.execArgv, entry, ...args8]] : ["baker", args8];
|
|
82033
82063
|
try {
|
|
82034
|
-
const { stdout } = await run2(file, argv, { maxBuffer: 32 * 1024 * 1024 });
|
|
82064
|
+
const { stdout } = await run2(file, argv, { maxBuffer: 32 * 1024 * 1024, ...signal ? { signal } : {} });
|
|
82035
82065
|
return lastJsonObject(stdout) ?? { ok: false, error: { code: "INTERNAL_ERROR", message: "no answer" } };
|
|
82036
82066
|
} catch (err) {
|
|
82037
82067
|
const { stdout = "", stderr = "" } = err;
|
|
@@ -82073,7 +82103,8 @@ var candidateOf = (c) => ({
|
|
|
82073
82103
|
async function pickReference(root, searches, deps = liveReferenceDeps(root), prepare) {
|
|
82074
82104
|
const candidates = (await findCandidates(searches, deps)).slice(0, FITS_TRIED);
|
|
82075
82105
|
const first = candidates[0];
|
|
82076
|
-
const
|
|
82106
|
+
const stop = new AbortController();
|
|
82107
|
+
const building = first && prepare ? prepare(candidateOf(first), stop.signal) : void 0;
|
|
82077
82108
|
void building?.catch(() => void 0);
|
|
82078
82109
|
const fits = await Promise.all(candidates.map((c) => deps.fit(asPlatform(c.platform), String(c.ad_id))));
|
|
82079
82110
|
let unchecked = null;
|
|
@@ -82085,6 +82116,7 @@ async function pickReference(root, searches, deps = liveReferenceDeps(root), pre
|
|
|
82085
82116
|
else checked += 1;
|
|
82086
82117
|
if (!(fit.ok && fitsIdea(fit.fit))) continue;
|
|
82087
82118
|
const layers = fit.fit.why_it_wins;
|
|
82119
|
+
if (index > 0) stop.abort();
|
|
82088
82120
|
return {
|
|
82089
82121
|
...candidateOf(candidate),
|
|
82090
82122
|
why: [layers.disguise, layers.trigger, layers.logic].filter(Boolean).join("; "),
|
|
@@ -82092,6 +82124,7 @@ async function pickReference(root, searches, deps = liveReferenceDeps(root), pre
|
|
|
82092
82124
|
...building ? index === 0 ? { prepared: building } : { discarded: building } : {}
|
|
82093
82125
|
};
|
|
82094
82126
|
}
|
|
82127
|
+
stop.abort();
|
|
82095
82128
|
return { ...noReference(candidates.length, checked, unchecked), ...building ? { discarded: building } : {} };
|
|
82096
82129
|
}
|
|
82097
82130
|
function noReference(found, checked, unchecked) {
|
|
@@ -82108,6 +82141,18 @@ function noReference(found, checked, unchecked) {
|
|
|
82108
82141
|
fix: "Change the idea's format to another container (`baker creatives ideas --full` lists them) and run make again, or pass --reference <adId> for one you chose. Do not stop the batch: build the other ideas."
|
|
82109
82142
|
};
|
|
82110
82143
|
}
|
|
82144
|
+
function alreadyWritten(promptJson, slug) {
|
|
82145
|
+
if (promptJson === null || promptJson === void 0) return null;
|
|
82146
|
+
try {
|
|
82147
|
+
if (unwrittenProblems(readAdSpec(promptJson)).length > 0) return null;
|
|
82148
|
+
} catch {
|
|
82149
|
+
}
|
|
82150
|
+
return {
|
|
82151
|
+
code: "ALREADY_MADE",
|
|
82152
|
+
message: `"${slug}" is already made and its ad is written: building it again would replace that ad with placeholders.`,
|
|
82153
|
+
fix: `To change this ad, edit src/creatives/${slug}/prompt.json and run \`baker canvas run\` on it. For another ad of the same idea, run make again with --slug <new-slug>.`
|
|
82154
|
+
};
|
|
82155
|
+
}
|
|
82111
82156
|
var flag = (value2) => typeof value2 === "string" && value2 ? value2 : void 0;
|
|
82112
82157
|
async function loadIdea(root, ideaId) {
|
|
82113
82158
|
const { ideas, unreadable } = await readIdeas(root);
|
|
@@ -82160,20 +82205,23 @@ async function scaffoldIdea(root, idea, args8, slug, temperature) {
|
|
|
82160
82205
|
reference: null
|
|
82161
82206
|
};
|
|
82162
82207
|
}
|
|
82163
|
-
const onReference = (c) => baker(
|
|
82164
|
-
|
|
82165
|
-
|
|
82166
|
-
|
|
82167
|
-
|
|
82168
|
-
|
|
82169
|
-
|
|
82170
|
-
|
|
82171
|
-
|
|
82172
|
-
|
|
82173
|
-
|
|
82174
|
-
|
|
82175
|
-
|
|
82176
|
-
|
|
82208
|
+
const onReference = (c, signal) => baker(
|
|
82209
|
+
[
|
|
82210
|
+
"canvas",
|
|
82211
|
+
"scaffold-static-ad",
|
|
82212
|
+
"--source-ad-id",
|
|
82213
|
+
c.adId,
|
|
82214
|
+
"--source-platform",
|
|
82215
|
+
c.platform,
|
|
82216
|
+
...c.advertiserId ? ["--source-advertiser-id", c.advertiserId] : [],
|
|
82217
|
+
"--slug",
|
|
82218
|
+
slug,
|
|
82219
|
+
"--title",
|
|
82220
|
+
idea.message.slice(0, 120),
|
|
82221
|
+
...viewerFlags
|
|
82222
|
+
],
|
|
82223
|
+
signal
|
|
82224
|
+
);
|
|
82177
82225
|
const dir = path46.join(root, "src", "creatives", slug);
|
|
82178
82226
|
const fresh = !await stat7(dir).catch(() => null);
|
|
82179
82227
|
const reference = args8.reference ? { adId: args8.reference, platform: asPlatform(args8.platform), why: "chosen by hand", tried: 0 } : args8.fromIdea ? { code: "FROM_IDEA", message: "Asked to start from the idea." } : await pickReference(
|
|
@@ -82302,6 +82350,18 @@ var makeCommand = defineCommand159({
|
|
|
82302
82350
|
const opts = makeArgsOf(args8);
|
|
82303
82351
|
const { idea, check } = await loadIdea(root, String(args8.idea));
|
|
82304
82352
|
const slug = opts.slug ?? idea.id;
|
|
82353
|
+
const written = await readFile38(path46.join(root, "src", "creatives", slug, "prompt.json"), "utf8").then(
|
|
82354
|
+
(text3) => {
|
|
82355
|
+
try {
|
|
82356
|
+
return JSON.parse(text3);
|
|
82357
|
+
} catch {
|
|
82358
|
+
return {};
|
|
82359
|
+
}
|
|
82360
|
+
},
|
|
82361
|
+
(err) => err.code === "ENOENT" ? null : {}
|
|
82362
|
+
);
|
|
82363
|
+
const refused = alreadyWritten(written, slug);
|
|
82364
|
+
if (refused) fail6(refused);
|
|
82305
82365
|
const temperature = await viewerTemperature(root, opts.adset, idea.awareness);
|
|
82306
82366
|
const { scaffold, reference, fromIdea } = await scaffoldIdea(root, idea, opts, slug, temperature);
|
|
82307
82367
|
if (scaffold.ok !== true) fail6(scaffoldFailure(scaffold));
|