@koda-sl/baker-cli 0.267.0-dev.1f1c09c80 → 0.270.0-dev.1f1c09c80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/cli.js +339 -239
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9786,11 +9786,11 @@ function rawTextEntries(value) {
|
|
|
9786
9786
|
const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
|
|
9787
9787
|
return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
|
|
9788
9788
|
}
|
|
9789
|
-
function rawFileEntries(
|
|
9790
|
-
if (typeof
|
|
9789
|
+
function rawFileEntries(path41) {
|
|
9790
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
9791
9791
|
return [];
|
|
9792
9792
|
}
|
|
9793
|
-
return readFileSync2(
|
|
9793
|
+
return readFileSync2(path41, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
9794
9794
|
}
|
|
9795
9795
|
function keywordEntries(args) {
|
|
9796
9796
|
const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
|
|
@@ -9813,19 +9813,19 @@ function keywordEntries(args) {
|
|
|
9813
9813
|
}
|
|
9814
9814
|
return entries;
|
|
9815
9815
|
}
|
|
9816
|
-
function loadJsonFileArg(
|
|
9817
|
-
if (typeof
|
|
9816
|
+
function loadJsonFileArg(path41) {
|
|
9817
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
9818
9818
|
return {};
|
|
9819
9819
|
}
|
|
9820
9820
|
try {
|
|
9821
|
-
const parsed = JSON.parse(readFileSync2(
|
|
9821
|
+
const parsed = JSON.parse(readFileSync2(path41, "utf8"));
|
|
9822
9822
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
9823
|
-
failWriteValidation(`${
|
|
9823
|
+
failWriteValidation(`${path41} must contain a JSON object`);
|
|
9824
9824
|
}
|
|
9825
9825
|
return parsed;
|
|
9826
9826
|
} catch (err) {
|
|
9827
9827
|
if (err instanceof SyntaxError) {
|
|
9828
|
-
failWriteValidation(`${
|
|
9828
|
+
failWriteValidation(`${path41} is not valid JSON: ${err.message}`);
|
|
9829
9829
|
}
|
|
9830
9830
|
throw err;
|
|
9831
9831
|
}
|
|
@@ -9955,10 +9955,10 @@ async function stageUpdate(kind, customerId, target, payload, hints) {
|
|
|
9955
9955
|
async function stageTarget(kind, customerId, target, hints) {
|
|
9956
9956
|
await stageGoogleOp({ kind, customerId, target }, hints);
|
|
9957
9957
|
}
|
|
9958
|
-
async function draftAction(
|
|
9958
|
+
async function draftAction(path41, body, chat) {
|
|
9959
9959
|
try {
|
|
9960
9960
|
const chatId = resolveChatId(chat);
|
|
9961
|
-
const response = await apiPost(
|
|
9961
|
+
const response = await apiPost(path41, { chatId, ...body });
|
|
9962
9962
|
writeJsonEnvelope(response);
|
|
9963
9963
|
} catch (err) {
|
|
9964
9964
|
handleGoogleError(err);
|
|
@@ -15172,19 +15172,19 @@ function failWriteValidation2(message) {
|
|
|
15172
15172
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
15173
15173
|
process.exit(1);
|
|
15174
15174
|
}
|
|
15175
|
-
function loadJsonFileArg2(
|
|
15176
|
-
if (typeof
|
|
15175
|
+
function loadJsonFileArg2(path41) {
|
|
15176
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
15177
15177
|
return {};
|
|
15178
15178
|
}
|
|
15179
15179
|
try {
|
|
15180
|
-
const parsed = JSON.parse(readFileSync4(
|
|
15180
|
+
const parsed = JSON.parse(readFileSync4(path41, "utf8"));
|
|
15181
15181
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
15182
|
-
failWriteValidation2(`${
|
|
15182
|
+
failWriteValidation2(`${path41} must contain a JSON object`);
|
|
15183
15183
|
}
|
|
15184
15184
|
return parsed;
|
|
15185
15185
|
} catch (err) {
|
|
15186
15186
|
if (err instanceof SyntaxError) {
|
|
15187
|
-
failWriteValidation2(`${
|
|
15187
|
+
failWriteValidation2(`${path41} is not valid JSON: ${err.message}`);
|
|
15188
15188
|
}
|
|
15189
15189
|
throw err;
|
|
15190
15190
|
}
|
|
@@ -15269,15 +15269,15 @@ function parseLocaleFlag(value) {
|
|
|
15269
15269
|
}
|
|
15270
15270
|
return { language: match[1], country: match[2].toUpperCase() };
|
|
15271
15271
|
}
|
|
15272
|
-
function loadTargetingFileArg(
|
|
15273
|
-
if (typeof
|
|
15272
|
+
function loadTargetingFileArg(path41) {
|
|
15273
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
15274
15274
|
return void 0;
|
|
15275
15275
|
}
|
|
15276
|
-
const parsed = loadJsonFileArg2(
|
|
15276
|
+
const parsed = loadJsonFileArg2(path41);
|
|
15277
15277
|
const criteria = parsed.targetingCriteria ?? parsed;
|
|
15278
15278
|
if (!criteria.include) {
|
|
15279
15279
|
failWriteValidation2(
|
|
15280
|
-
`${
|
|
15280
|
+
`${path41} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
|
|
15281
15281
|
);
|
|
15282
15282
|
}
|
|
15283
15283
|
return criteria;
|
|
@@ -15312,14 +15312,14 @@ function parseCsvLine(line) {
|
|
|
15312
15312
|
cells.push(current);
|
|
15313
15313
|
return cells.map((cell2) => cell2.trim());
|
|
15314
15314
|
}
|
|
15315
|
-
function parseListFileArg(
|
|
15316
|
-
if (typeof
|
|
15315
|
+
function parseListFileArg(path41, maxRows) {
|
|
15316
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
15317
15317
|
return void 0;
|
|
15318
15318
|
}
|
|
15319
|
-
const raw = readFileSync4(
|
|
15319
|
+
const raw = readFileSync4(path41, "utf8");
|
|
15320
15320
|
const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
15321
15321
|
if (lines.length < 2) {
|
|
15322
|
-
failWriteValidation2(`${
|
|
15322
|
+
failWriteValidation2(`${path41} needs a header row and at least one data row`);
|
|
15323
15323
|
}
|
|
15324
15324
|
const columns = parseCsvLine(lines[0]).map((column) => column.trim());
|
|
15325
15325
|
const rows = [];
|
|
@@ -15338,7 +15338,7 @@ function parseListFileArg(path40, maxRows) {
|
|
|
15338
15338
|
}
|
|
15339
15339
|
}
|
|
15340
15340
|
if (rows.length > maxRows) {
|
|
15341
|
-
failWriteValidation2(`${
|
|
15341
|
+
failWriteValidation2(`${path41} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
|
|
15342
15342
|
}
|
|
15343
15343
|
return { columns, rows };
|
|
15344
15344
|
}
|
|
@@ -15434,11 +15434,11 @@ function readPositionals(args) {
|
|
|
15434
15434
|
function splitIdList(raw) {
|
|
15435
15435
|
return raw.split(",").map((id) => id.trim()).filter(Boolean);
|
|
15436
15436
|
}
|
|
15437
|
-
function idsFileEntries(
|
|
15438
|
-
if (typeof
|
|
15437
|
+
function idsFileEntries(path41) {
|
|
15438
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
15439
15439
|
return [];
|
|
15440
15440
|
}
|
|
15441
|
-
return readFileSync4(
|
|
15441
|
+
return readFileSync4(path41, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
|
|
15442
15442
|
}
|
|
15443
15443
|
function requireTargets(args, entity) {
|
|
15444
15444
|
const positionals = readPositionals(args);
|
|
@@ -18059,9 +18059,9 @@ function compactRow(row) {
|
|
|
18059
18059
|
...destination.postUrn ? { postUrn: destination.postUrn } : {}
|
|
18060
18060
|
};
|
|
18061
18061
|
}
|
|
18062
|
-
function readPath(row,
|
|
18062
|
+
function readPath(row, path41) {
|
|
18063
18063
|
let current = row;
|
|
18064
|
-
for (const segment of
|
|
18064
|
+
for (const segment of path41.split(".")) {
|
|
18065
18065
|
const record = asRecord2(current);
|
|
18066
18066
|
if (!record) return void 0;
|
|
18067
18067
|
current = record[segment];
|
|
@@ -18071,10 +18071,10 @@ function readPath(row, path40) {
|
|
|
18071
18071
|
function projectFields(rows, paths) {
|
|
18072
18072
|
return rows.map((row) => {
|
|
18073
18073
|
const projected = {};
|
|
18074
|
-
for (const
|
|
18075
|
-
const value = readPath(row,
|
|
18074
|
+
for (const path41 of paths) {
|
|
18075
|
+
const value = readPath(row, path41);
|
|
18076
18076
|
if (value !== void 0) {
|
|
18077
|
-
projected[
|
|
18077
|
+
projected[path41] = value;
|
|
18078
18078
|
}
|
|
18079
18079
|
}
|
|
18080
18080
|
return projected;
|
|
@@ -19374,11 +19374,11 @@ var updateStatusSchema = z25.enum(UPDATE_STATUSES);
|
|
|
19374
19374
|
function currencyMinimums2(currencyCode) {
|
|
19375
19375
|
return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
|
|
19376
19376
|
}
|
|
19377
|
-
function validateDailyBudgetFloor(money, ctx,
|
|
19377
|
+
function validateDailyBudgetFloor(money, ctx, path41) {
|
|
19378
19378
|
if (money?.currencyCode) {
|
|
19379
19379
|
const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
|
|
19380
19380
|
if (Number(money.amount) < min) {
|
|
19381
|
-
ctx.addIssue({ code: "custom", path:
|
|
19381
|
+
ctx.addIssue({ code: "custom", path: path41, message: `below the ${min} ${money.currencyCode} daily minimum` });
|
|
19382
19382
|
}
|
|
19383
19383
|
}
|
|
19384
19384
|
}
|
|
@@ -20047,19 +20047,19 @@ function failWriteValidation3(message) {
|
|
|
20047
20047
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
20048
20048
|
process.exit(1);
|
|
20049
20049
|
}
|
|
20050
|
-
function loadJsonFileArg3(
|
|
20051
|
-
if (typeof
|
|
20050
|
+
function loadJsonFileArg3(path41) {
|
|
20051
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
20052
20052
|
return {};
|
|
20053
20053
|
}
|
|
20054
20054
|
try {
|
|
20055
|
-
const parsed = JSON.parse(readFileSync8(
|
|
20055
|
+
const parsed = JSON.parse(readFileSync8(path41, "utf8"));
|
|
20056
20056
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
20057
|
-
failWriteValidation3(`${
|
|
20057
|
+
failWriteValidation3(`${path41} must contain a JSON object`);
|
|
20058
20058
|
}
|
|
20059
20059
|
return parsed;
|
|
20060
20060
|
} catch (err) {
|
|
20061
20061
|
if (err instanceof SyntaxError) {
|
|
20062
|
-
failWriteValidation3(`${
|
|
20062
|
+
failWriteValidation3(`${path41} is not valid JSON: ${err.message}`);
|
|
20063
20063
|
}
|
|
20064
20064
|
throw err;
|
|
20065
20065
|
}
|
|
@@ -24452,8 +24452,8 @@ import { defineCommand as defineCommand94 } from "citty";
|
|
|
24452
24452
|
import { defineCommand as defineCommand89 } from "citty";
|
|
24453
24453
|
|
|
24454
24454
|
// src/commands/avatars/casting.ts
|
|
24455
|
-
var CAST_FLAG_RULE = "Cast with `--avatar <handle>` on `baker studio generate` and `baker
|
|
24456
|
-
var VERBATIM_RULE = "When you do write the description yourself \u2014 a canvas node, a landing image, anywhere `--avatar` does not exist \u2014 copy `subjectDescription` VERBATIM, word for word. Re-phrasing it per generation is the other reason a face drifts across a set.";
|
|
24455
|
+
var CAST_FLAG_RULE = "Cast with `--avatar <handle>` on `baker studio generate`, `baker studio animate` and `baker canvas scaffold-ad`. Do NOT hand-roll it by pasting `subjectDescription` into the prompt or by passing the sheet through `--reference` \u2014 both render something that merely resembles them. `--avatar` is also the ONLY thing that carries their pinned VOICE and how they speak into a clip; without it the video model invents a different voice, and no later step can put theirs back.";
|
|
24456
|
+
var VERBATIM_RULE = "When you do write the description yourself \u2014 a hand-built canvas node, a landing image, anywhere `--avatar` does not exist \u2014 copy `subjectDescription` VERBATIM, word for word. Re-phrasing it per generation is the other reason a face drifts across a set.";
|
|
24457
24457
|
var VIDEO_ROUTING = "In video, a photoreal presenter renders on `google/veo-3.1` (or `google/veo-3.1-fast`), never `bytedance/seedance-2.0` \u2014 it refuses photoreal human faces, AI-generated ones included. Scaffolding a video creative: `baker canvas scaffold-video \u2026 --real-face`.";
|
|
24458
24458
|
function castingHints(avatar) {
|
|
24459
24459
|
if (avatar.status === "generating") {
|
|
@@ -24668,11 +24668,11 @@ function unwrap(response) {
|
|
|
24668
24668
|
}
|
|
24669
24669
|
return response.data;
|
|
24670
24670
|
}
|
|
24671
|
-
async function readAvatars(
|
|
24672
|
-
return unwrap(await apiGet(
|
|
24671
|
+
async function readAvatars(path41, params) {
|
|
24672
|
+
return unwrap(await apiGet(path41, params));
|
|
24673
24673
|
}
|
|
24674
|
-
async function writeAvatars(
|
|
24675
|
-
return unwrap(await apiPost(
|
|
24674
|
+
async function writeAvatars(path41, body) {
|
|
24675
|
+
return unwrap(await apiPost(path41, body));
|
|
24676
24676
|
}
|
|
24677
24677
|
|
|
24678
24678
|
// src/commands/avatars/create.ts
|
|
@@ -25516,12 +25516,12 @@ function missingFontFiles(urls, available) {
|
|
|
25516
25516
|
function planFontAdoption(sources, families) {
|
|
25517
25517
|
const wanted = new Map(families.map((family) => [normalizeFamily(family), family]));
|
|
25518
25518
|
const byFamily = /* @__PURE__ */ new Map();
|
|
25519
|
-
for (const { path:
|
|
25520
|
-
const dir = posix.dirname(
|
|
25519
|
+
for (const { path: path41, source } of sources) {
|
|
25520
|
+
const dir = posix.dirname(path41);
|
|
25521
25521
|
for (const face of declaredFontFaces(source)) {
|
|
25522
25522
|
if (!wanted.has(face.family)) continue;
|
|
25523
25523
|
const perFile = byFamily.get(face.family) ?? /* @__PURE__ */ new Map();
|
|
25524
|
-
perFile.set(
|
|
25524
|
+
perFile.set(path41, [...perFile.get(path41) ?? [], rebaseFontFaceSrc(face.block, dir)]);
|
|
25525
25525
|
byFamily.set(face.family, perFile);
|
|
25526
25526
|
}
|
|
25527
25527
|
}
|
|
@@ -30515,10 +30515,10 @@ function runDirsToPrune(entries, keep, currentRunId) {
|
|
|
30515
30515
|
return runs.slice(0, Math.max(0, runs.length - keep));
|
|
30516
30516
|
}
|
|
30517
30517
|
async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
|
|
30518
|
-
const { readdir:
|
|
30518
|
+
const { readdir: readdir11 } = await import("fs/promises");
|
|
30519
30519
|
let entries;
|
|
30520
30520
|
try {
|
|
30521
|
-
entries = await
|
|
30521
|
+
entries = await readdir11(outputsDir);
|
|
30522
30522
|
} catch {
|
|
30523
30523
|
return;
|
|
30524
30524
|
}
|
|
@@ -32436,8 +32436,8 @@ var scaffoldStaticAdCommand = defineCommand103({
|
|
|
32436
32436
|
});
|
|
32437
32437
|
|
|
32438
32438
|
// src/commands/canvas/scaffold-ad.ts
|
|
32439
|
-
import { copyFile, cp, mkdir as mkdir7, readFile as
|
|
32440
|
-
import
|
|
32439
|
+
import { copyFile, cp, mkdir as mkdir7, readFile as readFile16, writeFile as writeFile9 } from "fs/promises";
|
|
32440
|
+
import path24 from "path";
|
|
32441
32441
|
import { defineCommand as defineCommand104 } from "citty";
|
|
32442
32442
|
|
|
32443
32443
|
// src/engine/scaffold/ad-spec.ts
|
|
@@ -32600,13 +32600,33 @@ var VOICE_LANGUAGE_NAME = {
|
|
|
32600
32600
|
"en-us": "english",
|
|
32601
32601
|
"en-gb": "english"
|
|
32602
32602
|
};
|
|
32603
|
+
var SCRIPT_LANGUAGE_MARKERS = [
|
|
32604
|
+
["spanish", /\b(el|la|los|las|un|una|de|del|que|para|con|tu|su|más|ya|pero|porque|cada|hasta|sin)\b/gi],
|
|
32605
|
+
["portuguese", /\b(o|a|os|as|um|uma|de|do|da|que|para|com|seu|sua|mais|já|mas|porque|até|sem)\b/gi],
|
|
32606
|
+
["french", /\b(le|la|les|un|une|de|du|des|que|pour|avec|ton|son|plus|déjà|mais|parce|jusqu|sans)\b/gi],
|
|
32607
|
+
["italian", /\b(il|lo|la|gli|un|una|di|del|che|per|con|tuo|suo|più|già|ma|perché|fino|senza)\b/gi],
|
|
32608
|
+
["german", /\b(der|die|das|ein|eine|und|von|dem|den|für|mit|dein|sein|mehr|schon|aber|weil|ohne)\b/gi],
|
|
32609
|
+
["dutch", /\b(de|het|een|en|van|voor|met|jouw|zijn|meer|maar|omdat|zonder)\b/gi],
|
|
32610
|
+
["polish", /\b(i|w|na|do|jest|nie|to|się|dla|z|już|ale|bez)\b/gi],
|
|
32611
|
+
["english", /\b(the|a|an|of|to|for|with|your|more|already|but|because|until|without)\b/gi]
|
|
32612
|
+
];
|
|
32613
|
+
function detectScriptLanguage(lines) {
|
|
32614
|
+
const text2 = lines.join(" ");
|
|
32615
|
+
if (text2.trim().length < 12) return void 0;
|
|
32616
|
+
let best = null;
|
|
32617
|
+
for (const [name, re2] of SCRIPT_LANGUAGE_MARKERS) {
|
|
32618
|
+
const hits = (text2.match(re2) ?? []).length;
|
|
32619
|
+
if (hits > 0 && (!best || hits > best.hits)) best = { name, hits };
|
|
32620
|
+
}
|
|
32621
|
+
return best?.name;
|
|
32622
|
+
}
|
|
32603
32623
|
function voiceLanguageFor(spec) {
|
|
32604
32624
|
const declared = spec.voice?.language?.trim().toLowerCase();
|
|
32605
|
-
if (declared) return VOICE_LANGUAGE_NAME[declared];
|
|
32625
|
+
if (declared && VOICE_LANGUAGE_NAME[declared]) return VOICE_LANGUAGE_NAME[declared];
|
|
32606
32626
|
const market = spec.market?.trim().toLowerCase();
|
|
32607
|
-
|
|
32608
|
-
|
|
32609
|
-
return
|
|
32627
|
+
const byMarket = market ? Object.entries(MARKET_BY_LANGUAGE).find(([, place]) => place.toLowerCase() === market) : void 0;
|
|
32628
|
+
if (byMarket && VOICE_LANGUAGE_NAME[byMarket[0]]) return VOICE_LANGUAGE_NAME[byMarket[0]];
|
|
32629
|
+
return detectScriptLanguage(spec.beats.map((b) => b.say));
|
|
32610
32630
|
}
|
|
32611
32631
|
function marketFor(spec) {
|
|
32612
32632
|
if (spec.market) return spec.market;
|
|
@@ -32626,8 +32646,10 @@ function endCardWanted(spec) {
|
|
|
32626
32646
|
}
|
|
32627
32647
|
function endCardCta(spec) {
|
|
32628
32648
|
if (!endCardWanted(spec)) return null;
|
|
32629
|
-
const declared = spec.end_card === false ? void 0 : spec.end_card?.cta;
|
|
32630
|
-
|
|
32649
|
+
const declared = spec.end_card === false ? void 0 : spec.end_card?.cta?.trim();
|
|
32650
|
+
if (!declared) return null;
|
|
32651
|
+
const closing = (spec.beats[spec.beats.length - 1]?.say ?? "").trim().toLowerCase();
|
|
32652
|
+
return declared.toLowerCase().replace(/[.!?]$/, "") === closing.replace(/[.!?]$/, "") ? null : declared;
|
|
32631
32653
|
}
|
|
32632
32654
|
function adSpecCastElements(spec, avatar) {
|
|
32633
32655
|
const description = (avatar?.subjectDescription ?? spec.cast?.description)?.trim();
|
|
@@ -32783,14 +32805,20 @@ function adBrandOverlayCss() {
|
|
|
32783
32805
|
" viewBox is wider than it is tall rendered clipped \u2014 the first real ad drew",
|
|
32784
32806
|
" `greenlea`. Constraining the height and letting the width follow keeps any",
|
|
32785
32807
|
" aspect ratio whole, and the max-width stops a very wide mark spanning the frame. */",
|
|
32808
|
+
" /* Legible over ANY picture. A drop shadow alone left a pale mark invisible",
|
|
32809
|
+
" against a bright window or an overcast sky for the first third of an ad;",
|
|
32810
|
+
" a dark, blurred pill behind it reads on both without boxing the mark in. */",
|
|
32786
32811
|
" .brand-mark { position: absolute; top: 90px; left: 56px; height: 64px; width: auto;",
|
|
32787
|
-
" max-width: 420px; object-fit: contain;",
|
|
32788
|
-
"
|
|
32812
|
+
" max-width: 420px; object-fit: contain; padding: 14px 20px; border-radius: 18px;",
|
|
32813
|
+
" background: rgba(0,0,0,0.34); backdrop-filter: blur(6px);",
|
|
32814
|
+
" filter: drop-shadow(0 2px 8px rgba(0,0,0,0.55)); }",
|
|
32789
32815
|
" .endcard { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);",
|
|
32790
32816
|
" display: flex; flex-direction: column; align-items: center; gap: 56px;",
|
|
32791
32817
|
" width: 100%; max-width: 1080px; padding: 0 60px; text-align: center; }",
|
|
32792
|
-
"
|
|
32793
|
-
"
|
|
32818
|
+
" /* Sized to FILL, not merely bounded. Given only max-* constraints an SVG",
|
|
32819
|
+
" renders at its own intrinsic size, and a small viewBox drew a closing mark",
|
|
32820
|
+
" a few dozen pixels tall in the middle of a 1080px frame. */",
|
|
32821
|
+
" .endcard-logo { width: 62%; height: auto; max-height: 460px; object-fit: contain; }",
|
|
32794
32822
|
" /* Capped and wrapping, not sized to its content: measured in a real browser,",
|
|
32795
32823
|
" `GET YOUR FREE QUOTE` renders 948px wide against a 1080px canvas, so the next",
|
|
32796
32824
|
" slightly longer call to action runs off the frame. Two lines beat an edge. */",
|
|
@@ -32813,20 +32841,69 @@ function injectAdBrandOverlay(indexHtml, overlayHtml) {
|
|
|
32813
32841
|
${withCss.slice(rootClose)}`;
|
|
32814
32842
|
}
|
|
32815
32843
|
|
|
32844
|
+
// src/engine/scaffold/ad-brand-source.ts
|
|
32845
|
+
import { readdir as readdir7, readFile as readFile15 } from "fs/promises";
|
|
32846
|
+
import path22 from "path";
|
|
32847
|
+
var BRAND_DIR = "src/brand";
|
|
32848
|
+
function paletteFromBrandDoc(markdown) {
|
|
32849
|
+
const seen = /* @__PURE__ */ new Set();
|
|
32850
|
+
const out = [];
|
|
32851
|
+
for (const match of markdown.matchAll(/#([0-9a-fA-F]{6})\b/g)) {
|
|
32852
|
+
const hex = `#${match[1].toLowerCase()}`;
|
|
32853
|
+
if (seen.has(hex)) continue;
|
|
32854
|
+
seen.add(hex);
|
|
32855
|
+
const r = Number.parseInt(hex.slice(1, 3), 16);
|
|
32856
|
+
const g = Number.parseInt(hex.slice(3, 5), 16);
|
|
32857
|
+
const b = Number.parseInt(hex.slice(5, 7), 16);
|
|
32858
|
+
const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
32859
|
+
if (luma > 236 || luma < 14) continue;
|
|
32860
|
+
out.push(hex);
|
|
32861
|
+
}
|
|
32862
|
+
return out;
|
|
32863
|
+
}
|
|
32864
|
+
function pickBrandMark(files) {
|
|
32865
|
+
const usable = files.filter((f) => /\.(svg|png|webp)$/i.test(f));
|
|
32866
|
+
if (usable.length === 0) return null;
|
|
32867
|
+
const score = (f) => {
|
|
32868
|
+
const name = f.toLowerCase();
|
|
32869
|
+
let s = 0;
|
|
32870
|
+
if (name.endsWith(".svg")) s += 4;
|
|
32871
|
+
if (/\b(logo|wordmark|lockup)\b/.test(name)) s += 2;
|
|
32872
|
+
if (/\b(mono|white|black|inverse|icon|favicon|mark-only|isotipo)\b/.test(name)) s -= 3;
|
|
32873
|
+
return s;
|
|
32874
|
+
};
|
|
32875
|
+
return [...usable].sort((a, b) => score(b) - score(a) || a.localeCompare(b))[0] ?? null;
|
|
32876
|
+
}
|
|
32877
|
+
async function readBrandFromWorkspace(root = ".") {
|
|
32878
|
+
const found = {};
|
|
32879
|
+
const doc = await readFile15(path22.join(root, BRAND_DIR, "BRAND.md"), "utf-8").catch(() => null);
|
|
32880
|
+
if (doc) {
|
|
32881
|
+
const palette = paletteFromBrandDoc(doc);
|
|
32882
|
+
if (palette.length > 0) found.palette = palette;
|
|
32883
|
+
}
|
|
32884
|
+
const dir = path22.join(root, BRAND_DIR, "logos");
|
|
32885
|
+
const files = await readdir7(dir).catch(() => null);
|
|
32886
|
+
if (files) {
|
|
32887
|
+
const mark = pickBrandMark(files);
|
|
32888
|
+
if (mark) found.logo = path22.join(BRAND_DIR, "logos", mark);
|
|
32889
|
+
}
|
|
32890
|
+
return found;
|
|
32891
|
+
}
|
|
32892
|
+
|
|
32816
32893
|
// src/commands/canvas/composition-path.ts
|
|
32817
32894
|
import { existsSync as existsSync4 } from "fs";
|
|
32818
|
-
import
|
|
32895
|
+
import path23 from "path";
|
|
32819
32896
|
function resolveShippedCanvasDir(name, startDir, exists = existsSync4, maxDepth = 8) {
|
|
32820
|
-
const rel =
|
|
32897
|
+
const rel = path23.join("canvas", name);
|
|
32821
32898
|
let dir = startDir;
|
|
32822
32899
|
for (let i = 0; i < maxDepth; i++) {
|
|
32823
|
-
const candidate =
|
|
32824
|
-
if (exists(
|
|
32825
|
-
const parent =
|
|
32900
|
+
const candidate = path23.join(dir, rel);
|
|
32901
|
+
if (exists(path23.join(candidate, "meta.json"))) return candidate;
|
|
32902
|
+
const parent = path23.dirname(dir);
|
|
32826
32903
|
if (parent === dir) break;
|
|
32827
32904
|
dir = parent;
|
|
32828
32905
|
}
|
|
32829
|
-
return
|
|
32906
|
+
return path23.resolve(startDir, "../../../", rel);
|
|
32830
32907
|
}
|
|
32831
32908
|
|
|
32832
32909
|
// src/commands/canvas/scaffold-ad.ts
|
|
@@ -32842,6 +32919,11 @@ registerSchema({
|
|
|
32842
32919
|
required: true,
|
|
32843
32920
|
description: 'Path to the ad spec JSON. Shape: { format?, market?, brand?, cast?, end_card?, voice?, music?, beats: [{ say, show, on_camera?, cast? }] }. Fill `brand` from src/brand/BRAND.md \u2014 `palette` (its hex tokens, most important first) and `logo` (the repo path to the mark) are what make the ad look like the client rather than like stock. `market` is where the ad is SET; omitted, it is inferred from the voice language, and getting it wrong is what fills a Spanish ad with British houses. `say` is ONE clause ending in its own punctuation \u2014 it becomes a caption card verbatim, so two sentences in one beat produce a card holding both. `show` is the shot brief for that line. Set `on_camera` ONLY when that beat\'s subject talks to camera. `cast` is WHO the ad is about, and an ad with people in it needs one: `{ "avatar": "marta" }` names a cast avatar (`baker avatars list`) and every beat is grounded on that avatar\'s identity sheet with its subject description copied verbatim \u2014 the same face here as in the rest of the company\'s work. Without it each beat invents its own stranger, which is how one 28-second ad came back with five different men playing one customer. `{ "description": "..." }` is the fallback when there is no avatar, and it scaffolds a canvas that asks you to drop a photo before it can run \u2014 so prefer the avatar. A beat sets `cast: false` for a shot they are not in. `end_card` is on by default whenever `brand` is set: the last beat becomes a flat brand plate with the mark and a call to action drawn over it. `{ "cta": "..." }` sets the button copy, `false` keeps the footage.'
|
|
32844
32921
|
},
|
|
32922
|
+
avatar: {
|
|
32923
|
+
type: "string",
|
|
32924
|
+
required: false,
|
|
32925
|
+
description: "Cast this avatar as the ad's recurring person \u2014 the same flag, the same word and the same meaning as on `baker studio generate` and `baker studio animate`. Every beat they appear in is grounded on their identity sheet and their subject description is copied verbatim, so it is one face throughout and the same face as the rest of the company's work. Overrides `cast.avatar` in the spec. `baker avatars list` shows who there is."
|
|
32926
|
+
},
|
|
32845
32927
|
slug: {
|
|
32846
32928
|
type: "string",
|
|
32847
32929
|
required: false,
|
|
@@ -32861,6 +32943,7 @@ var scaffoldAdCommand = defineCommand104({
|
|
|
32861
32943
|
},
|
|
32862
32944
|
args: {
|
|
32863
32945
|
spec: { type: "string", required: true, description: "Path to the ad spec JSON" },
|
|
32946
|
+
avatar: { type: "string", required: false, description: "Cast this avatar as the ad's recurring person \u2014 the same flag, the same word and the same meaning as on `baker studio generate` and `baker studio animate`. Every beat they appear in is grounded on their identity sheet and their subject description is copied verbatim, so it is one face throughout and the same face as the rest of the company's work. Overrides `cast.avatar` in the spec. `baker avatars list` shows who there is." },
|
|
32864
32947
|
slug: {
|
|
32865
32948
|
type: "string",
|
|
32866
32949
|
required: false,
|
|
@@ -32872,7 +32955,7 @@ var scaffoldAdCommand = defineCommand104({
|
|
|
32872
32955
|
const specPath = args.spec;
|
|
32873
32956
|
let parsed;
|
|
32874
32957
|
try {
|
|
32875
|
-
parsed = JSON.parse(await
|
|
32958
|
+
parsed = JSON.parse(await readFile16(specPath, "utf-8"));
|
|
32876
32959
|
} catch (e) {
|
|
32877
32960
|
writeJson({
|
|
32878
32961
|
ok: false,
|
|
@@ -32898,7 +32981,11 @@ var scaffoldAdCommand = defineCommand104({
|
|
|
32898
32981
|
process.exit(1);
|
|
32899
32982
|
return;
|
|
32900
32983
|
}
|
|
32901
|
-
const
|
|
32984
|
+
const flagAvatar = args.avatar?.trim();
|
|
32985
|
+
if (flagAvatar) {
|
|
32986
|
+
spec.data.cast = { ...spec.data.cast, avatar: flagAvatar };
|
|
32987
|
+
}
|
|
32988
|
+
const handle = flagAvatar || spec.data.cast?.avatar?.trim();
|
|
32902
32989
|
let avatar = null;
|
|
32903
32990
|
let avatarError = null;
|
|
32904
32991
|
if (handle) {
|
|
@@ -32918,27 +33005,37 @@ var scaffoldAdCommand = defineCommand104({
|
|
|
32918
33005
|
avatarError = `Could not read avatar \`${handle}\`: ${e instanceof Error ? e.message : String(e)}. \`baker avatars list\` shows who there is.`;
|
|
32919
33006
|
}
|
|
32920
33007
|
}
|
|
33008
|
+
const fromWorkspace = await readBrandFromWorkspace();
|
|
33009
|
+
const filledLogo = !spec.data.brand?.logo && fromWorkspace.logo ? fromWorkspace.logo : void 0;
|
|
33010
|
+
const filledPalette = !spec.data.brand?.palette?.length && fromWorkspace.palette?.length ? fromWorkspace.palette : void 0;
|
|
33011
|
+
if (filledLogo || filledPalette) {
|
|
33012
|
+
spec.data.brand = {
|
|
33013
|
+
...spec.data.brand,
|
|
33014
|
+
...filledLogo ? { logo: filledLogo } : {},
|
|
33015
|
+
...filledPalette ? { palette: filledPalette } : {}
|
|
33016
|
+
};
|
|
33017
|
+
}
|
|
32921
33018
|
const blueprint = adSpecToBlueprint(spec.data);
|
|
32922
|
-
const slug = args.slug ??
|
|
32923
|
-
const outPath = args.out ?? (args.slug ?
|
|
32924
|
-
const outDir =
|
|
33019
|
+
const slug = args.slug ?? path24.basename(specPath).replace(/\.[^.]+$/, "");
|
|
33020
|
+
const outPath = args.out ?? (args.slug ? path24.join("src/creatives", slug, `${slug}.canvas.json`) : path24.join(path24.dirname(specPath), `${slug}.canvas.json`));
|
|
33021
|
+
const outDir = path24.dirname(outPath);
|
|
32925
33022
|
await mkdir7(outDir, { recursive: true });
|
|
32926
|
-
const compositionDest =
|
|
32927
|
-
const captionsDest =
|
|
33023
|
+
const compositionDest = path24.join(outDir, "video-overlay-composition");
|
|
33024
|
+
const captionsDest = path24.join(outDir, "tiktok-captions-composition");
|
|
32928
33025
|
await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
|
|
32929
33026
|
await cp(SHIPPED_CAPTIONS_DIR, captionsDest, { recursive: true });
|
|
32930
|
-
const blueprintPath =
|
|
32931
|
-
const blueprintStylePath =
|
|
33027
|
+
const blueprintPath = path24.join(outDir, "prompt.json");
|
|
33028
|
+
const blueprintStylePath = path24.join(outDir, "prompt.style.json");
|
|
32932
33029
|
await writeSceneFiles(outDir, blueprint);
|
|
32933
33030
|
await writeFile9(blueprintStylePath, renderStyleProjectionFromValue(blueprint), "utf8");
|
|
32934
33031
|
const logoPath = spec.data.brand?.logo?.trim();
|
|
32935
33032
|
const opts = {
|
|
32936
33033
|
imageModel: AD_IMAGE_MODEL,
|
|
32937
33034
|
videoModel: DEFAULT_VIDEO_GENERATE_MODEL,
|
|
32938
|
-
overlayCompositionPath:
|
|
32939
|
-
captionsCompositionPath:
|
|
32940
|
-
blueprintPath:
|
|
32941
|
-
blueprintStylePath:
|
|
33035
|
+
overlayCompositionPath: path24.relative(outDir, compositionDest),
|
|
33036
|
+
captionsCompositionPath: path24.relative(outDir, captionsDest),
|
|
33037
|
+
blueprintPath: path24.relative(outDir, blueprintPath),
|
|
33038
|
+
blueprintStylePath: path24.relative(outDir, blueprintStylePath),
|
|
32942
33039
|
aspect: spec.data.format.aspect_ratio,
|
|
32943
33040
|
resolution: spec.data.format.resolution,
|
|
32944
33041
|
// A beat is one clause and one card, so the cap has to clear a whole clause.
|
|
@@ -32952,12 +33049,12 @@ var scaffoldAdCommand = defineCommand104({
|
|
|
32952
33049
|
};
|
|
32953
33050
|
const canvas = scaffoldVideoCanvas(blueprint, adSpecCastElements(spec.data, avatar), opts);
|
|
32954
33051
|
const renderNode3 = canvas.nodes.find((n) => n.type === "hyperframe_render");
|
|
32955
|
-
const renderedDir = renderNode3 ?
|
|
33052
|
+
const renderedDir = renderNode3 ? path24.join(outDir, String(renderNode3.params?.composition ?? "")) : null;
|
|
32956
33053
|
let staged = null;
|
|
32957
33054
|
if (logoPath && renderedDir) {
|
|
32958
|
-
const ext =
|
|
33055
|
+
const ext = path24.extname(logoPath) || ".png";
|
|
32959
33056
|
try {
|
|
32960
|
-
await copyFile(logoPath,
|
|
33057
|
+
await copyFile(logoPath, path24.join(renderedDir, `brand-mark${ext}`));
|
|
32961
33058
|
staged = { file: `brand-mark${ext}`, alt: spec.data.brand?.name ?? "brand" };
|
|
32962
33059
|
} catch {
|
|
32963
33060
|
}
|
|
@@ -32972,8 +33069,8 @@ var scaffoldAdCommand = defineCommand104({
|
|
|
32972
33069
|
accent: firstHex(spec.data.brand?.palette)
|
|
32973
33070
|
}) : "";
|
|
32974
33071
|
if (overlayHtml && renderedDir) {
|
|
32975
|
-
const indexPath =
|
|
32976
|
-
const html = await
|
|
33072
|
+
const indexPath = path24.join(renderedDir, "index.html");
|
|
33073
|
+
const html = await readFile16(indexPath, "utf-8");
|
|
32977
33074
|
await writeFile9(indexPath, injectAdBrandOverlay(html, overlayHtml), "utf-8");
|
|
32978
33075
|
}
|
|
32979
33076
|
await writeFile9(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
@@ -32987,6 +33084,9 @@ var scaffoldAdCommand = defineCommand104({
|
|
|
32987
33084
|
...logoPath && !staged ? [`Could not read the logo at \`${logoPath}\` \u2014 the ad has no mark on screen. Give the path as it appears in src/brand/BRAND.md, relative to the workspace root.`] : [],
|
|
32988
33085
|
...staged ? ["The brand mark is drawn by the composition \u2014 top-left throughout, and large on the closing card. It is never generated as a picture, so it cannot come back garbled."] : [],
|
|
32989
33086
|
...endCardWanted(spec.data) ? ["The last beat closes on a flat brand plate with the call to action over it, instead of one more photograph."] : [],
|
|
33087
|
+
...filledLogo || filledPalette ? [
|
|
33088
|
+
`The spec left the brand empty, so it was read from the workspace: ${[filledLogo ? `logo \`${filledLogo}\`` : null, filledPalette ? `palette ${filledPalette.slice(0, 3).join(", ")}` : null].filter(Boolean).join(" and ")}. Pass them yourself when the ad needs a different mark or colour.`
|
|
33089
|
+
] : [],
|
|
32990
33090
|
...avatarError ? [avatarError] : [],
|
|
32991
33091
|
...avatar ? [
|
|
32992
33092
|
`Every beat the customer appears in is grounded on \`${handle}\`'s identity sheet, and their subject description was copied into the frames verbatim \u2014 so it is the same face throughout and the same face as the rest of this company's work.`
|
|
@@ -33016,9 +33116,9 @@ var scaffoldAdCommand = defineCommand104({
|
|
|
33016
33116
|
});
|
|
33017
33117
|
|
|
33018
33118
|
// src/commands/canvas/scaffold-video.ts
|
|
33019
|
-
import { access as access2, cp as cp2, mkdir as mkdir8, readFile as
|
|
33119
|
+
import { access as access2, cp as cp2, mkdir as mkdir8, readFile as readFile19, rm as rm7, writeFile as writeFile10 } from "fs/promises";
|
|
33020
33120
|
import { tmpdir as tmpdir3 } from "os";
|
|
33021
|
-
import
|
|
33121
|
+
import path26 from "path";
|
|
33022
33122
|
import { defineCommand as defineCommand105 } from "citty";
|
|
33023
33123
|
|
|
33024
33124
|
// src/engine/scaffold/lib/model-router.ts
|
|
@@ -33058,7 +33158,7 @@ function routeVideoModel(input) {
|
|
|
33058
33158
|
|
|
33059
33159
|
// src/engine/nodes/local/lib/sceneDetect.ts
|
|
33060
33160
|
import { execFile as execFile3 } from "child_process";
|
|
33061
|
-
import { mkdtemp as mkdtemp2, readdir as
|
|
33161
|
+
import { mkdtemp as mkdtemp2, readdir as readdir8, readFile as readFile17, rm as rm6 } from "fs/promises";
|
|
33062
33162
|
import { tmpdir as tmpdir2 } from "os";
|
|
33063
33163
|
import { join as join2 } from "path";
|
|
33064
33164
|
import { promisify as promisify3 } from "util";
|
|
@@ -33132,9 +33232,9 @@ async function runSceneDetectOnce(filePath, threshold, minSceneLenS, timeoutMs)
|
|
|
33132
33232
|
],
|
|
33133
33233
|
{ encoding: "utf-8", maxBuffer: 32 * 1024 * 1024, timeout: timeoutMs }
|
|
33134
33234
|
);
|
|
33135
|
-
const csvName = (await
|
|
33235
|
+
const csvName = (await readdir8(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
|
|
33136
33236
|
if (!csvName) return [];
|
|
33137
|
-
return parsePySceneDetectCsvCuts(await
|
|
33237
|
+
return parsePySceneDetectCsvCuts(await readFile17(join2(outDir, csvName), "utf-8"));
|
|
33138
33238
|
} finally {
|
|
33139
33239
|
await rm6(outDir, { recursive: true, force: true });
|
|
33140
33240
|
}
|
|
@@ -33158,8 +33258,8 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
|
|
|
33158
33258
|
}
|
|
33159
33259
|
|
|
33160
33260
|
// src/commands/canvas/gitignore.ts
|
|
33161
|
-
import { appendFile, readFile as
|
|
33162
|
-
import
|
|
33261
|
+
import { appendFile, readFile as readFile18 } from "fs/promises";
|
|
33262
|
+
import path25 from "path";
|
|
33163
33263
|
function missingGitignoreEntries(existing, entries) {
|
|
33164
33264
|
const present2 = new Set(
|
|
33165
33265
|
existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
|
|
@@ -33167,10 +33267,10 @@ function missingGitignoreEntries(existing, entries) {
|
|
|
33167
33267
|
return entries.filter((e) => !present2.has(e.trim().replace(/\/+$/, "")));
|
|
33168
33268
|
}
|
|
33169
33269
|
async function ensureGitignore(dir, entries) {
|
|
33170
|
-
const file =
|
|
33270
|
+
const file = path25.join(dir, ".gitignore");
|
|
33171
33271
|
let existing;
|
|
33172
33272
|
try {
|
|
33173
|
-
existing = await
|
|
33273
|
+
existing = await readFile18(file, "utf8");
|
|
33174
33274
|
} catch {
|
|
33175
33275
|
return;
|
|
33176
33276
|
}
|
|
@@ -33209,7 +33309,7 @@ ONE PERSON, MULTIPLE LOOKS: if a single individual plays MULTIPLE personas or wa
|
|
|
33209
33309
|
For each kept element return: { "type": one of person|animal|product|logo|badge|location, "label": a short UPPER_SNAKE_CASE name (e.g. HERO, CREATOR_SKEPTIC, INSURANCE_CARD, LOGO), "description": a concrete reusable description to source/shoot the real asset \u2014 for a person/animal give a NEUTRAL castable role (e.g. "hero pet-owner, woman in her 30s" or "a small beagle"), NOT the original individual's literal face/identity: we RECAST with a FRESH person/animal, so never tell the agent to reuse the original. "expression": a living subject's typical expression or null, "cast_id": the global.cast id if it maps to one else null, "same_as": the label of another element this is the SAME individual as (different wardrobe/persona) else null, "scenes": the 0-based indices of ONLY the scenes where the element is ACTUALLY VISIBLE ON SCREEN \u2014 judged from that scene's start_frame_prompt / end_frame_prompt subjects and its action_detail, NOT from who is merely speaking. A narrator heard over b-roll is NOT present in that b-roll scene; a dog-running cutaway does NOT contain the couch creator just because she talks across it. Do NOT pad the list \u2014 an element wrongly listed in a scene makes the reproduction render the wrong subject there (e.g. the creator appearing in a pure-dog b-roll). When in doubt, leave a scene OUT. Output ONLY the JSON object.`;
|
|
33210
33310
|
async function loadAssetText2(ref, label) {
|
|
33211
33311
|
const r = ref;
|
|
33212
|
-
if (typeof r?.path === "string") return
|
|
33312
|
+
if (typeof r?.path === "string") return readFile19(r.path, "utf8");
|
|
33213
33313
|
if (typeof r?.url === "string") {
|
|
33214
33314
|
const res = await fetch(r.url);
|
|
33215
33315
|
if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
|
|
@@ -33228,7 +33328,7 @@ async function loadTranscriptBestEffort(ref) {
|
|
|
33228
33328
|
async function stageCaptions(outDir, transcript) {
|
|
33229
33329
|
const text2 = transcript?.trim();
|
|
33230
33330
|
if (!text2 || text2 === "[]") return {};
|
|
33231
|
-
const compositionPath =
|
|
33331
|
+
const compositionPath = path26.join(outDir, "tiktok-captions-composition");
|
|
33232
33332
|
await cp2(SHIPPED_CAPTIONS_DIR2, compositionPath, { recursive: true });
|
|
33233
33333
|
return { compositionPath };
|
|
33234
33334
|
}
|
|
@@ -33246,11 +33346,11 @@ function patchCompositionHtml(html, dims) {
|
|
|
33246
33346
|
return html.replace(/(<meta\s+name="viewport"\s+content="width=)\d+(,\s*height=)\d+(")/i, `$1${dims.w}$2${dims.h}$3`).replace(/(width:\s*)\d+(px;\s*height:\s*)\d+(px;)/i, `$1${dims.w}$2${dims.h}$3`).replace(/(data-width=")\d+(")/i, `$1${dims.w}$2`).replace(/(data-height=")\d+(")/i, `$1${dims.h}$2`);
|
|
33247
33347
|
}
|
|
33248
33348
|
async function stampCompositionDims(compositionDir, dims) {
|
|
33249
|
-
const metaPath =
|
|
33250
|
-
const rawMeta = await
|
|
33349
|
+
const metaPath = path26.join(compositionDir, "meta.json");
|
|
33350
|
+
const rawMeta = await readFile19(metaPath, "utf8");
|
|
33251
33351
|
await writeFile10(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
|
|
33252
|
-
const htmlPath =
|
|
33253
|
-
const rawHtml = await
|
|
33352
|
+
const htmlPath = path26.join(compositionDir, "index.html");
|
|
33353
|
+
const rawHtml = await readFile19(htmlPath, "utf8");
|
|
33254
33354
|
await writeFile10(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
|
|
33255
33355
|
}
|
|
33256
33356
|
function parseElements2(raw) {
|
|
@@ -33298,7 +33398,7 @@ var VIDEO_EXT_BY_MIME = {
|
|
|
33298
33398
|
"video/x-matroska": ".mkv"
|
|
33299
33399
|
};
|
|
33300
33400
|
function referenceVideoExt(url, contentType) {
|
|
33301
|
-
const fromPath =
|
|
33401
|
+
const fromPath = path26.extname(new URL(url).pathname).toLowerCase();
|
|
33302
33402
|
if (fromPath && fromPath.length <= 5) return fromPath;
|
|
33303
33403
|
const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase();
|
|
33304
33404
|
return mime && VIDEO_EXT_BY_MIME[mime] || ".mp4";
|
|
@@ -33324,7 +33424,7 @@ function videoDefinitionDescription(blueprint) {
|
|
|
33324
33424
|
return typeof product === "string" && product.trim() ? product.trim() : void 0;
|
|
33325
33425
|
}
|
|
33326
33426
|
async function materializeReferenceVideo(fileArg2) {
|
|
33327
|
-
if (!/^https?:\/\//i.test(fileArg2)) return
|
|
33427
|
+
if (!/^https?:\/\//i.test(fileArg2)) return path26.resolve(fileArg2);
|
|
33328
33428
|
let bytes;
|
|
33329
33429
|
let contentType;
|
|
33330
33430
|
try {
|
|
@@ -33336,7 +33436,7 @@ async function materializeReferenceVideo(fileArg2) {
|
|
|
33336
33436
|
throw new Error(`failed to download reference video: ${e instanceof Error ? e.message : String(e)}`);
|
|
33337
33437
|
}
|
|
33338
33438
|
if (bytes.length === 0) throw new Error("reference video download was empty");
|
|
33339
|
-
const dest =
|
|
33439
|
+
const dest = path26.join(
|
|
33340
33440
|
tmpdir3(),
|
|
33341
33441
|
`baker-ref-${sha256Hex(bytes).slice(0, 16)}${referenceVideoExt(fileArg2, contentType)}`
|
|
33342
33442
|
);
|
|
@@ -33556,11 +33656,11 @@ var scaffoldVideoCommand = defineCommand105({
|
|
|
33556
33656
|
} catch (e) {
|
|
33557
33657
|
return fail4("download", e instanceof Error ? e.message : String(e));
|
|
33558
33658
|
}
|
|
33559
|
-
const base =
|
|
33560
|
-
const outPath = args.out ?
|
|
33561
|
-
const outDir =
|
|
33562
|
-
const blueprintPath =
|
|
33563
|
-
const blueprintStylePath =
|
|
33659
|
+
const base = path26.basename(videoPath, path26.extname(videoPath));
|
|
33660
|
+
const outPath = args.out ? path26.resolve(String(args.out)) : slug ? path26.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path26.join(path26.dirname(videoPath), `${base}.video.canvas.json`);
|
|
33661
|
+
const outDir = path26.dirname(outPath);
|
|
33662
|
+
const blueprintPath = path26.join(outDir, "prompt.json");
|
|
33663
|
+
const blueprintStylePath = path26.join(outDir, "prompt.style.json");
|
|
33564
33664
|
const frames = args.frames === "reuse" ? "reuse" : "generate";
|
|
33565
33665
|
const maxScenes = args["max-scenes"] ? Number(args["max-scenes"]) : void 0;
|
|
33566
33666
|
if (Number.isFinite(maxScenes)) {
|
|
@@ -33605,12 +33705,12 @@ var scaffoldVideoCommand = defineCommand105({
|
|
|
33605
33705
|
`
|
|
33606
33706
|
);
|
|
33607
33707
|
}
|
|
33608
|
-
const compositionDest =
|
|
33708
|
+
const compositionDest = path26.join(outDir, "video-overlay-composition");
|
|
33609
33709
|
await cp2(SHIPPED_COMPOSITION_DIR2, compositionDest, { recursive: true });
|
|
33610
33710
|
await stampCompositionDims(compositionDest, outDims);
|
|
33611
|
-
const indexPath =
|
|
33711
|
+
const indexPath = path26.join(compositionDest, "index.html");
|
|
33612
33712
|
const overlayHtml = buildOverlayHtml(blueprint, { captionsActive: Boolean(transcript) });
|
|
33613
|
-
const indexHtml = await
|
|
33713
|
+
const indexHtml = await readFile19(indexPath, "utf8");
|
|
33614
33714
|
const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
|
|
33615
33715
|
if (injected === indexHtml && overlayHtml.trim()) {
|
|
33616
33716
|
fail4(
|
|
@@ -33624,10 +33724,10 @@ var scaffoldVideoCommand = defineCommand105({
|
|
|
33624
33724
|
const opts = {
|
|
33625
33725
|
imageModel,
|
|
33626
33726
|
videoModel,
|
|
33627
|
-
overlayCompositionPath:
|
|
33628
|
-
captionsCompositionPath: captions.compositionPath ?
|
|
33629
|
-
blueprintPath:
|
|
33630
|
-
blueprintStylePath:
|
|
33727
|
+
overlayCompositionPath: path26.relative(outDir, compositionDest),
|
|
33728
|
+
captionsCompositionPath: captions.compositionPath ? path26.relative(outDir, captions.compositionPath) : void 0,
|
|
33729
|
+
blueprintPath: path26.relative(outDir, blueprintPath),
|
|
33730
|
+
blueprintStylePath: path26.relative(outDir, blueprintStylePath),
|
|
33631
33731
|
frames,
|
|
33632
33732
|
ambient: Boolean(args.ambient),
|
|
33633
33733
|
seamDedup: resolveSeamDedup(args["seam-dedup"]),
|
|
@@ -33655,7 +33755,7 @@ var scaffoldVideoCommand = defineCommand105({
|
|
|
33655
33755
|
await writeFile10(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
33656
33756
|
`, "utf8");
|
|
33657
33757
|
await writeFile10(
|
|
33658
|
-
|
|
33758
|
+
path26.join(outDir, REBUILD_FILE),
|
|
33659
33759
|
`${JSON.stringify({ elements, opts }, null, 2)}
|
|
33660
33760
|
`,
|
|
33661
33761
|
"utf8"
|
|
@@ -33680,7 +33780,7 @@ var scaffoldVideoCommand = defineCommand105({
|
|
|
33680
33780
|
await ensureGitignore(process.cwd(), ["canvas/", ".context/"]);
|
|
33681
33781
|
const sourceRef = videoSourceReference(blueprint, fileArg2);
|
|
33682
33782
|
if (slug) {
|
|
33683
|
-
const definitionPath =
|
|
33783
|
+
const definitionPath = path26.join(outDir, "_definition.md");
|
|
33684
33784
|
if (!await fileExists2(definitionPath)) {
|
|
33685
33785
|
await writeFile10(
|
|
33686
33786
|
definitionPath,
|
|
@@ -33735,7 +33835,7 @@ var scaffoldVideoCommand = defineCommand105({
|
|
|
33735
33835
|
graph: canvas.metadata?.video?.graph_stats
|
|
33736
33836
|
},
|
|
33737
33837
|
checklist: {
|
|
33738
|
-
edit_prompt: `The blueprint is split so you edit ONE small file at a time \u2014 never a giant one. Per-scene content (a scene's dialogue, action, frame prompts, overlays) lives in \`scenes/sNN.json\` \u2014 edit the single scene you want to change. Global cast/palette/brand/copy lives in \`${
|
|
33838
|
+
edit_prompt: `The blueprint is split so you edit ONE small file at a time \u2014 never a giant one. Per-scene content (a scene's dialogue, action, frame prompts, overlays) lives in \`scenes/sNN.json\` \u2014 edit the single scene you want to change. Global cast/palette/brand/copy lives in \`${path26.basename(blueprintPath)}\`. \`baker canvas validate\`/\`run\` re-assemble the blueprint and re-flow every edited scene back into the render (and regenerate ${path26.basename(blueprintStylePath)}, the projection each frame's target_blueprint reads) \u2014 so your scene edits reach the render automatically. Never hand-edit the inlined node prompts in the canvas or the derived ${path26.basename(blueprintStylePath)}; both are regenerated.`,
|
|
33739
33839
|
recurring_elements_to_supply: report.elements,
|
|
33740
33840
|
voices_to_confirm: report.dialogue.map((d) => ({
|
|
33741
33841
|
scene: d.scene,
|
|
@@ -33772,8 +33872,8 @@ var scaffoldVideoCommand = defineCommand105({
|
|
|
33772
33872
|
});
|
|
33773
33873
|
|
|
33774
33874
|
// src/commands/canvas/set-prompt.ts
|
|
33775
|
-
import { readFile as
|
|
33776
|
-
import
|
|
33875
|
+
import { readFile as readFile20, writeFile as writeFile11 } from "fs/promises";
|
|
33876
|
+
import path27 from "path";
|
|
33777
33877
|
import { defineCommand as defineCommand106 } from "citty";
|
|
33778
33878
|
function setNodePrompt(canvas, nodeId, text2) {
|
|
33779
33879
|
const nodes = canvas?.nodes;
|
|
@@ -33801,17 +33901,17 @@ var setPromptCommand = defineCommand106({
|
|
|
33801
33901
|
"text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
|
|
33802
33902
|
},
|
|
33803
33903
|
async run({ args }) {
|
|
33804
|
-
const filePath =
|
|
33904
|
+
const filePath = path27.resolve(String(args.file));
|
|
33805
33905
|
let canvas;
|
|
33806
33906
|
try {
|
|
33807
|
-
canvas = JSON.parse(await
|
|
33907
|
+
canvas = JSON.parse(await readFile20(filePath, "utf8"));
|
|
33808
33908
|
} catch (e) {
|
|
33809
33909
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "parse", message: String(e) } }, null, 2)}
|
|
33810
33910
|
`);
|
|
33811
33911
|
process.exit(2);
|
|
33812
33912
|
}
|
|
33813
33913
|
let text2;
|
|
33814
|
-
if (args["text-file"]) text2 = await
|
|
33914
|
+
if (args["text-file"]) text2 = await readFile20(path27.resolve(String(args["text-file"])), "utf8");
|
|
33815
33915
|
else if (args.text !== void 0) text2 = String(args.text);
|
|
33816
33916
|
else {
|
|
33817
33917
|
process.stderr.write(
|
|
@@ -33832,7 +33932,7 @@ var setPromptCommand = defineCommand106({
|
|
|
33832
33932
|
process.exit(2);
|
|
33833
33933
|
return;
|
|
33834
33934
|
}
|
|
33835
|
-
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated,
|
|
33935
|
+
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path27.dirname(filePath)), defaultRegistry());
|
|
33836
33936
|
if (!validation.ok) {
|
|
33837
33937
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
|
|
33838
33938
|
`);
|
|
@@ -33847,8 +33947,8 @@ var setPromptCommand = defineCommand106({
|
|
|
33847
33947
|
});
|
|
33848
33948
|
|
|
33849
33949
|
// src/commands/canvas/validate.ts
|
|
33850
|
-
import { readFile as
|
|
33851
|
-
import
|
|
33950
|
+
import { readFile as readFile21 } from "fs/promises";
|
|
33951
|
+
import path28 from "path";
|
|
33852
33952
|
import { defineCommand as defineCommand107 } from "citty";
|
|
33853
33953
|
var validateCommand = defineCommand107({
|
|
33854
33954
|
meta: {
|
|
@@ -33857,8 +33957,8 @@ var validateCommand = defineCommand107({
|
|
|
33857
33957
|
},
|
|
33858
33958
|
args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
|
|
33859
33959
|
async run({ args }) {
|
|
33860
|
-
const filePath =
|
|
33861
|
-
const raw = await
|
|
33960
|
+
const filePath = path28.resolve(String(args.file));
|
|
33961
|
+
const raw = await readFile21(filePath, "utf8");
|
|
33862
33962
|
let parsed;
|
|
33863
33963
|
try {
|
|
33864
33964
|
parsed = JSON.parse(raw);
|
|
@@ -33870,7 +33970,7 @@ var validateCommand = defineCommand107({
|
|
|
33870
33970
|
}
|
|
33871
33971
|
const healed = await healAbsoluteCanvasPaths(filePath, parsed);
|
|
33872
33972
|
parsed = healed.canvas;
|
|
33873
|
-
parsed = resolveRelativeCanvasPaths(parsed,
|
|
33973
|
+
parsed = resolveRelativeCanvasPaths(parsed, path28.dirname(filePath));
|
|
33874
33974
|
let styleProjection = "not_applicable";
|
|
33875
33975
|
try {
|
|
33876
33976
|
styleProjection = await syncStyleProjection(parsed, (line) => process.stderr.write(`${line}
|
|
@@ -34296,7 +34396,7 @@ import { defineCommand as defineCommand112 } from "citty";
|
|
|
34296
34396
|
import { defineCommand as defineCommand111 } from "citty";
|
|
34297
34397
|
|
|
34298
34398
|
// src/commands/images/api.ts
|
|
34299
|
-
import { readFile as
|
|
34399
|
+
import { readFile as readFile22 } from "fs/promises";
|
|
34300
34400
|
import { basename, extname } from "path";
|
|
34301
34401
|
var imageProcessingTimeoutMs = 18e4;
|
|
34302
34402
|
var imageReadyPollIntervalMs = 2e3;
|
|
@@ -34310,7 +34410,7 @@ var mimeMap = {
|
|
|
34310
34410
|
".avif": "image/avif"
|
|
34311
34411
|
};
|
|
34312
34412
|
var defaultImageApiDeps = {
|
|
34313
|
-
readFile:
|
|
34413
|
+
readFile: readFile22,
|
|
34314
34414
|
post: apiPost,
|
|
34315
34415
|
get: apiGet,
|
|
34316
34416
|
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
|
|
@@ -34572,12 +34672,12 @@ function collectSideEffects(tree) {
|
|
|
34572
34672
|
);
|
|
34573
34673
|
}
|
|
34574
34674
|
function readFlowTree(slug) {
|
|
34575
|
-
const
|
|
34576
|
-
if (!existsSync5(
|
|
34675
|
+
const path41 = join3(flowsDir(), slug, "_data.json");
|
|
34676
|
+
if (!existsSync5(path41)) {
|
|
34577
34677
|
failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
|
|
34578
34678
|
}
|
|
34579
34679
|
try {
|
|
34580
|
-
return JSON.parse(readFileSync9(
|
|
34680
|
+
return JSON.parse(readFileSync9(path41, "utf-8"));
|
|
34581
34681
|
} catch (error) {
|
|
34582
34682
|
failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
|
|
34583
34683
|
}
|
|
@@ -34969,10 +35069,10 @@ function parseValueExpression(raw) {
|
|
|
34969
35069
|
return parts.map(parsePart);
|
|
34970
35070
|
}
|
|
34971
35071
|
function trackingFieldIds() {
|
|
34972
|
-
const
|
|
34973
|
-
if (!existsSync6(
|
|
35072
|
+
const path41 = join4(flowsDir(), "..", "tracking.ts");
|
|
35073
|
+
if (!existsSync6(path41)) return null;
|
|
34974
35074
|
try {
|
|
34975
|
-
const source = readFileSync10(
|
|
35075
|
+
const source = readFileSync10(path41, "utf-8");
|
|
34976
35076
|
const block2 = source.match(/TRACKING_FIELD_IDS\s*=\s*\[([\s\S]*?)\]\s*as const/)?.[1];
|
|
34977
35077
|
if (!block2) return null;
|
|
34978
35078
|
const ids = [...block2.matchAll(/"(tracking\.[a-z0-9_]+)"/g)].map((match) => match[1]);
|
|
@@ -35524,13 +35624,13 @@ function specsFromFile(parsed) {
|
|
|
35524
35624
|
return `${destField}${type}=${entry?.value ?? ""}`;
|
|
35525
35625
|
});
|
|
35526
35626
|
}
|
|
35527
|
-
function readSpecFile(
|
|
35627
|
+
function readSpecFile(path41) {
|
|
35528
35628
|
let raw;
|
|
35529
35629
|
try {
|
|
35530
|
-
raw =
|
|
35630
|
+
raw = path41 === "-" ? readFileSync11(0, "utf-8") : readFileSync11(path41, "utf-8");
|
|
35531
35631
|
} catch (error) {
|
|
35532
35632
|
refuse(
|
|
35533
|
-
`Could not read ${
|
|
35633
|
+
`Could not read ${path41 === "-" ? "the mapping from stdin" : `"${path41}"`}: ${error instanceof Error ? error.message : String(error)}`
|
|
35534
35634
|
);
|
|
35535
35635
|
}
|
|
35536
35636
|
let parsed;
|
|
@@ -35538,7 +35638,7 @@ function readSpecFile(path40) {
|
|
|
35538
35638
|
parsed = JSON.parse(raw);
|
|
35539
35639
|
} catch (error) {
|
|
35540
35640
|
refuse(
|
|
35541
|
-
`${
|
|
35641
|
+
`${path41 === "-" ? "stdin" : `"${path41}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
35542
35642
|
'Expected { "map": { "<destField>": "<value>", \u2026 } }'
|
|
35543
35643
|
);
|
|
35544
35644
|
}
|
|
@@ -35792,18 +35892,18 @@ var ARRAY_FIELDS = [
|
|
|
35792
35892
|
"tagIds"
|
|
35793
35893
|
];
|
|
35794
35894
|
var ARRAY_OWNERS = ["", "body"];
|
|
35795
|
-
function dropUnsetOptionals(sideEffect,
|
|
35895
|
+
function dropUnsetOptionals(sideEffect, path41) {
|
|
35796
35896
|
return OPTIONAL_STRINGS.flatMap((key) => {
|
|
35797
35897
|
if (!(key in sideEffect) || sideEffect[key] !== null && sideEffect[key] !== "") return [];
|
|
35798
35898
|
delete sideEffect[key];
|
|
35799
|
-
return [{ path:
|
|
35899
|
+
return [{ path: path41, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
|
|
35800
35900
|
});
|
|
35801
35901
|
}
|
|
35802
|
-
function fillNulledArrays(target, prefix,
|
|
35902
|
+
function fillNulledArrays(target, prefix, path41) {
|
|
35803
35903
|
return ARRAY_FIELDS.flatMap((key) => {
|
|
35804
35904
|
if (!(key in target) || target[key] !== null) return [];
|
|
35805
35905
|
target[key] = [];
|
|
35806
|
-
return [{ path:
|
|
35906
|
+
return [{ path: path41, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
|
|
35807
35907
|
});
|
|
35808
35908
|
}
|
|
35809
35909
|
function sideEffectsOf(node) {
|
|
@@ -35813,13 +35913,13 @@ function sideEffectsOf(node) {
|
|
|
35813
35913
|
);
|
|
35814
35914
|
}
|
|
35815
35915
|
function normalizeSideEffect(sideEffect, where) {
|
|
35816
|
-
const
|
|
35916
|
+
const path41 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
|
|
35817
35917
|
const arrays = ARRAY_OWNERS.flatMap((owner) => {
|
|
35818
35918
|
const target = owner ? sideEffect[owner] : sideEffect;
|
|
35819
35919
|
if (!target || typeof target !== "object") return [];
|
|
35820
|
-
return fillNulledArrays(target, owner ? `${owner}.` : "",
|
|
35920
|
+
return fillNulledArrays(target, owner ? `${owner}.` : "", path41);
|
|
35821
35921
|
});
|
|
35822
|
-
return [...dropUnsetOptionals(sideEffect,
|
|
35922
|
+
return [...dropUnsetOptionals(sideEffect, path41), ...arrays];
|
|
35823
35923
|
}
|
|
35824
35924
|
function normalizeFlowTree(tree) {
|
|
35825
35925
|
const changes = [];
|
|
@@ -36357,10 +36457,10 @@ async function stageOps(ops) {
|
|
|
36357
36457
|
handleError2(err);
|
|
36358
36458
|
}
|
|
36359
36459
|
}
|
|
36360
|
-
async function draftAction2(
|
|
36460
|
+
async function draftAction2(path41, body, chat) {
|
|
36361
36461
|
const chatId = resolveChatId(chat);
|
|
36362
36462
|
try {
|
|
36363
|
-
const data = await apiPost(
|
|
36463
|
+
const data = await apiPost(path41, { chatId, ...body });
|
|
36364
36464
|
writeJsonEnvelope({ ok: true, data });
|
|
36365
36465
|
return data;
|
|
36366
36466
|
} catch (err) {
|
|
@@ -38757,7 +38857,7 @@ function cropSprite(input, region) {
|
|
|
38757
38857
|
|
|
38758
38858
|
// src/lib/image/io.ts
|
|
38759
38859
|
import { randomBytes } from "crypto";
|
|
38760
|
-
import { glob as fsGlob, readFile as
|
|
38860
|
+
import { glob as fsGlob, readFile as readFile23, rename, stat as stat4, writeFile as writeFile12 } from "fs/promises";
|
|
38761
38861
|
import { dirname as dirname2, extname as extname2, join as join5, resolve as resolve4 } from "path";
|
|
38762
38862
|
var REMOTE_RE = /^https?:\/\//i;
|
|
38763
38863
|
var GLOB_RE = /[*?[\]{}]/;
|
|
@@ -38790,11 +38890,11 @@ async function readImageBuffer(pathOrUrl) {
|
|
|
38790
38890
|
const { buffer } = await fetchExternalBytes(pathOrUrl, { maxBytes: MAX_REMOTE_IMAGE_BYTES });
|
|
38791
38891
|
return buffer;
|
|
38792
38892
|
}
|
|
38793
|
-
return
|
|
38893
|
+
return readFile23(pathOrUrl);
|
|
38794
38894
|
}
|
|
38795
|
-
async function isDirectory(
|
|
38895
|
+
async function isDirectory(path41) {
|
|
38796
38896
|
try {
|
|
38797
|
-
const s = await stat4(
|
|
38897
|
+
const s = await stat4(path41);
|
|
38798
38898
|
return s.isDirectory();
|
|
38799
38899
|
} catch {
|
|
38800
38900
|
return false;
|
|
@@ -39096,13 +39196,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
|
|
|
39096
39196
|
}
|
|
39097
39197
|
function disambiguate(paths) {
|
|
39098
39198
|
const taken = /* @__PURE__ */ new Set();
|
|
39099
|
-
return paths.map((
|
|
39100
|
-
if (!taken.has(
|
|
39101
|
-
taken.add(
|
|
39102
|
-
return
|
|
39199
|
+
return paths.map((path41) => {
|
|
39200
|
+
if (!taken.has(path41)) {
|
|
39201
|
+
taken.add(path41);
|
|
39202
|
+
return path41;
|
|
39103
39203
|
}
|
|
39104
|
-
const ext = extname3(
|
|
39105
|
-
const stem =
|
|
39204
|
+
const ext = extname3(path41);
|
|
39205
|
+
const stem = path41.slice(0, path41.length - ext.length);
|
|
39106
39206
|
let n = 2;
|
|
39107
39207
|
while (taken.has(`${stem}-${n}${ext}`)) n += 1;
|
|
39108
39208
|
const unique = `${stem}-${n}${ext}`;
|
|
@@ -39229,10 +39329,10 @@ async function runDownloads(plan) {
|
|
|
39229
39329
|
const paths = disambiguate(fetched.map((item) => item.path));
|
|
39230
39330
|
const downloaded = [];
|
|
39231
39331
|
for (const [index, item] of fetched.entries()) {
|
|
39232
|
-
const
|
|
39332
|
+
const path41 = paths[index] ?? item.path;
|
|
39233
39333
|
try {
|
|
39234
|
-
await atomicWrite(
|
|
39235
|
-
downloaded.push({ input: item.input, output:
|
|
39334
|
+
await atomicWrite(path41, item.buffer);
|
|
39335
|
+
downloaded.push({ input: item.input, output: path41, bytes: item.buffer.length, contentType: item.contentType });
|
|
39236
39336
|
} catch (err) {
|
|
39237
39337
|
failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
|
|
39238
39338
|
}
|
|
@@ -42191,8 +42291,8 @@ Full guide: __tooling__/docs/tools/baker/images.md`
|
|
|
42191
42291
|
import { defineCommand as defineCommand167 } from "citty";
|
|
42192
42292
|
|
|
42193
42293
|
// src/commands/landing/critique.ts
|
|
42194
|
-
import { readdir as
|
|
42195
|
-
import
|
|
42294
|
+
import { readdir as readdir10, stat as stat6 } from "fs/promises";
|
|
42295
|
+
import path31 from "path";
|
|
42196
42296
|
import { defineCommand as defineCommand157 } from "citty";
|
|
42197
42297
|
|
|
42198
42298
|
// src/engine/landing/lib/constants.ts
|
|
@@ -43160,13 +43260,13 @@ function describeCounts(findings) {
|
|
|
43160
43260
|
|
|
43161
43261
|
// src/commands/landing/snapshot.ts
|
|
43162
43262
|
import { mkdir as mkdir9, rename as rename2, writeFile as writeFile13 } from "fs/promises";
|
|
43163
|
-
import
|
|
43263
|
+
import path29 from "path";
|
|
43164
43264
|
var CRITIC_VERSION = "2";
|
|
43165
43265
|
function critiqueCacheDir(projectRoot) {
|
|
43166
|
-
return
|
|
43266
|
+
return path29.join(projectRoot, ".cache", "landing-critique");
|
|
43167
43267
|
}
|
|
43168
43268
|
function snapshotPath(projectRoot, slug) {
|
|
43169
|
-
return
|
|
43269
|
+
return path29.join(critiqueCacheDir(projectRoot), `${slug}.json`);
|
|
43170
43270
|
}
|
|
43171
43271
|
async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
43172
43272
|
await mkdir9(critiqueCacheDir(projectRoot), { recursive: true });
|
|
@@ -43178,21 +43278,21 @@ async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
|
43178
43278
|
}
|
|
43179
43279
|
|
|
43180
43280
|
// src/commands/landing/source-version.ts
|
|
43181
|
-
import { readdir as
|
|
43182
|
-
import
|
|
43281
|
+
import { readdir as readdir9, readFile as readFile24, stat as stat5 } from "fs/promises";
|
|
43282
|
+
import path30 from "path";
|
|
43183
43283
|
async function landingSourceRelPaths(landingDir) {
|
|
43184
43284
|
const rel = [];
|
|
43185
|
-
if (await isFile(
|
|
43186
|
-
const componentsDir =
|
|
43285
|
+
if (await isFile(path30.join(landingDir, "index.astro"))) rel.push("index.astro");
|
|
43286
|
+
const componentsDir = path30.join(landingDir, "_components");
|
|
43187
43287
|
for (const abs of await walkAstro(componentsDir)) {
|
|
43188
|
-
rel.push(
|
|
43288
|
+
rel.push(path30.relative(landingDir, abs).split(path30.sep).join("/"));
|
|
43189
43289
|
}
|
|
43190
43290
|
return rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
43191
43291
|
}
|
|
43192
43292
|
async function readLandingSources(landingDir) {
|
|
43193
43293
|
const rel = await landingSourceRelPaths(landingDir);
|
|
43194
43294
|
const out = [];
|
|
43195
|
-
for (const r of rel) out.push({ path: r, text: await
|
|
43295
|
+
for (const r of rel) out.push({ path: r, text: await readFile24(path30.join(landingDir, r), "utf8") });
|
|
43196
43296
|
return out;
|
|
43197
43297
|
}
|
|
43198
43298
|
async function computeLandingSourceSha(landingDir) {
|
|
@@ -43201,7 +43301,7 @@ async function computeLandingSourceSha(landingDir) {
|
|
|
43201
43301
|
for (const r of rel) {
|
|
43202
43302
|
let bytes;
|
|
43203
43303
|
try {
|
|
43204
|
-
bytes = await
|
|
43304
|
+
bytes = await readFile24(path30.join(landingDir, r));
|
|
43205
43305
|
} catch {
|
|
43206
43306
|
bytes = Buffer.alloc(0);
|
|
43207
43307
|
}
|
|
@@ -43219,13 +43319,13 @@ async function isFile(p) {
|
|
|
43219
43319
|
async function walkAstro(dir) {
|
|
43220
43320
|
let entries;
|
|
43221
43321
|
try {
|
|
43222
|
-
entries = await
|
|
43322
|
+
entries = await readdir9(dir, { withFileTypes: true });
|
|
43223
43323
|
} catch {
|
|
43224
43324
|
return [];
|
|
43225
43325
|
}
|
|
43226
43326
|
const out = [];
|
|
43227
43327
|
for (const entry of entries) {
|
|
43228
|
-
const abs =
|
|
43328
|
+
const abs = path30.join(dir, entry.name);
|
|
43229
43329
|
if (entry.isDirectory()) out.push(...await walkAstro(abs));
|
|
43230
43330
|
else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
|
|
43231
43331
|
}
|
|
@@ -43286,7 +43386,7 @@ var critiqueCommand2 = defineCommand157({
|
|
|
43286
43386
|
{ availableSlugs: await listLandingSlugs(projectRoot) }
|
|
43287
43387
|
);
|
|
43288
43388
|
}
|
|
43289
|
-
if (!await isDir(
|
|
43389
|
+
if (!await isDir(path31.resolve(projectRoot, "src", "pages", slug))) {
|
|
43290
43390
|
fail5("NOT_FOUND", `No landing at src/pages/${slug}/`, {
|
|
43291
43391
|
availableSlugs: await listLandingSlugs(projectRoot)
|
|
43292
43392
|
});
|
|
@@ -43325,7 +43425,7 @@ var critiqueCommand2 = defineCommand157({
|
|
|
43325
43425
|
}
|
|
43326
43426
|
});
|
|
43327
43427
|
async function critiqueOne(projectRoot, slug, brand) {
|
|
43328
|
-
const landingDir =
|
|
43428
|
+
const landingDir = path31.resolve(projectRoot, "src", "pages", slug);
|
|
43329
43429
|
const [sources, sourceSha] = await Promise.all([readLandingSources(landingDir), computeLandingSourceSha(landingDir)]);
|
|
43330
43430
|
const report = critiqueLanding({ slug, sources, brand });
|
|
43331
43431
|
let snapshotFailed = false;
|
|
@@ -43345,7 +43445,7 @@ async function critiqueOne(projectRoot, slug, brand) {
|
|
|
43345
43445
|
}
|
|
43346
43446
|
async function listLandingSlugs(projectRoot) {
|
|
43347
43447
|
try {
|
|
43348
|
-
const entries = await
|
|
43448
|
+
const entries = await readdir10(path31.join(projectRoot, "src", "pages"), { withFileTypes: true });
|
|
43349
43449
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
43350
43450
|
} catch {
|
|
43351
43451
|
return [];
|
|
@@ -43497,7 +43597,7 @@ var addCommand = defineCommand158({
|
|
|
43497
43597
|
|
|
43498
43598
|
// src/commands/landing/inspiration/code.ts
|
|
43499
43599
|
import { mkdir as mkdir10, writeFile as writeFile14 } from "fs/promises";
|
|
43500
|
-
import
|
|
43600
|
+
import path32 from "path";
|
|
43501
43601
|
import { defineCommand as defineCommand159 } from "citty";
|
|
43502
43602
|
registerSchema({
|
|
43503
43603
|
command: "landing.inspiration.code",
|
|
@@ -43520,9 +43620,9 @@ var codeCommand = defineCommand159({
|
|
|
43520
43620
|
try {
|
|
43521
43621
|
const id = args.id;
|
|
43522
43622
|
const data = await apiGet("/api/landing-inspiration/section-code", { id });
|
|
43523
|
-
const dir =
|
|
43623
|
+
const dir = path32.join(process.cwd(), ".baker", "inspiration", id);
|
|
43524
43624
|
await mkdir10(dir, { recursive: true });
|
|
43525
|
-
const file =
|
|
43625
|
+
const file = path32.join(dir, "section.html");
|
|
43526
43626
|
await writeFile14(file, data.html);
|
|
43527
43627
|
const hints = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
43528
43628
|
const fidelity = fidelityHint(data.fidelity);
|
|
@@ -43532,7 +43632,7 @@ var codeCommand = defineCommand159({
|
|
|
43532
43632
|
ok: true,
|
|
43533
43633
|
data: {
|
|
43534
43634
|
id,
|
|
43535
|
-
file:
|
|
43635
|
+
file: path32.relative(process.cwd(), file),
|
|
43536
43636
|
bytes: data.html.length,
|
|
43537
43637
|
fidelity: data.fidelity,
|
|
43538
43638
|
reproduction_notes: data.reproductionNotes,
|
|
@@ -43964,7 +44064,7 @@ function classifyCaptureFailure(error) {
|
|
|
43964
44064
|
|
|
43965
44065
|
// src/engine/landing-library/run.ts
|
|
43966
44066
|
import { mkdir as mkdir11, writeFile as writeFile16 } from "fs/promises";
|
|
43967
|
-
import
|
|
44067
|
+
import path34 from "path";
|
|
43968
44068
|
|
|
43969
44069
|
// ../proxy/src/preflight.ts
|
|
43970
44070
|
import http from "http";
|
|
@@ -45380,9 +45480,9 @@ async function renderBundleToPng(browser, html, viewportWidth, options = {}) {
|
|
|
45380
45480
|
|
|
45381
45481
|
// src/engine/landing-library/report.ts
|
|
45382
45482
|
import { writeFile as writeFile15 } from "fs/promises";
|
|
45383
|
-
import
|
|
45483
|
+
import path33 from "path";
|
|
45384
45484
|
async function writeCaptureReport(manifest, outDir) {
|
|
45385
|
-
const file =
|
|
45485
|
+
const file = path33.join(outDir, "report.html");
|
|
45386
45486
|
await writeFile15(file, renderReport(manifest));
|
|
45387
45487
|
return file;
|
|
45388
45488
|
}
|
|
@@ -45561,32 +45661,32 @@ async function reproducePage(args) {
|
|
|
45561
45661
|
const { browser, page, outDir, pageUrl, livePageShot } = args;
|
|
45562
45662
|
const built = await buildSectionBundle(page, "body", pageUrl).catch(() => null);
|
|
45563
45663
|
if (!built) return { bundle: null, fidelity: null };
|
|
45564
|
-
await writeFile16(
|
|
45664
|
+
await writeFile16(path34.join(outDir, "page.html"), built.html);
|
|
45565
45665
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width, {
|
|
45566
45666
|
wholePage: true,
|
|
45567
45667
|
timeoutMs: 6e4
|
|
45568
45668
|
});
|
|
45569
45669
|
if (!rendered || !livePageShot) return { bundle: "page.html", fidelity: null };
|
|
45570
|
-
await writeFile16(
|
|
45670
|
+
await writeFile16(path34.join(outDir, "page-rendered.png"), rendered);
|
|
45571
45671
|
const { score, note } = await scoreFidelity(livePageShot, rendered);
|
|
45572
45672
|
return { bundle: "page.html", fidelity: score, ...note ? { fidelityNote: note } : {} };
|
|
45573
45673
|
}
|
|
45574
45674
|
async function captureOneSection(args) {
|
|
45575
45675
|
const { browser, page, candidate, sectionsDir, outDir, pageUrl, withCode } = args;
|
|
45576
|
-
const dir =
|
|
45676
|
+
const dir = path34.join(sectionsDir, String(candidate.index).padStart(2, "0"));
|
|
45577
45677
|
await mkdir11(dir, { recursive: true });
|
|
45578
45678
|
const desktop = await captureSection(page, candidate);
|
|
45579
|
-
if (desktop) await writeFile16(
|
|
45679
|
+
if (desktop) await writeFile16(path34.join(dir, "desktop.png"), desktop);
|
|
45580
45680
|
const visualHash = desktop ? await perceptualHash(desktop) : null;
|
|
45581
45681
|
const motion = await collectMotion(page, candidate.selector);
|
|
45582
45682
|
const built = withCode ? await buildSectionBundle(page, candidate.selector, pageUrl) : null;
|
|
45583
45683
|
let fidelity = null;
|
|
45584
45684
|
let fidelityNote;
|
|
45585
45685
|
if (built) {
|
|
45586
|
-
await writeFile16(
|
|
45686
|
+
await writeFile16(path34.join(dir, "section.html"), built.html);
|
|
45587
45687
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width);
|
|
45588
45688
|
if (rendered && desktop) {
|
|
45589
|
-
await writeFile16(
|
|
45689
|
+
await writeFile16(path34.join(dir, "section-rendered.png"), rendered);
|
|
45590
45690
|
const result = await scoreFidelity(desktop, rendered);
|
|
45591
45691
|
fidelity = result.score;
|
|
45592
45692
|
fidelityNote = result.note;
|
|
@@ -45594,9 +45694,9 @@ async function captureOneSection(args) {
|
|
|
45594
45694
|
}
|
|
45595
45695
|
return {
|
|
45596
45696
|
...candidate,
|
|
45597
|
-
desktopShot: desktop ?
|
|
45697
|
+
desktopShot: desktop ? path34.relative(outDir, path34.join(dir, "desktop.png")) : null,
|
|
45598
45698
|
mobileShot: null,
|
|
45599
|
-
bundle: built ?
|
|
45699
|
+
bundle: built ? path34.relative(outDir, path34.join(dir, "section.html")) : null,
|
|
45600
45700
|
fidelity,
|
|
45601
45701
|
...fidelityNote ? { fidelityNote } : {},
|
|
45602
45702
|
...built ? { cssStats: built.stats } : {},
|
|
@@ -45615,9 +45715,9 @@ async function captureMobileShots(args) {
|
|
|
45615
45715
|
for (const section of sections) {
|
|
45616
45716
|
const shot = await captureSectionOnMobile(mobile.page, section);
|
|
45617
45717
|
if (!shot) continue;
|
|
45618
|
-
const file =
|
|
45718
|
+
const file = path34.join(sectionsDir, String(section.index).padStart(2, "0"), "mobile.png");
|
|
45619
45719
|
await writeFile16(file, shot);
|
|
45620
|
-
section.mobileShot =
|
|
45720
|
+
section.mobileShot = path34.relative(outDir, file);
|
|
45621
45721
|
}
|
|
45622
45722
|
} finally {
|
|
45623
45723
|
await mobile.context.close();
|
|
@@ -45632,10 +45732,10 @@ async function captureMotionTakes(args) {
|
|
|
45632
45732
|
const filmOne = async (section) => {
|
|
45633
45733
|
const take = await captureMotionTake(browser, pageUrl, section.selector).catch(() => null);
|
|
45634
45734
|
if (!take) return;
|
|
45635
|
-
const dir =
|
|
45636
|
-
const file =
|
|
45735
|
+
const dir = path34.join(sectionsDir, String(section.index).padStart(2, "0"));
|
|
45736
|
+
const file = path34.join(dir, "motion-filmstrip.png");
|
|
45637
45737
|
await writeFile16(file, take.filmstrip);
|
|
45638
|
-
section.motionFilmstrip =
|
|
45738
|
+
section.motionFilmstrip = path34.relative(outDir, file);
|
|
45639
45739
|
log(` [${section.index}] ${section.motion.summary}`);
|
|
45640
45740
|
};
|
|
45641
45741
|
const queue = [...moving];
|
|
@@ -45692,7 +45792,7 @@ async function captureAlternateViews(args) {
|
|
|
45692
45792
|
async function reproduceWholePage(args) {
|
|
45693
45793
|
const { browser, page, outDir, pageUrl, withCode, log } = args;
|
|
45694
45794
|
const fullPage = await page.screenshot({ type: "png", fullPage: true }).catch(() => null);
|
|
45695
|
-
if (fullPage) await writeFile16(
|
|
45795
|
+
if (fullPage) await writeFile16(path34.join(outDir, "full-page.png"), fullPage);
|
|
45696
45796
|
if (!withCode) return { bundle: null, fidelity: null };
|
|
45697
45797
|
const reproduction = await reproducePage({ browser, page, outDir, pageUrl, livePageShot: fullPage });
|
|
45698
45798
|
log(`page reproduction: ${reproduction.fidelity === null ? "unavailable" : reproduction.fidelity.toFixed(2)}`);
|
|
@@ -45763,7 +45863,7 @@ async function openViaLadder(args) {
|
|
|
45763
45863
|
async function scrapeLanding(options) {
|
|
45764
45864
|
const timeoutMs = options.timeoutMs ?? 45e3;
|
|
45765
45865
|
const log = options.onProgress ?? (() => void 0);
|
|
45766
|
-
const sectionsDir =
|
|
45866
|
+
const sectionsDir = path34.join(options.outDir, "sections");
|
|
45767
45867
|
const nonPublic = refuseNonPublicUrl(options.url);
|
|
45768
45868
|
if (nonPublic) {
|
|
45769
45869
|
throw new BlockedPageError({
|
|
@@ -45825,7 +45925,7 @@ async function scrapeLanding(options) {
|
|
|
45825
45925
|
security: prepared.security,
|
|
45826
45926
|
captureTier: tier
|
|
45827
45927
|
};
|
|
45828
|
-
await writeFile16(
|
|
45928
|
+
await writeFile16(path34.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
45829
45929
|
`);
|
|
45830
45930
|
if (options.report !== false) {
|
|
45831
45931
|
const reportPath = await writeCaptureReport(manifest, options.outDir);
|
|
@@ -45840,28 +45940,28 @@ async function scrapeLanding(options) {
|
|
|
45840
45940
|
|
|
45841
45941
|
// src/commands/landing/inspiration/captureOut.ts
|
|
45842
45942
|
import { existsSync as existsSync9 } from "fs";
|
|
45843
|
-
import
|
|
45943
|
+
import path35 from "path";
|
|
45844
45944
|
var SCRATCH_DIR = ".baker";
|
|
45845
45945
|
function isWithin(parent, target) {
|
|
45846
|
-
const relative =
|
|
45847
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
45946
|
+
const relative = path35.relative(parent, target);
|
|
45947
|
+
return relative === "" || !relative.startsWith("..") && !path35.isAbsolute(relative);
|
|
45848
45948
|
}
|
|
45849
45949
|
function findRepoRoot(from) {
|
|
45850
|
-
let dir =
|
|
45950
|
+
let dir = path35.resolve(from);
|
|
45851
45951
|
for (; ; ) {
|
|
45852
|
-
if (existsSync9(
|
|
45853
|
-
const parent =
|
|
45952
|
+
if (existsSync9(path35.join(dir, ".git"))) return dir;
|
|
45953
|
+
const parent = path35.dirname(dir);
|
|
45854
45954
|
if (parent === dir) return null;
|
|
45855
45955
|
dir = parent;
|
|
45856
45956
|
}
|
|
45857
45957
|
}
|
|
45858
45958
|
function checkCaptureOut(out, options) {
|
|
45859
45959
|
const { cwd, repoRoot } = options;
|
|
45860
|
-
const resolved =
|
|
45960
|
+
const resolved = path35.resolve(cwd, out);
|
|
45861
45961
|
if (repoRoot === null || !isWithin(repoRoot, resolved)) return { ok: true };
|
|
45862
|
-
const scratch =
|
|
45962
|
+
const scratch = path35.join(repoRoot, SCRATCH_DIR);
|
|
45863
45963
|
if (isWithin(scratch, resolved)) return { ok: true };
|
|
45864
|
-
const suggestion =
|
|
45964
|
+
const suggestion = path35.posix.join(SCRATCH_DIR, "teardowns", path35.basename(resolved) || "capture");
|
|
45865
45965
|
return {
|
|
45866
45966
|
ok: false,
|
|
45867
45967
|
error: {
|
|
@@ -46035,12 +46135,12 @@ var scrapeCommand = defineCommand162({
|
|
|
46035
46135
|
});
|
|
46036
46136
|
|
|
46037
46137
|
// src/commands/landing/inspiration/search.ts
|
|
46038
|
-
import
|
|
46138
|
+
import path37 from "path";
|
|
46039
46139
|
import { defineCommand as defineCommand163 } from "citty";
|
|
46040
46140
|
|
|
46041
46141
|
// src/commands/landing/inspiration/shot.ts
|
|
46042
46142
|
import { mkdir as mkdir12, writeFile as writeFile17 } from "fs/promises";
|
|
46043
|
-
import
|
|
46143
|
+
import path36 from "path";
|
|
46044
46144
|
import sharp6 from "sharp";
|
|
46045
46145
|
var READABLE_SHOT = {
|
|
46046
46146
|
maxWidth: 1440,
|
|
@@ -46066,9 +46166,9 @@ async function downloadReadableShot(url, file) {
|
|
|
46066
46166
|
const response = await fetch(url);
|
|
46067
46167
|
if (!response.ok) return null;
|
|
46068
46168
|
const shot = await toReadableShot(Buffer.from(await response.arrayBuffer()));
|
|
46069
|
-
await mkdir12(
|
|
46169
|
+
await mkdir12(path36.dirname(file), { recursive: true });
|
|
46070
46170
|
await writeFile17(file, shot);
|
|
46071
|
-
return
|
|
46171
|
+
return path36.relative(process.cwd(), file);
|
|
46072
46172
|
} catch {
|
|
46073
46173
|
return null;
|
|
46074
46174
|
}
|
|
@@ -46160,13 +46260,13 @@ function buildSearchBody(args) {
|
|
|
46160
46260
|
return body;
|
|
46161
46261
|
}
|
|
46162
46262
|
async function downloadShots(results) {
|
|
46163
|
-
const dir =
|
|
46263
|
+
const dir = path37.join(process.cwd(), ".baker", "inspiration");
|
|
46164
46264
|
const saved = /* @__PURE__ */ new Map();
|
|
46165
46265
|
await Promise.all(
|
|
46166
46266
|
results.map(async (result) => {
|
|
46167
46267
|
const file = await downloadReadableShot(
|
|
46168
46268
|
result.desktopShotUrl,
|
|
46169
|
-
|
|
46269
|
+
path37.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
|
|
46170
46270
|
);
|
|
46171
46271
|
if (file) saved.set(result.id, file);
|
|
46172
46272
|
})
|
|
@@ -46394,7 +46494,7 @@ var sequencesCommand = defineCommand164({
|
|
|
46394
46494
|
});
|
|
46395
46495
|
|
|
46396
46496
|
// src/commands/landing/inspiration/view.ts
|
|
46397
|
-
import
|
|
46497
|
+
import path38 from "path";
|
|
46398
46498
|
import { defineCommand as defineCommand165 } from "citty";
|
|
46399
46499
|
registerSchema({
|
|
46400
46500
|
command: "landing.inspiration.view",
|
|
@@ -46427,12 +46527,12 @@ var viewCommand2 = defineCommand165({
|
|
|
46427
46527
|
const id = args.id;
|
|
46428
46528
|
const data = await apiGet("/api/landing-inspiration/section", { id });
|
|
46429
46529
|
const section = data.section;
|
|
46430
|
-
const dir =
|
|
46530
|
+
const dir = path38.join(process.cwd(), ".baker", "inspiration", id);
|
|
46431
46531
|
const ext = READABLE_SHOT.extension;
|
|
46432
46532
|
const [desktop, mobile, filmstrip] = await Promise.all([
|
|
46433
|
-
downloadReadableShot(section.desktopShotUrl,
|
|
46434
|
-
downloadReadableShot(section.mobileShotUrl,
|
|
46435
|
-
downloadReadableShot(section.motionFilmstripUrl,
|
|
46533
|
+
downloadReadableShot(section.desktopShotUrl, path38.join(dir, `desktop.${ext}`)),
|
|
46534
|
+
downloadReadableShot(section.mobileShotUrl, path38.join(dir, `mobile.${ext}`)),
|
|
46535
|
+
downloadReadableShot(section.motionFilmstripUrl, path38.join(dir, `motion-filmstrip.${ext}`))
|
|
46436
46536
|
]);
|
|
46437
46537
|
const full = args.full;
|
|
46438
46538
|
const hints = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
@@ -48281,8 +48381,8 @@ var listCommand15 = defineCommand184({
|
|
|
48281
48381
|
});
|
|
48282
48382
|
|
|
48283
48383
|
// src/commands/scheduled-actions/templates.ts
|
|
48284
|
-
import { readFile as
|
|
48285
|
-
import
|
|
48384
|
+
import { readFile as readFile25 } from "fs/promises";
|
|
48385
|
+
import path39 from "path";
|
|
48286
48386
|
import { defineCommand as defineCommand185 } from "citty";
|
|
48287
48387
|
registerSchema({
|
|
48288
48388
|
command: "scheduled-actions.templates",
|
|
@@ -48385,7 +48485,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
|
|
|
48385
48485
|
}
|
|
48386
48486
|
if (save.length > 0) {
|
|
48387
48487
|
const briefFile = flag("brief-file");
|
|
48388
|
-
const brief = briefFile.length > 0 ? await
|
|
48488
|
+
const brief = briefFile.length > 0 ? await readFile25(path39.resolve(briefFile), "utf8") : flag("brief");
|
|
48389
48489
|
if (brief.trim().length === 0) {
|
|
48390
48490
|
failValidation4("--brief-file (preferred) or --brief is required: the brief is the recipe.");
|
|
48391
48491
|
}
|
|
@@ -48856,7 +48956,7 @@ function parseImageRefs(spec) {
|
|
|
48856
48956
|
}
|
|
48857
48957
|
var defaultDeps = {
|
|
48858
48958
|
ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
|
|
48859
|
-
upload: (
|
|
48959
|
+
upload: (path41) => uploadLocalImage({ file: path41, contentType: detectImageContentType(path41), source: "uploaded" })
|
|
48860
48960
|
};
|
|
48861
48961
|
async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
48862
48962
|
const refs = parseImageRefs(spec);
|
|
@@ -48876,10 +48976,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
|
48876
48976
|
}
|
|
48877
48977
|
return { imageIds, added };
|
|
48878
48978
|
}
|
|
48879
|
-
function uploadFailure(
|
|
48979
|
+
function uploadFailure(path41) {
|
|
48880
48980
|
return (error) => {
|
|
48881
48981
|
if (error instanceof ApiError) throw error;
|
|
48882
|
-
throw new ApiError("VALIDATION_ERROR", `Could not read "${
|
|
48982
|
+
throw new ApiError("VALIDATION_ERROR", `Could not read "${path41}" as an image.`);
|
|
48883
48983
|
};
|
|
48884
48984
|
}
|
|
48885
48985
|
|
|
@@ -49951,10 +50051,10 @@ async function stageOp4(op) {
|
|
|
49951
50051
|
handleError5(err);
|
|
49952
50052
|
}
|
|
49953
50053
|
}
|
|
49954
|
-
async function draftAction3(
|
|
50054
|
+
async function draftAction3(path41, body, chat) {
|
|
49955
50055
|
const chatId = resolveChatId(chat);
|
|
49956
50056
|
try {
|
|
49957
|
-
const data = await apiPost(
|
|
50057
|
+
const data = await apiPost(path41, { chatId, ...body });
|
|
49958
50058
|
writeJsonEnvelope({ ok: true, data });
|
|
49959
50059
|
return data;
|
|
49960
50060
|
} catch (err) {
|
|
@@ -51061,7 +51161,7 @@ var groupCommand2 = defineCommand210({
|
|
|
51061
51161
|
// src/commands/videos/ingest.ts
|
|
51062
51162
|
import { mkdtemp as mkdtemp3, rm as rm8, stat as stat7 } from "fs/promises";
|
|
51063
51163
|
import { tmpdir as tmpdir4 } from "os";
|
|
51064
|
-
import
|
|
51164
|
+
import path40 from "path";
|
|
51065
51165
|
import { defineCommand as defineCommand211 } from "citty";
|
|
51066
51166
|
|
|
51067
51167
|
// src/lib/streamUpload.ts
|
|
@@ -51412,7 +51512,7 @@ function ingestUrl(args) {
|
|
|
51412
51512
|
}
|
|
51413
51513
|
async function downloadThenIngest(args, country) {
|
|
51414
51514
|
const vimeoCookie = captureVimeoCookie();
|
|
51415
|
-
const workDir = await mkdtemp3(
|
|
51515
|
+
const workDir = await mkdtemp3(path40.join(tmpdir4(), "videos-ingest-"));
|
|
51416
51516
|
try {
|
|
51417
51517
|
const probe = await probeYtDlp({ url: args.url, country, vimeoCookie, cookieDir: workDir });
|
|
51418
51518
|
if (isAudioOnly(probe.info)) {
|
|
@@ -51548,7 +51648,7 @@ var searchCommand4 = defineCommand212({
|
|
|
51548
51648
|
var tagsCommand6 = makeTagsCommand("videos", "video", "/api/videos/tags");
|
|
51549
51649
|
|
|
51550
51650
|
// src/commands/videos/upload.ts
|
|
51551
|
-
import { readFile as
|
|
51651
|
+
import { readFile as readFile26, stat as stat8 } from "fs/promises";
|
|
51552
51652
|
import { basename as basename3, extname as extname4 } from "path";
|
|
51553
51653
|
import { defineCommand as defineCommand213 } from "citty";
|
|
51554
51654
|
var MIME_MAP = {
|
|
@@ -51645,7 +51745,7 @@ var uploadCommand2 = defineCommand213({
|
|
|
51645
51745
|
originalFilename,
|
|
51646
51746
|
descriptionContext
|
|
51647
51747
|
});
|
|
51648
|
-
const fileBuffer = await
|
|
51748
|
+
const fileBuffer = await readFile26(filePath);
|
|
51649
51749
|
const uploadResponse = await fetch(uploadUrl, {
|
|
51650
51750
|
method: "PUT",
|
|
51651
51751
|
headers: { "Content-Type": contentType },
|
|
@@ -53032,7 +53132,7 @@ function unknownFlagEnvelope(unknown, commandPath, suggestion) {
|
|
|
53032
53132
|
};
|
|
53033
53133
|
}
|
|
53034
53134
|
function commandPathOf(root, argv) {
|
|
53035
|
-
const
|
|
53135
|
+
const path41 = [];
|
|
53036
53136
|
let command = root;
|
|
53037
53137
|
for (const token of argv) {
|
|
53038
53138
|
if (token === "--" || token.startsWith("-")) {
|
|
@@ -53043,10 +53143,10 @@ function commandPathOf(root, argv) {
|
|
|
53043
53143
|
if (next === void 0 || typeof next !== "object") {
|
|
53044
53144
|
break;
|
|
53045
53145
|
}
|
|
53046
|
-
|
|
53146
|
+
path41.push(token);
|
|
53047
53147
|
command = next;
|
|
53048
53148
|
}
|
|
53049
|
-
return
|
|
53149
|
+
return path41.join(" ");
|
|
53050
53150
|
}
|
|
53051
53151
|
function refuseUnknownFlags(root, argv) {
|
|
53052
53152
|
const unknown = findUnknownFlags(root, argv);
|