@koda-sl/baker-cli 0.447.0-dev.397af7869 → 0.452.0-dev.63867ce0e
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-Q2ZO6I47.js} +146 -42
- package/dist/chunk-Q2ZO6I47.js.map +1 -0
- package/dist/cli.js +206 -71
- 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-Q2ZO6I47.js";
|
|
205
205
|
import {
|
|
206
206
|
NAME_CONTAINS_ARG,
|
|
207
207
|
csvOrJson,
|
|
@@ -67690,11 +67690,15 @@ function unsourcedFigureLines(spec, knowledge2) {
|
|
|
67690
67690
|
const records = withDigits(knowledge2);
|
|
67691
67691
|
const lower = knowledge2.toLowerCase();
|
|
67692
67692
|
return spec.copy.flatMap((line) => {
|
|
67693
|
-
const
|
|
67694
|
-
const
|
|
67695
|
-
|
|
67693
|
+
const figures = [...withDigits(line.text).matchAll(CLAIMED_FIGURE)].filter((m) => !figureSourced(m, records));
|
|
67694
|
+
const superlatives = [...line.text.matchAll(SUPERLATIVE)].filter((m) => !lower.includes(m[0].toLowerCase()));
|
|
67695
|
+
const claims = [...new Set([...superlatives, ...figures].map((m) => m[0].trim()))];
|
|
67696
|
+
return claims.length > 0 ? [{ text: line.text, claims }] : [];
|
|
67696
67697
|
});
|
|
67697
67698
|
}
|
|
67699
|
+
function claimsWithoutSource(spec, knowledge2) {
|
|
67700
|
+
return [...new Set(unsourcedFigureLines(spec, knowledge2).flatMap((l) => l.claims))];
|
|
67701
|
+
}
|
|
67698
67702
|
function proofProblems(spec, knowledge2) {
|
|
67699
67703
|
const problems = [];
|
|
67700
67704
|
const named = /* @__PURE__ */ new Set();
|
|
@@ -67713,9 +67717,10 @@ function proofProblems(spec, knowledge2) {
|
|
|
67713
67717
|
);
|
|
67714
67718
|
}
|
|
67715
67719
|
}
|
|
67716
|
-
for (const text3 of unsourcedFigureLines(spec, knowledge2)) {
|
|
67720
|
+
for (const { text: text3, claims } of unsourcedFigureLines(spec, knowledge2)) {
|
|
67721
|
+
const named2 = claims.map((c) => `"${c}"`).join(", ");
|
|
67717
67722
|
problems.push(
|
|
67718
|
-
|
|
67723
|
+
`${claims.length === 1 && claims[0] === text3.trim() ? named2 : `${named2} in "${text3}"`}: the company's records (src/content, COMPANY.md) do not hold it, so the line promises what nobody wrote. Say only what they record, or drop ${claims.length === 1 ? "it" : "them"}.`
|
|
67719
67724
|
);
|
|
67720
67725
|
}
|
|
67721
67726
|
return problems;
|
|
@@ -71148,7 +71153,10 @@ function fieldProblems(f) {
|
|
|
71148
71153
|
"message is missing: the one promise a stranger should get, what is offered and what changes for them"
|
|
71149
71154
|
);
|
|
71150
71155
|
if (!f.why) problems.push("why is missing: the brief finding it answers");
|
|
71151
|
-
if (!f.awareness)
|
|
71156
|
+
if (!f.awareness) {
|
|
71157
|
+
const written = f.awarenessWritten ? `"${f.awarenessWritten}" ` : "";
|
|
71158
|
+
problems.push(`awareness ${written}must be one of ${AWARENESS_STAGES.join(", ")}`);
|
|
71159
|
+
}
|
|
71152
71160
|
if (!(f.angle && IDEA_ANGLES.includes(f.angle)))
|
|
71153
71161
|
problems.push(`angle ${f.angle ? `"${f.angle}" ` : ""}must be one of ${IDEA_ANGLES.join(", ")}`);
|
|
71154
71162
|
if (!(f.format && IDEA_FORMATS.includes(f.format)))
|
|
@@ -71165,7 +71173,8 @@ function parseIdea(id, markdown) {
|
|
|
71165
71173
|
why: text2(fm.why),
|
|
71166
71174
|
angle: text2(fm.angle),
|
|
71167
71175
|
format: text2(fm.format),
|
|
71168
|
-
awareness: oneOf(AWARENESS_STAGES, fm.awareness)
|
|
71176
|
+
awareness: oneOf(AWARENESS_STAGES, fm.awareness),
|
|
71177
|
+
awarenessWritten: text2(fm.awareness)
|
|
71169
71178
|
};
|
|
71170
71179
|
const problems = fieldProblems(f);
|
|
71171
71180
|
const { who, message, why, angle, format, awareness } = f;
|
|
@@ -71249,11 +71258,28 @@ function repeatedFailure(idea, failed) {
|
|
|
71249
71258
|
);
|
|
71250
71259
|
return lost ? [`says what "${lost.id}" said, and that idea failed in the account: change the message, not just the picture`] : [];
|
|
71251
71260
|
}
|
|
71261
|
+
function likelyMeant(id, insights) {
|
|
71262
|
+
const words2 = (text3) => new Set(
|
|
71263
|
+
text3.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((w) => w.length >= 3)
|
|
71264
|
+
);
|
|
71265
|
+
const wanted = words2(id);
|
|
71266
|
+
let best;
|
|
71267
|
+
for (const insight of insights.values()) {
|
|
71268
|
+
const said = words2(`${insight.id} ${insight.quote}`);
|
|
71269
|
+
const shared = [...wanted].filter((w) => said.has(w)).length;
|
|
71270
|
+
if (shared > 0 && shared > (best?.shared ?? 0)) best = { id: insight.id, shared };
|
|
71271
|
+
}
|
|
71272
|
+
return best?.id;
|
|
71273
|
+
}
|
|
71252
71274
|
function backingOf(idea, insights, problems) {
|
|
71253
71275
|
const backing = idea.evidence.flatMap((id) => {
|
|
71254
71276
|
const insight = insights.get(id);
|
|
71255
|
-
if (!insight)
|
|
71256
|
-
|
|
71277
|
+
if (!insight) {
|
|
71278
|
+
const meant = likelyMeant(id, insights);
|
|
71279
|
+
problems.push(
|
|
71280
|
+
`evidence "${id}" is not in src/content/insights/: ${meant ? `did you mean "${meant}"? ` : ""}name an insight on file by its id, or leave it out`
|
|
71281
|
+
);
|
|
71282
|
+
}
|
|
71257
71283
|
return insight ? [insight] : [];
|
|
71258
71284
|
});
|
|
71259
71285
|
return [...backing].sort((a, b) => EVIDENCE_WEIGHT[b.status] - EVIDENCE_WEIGHT[a.status])[0];
|
|
@@ -71731,33 +71757,49 @@ async function staticSpecGate(canvas, canvasDir) {
|
|
|
71731
71757
|
fix: "Write the ad in prompt.json: the scene the viewer sees and each line of copy as it prints. Then re-run."
|
|
71732
71758
|
};
|
|
71733
71759
|
}
|
|
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
|
-
}
|
|
71760
|
+
const concept = {
|
|
71761
|
+
code: "concept_does_not_fit",
|
|
71762
|
+
problems: conceptProblems(spec),
|
|
71763
|
+
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."
|
|
71764
|
+
};
|
|
71750
71765
|
const root = await findBrandRoot(canvasDir) ?? findWorkspaceRoot(canvasDir) ?? canvasDir;
|
|
71751
71766
|
const records = await readRecords(root);
|
|
71752
|
-
|
|
71753
|
-
|
|
71754
|
-
|
|
71755
|
-
|
|
71756
|
-
|
|
71757
|
-
|
|
71758
|
-
|
|
71759
|
-
|
|
71760
|
-
|
|
71767
|
+
const gates = spec.native ? [
|
|
71768
|
+
concept,
|
|
71769
|
+
{
|
|
71770
|
+
// A native screen's people are photos it describes, briefed as ordinary people of the market.
|
|
71771
|
+
code: "native_screen_invalid",
|
|
71772
|
+
problems: nativeGateProblems(spec.native, records, spec.brand?.name),
|
|
71773
|
+
fix: "Fix prompt.json's `native` block."
|
|
71774
|
+
}
|
|
71775
|
+
] : [
|
|
71776
|
+
{
|
|
71777
|
+
// Words the scene asks for that the copy does not hold: refused here, free, rather than erased by a paid round.
|
|
71778
|
+
code: "scene_words_not_in_copy",
|
|
71779
|
+
problems: sceneWordProblems(spec),
|
|
71780
|
+
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."
|
|
71781
|
+
},
|
|
71782
|
+
concept,
|
|
71783
|
+
{
|
|
71784
|
+
code: "proof_without_source",
|
|
71785
|
+
problems: proofProblems(spec, records),
|
|
71786
|
+
fix: proofFix(claimsWithoutSource(spec, records))
|
|
71787
|
+
}
|
|
71788
|
+
];
|
|
71789
|
+
return oneRefusal(gates.filter((g) => g.problems.length > 0));
|
|
71790
|
+
}
|
|
71791
|
+
function proofFix(claims) {
|
|
71792
|
+
const named = claims.length > 0 ? `drop ${claims.map((c) => `"${c}"`).join(", ")} or source it in the Knowledge, and ` : "";
|
|
71793
|
+
return `In prompt.json, ${named}wire the badge or drop any other claim named above, everywhere it appears (copy and scene).`;
|
|
71794
|
+
}
|
|
71795
|
+
function oneRefusal(gates) {
|
|
71796
|
+
const first = gates[0];
|
|
71797
|
+
if (!first) return null;
|
|
71798
|
+
return {
|
|
71799
|
+
code: first.code,
|
|
71800
|
+
problems: [...new Set(gates.flatMap((g) => g.problems))],
|
|
71801
|
+
fix: `${gates.map((g) => g.fix).join(" ")} Then re-run.`
|
|
71802
|
+
};
|
|
71761
71803
|
}
|
|
71762
71804
|
async function readRecords(root) {
|
|
71763
71805
|
const parts = [await readFile12(path14.join(root, "COMPANY.md"), "utf8").catch(() => "")];
|
|
@@ -72735,6 +72777,7 @@ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
|
|
|
72735
72777
|
|
|
72736
72778
|
// src/commands/canvas/run-resume.ts
|
|
72737
72779
|
import { randomUUID } from "crypto";
|
|
72780
|
+
import { rmSync } from "fs";
|
|
72738
72781
|
import { mkdir as mkdir4, readFile as readFile17, rm as rm4, writeFile as writeFile9 } from "fs/promises";
|
|
72739
72782
|
import path22 from "path";
|
|
72740
72783
|
function markerKey(canvasPath) {
|
|
@@ -72838,6 +72881,40 @@ async function markRunInFlight(outputsDir, canvasPath, runId, sourceSha) {
|
|
|
72838
72881
|
timer.unref();
|
|
72839
72882
|
return { token, stop: () => clearInterval(timer) };
|
|
72840
72883
|
}
|
|
72884
|
+
function busyFile(outputsDir, canvasPath) {
|
|
72885
|
+
return path22.join(outputsDir, ".inflight", `${markerKey(canvasPath)}.busy.json`);
|
|
72886
|
+
}
|
|
72887
|
+
async function markCanvasBusy(outputsDir, canvasPath, runId, pid = process.pid) {
|
|
72888
|
+
const file = busyFile(outputsDir, canvasPath);
|
|
72889
|
+
const write = () => writeFile9(file, JSON.stringify({ runId, pid, heartbeatAt: Date.now() }));
|
|
72890
|
+
try {
|
|
72891
|
+
await mkdir4(path22.dirname(file), { recursive: true });
|
|
72892
|
+
await write();
|
|
72893
|
+
} catch {
|
|
72894
|
+
}
|
|
72895
|
+
const timer = setInterval(() => void write().catch(() => void 0), HEARTBEAT_MS);
|
|
72896
|
+
timer.unref();
|
|
72897
|
+
let released = false;
|
|
72898
|
+
const release = () => {
|
|
72899
|
+
if (released) return;
|
|
72900
|
+
released = true;
|
|
72901
|
+
clearInterval(timer);
|
|
72902
|
+
try {
|
|
72903
|
+
rmSync(file, { force: true });
|
|
72904
|
+
} catch {
|
|
72905
|
+
}
|
|
72906
|
+
};
|
|
72907
|
+
return { release };
|
|
72908
|
+
}
|
|
72909
|
+
async function canvasBusyElsewhere(outputsDir, canvasPath, now = Date.now()) {
|
|
72910
|
+
try {
|
|
72911
|
+
const held = JSON.parse(await readFile17(busyFile(outputsDir, canvasPath), "utf8"));
|
|
72912
|
+
if (typeof held.runId !== "string" || held.pid === void 0 || held.pid === process.pid) return null;
|
|
72913
|
+
return isLiveRender(held, now) ? { runId: held.runId, pid: held.pid } : null;
|
|
72914
|
+
} catch {
|
|
72915
|
+
return null;
|
|
72916
|
+
}
|
|
72917
|
+
}
|
|
72841
72918
|
async function clearRunMarker(outputsDir, canvasPath, token) {
|
|
72842
72919
|
try {
|
|
72843
72920
|
if (token !== void 0) {
|
|
@@ -73557,7 +73634,7 @@ var sizesOf = (what) => what.match(/^(\d+(?:\.\d+)?:\d+(?:\.\d+)?(?:,\s*\d+(?:\.
|
|
|
73557
73634
|
function carryUnchanged(now, prior, unchanged) {
|
|
73558
73635
|
const settled = (f) => {
|
|
73559
73636
|
const sizes = sizesOf(f.what);
|
|
73560
|
-
return sizes.length > 0 && sizes.every((s) => unchanged.includes(s));
|
|
73637
|
+
return sizes.length > 0 && sizes.every((s) => unchanged.includes(s)) && !SIZES_FINDING.test(withoutSize(f.what));
|
|
73561
73638
|
};
|
|
73562
73639
|
return [...now.filter((f) => !settled(f)), ...prior.filter(settled)];
|
|
73563
73640
|
}
|
|
@@ -73566,6 +73643,7 @@ var PAST_BAND = /^(.+?) sits? (?:close to )?where (.+?) covers? (?:it|them) \((.
|
|
|
73566
73643
|
var OFF_EDGE = /^(.+?) runs? off the edge of the picture \((.+)\)\.?$/;
|
|
73567
73644
|
var OUTSIDE_SPACE = /^(.+?) sits? outside the space every size keeps clear \((.+)\)\.?$/;
|
|
73568
73645
|
var EXTRA_WORDS = /^the picture adds words that aren't in the copy: (.+?)\. Remove them, or add them to the copy\.?$/;
|
|
73646
|
+
var SCENE_PRICES = /^the picture shows prices the copy does not hold: (.+?)\. Make them unreadable\.?$/;
|
|
73569
73647
|
var PLACED = [PAST_BAND, OFF_EDGE, OUTSIDE_SPACE];
|
|
73570
73648
|
var MISREAD = /^Text: (".+") reads (".*")\.?$/;
|
|
73571
73649
|
var MISSING = /^Text: (".+") is missing\.?$/;
|
|
@@ -73601,6 +73679,10 @@ function correctionOf(finding, band) {
|
|
|
73601
73679
|
if (extra) {
|
|
73602
73680
|
return `Erase these words wherever they appear, leaving the surface they are on blank or with plain lines: ${extra[1]}.`;
|
|
73603
73681
|
}
|
|
73682
|
+
const prices = what.match(SCENE_PRICES);
|
|
73683
|
+
if (prices) {
|
|
73684
|
+
return `Blur these figures until no digit can be read, keeping the document or screen they are on as it is: ${prices[1]}.`;
|
|
73685
|
+
}
|
|
73604
73686
|
const misread2 = what.match(MISREAD);
|
|
73605
73687
|
if (misread2) return `Re-letter ${misread2[1]} exactly as written, letter for letter (it now reads ${misread2[2]}).`;
|
|
73606
73688
|
const missing = what.match(MISSING);
|
|
@@ -73636,7 +73718,12 @@ function autoFixStep(canvas, review, opts = {}) {
|
|
|
73636
73718
|
if (ids2.length > 0) return { kind: "relayout", ids: ids2, edits, ...placing };
|
|
73637
73719
|
return extend.length > 0 ? { kind: "extend", sizes: extend } : null;
|
|
73638
73720
|
};
|
|
73639
|
-
const
|
|
73721
|
+
const onSeveralSizes = (f) => {
|
|
73722
|
+
const what = withoutSize(f.what);
|
|
73723
|
+
return new Set(rest.filter((g) => withoutSize(g.what) === what).flatMap((g) => sizesOf(g.what))).size >= 2;
|
|
73724
|
+
};
|
|
73725
|
+
const settledPicture = opts.previous === "relayout" || opts.previous === "extend";
|
|
73726
|
+
const pictureFindings = rest.filter((f) => !at(f) && (!settledPicture || onSeveralSizes(f)));
|
|
73640
73727
|
const onPicture = unique(pictureFindings.map((f) => withoutSize(f.what)));
|
|
73641
73728
|
if (onPicture.length === 0) return sizesOnly();
|
|
73642
73729
|
const every = unique([...extend, ...deliveredSizes(canvas)]);
|
|
@@ -73911,7 +73998,7 @@ var CHOSEN = [
|
|
|
73911
73998
|
(id, out) => id === "ad" && out.role === void 0
|
|
73912
73999
|
];
|
|
73913
74000
|
function urlOf(out) {
|
|
73914
|
-
const url = out.images?.find((o) => typeof o?.url === "string")?.url ?? out.image?.url;
|
|
74001
|
+
const url = out.images?.find((o) => typeof o?.url === "string")?.url ?? out.image?.url ?? out.asset?.url;
|
|
73915
74002
|
return typeof url === "string" ? url : null;
|
|
73916
74003
|
}
|
|
73917
74004
|
function chosenUrl(byNode) {
|
|
@@ -74155,7 +74242,7 @@ function turnRenderRefusal(ledger, turnId, slug) {
|
|
|
74155
74242
|
return {
|
|
74156
74243
|
code: "CONFLICT",
|
|
74157
74244
|
message: `This ad was rendered twice in this turn, its automatic corrections included, and is still not ready.`,
|
|
74158
|
-
fix:
|
|
74245
|
+
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
74246
|
};
|
|
74160
74247
|
}
|
|
74161
74248
|
return null;
|
|
@@ -75369,6 +75456,7 @@ async function canvasFileFor(opts) {
|
|
|
75369
75456
|
throw e;
|
|
75370
75457
|
}
|
|
75371
75458
|
}
|
|
75459
|
+
var canvasHeld = false;
|
|
75372
75460
|
async function executeCanvasRun(opts) {
|
|
75373
75461
|
const { canvasPath: filePath, adoptRunId } = await canvasFileFor(opts);
|
|
75374
75462
|
const canvasRoot = canvasOutputsRoot(filePath);
|
|
@@ -75608,7 +75696,8 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75608
75696
|
);
|
|
75609
75697
|
process.exit(2);
|
|
75610
75698
|
}
|
|
75611
|
-
|
|
75699
|
+
const advisories = (validation.warnings ?? []).filter((w) => w.code !== "ORPHAN_NODE");
|
|
75700
|
+
for (const w of [...advisories, ...await outputRatioWarningsFor(parsed, filePath)]) {
|
|
75612
75701
|
process.stdout.write(`[warn] ${w.code}: ${w.message}
|
|
75613
75702
|
`);
|
|
75614
75703
|
}
|
|
@@ -75625,14 +75714,15 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75625
75714
|
sourceSha,
|
|
75626
75715
|
remote: client && creativeSlug && sourceSha ? { creativeSlug, sourceSha, getActiveRun: (slug, sha) => client.getActiveRun(slug, sha) } : void 0
|
|
75627
75716
|
});
|
|
75628
|
-
|
|
75717
|
+
const holder = rendering ?? await canvasBusyElsewhere(outputsDir, filePath);
|
|
75718
|
+
if (holder) {
|
|
75629
75719
|
process.stderr.write(
|
|
75630
75720
|
`${JSON.stringify(
|
|
75631
75721
|
{
|
|
75632
75722
|
ok: false,
|
|
75633
75723
|
error: {
|
|
75634
75724
|
code: "CONFLICT",
|
|
75635
|
-
message: `This canvas is already rendering in another process (run ${
|
|
75725
|
+
message: `This canvas is already rendering in another process (run ${holder.runId}, pid ${holder.pid}).`,
|
|
75636
75726
|
fix: "Wait for that render to finish and use its result instead of starting a second one: two renders of one canvas share a run and overwrite each other. If you started it in the background, read its output when it completes."
|
|
75637
75727
|
}
|
|
75638
75728
|
},
|
|
@@ -75695,6 +75785,11 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75695
75785
|
});
|
|
75696
75786
|
}
|
|
75697
75787
|
const inFlight = await markRunInFlight(outputsDir, filePath, runId, sourceSha);
|
|
75788
|
+
if (!canvasHeld) {
|
|
75789
|
+
const held = await markCanvasBusy(outputsDir, filePath, runId);
|
|
75790
|
+
canvasHeld = true;
|
|
75791
|
+
process.once("exit", held.release);
|
|
75792
|
+
}
|
|
75698
75793
|
const clearMarker = () => {
|
|
75699
75794
|
inFlight.stop();
|
|
75700
75795
|
return clearRunMarker(outputsDir, filePath, inFlight.token);
|
|
@@ -81359,22 +81454,32 @@ async function readViewer(root, adSet) {
|
|
|
81359
81454
|
}
|
|
81360
81455
|
var MARKET_ADS = 40;
|
|
81361
81456
|
var MARKET_FALLBACK_ADS = 15;
|
|
81457
|
+
var MARKET_SEARCH_MS = 12e3;
|
|
81458
|
+
var MARKET_QUERY_WORDS = 6;
|
|
81362
81459
|
function marketQuery(companyDoc) {
|
|
81363
|
-
const
|
|
81364
|
-
|
|
81460
|
+
const first = companyDoc.split("\n").find((line) => line.trim() && !line.trim().startsWith("#")) ?? "";
|
|
81461
|
+
const what = first.replace(/^\s*[-*]\s+/, "").replace(/^\*\*[^*]+\*\*\s*[—–:-]?\s*/, "").split(/\s+for\s+|[,.;:(]|\s[—–-]\s/)[0];
|
|
81462
|
+
return (what ?? "").trim().split(/\s+/).filter(Boolean).slice(0, MARKET_QUERY_WORDS).join(" ");
|
|
81365
81463
|
}
|
|
81366
81464
|
async function readMarket(root) {
|
|
81367
81465
|
const query = marketQuery(await readFile36(path44.join(root, "COMPANY.md"), "utf8").catch(() => ""));
|
|
81368
81466
|
if (!query) return null;
|
|
81369
|
-
const search = (body) => apiPost("/api/ad-library/winners/search", body
|
|
81370
|
-
|
|
81371
|
-
|
|
81372
|
-
|
|
81373
|
-
|
|
81374
|
-
|
|
81375
|
-
|
|
81376
|
-
);
|
|
81377
|
-
|
|
81467
|
+
const search = (body) => apiPost("/api/ad-library/winners/search", body, {
|
|
81468
|
+
timeoutMs: MARKET_SEARCH_MS
|
|
81469
|
+
}).catch((err) => {
|
|
81470
|
+
if (err instanceof ApiError && err.code === "TIMEOUT") {
|
|
81471
|
+
throw new Error("the ad library did not answer in time, so competitors' claims are left out");
|
|
81472
|
+
}
|
|
81473
|
+
throw err;
|
|
81474
|
+
});
|
|
81475
|
+
const [followed, all] = await Promise.allSettled([
|
|
81476
|
+
search(buildSearchBody({ scope: "followed", platform: "meta", limit: MARKET_ADS, maxPerAdvertiser: 8 })),
|
|
81477
|
+
search(buildSearchBody({ query, scope: "all", platform: "meta", limit: MARKET_FALLBACK_ADS, maxPerAdvertiser: 3 }))
|
|
81478
|
+
]);
|
|
81479
|
+
if (followed.status === "fulfilled" && followed.value.results.length > 0)
|
|
81480
|
+
return marketMap(followed.value.results, "followed");
|
|
81481
|
+
if (all.status === "rejected") throw all.reason;
|
|
81482
|
+
return all.value.results.length > 0 ? marketMap(all.value.results, "library") : null;
|
|
81378
81483
|
}
|
|
81379
81484
|
async function metaAccountId(given) {
|
|
81380
81485
|
if (given) return given.startsWith("act_") ? given : `act_${given}`;
|
|
@@ -81464,7 +81569,7 @@ function briefHints(parts) {
|
|
|
81464
81569
|
hints2.push(`Ideas ready to build: ${parts.buildNext.join(", ")} (\`baker creatives make <idea>\`).`);
|
|
81465
81570
|
} else {
|
|
81466
81571
|
hints2.push(
|
|
81467
|
-
|
|
81572
|
+
`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
81573
|
);
|
|
81469
81574
|
}
|
|
81470
81575
|
return hints2;
|
|
@@ -82027,11 +82132,11 @@ function lastJsonObject(stdout) {
|
|
|
82027
82132
|
}
|
|
82028
82133
|
return null;
|
|
82029
82134
|
}
|
|
82030
|
-
async function baker(args8) {
|
|
82135
|
+
async function baker(args8, signal) {
|
|
82031
82136
|
const entry = process.argv[1];
|
|
82032
82137
|
const [file, argv] = entry ? [process.execPath, [...process.execArgv, entry, ...args8]] : ["baker", args8];
|
|
82033
82138
|
try {
|
|
82034
|
-
const { stdout } = await run2(file, argv, { maxBuffer: 32 * 1024 * 1024 });
|
|
82139
|
+
const { stdout } = await run2(file, argv, { maxBuffer: 32 * 1024 * 1024, ...signal ? { signal } : {} });
|
|
82035
82140
|
return lastJsonObject(stdout) ?? { ok: false, error: { code: "INTERNAL_ERROR", message: "no answer" } };
|
|
82036
82141
|
} catch (err) {
|
|
82037
82142
|
const { stdout = "", stderr = "" } = err;
|
|
@@ -82073,7 +82178,8 @@ var candidateOf = (c) => ({
|
|
|
82073
82178
|
async function pickReference(root, searches, deps = liveReferenceDeps(root), prepare) {
|
|
82074
82179
|
const candidates = (await findCandidates(searches, deps)).slice(0, FITS_TRIED);
|
|
82075
82180
|
const first = candidates[0];
|
|
82076
|
-
const
|
|
82181
|
+
const stop = new AbortController();
|
|
82182
|
+
const building = first && prepare ? prepare(candidateOf(first), stop.signal) : void 0;
|
|
82077
82183
|
void building?.catch(() => void 0);
|
|
82078
82184
|
const fits = await Promise.all(candidates.map((c) => deps.fit(asPlatform(c.platform), String(c.ad_id))));
|
|
82079
82185
|
let unchecked = null;
|
|
@@ -82085,6 +82191,7 @@ async function pickReference(root, searches, deps = liveReferenceDeps(root), pre
|
|
|
82085
82191
|
else checked += 1;
|
|
82086
82192
|
if (!(fit.ok && fitsIdea(fit.fit))) continue;
|
|
82087
82193
|
const layers = fit.fit.why_it_wins;
|
|
82194
|
+
if (index > 0) stop.abort();
|
|
82088
82195
|
return {
|
|
82089
82196
|
...candidateOf(candidate),
|
|
82090
82197
|
why: [layers.disguise, layers.trigger, layers.logic].filter(Boolean).join("; "),
|
|
@@ -82092,6 +82199,7 @@ async function pickReference(root, searches, deps = liveReferenceDeps(root), pre
|
|
|
82092
82199
|
...building ? index === 0 ? { prepared: building } : { discarded: building } : {}
|
|
82093
82200
|
};
|
|
82094
82201
|
}
|
|
82202
|
+
stop.abort();
|
|
82095
82203
|
return { ...noReference(candidates.length, checked, unchecked), ...building ? { discarded: building } : {} };
|
|
82096
82204
|
}
|
|
82097
82205
|
function noReference(found, checked, unchecked) {
|
|
@@ -82108,6 +82216,18 @@ function noReference(found, checked, unchecked) {
|
|
|
82108
82216
|
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
82217
|
};
|
|
82110
82218
|
}
|
|
82219
|
+
function alreadyWritten(promptJson, slug) {
|
|
82220
|
+
if (promptJson === null || promptJson === void 0) return null;
|
|
82221
|
+
try {
|
|
82222
|
+
if (unwrittenProblems(readAdSpec(promptJson)).length > 0) return null;
|
|
82223
|
+
} catch {
|
|
82224
|
+
}
|
|
82225
|
+
return {
|
|
82226
|
+
code: "ALREADY_MADE",
|
|
82227
|
+
message: `"${slug}" is already made and its ad is written: building it again would replace that ad with placeholders.`,
|
|
82228
|
+
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>.`
|
|
82229
|
+
};
|
|
82230
|
+
}
|
|
82111
82231
|
var flag = (value2) => typeof value2 === "string" && value2 ? value2 : void 0;
|
|
82112
82232
|
async function loadIdea(root, ideaId) {
|
|
82113
82233
|
const { ideas, unreadable } = await readIdeas(root);
|
|
@@ -82160,20 +82280,23 @@ async function scaffoldIdea(root, idea, args8, slug, temperature) {
|
|
|
82160
82280
|
reference: null
|
|
82161
82281
|
};
|
|
82162
82282
|
}
|
|
82163
|
-
const onReference = (c) => baker(
|
|
82164
|
-
|
|
82165
|
-
|
|
82166
|
-
|
|
82167
|
-
|
|
82168
|
-
|
|
82169
|
-
|
|
82170
|
-
|
|
82171
|
-
|
|
82172
|
-
|
|
82173
|
-
|
|
82174
|
-
|
|
82175
|
-
|
|
82176
|
-
|
|
82283
|
+
const onReference = (c, signal) => baker(
|
|
82284
|
+
[
|
|
82285
|
+
"canvas",
|
|
82286
|
+
"scaffold-static-ad",
|
|
82287
|
+
"--source-ad-id",
|
|
82288
|
+
c.adId,
|
|
82289
|
+
"--source-platform",
|
|
82290
|
+
c.platform,
|
|
82291
|
+
...c.advertiserId ? ["--source-advertiser-id", c.advertiserId] : [],
|
|
82292
|
+
"--slug",
|
|
82293
|
+
slug,
|
|
82294
|
+
"--title",
|
|
82295
|
+
idea.message.slice(0, 120),
|
|
82296
|
+
...viewerFlags
|
|
82297
|
+
],
|
|
82298
|
+
signal
|
|
82299
|
+
);
|
|
82177
82300
|
const dir = path46.join(root, "src", "creatives", slug);
|
|
82178
82301
|
const fresh = !await stat7(dir).catch(() => null);
|
|
82179
82302
|
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 +82425,18 @@ var makeCommand = defineCommand159({
|
|
|
82302
82425
|
const opts = makeArgsOf(args8);
|
|
82303
82426
|
const { idea, check } = await loadIdea(root, String(args8.idea));
|
|
82304
82427
|
const slug = opts.slug ?? idea.id;
|
|
82428
|
+
const written = await readFile38(path46.join(root, "src", "creatives", slug, "prompt.json"), "utf8").then(
|
|
82429
|
+
(text3) => {
|
|
82430
|
+
try {
|
|
82431
|
+
return JSON.parse(text3);
|
|
82432
|
+
} catch {
|
|
82433
|
+
return {};
|
|
82434
|
+
}
|
|
82435
|
+
},
|
|
82436
|
+
(err) => err.code === "ENOENT" ? null : {}
|
|
82437
|
+
);
|
|
82438
|
+
const refused = alreadyWritten(written, slug);
|
|
82439
|
+
if (refused) fail6(refused);
|
|
82305
82440
|
const temperature = await viewerTemperature(root, opts.adset, idea.awareness);
|
|
82306
82441
|
const { scaffold, reference, fromIdea } = await scaffoldIdea(root, idea, opts, slug, temperature);
|
|
82307
82442
|
if (scaffold.ok !== true) fail6(scaffoldFailure(scaffold));
|