@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/dist/cli.js CHANGED
@@ -201,7 +201,7 @@ import {
201
201
  withoutCastName,
202
202
  wordCount,
203
203
  ytDlpBlockSignal
204
- } from "./chunk-WMAHVZMY.js";
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) problems.push(`awareness must be one of ${AWARENESS_STAGES.join(", ")}`);
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 sceneWords = spec.native ? [] : sceneWordProblems(spec);
71735
- if (sceneWords.length > 0) {
71736
- return {
71737
- code: "scene_words_not_in_copy",
71738
- problems: sceneWords,
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
- if (spec.native) {
71753
- const native = nativeGateProblems(spec.native, records, spec.brand?.name);
71754
- return native.length > 0 ? { code: "native_screen_invalid", problems: native, fix: "Fix prompt.json's `native` block, then re-run." } : null;
71755
- }
71756
- const proof = proofProblems(spec, records);
71757
- if (proof.length > 0) {
71758
- return { code: "proof_without_source", problems: proof, fix: "Wire the badge or drop the claim in prompt.json, then re-run." };
71759
- }
71760
- return null;
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: "Show the best version and say in one plain sentence what is left, in the person's words; they decide what changes next."
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
- for (const w of [...validation.warnings ?? [], ...await outputRatioWarningsFor(parsed, filePath)]) {
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
- "Write 4-8 ideas in src/creatives/_ideas/<idea>.md (who, message, evidence, awareness, angle, format, job, why), then `baker creatives ideas`."
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 building = first && prepare ? prepare(candidateOf(first)) : void 0;
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
- "canvas",
82165
- "scaffold-static-ad",
82166
- "--source-ad-id",
82167
- c.adId,
82168
- "--source-platform",
82169
- c.platform,
82170
- ...c.advertiserId ? ["--source-advertiser-id", c.advertiserId] : [],
82171
- "--slug",
82172
- slug,
82173
- "--title",
82174
- idea.message.slice(0, 120),
82175
- ...viewerFlags
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));