@koda-sl/baker-cli 0.436.0-dev.7e3937022 → 0.437.0-dev.ee2f8e252
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
|
@@ -211,7 +211,7 @@ import {
|
|
|
211
211
|
withoutCastName,
|
|
212
212
|
wordCount,
|
|
213
213
|
ytDlpBlockSignal
|
|
214
|
-
} from "./chunk-
|
|
214
|
+
} from "./chunk-FB3GTXQG.js";
|
|
215
215
|
import {
|
|
216
216
|
NAME_CONTAINS_ARG,
|
|
217
217
|
csvOrJson,
|
|
@@ -28236,7 +28236,12 @@ var creativesFeedbackRecordRequestSchema = z20.object({
|
|
|
28236
28236
|
/** Why it was rejected. Ignored on an approval. */
|
|
28237
28237
|
reasons: z20.array(creativeFeedbackReasonSchema).max(CREATIVE_FEEDBACK_REASONS.length).optional(),
|
|
28238
28238
|
/** The person's own words. Stored up to 1000 characters. */
|
|
28239
|
-
note: z20.string().max(4e3).optional()
|
|
28239
|
+
note: z20.string().max(4e3).optional(),
|
|
28240
|
+
/**
|
|
28241
|
+
* The CLI could not tell whether `note` is the person's words or the agent's own option (the
|
|
28242
|
+
* decision model gave no answer). The verdict is kept; the note is never read back as taste.
|
|
28243
|
+
*/
|
|
28244
|
+
noteUnconfirmed: z20.boolean().optional()
|
|
28240
28245
|
});
|
|
28241
28246
|
var creativesFeedbackRecordResponseSchema = z20.object({
|
|
28242
28247
|
ok: z20.literal(true),
|
|
@@ -68611,7 +68616,7 @@ var catalogCommand = defineCommand137({
|
|
|
68611
68616
|
});
|
|
68612
68617
|
|
|
68613
68618
|
// src/commands/canvas/critique.ts
|
|
68614
|
-
import
|
|
68619
|
+
import path24 from "path";
|
|
68615
68620
|
import { defineCommand as defineCommand138 } from "citty";
|
|
68616
68621
|
|
|
68617
68622
|
// src/engine/scaffold/lib/shoot-modes.ts
|
|
@@ -69367,8 +69372,8 @@ function critiqueCanvas(canvas) {
|
|
|
69367
69372
|
}
|
|
69368
69373
|
|
|
69369
69374
|
// src/commands/canvas/prepare-canvas.ts
|
|
69370
|
-
import { readdir as readdir9, readFile as
|
|
69371
|
-
import
|
|
69375
|
+
import { readdir as readdir9, readFile as readFile19, writeFile as writeFile14 } from "fs/promises";
|
|
69376
|
+
import path23 from "path";
|
|
69372
69377
|
|
|
69373
69378
|
// src/commands/canvas/placeholders.ts
|
|
69374
69379
|
function unsuppliedPlaceholderAssets(canvas) {
|
|
@@ -69479,34 +69484,45 @@ var CLAIMS_REQUEST = {
|
|
|
69479
69484
|
var CLAIMS_TIMEOUT_MS = 3e4;
|
|
69480
69485
|
var SUPERLATIVE_HELD = 0.7;
|
|
69481
69486
|
var RECORDS_BATCH_CHARS = 4e4;
|
|
69487
|
+
var CHUNK_OVERLAP = 200;
|
|
69488
|
+
function pack(pieces, joiner) {
|
|
69489
|
+
const runs = [];
|
|
69490
|
+
let run3 = "";
|
|
69491
|
+
for (const piece of pieces) {
|
|
69492
|
+
if (run3 && run3.length + joiner.length + piece.length > RECORDS_BATCH_CHARS) {
|
|
69493
|
+
runs.push(run3);
|
|
69494
|
+
run3 = "";
|
|
69495
|
+
}
|
|
69496
|
+
run3 = run3 ? `${run3}${joiner}${piece}` : piece;
|
|
69497
|
+
}
|
|
69498
|
+
return run3 ? [...runs, run3] : runs;
|
|
69499
|
+
}
|
|
69500
|
+
function withinReading(text4, cuts) {
|
|
69501
|
+
if (text4.length <= RECORDS_BATCH_CHARS) return [text4];
|
|
69502
|
+
const [cut, ...finer] = cuts;
|
|
69503
|
+
if (!cut) {
|
|
69504
|
+
const chunks = [];
|
|
69505
|
+
for (let at = 0; at < text4.length; at += RECORDS_BATCH_CHARS - CHUNK_OVERLAP) {
|
|
69506
|
+
chunks.push(text4.slice(at, at + RECORDS_BATCH_CHARS));
|
|
69507
|
+
if (at + RECORDS_BATCH_CHARS >= text4.length) break;
|
|
69508
|
+
}
|
|
69509
|
+
return chunks;
|
|
69510
|
+
}
|
|
69511
|
+
return pack(
|
|
69512
|
+
text4.split(cut.at).flatMap((piece) => withinReading(piece, finer)),
|
|
69513
|
+
cut.joiner
|
|
69514
|
+
);
|
|
69515
|
+
}
|
|
69516
|
+
var CUTS = [
|
|
69517
|
+
{ at: /\n\s*\n/, joiner: "\n\n" },
|
|
69518
|
+
{ at: /\n/, joiner: "\n" }
|
|
69519
|
+
];
|
|
69482
69520
|
function recordBatches(parts) {
|
|
69483
|
-
const pieces = parts.flatMap((part) =>
|
|
69484
|
-
|
|
69485
|
-
|
|
69486
|
-
|
|
69487
|
-
|
|
69488
|
-
if (piece && piece.length + paragraph.length > RECORDS_BATCH_CHARS) {
|
|
69489
|
-
out.push(piece);
|
|
69490
|
-
piece = "";
|
|
69491
|
-
}
|
|
69492
|
-
piece = piece ? `${piece}
|
|
69493
|
-
|
|
69494
|
-
${paragraph.slice(0, RECORDS_BATCH_CHARS)}` : paragraph.slice(0, RECORDS_BATCH_CHARS);
|
|
69495
|
-
}
|
|
69496
|
-
return piece ? [...out, piece] : out;
|
|
69497
|
-
});
|
|
69498
|
-
const batches = [];
|
|
69499
|
-
let batch = "";
|
|
69500
|
-
for (const piece of pieces.filter((p) => p.trim())) {
|
|
69501
|
-
if (batch && batch.length + piece.length > RECORDS_BATCH_CHARS) {
|
|
69502
|
-
batches.push(batch);
|
|
69503
|
-
batch = "";
|
|
69504
|
-
}
|
|
69505
|
-
batch = batch ? `${batch}
|
|
69506
|
-
|
|
69507
|
-
${piece}` : piece;
|
|
69508
|
-
}
|
|
69509
|
-
return batch ? [...batches, batch] : batches;
|
|
69521
|
+
const pieces = parts.flatMap((part) => withinReading(part.text, CUTS));
|
|
69522
|
+
return pack(
|
|
69523
|
+
pieces.filter((p) => p.trim()),
|
|
69524
|
+
"\n\n"
|
|
69525
|
+
);
|
|
69510
69526
|
}
|
|
69511
69527
|
var RECORDS_SAY = "the company's records (src/content, COMPANY.md, its customers' words) do not hold";
|
|
69512
69528
|
function lineOf(claim, lines) {
|
|
@@ -71161,10 +71177,15 @@ function badgeWired(spec, name) {
|
|
|
71161
71177
|
}
|
|
71162
71178
|
var QUOTED_RUN = 5;
|
|
71163
71179
|
var SIGNING_ROLE = /^(attribution|author|byline|name|reviewer|source|signature)$/i;
|
|
71164
|
-
var
|
|
71180
|
+
var SIGNED_AFTER_STOP = /(?:^\s*|[\p{Pf}\p{Pe}"'.!?…。!?]\s*)[—–―-]\s*[\p{L}\p{N}]/u;
|
|
71181
|
+
var SIGNED_AT_END = new RegExp("\\s[\u2014\u2013\u2015-]\\s+\\p{Lu}[\\p{L}\\p{M}.'\u2019-]*(?:(?:\\s+[\\p{L}\\p{M}.'\u2019-]+){0,2}\\s+\\p{Lu}[\\p{L}\\p{M}.'\u2019-]*)?(?:,[^,\u2014\u2013\u2015]{1,40})?\\s*$", "u");
|
|
71182
|
+
var signed = (text4) => SIGNED_AFTER_STOP.test(text4) || SIGNED_AT_END.test(text4);
|
|
71165
71183
|
var QUOTE_ROLE = /^(quote|testimonial|review)$/i;
|
|
71184
|
+
function isSignatureLine(line) {
|
|
71185
|
+
return SIGNING_ROLE.test(line.role ?? "") || /^\s*[—–―-]\s*[\p{L}\p{N}]/u.test(line.text);
|
|
71186
|
+
}
|
|
71166
71187
|
function isCustomersWords(line) {
|
|
71167
|
-
return
|
|
71188
|
+
return signed(line.text) || QUOTE_ROLE.test(line.role ?? "") || SIGNING_ROLE.test(line.role ?? "");
|
|
71168
71189
|
}
|
|
71169
71190
|
var quoteWords = (text4) => text4.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean);
|
|
71170
71191
|
function quotes(line, quote) {
|
|
@@ -71184,7 +71205,7 @@ function namesAuthor(line, by) {
|
|
|
71184
71205
|
function unattributedQuotes(spec, said) {
|
|
71185
71206
|
const signedApart = spec.copy.some((line) => SIGNING_ROLE.test(line.role ?? ""));
|
|
71186
71207
|
return spec.copy.flatMap((line) => {
|
|
71187
|
-
if (signedApart ||
|
|
71208
|
+
if (signedApart || signed(line.text)) return [];
|
|
71188
71209
|
const by = said.find((q) => quotes(line.text, q.quote));
|
|
71189
71210
|
if (!by || namesAuthor(line.text, by)) return [];
|
|
71190
71211
|
const name = by.author ? `"\u2014 ${by.author}"` : '"\u2014 <their first name and initial>"';
|
|
@@ -71577,7 +71598,7 @@ function backendDurableUrlFor(log = () => void 0) {
|
|
|
71577
71598
|
}
|
|
71578
71599
|
|
|
71579
71600
|
// src/commands/canvas/scene-projection.ts
|
|
71580
|
-
import { readFile as readFile13 } from "fs/promises";
|
|
71601
|
+
import { readFile as readFile13, writeFile as writeFile10 } from "fs/promises";
|
|
71581
71602
|
import path18 from "path";
|
|
71582
71603
|
|
|
71583
71604
|
// src/engine/scaffold/video/aspect.ts
|
|
@@ -72452,14 +72473,21 @@ function spokenLanguage(code) {
|
|
|
72452
72473
|
}
|
|
72453
72474
|
var NAMES2 = new Intl.DisplayNames(["en"], { type: "language", fallback: "none" });
|
|
72454
72475
|
var LETTERS = "abcdefghijklmnopqrstuvwxyz";
|
|
72455
|
-
var LANGUAGE_OPTIONS =
|
|
72456
|
-
|
|
72457
|
-
|
|
72458
|
-
|
|
72459
|
-
|
|
72460
|
-
|
|
72461
|
-
|
|
72462
|
-
);
|
|
72476
|
+
var LANGUAGE_OPTIONS = (() => {
|
|
72477
|
+
const options = {};
|
|
72478
|
+
const named = /* @__PURE__ */ new Set();
|
|
72479
|
+
for (const a of LETTERS) {
|
|
72480
|
+
for (const b of LETTERS) {
|
|
72481
|
+
const code = `${a}${b}`;
|
|
72482
|
+
const name = NAMES2.of(code);
|
|
72483
|
+
if (!name || name.toLowerCase() === code || named.has(name)) continue;
|
|
72484
|
+
if (Intl.getCanonicalLocales(code)[0] !== code) continue;
|
|
72485
|
+
named.add(name);
|
|
72486
|
+
options[code] = name;
|
|
72487
|
+
}
|
|
72488
|
+
}
|
|
72489
|
+
return options;
|
|
72490
|
+
})();
|
|
72463
72491
|
var LANGUAGE_HELD = 0.8;
|
|
72464
72492
|
function languageQuestion(lines) {
|
|
72465
72493
|
return {
|
|
@@ -74128,6 +74156,81 @@ function scaffoldVideoCanvas(input, elementsInput, requested) {
|
|
|
74128
74156
|
};
|
|
74129
74157
|
}
|
|
74130
74158
|
|
|
74159
|
+
// src/engine/nodes/local/lib/caption-groups.ts
|
|
74160
|
+
function captionWordKey(text4) {
|
|
74161
|
+
return text4.replace(/[.,;:!?…]+$/, "").toLocaleLowerCase();
|
|
74162
|
+
}
|
|
74163
|
+
var CARRIES_ON_HELD = 0.4;
|
|
74164
|
+
var CARRIES_ON_QUESTION = "Is `word`, as used in `line`, a function word that cannot close a phrase: an article, a preposition, a conjunction, a possessive or another determiner? A noun, verb, adjective, adverb, name or number is not.";
|
|
74165
|
+
function carriesOnQuestions(lines) {
|
|
74166
|
+
const seen = /* @__PURE__ */ new Set();
|
|
74167
|
+
const questions = [];
|
|
74168
|
+
for (const line of lines) {
|
|
74169
|
+
for (const text4 of line.split(/\s+/)) {
|
|
74170
|
+
const word = captionWordKey(text4);
|
|
74171
|
+
if (!word || seen.has(word)) continue;
|
|
74172
|
+
seen.add(word);
|
|
74173
|
+
questions.push({ type: "noul", instructions: { line, word, question: CARRIES_ON_QUESTION } });
|
|
74174
|
+
}
|
|
74175
|
+
}
|
|
74176
|
+
return { questions, words: [...seen] };
|
|
74177
|
+
}
|
|
74178
|
+
function carriesOnFromAnswers(words, answers) {
|
|
74179
|
+
if (!answers) return [];
|
|
74180
|
+
return words.filter((_, i) => {
|
|
74181
|
+
const answer = answers[i];
|
|
74182
|
+
return answer !== void 0 && "noul" in answer && answer.noul >= CARRIES_ON_HELD;
|
|
74183
|
+
});
|
|
74184
|
+
}
|
|
74185
|
+
|
|
74186
|
+
// src/engine/scaffold/video/speech-reading.ts
|
|
74187
|
+
var asBlueprint = (input) => input && typeof input === "object" ? input : {};
|
|
74188
|
+
var dialogueOf = (b) => (b.scenes ?? []).flatMap((s) => s.dialogue ?? []);
|
|
74189
|
+
function blueprintVoices(input) {
|
|
74190
|
+
const b = asBlueprint(input);
|
|
74191
|
+
const all = [
|
|
74192
|
+
...dialogueOf(b).map((l) => l.voice_description),
|
|
74193
|
+
...(b.global?.cast ?? []).map((c) => c.description),
|
|
74194
|
+
b.global?.voiceover?.voice_description
|
|
74195
|
+
];
|
|
74196
|
+
return [...new Set(all.filter((d) => typeof d === "string" && d.length > 0))];
|
|
74197
|
+
}
|
|
74198
|
+
async function readBlueprintSpeech(input, known, readers) {
|
|
74199
|
+
const lines = dialogueOf(asBlueprint(input)).map((l) => l.line?.trim() ?? "").filter(Boolean);
|
|
74200
|
+
const declared = known.declaredLanguage ? spokenLanguage(known.declaredLanguage)?.tag : void 0;
|
|
74201
|
+
const voices = blueprintVoices(input);
|
|
74202
|
+
const carries2 = carriesOnQuestions(lines);
|
|
74203
|
+
const language = !declared && lines.length > 0 ? [languageQuestion(lines)] : [];
|
|
74204
|
+
const [answers, genders] = await Promise.all([
|
|
74205
|
+
readers.ask([...language, ...carries2.questions]),
|
|
74206
|
+
readers.genders(voices).catch(() => null)
|
|
74207
|
+
]);
|
|
74208
|
+
const voiceGenders = genders ?? {};
|
|
74209
|
+
if (!answers)
|
|
74210
|
+
return { ...declared ? { spokenLanguage: declared } : {}, voiceGenders, captionCarriesOn: [], read: false };
|
|
74211
|
+
const read = declared ?? (language.length > 0 ? languageFromAnswer(answers[0]) : void 0);
|
|
74212
|
+
return {
|
|
74213
|
+
...read ? { spokenLanguage: read } : {},
|
|
74214
|
+
voiceGenders,
|
|
74215
|
+
captionCarriesOn: carriesOnFromAnswers(carries2.words, answers.slice(language.length)),
|
|
74216
|
+
read: true
|
|
74217
|
+
};
|
|
74218
|
+
}
|
|
74219
|
+
async function speechMissingFromRebuild(input, opts, readers) {
|
|
74220
|
+
if (opts.voiceGenders !== void 0) return null;
|
|
74221
|
+
const lines = dialogueOf(asBlueprint(input)).map((l) => l.line?.trim() ?? "").filter(Boolean);
|
|
74222
|
+
const voices = blueprintVoices(input);
|
|
74223
|
+
if (lines.length === 0 && voices.length === 0) return null;
|
|
74224
|
+
const askLanguage = !voiceLanguageOf(opts) && lines.length > 0;
|
|
74225
|
+
const [answers, genders] = await Promise.all([
|
|
74226
|
+
askLanguage ? readers.ask([languageQuestion(lines)]).catch(() => null) : Promise.resolve([]),
|
|
74227
|
+
readers.genders(voices).catch(() => null)
|
|
74228
|
+
]);
|
|
74229
|
+
if (!answers || !genders) return null;
|
|
74230
|
+
const language = askLanguage ? languageFromAnswer(answers[0]) : void 0;
|
|
74231
|
+
return { ...language ? { spokenLanguage: language } : {}, voiceGenders: genders };
|
|
74232
|
+
}
|
|
74233
|
+
|
|
74131
74234
|
// src/commands/canvas/scene-files.ts
|
|
74132
74235
|
import { mkdir as mkdir7, readFile as readFile12, readdir as readdir6, rm, writeFile as writeFile9 } from "fs/promises";
|
|
74133
74236
|
import path17 from "path";
|
|
@@ -74255,6 +74358,15 @@ function parseRebuild(raw) {
|
|
|
74255
74358
|
const { elements, opts } = JSON.parse(raw);
|
|
74256
74359
|
return { elements, ...registeredRebuildOpts(opts) };
|
|
74257
74360
|
}
|
|
74361
|
+
async function withSpeechRead(file, rebuildRaw, blueprint, opts, readers) {
|
|
74362
|
+
const read = await speechMissingFromRebuild(blueprint, opts, readers);
|
|
74363
|
+
if (!read) return opts;
|
|
74364
|
+
const stored = JSON.parse(rebuildRaw);
|
|
74365
|
+
const kept = { ...stored, opts: { ...stored.opts, ...read } };
|
|
74366
|
+
await writeFile10(file, `${JSON.stringify(kept, null, 2)}
|
|
74367
|
+
`, "utf8").catch(() => void 0);
|
|
74368
|
+
return { ...opts, ...read };
|
|
74369
|
+
}
|
|
74258
74370
|
async function sceneOwnedPrompts(canvasPath) {
|
|
74259
74371
|
const creativeDir3 = path18.dirname(canvasPath);
|
|
74260
74372
|
const blueprint = await reassembleBlueprint(creativeDir3);
|
|
@@ -74276,7 +74388,7 @@ async function sceneOwnedPrompts(canvasPath) {
|
|
|
74276
74388
|
}
|
|
74277
74389
|
return owned;
|
|
74278
74390
|
}
|
|
74279
|
-
async function syncSceneNodeParams(canvas, canvasPath, log) {
|
|
74391
|
+
async function syncSceneNodeParams(canvas, canvasPath, log, readers) {
|
|
74280
74392
|
const creativeDir3 = path18.dirname(canvasPath);
|
|
74281
74393
|
const blueprint = await reassembleBlueprint(creativeDir3);
|
|
74282
74394
|
if (!blueprint) return "not_applicable";
|
|
@@ -74289,8 +74401,10 @@ async function syncSceneNodeParams(canvas, canvasPath, log) {
|
|
|
74289
74401
|
);
|
|
74290
74402
|
return "not_applicable";
|
|
74291
74403
|
}
|
|
74292
|
-
const
|
|
74404
|
+
const parsed = parseRebuild(rebuildRaw);
|
|
74405
|
+
const { elements, replaced } = parsed;
|
|
74293
74406
|
warnReplacedModels(replaced, log);
|
|
74407
|
+
const opts = readers ? await withSpeechRead(path18.join(creativeDir3, REBUILD_FILE), rebuildRaw, blueprint, parsed.opts, readers) : parsed.opts;
|
|
74294
74408
|
const rebuilt = scaffoldVideoCanvas(blueprint, elements, opts);
|
|
74295
74409
|
const rebuiltNodes = promptNodes(rebuilt);
|
|
74296
74410
|
const liveNodes = promptNodes(canvas);
|
|
@@ -74430,6 +74544,93 @@ function toPosix(p) {
|
|
|
74430
74544
|
return p.split(path19.sep).join("/");
|
|
74431
74545
|
}
|
|
74432
74546
|
|
|
74547
|
+
// src/engine/nodes/local/lib/jev-cache.ts
|
|
74548
|
+
import { createHash as createHash7 } from "crypto";
|
|
74549
|
+
import { mkdir as mkdir8, readFile as readFile15, writeFile as writeFile11 } from "fs/promises";
|
|
74550
|
+
import path20 from "path";
|
|
74551
|
+
import { z as z71 } from "zod";
|
|
74552
|
+
var Kept = z71.array(
|
|
74553
|
+
z71.union([
|
|
74554
|
+
z71.object({ choice: z71.string(), probabilities: z71.record(z71.string(), z71.number()), confidence: z71.number() }),
|
|
74555
|
+
z71.object({ noul: z71.number() })
|
|
74556
|
+
])
|
|
74557
|
+
);
|
|
74558
|
+
function cachedJev(cacheDir, ask) {
|
|
74559
|
+
const dir = path20.join(cacheDir, "jev");
|
|
74560
|
+
return async (questions) => {
|
|
74561
|
+
const key = createHash7("sha256").update(JSON.stringify([JEV_MODEL, questions])).digest("hex");
|
|
74562
|
+
const file = path20.join(dir, `${key}.json`);
|
|
74563
|
+
const kept = Kept.safeParse(
|
|
74564
|
+
await readFile15(file, "utf8").then(JSON.parse, () => null).catch(() => null)
|
|
74565
|
+
);
|
|
74566
|
+
if (kept.success && kept.data.length === questions.length) return kept.data;
|
|
74567
|
+
const answers = await ask(questions);
|
|
74568
|
+
if (answers) {
|
|
74569
|
+
await mkdir8(dir, { recursive: true });
|
|
74570
|
+
await writeFile11(file, JSON.stringify(answers), "utf8");
|
|
74571
|
+
}
|
|
74572
|
+
return answers;
|
|
74573
|
+
};
|
|
74574
|
+
}
|
|
74575
|
+
|
|
74576
|
+
// src/engine/scaffold/video/dialogue/voice-gender.ts
|
|
74577
|
+
import { createHash as createHash8 } from "crypto";
|
|
74578
|
+
import { mkdir as mkdir9, readFile as readFile16, writeFile as writeFile12 } from "fs/promises";
|
|
74579
|
+
import path21 from "path";
|
|
74580
|
+
import { z as z72 } from "zod";
|
|
74581
|
+
var VOICE_GENDER_MODEL = "google/gemini-2.5-flash-lite";
|
|
74582
|
+
var PROMPT_VERSION2 = 1;
|
|
74583
|
+
var REQUEST2 = {
|
|
74584
|
+
provider: { order: ["google-vertex"], allow_fallbacks: false },
|
|
74585
|
+
temperature: 0,
|
|
74586
|
+
response_format: { type: "json_object" }
|
|
74587
|
+
};
|
|
74588
|
+
var TIMEOUT_MS2 = 2e4;
|
|
74589
|
+
function voiceGenderPrompt(voices) {
|
|
74590
|
+
return [
|
|
74591
|
+
"Each line below describes the voice wanted for a speaker in an ad, written in any language.",
|
|
74592
|
+
"For each, say whether it asks for a woman's voice or a man's voice. Read it in its own language: a noun that is feminine or masculine by itself says so. Answer null when it does not say.",
|
|
74593
|
+
"",
|
|
74594
|
+
...voices.map((voice, i) => `${i + 1}. ${JSON.stringify(voice)}`),
|
|
74595
|
+
"",
|
|
74596
|
+
`Reply with only this JSON: {"genders": ["female" | "male" | null, ... one per line, ${voices.length} in order]}`
|
|
74597
|
+
].join("\n");
|
|
74598
|
+
}
|
|
74599
|
+
var Answer3 = z72.object({ genders: z72.array(z72.enum(["female", "male"]).nullable()) });
|
|
74600
|
+
function parseVoiceGenders(answer, voices) {
|
|
74601
|
+
const parsed = Answer3.safeParse(answer);
|
|
74602
|
+
if (!parsed.success || parsed.data.genders.length !== voices.length) return null;
|
|
74603
|
+
const out = {};
|
|
74604
|
+
voices.forEach((voice, i) => {
|
|
74605
|
+
const gender = parsed.data.genders[i];
|
|
74606
|
+
if (gender) out[voice] = gender;
|
|
74607
|
+
});
|
|
74608
|
+
return out;
|
|
74609
|
+
}
|
|
74610
|
+
function voiceGenderModel(cacheDir, onSpend) {
|
|
74611
|
+
const dir = path21.join(cacheDir, "voice-gender");
|
|
74612
|
+
return async (voices) => {
|
|
74613
|
+
if (voices.length === 0) return {};
|
|
74614
|
+
const key = createHash8("sha256").update(JSON.stringify([PROMPT_VERSION2, VOICE_GENDER_MODEL, voices])).digest("hex");
|
|
74615
|
+
const file = path21.join(dir, `${key}.json`);
|
|
74616
|
+
const kept = parseVoiceGenders(
|
|
74617
|
+
await readFile16(file, "utf8").then(JSON.parse, () => null).catch(() => null),
|
|
74618
|
+
voices
|
|
74619
|
+
);
|
|
74620
|
+
if (kept) return kept;
|
|
74621
|
+
const answer = await askAboutText(VOICE_GENDER_MODEL, voiceGenderPrompt(voices), onSpend, {
|
|
74622
|
+
timeoutMs: TIMEOUT_MS2,
|
|
74623
|
+
request: REQUEST2
|
|
74624
|
+
}).catch(() => null);
|
|
74625
|
+
const genders = parseVoiceGenders(answer, voices);
|
|
74626
|
+
if (genders) {
|
|
74627
|
+
await mkdir9(dir, { recursive: true });
|
|
74628
|
+
await writeFile12(file, JSON.stringify(answer), "utf8");
|
|
74629
|
+
}
|
|
74630
|
+
return genders;
|
|
74631
|
+
};
|
|
74632
|
+
}
|
|
74633
|
+
|
|
74433
74634
|
// src/commands/creatives/ideas-model.ts
|
|
74434
74635
|
import { parse as parseYaml2, YAMLParseError } from "yaml";
|
|
74435
74636
|
var IDEA_ANGLES = [
|
|
@@ -74769,8 +74970,8 @@ ${ownQuotes.join("\n")}`);
|
|
|
74769
74970
|
}
|
|
74770
74971
|
|
|
74771
74972
|
// src/commands/creatives/ideas-workspace.ts
|
|
74772
|
-
import { readdir as readdir8, readFile as
|
|
74773
|
-
import
|
|
74973
|
+
import { readdir as readdir8, readFile as readFile17 } from "fs/promises";
|
|
74974
|
+
import path22 from "path";
|
|
74774
74975
|
|
|
74775
74976
|
// src/commands/creatives/ideas-judgments.ts
|
|
74776
74977
|
var STATISTIC_QUESTION = `\`quote\` was filed as something a customer said. Is it instead a statistic about a group of people, a share or a figure from a survey, a study or an article ("39% of UK homeowners say\u2026", "a quarter of buyers\u2026", "1 in 3 owners\u2026"), rather than one person's own words about their own experience? A person giving their own number ("My bill dropped 40%") is their own words. The quote may be in any language.`;
|
|
@@ -74800,26 +75001,26 @@ async function sameMessageRead(ideas, ask) {
|
|
|
74800
75001
|
}
|
|
74801
75002
|
|
|
74802
75003
|
// src/commands/creatives/ideas-workspace.ts
|
|
74803
|
-
var IDEAS_DIR =
|
|
74804
|
-
var INSIGHTS_DIR =
|
|
75004
|
+
var IDEAS_DIR = path22.join("src", "creatives", "_ideas");
|
|
75005
|
+
var INSIGHTS_DIR = path22.join("src", "content", "insights");
|
|
74805
75006
|
async function markdownFiles(dir, recursive = false) {
|
|
74806
75007
|
const entries = await readdir8(dir, { recursive }).catch(() => []);
|
|
74807
75008
|
return entries.filter((f) => f.endsWith(".md")).sort();
|
|
74808
75009
|
}
|
|
74809
75010
|
var idOf2 = (file) => file.replace(/\\/g, "/").replace(/\.md$/, "");
|
|
74810
75011
|
function workspaceIdeasJudge(root) {
|
|
74811
|
-
const cacheDir =
|
|
75012
|
+
const cacheDir = path22.join(root, "canvas", ".cache");
|
|
74812
75013
|
const spent = [];
|
|
74813
75014
|
return {
|
|
74814
|
-
ask: rememberedJev(
|
|
75015
|
+
ask: rememberedJev(path22.join(cacheDir, "ideas-answers.json"), (u) => spent.push(u)),
|
|
74815
75016
|
done: () => holdUnbilledSpend(cacheDir, spent.splice(0)).catch(() => void 0)
|
|
74816
75017
|
};
|
|
74817
75018
|
}
|
|
74818
75019
|
async function readInsights(root, opts = {}) {
|
|
74819
|
-
const dir =
|
|
75020
|
+
const dir = path22.join(root, INSIGHTS_DIR);
|
|
74820
75021
|
const insights = [];
|
|
74821
75022
|
for (const file of await markdownFiles(dir, true)) {
|
|
74822
|
-
const insight = parseInsight(idOf2(file), await
|
|
75023
|
+
const insight = parseInsight(idOf2(file), await readFile17(path22.join(dir, file), "utf8").catch(() => ""));
|
|
74823
75024
|
if (insight) insights.push(insight);
|
|
74824
75025
|
}
|
|
74825
75026
|
if (opts.ask) return withStatisticsRead(insights, opts.ask);
|
|
@@ -74829,41 +75030,41 @@ async function readInsights(root, opts = {}) {
|
|
|
74829
75030
|
return read;
|
|
74830
75031
|
}
|
|
74831
75032
|
async function readIdeas(root) {
|
|
74832
|
-
const dir =
|
|
75033
|
+
const dir = path22.join(root, IDEAS_DIR);
|
|
74833
75034
|
const ideas = [];
|
|
74834
75035
|
const unreadable = [];
|
|
74835
75036
|
for (const file of await markdownFiles(dir)) {
|
|
74836
75037
|
const id = idOf2(file);
|
|
74837
|
-
const parsed = parseIdea(id, await
|
|
75038
|
+
const parsed = parseIdea(id, await readFile17(path22.join(dir, file), "utf8").catch(() => ""));
|
|
74838
75039
|
if (parsed.idea) ideas.push(parsed.idea);
|
|
74839
75040
|
else unreadable.push({ id, problems: parsed.problems });
|
|
74840
75041
|
}
|
|
74841
75042
|
return { ideas, unreadable };
|
|
74842
75043
|
}
|
|
74843
75044
|
async function readPersonaIds(root) {
|
|
74844
|
-
return (await markdownFiles(
|
|
75045
|
+
return (await markdownFiles(path22.join(root, "src", "content", "personas"), true)).map(idOf2);
|
|
74845
75046
|
}
|
|
74846
75047
|
var MAX_KNOWLEDGE_CHARS = 2e5;
|
|
74847
75048
|
async function readKnowledgeCorpus(root) {
|
|
74848
75049
|
const parts = [];
|
|
74849
|
-
const company = await
|
|
75050
|
+
const company = await readFile17(path22.join(root, "COMPANY.md"), "utf8").catch(() => "");
|
|
74850
75051
|
if (company) parts.push(company);
|
|
74851
|
-
const content =
|
|
74852
|
-
const insights =
|
|
75052
|
+
const content = path22.join(root, "src", "content");
|
|
75053
|
+
const insights = path22.relative(content, path22.join(root, INSIGHTS_DIR));
|
|
74853
75054
|
const files = (await markdownFiles(content, true)).filter((f) => !f.replace(/\\/g, "/").startsWith(`${insights}/`));
|
|
74854
75055
|
for (const file of files) {
|
|
74855
|
-
parts.push(await
|
|
75056
|
+
parts.push(await readFile17(path22.join(content, file), "utf8").catch(() => ""));
|
|
74856
75057
|
if (parts.join("").length > MAX_KNOWLEDGE_CHARS) break;
|
|
74857
75058
|
}
|
|
74858
75059
|
return parts.join("\n").slice(0, MAX_KNOWLEDGE_CHARS);
|
|
74859
75060
|
}
|
|
74860
75061
|
async function readCreativeRecords(root) {
|
|
74861
|
-
const dir =
|
|
75062
|
+
const dir = path22.join(root, "src", "creatives");
|
|
74862
75063
|
const entries = await readdir8(dir, { withFileTypes: true }).catch(() => []);
|
|
74863
75064
|
const records = [];
|
|
74864
75065
|
for (const entry of entries) {
|
|
74865
75066
|
if (!entry.isDirectory() || entry.name.startsWith("_")) continue;
|
|
74866
|
-
const raw = await
|
|
75067
|
+
const raw = await readFile17(path22.join(dir, entry.name, "_definition.md"), "utf8").catch(() => null);
|
|
74867
75068
|
if (raw === null) continue;
|
|
74868
75069
|
const fm = frontmatterOf(raw) ?? {};
|
|
74869
75070
|
const pick2 = (key) => typeof fm[key] === "string" && fm[key] ? { [key]: fm[key] } : {};
|
|
@@ -75101,7 +75302,7 @@ function upgradedDescription(text4, old, plan) {
|
|
|
75101
75302
|
}
|
|
75102
75303
|
|
|
75103
75304
|
// src/commands/canvas/style-projection.ts
|
|
75104
|
-
import { readFile as
|
|
75305
|
+
import { readFile as readFile18, writeFile as writeFile13 } from "fs/promises";
|
|
75105
75306
|
function findBlueprintProjection(canvas) {
|
|
75106
75307
|
if (!canvas || typeof canvas !== "object") return null;
|
|
75107
75308
|
const nodes = canvas.nodes;
|
|
@@ -75129,10 +75330,10 @@ function renderStyleProjectionFromValue(blueprint) {
|
|
|
75129
75330
|
async function syncStyleProjection(canvas, log) {
|
|
75130
75331
|
const pair = findBlueprintProjection(canvas);
|
|
75131
75332
|
if (!pair) return "not_applicable";
|
|
75132
|
-
const rendered = renderStyleProjection(await
|
|
75133
|
-
const current = await
|
|
75333
|
+
const rendered = renderStyleProjection(await readFile18(pair.promptPath, "utf8"));
|
|
75334
|
+
const current = await readFile18(pair.stylePath, "utf8").catch(() => null);
|
|
75134
75335
|
if (current === rendered) return "up_to_date";
|
|
75135
|
-
await
|
|
75336
|
+
await writeFile13(pair.stylePath, rendered, "utf8");
|
|
75136
75337
|
log(
|
|
75137
75338
|
"[style] prompt.style.json regenerated from prompt.json \u2014 every frame's shared ad spec changed; affected image frames will re-bill on the next run"
|
|
75138
75339
|
);
|
|
@@ -75166,7 +75367,7 @@ async function staticSpecGate(canvas, canvasDir) {
|
|
|
75166
75367
|
const parts = await readRecordParts(root);
|
|
75167
75368
|
const records = parts.map((p) => p.text).join("\n");
|
|
75168
75369
|
const ours = (await readInsights(root)).filter(sourcesClaims);
|
|
75169
|
-
const cacheDir =
|
|
75370
|
+
const cacheDir = path23.join(findWorkspaceRoot(canvasDir) ?? root, "canvas", ".cache");
|
|
75170
75371
|
const spent = [];
|
|
75171
75372
|
const claims = spec.native ? { problems: [], unheld: [] } : await claimProblems(
|
|
75172
75373
|
spec.copy.map((l) => l.text),
|
|
@@ -75217,14 +75418,14 @@ function oneRefusal(gates) {
|
|
|
75217
75418
|
}
|
|
75218
75419
|
async function readRecordParts(root) {
|
|
75219
75420
|
const parts = [
|
|
75220
|
-
{ source: "COMPANY.md", text: await
|
|
75421
|
+
{ source: "COMPANY.md", text: await readFile19(path23.join(root, "COMPANY.md"), "utf8").catch(() => "") }
|
|
75221
75422
|
];
|
|
75222
|
-
const content =
|
|
75423
|
+
const content = path23.join(root, "src", "content");
|
|
75223
75424
|
const files = (await readdir9(content, { recursive: true }).catch(() => [])).filter(
|
|
75224
75425
|
(f) => /\.(md|mdx|json|ya?ml)$/.test(f) && !f.replace(/\\/g, "/").startsWith("insights/")
|
|
75225
75426
|
);
|
|
75226
75427
|
for (const file of files.slice(0, 400)) {
|
|
75227
|
-
parts.push({ source: file, text: await
|
|
75428
|
+
parts.push({ source: file, text: await readFile19(path23.join(content, file), "utf8").catch(() => "") });
|
|
75228
75429
|
}
|
|
75229
75430
|
const quotes2 = (await readInsights(root)).filter(sourcesClaims).map((insight) => insight.quote);
|
|
75230
75431
|
if (quotes2.length > 0) parts.push({ source: "customers' words", text: quotes2.join("\n") });
|
|
@@ -75250,14 +75451,14 @@ var CanvasPrepareError = class extends Error {
|
|
|
75250
75451
|
}
|
|
75251
75452
|
};
|
|
75252
75453
|
async function prepareCanvas(canvasPath, opts = {}) {
|
|
75253
|
-
const filePath =
|
|
75254
|
-
const canvasDir =
|
|
75454
|
+
const filePath = path23.resolve(canvasPath);
|
|
75455
|
+
const canvasDir = path23.dirname(filePath);
|
|
75255
75456
|
const notes = [];
|
|
75256
75457
|
const log = (line) => {
|
|
75257
75458
|
notes.push(line);
|
|
75258
75459
|
opts.log?.(line);
|
|
75259
75460
|
};
|
|
75260
|
-
const raw = await
|
|
75461
|
+
const raw = await readFile19(filePath, "utf8");
|
|
75261
75462
|
let parsed;
|
|
75262
75463
|
try {
|
|
75263
75464
|
parsed = JSON.parse(raw);
|
|
@@ -75276,7 +75477,7 @@ async function prepareCanvas(canvasPath, opts = {}) {
|
|
|
75276
75477
|
onDisk = promoted.canvas;
|
|
75277
75478
|
text4 = `${JSON.stringify(onDisk, null, 2)}
|
|
75278
75479
|
`;
|
|
75279
|
-
await
|
|
75480
|
+
await writeFile14(filePath, text4, "utf8");
|
|
75280
75481
|
rewrites.push(...promoted.rewrites);
|
|
75281
75482
|
}
|
|
75282
75483
|
}
|
|
@@ -75289,7 +75490,7 @@ async function prepareCanvas(canvasPath, opts = {}) {
|
|
|
75289
75490
|
let staticCopy;
|
|
75290
75491
|
try {
|
|
75291
75492
|
if (write) style = await syncStyleProjection(canvas, log);
|
|
75292
|
-
const cacheDir =
|
|
75493
|
+
const cacheDir = path23.join(findWorkspaceRoot(canvasDir) ?? canvasDir, "canvas", ".cache");
|
|
75293
75494
|
const writing = [];
|
|
75294
75495
|
const native = await syncNativeGraph(canvas, log, interfaceWordsWriter(cacheDir, (u) => writing.push(u)));
|
|
75295
75496
|
await holdUnbilledSpend(cacheDir, writing);
|
|
@@ -75300,19 +75501,26 @@ async function prepareCanvas(canvasPath, opts = {}) {
|
|
|
75300
75501
|
} catch (e) {
|
|
75301
75502
|
throw new CanvasPrepareError("style_projection", `prompt.json is not readable or not a valid ad:
|
|
75302
75503
|
${adSpecErrorText(e)}`, {
|
|
75303
|
-
fix: `Fix ${
|
|
75504
|
+
fix: `Fix ${path23.join(canvasDir, "prompt.json")} so it parses as JSON (and, for a static ad, matches the ad spec the message names), then re-run.`
|
|
75304
75505
|
});
|
|
75305
75506
|
}
|
|
75306
75507
|
const gate = opts.gate && isStaticBriefCanvas(canvas) ? await staticSpecGate(canvas, canvasDir) : null;
|
|
75307
75508
|
if (gate) throw new CanvasPrepareError(gate.code, gate.problems.join(" "), { fix: gate.fix });
|
|
75308
75509
|
let scenes;
|
|
75309
75510
|
try {
|
|
75310
|
-
|
|
75511
|
+
const speechCache = path23.join(findWorkspaceRoot(canvasDir) ?? canvasDir, "canvas", ".cache");
|
|
75512
|
+
const speechSpent = [];
|
|
75513
|
+
const readers = write ? {
|
|
75514
|
+
ask: cachedJev(speechCache, (questions) => askJevQuestions(questions, (u) => speechSpent.push(u))),
|
|
75515
|
+
genders: voiceGenderModel(speechCache, (u) => speechSpent.push(u))
|
|
75516
|
+
} : void 0;
|
|
75517
|
+
scenes = await syncSceneNodeParams(canvas, filePath, log, readers);
|
|
75518
|
+
await holdUnbilledSpend(speechCache, speechSpent).catch(() => void 0);
|
|
75311
75519
|
} catch (e) {
|
|
75312
75520
|
throw new CanvasPrepareError(
|
|
75313
75521
|
"scene_projection",
|
|
75314
75522
|
`a scenes/*.json file is not readable/valid or its rebuild failed: ${errorMessage(e)}`,
|
|
75315
|
-
{ fix: `Fix the scene files in ${
|
|
75523
|
+
{ fix: `Fix the scene files in ${path23.join(canvasDir, "scenes")} so each parses as JSON, then re-run.` }
|
|
75316
75524
|
);
|
|
75317
75525
|
}
|
|
75318
75526
|
const projected = withFilePaths(canvas, onDisk, canvasDir);
|
|
@@ -75320,7 +75528,7 @@ ${adSpecErrorText(e)}`, {
|
|
|
75320
75528
|
if (write && projected !== null && JSON.stringify(projected) !== JSON.stringify(onDisk)) {
|
|
75321
75529
|
text4 = `${JSON.stringify(projected, null, 2)}
|
|
75322
75530
|
`;
|
|
75323
|
-
await
|
|
75531
|
+
await writeFile14(filePath, text4, "utf8");
|
|
75324
75532
|
written = true;
|
|
75325
75533
|
}
|
|
75326
75534
|
if (opts.requireSuppliedAssets) {
|
|
@@ -75348,7 +75556,7 @@ ${adSpecErrorText(e)}`, {
|
|
|
75348
75556
|
};
|
|
75349
75557
|
}
|
|
75350
75558
|
function healInMemory(filePath, canvas) {
|
|
75351
|
-
const canvasDir =
|
|
75559
|
+
const canvasDir = path23.dirname(filePath);
|
|
75352
75560
|
const workspaceRoot = findWorkspaceRoot(canvasDir);
|
|
75353
75561
|
if (!workspaceRoot) return { canvas, rewrites: [], text: null };
|
|
75354
75562
|
return { ...normalizeCanvasPaths(canvas, canvasDir, workspaceRoot), text: null };
|
|
@@ -75369,10 +75577,10 @@ function withFilePaths(projected, onDisk, canvasDir) {
|
|
|
75369
75577
|
if (!key || !node.params || typeof node.params[key] !== "string") continue;
|
|
75370
75578
|
const value2 = node.params[key];
|
|
75371
75579
|
const original = byId.get(node.id)?.params?.[key];
|
|
75372
|
-
if (typeof original === "string" &&
|
|
75580
|
+
if (typeof original === "string" && path23.resolve(canvasDir, original) === path23.resolve(canvasDir, value2)) {
|
|
75373
75581
|
node.params[key] = original;
|
|
75374
|
-
} else if (
|
|
75375
|
-
const rel =
|
|
75582
|
+
} else if (path23.isAbsolute(value2)) {
|
|
75583
|
+
const rel = path23.relative(canvasDir, value2).split(path23.sep).join("/");
|
|
75376
75584
|
node.params[key] = rel.startsWith(".") ? rel : `./${rel}`;
|
|
75377
75585
|
}
|
|
75378
75586
|
}
|
|
@@ -75396,7 +75604,7 @@ var critiqueCommand = defineCommand138({
|
|
|
75396
75604
|
},
|
|
75397
75605
|
args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
|
|
75398
75606
|
async run({ args: args8 }) {
|
|
75399
|
-
const filePath =
|
|
75607
|
+
const filePath = path24.resolve(String(args8.file));
|
|
75400
75608
|
let prepared;
|
|
75401
75609
|
try {
|
|
75402
75610
|
prepared = await prepareCanvas(filePath, { log: (line) => process.stderr.write(`${line}
|
|
@@ -75424,8 +75632,8 @@ var critiqueCommand = defineCommand138({
|
|
|
75424
75632
|
|
|
75425
75633
|
// src/commands/canvas/inspect.ts
|
|
75426
75634
|
import { execFile } from "child_process";
|
|
75427
|
-
import { readdir as readdir10, readFile as
|
|
75428
|
-
import
|
|
75635
|
+
import { readdir as readdir10, readFile as readFile20, stat as stat2 } from "fs/promises";
|
|
75636
|
+
import path25 from "path";
|
|
75429
75637
|
import { promisify } from "util";
|
|
75430
75638
|
import { defineCommand as defineCommand139 } from "citty";
|
|
75431
75639
|
|
|
@@ -75457,12 +75665,12 @@ var inspectCommand = defineCommand139({
|
|
|
75457
75665
|
},
|
|
75458
75666
|
async run({ args: args8 }) {
|
|
75459
75667
|
const cwd = process.cwd();
|
|
75460
|
-
const outputsDir = args8["outputs-dir"] ?
|
|
75668
|
+
const outputsDir = args8["outputs-dir"] ? path25.resolve(String(args8["outputs-dir"])) : path25.join(findWorkspaceRoot(cwd) ?? cwd, "canvas");
|
|
75461
75669
|
const runArg = String(args8.run);
|
|
75462
75670
|
const runDir = await localRunDir(runArg, outputsDir);
|
|
75463
75671
|
if (!runDir) {
|
|
75464
|
-
if (
|
|
75465
|
-
await inspectFromHistory(runArg,
|
|
75672
|
+
if (path25.isAbsolute(runArg)) fail3("not_found", `${runArg} is not a run directory`);
|
|
75673
|
+
await inspectFromHistory(runArg, path25.join(outputsDir, runArg));
|
|
75466
75674
|
return;
|
|
75467
75675
|
}
|
|
75468
75676
|
const manifest = await loadManifest(runDir);
|
|
@@ -75475,7 +75683,7 @@ var inspectCommand = defineCommand139({
|
|
|
75475
75683
|
const summary = {
|
|
75476
75684
|
ok: true,
|
|
75477
75685
|
source: "local",
|
|
75478
|
-
run_id: manifest.run_id ??
|
|
75686
|
+
run_id: manifest.run_id ?? path25.basename(runDir),
|
|
75479
75687
|
run_dir: runDir,
|
|
75480
75688
|
stats: manifest.stats ?? null,
|
|
75481
75689
|
output: manifest.output ?? null,
|
|
@@ -75488,7 +75696,7 @@ var inspectCommand = defineCommand139({
|
|
|
75488
75696
|
}
|
|
75489
75697
|
});
|
|
75490
75698
|
async function localRunDir(run3, outputsDir) {
|
|
75491
|
-
const candidate =
|
|
75699
|
+
const candidate = path25.isAbsolute(run3) ? run3 : path25.join(outputsDir, run3);
|
|
75492
75700
|
const s = await stat2(candidate).catch(() => null);
|
|
75493
75701
|
return s?.isDirectory() ? candidate : null;
|
|
75494
75702
|
}
|
|
@@ -75543,9 +75751,9 @@ function fail3(code, message) {
|
|
|
75543
75751
|
process.exit(2);
|
|
75544
75752
|
}
|
|
75545
75753
|
async function loadManifest(runDir) {
|
|
75546
|
-
const manifestPath =
|
|
75754
|
+
const manifestPath = path25.join(runDir, "manifest.json");
|
|
75547
75755
|
try {
|
|
75548
|
-
const raw = await
|
|
75756
|
+
const raw = await readFile20(manifestPath, "utf-8");
|
|
75549
75757
|
return JSON.parse(raw);
|
|
75550
75758
|
} catch {
|
|
75551
75759
|
return {};
|
|
@@ -75555,7 +75763,7 @@ async function listRunFiles(runDir) {
|
|
|
75555
75763
|
const out = [];
|
|
75556
75764
|
const names = await readdir10(runDir);
|
|
75557
75765
|
for (const name of names) {
|
|
75558
|
-
const abs =
|
|
75766
|
+
const abs = path25.join(runDir, name);
|
|
75559
75767
|
const s = await stat2(abs).catch(() => null);
|
|
75560
75768
|
if (!s?.isFile()) continue;
|
|
75561
75769
|
out.push({ name, path: abs, size: s.size });
|
|
@@ -75603,18 +75811,18 @@ async function probeDuration(filePath) {
|
|
|
75603
75811
|
import { defineCommand as defineCommand143 } from "citty";
|
|
75604
75812
|
|
|
75605
75813
|
// src/commands/canvas/run.ts
|
|
75606
|
-
import
|
|
75814
|
+
import path41 from "path";
|
|
75607
75815
|
import { defineCommand as defineCommand142 } from "citty";
|
|
75608
75816
|
|
|
75609
75817
|
// src/commands/canvas/definition-status.ts
|
|
75610
|
-
import { readFile as
|
|
75611
|
-
import
|
|
75818
|
+
import { readFile as readFile21, writeFile as writeFile15 } from "fs/promises";
|
|
75819
|
+
import path26 from "path";
|
|
75612
75820
|
import { parseDocument } from "yaml";
|
|
75613
75821
|
var ORDER = { draft: 0, rendered: 1, published: 2 };
|
|
75614
75822
|
async function advanceDefinitionStatus(definitionPath, to) {
|
|
75615
75823
|
let raw;
|
|
75616
75824
|
try {
|
|
75617
|
-
raw = await
|
|
75825
|
+
raw = await readFile21(definitionPath, "utf8");
|
|
75618
75826
|
} catch {
|
|
75619
75827
|
return "missing";
|
|
75620
75828
|
}
|
|
@@ -75632,17 +75840,17 @@ async function advanceDefinitionStatus(definitionPath, to) {
|
|
|
75632
75840
|
const nextFrontmatter = statusLine.test(frontmatter) ? frontmatter.replace(statusLine, `status: ${to}`) : `${frontmatter}status: ${to}
|
|
75633
75841
|
`;
|
|
75634
75842
|
if (nextFrontmatter === frontmatter) return "unchanged";
|
|
75635
|
-
await
|
|
75843
|
+
await writeFile15(definitionPath, `---
|
|
75636
75844
|
${nextFrontmatter}---${raw.slice(match[0].length)}`, "utf8");
|
|
75637
75845
|
return "updated";
|
|
75638
75846
|
}
|
|
75639
75847
|
function definitionPathFor(workspaceRoot, slug) {
|
|
75640
|
-
return
|
|
75848
|
+
return path26.join(workspaceRoot, "src", "creatives", slug, "_definition.md");
|
|
75641
75849
|
}
|
|
75642
75850
|
|
|
75643
75851
|
// src/commands/canvas/output-ratio.ts
|
|
75644
|
-
import { readFile as
|
|
75645
|
-
import
|
|
75852
|
+
import { readFile as readFile22 } from "fs/promises";
|
|
75853
|
+
import path27 from "path";
|
|
75646
75854
|
function definitionFormats2(markdown) {
|
|
75647
75855
|
const front = markdown.match(/^---\n([\s\S]*?)\n---/)?.[1];
|
|
75648
75856
|
if (!front) return [];
|
|
@@ -75717,21 +75925,21 @@ function outputRatioWarnings(declared, produced) {
|
|
|
75717
75925
|
});
|
|
75718
75926
|
}
|
|
75719
75927
|
async function outputRatioWarningsFor(canvas, canvasPath) {
|
|
75720
|
-
const definition = await
|
|
75928
|
+
const definition = await readFile22(path27.join(path27.dirname(canvasPath), "_definition.md"), "utf8").catch(() => null);
|
|
75721
75929
|
if (!definition) return [];
|
|
75722
75930
|
return outputRatioWarnings(definitionFormats2(definition), await producedRatios(canvas));
|
|
75723
75931
|
}
|
|
75724
75932
|
|
|
75725
75933
|
// src/commands/canvas/render-state.ts
|
|
75726
|
-
import { mkdir as
|
|
75727
|
-
import
|
|
75934
|
+
import { mkdir as mkdir10, readdir as readdir11, readFile as readFile23, rm as rm2, writeFile as writeFile16 } from "fs/promises";
|
|
75935
|
+
import path28 from "path";
|
|
75728
75936
|
var RENDER_STATE_DIR = ".rendered";
|
|
75729
75937
|
function statePath(canvasRoot, slug) {
|
|
75730
|
-
return
|
|
75938
|
+
return path28.join(canvasRoot, RENDER_STATE_DIR, `${slug}.json`);
|
|
75731
75939
|
}
|
|
75732
75940
|
async function readRenderState(canvasRoot, slug) {
|
|
75733
75941
|
try {
|
|
75734
|
-
const parsed = JSON.parse(await
|
|
75942
|
+
const parsed = JSON.parse(await readFile23(statePath(canvasRoot, slug), "utf8"));
|
|
75735
75943
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
75736
75944
|
} catch {
|
|
75737
75945
|
return {};
|
|
@@ -75743,8 +75951,8 @@ async function latestVersion(canvasRoot, slug) {
|
|
|
75743
75951
|
async function updateRenderState(canvasRoot, slug, patch) {
|
|
75744
75952
|
try {
|
|
75745
75953
|
const next = { ...await readRenderState(canvasRoot, slug), ...patch };
|
|
75746
|
-
await
|
|
75747
|
-
await
|
|
75954
|
+
await mkdir10(path28.join(canvasRoot, RENDER_STATE_DIR), { recursive: true });
|
|
75955
|
+
await writeFile16(statePath(canvasRoot, slug), `${JSON.stringify(next, null, 2)}
|
|
75748
75956
|
`, "utf8");
|
|
75749
75957
|
} catch {
|
|
75750
75958
|
}
|
|
@@ -75775,8 +75983,8 @@ function isRenderStale(state, currentSourceSha) {
|
|
|
75775
75983
|
return state.attempted?.sourceSha !== currentSourceSha;
|
|
75776
75984
|
}
|
|
75777
75985
|
async function staleCreatives(workspaceRoot) {
|
|
75778
|
-
const creativesDir =
|
|
75779
|
-
const canvasRoot =
|
|
75986
|
+
const creativesDir = path28.join(workspaceRoot, "src", "creatives");
|
|
75987
|
+
const canvasRoot = path28.join(workspaceRoot, "canvas");
|
|
75780
75988
|
let slugs;
|
|
75781
75989
|
try {
|
|
75782
75990
|
slugs = (await readdir11(creativesDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -75787,7 +75995,7 @@ async function staleCreatives(workspaceRoot) {
|
|
|
75787
75995
|
for (const slug of slugs.sort()) {
|
|
75788
75996
|
const state = await readRenderState(canvasRoot, slug);
|
|
75789
75997
|
if (!state.rendered) continue;
|
|
75790
|
-
const canvasPath =
|
|
75998
|
+
const canvasPath = path28.join(creativesDir, slug, `${slug}.canvas.json`);
|
|
75791
75999
|
let prepared;
|
|
75792
76000
|
try {
|
|
75793
76001
|
prepared = await prepareCanvas(canvasPath, { write: false });
|
|
@@ -75799,7 +76007,7 @@ async function staleCreatives(workspaceRoot) {
|
|
|
75799
76007
|
if (!current || !isRenderStale(state, current)) continue;
|
|
75800
76008
|
stale.push({
|
|
75801
76009
|
slug,
|
|
75802
|
-
canvasPath:
|
|
76010
|
+
canvasPath: path28.relative(workspaceRoot, canvasPath).split(path28.sep).join("/"),
|
|
75803
76011
|
renderedRunId: state.rendered.runId,
|
|
75804
76012
|
sourceSha: current
|
|
75805
76013
|
});
|
|
@@ -75807,7 +76015,7 @@ async function staleCreatives(workspaceRoot) {
|
|
|
75807
76015
|
return stale;
|
|
75808
76016
|
}
|
|
75809
76017
|
async function clearLegacyDirtyMarker(canvasRoot, slug) {
|
|
75810
|
-
await rm2(
|
|
76018
|
+
await rm2(path28.join(canvasRoot, ".dirty", `${slug}.json`), { force: true }).catch(() => void 0);
|
|
75811
76019
|
}
|
|
75812
76020
|
|
|
75813
76021
|
// src/commands/canvas/node-preview.ts
|
|
@@ -75863,13 +76071,13 @@ ${body}` : header || body || compactJson(record);
|
|
|
75863
76071
|
}
|
|
75864
76072
|
|
|
75865
76073
|
// src/commands/canvas/run-record.ts
|
|
75866
|
-
import
|
|
76074
|
+
import path29 from "path";
|
|
75867
76075
|
var MAX_RUN_NODES = 200;
|
|
75868
76076
|
var MAX_OUTPUTS_PER_NODE = 10;
|
|
75869
76077
|
var MAX_FINAL_OUTPUTS = 10;
|
|
75870
76078
|
var MAX_CREATIVE_SLUG_LENGTH = 100;
|
|
75871
76079
|
function creativeSlugFromCanvasPath(filePath) {
|
|
75872
|
-
const normalized2 = filePath.split(
|
|
76080
|
+
const normalized2 = filePath.split(path29.sep).join("/");
|
|
75873
76081
|
const match = normalized2.match(/(?:^|\/)src\/creatives\/([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\//);
|
|
75874
76082
|
const slug = match?.[1] ?? null;
|
|
75875
76083
|
return slug && slug.length <= MAX_CREATIVE_SLUG_LENGTH ? slug : null;
|
|
@@ -76178,7 +76386,7 @@ var RunRecordPoster = class {
|
|
|
76178
76386
|
|
|
76179
76387
|
// src/commands/canvas/run-retention.ts
|
|
76180
76388
|
import { rm as rm3 } from "fs/promises";
|
|
76181
|
-
import
|
|
76389
|
+
import path30 from "path";
|
|
76182
76390
|
function runDirsToPrune(entries, keep, currentRunId) {
|
|
76183
76391
|
const runs = entries.filter((e) => /^r_[0-9A-Za-z]+$/.test(e) && e !== currentRunId).sort();
|
|
76184
76392
|
if (keep <= 0) return runs;
|
|
@@ -76195,7 +76403,7 @@ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
|
|
|
76195
76403
|
const toPrune = runDirsToPrune(entries, keep, currentRunId);
|
|
76196
76404
|
if (toPrune.length === 0) return;
|
|
76197
76405
|
for (const dir of toPrune) {
|
|
76198
|
-
await rm3(
|
|
76406
|
+
await rm3(path30.join(outputsDir, dir), { recursive: true, force: true }).catch(
|
|
76199
76407
|
(e) => log(`[prune ] could not remove ${dir}: ${e.message}`)
|
|
76200
76408
|
);
|
|
76201
76409
|
}
|
|
@@ -76205,15 +76413,15 @@ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
|
|
|
76205
76413
|
// src/commands/canvas/run-resume.ts
|
|
76206
76414
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
76207
76415
|
import { rmSync } from "fs";
|
|
76208
|
-
import { mkdir as
|
|
76209
|
-
import
|
|
76416
|
+
import { mkdir as mkdir11, readFile as readFile24, rm as rm4, writeFile as writeFile17 } from "fs/promises";
|
|
76417
|
+
import path31 from "path";
|
|
76210
76418
|
function markerKey(canvasPath) {
|
|
76211
76419
|
const slug = creativeSlugFromCanvasPath(canvasPath);
|
|
76212
|
-
const identity = slug ??
|
|
76420
|
+
const identity = slug ?? path31.relative(process.cwd(), path31.resolve(canvasPath));
|
|
76213
76421
|
return sha256Hex(Buffer.from(identity)).slice(0, 32);
|
|
76214
76422
|
}
|
|
76215
76423
|
function markerFile(outputsDir, canvasPath) {
|
|
76216
|
-
return
|
|
76424
|
+
return path31.join(outputsDir, ".inflight", `${markerKey(canvasPath)}.json`);
|
|
76217
76425
|
}
|
|
76218
76426
|
function classifyRemoteRun(run3) {
|
|
76219
76427
|
if (run3.interrupted === true || run3.status === "awaiting_approval") return "adopt";
|
|
@@ -76266,7 +76474,7 @@ async function resolveRunId(opts) {
|
|
|
76266
76474
|
}
|
|
76267
76475
|
async function readMarker(outputsDir, canvasPath) {
|
|
76268
76476
|
try {
|
|
76269
|
-
const parsed = JSON.parse(await
|
|
76477
|
+
const parsed = JSON.parse(await readFile24(markerFile(outputsDir, canvasPath), "utf8"));
|
|
76270
76478
|
if (typeof parsed.runId !== "string" || parsed.runId.length === 0) return null;
|
|
76271
76479
|
const num4 = (value2) => typeof value2 === "number" && Number.isFinite(value2) ? value2 : void 0;
|
|
76272
76480
|
return {
|
|
@@ -76285,20 +76493,20 @@ async function markRunInFlight(outputsDir, canvasPath, runId, sourceSha) {
|
|
|
76285
76493
|
const file = markerFile(outputsDir, canvasPath);
|
|
76286
76494
|
const token = randomUUID2();
|
|
76287
76495
|
const startedAt = Date.now();
|
|
76288
|
-
const write = (heartbeatAt) =>
|
|
76496
|
+
const write = (heartbeatAt) => writeFile17(
|
|
76289
76497
|
file,
|
|
76290
76498
|
JSON.stringify({
|
|
76291
76499
|
runId,
|
|
76292
76500
|
pid: process.pid,
|
|
76293
76501
|
token,
|
|
76294
|
-
canvasPath:
|
|
76502
|
+
canvasPath: path31.resolve(canvasPath),
|
|
76295
76503
|
startedAt,
|
|
76296
76504
|
heartbeatAt,
|
|
76297
76505
|
...sourceSha ? { sourceSha } : {}
|
|
76298
76506
|
})
|
|
76299
76507
|
);
|
|
76300
76508
|
try {
|
|
76301
|
-
await
|
|
76509
|
+
await mkdir11(path31.dirname(file), { recursive: true });
|
|
76302
76510
|
await write(startedAt);
|
|
76303
76511
|
} catch {
|
|
76304
76512
|
}
|
|
@@ -76309,13 +76517,13 @@ async function markRunInFlight(outputsDir, canvasPath, runId, sourceSha) {
|
|
|
76309
76517
|
return { token, stop: () => clearInterval(timer) };
|
|
76310
76518
|
}
|
|
76311
76519
|
function busyFile(outputsDir, canvasPath) {
|
|
76312
|
-
return
|
|
76520
|
+
return path31.join(outputsDir, ".inflight", `${markerKey(canvasPath)}.busy.json`);
|
|
76313
76521
|
}
|
|
76314
76522
|
async function markCanvasBusy(outputsDir, canvasPath, runId, pid = process.pid) {
|
|
76315
76523
|
const file = busyFile(outputsDir, canvasPath);
|
|
76316
|
-
const write = () =>
|
|
76524
|
+
const write = () => writeFile17(file, JSON.stringify({ runId, pid, heartbeatAt: Date.now() }));
|
|
76317
76525
|
try {
|
|
76318
|
-
await
|
|
76526
|
+
await mkdir11(path31.dirname(file), { recursive: true });
|
|
76319
76527
|
await write();
|
|
76320
76528
|
} catch {
|
|
76321
76529
|
}
|
|
@@ -76335,7 +76543,7 @@ async function markCanvasBusy(outputsDir, canvasPath, runId, pid = process.pid)
|
|
|
76335
76543
|
}
|
|
76336
76544
|
async function canvasBusyElsewhere(outputsDir, canvasPath, now = Date.now()) {
|
|
76337
76545
|
try {
|
|
76338
|
-
const held = JSON.parse(await
|
|
76546
|
+
const held = JSON.parse(await readFile24(busyFile(outputsDir, canvasPath), "utf8"));
|
|
76339
76547
|
if (typeof held.runId !== "string" || held.pid === void 0 || held.pid === process.pid) return null;
|
|
76340
76548
|
return isLiveRender(held, now) ? { runId: held.runId, pid: held.pid } : null;
|
|
76341
76549
|
} catch {
|
|
@@ -76355,8 +76563,8 @@ async function clearRunMarker(outputsDir, canvasPath, token) {
|
|
|
76355
76563
|
|
|
76356
76564
|
// src/commands/canvas/run-snapshot.ts
|
|
76357
76565
|
import { existsSync as existsSync5 } from "fs";
|
|
76358
|
-
import { mkdir as
|
|
76359
|
-
import
|
|
76566
|
+
import { mkdir as mkdir12, readdir as readdir12, readFile as readFile25, stat as stat3, writeFile as writeFile18 } from "fs/promises";
|
|
76567
|
+
import path32 from "path";
|
|
76360
76568
|
var SNAPSHOT_SCHEMA = "baker-canvas-snapshot/1";
|
|
76361
76569
|
var MAX_SNAPSHOT_FILE_BYTES = 32 * 1024 * 1024;
|
|
76362
76570
|
var EXT_TO_MIME = {
|
|
@@ -76383,15 +76591,15 @@ var EXT_TO_MIME = {
|
|
|
76383
76591
|
woff2: "font/woff2"
|
|
76384
76592
|
};
|
|
76385
76593
|
function mimeForFile(filePath) {
|
|
76386
|
-
const ext =
|
|
76594
|
+
const ext = path32.extname(filePath).slice(1).toLowerCase();
|
|
76387
76595
|
return EXT_TO_MIME[ext] ?? "application/octet-stream";
|
|
76388
76596
|
}
|
|
76389
76597
|
function toPosix2(p) {
|
|
76390
|
-
return p.split(
|
|
76598
|
+
return p.split(path32.sep).join("/");
|
|
76391
76599
|
}
|
|
76392
76600
|
function isInside2(dir, target2) {
|
|
76393
|
-
const rel =
|
|
76394
|
-
return !rel.startsWith("..") && !
|
|
76601
|
+
const rel = path32.relative(dir, target2);
|
|
76602
|
+
return !rel.startsWith("..") && !path32.isAbsolute(rel);
|
|
76395
76603
|
}
|
|
76396
76604
|
function localPathRefsFromCanvas(parsed) {
|
|
76397
76605
|
const nodes = parsed?.nodes;
|
|
@@ -76414,20 +76622,20 @@ var FINGERPRINTED_FILES = ["prompt.json", "_definition.md"];
|
|
|
76414
76622
|
async function sourceRefsToSnapshot(canvasDir, parsed) {
|
|
76415
76623
|
const refs = new Set(localPathRefsFromCanvas(parsed));
|
|
76416
76624
|
for (const sceneFile of await listSceneFiles(canvasDir)) {
|
|
76417
|
-
refs.add(toPosix2(
|
|
76625
|
+
refs.add(toPosix2(path32.relative(canvasDir, sceneFile)));
|
|
76418
76626
|
}
|
|
76419
76627
|
refs.add(REBUILD_FILE);
|
|
76420
76628
|
for (const fingerprinted of FINGERPRINTED_FILES) {
|
|
76421
|
-
if (existsSync5(
|
|
76629
|
+
if (existsSync5(path32.join(canvasDir, fingerprinted))) refs.add(fingerprinted);
|
|
76422
76630
|
}
|
|
76423
76631
|
return [...refs];
|
|
76424
76632
|
}
|
|
76425
76633
|
async function uploadRunSnapshot(client, opts) {
|
|
76426
76634
|
try {
|
|
76427
|
-
const canvasDir =
|
|
76635
|
+
const canvasDir = path32.dirname(opts.canvasPath);
|
|
76428
76636
|
const canvasBytes = Buffer.from(opts.raw);
|
|
76429
76637
|
const canvasEntry = {
|
|
76430
|
-
path:
|
|
76638
|
+
path: path32.basename(opts.canvasPath),
|
|
76431
76639
|
bytes: canvasBytes,
|
|
76432
76640
|
sha256: sha256Hex(canvasBytes),
|
|
76433
76641
|
mime: "application/json"
|
|
@@ -76435,10 +76643,10 @@ async function uploadRunSnapshot(client, opts) {
|
|
|
76435
76643
|
const files = [];
|
|
76436
76644
|
const skipped = [];
|
|
76437
76645
|
for (const refPath of await sourceRefsToSnapshot(canvasDir, opts.parsed)) {
|
|
76438
|
-
const abs =
|
|
76646
|
+
const abs = path32.isAbsolute(refPath) ? refPath : path32.resolve(canvasDir, refPath);
|
|
76439
76647
|
if (!isInside2(canvasDir, abs)) {
|
|
76440
76648
|
skipped.push({
|
|
76441
|
-
path: toPosix2(
|
|
76649
|
+
path: toPosix2(path32.relative(canvasDir, abs)),
|
|
76442
76650
|
reason: "outside the creative folder \u2014 read from the workspace on rerun"
|
|
76443
76651
|
});
|
|
76444
76652
|
continue;
|
|
@@ -76447,18 +76655,18 @@ async function uploadRunSnapshot(client, opts) {
|
|
|
76447
76655
|
try {
|
|
76448
76656
|
st = await stat3(abs);
|
|
76449
76657
|
} catch {
|
|
76450
|
-
skipped.push({ path: toPosix2(
|
|
76658
|
+
skipped.push({ path: toPosix2(path32.relative(canvasDir, abs)), reason: "missing" });
|
|
76451
76659
|
continue;
|
|
76452
76660
|
}
|
|
76453
76661
|
const fileList = st.isDirectory() ? await listFilesRecursive(abs) : [abs];
|
|
76454
76662
|
for (const file of fileList) {
|
|
76455
|
-
const rel = toPosix2(
|
|
76663
|
+
const rel = toPosix2(path32.relative(canvasDir, file));
|
|
76456
76664
|
const size = (await stat3(file)).size;
|
|
76457
76665
|
if (size > MAX_SNAPSHOT_FILE_BYTES) {
|
|
76458
76666
|
skipped.push({ path: rel, reason: `too large (${size} bytes)` });
|
|
76459
76667
|
continue;
|
|
76460
76668
|
}
|
|
76461
|
-
const bytes = await
|
|
76669
|
+
const bytes = await readFile25(file);
|
|
76462
76670
|
files.push({ path: rel, bytes, sha256: sha256Hex(bytes), mime: mimeForFile(file) });
|
|
76463
76671
|
}
|
|
76464
76672
|
}
|
|
@@ -76515,7 +76723,7 @@ async function putContentAddressed(client, bytes, mime, signal) {
|
|
|
76515
76723
|
}
|
|
76516
76724
|
async function listFilesRecursive(dir) {
|
|
76517
76725
|
const entries = await readdir12(dir, { recursive: true, withFileTypes: true });
|
|
76518
|
-
return entries.filter((d) => d.isFile()).map((d) =>
|
|
76726
|
+
return entries.filter((d) => d.isFile()).map((d) => path32.join(d.parentPath, d.name));
|
|
76519
76727
|
}
|
|
76520
76728
|
var SnapshotConflictError = class extends Error {
|
|
76521
76729
|
conflicts;
|
|
@@ -76527,19 +76735,19 @@ var SnapshotConflictError = class extends Error {
|
|
|
76527
76735
|
};
|
|
76528
76736
|
async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
|
|
76529
76737
|
const entries = [manifest.canvas, ...manifest.files];
|
|
76530
|
-
const resolvedTarget =
|
|
76738
|
+
const resolvedTarget = path32.resolve(targetDir);
|
|
76531
76739
|
const planned = [];
|
|
76532
76740
|
const conflicts = [];
|
|
76533
76741
|
const upToDate = [];
|
|
76534
76742
|
for (const entry of entries) {
|
|
76535
|
-
if (
|
|
76743
|
+
if (path32.isAbsolute(entry.path) || entry.path.split("/").includes("..")) {
|
|
76536
76744
|
throw new Error(`snapshot entry escapes the creative directory: ${entry.path}`);
|
|
76537
76745
|
}
|
|
76538
|
-
const target2 =
|
|
76539
|
-
if (target2 !== resolvedTarget && !target2.startsWith(resolvedTarget +
|
|
76746
|
+
const target2 = path32.resolve(resolvedTarget, entry.path);
|
|
76747
|
+
if (target2 !== resolvedTarget && !target2.startsWith(resolvedTarget + path32.sep)) {
|
|
76540
76748
|
throw new Error(`snapshot entry escapes the creative directory: ${entry.path}`);
|
|
76541
76749
|
}
|
|
76542
|
-
const existing = await
|
|
76750
|
+
const existing = await readFile25(target2).catch(() => null);
|
|
76543
76751
|
if (existing) {
|
|
76544
76752
|
if (sha256Hex(existing) === entry.sha256) {
|
|
76545
76753
|
upToDate.push(entry.path);
|
|
@@ -76561,11 +76769,11 @@ async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
|
|
|
76561
76769
|
if (sha256Hex(bytes) !== entry.sha256) {
|
|
76562
76770
|
throw new Error(`snapshot download for ${entry.path} does not match its recorded sha256`);
|
|
76563
76771
|
}
|
|
76564
|
-
await
|
|
76565
|
-
await
|
|
76772
|
+
await mkdir12(path32.dirname(target2), { recursive: true });
|
|
76773
|
+
await writeFile18(target2, bytes);
|
|
76566
76774
|
restored.push(entry.path);
|
|
76567
76775
|
}
|
|
76568
|
-
return { canvasPath:
|
|
76776
|
+
return { canvasPath: path32.resolve(resolvedTarget, manifest.canvas.path), restored, upToDate };
|
|
76569
76777
|
}
|
|
76570
76778
|
|
|
76571
76779
|
// src/commands/canvas/static-ad-canvas.ts
|
|
@@ -76580,18 +76788,18 @@ function isStaticAdCanvas(parsed) {
|
|
|
76580
76788
|
}
|
|
76581
76789
|
|
|
76582
76790
|
// src/commands/canvas/named-people-read.ts
|
|
76583
|
-
import { createHash as
|
|
76584
|
-
import { mkdir as
|
|
76585
|
-
import
|
|
76791
|
+
import { createHash as createHash9 } from "crypto";
|
|
76792
|
+
import { mkdir as mkdir13, readFile as readFile26, writeFile as writeFile19 } from "fs/promises";
|
|
76793
|
+
import path33 from "path";
|
|
76586
76794
|
var PEOPLE_TIMEOUT_MS = 3e4;
|
|
76587
76795
|
function peopleReader(cacheDir, spent) {
|
|
76588
|
-
const dir =
|
|
76796
|
+
const dir = path33.join(cacheDir, "named-people");
|
|
76589
76797
|
return async (lines) => {
|
|
76590
76798
|
const said = lines.filter((l) => l.trim());
|
|
76591
76799
|
if (said.length === 0) return [];
|
|
76592
|
-
const key =
|
|
76593
|
-
const file =
|
|
76594
|
-
const cached = await
|
|
76800
|
+
const key = createHash9("sha256").update(JSON.stringify([PEOPLE_PROMPT_VERSION, CLAIMS_MODEL, said])).digest("hex");
|
|
76801
|
+
const file = path33.join(dir, `${key}.json`);
|
|
76802
|
+
const cached = await readFile26(file, "utf8").then((text4) => readPeople(JSON.parse(text4), said)).catch(() => null);
|
|
76595
76803
|
if (cached) return cached;
|
|
76596
76804
|
const answer = await askAboutText(CLAIMS_MODEL, peoplePrompt(said), spent, {
|
|
76597
76805
|
timeoutMs: PEOPLE_TIMEOUT_MS,
|
|
@@ -76599,8 +76807,8 @@ function peopleReader(cacheDir, spent) {
|
|
|
76599
76807
|
});
|
|
76600
76808
|
const people = readPeople(answer, said);
|
|
76601
76809
|
if (people) {
|
|
76602
|
-
await
|
|
76603
|
-
await
|
|
76810
|
+
await mkdir13(dir, { recursive: true });
|
|
76811
|
+
await writeFile19(file, JSON.stringify(answer), "utf8");
|
|
76604
76812
|
}
|
|
76605
76813
|
return people;
|
|
76606
76814
|
};
|
|
@@ -76919,7 +77127,7 @@ async function fetchBrandLogo(domain) {
|
|
|
76919
77127
|
|
|
76920
77128
|
// src/commands/canvas/slug-restore.ts
|
|
76921
77129
|
import { existsSync as existsSync6 } from "fs";
|
|
76922
|
-
import
|
|
77130
|
+
import path34 from "path";
|
|
76923
77131
|
var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
76924
77132
|
var SlugRestoreError = class extends Error {
|
|
76925
77133
|
code;
|
|
@@ -76935,8 +77143,8 @@ async function canvasForSlug(slug, opts) {
|
|
|
76935
77143
|
if (!SLUG_PATTERN.test(slug)) {
|
|
76936
77144
|
throw new SlugRestoreError("invalid_slug", `"${slug}" is not a valid creative slug`);
|
|
76937
77145
|
}
|
|
76938
|
-
const targetDir =
|
|
76939
|
-
const local =
|
|
77146
|
+
const targetDir = path34.join(opts.workspaceRoot, "src", "creatives", slug);
|
|
77147
|
+
const local = path34.join(targetDir, `${slug}.canvas.json`);
|
|
76940
77148
|
if (!opts.forceRemote && existsSync6(local)) return { canvasPath: local };
|
|
76941
77149
|
const client = opts.client();
|
|
76942
77150
|
if (!client) {
|
|
@@ -76995,8 +77203,8 @@ async function fetchManifest(url) {
|
|
|
76995
77203
|
|
|
76996
77204
|
// src/commands/canvas/series-member.ts
|
|
76997
77205
|
import { existsSync as existsSync7 } from "fs";
|
|
76998
|
-
import { readdir as readdir13, readFile as
|
|
76999
|
-
import
|
|
77206
|
+
import { readdir as readdir13, readFile as readFile28, writeFile as writeFile20 } from "fs/promises";
|
|
77207
|
+
import path36 from "path";
|
|
77000
77208
|
|
|
77001
77209
|
// src/commands/canvas/creative-definition.ts
|
|
77002
77210
|
import { parseDocument as parseDocument2 } from "yaml";
|
|
@@ -77122,11 +77330,11 @@ ${doc.toString()}---${raw.slice(match[0].length)}`;
|
|
|
77122
77330
|
}
|
|
77123
77331
|
|
|
77124
77332
|
// src/commands/canvas/static-fix.ts
|
|
77125
|
-
import { readFile as
|
|
77126
|
-
import
|
|
77333
|
+
import { readFile as readFile27 } from "fs/promises";
|
|
77334
|
+
import path35 from "path";
|
|
77127
77335
|
async function previousAdImage(canvasRoot, runId, fetchRecord = fetchRunRecord, roles = {}) {
|
|
77128
77336
|
try {
|
|
77129
|
-
const manifest = JSON.parse(await
|
|
77337
|
+
const manifest = JSON.parse(await readFile27(path35.join(canvasRoot, runId, "manifest.json"), "utf8"));
|
|
77130
77338
|
const byNode = Object.fromEntries(
|
|
77131
77339
|
Object.entries(manifest.outputs_by_node ?? {}).map(([id, out]) => [id, { ...out, ...roles[id] ? { role: roles[id] } : {} }])
|
|
77132
77340
|
);
|
|
@@ -77147,7 +77355,7 @@ async function previousAdImage(canvasRoot, runId, fetchRecord = fetchRunRecord,
|
|
|
77147
77355
|
}
|
|
77148
77356
|
async function previousDelivered(canvasRoot, runId, finalNode, labels, opts = {}) {
|
|
77149
77357
|
try {
|
|
77150
|
-
const manifest = JSON.parse(await
|
|
77358
|
+
const manifest = JSON.parse(await readFile27(path35.join(canvasRoot, runId, "manifest.json"), "utf8"));
|
|
77151
77359
|
const images = manifest.outputs_by_node?.[finalNode]?.images ?? [];
|
|
77152
77360
|
return images.flatMap((image, i) => {
|
|
77153
77361
|
const local = typeof image.path === "string" ? image.path : null;
|
|
@@ -77193,7 +77401,7 @@ function correctedUrl(byNode) {
|
|
|
77193
77401
|
}
|
|
77194
77402
|
async function correctedPicture(canvasRoot, runId, fetchRecord = fetchRunRecord) {
|
|
77195
77403
|
try {
|
|
77196
|
-
const manifest = JSON.parse(await
|
|
77404
|
+
const manifest = JSON.parse(await readFile27(path35.join(canvasRoot, runId, "manifest.json"), "utf8"));
|
|
77197
77405
|
if (manifest.outputs_by_node) return correctedUrl(manifest.outputs_by_node);
|
|
77198
77406
|
} catch {
|
|
77199
77407
|
}
|
|
@@ -77225,7 +77433,7 @@ async function variantImage(canvasRoot, runId, sha2562, fetchRecord = fetchRunRe
|
|
|
77225
77433
|
const sha = sha2562.trim().toLowerCase();
|
|
77226
77434
|
if (!SHA_PREFIX.test(sha)) return null;
|
|
77227
77435
|
try {
|
|
77228
|
-
const manifest = JSON.parse(await
|
|
77436
|
+
const manifest = JSON.parse(await readFile27(path35.join(canvasRoot, runId, "manifest.json"), "utf8"));
|
|
77229
77437
|
for (const [id, out] of Object.entries(manifest.outputs_by_node ?? {})) {
|
|
77230
77438
|
if (roles[id] !== STATIC_VARIANTS_ROLE) continue;
|
|
77231
77439
|
const url = matchVariant(out.images ?? [], sha);
|
|
@@ -77239,10 +77447,10 @@ async function variantImage(canvasRoot, runId, sha2562, fetchRecord = fetchRunRe
|
|
|
77239
77447
|
}
|
|
77240
77448
|
|
|
77241
77449
|
// src/commands/canvas/series-member.ts
|
|
77242
|
-
var creativeDir = (root, slug) =>
|
|
77243
|
-
var canvasFile = (root, slug) =>
|
|
77450
|
+
var creativeDir = (root, slug) => path36.join(root, "src", "creatives", slug);
|
|
77451
|
+
var canvasFile = (root, slug) => path36.join(creativeDir(root, slug), `${slug}.canvas.json`);
|
|
77244
77452
|
async function seriesFieldsOf(root, slug) {
|
|
77245
|
-
const raw = await
|
|
77453
|
+
const raw = await readFile28(path36.join(creativeDir(root, slug), "_definition.md"), "utf8").catch(() => null);
|
|
77246
77454
|
return raw === null ? {} : parseDefinitionStrategyFields(raw);
|
|
77247
77455
|
}
|
|
77248
77456
|
async function seriesLeadOf(root, slug) {
|
|
@@ -77264,7 +77472,7 @@ async function seriesLeadsAbove(root, slug) {
|
|
|
77264
77472
|
return leads;
|
|
77265
77473
|
}
|
|
77266
77474
|
async function seriesMembers(root, lead) {
|
|
77267
|
-
const slugs = await readdir13(
|
|
77475
|
+
const slugs = await readdir13(path36.join(root, "src", "creatives")).catch(() => []);
|
|
77268
77476
|
const members = [];
|
|
77269
77477
|
for (const slug of slugs.sort()) {
|
|
77270
77478
|
if (slug !== lead && await seriesLeadOf(root, slug) === lead) members.push(slug);
|
|
@@ -77276,7 +77484,7 @@ function refusal(code, message, fix) {
|
|
|
77276
77484
|
}
|
|
77277
77485
|
async function leadVersion(root, lead, fetchRecord = fetchRunRecord) {
|
|
77278
77486
|
const file = canvasFile(root, lead);
|
|
77279
|
-
const canvas = existsSync7(file) ? JSON.parse(await
|
|
77487
|
+
const canvas = existsSync7(file) ? JSON.parse(await readFile28(file, "utf8")) : null;
|
|
77280
77488
|
if (!canvas || !isStaticAdCanvas(canvas)) {
|
|
77281
77489
|
return refusal(
|
|
77282
77490
|
"series_lead_not_found",
|
|
@@ -77284,7 +77492,7 @@ async function leadVersion(root, lead, fetchRecord = fetchRunRecord) {
|
|
|
77284
77492
|
"Name a static ad of this workspace as the lead: make it with `baker creatives make <idea>` and render it first."
|
|
77285
77493
|
);
|
|
77286
77494
|
}
|
|
77287
|
-
const canvasRoot =
|
|
77495
|
+
const canvasRoot = path36.join(root, "canvas");
|
|
77288
77496
|
const state = await readRenderState(canvasRoot, lead);
|
|
77289
77497
|
const runId = state.rendered?.runId;
|
|
77290
77498
|
if (!runId) {
|
|
@@ -77311,7 +77519,7 @@ async function leadVersion(root, lead, fetchRecord = fetchRunRecord) {
|
|
|
77311
77519
|
`Render the lead again (baker canvas run --slug ${lead}), then run the series.`
|
|
77312
77520
|
);
|
|
77313
77521
|
}
|
|
77314
|
-
const drawn = state.prompt ?? JSON.parse(await
|
|
77522
|
+
const drawn = state.prompt ?? JSON.parse(await readFile28(path36.join(creativeDir(root, lead), "prompt.json"), "utf8"));
|
|
77315
77523
|
const copy = readAdSpec(drawn).copy.map((l) => ({ text: l.text, ...l.role ? { role: l.role } : {} }));
|
|
77316
77524
|
return { lead: { slug: lead, runId, copy, picture, sizes }, canvas, spec: drawn };
|
|
77317
77525
|
}
|
|
@@ -77331,8 +77539,8 @@ function rebaseLeadPaths(canvas, leadDir, memberDir) {
|
|
|
77331
77539
|
for (const node of out.nodes ?? []) {
|
|
77332
77540
|
const key = localPathParam(node);
|
|
77333
77541
|
const value2 = key ? node.params?.[key] : void 0;
|
|
77334
|
-
if (!key || !node.params || typeof value2 !== "string" || node.id === "prompt" ||
|
|
77335
|
-
const rel =
|
|
77542
|
+
if (!key || !node.params || typeof value2 !== "string" || node.id === "prompt" || path36.isAbsolute(value2)) continue;
|
|
77543
|
+
const rel = path36.relative(memberDir, path36.resolve(leadDir, value2)).split(path36.sep).join("/");
|
|
77336
77544
|
node.params[key] = rel.startsWith(".") ? rel : `./${rel}`;
|
|
77337
77545
|
}
|
|
77338
77546
|
return out;
|
|
@@ -77395,12 +77603,12 @@ async function writeSeriesMemberCanvas(root, slug, opts = {}) {
|
|
|
77395
77603
|
const version = await leadVersion(root, lead, opts.fetchRecord);
|
|
77396
77604
|
if ("refused" in version) return version;
|
|
77397
77605
|
const dir = creativeDir(root, slug);
|
|
77398
|
-
const promptPath =
|
|
77399
|
-
let memberSpec = JSON.parse(await
|
|
77606
|
+
const promptPath = path36.join(dir, "prompt.json");
|
|
77607
|
+
let memberSpec = JSON.parse(await readFile28(promptPath, "utf8").catch(() => "null"));
|
|
77400
77608
|
const unchanged = opts.leadWords ? null : unchangedCopyRefusal(version.lead, memberSpec);
|
|
77401
77609
|
if (unchanged) return unchanged;
|
|
77402
77610
|
const file = canvasFile(root, slug);
|
|
77403
|
-
const before = await
|
|
77611
|
+
const before = await readFile28(file, "utf8").catch(() => null);
|
|
77404
77612
|
const leadDesign = seriesDesignOf(version.spec);
|
|
77405
77613
|
const ownDesign = seriesDesignOf(memberSpec);
|
|
77406
77614
|
const previous = before ? JSON.parse(before).metadata?.series : void 0;
|
|
@@ -77411,7 +77619,7 @@ async function writeSeriesMemberCanvas(root, slug, opts = {}) {
|
|
|
77411
77619
|
return designChangedRefusal(lead, await seriesLeadsAbove(root, slug));
|
|
77412
77620
|
}
|
|
77413
77621
|
memberSpec = withLeadDesign(memberSpec, version.spec);
|
|
77414
|
-
await
|
|
77622
|
+
await writeFile20(promptPath, `${JSON.stringify(memberSpec, null, 2)}
|
|
77415
77623
|
`, "utf8");
|
|
77416
77624
|
designFromLead = true;
|
|
77417
77625
|
}
|
|
@@ -77423,8 +77631,8 @@ async function writeSeriesMemberCanvas(root, slug, opts = {}) {
|
|
|
77423
77631
|
);
|
|
77424
77632
|
const text4 = `${JSON.stringify(canvas, null, 2)}
|
|
77425
77633
|
`;
|
|
77426
|
-
if (before !== text4) await
|
|
77427
|
-
const madeFrom = (await readRenderState(
|
|
77634
|
+
if (before !== text4) await writeFile20(file, text4, "utf8");
|
|
77635
|
+
const madeFrom = (await readRenderState(path36.join(root, "canvas"), slug)).seriesLeadRun;
|
|
77428
77636
|
const newLeadVersion = madeFrom !== void 0 && madeFrom !== version.lead.runId;
|
|
77429
77637
|
return { lead, leadRunId: version.lead.runId, canvasPath: file, written: before !== text4, designFromLead, newLeadVersion };
|
|
77430
77638
|
}
|
|
@@ -77975,30 +78183,30 @@ function withDeliveredSizes(canvas, last) {
|
|
|
77975
78183
|
}
|
|
77976
78184
|
|
|
77977
78185
|
// src/commands/canvas/static-run-review.ts
|
|
77978
|
-
import { readFile as
|
|
77979
|
-
import
|
|
78186
|
+
import { readFile as readFile32 } from "fs/promises";
|
|
78187
|
+
import path40 from "path";
|
|
77980
78188
|
|
|
77981
78189
|
// src/commands/canvas/glance-check.ts
|
|
77982
78190
|
import sharp2 from "sharp";
|
|
77983
|
-
import { z as
|
|
78191
|
+
import { z as z76 } from "zod";
|
|
77984
78192
|
|
|
77985
78193
|
// src/commands/canvas/viewer-check.ts
|
|
77986
|
-
import { createHash as
|
|
77987
|
-
import { mkdir as
|
|
77988
|
-
import
|
|
78194
|
+
import { createHash as createHash11 } from "crypto";
|
|
78195
|
+
import { mkdir as mkdir16, readdir as readdir14, readFile as readFile31, stat as stat4, writeFile as writeFile23 } from "fs/promises";
|
|
78196
|
+
import path39 from "path";
|
|
77989
78197
|
import { defineCommand as defineCommand141 } from "citty";
|
|
77990
78198
|
import { parse as parseYaml3 } from "yaml";
|
|
77991
|
-
import { z as
|
|
78199
|
+
import { z as z75 } from "zod";
|
|
77992
78200
|
|
|
77993
78201
|
// src/commands/canvas/casting-check.ts
|
|
77994
|
-
import { createHash as
|
|
77995
|
-
import { mkdir as
|
|
77996
|
-
import
|
|
77997
|
-
import { z as
|
|
77998
|
-
var CastingAnswerSchema =
|
|
77999
|
-
passes:
|
|
78000
|
-
why:
|
|
78001
|
-
instead:
|
|
78202
|
+
import { createHash as createHash10 } from "crypto";
|
|
78203
|
+
import { mkdir as mkdir14, readFile as readFile29, writeFile as writeFile21 } from "fs/promises";
|
|
78204
|
+
import path37 from "path";
|
|
78205
|
+
import { z as z73 } from "zod";
|
|
78206
|
+
var CastingAnswerSchema = z73.object({
|
|
78207
|
+
passes: z73.boolean(),
|
|
78208
|
+
why: z73.string().default(""),
|
|
78209
|
+
instead: z73.string().default("")
|
|
78002
78210
|
});
|
|
78003
78211
|
function buildCastingPrompt(input) {
|
|
78004
78212
|
return [
|
|
@@ -78014,7 +78222,7 @@ function buildCastingPrompt(input) {
|
|
|
78014
78222
|
].join("\n");
|
|
78015
78223
|
}
|
|
78016
78224
|
function cacheKey(input, model) {
|
|
78017
|
-
return
|
|
78225
|
+
return createHash10("sha256").update(JSON.stringify({ prompt: buildCastingPrompt(input), model })).digest("hex");
|
|
78018
78226
|
}
|
|
78019
78227
|
function parseOrNull(raw) {
|
|
78020
78228
|
try {
|
|
@@ -78025,16 +78233,16 @@ function parseOrNull(raw) {
|
|
|
78025
78233
|
}
|
|
78026
78234
|
async function castingCheck(input, opts) {
|
|
78027
78235
|
if (input.people.length === 0) return { status: "skipped" };
|
|
78028
|
-
const file =
|
|
78029
|
-
const cachedRaw = await
|
|
78236
|
+
const file = path37.join(opts.cacheDir, "casting-check", `${cacheKey(input, opts.model)}.json`);
|
|
78237
|
+
const cachedRaw = await readFile29(file, "utf8").catch(() => null);
|
|
78030
78238
|
const cachedParse = cachedRaw ? CastingAnswerSchema.safeParse(parseOrNull(cachedRaw)) : null;
|
|
78031
78239
|
if (cachedParse?.success) return { status: cachedParse.data.passes ? "passed" : "failed", answer: cachedParse.data, cached: true };
|
|
78032
78240
|
const prompt = buildCastingPrompt(input);
|
|
78033
78241
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
78034
78242
|
const parsed = CastingAnswerSchema.safeParse(await opts.ask(prompt));
|
|
78035
78243
|
if (!parsed.success) continue;
|
|
78036
|
-
await
|
|
78037
|
-
await
|
|
78244
|
+
await mkdir14(path37.dirname(file), { recursive: true });
|
|
78245
|
+
await writeFile21(file, `${JSON.stringify(parsed.data, null, 2)}
|
|
78038
78246
|
`, "utf8");
|
|
78039
78247
|
return { status: parsed.data.passes ? "passed" : "failed", answer: parsed.data, cached: false };
|
|
78040
78248
|
}
|
|
@@ -78056,9 +78264,9 @@ function castingCheckFailure(answer, slug) {
|
|
|
78056
78264
|
}
|
|
78057
78265
|
|
|
78058
78266
|
// src/commands/canvas/turn-renders.ts
|
|
78059
|
-
import { mkdir as
|
|
78060
|
-
import
|
|
78061
|
-
import { z as
|
|
78267
|
+
import { mkdir as mkdir15, readFile as readFile30, writeFile as writeFile22 } from "fs/promises";
|
|
78268
|
+
import path38 from "path";
|
|
78269
|
+
import { z as z74 } from "zod";
|
|
78062
78270
|
var RUNS_PER_TURN = 2;
|
|
78063
78271
|
var VIEWER_FAILS_PER_TURN = 2;
|
|
78064
78272
|
var AD_TURN_MS = 12 * 6e4;
|
|
@@ -78070,15 +78278,15 @@ var ROUND_MS = {
|
|
|
78070
78278
|
regenerate: 22e4
|
|
78071
78279
|
};
|
|
78072
78280
|
var RENDER_MS = 15e4;
|
|
78073
|
-
var LedgerSchema =
|
|
78074
|
-
turnId:
|
|
78075
|
-
creatives:
|
|
78076
|
-
|
|
78077
|
-
|
|
78078
|
-
runs:
|
|
78079
|
-
verdict:
|
|
78080
|
-
viewerFails:
|
|
78081
|
-
spentMs:
|
|
78281
|
+
var LedgerSchema = z74.object({
|
|
78282
|
+
turnId: z74.string(),
|
|
78283
|
+
creatives: z74.record(
|
|
78284
|
+
z74.string(),
|
|
78285
|
+
z74.object({
|
|
78286
|
+
runs: z74.number().default(0),
|
|
78287
|
+
verdict: z74.string().optional(),
|
|
78288
|
+
viewerFails: z74.number().optional(),
|
|
78289
|
+
spentMs: z74.number().optional()
|
|
78082
78290
|
})
|
|
78083
78291
|
)
|
|
78084
78292
|
});
|
|
@@ -78134,18 +78342,18 @@ function viewerChecksSpent(ledger, turnId, slug) {
|
|
|
78134
78342
|
const entry = turnId !== void 0 && ledger?.turnId === turnId ? ledger.creatives[slug] : void 0;
|
|
78135
78343
|
return (entry?.viewerFails ?? 0) >= VIEWER_FAILS_PER_TURN;
|
|
78136
78344
|
}
|
|
78137
|
-
var ledgerPath = (cacheDir) =>
|
|
78345
|
+
var ledgerPath = (cacheDir) => path38.join(cacheDir, "turn-renders.json");
|
|
78138
78346
|
async function readTurnRenders(cacheDir) {
|
|
78139
78347
|
try {
|
|
78140
|
-
const parsed = LedgerSchema.safeParse(JSON.parse(await
|
|
78348
|
+
const parsed = LedgerSchema.safeParse(JSON.parse(await readFile30(ledgerPath(cacheDir), "utf8")));
|
|
78141
78349
|
return parsed.success ? parsed.data : void 0;
|
|
78142
78350
|
} catch {
|
|
78143
78351
|
return void 0;
|
|
78144
78352
|
}
|
|
78145
78353
|
}
|
|
78146
78354
|
async function writeTurnRenders(cacheDir, ledger) {
|
|
78147
|
-
await
|
|
78148
|
-
await
|
|
78355
|
+
await mkdir15(cacheDir, { recursive: true });
|
|
78356
|
+
await writeFile22(ledgerPath(cacheDir), JSON.stringify(ledger), "utf8");
|
|
78149
78357
|
}
|
|
78150
78358
|
|
|
78151
78359
|
// src/commands/creatives/company-offer.ts
|
|
@@ -78160,14 +78368,14 @@ function marketQuery(companyDoc) {
|
|
|
78160
78368
|
// src/commands/canvas/viewer-check.ts
|
|
78161
78369
|
var VIEWER_QUESTIONS = ["offer", "for_whom", "next_step"];
|
|
78162
78370
|
var VIEWER_CLARITY = ["clear", "guess", "unclear"];
|
|
78163
|
-
var ViewerReply =
|
|
78164
|
-
var ViewerAnswerSchema =
|
|
78371
|
+
var ViewerReply = z75.object({ answer: z75.string().default(""), clarity: z75.enum(VIEWER_CLARITY) });
|
|
78372
|
+
var ViewerAnswerSchema = z75.object({
|
|
78165
78373
|
offer: ViewerReply,
|
|
78166
78374
|
for_whom: ViewerReply,
|
|
78167
78375
|
next_step: ViewerReply,
|
|
78168
78376
|
/** The words that talk to the viewer as if they had already done something with the brand; "" when none. */
|
|
78169
|
-
presumes:
|
|
78170
|
-
missing:
|
|
78377
|
+
presumes: z75.string().default(""),
|
|
78378
|
+
missing: z75.string().default("")
|
|
78171
78379
|
});
|
|
78172
78380
|
var VIEWER_REQUIRED_CLARITY = {
|
|
78173
78381
|
offer: "clear",
|
|
@@ -78274,18 +78482,18 @@ function viewerCheckCacheKey(input, model) {
|
|
|
78274
78482
|
intended: input.intended,
|
|
78275
78483
|
model
|
|
78276
78484
|
};
|
|
78277
|
-
return
|
|
78485
|
+
return createHash11("sha256").update(JSON.stringify(canonical)).digest("hex");
|
|
78278
78486
|
}
|
|
78279
78487
|
async function cachedAnswer(file) {
|
|
78280
78488
|
try {
|
|
78281
|
-
const parsed = ViewerAnswerSchema.safeParse(JSON.parse(await
|
|
78489
|
+
const parsed = ViewerAnswerSchema.safeParse(JSON.parse(await readFile31(file, "utf8")));
|
|
78282
78490
|
return parsed.success ? parsed.data : null;
|
|
78283
78491
|
} catch {
|
|
78284
78492
|
return null;
|
|
78285
78493
|
}
|
|
78286
78494
|
}
|
|
78287
78495
|
async function checkViewer(input, opts) {
|
|
78288
|
-
const file =
|
|
78496
|
+
const file = path39.join(opts.cacheDir, "viewer-check", `${viewerCheckCacheKey(input, opts.model)}.json`);
|
|
78289
78497
|
let answer = await cachedAnswer(file);
|
|
78290
78498
|
const cached = answer !== null;
|
|
78291
78499
|
const prompt = buildViewerPrompt(input);
|
|
@@ -78295,8 +78503,8 @@ async function checkViewer(input, opts) {
|
|
|
78295
78503
|
}
|
|
78296
78504
|
if (answer === null) return { status: "unavailable", input };
|
|
78297
78505
|
if (!cached) {
|
|
78298
|
-
await
|
|
78299
|
-
await
|
|
78506
|
+
await mkdir16(path39.dirname(file), { recursive: true });
|
|
78507
|
+
await writeFile23(file, `${JSON.stringify(answer, null, 2)}
|
|
78300
78508
|
`, "utf8");
|
|
78301
78509
|
}
|
|
78302
78510
|
const sure = sureEnough(answer.offer.clarity, VIEWER_REQUIRED_CLARITY.offer);
|
|
@@ -78355,17 +78563,17 @@ function viewerCheckFailure(outcome2, slug) {
|
|
|
78355
78563
|
}
|
|
78356
78564
|
};
|
|
78357
78565
|
}
|
|
78358
|
-
var RecordedViewer =
|
|
78359
|
-
temperature:
|
|
78360
|
-
knows:
|
|
78361
|
-
placements:
|
|
78362
|
-
source:
|
|
78363
|
-
reasons:
|
|
78364
|
-
market:
|
|
78365
|
-
country:
|
|
78366
|
-
language:
|
|
78367
|
-
currency:
|
|
78368
|
-
address:
|
|
78566
|
+
var RecordedViewer = z75.object({
|
|
78567
|
+
temperature: z75.enum(VIEWER_TEMPERATURE_VALUES),
|
|
78568
|
+
knows: z75.array(z75.enum(VIEWER_KNOWS_VALUES)).default([]),
|
|
78569
|
+
placements: z75.array(z75.string().min(1)).default(["automatic"]),
|
|
78570
|
+
source: z75.enum(VIEWER_SOURCE_VALUES),
|
|
78571
|
+
reasons: z75.array(z75.string()).default([]),
|
|
78572
|
+
market: z75.object({
|
|
78573
|
+
country: z75.string().min(2),
|
|
78574
|
+
language: z75.string().optional(),
|
|
78575
|
+
currency: z75.string().optional(),
|
|
78576
|
+
address: z75.string().optional()
|
|
78369
78577
|
}).optional()
|
|
78370
78578
|
});
|
|
78371
78579
|
var nameKey = (text4) => text4.normalize("NFD").replace(new RegExp("\\p{M}", "gu"), "").toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean).join(" ");
|
|
@@ -78424,12 +78632,12 @@ async function resolveViewerAlone(sources, lookups) {
|
|
|
78424
78632
|
notes
|
|
78425
78633
|
};
|
|
78426
78634
|
}
|
|
78427
|
-
var Frontmatter =
|
|
78428
|
-
viewer:
|
|
78429
|
-
targetAdSet:
|
|
78430
|
-
persona:
|
|
78431
|
-
awareness:
|
|
78432
|
-
angle:
|
|
78635
|
+
var Frontmatter = z75.object({
|
|
78636
|
+
viewer: z75.unknown().optional(),
|
|
78637
|
+
targetAdSet: z75.union([z75.string(), z75.number()]).transform(String).optional(),
|
|
78638
|
+
persona: z75.string().optional(),
|
|
78639
|
+
awareness: z75.enum(AWARENESS_VALUES).optional().catch(void 0),
|
|
78640
|
+
angle: z75.string().optional()
|
|
78433
78641
|
});
|
|
78434
78642
|
function frontmatterOf2(raw) {
|
|
78435
78643
|
const block2 = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1];
|
|
@@ -78442,12 +78650,12 @@ function frontmatterOf2(raw) {
|
|
|
78442
78650
|
}
|
|
78443
78651
|
}
|
|
78444
78652
|
async function readCreativeForViewerCheck(creativeDir3) {
|
|
78445
|
-
const spec = readAdSpec(JSON.parse(await
|
|
78446
|
-
const definition = frontmatterOf2(await
|
|
78653
|
+
const spec = readAdSpec(JSON.parse(await readFile31(path39.join(creativeDir3, "prompt.json"), "utf8")));
|
|
78654
|
+
const definition = frontmatterOf2(await readFile31(path39.join(creativeDir3, "_definition.md"), "utf8").catch(() => ""));
|
|
78447
78655
|
const offer = spec.message?.trim() || definition.angle?.trim();
|
|
78448
78656
|
const persona = spec.viewer?.who.trim() || definition.persona?.trim();
|
|
78449
78657
|
const brand = spec.brand?.name?.trim();
|
|
78450
|
-
const sells = marketQuery(await
|
|
78658
|
+
const sells = marketQuery(await readFile31(path39.resolve(creativeDir3, "../../../COMPANY.md"), "utf8").catch(() => ""));
|
|
78451
78659
|
return {
|
|
78452
78660
|
ad: {
|
|
78453
78661
|
copy: spec.copy.map((line) => ({
|
|
@@ -78473,7 +78681,7 @@ function castPeople(spec) {
|
|
|
78473
78681
|
return people.length > 0 ? { people } : {};
|
|
78474
78682
|
}
|
|
78475
78683
|
async function readPersonas(workspaceRoot) {
|
|
78476
|
-
const dir =
|
|
78684
|
+
const dir = path39.join(workspaceRoot, "src", "content", "personas");
|
|
78477
78685
|
let files;
|
|
78478
78686
|
try {
|
|
78479
78687
|
files = (await readdir14(dir, { recursive: true })).filter((f) => f.endsWith(".md"));
|
|
@@ -78482,7 +78690,7 @@ async function readPersonas(workspaceRoot) {
|
|
|
78482
78690
|
}
|
|
78483
78691
|
return await Promise.all(
|
|
78484
78692
|
files.map(async (file) => {
|
|
78485
|
-
const raw = await
|
|
78693
|
+
const raw = await readFile31(path39.join(dir, file), "utf8");
|
|
78486
78694
|
const block2 = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1];
|
|
78487
78695
|
let segment;
|
|
78488
78696
|
try {
|
|
@@ -78492,7 +78700,7 @@ async function readPersonas(workspaceRoot) {
|
|
|
78492
78700
|
}
|
|
78493
78701
|
const title = raw.match(/^#\s+(.+)$/m)?.[1]?.trim();
|
|
78494
78702
|
return {
|
|
78495
|
-
slug:
|
|
78703
|
+
slug: path39.basename(file, ".md"),
|
|
78496
78704
|
...title ? { title } : {},
|
|
78497
78705
|
...typeof segment === "string" && segment.trim() ? { segment: segment.trim() } : {}
|
|
78498
78706
|
};
|
|
@@ -78523,11 +78731,11 @@ async function metaAdSetViewer(adSetId, workspaceRoot) {
|
|
|
78523
78731
|
read("/api/ads/meta/audiences", params).catch(() => []),
|
|
78524
78732
|
read("/api/ads/meta/campaigns", params).catch(() => [])
|
|
78525
78733
|
]);
|
|
78526
|
-
const cacheDir =
|
|
78734
|
+
const cacheDir = path39.join(workspaceRoot, "canvas", ".cache");
|
|
78527
78735
|
const spent = [];
|
|
78528
78736
|
const readName = await readAudienceNames(
|
|
78529
78737
|
audienceNamesOf([adSet], audiences),
|
|
78530
|
-
rememberedJev(
|
|
78738
|
+
rememberedJev(path39.join(cacheDir, "audience-names.json"), (u) => spent.push(u))
|
|
78531
78739
|
);
|
|
78532
78740
|
await holdUnbilledSpend(cacheDir, spent).catch(() => void 0);
|
|
78533
78741
|
return adSetViewerRows([adSet], audiences, campaigns, readName)[0]?.viewer ?? null;
|
|
@@ -78538,8 +78746,8 @@ function liveViewerLookups(workspaceRoot) {
|
|
|
78538
78746
|
return {
|
|
78539
78747
|
adSetViewer: (adSetId) => metaAdSetViewer(adSetId, workspaceRoot),
|
|
78540
78748
|
personas: () => readPersonas(workspaceRoot),
|
|
78541
|
-
companyMarket: async () => marketFromCompanyDoc(await
|
|
78542
|
-
company: async () => (await
|
|
78749
|
+
companyMarket: async () => marketFromCompanyDoc(await readFile31(path39.join(workspaceRoot, "COMPANY.md"), "utf8").catch(() => null)),
|
|
78750
|
+
company: async () => (await readFile31(path39.join(workspaceRoot, "COMPANY.md"), "utf8").catch(() => "")).slice(0, 800)
|
|
78543
78751
|
};
|
|
78544
78752
|
}
|
|
78545
78753
|
function gatewayViewerModel(model, onSpend) {
|
|
@@ -78617,11 +78825,11 @@ var ARGS5 = {
|
|
|
78617
78825
|
};
|
|
78618
78826
|
registerSchema({ command: "canvas.viewer-check", description: DESCRIPTION2, args: ARGS5 });
|
|
78619
78827
|
async function creativeDirOf(arg, workspaceRoot) {
|
|
78620
|
-
const direct =
|
|
78828
|
+
const direct = path39.resolve(arg);
|
|
78621
78829
|
try {
|
|
78622
|
-
return (await stat4(direct)).isDirectory() ? direct :
|
|
78830
|
+
return (await stat4(direct)).isDirectory() ? direct : path39.dirname(direct);
|
|
78623
78831
|
} catch {
|
|
78624
|
-
return
|
|
78832
|
+
return path39.join(workspaceRoot, "src", "creatives", arg);
|
|
78625
78833
|
}
|
|
78626
78834
|
}
|
|
78627
78835
|
var viewerCheckCommand = defineCommand141({
|
|
@@ -78631,7 +78839,7 @@ var viewerCheckCommand = defineCommand141({
|
|
|
78631
78839
|
const cwd = process.cwd();
|
|
78632
78840
|
const workspaceRoot = findWorkspaceRoot(cwd) ?? cwd;
|
|
78633
78841
|
const creativeDir3 = await creativeDirOf(String(args8.slug), workspaceRoot);
|
|
78634
|
-
const slug =
|
|
78842
|
+
const slug = path39.basename(creativeDir3);
|
|
78635
78843
|
let creative;
|
|
78636
78844
|
try {
|
|
78637
78845
|
creative = await readCreativeForViewerCheck(creativeDir3);
|
|
@@ -78648,7 +78856,7 @@ var viewerCheckCommand = defineCommand141({
|
|
|
78648
78856
|
}
|
|
78649
78857
|
const resolved = await resolveViewer(creative.sources, liveViewerLookups(workspaceRoot));
|
|
78650
78858
|
const input = { ...creative.ad, viewer: resolved.viewer };
|
|
78651
|
-
const cacheDir =
|
|
78859
|
+
const cacheDir = path39.join(workspaceRoot, "canvas", ".cache");
|
|
78652
78860
|
const spent = [];
|
|
78653
78861
|
const outcome2 = await checkViewer(input, {
|
|
78654
78862
|
cacheDir,
|
|
@@ -78721,9 +78929,9 @@ var GLANCE_REQUIRED = {
|
|
|
78721
78929
|
var GLANCE_WIDTH = 400;
|
|
78722
78930
|
var GlanceAnswerSchema = ViewerAnswerSchema.extend({
|
|
78723
78931
|
/** What kind of product or service they take it for (D4). */
|
|
78724
|
-
category:
|
|
78725
|
-
stopped:
|
|
78726
|
-
me:
|
|
78932
|
+
category: z76.object({ answer: z76.string().default(""), clarity: z76.enum(["clear", "guess", "unclear"]).default("unclear") }).default({ answer: "", clarity: "unclear" }),
|
|
78933
|
+
stopped: z76.string().default(""),
|
|
78934
|
+
me: z76.enum(["yes", "no", "unsure"]).default("unsure")
|
|
78727
78935
|
});
|
|
78728
78936
|
function wherePhrase2(placements) {
|
|
78729
78937
|
const fullScreen = placements.length === 0 || placements.some((p) => /automatic|stor|reel/i.test(p));
|
|
@@ -78907,7 +79115,7 @@ function runCostHint(stats, kind) {
|
|
|
78907
79115
|
async function textOf(ref2) {
|
|
78908
79116
|
const r = ref2;
|
|
78909
79117
|
try {
|
|
78910
|
-
if (typeof r?.path === "string") return await
|
|
79118
|
+
if (typeof r?.path === "string") return await readFile32(r.path, "utf8");
|
|
78911
79119
|
if (typeof r?.url === "string") {
|
|
78912
79120
|
const res = await fetch(r.url);
|
|
78913
79121
|
return res.ok ? await res.text() : null;
|
|
@@ -78974,7 +79182,7 @@ async function reviewStaticRun(opts) {
|
|
|
78974
79182
|
let native;
|
|
78975
79183
|
let palette = [];
|
|
78976
79184
|
try {
|
|
78977
|
-
const spec = readAdSpec(JSON.parse(await
|
|
79185
|
+
const spec = readAdSpec(JSON.parse(await readFile32(targets.promptPath, "utf8")));
|
|
78978
79186
|
copy = spec.copy.map((l) => l.text);
|
|
78979
79187
|
lines = spec.copy.map((l) => ({ text: l.text, lettering: l.lettering, ...l.role ? { role: l.role } : {} }));
|
|
78980
79188
|
if (spec.native) {
|
|
@@ -78986,7 +79194,7 @@ async function reviewStaticRun(opts) {
|
|
|
78986
79194
|
mechanisms = spec.mechanism.map((m) => m.device);
|
|
78987
79195
|
brand = spec.brand?.name ?? opts.brandName;
|
|
78988
79196
|
const sells = marketQuery(
|
|
78989
|
-
await
|
|
79197
|
+
await readFile32(path40.resolve(path40.dirname(targets.promptPath), "../../../COMPANY.md"), "utf8").catch(() => "")
|
|
78990
79198
|
);
|
|
78991
79199
|
intended = {
|
|
78992
79200
|
...spec.message?.trim() ? { offer: spec.message.trim() } : {},
|
|
@@ -79001,7 +79209,7 @@ async function reviewStaticRun(opts) {
|
|
|
79001
79209
|
];
|
|
79002
79210
|
expectLogo = spec.logo && !spec.native && staticRunShape(opts.parsed).logo;
|
|
79003
79211
|
concept = conceptSummary(spec);
|
|
79004
|
-
const cacheDir =
|
|
79212
|
+
const cacheDir = path40.join(findWorkspaceRoot(path40.dirname(targets.promptPath)) ?? path40.dirname(targets.promptPath), "canvas", ".cache");
|
|
79005
79213
|
const people = spec.native ? [] : await peopleReader(cacheDir, (u) => opts.onSpend?.(u))(copy) ?? [];
|
|
79006
79214
|
uncast = uncastPeople(spec, people);
|
|
79007
79215
|
} catch {
|
|
@@ -79173,12 +79381,12 @@ function variantHints(judge, prefilter) {
|
|
|
79173
79381
|
}
|
|
79174
79382
|
|
|
79175
79383
|
// src/commands/canvas/static-spec-repair.ts
|
|
79176
|
-
import { readFile as
|
|
79384
|
+
import { readFile as readFile33, writeFile as writeFile24 } from "fs/promises";
|
|
79177
79385
|
import sharp3 from "sharp";
|
|
79178
|
-
import { z as
|
|
79179
|
-
var
|
|
79180
|
-
changed:
|
|
79181
|
-
set:
|
|
79386
|
+
import { z as z77 } from "zod";
|
|
79387
|
+
var Answer4 = z77.object({
|
|
79388
|
+
changed: z77.string().min(1),
|
|
79389
|
+
set: z77.array(z77.object({ path: z77.string().min(1), value: z77.unknown() }))
|
|
79182
79390
|
});
|
|
79183
79391
|
function withValueAt(root, path83, value2) {
|
|
79184
79392
|
const [key, ...rest] = path83;
|
|
@@ -79197,7 +79405,7 @@ function printedWords(spec) {
|
|
|
79197
79405
|
return [...spec.copy.map((l) => l.text), ...spec.native ? nativeCopy(spec.native) : []];
|
|
79198
79406
|
}
|
|
79199
79407
|
function acceptRepair(before, answer) {
|
|
79200
|
-
const parsed =
|
|
79408
|
+
const parsed = Answer4.safeParse(answer);
|
|
79201
79409
|
if (!parsed.success || !isAdSpec(before)) return null;
|
|
79202
79410
|
const spec = parsed.data.set.reduce((at, { path: path83, value: value2 }) => withValueAt(at, path83.split("."), value2), before);
|
|
79203
79411
|
if (!isAdSpec(spec)) return null;
|
|
@@ -79232,7 +79440,7 @@ function buildSpecRepairPrompt(spec, findings, schema) {
|
|
|
79232
79440
|
var REPAIR_TIMEOUT_MS = 15e4;
|
|
79233
79441
|
var SHOWN_WIDTH = 768;
|
|
79234
79442
|
async function repairSpec(opts) {
|
|
79235
|
-
const text4 = await
|
|
79443
|
+
const text4 = await readFile33(opts.promptPath, "utf8").catch(() => null);
|
|
79236
79444
|
if (text4 === null) return null;
|
|
79237
79445
|
let before;
|
|
79238
79446
|
try {
|
|
@@ -79244,7 +79452,7 @@ async function repairSpec(opts) {
|
|
|
79244
79452
|
const prompt = buildSpecRepairPrompt(text4, opts.findings, adSpecJsonSchema());
|
|
79245
79453
|
const repair = acceptRepair(before, await askAboutImages(RENDER_REVIEW_MODEL, shown ? [shown] : [], prompt, opts.onSpend, REPAIR_TIMEOUT_MS));
|
|
79246
79454
|
if (!repair) return null;
|
|
79247
|
-
await
|
|
79455
|
+
await writeFile24(opts.promptPath, `${JSON.stringify(repair.spec, null, 2)}
|
|
79248
79456
|
`, "utf8");
|
|
79249
79457
|
return repair.changed;
|
|
79250
79458
|
}
|
|
@@ -79379,7 +79587,7 @@ function failPreflight3(code, message, fix) {
|
|
|
79379
79587
|
process.exit(2);
|
|
79380
79588
|
}
|
|
79381
79589
|
async function canvasFileFor(opts) {
|
|
79382
|
-
if (opts.file) return { canvasPath:
|
|
79590
|
+
if (opts.file) return { canvasPath: path41.resolve(opts.file) };
|
|
79383
79591
|
if (!opts.slug) failPreflight3("missing_canvas", "Pass the canvas file, or --slug <slug> to run a creative by name.");
|
|
79384
79592
|
const cwd = process.cwd();
|
|
79385
79593
|
try {
|
|
@@ -79405,7 +79613,7 @@ async function executeSeriesRun(lead) {
|
|
|
79405
79613
|
`Make each other message as an ad of the series first (baker creatives make <idea> --series ${lead}), or each language (baker creatives translate --slug ${lead} --to es-MX,de).`
|
|
79406
79614
|
);
|
|
79407
79615
|
}
|
|
79408
|
-
const canvasRoot =
|
|
79616
|
+
const canvasRoot = path41.join(root, "canvas");
|
|
79409
79617
|
process.stdout.write(`[series] rendering ${members.length} ads of ${lead} at once: ${members.join(", ")}
|
|
79410
79618
|
`);
|
|
79411
79619
|
const results = await runSeries(members, {
|
|
@@ -79437,9 +79645,9 @@ async function executeSeriesRun(lead) {
|
|
|
79437
79645
|
`);
|
|
79438
79646
|
}
|
|
79439
79647
|
async function refreshSeriesAd(opts) {
|
|
79440
|
-
const slug = opts.slug ?? (opts.file ? creativeSlugFromCanvasPath(
|
|
79648
|
+
const slug = opts.slug ?? (opts.file ? creativeSlugFromCanvasPath(path41.resolve(opts.file)) : null);
|
|
79441
79649
|
if (!slug) return false;
|
|
79442
|
-
const root = findWorkspaceRoot(opts.file ?
|
|
79650
|
+
const root = findWorkspaceRoot(opts.file ? path41.dirname(path41.resolve(opts.file)) : process.cwd());
|
|
79443
79651
|
if (!root) return false;
|
|
79444
79652
|
const made = await writeSeriesMemberCanvas(root, slug);
|
|
79445
79653
|
if (made === null) return false;
|
|
@@ -79459,7 +79667,7 @@ async function executeCanvasRun(opts) {
|
|
|
79459
79667
|
const creativeSlug = creativeSlugFromCanvasPath(filePath) ?? void 0;
|
|
79460
79668
|
if (creativeSlug) await clearLegacyDirtyMarker(canvasRoot, creativeSlug);
|
|
79461
79669
|
const turnId = optionalTurnId();
|
|
79462
|
-
const turnCache = opts.cacheDir ??
|
|
79670
|
+
const turnCache = opts.cacheDir ?? path41.join(canvasRoot, ".cache");
|
|
79463
79671
|
if (creativeSlug && (opts.autoFixRound ?? 0) === 0) {
|
|
79464
79672
|
const refusal2 = turnRenderRefusal(await readTurnRenders(turnCache), turnId, creativeSlug);
|
|
79465
79673
|
if (refusal2) {
|
|
@@ -79472,7 +79680,7 @@ async function executeCanvasRun(opts) {
|
|
|
79472
79680
|
}
|
|
79473
79681
|
const citing = [];
|
|
79474
79682
|
await wireProofBadges(
|
|
79475
|
-
|
|
79683
|
+
path41.join(path41.dirname(filePath), "prompt.json"),
|
|
79476
79684
|
fetchBrandLogo,
|
|
79477
79685
|
(line) => process.stderr.write(`${line}
|
|
79478
79686
|
`),
|
|
@@ -79482,7 +79690,7 @@ async function executeCanvasRun(opts) {
|
|
|
79482
79690
|
if ((opts.autoFixRound ?? 0) === 0) {
|
|
79483
79691
|
const reading = [];
|
|
79484
79692
|
await wireNamedPeople(
|
|
79485
|
-
|
|
79693
|
+
path41.join(path41.dirname(filePath), "prompt.json"),
|
|
79486
79694
|
avatarOfNamedPerson,
|
|
79487
79695
|
(line) => process.stderr.write(`${line}
|
|
79488
79696
|
`),
|
|
@@ -79682,7 +79890,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
79682
79890
|
process.stdout.write(`[extend] placing ${opts.extendSizes.join(", ")} clear of what covers or cuts it, its margins painted
|
|
79683
79891
|
`);
|
|
79684
79892
|
}
|
|
79685
|
-
const cacheDir = opts.cacheDir ??
|
|
79893
|
+
const cacheDir = opts.cacheDir ?? path41.join(canvasRoot, ".cache");
|
|
79686
79894
|
const engine = createEngineFromEnv({
|
|
79687
79895
|
cacheDir,
|
|
79688
79896
|
outputsDir: opts.outputsDir ?? canvasRoot,
|
|
@@ -79706,7 +79914,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
79706
79914
|
const canvasSha = sha256Hex(Buffer.from(canvasText));
|
|
79707
79915
|
const sourceSha = prepared.sourceSha;
|
|
79708
79916
|
const client = opts.record === false ? null : buildBackendClient();
|
|
79709
|
-
const outputsDir = opts.outputsDir ?
|
|
79917
|
+
const outputsDir = opts.outputsDir ? path41.resolve(opts.outputsDir) : canvasRoot;
|
|
79710
79918
|
const { runId, resumed, source, sameSource, concurrentRunId, rendering } = await resolveRunId({
|
|
79711
79919
|
explicitRunId: opts.runId,
|
|
79712
79920
|
adoptedRunId: adoptRunId,
|
|
@@ -79737,7 +79945,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
79737
79945
|
}
|
|
79738
79946
|
const viewerUsage = [];
|
|
79739
79947
|
const viewerCheck = creativeSlug && isStaticAdCanvas(parsed) ? await viewerCheckBeforeRender({
|
|
79740
|
-
creativeDir:
|
|
79948
|
+
creativeDir: path41.dirname(filePath),
|
|
79741
79949
|
cacheDir,
|
|
79742
79950
|
slug: creativeSlug,
|
|
79743
79951
|
skip: viewerCheckSkip({
|
|
@@ -79749,7 +79957,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
79749
79957
|
asked: opts.skipViewerCheck === true
|
|
79750
79958
|
}),
|
|
79751
79959
|
spent: viewerChecksSpent(await readTurnRenders(turnCache), turnId, creativeSlug),
|
|
79752
|
-
lookups: liveViewerLookups(
|
|
79960
|
+
lookups: liveViewerLookups(path41.dirname(canvasRoot)),
|
|
79753
79961
|
model: VIEWER_CHECK_MODEL,
|
|
79754
79962
|
ask: gatewayViewerModel(VIEWER_CHECK_MODEL, (usage) => viewerUsage.push(usage)),
|
|
79755
79963
|
judge: jevReadJudge((usage) => viewerUsage.push(usage))
|
|
@@ -79807,7 +80015,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
79807
80015
|
if (snapshot && creativeSlug) await recordSnapshot(canvasRoot, creativeSlug, snapshot);
|
|
79808
80016
|
const recordMeta = {
|
|
79809
80017
|
creativeSlug,
|
|
79810
|
-
canvasPath:
|
|
80018
|
+
canvasPath: path41.relative(path41.dirname(canvasRoot), filePath) || void 0,
|
|
79811
80019
|
canvasSha,
|
|
79812
80020
|
// The fingerprint the dashboard compares against the current source to flag
|
|
79813
80021
|
// "edited since last render": the same `computeSourceSha` the edit-sync posts,
|
|
@@ -79897,7 +80105,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
79897
80105
|
brief: opts.brief?.trim() || void 0,
|
|
79898
80106
|
onSpend: (usage) => reviewUsage.push(usage)
|
|
79899
80107
|
}) : null;
|
|
79900
|
-
const brandKit = reviewable ? null : await readStaticBrandKit(await findBrandRoot(
|
|
80108
|
+
const brandKit = reviewable ? null : await readStaticBrandKit(await findBrandRoot(path41.dirname(filePath)) ?? process.cwd());
|
|
79901
80109
|
const runShape = {
|
|
79902
80110
|
fix: fixInstruction !== void 0,
|
|
79903
80111
|
useVariant: opts.useVariant !== void 0,
|
|
@@ -79932,7 +80140,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
79932
80140
|
}
|
|
79933
80141
|
const costHint = staticReview ? runCostHint(result.stats, paidFor(runShape)) : null;
|
|
79934
80142
|
const corrected = opts.autoFixed ?? [];
|
|
79935
|
-
const seriesRoot = creativeSlug && staticReview ? findWorkspaceRoot(
|
|
80143
|
+
const seriesRoot = creativeSlug && staticReview ? findWorkspaceRoot(path41.dirname(filePath)) : null;
|
|
79936
80144
|
const seriesHints = seriesRoot && creativeSlug ? seriesRunHints(creativeSlug, {
|
|
79937
80145
|
leads: await seriesLeadsAbove(seriesRoot, creativeSlug),
|
|
79938
80146
|
members: await seriesMembers(seriesRoot, creativeSlug)
|
|
@@ -79956,7 +80164,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
79956
80164
|
] : [];
|
|
79957
80165
|
const findings = staticReview ? staticReview.review : review;
|
|
79958
80166
|
const ready = staticReview ? staticReview.verdict === "ready" || staticReview.verdict === "ready_with_notes" : review !== null && !review.some((f) => f.severity === "blocking");
|
|
79959
|
-
const definitionStatus = ready && creativeSlug ? await advanceDefinitionStatus(
|
|
80167
|
+
const definitionStatus = ready && creativeSlug ? await advanceDefinitionStatus(path41.join(path41.dirname(filePath), "_definition.md"), "rendered") : null;
|
|
79960
80168
|
if (poster) {
|
|
79961
80169
|
await flushBilled(
|
|
79962
80170
|
poster,
|
|
@@ -80227,14 +80435,14 @@ var rerunCommand = defineCommand143({
|
|
|
80227
80435
|
});
|
|
80228
80436
|
|
|
80229
80437
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
80230
|
-
import { mkdir as
|
|
80231
|
-
import
|
|
80438
|
+
import { mkdir as mkdir19, readFile as readFile37, writeFile as writeFile27 } from "fs/promises";
|
|
80439
|
+
import path47 from "path";
|
|
80232
80440
|
import { defineCommand as defineCommand146 } from "citty";
|
|
80233
80441
|
|
|
80234
80442
|
// src/commands/canvas/creative-limit.ts
|
|
80235
80443
|
import { existsSync as existsSync8 } from "fs";
|
|
80236
|
-
import { mkdir as
|
|
80237
|
-
import
|
|
80444
|
+
import { mkdir as mkdir17, readdir as readdir15, writeFile as writeFile25 } from "fs/promises";
|
|
80445
|
+
import path42 from "path";
|
|
80238
80446
|
var NEW_CREATIVES_PER_CHAT = 10;
|
|
80239
80447
|
function claimNewCreative(made, slug) {
|
|
80240
80448
|
if (made.includes(slug)) return { allowed: true, count: made.length };
|
|
@@ -80242,7 +80450,7 @@ function claimNewCreative(made, slug) {
|
|
|
80242
80450
|
return { allowed: true, count: made.length + 1 };
|
|
80243
80451
|
}
|
|
80244
80452
|
function chatDir(canvasRoot, chatId) {
|
|
80245
|
-
return
|
|
80453
|
+
return path42.join(canvasRoot, ".creative-limit", (chatId ?? "no-chat").replace(/[^A-Za-z0-9_-]/g, "_"));
|
|
80246
80454
|
}
|
|
80247
80455
|
async function madeInChat(dir) {
|
|
80248
80456
|
return readdir15(dir).catch(() => []);
|
|
@@ -80254,8 +80462,8 @@ async function claimNewCreativeInWorkspace(canvasRoot, slug, chatId, definitionP
|
|
|
80254
80462
|
const claim = claimNewCreative(made, slug);
|
|
80255
80463
|
if (claim.allowed && !made.includes(slug)) {
|
|
80256
80464
|
try {
|
|
80257
|
-
await
|
|
80258
|
-
await
|
|
80465
|
+
await mkdir17(dir, { recursive: true });
|
|
80466
|
+
await writeFile25(path42.join(dir, slug), "", { flag: "wx" });
|
|
80259
80467
|
} catch {
|
|
80260
80468
|
}
|
|
80261
80469
|
}
|
|
@@ -80276,7 +80484,7 @@ async function enforceNewCreativeLimit(slug, canvasPath) {
|
|
|
80276
80484
|
canvasOutputsRoot(canvasPath),
|
|
80277
80485
|
slug,
|
|
80278
80486
|
optionalChatId() ?? null,
|
|
80279
|
-
|
|
80487
|
+
path42.join(path42.dirname(path42.resolve(canvasPath)), "_definition.md")
|
|
80280
80488
|
);
|
|
80281
80489
|
if (claim.allowed) return;
|
|
80282
80490
|
process.stderr.write(`${JSON.stringify(creativeLimitRefusal(slug), null, 2)}
|
|
@@ -80285,22 +80493,22 @@ async function enforceNewCreativeLimit(slug, canvasPath) {
|
|
|
80285
80493
|
}
|
|
80286
80494
|
|
|
80287
80495
|
// src/commands/canvas/scaffold-common.ts
|
|
80288
|
-
import { access, readFile as
|
|
80496
|
+
import { access, readFile as readFile34 } from "fs/promises";
|
|
80289
80497
|
|
|
80290
80498
|
// src/commands/canvas/composition-path.ts
|
|
80291
80499
|
import { existsSync as existsSync9 } from "fs";
|
|
80292
|
-
import
|
|
80500
|
+
import path43 from "path";
|
|
80293
80501
|
function resolveShippedCanvasDir(name, startDir, exists3 = existsSync9, maxDepth = 8) {
|
|
80294
|
-
const rel =
|
|
80502
|
+
const rel = path43.join("canvas", name);
|
|
80295
80503
|
let dir = startDir;
|
|
80296
80504
|
for (let i = 0; i < maxDepth; i++) {
|
|
80297
|
-
const candidate =
|
|
80298
|
-
if (exists3(
|
|
80299
|
-
const parent =
|
|
80505
|
+
const candidate = path43.join(dir, rel);
|
|
80506
|
+
if (exists3(path43.join(candidate, "meta.json"))) return candidate;
|
|
80507
|
+
const parent = path43.dirname(dir);
|
|
80300
80508
|
if (parent === dir) break;
|
|
80301
80509
|
dir = parent;
|
|
80302
80510
|
}
|
|
80303
|
-
return
|
|
80511
|
+
return path43.resolve(startDir, "../../../", rel);
|
|
80304
80512
|
}
|
|
80305
80513
|
|
|
80306
80514
|
// src/commands/canvas/scaffold-common.ts
|
|
@@ -80317,7 +80525,7 @@ function resolveModel(kind, requested, fallback) {
|
|
|
80317
80525
|
}
|
|
80318
80526
|
async function loadAssetText(ref2, label) {
|
|
80319
80527
|
const r = ref2;
|
|
80320
|
-
if (typeof r?.path === "string") return
|
|
80528
|
+
if (typeof r?.path === "string") return readFile34(r.path, "utf8");
|
|
80321
80529
|
if (typeof r?.url === "string") {
|
|
80322
80530
|
const res = await fetch(r.url);
|
|
80323
80531
|
if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
|
|
@@ -80346,16 +80554,16 @@ function fileExists(target2) {
|
|
|
80346
80554
|
}
|
|
80347
80555
|
|
|
80348
80556
|
// src/commands/canvas/scaffold-static-ad-paths.ts
|
|
80349
|
-
import
|
|
80557
|
+
import path44 from "path";
|
|
80350
80558
|
function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd(), slug) {
|
|
80351
80559
|
const file = rawFile.trim();
|
|
80352
80560
|
const imageIsUrl = /^https?:\/\//i.test(file);
|
|
80353
|
-
const imageSource = imageIsUrl ? file :
|
|
80354
|
-
const outPath = out ?
|
|
80355
|
-
const blueprintPath2 =
|
|
80356
|
-
const creativeDir3 = slug ?
|
|
80357
|
-
const definitionPath = creativeDir3 ?
|
|
80358
|
-
const referencesDir = creativeDir3 ?
|
|
80561
|
+
const imageSource = imageIsUrl ? file : path44.resolve(cwd, file);
|
|
80562
|
+
const outPath = out ? path44.resolve(cwd, out) : slug ? path44.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path44.join(cwd, "static-ad.canvas.json") : path44.join(path44.dirname(imageSource), "static-ad.canvas.json");
|
|
80563
|
+
const blueprintPath2 = path44.join(path44.dirname(outPath), "prompt.json");
|
|
80564
|
+
const creativeDir3 = slug ? path44.dirname(outPath) : null;
|
|
80565
|
+
const definitionPath = creativeDir3 ? path44.join(creativeDir3, "_definition.md") : null;
|
|
80566
|
+
const referencesDir = creativeDir3 ? path44.join(creativeDir3, "references") : null;
|
|
80359
80567
|
return { imageIsUrl, imageSource, outPath, blueprintPath: blueprintPath2, creativeDir: creativeDir3, definitionPath, referencesDir };
|
|
80360
80568
|
}
|
|
80361
80569
|
var SCAFFOLD_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
@@ -80365,8 +80573,8 @@ function isValidScaffoldSlug(slug) {
|
|
|
80365
80573
|
}
|
|
80366
80574
|
|
|
80367
80575
|
// src/commands/canvas/sync-definition.ts
|
|
80368
|
-
import { readdir as readdir16, readFile as
|
|
80369
|
-
import
|
|
80576
|
+
import { readdir as readdir16, readFile as readFile35, stat as stat5 } from "fs/promises";
|
|
80577
|
+
import path45 from "path";
|
|
80370
80578
|
import { parseDocument as parseDocument3 } from "yaml";
|
|
80371
80579
|
import { defineCommand as defineCommand144 } from "citty";
|
|
80372
80580
|
|
|
@@ -80424,14 +80632,14 @@ function buildDefinitionSyncPayload(input, graph, chatId) {
|
|
|
80424
80632
|
}
|
|
80425
80633
|
async function readDefinitionSourceFields(canvasPath) {
|
|
80426
80634
|
try {
|
|
80427
|
-
return parseDefinitionSourceFields(await
|
|
80635
|
+
return parseDefinitionSourceFields(await readFile35(path45.join(path45.dirname(canvasPath), "_definition.md"), "utf8"));
|
|
80428
80636
|
} catch {
|
|
80429
80637
|
return {};
|
|
80430
80638
|
}
|
|
80431
80639
|
}
|
|
80432
80640
|
async function readDefinitionTitle(canvasPath) {
|
|
80433
80641
|
try {
|
|
80434
|
-
const raw = await
|
|
80642
|
+
const raw = await readFile35(path45.join(path45.dirname(canvasPath), "_definition.md"), "utf8");
|
|
80435
80643
|
const match = raw.match(/^---\r?\n([\s\S]*?\r?\n)?---/);
|
|
80436
80644
|
const doc = match ? parseDocument3(match[1] ?? "") : null;
|
|
80437
80645
|
const title = doc && doc.errors.length === 0 ? doc.get("title") : void 0;
|
|
@@ -80442,7 +80650,7 @@ async function readDefinitionTitle(canvasPath) {
|
|
|
80442
80650
|
}
|
|
80443
80651
|
async function readDefinitionStrategyFields(canvasPath) {
|
|
80444
80652
|
try {
|
|
80445
|
-
return parseDefinitionStrategyFields(await
|
|
80653
|
+
return parseDefinitionStrategyFields(await readFile35(path45.join(path45.dirname(canvasPath), "_definition.md"), "utf8"));
|
|
80446
80654
|
} catch {
|
|
80447
80655
|
return {};
|
|
80448
80656
|
}
|
|
@@ -80465,15 +80673,15 @@ async function syncCreativeDefinitionBestEffort(input) {
|
|
|
80465
80673
|
}
|
|
80466
80674
|
}
|
|
80467
80675
|
async function resolveCanvasPath(inputPath) {
|
|
80468
|
-
const resolved =
|
|
80676
|
+
const resolved = path45.resolve(inputPath);
|
|
80469
80677
|
let dir = resolved;
|
|
80470
80678
|
try {
|
|
80471
80679
|
if ((await stat5(resolved)).isFile()) {
|
|
80472
80680
|
if (resolved.endsWith(".canvas.json")) return resolved;
|
|
80473
|
-
dir =
|
|
80681
|
+
dir = path45.dirname(resolved);
|
|
80474
80682
|
}
|
|
80475
80683
|
} catch {
|
|
80476
|
-
dir = resolved.endsWith(".canvas.json") ?
|
|
80684
|
+
dir = resolved.endsWith(".canvas.json") ? path45.dirname(resolved) : resolved;
|
|
80477
80685
|
}
|
|
80478
80686
|
let entries;
|
|
80479
80687
|
try {
|
|
@@ -80484,7 +80692,7 @@ async function resolveCanvasPath(inputPath) {
|
|
|
80484
80692
|
const canvases = entries.filter((name) => name.endsWith(".canvas.json"));
|
|
80485
80693
|
const slug = creativeSlugFromCanvasPath(`${dir}/x/`);
|
|
80486
80694
|
const chosen = (slug ? canvases.find((name) => name === `${slug}.canvas.json`) : void 0) ?? canvases[0];
|
|
80487
|
-
return chosen ?
|
|
80695
|
+
return chosen ? path45.join(dir, chosen) : null;
|
|
80488
80696
|
}
|
|
80489
80697
|
var syncDefinitionCommand = defineCommand144({
|
|
80490
80698
|
meta: {
|
|
@@ -80760,32 +80968,32 @@ function mergeReferenceIntoBlueprint(blueprint, reference) {
|
|
|
80760
80968
|
}
|
|
80761
80969
|
|
|
80762
80970
|
// src/commands/winning-ads/fit.ts
|
|
80763
|
-
import { mkdir as
|
|
80764
|
-
import
|
|
80971
|
+
import { mkdir as mkdir18, readdir as readdir17, readFile as readFile36, writeFile as writeFile26 } from "fs/promises";
|
|
80972
|
+
import path46 from "path";
|
|
80765
80973
|
import { defineCommand as defineCommand145 } from "citty";
|
|
80766
80974
|
import sharp4 from "sharp";
|
|
80767
|
-
import { z as
|
|
80768
|
-
var Layers =
|
|
80769
|
-
disguise:
|
|
80770
|
-
voice:
|
|
80771
|
-
trigger:
|
|
80772
|
-
attention:
|
|
80773
|
-
logic:
|
|
80975
|
+
import { z as z78 } from "zod";
|
|
80976
|
+
var Layers = z78.object({
|
|
80977
|
+
disguise: z78.string().optional(),
|
|
80978
|
+
voice: z78.string().optional(),
|
|
80979
|
+
trigger: z78.string().optional(),
|
|
80980
|
+
attention: z78.string().optional(),
|
|
80981
|
+
logic: z78.string().optional()
|
|
80774
80982
|
}).strip();
|
|
80775
|
-
var FitAnswerSchema =
|
|
80983
|
+
var FitAnswerSchema = z78.object({
|
|
80776
80984
|
why_it_wins: Layers.default({}),
|
|
80777
|
-
load_bearing:
|
|
80778
|
-
|
|
80779
|
-
element:
|
|
80780
|
-
needs:
|
|
80781
|
-
have:
|
|
80782
|
-
ours:
|
|
80985
|
+
load_bearing: z78.array(
|
|
80986
|
+
z78.object({
|
|
80987
|
+
element: z78.string().trim().min(1),
|
|
80988
|
+
needs: z78.string().optional(),
|
|
80989
|
+
have: z78.enum(["have", "adapt", "missing"]),
|
|
80990
|
+
ours: z78.string().optional()
|
|
80783
80991
|
}).strip()
|
|
80784
80992
|
).default([]),
|
|
80785
|
-
verdict:
|
|
80786
|
-
transfer_note:
|
|
80787
|
-
brand_presence:
|
|
80788
|
-
recognition:
|
|
80993
|
+
verdict: z78.enum(["use", "adapt", "reject"]),
|
|
80994
|
+
transfer_note: z78.string().optional(),
|
|
80995
|
+
brand_presence: z78.enum(["native", "standard"]).optional(),
|
|
80996
|
+
recognition: z78.string().optional()
|
|
80789
80997
|
});
|
|
80790
80998
|
function buildFitPrompt(input) {
|
|
80791
80999
|
const lib = input.library;
|
|
@@ -80846,33 +81054,33 @@ var MAX_DOC_CHARS = 1500;
|
|
|
80846
81054
|
var MAX_ENTRY_CHARS = 400;
|
|
80847
81055
|
var KNOWLEDGE_COLLECTIONS = ["offers", "products", "personas"];
|
|
80848
81056
|
function readText(file) {
|
|
80849
|
-
return
|
|
81057
|
+
return readFile36(file, "utf8").catch(() => null);
|
|
80850
81058
|
}
|
|
80851
81059
|
async function readKnowledge(root) {
|
|
80852
81060
|
const parts = [];
|
|
80853
|
-
const company = await readText(
|
|
81061
|
+
const company = await readText(path46.join(root, "COMPANY.md"));
|
|
80854
81062
|
if (company) parts.push(`COMPANY.md:
|
|
80855
81063
|
${company.slice(0, MAX_DOC_CHARS)}`);
|
|
80856
|
-
const brand = await readText(
|
|
81064
|
+
const brand = await readText(path46.join(root, "src", "brand", "BRAND.md"));
|
|
80857
81065
|
if (brand) parts.push(`BRAND.md (start):
|
|
80858
81066
|
${brand.slice(0, MAX_DOC_CHARS)}`);
|
|
80859
81067
|
for (const collection of KNOWLEDGE_COLLECTIONS) {
|
|
80860
|
-
const dir =
|
|
81068
|
+
const dir = path46.join(root, "src", "content", collection);
|
|
80861
81069
|
const files = (await readdir17(dir, { recursive: true }).catch(() => [])).filter(
|
|
80862
81070
|
(f) => f.endsWith(".md")
|
|
80863
81071
|
);
|
|
80864
81072
|
for (const file of files.sort().slice(0, 12)) {
|
|
80865
|
-
const text4 = await readText(
|
|
81073
|
+
const text4 = await readText(path46.join(dir, file));
|
|
80866
81074
|
if (text4) parts.push(`${collection}/${file}:
|
|
80867
81075
|
${text4.slice(0, MAX_ENTRY_CHARS)}`);
|
|
80868
81076
|
}
|
|
80869
81077
|
}
|
|
80870
81078
|
return parts.join("\n\n");
|
|
80871
81079
|
}
|
|
80872
|
-
var ReviewSchema =
|
|
80873
|
-
reviewText:
|
|
80874
|
-
sourceType:
|
|
80875
|
-
rating:
|
|
81080
|
+
var ReviewSchema = z78.object({
|
|
81081
|
+
reviewText: z78.string(),
|
|
81082
|
+
sourceType: z78.string().optional(),
|
|
81083
|
+
rating: z78.number().optional()
|
|
80876
81084
|
});
|
|
80877
81085
|
function reviewsForFit(data) {
|
|
80878
81086
|
const list2 = Array.isArray(data) ? data : [];
|
|
@@ -80894,7 +81102,7 @@ async function readReviews() {
|
|
|
80894
81102
|
}
|
|
80895
81103
|
}
|
|
80896
81104
|
function fitCachePath(root, platform, adId) {
|
|
80897
|
-
return
|
|
81105
|
+
return path46.join(root, "canvas", ".cache", "fit", `${platform}-${adId.replace(/[^a-zA-Z0-9_-]/g, "_")}.json`);
|
|
80898
81106
|
}
|
|
80899
81107
|
async function readCachedFit(root, platform, adId) {
|
|
80900
81108
|
const raw = await readText(fitCachePath(root, platform, adId));
|
|
@@ -80949,8 +81157,8 @@ async function fitOnce(root, platform, adId, ask) {
|
|
|
80949
81157
|
const fit = await ask();
|
|
80950
81158
|
if (!fit) return null;
|
|
80951
81159
|
const file = fitCachePath(root, platform, adId);
|
|
80952
|
-
await
|
|
80953
|
-
await
|
|
81160
|
+
await mkdir18(path46.dirname(file), { recursive: true });
|
|
81161
|
+
await writeFile26(file, `${JSON.stringify(fit, null, 2)}
|
|
80954
81162
|
`, "utf8");
|
|
80955
81163
|
return fit;
|
|
80956
81164
|
}
|
|
@@ -80988,7 +81196,7 @@ async function fitReference(root, platform, adId) {
|
|
|
80988
81196
|
const prompt = buildFitPrompt({ library: libraryRead(ad), knowledge: knowledge2 });
|
|
80989
81197
|
const spent = [];
|
|
80990
81198
|
const answer = await askFit(jpeg, prompt, (u) => spent.push(u));
|
|
80991
|
-
await holdUnbilledSpend(
|
|
81199
|
+
await holdUnbilledSpend(path46.join(root, "canvas", ".cache"), spent);
|
|
80992
81200
|
return answer;
|
|
80993
81201
|
});
|
|
80994
81202
|
if (fit) return { ok: true, fit };
|
|
@@ -81041,7 +81249,7 @@ var fitCommand = defineCommand145({
|
|
|
81041
81249
|
|
|
81042
81250
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
81043
81251
|
function toCanvasRelative2(canvasDir, file) {
|
|
81044
|
-
const rel =
|
|
81252
|
+
const rel = path47.relative(canvasDir, file).split(path47.sep).join("/");
|
|
81045
81253
|
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
81046
81254
|
}
|
|
81047
81255
|
async function uploadSourceAsReference(source, isUrl, client) {
|
|
@@ -81053,7 +81261,7 @@ async function uploadSourceAsReference(source, isUrl, client) {
|
|
|
81053
81261
|
throw new Error(`failed to download source image (${e instanceof Error ? e.message : String(e)})`);
|
|
81054
81262
|
}
|
|
81055
81263
|
} else {
|
|
81056
|
-
bytes = await
|
|
81264
|
+
bytes = await readFile37(source);
|
|
81057
81265
|
}
|
|
81058
81266
|
const safe = await toModelSafeImage(bytes);
|
|
81059
81267
|
const sha2562 = sha256Hex(safe.bytes);
|
|
@@ -81333,25 +81541,25 @@ async function lookupReference(args8) {
|
|
|
81333
81541
|
}
|
|
81334
81542
|
async function scaffoldFromSpec(args8, slug) {
|
|
81335
81543
|
if (!slug) return fail4("missing_slug", "--spec needs --slug: an ad started from a spec is a creative.");
|
|
81336
|
-
const specPath =
|
|
81544
|
+
const specPath = path47.resolve(process.cwd(), String(args8.spec));
|
|
81337
81545
|
let started;
|
|
81338
81546
|
try {
|
|
81339
|
-
started = readAdSpec(JSON.parse(await
|
|
81547
|
+
started = readAdSpec(JSON.parse(await readFile37(specPath, "utf8")));
|
|
81340
81548
|
} catch (e) {
|
|
81341
81549
|
return fail4("invalid_spec", `${specPath} is not an ad spec: ${e instanceof Error ? e.message : String(e)}`);
|
|
81342
81550
|
}
|
|
81343
|
-
const outPath =
|
|
81344
|
-
const dir =
|
|
81345
|
-
const blueprintPath2 =
|
|
81346
|
-
const definitionPath =
|
|
81551
|
+
const outPath = path47.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`);
|
|
81552
|
+
const dir = path47.dirname(outPath);
|
|
81553
|
+
const blueprintPath2 = path47.join(dir, "prompt.json");
|
|
81554
|
+
const definitionPath = path47.join(dir, "_definition.md");
|
|
81347
81555
|
await enforceNewCreativeLimit(slug, outPath);
|
|
81348
|
-
const existingDefinition = await fileExists(definitionPath) ? await
|
|
81556
|
+
const existingDefinition = await fileExists(definitionPath) ? await readFile37(definitionPath, "utf8") : null;
|
|
81349
81557
|
const who = await scaffoldViewer(args8, existingDefinition, liveViewerLookups(process.cwd()).adSetViewer);
|
|
81350
81558
|
if (!who.ok) return fail4("invalid_viewer", who.message);
|
|
81351
|
-
await
|
|
81559
|
+
await mkdir19(dir, { recursive: true });
|
|
81352
81560
|
const workspaceRoot = await findBrandRoot(dir) ?? process.cwd();
|
|
81353
81561
|
const kit = await readStaticBrandKit(workspaceRoot);
|
|
81354
|
-
const market = who.viewer?.market ?? marketFromCompanyDoc(await
|
|
81562
|
+
const market = who.viewer?.market ?? marketFromCompanyDoc(await readFile37(path47.join(workspaceRoot, "COMPANY.md"), "utf8").catch(() => null));
|
|
81355
81563
|
const brand = brandBlock(kit);
|
|
81356
81564
|
const platform = resolvePlatform(args8.platform ? String(args8.platform) : void 0);
|
|
81357
81565
|
const placementsResult = resolvePlacements(args8.placements ? String(args8.placements) : void 0, platform);
|
|
@@ -81373,18 +81581,18 @@ async function scaffoldFromSpec(args8, slug) {
|
|
|
81373
81581
|
logo: Boolean(kit.logo),
|
|
81374
81582
|
...market ? { viewer: { ...started.viewer ?? { who: "the people this ad is for" }, market } } : {}
|
|
81375
81583
|
};
|
|
81376
|
-
await
|
|
81584
|
+
await writeFile27(blueprintPath2, `${JSON.stringify(spec, null, 2)}
|
|
81377
81585
|
`, "utf8");
|
|
81378
81586
|
const canvas = scaffoldStaticAd({}, [], { ...opts, spec });
|
|
81379
81587
|
const validation = await validateScaffoldedStaticAd(canvas, dir);
|
|
81380
81588
|
if (validation.blocking.length > 0) return fail4("validation", validation.blocking.map((i) => i.message).join("; "));
|
|
81381
|
-
await
|
|
81589
|
+
await writeFile27(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
81382
81590
|
`, "utf8");
|
|
81383
81591
|
const title = args8.title ? String(args8.title) : titleFromSlug(slug);
|
|
81384
81592
|
const recorded = who.record ? { targetAdSet: who.targetAdSet, viewer: who.viewer } : {};
|
|
81385
81593
|
if (existingDefinition === null) {
|
|
81386
81594
|
const formats = resolveFormatList(report.output_formats);
|
|
81387
|
-
await
|
|
81595
|
+
await writeFile27(definitionPath, buildCreativeDefinition({ title, kind: "static", platform, formats, ...recorded }), "utf8");
|
|
81388
81596
|
}
|
|
81389
81597
|
await syncCreativeDefinitionBestEffort({
|
|
81390
81598
|
slug,
|
|
@@ -81498,14 +81706,14 @@ var scaffoldStaticAdCommand = defineCommand146({
|
|
|
81498
81706
|
}
|
|
81499
81707
|
const { imageIsUrl, imageSource, outPath, blueprintPath: blueprintPath2, definitionPath, referencesDir } = resolveScaffoldStaticAdPaths(sourceFile, args8.out ? String(args8.out) : void 0, process.cwd(), slug);
|
|
81500
81708
|
if (slug) await enforceNewCreativeLimit(slug, outPath);
|
|
81501
|
-
const existingDefinition = definitionPath && await fileExists(definitionPath) ? await
|
|
81709
|
+
const existingDefinition = definitionPath && await fileExists(definitionPath) ? await readFile37(definitionPath, "utf8") : null;
|
|
81502
81710
|
const who = await scaffoldViewer(args8, existingDefinition, liveViewerLookups(process.cwd()).adSetViewer);
|
|
81503
81711
|
if (!who.ok) return fail4("invalid_viewer", who.message);
|
|
81504
81712
|
const fitRoot = findWorkspaceRoot(process.cwd()) ?? process.cwd();
|
|
81505
81713
|
const fit = reference || lookup.status === "unavailable" ? await readCachedFit(fitRoot, reference?.platform ?? (lookup.status === "unavailable" ? lookup.platform : "meta"), reference?.ad_id ?? (lookup.status === "unavailable" ? lookup.adId : "")) : null;
|
|
81506
81714
|
const fitProblems = fit ? fitHints(fit, reference?.ad_id ?? "").filter((h) => h.startsWith("Pick another reference")) : [];
|
|
81507
81715
|
if (fitProblems.length > 0) return fail4("concept_does_not_fit", fitProblems.join(" "));
|
|
81508
|
-
await
|
|
81716
|
+
await mkdir19(path47.dirname(outPath), { recursive: true });
|
|
81509
81717
|
let models;
|
|
81510
81718
|
try {
|
|
81511
81719
|
models = resolveModels(args8);
|
|
@@ -81539,10 +81747,10 @@ var scaffoldStaticAdCommand = defineCommand146({
|
|
|
81539
81747
|
const persona = existingDefinition?.match(/^persona:\s*"?([^"\n]+)"?\s*$/m)?.[1];
|
|
81540
81748
|
const elements = castAvatarsOnStaticElements(vision.elements, cast.avatars, persona);
|
|
81541
81749
|
const uncast = staticAvatarsNotCast(elements, cast.handles);
|
|
81542
|
-
const canvasDir =
|
|
81750
|
+
const canvasDir = path47.dirname(outPath);
|
|
81543
81751
|
const workspaceRoot = await findBrandRoot(canvasDir) ?? process.cwd();
|
|
81544
81752
|
const kit = await readStaticBrandKit(workspaceRoot);
|
|
81545
|
-
const market = who.viewer?.market ?? marketFromCompanyDoc(await
|
|
81753
|
+
const market = who.viewer?.market ?? marketFromCompanyDoc(await readFile37(path47.join(workspaceRoot, "COMPANY.md"), "utf8").catch(() => null));
|
|
81546
81754
|
const brand = brandBlock(kit);
|
|
81547
81755
|
let canvasImagePath = imageSource;
|
|
81548
81756
|
let canvasImageIsUrl = imageIsUrl;
|
|
@@ -81592,7 +81800,7 @@ var scaffoldStaticAdCommand = defineCommand146({
|
|
|
81592
81800
|
...fit ? { fit: conceptFromFit(fit, reference?.ad_id ?? "") } : {},
|
|
81593
81801
|
...market ? { market } : {}
|
|
81594
81802
|
});
|
|
81595
|
-
await
|
|
81803
|
+
await writeFile27(blueprintPath2, `${JSON.stringify(spec, null, 2)}
|
|
81596
81804
|
`, "utf8");
|
|
81597
81805
|
conceptNotes = conceptProblems(spec).map(
|
|
81598
81806
|
(p) => `CONCEPT: ${p} Then \`baker canvas run\`; until then it refuses to render.`
|
|
@@ -81606,7 +81814,7 @@ var scaffoldStaticAdCommand = defineCommand146({
|
|
|
81606
81814
|
} catch (e) {
|
|
81607
81815
|
return fail4("scaffold", e instanceof Error ? e.message : String(e));
|
|
81608
81816
|
}
|
|
81609
|
-
const validation = await validateScaffoldedStaticAd(canvas,
|
|
81817
|
+
const validation = await validateScaffoldedStaticAd(canvas, path47.dirname(outPath));
|
|
81610
81818
|
if (validation.blocking.length > 0) {
|
|
81611
81819
|
process.stderr.write(
|
|
81612
81820
|
`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.blocking } }, null, 2)}
|
|
@@ -81614,7 +81822,7 @@ var scaffoldStaticAdCommand = defineCommand146({
|
|
|
81614
81822
|
);
|
|
81615
81823
|
process.exit(2);
|
|
81616
81824
|
}
|
|
81617
|
-
await
|
|
81825
|
+
await writeFile27(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
81618
81826
|
`, "utf8");
|
|
81619
81827
|
const sourceFields = reference ? {
|
|
81620
81828
|
sourceAdId: reference.ad_id,
|
|
@@ -81627,10 +81835,10 @@ var scaffoldStaticAdCommand = defineCommand146({
|
|
|
81627
81835
|
} : {};
|
|
81628
81836
|
const recorded = who.record ? { targetAdSet: who.targetAdSet, viewer: who.viewer } : {};
|
|
81629
81837
|
if (definitionPath && existingDefinition !== null && who.record) {
|
|
81630
|
-
await
|
|
81838
|
+
await writeFile27(definitionPath, setDefinitionViewer(existingDefinition, recorded), "utf8");
|
|
81631
81839
|
}
|
|
81632
81840
|
if (definitionPath && existingDefinition === null) {
|
|
81633
|
-
await
|
|
81841
|
+
await writeFile27(
|
|
81634
81842
|
definitionPath,
|
|
81635
81843
|
buildCreativeDefinition({
|
|
81636
81844
|
title: args8.title ? String(args8.title) : titleFromSlug(slug ?? ""),
|
|
@@ -81703,7 +81911,7 @@ var scaffoldStaticAdCommand = defineCommand146({
|
|
|
81703
81911
|
run_estimated_credits: validation.estimatedCredits
|
|
81704
81912
|
},
|
|
81705
81913
|
checklist: {
|
|
81706
|
-
edit_prompt: `Edit ${
|
|
81914
|
+
edit_prompt: `Edit ${path47.basename(blueprintPath2)}: it is the ad spec, read from your image, to rewrite into the ad you want. \`viewer\` (who sees it and what they already know), \`message\` (the one thing it tells them), \`copy\` (each line, \`in_scene\` or \`designed\`), \`scene\`, \`references\` (one picture each, by role), \`colors\` (each tied to an object, from the brand) and an optional \`direction\`. You never write a model prompt: every render prompt is compiled from this file and shown on its node.${reference ? " Its `reference` block is the library's read of the source ad (container, layout, mechanism, offer, text blocks): keep it as the account of why the ad won, and rebuild it for our brand." : ""}`,
|
|
81707
81915
|
mechanism: report.winning_mechanisms,
|
|
81708
81916
|
rebuild_the_mechanism: "`mechanism` is WHY the source ad works. Rebuild each device for our brand in prompt.json's copy and scene (a rhyme stays a rhyme in our words, a before/after stays a before/after of our product); never copy the source's literal copy.",
|
|
81709
81917
|
assets_to_supply: report.elements,
|
|
@@ -81765,25 +81973,25 @@ function clientLogoMissNote(decision, domain) {
|
|
|
81765
81973
|
}
|
|
81766
81974
|
|
|
81767
81975
|
// src/commands/canvas/scaffold-ad.ts
|
|
81768
|
-
import { copyFile, cp, mkdir as mkdir20, readFile as readFile39, stat as stat6, writeFile as
|
|
81976
|
+
import { copyFile, cp, mkdir as mkdir20, readFile as readFile39, stat as stat6, writeFile as writeFile29 } from "fs/promises";
|
|
81769
81977
|
import path49 from "path";
|
|
81770
81978
|
import { defineCommand as defineCommand147 } from "citty";
|
|
81771
81979
|
|
|
81772
81980
|
// src/engine/scaffold/ad-spec.ts
|
|
81773
|
-
import { z as
|
|
81981
|
+
import { z as z79 } from "zod";
|
|
81774
81982
|
var oneClause = (line) => (line.match(/[.!?](\s|$)/g) ?? []).length <= 1;
|
|
81775
81983
|
var isSpeech = (line) => new RegExp("\\p{L}\\p{L}", "u").test(line);
|
|
81776
|
-
var Beat =
|
|
81984
|
+
var Beat = z79.object({
|
|
81777
81985
|
/**
|
|
81778
81986
|
* The line the voice says in this beat — ONE clause, ending in its own
|
|
81779
81987
|
* punctuation. It becomes the caption card verbatim.
|
|
81780
81988
|
*/
|
|
81781
|
-
say:
|
|
81989
|
+
say: z79.string().min(1).refine(
|
|
81782
81990
|
isSpeech,
|
|
81783
81991
|
"a beat's line has to be words someone says \u2014 a dot or a dash is not a line, and an ad with no voiceover is not supported yet"
|
|
81784
81992
|
).refine(oneClause, "a beat is one clause: split a second sentence into its own beat"),
|
|
81785
81993
|
/** What is on screen while it is said, as a shot brief for the video model. */
|
|
81786
|
-
show:
|
|
81994
|
+
show: z79.string().min(1),
|
|
81787
81995
|
/**
|
|
81788
81996
|
* What this shot SOUNDS like — one effect, in words. Usually absent.
|
|
81789
81997
|
*
|
|
@@ -81800,7 +82008,7 @@ var Beat = z77.object({
|
|
|
81800
82008
|
* Describe the SOUND, not the picture: "a cordless drill tightening a bolt", not "the
|
|
81801
82009
|
* fitter works on the rail".
|
|
81802
82010
|
*/
|
|
81803
|
-
sound:
|
|
82011
|
+
sound: z79.string().min(1).max(200).optional(),
|
|
81804
82012
|
/**
|
|
81805
82013
|
* Whether this beat's subject is talking to camera.
|
|
81806
82014
|
*
|
|
@@ -81814,36 +82022,36 @@ var Beat = z77.object({
|
|
|
81814
82022
|
* way through" produced an ad with her face on screen and a narrator cast from a prose
|
|
81815
82023
|
* description reading her lines, because nothing set this flag.
|
|
81816
82024
|
*/
|
|
81817
|
-
on_camera:
|
|
82025
|
+
on_camera: z79.boolean().optional(),
|
|
81818
82026
|
/**
|
|
81819
82027
|
* Whether the ad's recurring person is in this shot. Defaults to true whenever the
|
|
81820
82028
|
* spec declares a `cast`, because an ad is usually about one person and defaulting
|
|
81821
82029
|
* the other way is how continuity gets forgotten. Set false for a beat they are not
|
|
81822
82030
|
* in — a fitter on a roof, a product close-up.
|
|
81823
82031
|
*/
|
|
81824
|
-
cast:
|
|
82032
|
+
cast: z79.boolean().optional()
|
|
81825
82033
|
});
|
|
81826
|
-
var AdSpec =
|
|
81827
|
-
format:
|
|
81828
|
-
aspect_ratio:
|
|
81829
|
-
resolution:
|
|
82034
|
+
var AdSpec = z79.object({
|
|
82035
|
+
format: z79.object({
|
|
82036
|
+
aspect_ratio: z79.string().default("9:16"),
|
|
82037
|
+
resolution: z79.string().default("720p")
|
|
81830
82038
|
}).default({ aspect_ratio: "9:16", resolution: "720p" }),
|
|
81831
|
-
brand:
|
|
81832
|
-
name:
|
|
82039
|
+
brand: z79.object({
|
|
82040
|
+
name: z79.string().optional(),
|
|
81833
82041
|
/** Figures to lift typographically — the one gold number every ad has. */
|
|
81834
|
-
highlight:
|
|
82042
|
+
highlight: z79.array(z79.string()).optional(),
|
|
81835
82043
|
/**
|
|
81836
82044
|
* Brand hex colours, most important first. They dress the brand plate and
|
|
81837
82045
|
* the type — never the scenes: pushing hex into a shot brief makes the
|
|
81838
82046
|
* model paint the whole frame that colour, which reads as a filter.
|
|
81839
82047
|
*/
|
|
81840
|
-
palette:
|
|
82048
|
+
palette: z79.array(z79.string()).optional(),
|
|
81841
82049
|
/** Repo path to the brand mark, so the CTA carries it instead of a name in a font. */
|
|
81842
|
-
logo:
|
|
82050
|
+
logo: z79.string().optional()
|
|
81843
82051
|
}).optional(),
|
|
81844
|
-
voice:
|
|
81845
|
-
description:
|
|
81846
|
-
language:
|
|
82052
|
+
voice: z79.object({
|
|
82053
|
+
description: z79.string().min(1),
|
|
82054
|
+
language: z79.string().optional()
|
|
81847
82055
|
}).optional(),
|
|
81848
82056
|
/**
|
|
81849
82057
|
* Where the ad is set, as a place a model can picture: "Spain", "Mexico",
|
|
@@ -81855,7 +82063,7 @@ var AdSpec = z77.object({
|
|
|
81855
82063
|
* houses — sash windows, slate roofs, English suburbs — in every single frame,
|
|
81856
82064
|
* from briefs that were otherwise perfectly good.
|
|
81857
82065
|
*/
|
|
81858
|
-
market:
|
|
82066
|
+
market: z79.string().min(1).optional(),
|
|
81859
82067
|
/**
|
|
81860
82068
|
* The one person the ad is about.
|
|
81861
82069
|
*
|
|
@@ -81865,7 +82073,7 @@ var AdSpec = z77.object({
|
|
|
81865
82073
|
* with five different men playing one customer, which reads as a stock-footage
|
|
81866
82074
|
* collage rather than a story.
|
|
81867
82075
|
*/
|
|
81868
|
-
cast:
|
|
82076
|
+
cast: z79.object({
|
|
81869
82077
|
/**
|
|
81870
82078
|
* A cast Avatar's handle — `baker avatars list` shows who there is.
|
|
81871
82079
|
*
|
|
@@ -81875,9 +82083,9 @@ var AdSpec = z77.object({
|
|
|
81875
82083
|
* rather than re-written per spec — re-phrasing it per render is the other reason
|
|
81876
82084
|
* a face drifts across a set.
|
|
81877
82085
|
*/
|
|
81878
|
-
avatar:
|
|
82086
|
+
avatar: z79.string().min(1).optional(),
|
|
81879
82087
|
/** Who they are, plainly: age, build, hair, wardrobe, the register of the ad. */
|
|
81880
|
-
description:
|
|
82088
|
+
description: z79.string().min(1).optional()
|
|
81881
82089
|
}).refine((c) => Boolean(c.avatar || c.description), {
|
|
81882
82090
|
message: "give `avatar` (a cast avatar's handle) or `description` \u2014 a cast with neither grounds on nothing"
|
|
81883
82091
|
}).optional(),
|
|
@@ -81894,7 +82102,7 @@ var AdSpec = z77.object({
|
|
|
81894
82102
|
* has to read clean. That is the engine's rule, not this one, and it is why the prompt
|
|
81895
82103
|
* is all this needs to carry.
|
|
81896
82104
|
*/
|
|
81897
|
-
music:
|
|
82105
|
+
music: z79.union([z79.string().min(1), z79.literal(false)]).optional(),
|
|
81898
82106
|
/**
|
|
81899
82107
|
* The place the ad keeps coming back to, held still across every shot.
|
|
81900
82108
|
*
|
|
@@ -81909,9 +82117,9 @@ var AdSpec = z77.object({
|
|
|
81909
82117
|
* is `image_generate` with extra steps — the model registry says exactly that. Generate
|
|
81910
82118
|
* the plate once with gpt-image-2 and pass its URL here.
|
|
81911
82119
|
*/
|
|
81912
|
-
set:
|
|
81913
|
-
description:
|
|
81914
|
-
image:
|
|
82120
|
+
set: z79.object({
|
|
82121
|
+
description: z79.string().min(1).max(400),
|
|
82122
|
+
image: z79.string().min(1).optional()
|
|
81915
82123
|
}).optional(),
|
|
81916
82124
|
/**
|
|
81917
82125
|
* How the ad closes.
|
|
@@ -81925,15 +82133,15 @@ var AdSpec = z77.object({
|
|
|
81925
82133
|
*
|
|
81926
82134
|
* `false` opts out, for an ad that genuinely ends on its footage.
|
|
81927
82135
|
*/
|
|
81928
|
-
end_card:
|
|
81929
|
-
|
|
81930
|
-
|
|
82136
|
+
end_card: z79.union([
|
|
82137
|
+
z79.literal(false),
|
|
82138
|
+
z79.object({
|
|
81931
82139
|
/**
|
|
81932
82140
|
* The words on the button. No default — with none the card carries the mark
|
|
81933
82141
|
* alone, because falling back to the last beat's line printed that sentence
|
|
81934
82142
|
* twice on the closing frame. See `endCardCta`.
|
|
81935
82143
|
*/
|
|
81936
|
-
cta:
|
|
82144
|
+
cta: z79.string().min(1).optional()
|
|
81937
82145
|
})
|
|
81938
82146
|
]).optional(),
|
|
81939
82147
|
/**
|
|
@@ -81953,8 +82161,8 @@ var AdSpec = z77.object({
|
|
|
81953
82161
|
* rather than from transcribing a rendered read, so the text is exact and no speech
|
|
81954
82162
|
* model is between the copy and the screen.
|
|
81955
82163
|
*/
|
|
81956
|
-
voiceover:
|
|
81957
|
-
beats:
|
|
82164
|
+
voiceover: z79.boolean().default(true),
|
|
82165
|
+
beats: z79.array(Beat).min(2)
|
|
81958
82166
|
});
|
|
81959
82167
|
function presenterIsInEveryShot(spec) {
|
|
81960
82168
|
if (!spec.cast) return false;
|
|
@@ -82257,33 +82465,6 @@ function beatsTooShortToPlay(spec) {
|
|
|
82257
82465
|
return spec.beats.map((beat, i) => ({ i, words: beat.say.trim().split(/\s+/).filter(Boolean).length })).filter(({ words }) => words < MIN_BEAT_WORDS).map(({ i }) => i + 1);
|
|
82258
82466
|
}
|
|
82259
82467
|
|
|
82260
|
-
// src/engine/nodes/local/lib/caption-groups.ts
|
|
82261
|
-
function captionWordKey(text4) {
|
|
82262
|
-
return text4.replace(/[.,;:!?…]+$/, "").toLocaleLowerCase();
|
|
82263
|
-
}
|
|
82264
|
-
var CARRIES_ON_HELD = 0.4;
|
|
82265
|
-
var CARRIES_ON_QUESTION = "Is `word`, as used in `line`, a function word that cannot close a phrase: an article, a preposition, a conjunction, a possessive or another determiner? A noun, verb, adjective, adverb, name or number is not.";
|
|
82266
|
-
function carriesOnQuestions(lines) {
|
|
82267
|
-
const seen = /* @__PURE__ */ new Set();
|
|
82268
|
-
const questions = [];
|
|
82269
|
-
for (const line of lines) {
|
|
82270
|
-
for (const text4 of line.split(/\s+/)) {
|
|
82271
|
-
const word = captionWordKey(text4);
|
|
82272
|
-
if (!word || seen.has(word)) continue;
|
|
82273
|
-
seen.add(word);
|
|
82274
|
-
questions.push({ type: "noul", instructions: { line, word, question: CARRIES_ON_QUESTION } });
|
|
82275
|
-
}
|
|
82276
|
-
}
|
|
82277
|
-
return { questions, words: [...seen] };
|
|
82278
|
-
}
|
|
82279
|
-
function carriesOnFromAnswers(words, answers) {
|
|
82280
|
-
if (!answers) return [];
|
|
82281
|
-
return words.filter((_, i) => {
|
|
82282
|
-
const answer = answers[i];
|
|
82283
|
-
return answer !== void 0 && "noul" in answer && answer.noul >= CARRIES_ON_HELD;
|
|
82284
|
-
});
|
|
82285
|
-
}
|
|
82286
|
-
|
|
82287
82468
|
// src/engine/scaffold/ad-spec-reading.ts
|
|
82288
82469
|
var INTRODUCES_SPEAKER_HELD = 0.5;
|
|
82289
82470
|
var WANTS_LEGIBLE_TEXT_HELD = 0.5;
|
|
@@ -82354,93 +82535,6 @@ async function readAdSpec2(spec, options, readers) {
|
|
|
82354
82535
|
};
|
|
82355
82536
|
}
|
|
82356
82537
|
|
|
82357
|
-
// src/engine/nodes/local/lib/jev-cache.ts
|
|
82358
|
-
import { createHash as createHash10 } from "crypto";
|
|
82359
|
-
import { mkdir as mkdir18, readFile as readFile36, writeFile as writeFile25 } from "fs/promises";
|
|
82360
|
-
import path46 from "path";
|
|
82361
|
-
import { z as z78 } from "zod";
|
|
82362
|
-
var Kept = z78.array(
|
|
82363
|
-
z78.union([
|
|
82364
|
-
z78.object({ choice: z78.string(), probabilities: z78.record(z78.string(), z78.number()), confidence: z78.number() }),
|
|
82365
|
-
z78.object({ noul: z78.number() })
|
|
82366
|
-
])
|
|
82367
|
-
);
|
|
82368
|
-
function cachedJev(cacheDir, ask) {
|
|
82369
|
-
const dir = path46.join(cacheDir, "jev");
|
|
82370
|
-
return async (questions) => {
|
|
82371
|
-
const key = createHash10("sha256").update(JSON.stringify([JEV_MODEL, questions])).digest("hex");
|
|
82372
|
-
const file = path46.join(dir, `${key}.json`);
|
|
82373
|
-
const kept = Kept.safeParse(
|
|
82374
|
-
await readFile36(file, "utf8").then(JSON.parse, () => null).catch(() => null)
|
|
82375
|
-
);
|
|
82376
|
-
if (kept.success && kept.data.length === questions.length) return kept.data;
|
|
82377
|
-
const answers = await ask(questions);
|
|
82378
|
-
if (answers) {
|
|
82379
|
-
await mkdir18(dir, { recursive: true });
|
|
82380
|
-
await writeFile25(file, JSON.stringify(answers), "utf8");
|
|
82381
|
-
}
|
|
82382
|
-
return answers;
|
|
82383
|
-
};
|
|
82384
|
-
}
|
|
82385
|
-
|
|
82386
|
-
// src/engine/scaffold/video/dialogue/voice-gender.ts
|
|
82387
|
-
import { createHash as createHash11 } from "crypto";
|
|
82388
|
-
import { mkdir as mkdir19, readFile as readFile37, writeFile as writeFile26 } from "fs/promises";
|
|
82389
|
-
import path47 from "path";
|
|
82390
|
-
import { z as z79 } from "zod";
|
|
82391
|
-
var VOICE_GENDER_MODEL = "google/gemini-2.5-flash-lite";
|
|
82392
|
-
var PROMPT_VERSION2 = 1;
|
|
82393
|
-
var REQUEST2 = {
|
|
82394
|
-
provider: { order: ["google-vertex"], allow_fallbacks: false },
|
|
82395
|
-
temperature: 0,
|
|
82396
|
-
response_format: { type: "json_object" }
|
|
82397
|
-
};
|
|
82398
|
-
var TIMEOUT_MS2 = 2e4;
|
|
82399
|
-
function voiceGenderPrompt(voices) {
|
|
82400
|
-
return [
|
|
82401
|
-
"Each line below describes the voice wanted for a speaker in an ad, written in any language.",
|
|
82402
|
-
"For each, say whether it asks for a woman's voice or a man's voice. Read it in its own language: a noun that is feminine or masculine by itself says so. Answer null when it does not say.",
|
|
82403
|
-
"",
|
|
82404
|
-
...voices.map((voice, i) => `${i + 1}. ${JSON.stringify(voice)}`),
|
|
82405
|
-
"",
|
|
82406
|
-
`Reply with only this JSON: {"genders": ["female" | "male" | null, ... one per line, ${voices.length} in order]}`
|
|
82407
|
-
].join("\n");
|
|
82408
|
-
}
|
|
82409
|
-
var Answer4 = z79.object({ genders: z79.array(z79.enum(["female", "male"]).nullable()) });
|
|
82410
|
-
function parseVoiceGenders(answer, voices) {
|
|
82411
|
-
const parsed = Answer4.safeParse(answer);
|
|
82412
|
-
if (!parsed.success || parsed.data.genders.length !== voices.length) return null;
|
|
82413
|
-
const out = {};
|
|
82414
|
-
voices.forEach((voice, i) => {
|
|
82415
|
-
const gender = parsed.data.genders[i];
|
|
82416
|
-
if (gender) out[voice] = gender;
|
|
82417
|
-
});
|
|
82418
|
-
return out;
|
|
82419
|
-
}
|
|
82420
|
-
function voiceGenderModel(cacheDir, onSpend) {
|
|
82421
|
-
const dir = path47.join(cacheDir, "voice-gender");
|
|
82422
|
-
return async (voices) => {
|
|
82423
|
-
if (voices.length === 0) return {};
|
|
82424
|
-
const key = createHash11("sha256").update(JSON.stringify([PROMPT_VERSION2, VOICE_GENDER_MODEL, voices])).digest("hex");
|
|
82425
|
-
const file = path47.join(dir, `${key}.json`);
|
|
82426
|
-
const kept = parseVoiceGenders(
|
|
82427
|
-
await readFile37(file, "utf8").then(JSON.parse, () => null).catch(() => null),
|
|
82428
|
-
voices
|
|
82429
|
-
);
|
|
82430
|
-
if (kept) return kept;
|
|
82431
|
-
const answer = await askAboutText(VOICE_GENDER_MODEL, voiceGenderPrompt(voices), onSpend, {
|
|
82432
|
-
timeoutMs: TIMEOUT_MS2,
|
|
82433
|
-
request: REQUEST2
|
|
82434
|
-
}).catch(() => null);
|
|
82435
|
-
const genders = parseVoiceGenders(answer, voices);
|
|
82436
|
-
if (genders) {
|
|
82437
|
-
await mkdir19(dir, { recursive: true });
|
|
82438
|
-
await writeFile26(file, JSON.stringify(answer), "utf8");
|
|
82439
|
-
}
|
|
82440
|
-
return genders;
|
|
82441
|
-
};
|
|
82442
|
-
}
|
|
82443
|
-
|
|
82444
82538
|
// src/engine/scaffold/ad-display-face.ts
|
|
82445
82539
|
var AD_DISPLAY_FAMILY = "AdDisplay";
|
|
82446
82540
|
function displayFontStack(face) {
|
|
@@ -82646,7 +82740,7 @@ function injectAdBrandOverlay(indexHtml, overlayHtml, style = {}) {
|
|
|
82646
82740
|
}
|
|
82647
82741
|
|
|
82648
82742
|
// src/commands/canvas/composition-dims.ts
|
|
82649
|
-
import { readFile as readFile38, writeFile as
|
|
82743
|
+
import { readFile as readFile38, writeFile as writeFile28 } from "fs/promises";
|
|
82650
82744
|
import path48 from "path";
|
|
82651
82745
|
function patchCompositionMeta(metaJson, dims) {
|
|
82652
82746
|
try {
|
|
@@ -82664,10 +82758,10 @@ function patchCompositionHtml(html, dims) {
|
|
|
82664
82758
|
async function stampCompositionDims(compositionDir, dims) {
|
|
82665
82759
|
const metaPath = path48.join(compositionDir, "meta.json");
|
|
82666
82760
|
const rawMeta = await readFile38(metaPath, "utf8");
|
|
82667
|
-
await
|
|
82761
|
+
await writeFile28(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
|
|
82668
82762
|
const htmlPath = path48.join(compositionDir, "index.html");
|
|
82669
82763
|
const rawHtml = await readFile38(htmlPath, "utf8");
|
|
82670
|
-
await
|
|
82764
|
+
await writeFile28(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
|
|
82671
82765
|
}
|
|
82672
82766
|
|
|
82673
82767
|
// src/commands/canvas/scaffold-ad.ts
|
|
@@ -83011,21 +83105,21 @@ var scaffoldAdCommand = defineCommand147({
|
|
|
83011
83105
|
await stampCompositionDims(compositionDest, outDims);
|
|
83012
83106
|
await stampCompositionDims(captionsDest, outDims);
|
|
83013
83107
|
const captionsIndex = path49.join(captionsDest, "index.html");
|
|
83014
|
-
await
|
|
83108
|
+
await writeFile29(captionsIndex, injectAdCaptionPlacement(await readFile39(captionsIndex, "utf-8"), outAr), "utf-8");
|
|
83015
83109
|
const face = await stageBrandDisplayFace(outDir, captionsDest);
|
|
83016
83110
|
if (face) {
|
|
83017
|
-
await
|
|
83111
|
+
await writeFile29(captionsIndex, injectAdDisplayFace(await readFile39(captionsIndex, "utf-8"), face.file), "utf-8");
|
|
83018
83112
|
}
|
|
83019
83113
|
const blueprintPath2 = path49.join(outDir, "prompt.json");
|
|
83020
83114
|
const blueprintStylePath = path49.join(outDir, "prompt.style.json");
|
|
83021
83115
|
await writeSceneFiles(outDir, blueprint);
|
|
83022
|
-
await
|
|
83023
|
-
await
|
|
83116
|
+
await writeFile29(blueprintStylePath, renderStyleProjectionFromValue(blueprint), "utf8");
|
|
83117
|
+
await writeFile29(path49.join(outDir, AD_SPEC_FILE), `${JSON.stringify(specArtifact, null, 2)}
|
|
83024
83118
|
`, "utf8");
|
|
83025
83119
|
const logoPath = noBrand ? void 0 : spec.data.brand?.logo?.trim();
|
|
83026
83120
|
const transcriptPath = path49.join(outDir, "transcript.json");
|
|
83027
83121
|
if (spec.data.voiceover === false) {
|
|
83028
|
-
await
|
|
83122
|
+
await writeFile29(transcriptPath, `${JSON.stringify(staticTranscript(spec.data, chosenVideoModel), null, 2)}
|
|
83029
83123
|
`, "utf-8");
|
|
83030
83124
|
}
|
|
83031
83125
|
const opts = {
|
|
@@ -83109,9 +83203,9 @@ var scaffoldAdCommand = defineCommand147({
|
|
|
83109
83203
|
if (overlayHtml && renderedDir) {
|
|
83110
83204
|
const indexPath = path49.join(renderedDir, "index.html");
|
|
83111
83205
|
const html = await readFile39(indexPath, "utf-8");
|
|
83112
|
-
await
|
|
83206
|
+
await writeFile29(indexPath, injectAdBrandOverlay(html, overlayHtml, { aspect: outAr, ...face ? { face: AD_DISPLAY_FAMILY } : {} }), "utf-8");
|
|
83113
83207
|
}
|
|
83114
|
-
await
|
|
83208
|
+
await writeFile29(
|
|
83115
83209
|
path49.join(outDir, REBUILD_FILE),
|
|
83116
83210
|
`${JSON.stringify({ elements: adSpecCastElements(spec.data, avatar), opts }, null, 2)}
|
|
83117
83211
|
`,
|
|
@@ -83122,7 +83216,7 @@ var scaffoldAdCommand = defineCommand147({
|
|
|
83122
83216
|
const meta = canvas.metadata;
|
|
83123
83217
|
meta.todo = { ...meta.todo ?? {}, blocking_validation_issues: validation.issues };
|
|
83124
83218
|
}
|
|
83125
|
-
await
|
|
83219
|
+
await writeFile29(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
83126
83220
|
`, "utf-8");
|
|
83127
83221
|
if (!validation.ok) {
|
|
83128
83222
|
process.stderr.write(
|
|
@@ -83145,7 +83239,7 @@ var scaffoldAdCommand = defineCommand147({
|
|
|
83145
83239
|
const definitionPath = path49.join(outDir, "_definition.md");
|
|
83146
83240
|
const definitionExists = await stat6(definitionPath).then(() => true, () => false);
|
|
83147
83241
|
if (!definitionExists) {
|
|
83148
|
-
await
|
|
83242
|
+
await writeFile29(
|
|
83149
83243
|
definitionPath,
|
|
83150
83244
|
buildCreativeDefinition({
|
|
83151
83245
|
title: titleFromSlug(slug),
|
|
@@ -83269,7 +83363,7 @@ async function fetchClientLogo() {
|
|
|
83269
83363
|
const dir = path49.join(BRAND_DIR, "logos");
|
|
83270
83364
|
await mkdir20(dir, { recursive: true });
|
|
83271
83365
|
const file = path49.join(dir, `brand-mark.${extension}`);
|
|
83272
|
-
await
|
|
83366
|
+
await writeFile29(file, bytes);
|
|
83273
83367
|
return { ok: true, path: file };
|
|
83274
83368
|
} catch {
|
|
83275
83369
|
return { ok: false, reason: clientLogoMissNote({ use: false, reason: "no_hits" }, domain) };
|
|
@@ -83294,7 +83388,7 @@ async function stageBrandDisplayFace(outDir, compositionDir) {
|
|
|
83294
83388
|
const { buffer } = await fetchExternalBytes(face.slot.url, { maxBytes: MAX_FONT_BYTES });
|
|
83295
83389
|
const extension = /\.(woff2|woff|ttf|otf)(?:\?|$)/i.exec(face.slot.url)?.[1]?.toLowerCase() ?? "woff2";
|
|
83296
83390
|
const file = `brand-face.${extension}`;
|
|
83297
|
-
await
|
|
83391
|
+
await writeFile29(path49.join(compositionDir, file), buffer);
|
|
83298
83392
|
return { file, label: `${face.family ?? "the brand's typeface"}, loaded as a web font` };
|
|
83299
83393
|
} catch {
|
|
83300
83394
|
return null;
|
|
@@ -83302,7 +83396,7 @@ async function stageBrandDisplayFace(outDir, compositionDir) {
|
|
|
83302
83396
|
}
|
|
83303
83397
|
|
|
83304
83398
|
// src/commands/canvas/scaffold-video.ts
|
|
83305
|
-
import { cp as cp2, mkdir as mkdir21, readFile as readFile42, rm as rm6, writeFile as
|
|
83399
|
+
import { cp as cp2, mkdir as mkdir21, readFile as readFile42, rm as rm6, writeFile as writeFile30 } from "fs/promises";
|
|
83306
83400
|
import { tmpdir as tmpdir2 } from "os";
|
|
83307
83401
|
import path51 from "path";
|
|
83308
83402
|
import { defineCommand as defineCommand148 } from "citty";
|
|
@@ -83469,40 +83563,6 @@ ${missing.join("\n")}
|
|
|
83469
83563
|
`);
|
|
83470
83564
|
}
|
|
83471
83565
|
|
|
83472
|
-
// src/engine/scaffold/video/speech-reading.ts
|
|
83473
|
-
var asBlueprint = (input) => input && typeof input === "object" ? input : {};
|
|
83474
|
-
var dialogueOf = (b) => (b.scenes ?? []).flatMap((s) => s.dialogue ?? []);
|
|
83475
|
-
function blueprintVoices(input) {
|
|
83476
|
-
const b = asBlueprint(input);
|
|
83477
|
-
const all = [
|
|
83478
|
-
...dialogueOf(b).map((l) => l.voice_description),
|
|
83479
|
-
...(b.global?.cast ?? []).map((c) => c.description),
|
|
83480
|
-
b.global?.voiceover?.voice_description
|
|
83481
|
-
];
|
|
83482
|
-
return [...new Set(all.filter((d) => typeof d === "string" && d.length > 0))];
|
|
83483
|
-
}
|
|
83484
|
-
async function readBlueprintSpeech(input, known, readers) {
|
|
83485
|
-
const lines = dialogueOf(asBlueprint(input)).map((l) => l.line?.trim() ?? "").filter(Boolean);
|
|
83486
|
-
const declared = known.declaredLanguage ? spokenLanguage(known.declaredLanguage)?.tag : void 0;
|
|
83487
|
-
const voices = blueprintVoices(input);
|
|
83488
|
-
const carries2 = carriesOnQuestions(lines);
|
|
83489
|
-
const language = !declared && lines.length > 0 ? [languageQuestion(lines)] : [];
|
|
83490
|
-
const [answers, genders] = await Promise.all([
|
|
83491
|
-
readers.ask([...language, ...carries2.questions]),
|
|
83492
|
-
readers.genders(voices).catch(() => null)
|
|
83493
|
-
]);
|
|
83494
|
-
const voiceGenders = genders ?? {};
|
|
83495
|
-
if (!answers)
|
|
83496
|
-
return { ...declared ? { spokenLanguage: declared } : {}, voiceGenders, captionCarriesOn: [], read: false };
|
|
83497
|
-
const read = declared ?? (language.length > 0 ? languageFromAnswer(answers[0]) : void 0);
|
|
83498
|
-
return {
|
|
83499
|
-
...read ? { spokenLanguage: read } : {},
|
|
83500
|
-
voiceGenders,
|
|
83501
|
-
captionCarriesOn: carriesOnFromAnswers(carries2.words, answers.slice(language.length)),
|
|
83502
|
-
read: true
|
|
83503
|
-
};
|
|
83504
|
-
}
|
|
83505
|
-
|
|
83506
83566
|
// src/commands/canvas/scaffold-video.ts
|
|
83507
83567
|
var SELECT_SYSTEM2 = 'You identify the RECURRING, identity-critical visual elements of a video ad from its deconstruction blueprint \u2014 the people, animals, products, and logos that must stay consistent every time they appear, so a faithful reproduction grounds each in ONE real reference image reused across every frame it appears in. Output ONLY a JSON object of the form { "elements": [ ... ] }, no prose.';
|
|
83508
83568
|
var SELECT_PROMPT2 = `VIDEO DECONSTRUCTION BLUEPRINT (video-deconstruct/1):
|
|
@@ -83610,7 +83670,7 @@ async function materializeReferenceVideo(fileArg2) {
|
|
|
83610
83670
|
tmpdir2(),
|
|
83611
83671
|
`baker-ref-${sha256Hex(bytes).slice(0, 16)}${referenceVideoExt(fileArg2, contentType)}`
|
|
83612
83672
|
);
|
|
83613
|
-
await
|
|
83673
|
+
await writeFile30(dest, bytes);
|
|
83614
83674
|
return dest;
|
|
83615
83675
|
}
|
|
83616
83676
|
function resolveSeamDedup(raw) {
|
|
@@ -83906,7 +83966,7 @@ var scaffoldVideoCommand = defineCommand148({
|
|
|
83906
83966
|
await mkdir21(outDir, { recursive: true });
|
|
83907
83967
|
const annotated = annotateBlueprintWithElements(blueprint, elements);
|
|
83908
83968
|
await writeSceneFiles(outDir, annotated);
|
|
83909
|
-
await
|
|
83969
|
+
await writeFile30(blueprintStylePath, renderStyleProjectionFromValue(annotated), "utf8");
|
|
83910
83970
|
let aspect;
|
|
83911
83971
|
try {
|
|
83912
83972
|
aspect = resolveAspect(
|
|
@@ -83937,7 +83997,7 @@ var scaffoldVideoCommand = defineCommand148({
|
|
|
83937
83997
|
`video-overlay-composition/index.html is missing the <!--OVERLAYS--> marker \u2014 cannot inject the overlay layer`
|
|
83938
83998
|
);
|
|
83939
83999
|
}
|
|
83940
|
-
await
|
|
84000
|
+
await writeFile30(indexPath, injected, "utf8");
|
|
83941
84001
|
const captions = await stageCaptions(outDir, transcript);
|
|
83942
84002
|
if (captions.compositionPath) await stampCompositionDims(captions.compositionPath, outDims);
|
|
83943
84003
|
const opts = {
|
|
@@ -83974,9 +84034,9 @@ var scaffoldVideoCommand = defineCommand148({
|
|
|
83974
84034
|
todo.blocking_validation_issues = validation.issues;
|
|
83975
84035
|
meta.todo = todo;
|
|
83976
84036
|
}
|
|
83977
|
-
await
|
|
84037
|
+
await writeFile30(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
83978
84038
|
`, "utf8");
|
|
83979
|
-
await
|
|
84039
|
+
await writeFile30(
|
|
83980
84040
|
path51.join(outDir, REBUILD_FILE),
|
|
83981
84041
|
`${JSON.stringify({ elements, opts }, null, 2)}
|
|
83982
84042
|
`,
|
|
@@ -84006,7 +84066,7 @@ var scaffoldVideoCommand = defineCommand148({
|
|
|
84006
84066
|
if (slug) {
|
|
84007
84067
|
const definitionPath = path51.join(outDir, "_definition.md");
|
|
84008
84068
|
if (!await fileExists(definitionPath)) {
|
|
84009
|
-
await
|
|
84069
|
+
await writeFile30(
|
|
84010
84070
|
definitionPath,
|
|
84011
84071
|
buildCreativeDefinition({
|
|
84012
84072
|
title: titleFromSlug(slug),
|
|
@@ -84097,7 +84157,7 @@ var scaffoldVideoCommand = defineCommand148({
|
|
|
84097
84157
|
});
|
|
84098
84158
|
|
|
84099
84159
|
// src/commands/canvas/set-prompt.ts
|
|
84100
|
-
import { readFile as readFile43, writeFile as
|
|
84160
|
+
import { readFile as readFile43, writeFile as writeFile31 } from "fs/promises";
|
|
84101
84161
|
import path52 from "path";
|
|
84102
84162
|
import { defineCommand as defineCommand149 } from "citty";
|
|
84103
84163
|
function setNodePrompt(canvas, nodeId, text4) {
|
|
@@ -84160,9 +84220,9 @@ async function setPromptInCanvasFile(canvasPath, nodeId, text4) {
|
|
|
84160
84220
|
} catch (e) {
|
|
84161
84221
|
throw new SetPromptError("node_not_found", e instanceof Error ? e.message : String(e));
|
|
84162
84222
|
}
|
|
84163
|
-
await
|
|
84223
|
+
await writeFile31(filePath, `${JSON.stringify(updated, null, 2)}
|
|
84164
84224
|
`, "utf8");
|
|
84165
|
-
const restore = () =>
|
|
84225
|
+
const restore = () => writeFile31(filePath, original, "utf8");
|
|
84166
84226
|
try {
|
|
84167
84227
|
const prepared = await prepareCanvas(filePath);
|
|
84168
84228
|
const validation = await validateCanvasDeep(prepared.canvas, defaultRegistry());
|
|
@@ -84711,7 +84771,7 @@ import path56 from "path";
|
|
|
84711
84771
|
import { defineCommand as defineCommand160 } from "citty";
|
|
84712
84772
|
|
|
84713
84773
|
// src/commands/research/customers.ts
|
|
84714
|
-
import { mkdir as mkdir22, readdir as readdir19, readFile as readFile44, writeFile as
|
|
84774
|
+
import { mkdir as mkdir22, readdir as readdir19, readFile as readFile44, writeFile as writeFile32 } from "fs/promises";
|
|
84715
84775
|
import path54 from "path";
|
|
84716
84776
|
import { defineCommand as defineCommand156 } from "citty";
|
|
84717
84777
|
import { parse as parseYaml4 } from "yaml";
|
|
@@ -85686,7 +85746,7 @@ async function writeInsights(dir, writes, opts) {
|
|
|
85686
85746
|
const written = [];
|
|
85687
85747
|
for (const entry of writes) {
|
|
85688
85748
|
const file = renderInsightFile(entry.insight, opts);
|
|
85689
|
-
const ok = await
|
|
85749
|
+
const ok = await writeFile32(path54.join(dir, `${entry.id}.md`), file, { encoding: "utf8", flag: "wx" }).then(
|
|
85690
85750
|
() => true,
|
|
85691
85751
|
() => false
|
|
85692
85752
|
);
|
|
@@ -86946,11 +87006,12 @@ function readsAsAgentOption(reading) {
|
|
|
86946
87006
|
if (reading.tagged >= NOTE_BANDS.tagged) return true;
|
|
86947
87007
|
return reading.bareFix >= NOTE_BANDS.bareFix && reading.namesProblem < NOTE_BANDS.namesProblem;
|
|
86948
87008
|
}
|
|
86949
|
-
async function
|
|
87009
|
+
async function readNoteAuthor(note, judge) {
|
|
86950
87010
|
const text4 = note.trim();
|
|
86951
|
-
if (!text4) return
|
|
87011
|
+
if (!text4) return "person";
|
|
86952
87012
|
const reading = await judge(text4).catch(() => null);
|
|
86953
|
-
|
|
87013
|
+
if (reading === null) return "unconfirmed";
|
|
87014
|
+
return readsAsAgentOption(reading) ? "agent_option" : "person";
|
|
86954
87015
|
}
|
|
86955
87016
|
|
|
86956
87017
|
// src/commands/creatives/version-of.ts
|
|
@@ -86999,18 +87060,21 @@ function fail6(code, message) {
|
|
|
86999
87060
|
process.exit(1);
|
|
87000
87061
|
}
|
|
87001
87062
|
async function refuseNotFeedback(body) {
|
|
87002
|
-
if (body.verdict === "rejected" && body.note && await isAgentOptionNote(body.note, jevNoteJudge)) {
|
|
87003
|
-
fail6(
|
|
87004
|
-
"AGENT_OPTION_NOT_FEEDBACK",
|
|
87005
|
-
`"${body.note}" is the answer to your own question, not the person's taste: a defect the checks found is yours to fix inside this version (\`baker canvas run --fix\`), never feedback to record. Record only what the person says about the ad in their own words.`
|
|
87006
|
-
);
|
|
87007
|
-
}
|
|
87008
87063
|
if (body.verdict === "rejected" && !body.reasons?.length && !body.note) {
|
|
87009
87064
|
fail6(
|
|
87010
87065
|
"VALIDATION_ERROR",
|
|
87011
87066
|
"A rejection needs --reasons or --note (the user's words), so the next version knows what to change."
|
|
87012
87067
|
);
|
|
87013
87068
|
}
|
|
87069
|
+
if (body.verdict !== "rejected" || !body.note) return false;
|
|
87070
|
+
const author = await readNoteAuthor(body.note, jevNoteJudge);
|
|
87071
|
+
if (author === "agent_option") {
|
|
87072
|
+
fail6(
|
|
87073
|
+
"AGENT_OPTION_NOT_FEEDBACK",
|
|
87074
|
+
`"${body.note}" is the answer to your own question, not the person's taste: a defect the checks found is yours to fix inside this version (\`baker canvas run --fix\`), never feedback to record. Record only what the person says about the ad in their own words.`
|
|
87075
|
+
);
|
|
87076
|
+
}
|
|
87077
|
+
return author === "unconfirmed";
|
|
87014
87078
|
}
|
|
87015
87079
|
var feedbackRecordCommand = defineCommand161({
|
|
87016
87080
|
meta: { name: "record", description: DESCRIPTION8 },
|
|
@@ -87038,9 +87102,12 @@ var feedbackRecordCommand = defineCommand161({
|
|
|
87038
87102
|
fail6("VALIDATION_ERROR", `--${String(issue?.path[0] ?? "args")}: ${issue?.message ?? "invalid"}`);
|
|
87039
87103
|
}
|
|
87040
87104
|
const body = parsed.data;
|
|
87041
|
-
await refuseNotFeedback(body);
|
|
87105
|
+
const noteUnconfirmed = await refuseNotFeedback(body);
|
|
87042
87106
|
try {
|
|
87043
|
-
const response = await apiPost(
|
|
87107
|
+
const response = await apiPost(
|
|
87108
|
+
"/api/creatives/feedback/record",
|
|
87109
|
+
noteUnconfirmed ? { ...body, noteUnconfirmed } : body
|
|
87110
|
+
);
|
|
87044
87111
|
writeJson(response);
|
|
87045
87112
|
} catch (err) {
|
|
87046
87113
|
if (err instanceof ApiError) fail6(err.code, err.message);
|
|
@@ -87101,7 +87168,7 @@ var feedbackCommand = defineCommand162({
|
|
|
87101
87168
|
|
|
87102
87169
|
// src/commands/creatives/learn.ts
|
|
87103
87170
|
import { readFileSync as readFileSync19 } from "fs";
|
|
87104
|
-
import { readFile as readFile46, writeFile as
|
|
87171
|
+
import { readFile as readFile46, writeFile as writeFile33 } from "fs/promises";
|
|
87105
87172
|
import path58 from "path";
|
|
87106
87173
|
import { defineCommand as defineCommand163 } from "citty";
|
|
87107
87174
|
|
|
@@ -87326,7 +87393,7 @@ async function writeBack(root, learnings) {
|
|
|
87326
87393
|
for (const learning of learnings) {
|
|
87327
87394
|
const file = path58.join(root, IDEAS_DIR, `${learning.idea}.md`);
|
|
87328
87395
|
const raw = await readFile46(file, "utf8");
|
|
87329
|
-
await
|
|
87396
|
+
await writeFile33(file, setDefinitionFields(raw, { status: learning.to, result: learning.result }), "utf8");
|
|
87330
87397
|
}
|
|
87331
87398
|
}
|
|
87332
87399
|
function readDefinition2(root, slug) {
|
|
@@ -87368,13 +87435,13 @@ var learnCommand = defineCommand163({
|
|
|
87368
87435
|
});
|
|
87369
87436
|
|
|
87370
87437
|
// src/commands/creatives/make.ts
|
|
87371
|
-
import { mkdtemp as mkdtemp2, readFile as readFile48, rm as rm7, stat as stat7, writeFile as
|
|
87438
|
+
import { mkdtemp as mkdtemp2, readFile as readFile48, rm as rm7, stat as stat7, writeFile as writeFile35 } from "fs/promises";
|
|
87372
87439
|
import { tmpdir as tmpdir3 } from "os";
|
|
87373
87440
|
import path60 from "path";
|
|
87374
87441
|
import { defineCommand as defineCommand164 } from "citty";
|
|
87375
87442
|
|
|
87376
87443
|
// src/commands/creatives/make-series.ts
|
|
87377
|
-
import { mkdir as mkdir23, readFile as readFile47, writeFile as
|
|
87444
|
+
import { mkdir as mkdir23, readFile as readFile47, writeFile as writeFile34 } from "fs/promises";
|
|
87378
87445
|
import path59 from "path";
|
|
87379
87446
|
import { parseDocument as parseDocument5 } from "yaml";
|
|
87380
87447
|
var LEAD_ONLY = ["idea", "message", "evidence", "angle", "persona", "awareness", "hypothesis", "series", "title"];
|
|
@@ -87402,7 +87469,7 @@ async function makeSeriesAd(root, idea, slug, lead, evidence, deps) {
|
|
|
87402
87469
|
const claim = await claimNewCreativeInWorkspace(path59.join(root, "canvas"), slug, deps.chatId, definitionPath);
|
|
87403
87470
|
if (!claim.allowed) return creativeLimitRefusal(slug).error;
|
|
87404
87471
|
await mkdir23(dir, { recursive: true });
|
|
87405
|
-
await
|
|
87472
|
+
await writeFile34(path59.join(dir, "prompt.json"), setSpecMessage(JSON.stringify(version.spec), idea.message), "utf8");
|
|
87406
87473
|
const leadDefinition = await readFile47(path59.join(root, "src", "creatives", lead, "_definition.md"), "utf8").catch(
|
|
87407
87474
|
() => "---\nkind: static\n---\n"
|
|
87408
87475
|
);
|
|
@@ -87412,7 +87479,7 @@ async function makeSeriesAd(root, idea, slug, lead, evidence, deps) {
|
|
|
87412
87479
|
status: "draft",
|
|
87413
87480
|
...definitionFieldsOf(idea, evidence)
|
|
87414
87481
|
});
|
|
87415
|
-
await
|
|
87482
|
+
await writeFile34(definitionPath, definition, "utf8");
|
|
87416
87483
|
const made = await writeSeriesMemberCanvas(root, slug, { leadWords: true });
|
|
87417
87484
|
if (made === null || "refused" in made) {
|
|
87418
87485
|
return made?.refused ?? { code: "series_not_made", message: `"${slug}" could not be made in the series of "${lead}".` };
|
|
@@ -87420,7 +87487,7 @@ async function makeSeriesAd(root, idea, slug, lead, evidence, deps) {
|
|
|
87420
87487
|
if (idea.status === "idea") {
|
|
87421
87488
|
const ideaPath = path59.join(root, IDEAS_DIR, `${idea.id}.md`);
|
|
87422
87489
|
const raw = await readFile47(ideaPath, "utf8").catch(() => null);
|
|
87423
|
-
if (raw !== null) await
|
|
87490
|
+
if (raw !== null) await writeFile34(ideaPath, setDefinitionFields(raw, { status: "built" }), "utf8");
|
|
87424
87491
|
}
|
|
87425
87492
|
await deps.sync(dir);
|
|
87426
87493
|
return { slug, lead, leadRunId: version.lead.runId, canvasPath: made.canvasPath };
|
|
@@ -87700,7 +87767,7 @@ async function scaffoldStatic(root, idea, args8, slug, temperature, viewerFlags)
|
|
|
87700
87767
|
async function scaffoldFromIdea(idea, slug, viewerFlags) {
|
|
87701
87768
|
const dir = await mkdtemp2(path60.join(tmpdir3(), "idea-spec-"));
|
|
87702
87769
|
const specPath = path60.join(dir, "prompt.json");
|
|
87703
|
-
await
|
|
87770
|
+
await writeFile35(specPath, JSON.stringify(ideaSpec(idea, idea.who.replace(/[-_]/g, " ")), null, 2), "utf8");
|
|
87704
87771
|
try {
|
|
87705
87772
|
return await runBaker([
|
|
87706
87773
|
"canvas",
|
|
@@ -87722,15 +87789,15 @@ async function recordIdea(root, slug, idea, evidence) {
|
|
|
87722
87789
|
const definitionPath = path60.join(dir, "_definition.md");
|
|
87723
87790
|
const definition = await readFile48(definitionPath, "utf8").catch(() => null);
|
|
87724
87791
|
if (definition !== null) {
|
|
87725
|
-
await
|
|
87792
|
+
await writeFile35(definitionPath, setDefinitionFields(definition, { ...definitionFieldsOf(idea, evidence) }), "utf8");
|
|
87726
87793
|
}
|
|
87727
87794
|
const promptPath = path60.join(dir, "prompt.json");
|
|
87728
87795
|
const prompt = isVideoIdea(idea) ? null : await readFile48(promptPath, "utf8").catch(() => null);
|
|
87729
|
-
if (prompt !== null) await
|
|
87796
|
+
if (prompt !== null) await writeFile35(promptPath, setSpecMessage(prompt, idea.message), "utf8").catch(() => void 0);
|
|
87730
87797
|
const unsourcedCopy = prompt === null ? [] : await draftedClaims(root, prompt);
|
|
87731
87798
|
if (idea.status === "idea") {
|
|
87732
87799
|
const ideaPath = path60.join(root, IDEAS_DIR, `${idea.id}.md`);
|
|
87733
|
-
await
|
|
87800
|
+
await writeFile35(ideaPath, setDefinitionFields(await readFile48(ideaPath, "utf8"), { status: "built" }), "utf8");
|
|
87734
87801
|
}
|
|
87735
87802
|
await runBaker(["canvas", "sync-definition", dir]);
|
|
87736
87803
|
return { dir, unsourcedCopy };
|
|
@@ -88329,7 +88396,7 @@ import path62 from "path";
|
|
|
88329
88396
|
import { defineCommand as defineCommand167 } from "citty";
|
|
88330
88397
|
|
|
88331
88398
|
// src/commands/creatives/translate-ad.ts
|
|
88332
|
-
import { mkdir as mkdir24, readFile as readFile51, writeFile as
|
|
88399
|
+
import { mkdir as mkdir24, readFile as readFile51, writeFile as writeFile36 } from "fs/promises";
|
|
88333
88400
|
import path61 from "path";
|
|
88334
88401
|
|
|
88335
88402
|
// src/commands/creatives/translate-model.ts
|
|
@@ -88376,7 +88443,8 @@ async function customersWordsAmong(lines, judge) {
|
|
|
88376
88443
|
const open = [];
|
|
88377
88444
|
lines.forEach((line, i) => {
|
|
88378
88445
|
if (line.role === MESSAGE_ROLE) return;
|
|
88379
|
-
|
|
88446
|
+
const below = lines[i + 1];
|
|
88447
|
+
if (isCustomersWords(line) || below !== void 0 && isSignatureLine(below)) kept.add(i);
|
|
88380
88448
|
else open.push(i);
|
|
88381
88449
|
});
|
|
88382
88450
|
if (!judge || open.length === 0) return kept;
|
|
@@ -88567,7 +88635,7 @@ async function makeTranslation(root, t, deps) {
|
|
|
88567
88635
|
const claim = await claimNewCreativeInWorkspace(path61.join(root, "canvas"), creative, deps.chatId, definitionPath);
|
|
88568
88636
|
if (!claim.allowed) return refused(creativeLimitRefusal(creative).error);
|
|
88569
88637
|
await mkdir24(dir, { recursive: true });
|
|
88570
|
-
await
|
|
88638
|
+
await writeFile36(
|
|
88571
88639
|
path61.join(dir, "prompt.json"),
|
|
88572
88640
|
`${JSON.stringify(translatedSpec(t.spec, t.words, t.language, t.quoted), null, 2)}
|
|
88573
88641
|
`,
|
|
@@ -88577,7 +88645,7 @@ async function makeTranslation(root, t, deps) {
|
|
|
88577
88645
|
0,
|
|
88578
88646
|
t.lines.findIndex((l) => l.role === "headline")
|
|
88579
88647
|
);
|
|
88580
|
-
await
|
|
88648
|
+
await writeFile36(
|
|
88581
88649
|
definitionPath,
|
|
88582
88650
|
translatedDefinition(t.sourceDefinition, {
|
|
88583
88651
|
source: t.source,
|
|
@@ -89329,7 +89397,7 @@ async function readForms(root, slug) {
|
|
|
89329
89397
|
|
|
89330
89398
|
// src/commands/experiment/fold.ts
|
|
89331
89399
|
import { statSync } from "fs";
|
|
89332
|
-
import { cp as cp3, mkdir as mkdir25, readdir as readdir23, readFile as readFile55, rm as rm8, stat as stat9, writeFile as
|
|
89400
|
+
import { cp as cp3, mkdir as mkdir25, readdir as readdir23, readFile as readFile55, rm as rm8, stat as stat9, writeFile as writeFile37 } from "fs/promises";
|
|
89333
89401
|
import path66 from "path";
|
|
89334
89402
|
|
|
89335
89403
|
// ../api/src/experiments/variants.ts
|
|
@@ -89433,7 +89501,7 @@ async function archiveArm(opts) {
|
|
|
89433
89501
|
await cp3(abs, dest);
|
|
89434
89502
|
} else {
|
|
89435
89503
|
const text4 = await readFile55(abs, "utf8");
|
|
89436
|
-
await
|
|
89504
|
+
await writeFile37(dest, repointIntoArchive(text4, abs, dest, archivedBySource), "utf8");
|
|
89437
89505
|
}
|
|
89438
89506
|
written.push(posix2(path66.relative(root, dest)));
|
|
89439
89507
|
}
|
|
@@ -89504,7 +89572,7 @@ async function promoteVariantFiles(opts) {
|
|
|
89504
89572
|
await cp3(from, to);
|
|
89505
89573
|
} else {
|
|
89506
89574
|
const text4 = await readFile55(from, "utf8");
|
|
89507
|
-
await
|
|
89575
|
+
await writeFile37(
|
|
89508
89576
|
to,
|
|
89509
89577
|
repointMoved(text4, {
|
|
89510
89578
|
fromDir: path66.dirname(from),
|
|
@@ -89564,7 +89632,7 @@ async function foldExperiment(root, target2) {
|
|
|
89564
89632
|
archiveDir,
|
|
89565
89633
|
armSlug: target2.survivor === "variant" ? target2.landingSlug : target2.variantSlug
|
|
89566
89634
|
});
|
|
89567
|
-
await
|
|
89635
|
+
await writeFile37(path66.join(archiveDir, "_variant.json"), archiveRecord(target2, variantNumber), "utf8");
|
|
89568
89636
|
written.push(posix2(path66.join(ARCHIVE_ROOT, archiveKey, String(variantNumber), "_variant.json")));
|
|
89569
89637
|
if (target2.survivor === "variant") {
|
|
89570
89638
|
written.push(...await promoteVariantFiles({ root, pageDir, variantDir }));
|
|
@@ -96991,7 +97059,7 @@ function cropSprite(input, region) {
|
|
|
96991
97059
|
|
|
96992
97060
|
// src/lib/image/io.ts
|
|
96993
97061
|
import { randomBytes } from "crypto";
|
|
96994
|
-
import { glob as fsGlob, readFile as readFile58, rename as rename3, stat as stat10, writeFile as
|
|
97062
|
+
import { glob as fsGlob, readFile as readFile58, rename as rename3, stat as stat10, writeFile as writeFile38 } from "fs/promises";
|
|
96995
97063
|
import { dirname as dirname2, extname as extname3, join as join5, resolve as resolve4 } from "path";
|
|
96996
97064
|
var REMOTE_RE = /^https?:\/\//i;
|
|
96997
97065
|
var GLOB_RE = /[*?[\]{}]/;
|
|
@@ -97047,7 +97115,7 @@ async function atomicWrite(targetPath, data) {
|
|
|
97047
97115
|
const absolute = resolve4(targetPath);
|
|
97048
97116
|
const dir = dirname2(absolute);
|
|
97049
97117
|
const tmp = join5(dir, `.baker-image-${randomBytes(8).toString("hex")}.tmp`);
|
|
97050
|
-
await
|
|
97118
|
+
await writeFile38(tmp, data);
|
|
97051
97119
|
await rename3(tmp, absolute);
|
|
97052
97120
|
}
|
|
97053
97121
|
|
|
@@ -101620,7 +101688,7 @@ function describeCounts(findings) {
|
|
|
101620
101688
|
}
|
|
101621
101689
|
|
|
101622
101690
|
// src/commands/landing/snapshot.ts
|
|
101623
|
-
import { mkdir as mkdir26, rename as rename4, writeFile as
|
|
101691
|
+
import { mkdir as mkdir26, rename as rename4, writeFile as writeFile39 } from "fs/promises";
|
|
101624
101692
|
import path69 from "path";
|
|
101625
101693
|
var CRITIC_VERSION = "5";
|
|
101626
101694
|
function critiqueCacheDir(projectRoot) {
|
|
@@ -101633,7 +101701,7 @@ async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
|
101633
101701
|
await mkdir26(critiqueCacheDir(projectRoot), { recursive: true });
|
|
101634
101702
|
const dest = snapshotPath(projectRoot, snapshot.slug);
|
|
101635
101703
|
const tmp = `${dest}.tmp`;
|
|
101636
|
-
await
|
|
101704
|
+
await writeFile39(tmp, `${JSON.stringify(snapshot, null, 2)}
|
|
101637
101705
|
`, "utf8");
|
|
101638
101706
|
await rename4(tmp, dest);
|
|
101639
101707
|
}
|
|
@@ -101788,7 +101856,7 @@ var critiqueCommand2 = defineCommand220({
|
|
|
101788
101856
|
const hints2 = [
|
|
101789
101857
|
blocked.length > 0 ? `Block-tier tells are held at publish \u2014 fix them (${blocked.join(", ")}). Warn/advisory are guidance. Scores are 0\u20131 (higher is better).` : "No block-tier tells. Warn/advisory findings are guidance, not gates. Re-run after edits so the publish gate stays fresh.",
|
|
101790
101858
|
...copyUnchecked.length > 0 ? [
|
|
101791
|
-
`The copy of ${copyUnchecked.join(", ")} could not be
|
|
101859
|
+
`The copy of ${copyUnchecked.join(", ")} could not be checked just now (concessions to a competitor, rivals named, buzzwords and AI copy tells are unchecked, so none is reported), and this critique was not recorded for publishing. Run \`baker landing critique ${copyUnchecked.join(" ")}\` again before publishing.`
|
|
101792
101860
|
] : [],
|
|
101793
101861
|
...snapshotFailures.length > 0 ? [
|
|
101794
101862
|
`Could not record the critique snapshot for ${snapshotFailures.join(", ")} (.cache/ not writable?) \u2014 the publish quality gate will still report ${snapshotFailures.length === 1 ? "this landing" : "these landings"} as un-critiqued.`
|
|
@@ -101816,6 +101884,7 @@ async function critiqueOne(projectRoot, slug, brand, competitors, ask) {
|
|
|
101816
101884
|
computeLegacyLandingSourceSha(path71.resolve(projectRoot, "src", "pages", slug))
|
|
101817
101885
|
]);
|
|
101818
101886
|
const report = await critiqueLanding({ slug, sources, brand, competitors, ask });
|
|
101887
|
+
if (!report.copyChecked) return { slug, report, snapshotFailed: false };
|
|
101819
101888
|
let snapshotFailed = false;
|
|
101820
101889
|
try {
|
|
101821
101890
|
await writeCritiqueSnapshot(projectRoot, {
|
|
@@ -102152,7 +102221,7 @@ var addCommand = defineCommand222({
|
|
|
102152
102221
|
});
|
|
102153
102222
|
|
|
102154
102223
|
// src/commands/landing/inspiration/code.ts
|
|
102155
|
-
import { mkdir as mkdir27, writeFile as
|
|
102224
|
+
import { mkdir as mkdir27, writeFile as writeFile40 } from "fs/promises";
|
|
102156
102225
|
import path72 from "path";
|
|
102157
102226
|
import { defineCommand as defineCommand223 } from "citty";
|
|
102158
102227
|
registerSchema({
|
|
@@ -102179,7 +102248,7 @@ var codeCommand = defineCommand223({
|
|
|
102179
102248
|
const dir = path72.join(process.cwd(), ".baker", "inspiration", id);
|
|
102180
102249
|
await mkdir27(dir, { recursive: true });
|
|
102181
102250
|
const file = path72.join(dir, "section.html");
|
|
102182
|
-
await
|
|
102251
|
+
await writeFile40(file, data.html);
|
|
102183
102252
|
const hints2 = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
102184
102253
|
const fidelity = fidelityHint(data.fidelity);
|
|
102185
102254
|
if (fidelity) hints2.push(fidelity);
|
|
@@ -102647,7 +102716,7 @@ function classifyCaptureFailure(error) {
|
|
|
102647
102716
|
}
|
|
102648
102717
|
|
|
102649
102718
|
// src/engine/landing-library/run.ts
|
|
102650
|
-
import { mkdir as mkdir28, writeFile as
|
|
102719
|
+
import { mkdir as mkdir28, writeFile as writeFile42 } from "fs/promises";
|
|
102651
102720
|
import path74 from "path";
|
|
102652
102721
|
|
|
102653
102722
|
// ../proxy/src/preflight.ts
|
|
@@ -104065,11 +104134,11 @@ async function renderBundleToPng(browser, html, viewportWidth, options = {}) {
|
|
|
104065
104134
|
}
|
|
104066
104135
|
|
|
104067
104136
|
// src/engine/landing-library/report.ts
|
|
104068
|
-
import { writeFile as
|
|
104137
|
+
import { writeFile as writeFile41 } from "fs/promises";
|
|
104069
104138
|
import path73 from "path";
|
|
104070
104139
|
async function writeCaptureReport(manifest, outDir) {
|
|
104071
104140
|
const file = path73.join(outDir, "report.html");
|
|
104072
|
-
await
|
|
104141
|
+
await writeFile41(file, renderReport(manifest));
|
|
104073
104142
|
return file;
|
|
104074
104143
|
}
|
|
104075
104144
|
function escapeHtml3(value2) {
|
|
@@ -104212,13 +104281,13 @@ async function reproducePage(args8) {
|
|
|
104212
104281
|
const { browser, page, outDir, pageUrl, livePageShot } = args8;
|
|
104213
104282
|
const built = await buildSectionBundle(page, "body", pageUrl).catch(() => null);
|
|
104214
104283
|
if (!built) return { bundle: null, fidelity: null };
|
|
104215
|
-
await
|
|
104284
|
+
await writeFile42(path74.join(outDir, "page.html"), built.html);
|
|
104216
104285
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width, {
|
|
104217
104286
|
wholePage: true,
|
|
104218
104287
|
timeoutMs: 6e4
|
|
104219
104288
|
});
|
|
104220
104289
|
if (!rendered || !livePageShot) return { bundle: "page.html", fidelity: null };
|
|
104221
|
-
await
|
|
104290
|
+
await writeFile42(path74.join(outDir, "page-rendered.png"), rendered);
|
|
104222
104291
|
const { score, note } = await scoreFidelity(livePageShot, rendered);
|
|
104223
104292
|
return { bundle: "page.html", fidelity: score, ...note ? { fidelityNote: note } : {} };
|
|
104224
104293
|
}
|
|
@@ -104227,17 +104296,17 @@ async function captureOneSection(args8) {
|
|
|
104227
104296
|
const dir = path74.join(sectionsDir, String(candidate.index).padStart(2, "0"));
|
|
104228
104297
|
await mkdir28(dir, { recursive: true });
|
|
104229
104298
|
const desktop = await captureSection(page, candidate);
|
|
104230
|
-
if (desktop) await
|
|
104299
|
+
if (desktop) await writeFile42(path74.join(dir, "desktop.png"), desktop);
|
|
104231
104300
|
const visualHash = desktop ? await perceptualHash(desktop) : null;
|
|
104232
104301
|
const motion = await collectMotion(page, candidate.selector);
|
|
104233
104302
|
const built = withCode ? await buildSectionBundle(page, candidate.selector, pageUrl) : null;
|
|
104234
104303
|
let fidelity = null;
|
|
104235
104304
|
let fidelityNote;
|
|
104236
104305
|
if (built) {
|
|
104237
|
-
await
|
|
104306
|
+
await writeFile42(path74.join(dir, "section.html"), built.html);
|
|
104238
104307
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width);
|
|
104239
104308
|
if (rendered && desktop) {
|
|
104240
|
-
await
|
|
104309
|
+
await writeFile42(path74.join(dir, "section-rendered.png"), rendered);
|
|
104241
104310
|
const result = await scoreFidelity(desktop, rendered);
|
|
104242
104311
|
fidelity = result.score;
|
|
104243
104312
|
fidelityNote = result.note;
|
|
@@ -104267,7 +104336,7 @@ async function captureMobileShots(args8) {
|
|
|
104267
104336
|
const shot = await captureSectionOnMobile(mobile.page, section2);
|
|
104268
104337
|
if (!shot) continue;
|
|
104269
104338
|
const file = path74.join(sectionsDir, String(section2.index).padStart(2, "0"), "mobile.png");
|
|
104270
|
-
await
|
|
104339
|
+
await writeFile42(file, shot);
|
|
104271
104340
|
section2.mobileShot = path74.relative(outDir, file);
|
|
104272
104341
|
}
|
|
104273
104342
|
} finally {
|
|
@@ -104285,7 +104354,7 @@ async function captureMotionTakes(args8) {
|
|
|
104285
104354
|
if (!take) return;
|
|
104286
104355
|
const dir = path74.join(sectionsDir, String(section2.index).padStart(2, "0"));
|
|
104287
104356
|
const file = path74.join(dir, "motion-filmstrip.png");
|
|
104288
|
-
await
|
|
104357
|
+
await writeFile42(file, take.filmstrip);
|
|
104289
104358
|
section2.motionFilmstrip = path74.relative(outDir, file);
|
|
104290
104359
|
log(` [${section2.index}] ${section2.motion.summary}`);
|
|
104291
104360
|
};
|
|
@@ -104343,7 +104412,7 @@ async function captureAlternateViews(args8) {
|
|
|
104343
104412
|
async function reproduceWholePage(args8) {
|
|
104344
104413
|
const { browser, page, outDir, pageUrl, withCode, log } = args8;
|
|
104345
104414
|
const fullPage = await page.screenshot({ type: "png", fullPage: true }).catch(() => null);
|
|
104346
|
-
if (fullPage) await
|
|
104415
|
+
if (fullPage) await writeFile42(path74.join(outDir, "full-page.png"), fullPage);
|
|
104347
104416
|
if (!withCode) return { bundle: null, fidelity: null };
|
|
104348
104417
|
const reproduction = await reproducePage({ browser, page, outDir, pageUrl, livePageShot: fullPage });
|
|
104349
104418
|
log(`page reproduction: ${reproduction.fidelity === null ? "unavailable" : reproduction.fidelity.toFixed(2)}`);
|
|
@@ -104476,7 +104545,7 @@ async function scrapeLanding(options) {
|
|
|
104476
104545
|
security: prepared.security,
|
|
104477
104546
|
captureTier: tier
|
|
104478
104547
|
};
|
|
104479
|
-
await
|
|
104548
|
+
await writeFile42(path74.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
104480
104549
|
`);
|
|
104481
104550
|
if (options.report !== false) {
|
|
104482
104551
|
const reportPath = await writeCaptureReport(manifest, options.outDir);
|
|
@@ -104690,7 +104759,7 @@ import path77 from "path";
|
|
|
104690
104759
|
import { defineCommand as defineCommand228 } from "citty";
|
|
104691
104760
|
|
|
104692
104761
|
// src/commands/landing/inspiration/shot.ts
|
|
104693
|
-
import { mkdir as mkdir29, writeFile as
|
|
104762
|
+
import { mkdir as mkdir29, writeFile as writeFile43 } from "fs/promises";
|
|
104694
104763
|
import path76 from "path";
|
|
104695
104764
|
import sharp9 from "sharp";
|
|
104696
104765
|
var READABLE_SHOT = {
|
|
@@ -104718,7 +104787,7 @@ async function downloadReadableShot(url, file) {
|
|
|
104718
104787
|
if (!response.ok) return null;
|
|
104719
104788
|
const shot = await toReadableShot(Buffer.from(await response.arrayBuffer()));
|
|
104720
104789
|
await mkdir29(path76.dirname(file), { recursive: true });
|
|
104721
|
-
await
|
|
104790
|
+
await writeFile43(file, shot);
|
|
104722
104791
|
return path76.relative(process.cwd(), file);
|
|
104723
104792
|
} catch {
|
|
104724
104793
|
return null;
|
|
@@ -105191,7 +105260,7 @@ Full guide: __tooling__/docs/tools/baker/landing.md`
|
|
|
105191
105260
|
|
|
105192
105261
|
// src/commands/landing/variant.ts
|
|
105193
105262
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
105194
|
-
import { mkdir as mkdir30, readdir as readdir26, readFile as readFile61, stat as stat13, writeFile as
|
|
105263
|
+
import { mkdir as mkdir30, readdir as readdir26, readFile as readFile61, stat as stat13, writeFile as writeFile44 } from "fs/promises";
|
|
105195
105264
|
import path79 from "path";
|
|
105196
105265
|
import { defineCommand as defineCommand232 } from "citty";
|
|
105197
105266
|
registerSchema({
|
|
@@ -105380,7 +105449,7 @@ async function writeVariant(opts) {
|
|
|
105380
105449
|
const written = [];
|
|
105381
105450
|
const indexText = await readFile61(path79.join(controlDir, "index.astro"), "utf8");
|
|
105382
105451
|
await mkdir30(variantDir, { recursive: true });
|
|
105383
|
-
await
|
|
105452
|
+
await writeFile44(
|
|
105384
105453
|
path79.join(variantDir, "index.astro"),
|
|
105385
105454
|
repointFile(indexText, { fromDir: controlDir, toDir: variantDir, controlDir, variantDir, forkedTargets }),
|
|
105386
105455
|
"utf8"
|
|
@@ -105391,7 +105460,7 @@ async function writeVariant(opts) {
|
|
|
105391
105460
|
const toFile = path79.join(variantDir, "_components", fork);
|
|
105392
105461
|
const text4 = await readFile61(fromFile, "utf8");
|
|
105393
105462
|
await mkdir30(path79.dirname(toFile), { recursive: true });
|
|
105394
|
-
await
|
|
105463
|
+
await writeFile44(
|
|
105395
105464
|
toFile,
|
|
105396
105465
|
repointFile(text4, {
|
|
105397
105466
|
fromDir: path79.dirname(fromFile),
|
|
@@ -105414,11 +105483,11 @@ async function writeVariant(opts) {
|
|
|
105414
105483
|
...opts.because ? { because: opts.because } : {},
|
|
105415
105484
|
...opts.change ? { change: opts.change } : {}
|
|
105416
105485
|
});
|
|
105417
|
-
await
|
|
105486
|
+
await writeFile44(path79.join(variantDir, "_definition.md"), definition, "utf8");
|
|
105418
105487
|
written.push(`src/pages/${variantSlug2}/_definition.md`);
|
|
105419
105488
|
}
|
|
105420
105489
|
await mkdir30(path79.join(variantDir, "_images"), { recursive: true });
|
|
105421
|
-
await
|
|
105490
|
+
await writeFile44(path79.join(variantDir, "_images", ".gitkeep"), "", "utf8");
|
|
105422
105491
|
return written;
|
|
105423
105492
|
}
|
|
105424
105493
|
var variantCommand = defineCommand232({
|