@koda-sl/baker-cli 0.115.0-dev.3bcc79f9c → 0.116.0-dev.3bcc79f9c
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 +83 -202
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -157,9 +157,9 @@ async function handleResponse(response) {
|
|
|
157
157
|
throw new ApiError("INTERNAL_ERROR", "Failed to parse API response as JSON");
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
|
-
async function apiGet(
|
|
160
|
+
async function apiGet(path13, params) {
|
|
161
161
|
const env = getEnv();
|
|
162
|
-
const url = new URL(
|
|
162
|
+
const url = new URL(path13, env.BAKER_API_URL);
|
|
163
163
|
if (params) {
|
|
164
164
|
const clean = sanitizeParams(params);
|
|
165
165
|
for (const [key, value] of Object.entries(clean)) {
|
|
@@ -184,12 +184,12 @@ async function apiGet(path14, params) {
|
|
|
184
184
|
}
|
|
185
185
|
return handleResponse(response);
|
|
186
186
|
}
|
|
187
|
-
async function apiPost(
|
|
187
|
+
async function apiPost(path13, body, opts) {
|
|
188
188
|
const env = getEnv();
|
|
189
189
|
const timeoutMs = opts?.timeoutMs ?? 6e4;
|
|
190
190
|
let response;
|
|
191
191
|
try {
|
|
192
|
-
response = await fetchWithRateLimitRetry(new URL(
|
|
192
|
+
response = await fetchWithRateLimitRetry(new URL(path13, env.BAKER_API_URL).toString(), {
|
|
193
193
|
method: "POST",
|
|
194
194
|
headers: {
|
|
195
195
|
Authorization: `Bearer ${env.BAKER_API_KEY}`,
|
|
@@ -2881,31 +2881,31 @@ function cachePath(category, key) {
|
|
|
2881
2881
|
return join2(dir, `${hashKey(key)}.json`);
|
|
2882
2882
|
}
|
|
2883
2883
|
function cacheGet(category, key) {
|
|
2884
|
-
const
|
|
2885
|
-
if (!existsSync2(
|
|
2884
|
+
const path13 = cachePath(category, key);
|
|
2885
|
+
if (!existsSync2(path13)) {
|
|
2886
2886
|
return null;
|
|
2887
2887
|
}
|
|
2888
2888
|
try {
|
|
2889
|
-
const raw = readFileSync2(
|
|
2889
|
+
const raw = readFileSync2(path13, "utf-8");
|
|
2890
2890
|
const entry = JSON.parse(raw);
|
|
2891
2891
|
if (entry.expiresAt < Date.now()) {
|
|
2892
|
-
rmSync(
|
|
2892
|
+
rmSync(path13, { force: true });
|
|
2893
2893
|
return null;
|
|
2894
2894
|
}
|
|
2895
2895
|
return entry;
|
|
2896
2896
|
} catch {
|
|
2897
|
-
rmSync(
|
|
2897
|
+
rmSync(path13, { force: true });
|
|
2898
2898
|
return null;
|
|
2899
2899
|
}
|
|
2900
2900
|
}
|
|
2901
2901
|
function cacheSet(category, key, data, ttlMs, fields) {
|
|
2902
|
-
const
|
|
2902
|
+
const path13 = cachePath(category, key);
|
|
2903
2903
|
const entry = {
|
|
2904
2904
|
expiresAt: Date.now() + ttlMs,
|
|
2905
2905
|
data,
|
|
2906
2906
|
fields
|
|
2907
2907
|
};
|
|
2908
|
-
writeFileSync(
|
|
2908
|
+
writeFileSync(path13, JSON.stringify(entry), "utf-8");
|
|
2909
2909
|
}
|
|
2910
2910
|
var HOUR = 60 * 60 * 1e3;
|
|
2911
2911
|
var MINUTE = 60 * 1e3;
|
|
@@ -4818,11 +4818,11 @@ function rawTextEntries(value) {
|
|
|
4818
4818
|
const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
|
|
4819
4819
|
return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
|
|
4820
4820
|
}
|
|
4821
|
-
function rawFileEntries(
|
|
4822
|
-
if (typeof
|
|
4821
|
+
function rawFileEntries(path13) {
|
|
4822
|
+
if (typeof path13 !== "string" || path13.length === 0) {
|
|
4823
4823
|
return [];
|
|
4824
4824
|
}
|
|
4825
|
-
return readFileSync3(
|
|
4825
|
+
return readFileSync3(path13, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
4826
4826
|
}
|
|
4827
4827
|
function keywordEntries(args) {
|
|
4828
4828
|
const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
|
|
@@ -4845,19 +4845,19 @@ function keywordEntries(args) {
|
|
|
4845
4845
|
}
|
|
4846
4846
|
return entries;
|
|
4847
4847
|
}
|
|
4848
|
-
function loadJsonFileArg(
|
|
4849
|
-
if (typeof
|
|
4848
|
+
function loadJsonFileArg(path13) {
|
|
4849
|
+
if (typeof path13 !== "string" || path13.length === 0) {
|
|
4850
4850
|
return {};
|
|
4851
4851
|
}
|
|
4852
4852
|
try {
|
|
4853
|
-
const parsed = JSON.parse(readFileSync3(
|
|
4853
|
+
const parsed = JSON.parse(readFileSync3(path13, "utf8"));
|
|
4854
4854
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
4855
|
-
failWriteValidation(`${
|
|
4855
|
+
failWriteValidation(`${path13} must contain a JSON object`);
|
|
4856
4856
|
}
|
|
4857
4857
|
return parsed;
|
|
4858
4858
|
} catch (err) {
|
|
4859
4859
|
if (err instanceof SyntaxError) {
|
|
4860
|
-
failWriteValidation(`${
|
|
4860
|
+
failWriteValidation(`${path13} is not valid JSON: ${err.message}`);
|
|
4861
4861
|
}
|
|
4862
4862
|
throw err;
|
|
4863
4863
|
}
|
|
@@ -4946,10 +4946,10 @@ async function stageUpdate(kind, customerId, target, payload) {
|
|
|
4946
4946
|
async function stageTarget(kind, customerId, target) {
|
|
4947
4947
|
await stageGoogleOp({ kind, customerId, target });
|
|
4948
4948
|
}
|
|
4949
|
-
async function draftAction(
|
|
4949
|
+
async function draftAction(path13, body) {
|
|
4950
4950
|
try {
|
|
4951
4951
|
const chatId = requireChatId();
|
|
4952
|
-
const response = await apiPost(
|
|
4952
|
+
const response = await apiPost(path13, { chatId, ...body });
|
|
4953
4953
|
writeJsonEnvelope(response);
|
|
4954
4954
|
} catch (err) {
|
|
4955
4955
|
handleGoogleError(err);
|
|
@@ -8490,19 +8490,19 @@ function failWriteValidation2(message) {
|
|
|
8490
8490
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
8491
8491
|
process.exit(1);
|
|
8492
8492
|
}
|
|
8493
|
-
function loadJsonFileArg2(
|
|
8494
|
-
if (typeof
|
|
8493
|
+
function loadJsonFileArg2(path13) {
|
|
8494
|
+
if (typeof path13 !== "string" || path13.length === 0) {
|
|
8495
8495
|
return {};
|
|
8496
8496
|
}
|
|
8497
8497
|
try {
|
|
8498
|
-
const parsed = JSON.parse(readFileSync7(
|
|
8498
|
+
const parsed = JSON.parse(readFileSync7(path13, "utf8"));
|
|
8499
8499
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
8500
|
-
failWriteValidation2(`${
|
|
8500
|
+
failWriteValidation2(`${path13} must contain a JSON object`);
|
|
8501
8501
|
}
|
|
8502
8502
|
return parsed;
|
|
8503
8503
|
} catch (err) {
|
|
8504
8504
|
if (err instanceof SyntaxError) {
|
|
8505
|
-
failWriteValidation2(`${
|
|
8505
|
+
failWriteValidation2(`${path13} is not valid JSON: ${err.message}`);
|
|
8506
8506
|
}
|
|
8507
8507
|
throw err;
|
|
8508
8508
|
}
|
|
@@ -8565,15 +8565,15 @@ function parseLocaleFlag(value) {
|
|
|
8565
8565
|
}
|
|
8566
8566
|
return { language: match[1], country: match[2].toUpperCase() };
|
|
8567
8567
|
}
|
|
8568
|
-
function loadTargetingFileArg(
|
|
8569
|
-
if (typeof
|
|
8568
|
+
function loadTargetingFileArg(path13) {
|
|
8569
|
+
if (typeof path13 !== "string" || path13.length === 0) {
|
|
8570
8570
|
return void 0;
|
|
8571
8571
|
}
|
|
8572
|
-
const parsed = loadJsonFileArg2(
|
|
8572
|
+
const parsed = loadJsonFileArg2(path13);
|
|
8573
8573
|
const criteria = parsed.targetingCriteria ?? parsed;
|
|
8574
8574
|
if (!criteria.include) {
|
|
8575
8575
|
failWriteValidation2(
|
|
8576
|
-
`${
|
|
8576
|
+
`${path13} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
|
|
8577
8577
|
);
|
|
8578
8578
|
}
|
|
8579
8579
|
return criteria;
|
|
@@ -8608,14 +8608,14 @@ function parseCsvLine(line) {
|
|
|
8608
8608
|
cells.push(current);
|
|
8609
8609
|
return cells.map((cell) => cell.trim());
|
|
8610
8610
|
}
|
|
8611
|
-
function parseListFileArg(
|
|
8612
|
-
if (typeof
|
|
8611
|
+
function parseListFileArg(path13, maxRows) {
|
|
8612
|
+
if (typeof path13 !== "string" || path13.length === 0) {
|
|
8613
8613
|
return void 0;
|
|
8614
8614
|
}
|
|
8615
|
-
const raw = readFileSync7(
|
|
8615
|
+
const raw = readFileSync7(path13, "utf8");
|
|
8616
8616
|
const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
8617
8617
|
if (lines.length < 2) {
|
|
8618
|
-
failWriteValidation2(`${
|
|
8618
|
+
failWriteValidation2(`${path13} needs a header row and at least one data row`);
|
|
8619
8619
|
}
|
|
8620
8620
|
const columns = parseCsvLine(lines[0]).map((column) => column.trim());
|
|
8621
8621
|
const rows = [];
|
|
@@ -8634,7 +8634,7 @@ function parseListFileArg(path14, maxRows) {
|
|
|
8634
8634
|
}
|
|
8635
8635
|
}
|
|
8636
8636
|
if (rows.length > maxRows) {
|
|
8637
|
-
failWriteValidation2(`${
|
|
8637
|
+
failWriteValidation2(`${path13} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
|
|
8638
8638
|
}
|
|
8639
8639
|
return { columns, rows };
|
|
8640
8640
|
}
|
|
@@ -13154,8 +13154,8 @@ function buildRecorder() {
|
|
|
13154
13154
|
}
|
|
13155
13155
|
|
|
13156
13156
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
13157
|
-
import {
|
|
13158
|
-
import
|
|
13157
|
+
import { readFile as readFile3, writeFile } from "fs/promises";
|
|
13158
|
+
import path7 from "path";
|
|
13159
13159
|
import { defineCommand as defineCommand86 } from "citty";
|
|
13160
13160
|
|
|
13161
13161
|
// src/engine/scaffold/staticAd.ts
|
|
@@ -13341,77 +13341,15 @@ function staticAdReport(input, elementsInput, opts) {
|
|
|
13341
13341
|
};
|
|
13342
13342
|
}
|
|
13343
13343
|
|
|
13344
|
-
// src/commands/canvas/creative-definition.ts
|
|
13345
|
-
import path6 from "path";
|
|
13346
|
-
var PLATFORM_VALUES = ["meta", "google", "linkedin", "tiktok", "youtube", "x", "other"];
|
|
13347
|
-
var FORMAT_VALUES = ["1:1", "4:5", "9:16", "16:9", "1.91:1"];
|
|
13348
|
-
function titleFromSlug(slug) {
|
|
13349
|
-
const title = slug.split("-").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
13350
|
-
return title || slug;
|
|
13351
|
-
}
|
|
13352
|
-
function resolvePlatform(platform) {
|
|
13353
|
-
const value = platform?.trim();
|
|
13354
|
-
return value && PLATFORM_VALUES.includes(value) ? value : "meta";
|
|
13355
|
-
}
|
|
13356
|
-
function resolveFormats(aspect) {
|
|
13357
|
-
const value = aspect?.trim();
|
|
13358
|
-
return value && FORMAT_VALUES.includes(value) ? [value] : ["4:5"];
|
|
13359
|
-
}
|
|
13360
|
-
function referenceRelativePath(kind, ext) {
|
|
13361
|
-
const name = kind === "video" ? "source" : "original";
|
|
13362
|
-
return `references/${name}${ext}`;
|
|
13363
|
-
}
|
|
13364
|
-
function sourceExtension(source, isUrl, kind) {
|
|
13365
|
-
const raw = isUrl ? urlPathname(source) : source;
|
|
13366
|
-
const ext = path6.extname(raw).toLowerCase();
|
|
13367
|
-
if (/^\.[a-z0-9]{1,5}$/.test(ext)) return ext;
|
|
13368
|
-
return kind === "video" ? ".mp4" : ".jpg";
|
|
13369
|
-
}
|
|
13370
|
-
function urlPathname(source) {
|
|
13371
|
-
try {
|
|
13372
|
-
return new URL(source).pathname;
|
|
13373
|
-
} catch {
|
|
13374
|
-
return source;
|
|
13375
|
-
}
|
|
13376
|
-
}
|
|
13377
|
-
function describeBlueprintIntent(blueprint) {
|
|
13378
|
-
const intent = blueprint?.ad_intent;
|
|
13379
|
-
if (typeof intent === "string" && intent.trim()) return intent.trim();
|
|
13380
|
-
if (intent && typeof intent === "object") {
|
|
13381
|
-
const summary = intent.summary ?? intent.feeling;
|
|
13382
|
-
if (typeof summary === "string" && summary.trim()) return summary.trim();
|
|
13383
|
-
}
|
|
13384
|
-
return void 0;
|
|
13385
|
-
}
|
|
13386
|
-
function yamlScalar(value) {
|
|
13387
|
-
return JSON.stringify(value);
|
|
13388
|
-
}
|
|
13389
|
-
function buildCreativeDefinition(input) {
|
|
13390
|
-
const lines = ["---", `title: ${yamlScalar(input.title)}`, `kind: ${input.kind}`, `platform: ${input.platform}`];
|
|
13391
|
-
lines.push(`formats: [${input.formats.map(yamlScalar).join(", ")}]`);
|
|
13392
|
-
lines.push(`status: ${input.status ?? "draft"}`);
|
|
13393
|
-
if (input.sourceReferenceUrl) lines.push(`sourceReferenceUrl: ${yamlScalar(input.sourceReferenceUrl)}`);
|
|
13394
|
-
if (input.sourceAdvertiser) lines.push(`sourceAdvertiser: ${yamlScalar(input.sourceAdvertiser)}`);
|
|
13395
|
-
if (input.sourceKind) lines.push(`sourceKind: ${input.sourceKind}`);
|
|
13396
|
-
if (input.sourcePath) lines.push(`sourcePath: ${yamlScalar(input.sourcePath)}`);
|
|
13397
|
-
lines.push("---", "");
|
|
13398
|
-
lines.push(input.description?.trim() || `${input.title} \u2014 canvas-built ${input.kind} ad for ${input.platform}.`);
|
|
13399
|
-
lines.push("");
|
|
13400
|
-
return lines.join("\n");
|
|
13401
|
-
}
|
|
13402
|
-
|
|
13403
13344
|
// src/commands/canvas/scaffold-static-ad-paths.ts
|
|
13404
|
-
import
|
|
13345
|
+
import path6 from "path";
|
|
13405
13346
|
function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd(), slug) {
|
|
13406
13347
|
const file = rawFile.trim();
|
|
13407
13348
|
const imageIsUrl = /^https?:\/\//i.test(file);
|
|
13408
|
-
const imageSource = imageIsUrl ? file :
|
|
13409
|
-
const outPath = out ?
|
|
13410
|
-
const blueprintPath =
|
|
13411
|
-
|
|
13412
|
-
const definitionPath = creativeDir ? path7.join(creativeDir, "_definition.md") : null;
|
|
13413
|
-
const referencesDir = creativeDir ? path7.join(creativeDir, "references") : null;
|
|
13414
|
-
return { imageIsUrl, imageSource, outPath, blueprintPath, creativeDir, definitionPath, referencesDir };
|
|
13349
|
+
const imageSource = imageIsUrl ? file : path6.resolve(cwd, file);
|
|
13350
|
+
const outPath = out ? path6.resolve(cwd, out) : slug ? path6.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path6.join(cwd, "static-ad.canvas.json") : path6.join(path6.dirname(imageSource), "static-ad.canvas.json");
|
|
13351
|
+
const blueprintPath = path6.join(path6.dirname(outPath), "prompt.json");
|
|
13352
|
+
return { imageIsUrl, imageSource, outPath, blueprintPath };
|
|
13415
13353
|
}
|
|
13416
13354
|
var SCAFFOLD_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
13417
13355
|
var SCAFFOLD_SLUG_MAX_LENGTH = 100;
|
|
@@ -13420,27 +13358,6 @@ function isValidScaffoldSlug(slug) {
|
|
|
13420
13358
|
}
|
|
13421
13359
|
|
|
13422
13360
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
13423
|
-
async function fileExists(target) {
|
|
13424
|
-
try {
|
|
13425
|
-
await access(target);
|
|
13426
|
-
return true;
|
|
13427
|
-
} catch {
|
|
13428
|
-
return false;
|
|
13429
|
-
}
|
|
13430
|
-
}
|
|
13431
|
-
async function copySourceIntoReferences(source, isUrl, referencesDir) {
|
|
13432
|
-
await mkdir(referencesDir, { recursive: true });
|
|
13433
|
-
const relPath = referenceRelativePath("image", sourceExtension(source, isUrl, "image"));
|
|
13434
|
-
const dest = path8.join(referencesDir, path8.basename(relPath));
|
|
13435
|
-
if (isUrl) {
|
|
13436
|
-
const res = await fetch(source);
|
|
13437
|
-
if (!res.ok) throw new Error(`failed to download source image (${res.status})`);
|
|
13438
|
-
await writeFile(dest, Buffer.from(await res.arrayBuffer()));
|
|
13439
|
-
} else {
|
|
13440
|
-
await cp(source, dest);
|
|
13441
|
-
}
|
|
13442
|
-
return relPath;
|
|
13443
|
-
}
|
|
13444
13361
|
function resolveModel(kind, preferred) {
|
|
13445
13362
|
const ids = Object.keys(MODEL_REGISTRY[kind]);
|
|
13446
13363
|
return ids.includes(preferred) ? preferred : ids[0] ?? preferred;
|
|
@@ -13604,12 +13521,6 @@ var scaffoldStaticAdCommand = defineCommand86({
|
|
|
13604
13521
|
type: "string",
|
|
13605
13522
|
description: "Creative slug \u2014 writes the canvas to src/creatives/<slug>/<slug>.canvas.json (repo convention)"
|
|
13606
13523
|
},
|
|
13607
|
-
title: { type: "string", description: "Creative title for _definition.md (default: title-cased slug)" },
|
|
13608
|
-
platform: {
|
|
13609
|
-
type: "string",
|
|
13610
|
-
description: "Ad platform for _definition.md (meta|google|linkedin|tiktok|youtube|x|other; default meta)"
|
|
13611
|
-
},
|
|
13612
|
-
advertiser: { type: "string", description: "Source advertiser recorded in _definition.md" },
|
|
13613
13524
|
"describe-model": { type: "string", description: "Override the image_describe model id" },
|
|
13614
13525
|
"select-model": { type: "string", description: "Override the text_generate model id for element selection" },
|
|
13615
13526
|
"layout-model": { type: "string", description: "Override the text_generate model id for the layout pass" },
|
|
@@ -13626,13 +13537,12 @@ var scaffoldStaticAdCommand = defineCommand86({
|
|
|
13626
13537
|
);
|
|
13627
13538
|
process.exit(2);
|
|
13628
13539
|
}
|
|
13629
|
-
const { imageIsUrl, imageSource, outPath, blueprintPath
|
|
13540
|
+
const { imageIsUrl, imageSource, outPath, blueprintPath } = resolveScaffoldStaticAdPaths(
|
|
13630
13541
|
String(args.file),
|
|
13631
13542
|
args.out ? String(args.out) : void 0,
|
|
13632
13543
|
process.cwd(),
|
|
13633
13544
|
slug
|
|
13634
13545
|
);
|
|
13635
|
-
await mkdir(path8.dirname(outPath), { recursive: true });
|
|
13636
13546
|
const { describeModel, selectModel, layoutModel, genModel } = resolveModels(args);
|
|
13637
13547
|
const describeCanvas = buildDescribeCanvas(
|
|
13638
13548
|
imageSource,
|
|
@@ -13649,21 +13559,11 @@ var scaffoldStaticAdCommand = defineCommand86({
|
|
|
13649
13559
|
}
|
|
13650
13560
|
await writeFile(blueprintPath, `${JSON.stringify(annotated, null, 2)}
|
|
13651
13561
|
`, "utf8");
|
|
13652
|
-
let canvasImagePath = imageSource;
|
|
13653
|
-
let canvasImageIsUrl = imageIsUrl;
|
|
13654
|
-
let canvasBlueprintPath = blueprintPath;
|
|
13655
|
-
let sourceRelPath;
|
|
13656
|
-
if (referencesDir) {
|
|
13657
|
-
sourceRelPath = await copySourceIntoReferences(imageSource, imageIsUrl, referencesDir);
|
|
13658
|
-
canvasImagePath = sourceRelPath;
|
|
13659
|
-
canvasImageIsUrl = false;
|
|
13660
|
-
canvasBlueprintPath = "./prompt.json";
|
|
13661
|
-
}
|
|
13662
13562
|
const opts = {
|
|
13663
13563
|
genModel,
|
|
13664
|
-
imagePath:
|
|
13665
|
-
imageIsUrl
|
|
13666
|
-
blueprintPath
|
|
13564
|
+
imagePath: imageSource,
|
|
13565
|
+
imageIsUrl,
|
|
13566
|
+
blueprintPath,
|
|
13667
13567
|
aspectRatio: args.aspect ? String(args.aspect) : void 0,
|
|
13668
13568
|
includeFont: !args["skip-font"]
|
|
13669
13569
|
};
|
|
@@ -13685,31 +13585,12 @@ var scaffoldStaticAdCommand = defineCommand86({
|
|
|
13685
13585
|
}
|
|
13686
13586
|
await writeFile(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
13687
13587
|
`, "utf8");
|
|
13688
|
-
if (definitionPath && !await fileExists(definitionPath)) {
|
|
13689
|
-
await writeFile(
|
|
13690
|
-
definitionPath,
|
|
13691
|
-
buildCreativeDefinition({
|
|
13692
|
-
title: args.title ? String(args.title) : titleFromSlug(slug ?? ""),
|
|
13693
|
-
kind: "static",
|
|
13694
|
-
platform: resolvePlatform(args.platform ? String(args.platform) : void 0),
|
|
13695
|
-
formats: resolveFormats(args.aspect ? String(args.aspect) : report.aspect_ratio),
|
|
13696
|
-
sourceReferenceUrl: imageIsUrl ? imageSource : void 0,
|
|
13697
|
-
sourceAdvertiser: args.advertiser ? String(args.advertiser) : args.context ? String(args.context) : void 0,
|
|
13698
|
-
sourceKind: "image",
|
|
13699
|
-
sourcePath: sourceRelPath,
|
|
13700
|
-
description: describeBlueprintIntent(blueprint)
|
|
13701
|
-
}),
|
|
13702
|
-
"utf8"
|
|
13703
|
-
);
|
|
13704
|
-
}
|
|
13705
13588
|
process.stdout.write(
|
|
13706
13589
|
`${JSON.stringify(
|
|
13707
13590
|
{
|
|
13708
13591
|
ok: true,
|
|
13709
13592
|
canvas_path: outPath,
|
|
13710
13593
|
prompt_path: blueprintPath,
|
|
13711
|
-
definition_path: definitionPath ?? void 0,
|
|
13712
|
-
source_reference: sourceRelPath ?? void 0,
|
|
13713
13594
|
output: canvas.output,
|
|
13714
13595
|
models: { describe: describeModel, select: selectModel, layout: layoutModel, gen: opts.genModel },
|
|
13715
13596
|
aspect_ratio: report.aspect_ratio,
|
|
@@ -13720,7 +13601,7 @@ var scaffoldStaticAdCommand = defineCommand86({
|
|
|
13720
13601
|
run_estimated_credits: validation.estimatedCredits
|
|
13721
13602
|
},
|
|
13722
13603
|
checklist: {
|
|
13723
|
-
edit_prompt: `Edit ${
|
|
13604
|
+
edit_prompt: `Edit ${path7.basename(blueprintPath)} \u2014 it is the blueprint generated from your image; rewrite it into the ad you want (palette, copy, claims, subjects). It feeds the generator directly.`,
|
|
13724
13605
|
assets_to_supply: report.elements,
|
|
13725
13606
|
font_slot: report.includes_font ? "Drop a brand font at the [TODO] brandfont path, or delete the brandfont + type_ref nodes to skip it." : "skipped (--skip-font)",
|
|
13726
13607
|
note: "Replace every [TODO] ingest path with a real file, then `baker canvas validate` and `baker canvas run`. Running generates a billed image \u2014 it is not free."
|
|
@@ -13735,8 +13616,8 @@ var scaffoldStaticAdCommand = defineCommand86({
|
|
|
13735
13616
|
});
|
|
13736
13617
|
|
|
13737
13618
|
// src/commands/canvas/scaffold-video.ts
|
|
13738
|
-
import { cp
|
|
13739
|
-
import
|
|
13619
|
+
import { cp, mkdir, readFile as readFile6, writeFile as writeFile2 } from "fs/promises";
|
|
13620
|
+
import path10 from "path";
|
|
13740
13621
|
import { defineCommand as defineCommand87 } from "citty";
|
|
13741
13622
|
|
|
13742
13623
|
// src/engine/nodes/local/lib/sceneDetect.ts
|
|
@@ -16520,23 +16401,23 @@ function videoReport(input, elementsInput) {
|
|
|
16520
16401
|
|
|
16521
16402
|
// src/commands/canvas/composition-path.ts
|
|
16522
16403
|
import { existsSync as existsSync4 } from "fs";
|
|
16523
|
-
import
|
|
16404
|
+
import path8 from "path";
|
|
16524
16405
|
function resolveShippedCanvasDir(name, startDir, exists = existsSync4, maxDepth = 8) {
|
|
16525
|
-
const rel =
|
|
16406
|
+
const rel = path8.join("canvas", name);
|
|
16526
16407
|
let dir = startDir;
|
|
16527
16408
|
for (let i = 0; i < maxDepth; i++) {
|
|
16528
|
-
const candidate =
|
|
16529
|
-
if (exists(
|
|
16530
|
-
const parent =
|
|
16409
|
+
const candidate = path8.join(dir, rel);
|
|
16410
|
+
if (exists(path8.join(candidate, "meta.json"))) return candidate;
|
|
16411
|
+
const parent = path8.dirname(dir);
|
|
16531
16412
|
if (parent === dir) break;
|
|
16532
16413
|
dir = parent;
|
|
16533
16414
|
}
|
|
16534
|
-
return
|
|
16415
|
+
return path8.resolve(startDir, "../../../", rel);
|
|
16535
16416
|
}
|
|
16536
16417
|
|
|
16537
16418
|
// src/commands/canvas/gitignore.ts
|
|
16538
16419
|
import { appendFile, readFile as readFile5 } from "fs/promises";
|
|
16539
|
-
import
|
|
16420
|
+
import path9 from "path";
|
|
16540
16421
|
function missingGitignoreEntries(existing, entries) {
|
|
16541
16422
|
const present = new Set(
|
|
16542
16423
|
existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
|
|
@@ -16544,7 +16425,7 @@ function missingGitignoreEntries(existing, entries) {
|
|
|
16544
16425
|
return entries.filter((e) => !present.has(e.trim().replace(/\/+$/, "")));
|
|
16545
16426
|
}
|
|
16546
16427
|
async function ensureGitignore(dir, entries) {
|
|
16547
|
-
const file =
|
|
16428
|
+
const file = path9.join(dir, ".gitignore");
|
|
16548
16429
|
let existing;
|
|
16549
16430
|
try {
|
|
16550
16431
|
existing = await readFile5(file, "utf8");
|
|
@@ -16605,8 +16486,8 @@ async function loadTranscriptBestEffort(ref) {
|
|
|
16605
16486
|
async function stageCaptions(outDir, transcript) {
|
|
16606
16487
|
const text = transcript?.trim();
|
|
16607
16488
|
if (!text || text === "[]") return {};
|
|
16608
|
-
const compositionPath =
|
|
16609
|
-
await
|
|
16489
|
+
const compositionPath = path10.join(outDir, "tiktok-captions-composition");
|
|
16490
|
+
await cp(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
|
|
16610
16491
|
return { compositionPath };
|
|
16611
16492
|
}
|
|
16612
16493
|
function patchCompositionMeta(metaJson, dims) {
|
|
@@ -16623,10 +16504,10 @@ function patchCompositionHtml(html, dims) {
|
|
|
16623
16504
|
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`);
|
|
16624
16505
|
}
|
|
16625
16506
|
async function stampCompositionDims(compositionDir, dims) {
|
|
16626
|
-
const metaPath =
|
|
16507
|
+
const metaPath = path10.join(compositionDir, "meta.json");
|
|
16627
16508
|
const rawMeta = await readFile6(metaPath, "utf8");
|
|
16628
16509
|
await writeFile2(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
|
|
16629
|
-
const htmlPath =
|
|
16510
|
+
const htmlPath = path10.join(compositionDir, "index.html");
|
|
16630
16511
|
const rawHtml = await readFile6(htmlPath, "utf8");
|
|
16631
16512
|
await writeFile2(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
|
|
16632
16513
|
}
|
|
@@ -16796,8 +16677,8 @@ var scaffoldVideoCommand = defineCommand87({
|
|
|
16796
16677
|
}
|
|
16797
16678
|
},
|
|
16798
16679
|
async run({ args }) {
|
|
16799
|
-
const videoPath =
|
|
16800
|
-
const base =
|
|
16680
|
+
const videoPath = path10.resolve(String(args.file));
|
|
16681
|
+
const base = path10.basename(videoPath, path10.extname(videoPath));
|
|
16801
16682
|
const slug = args.slug ? String(args.slug) : void 0;
|
|
16802
16683
|
if (slug && !isValidScaffoldSlug(slug)) {
|
|
16803
16684
|
process.stderr.write(
|
|
@@ -16806,9 +16687,9 @@ var scaffoldVideoCommand = defineCommand87({
|
|
|
16806
16687
|
);
|
|
16807
16688
|
process.exit(2);
|
|
16808
16689
|
}
|
|
16809
|
-
const outPath = args.out ?
|
|
16810
|
-
const outDir =
|
|
16811
|
-
const blueprintPath =
|
|
16690
|
+
const outPath = args.out ? path10.resolve(String(args.out)) : slug ? path10.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path10.join(path10.dirname(videoPath), `${base}.video.canvas.json`);
|
|
16691
|
+
const outDir = path10.dirname(outPath);
|
|
16692
|
+
const blueprintPath = path10.join(outDir, "prompt.json");
|
|
16812
16693
|
const frames = args.frames === "reuse" ? "reuse" : "generate";
|
|
16813
16694
|
const maxScenes = args["max-scenes"] ? Number(args["max-scenes"]) : void 0;
|
|
16814
16695
|
if (Number.isFinite(maxScenes)) {
|
|
@@ -16827,7 +16708,7 @@ var scaffoldVideoCommand = defineCommand87({
|
|
|
16827
16708
|
shotCuts
|
|
16828
16709
|
});
|
|
16829
16710
|
const { blueprint, elements, transcript, creditsSpent } = await runAnalysisPasses(deconstructCanvas, selectModel);
|
|
16830
|
-
await
|
|
16711
|
+
await mkdir(outDir, { recursive: true });
|
|
16831
16712
|
const annotated = annotateBlueprintWithElements(blueprint, elements);
|
|
16832
16713
|
await writeFile2(blueprintPath, `${JSON.stringify(annotated, null, 2)}
|
|
16833
16714
|
`, "utf8");
|
|
@@ -16848,10 +16729,10 @@ var scaffoldVideoCommand = defineCommand87({
|
|
|
16848
16729
|
`
|
|
16849
16730
|
);
|
|
16850
16731
|
}
|
|
16851
|
-
const compositionDest =
|
|
16852
|
-
await
|
|
16732
|
+
const compositionDest = path10.join(outDir, "video-overlay-composition");
|
|
16733
|
+
await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
|
|
16853
16734
|
await stampCompositionDims(compositionDest, outDims);
|
|
16854
|
-
const indexPath =
|
|
16735
|
+
const indexPath = path10.join(compositionDest, "index.html");
|
|
16855
16736
|
const overlayHtml = buildOverlayHtml(blueprint);
|
|
16856
16737
|
const indexHtml = await readFile6(indexPath, "utf8");
|
|
16857
16738
|
const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
|
|
@@ -16867,9 +16748,9 @@ var scaffoldVideoCommand = defineCommand87({
|
|
|
16867
16748
|
const opts = {
|
|
16868
16749
|
imageModel,
|
|
16869
16750
|
videoModel,
|
|
16870
|
-
overlayCompositionPath:
|
|
16871
|
-
captionsCompositionPath: captions.compositionPath ?
|
|
16872
|
-
blueprintPath:
|
|
16751
|
+
overlayCompositionPath: path10.relative(outDir, compositionDest),
|
|
16752
|
+
captionsCompositionPath: captions.compositionPath ? path10.relative(outDir, captions.compositionPath) : void 0,
|
|
16753
|
+
blueprintPath: path10.relative(outDir, blueprintPath),
|
|
16873
16754
|
frames,
|
|
16874
16755
|
ambient: Boolean(args.ambient),
|
|
16875
16756
|
...args.aspect ? { aspect: String(args.aspect) } : {},
|
|
@@ -16911,7 +16792,7 @@ var scaffoldVideoCommand = defineCommand87({
|
|
|
16911
16792
|
run_estimated_credits: validation.estimatedCredits
|
|
16912
16793
|
},
|
|
16913
16794
|
checklist: {
|
|
16914
|
-
edit_prompt: `Edit ${
|
|
16795
|
+
edit_prompt: `Edit ${path10.basename(blueprintPath)} \u2014 the blueprint deconstructed from your video; rewrite it into the ad you want (cast, palette, copy, claims). Every scene frame reads it via target_blueprint.`,
|
|
16915
16796
|
recurring_elements_to_supply: report.elements,
|
|
16916
16797
|
voices_to_confirm: report.dialogue.map((d) => ({
|
|
16917
16798
|
scene: d.scene,
|
|
@@ -16938,7 +16819,7 @@ var scaffoldVideoCommand = defineCommand87({
|
|
|
16938
16819
|
|
|
16939
16820
|
// src/commands/canvas/set-prompt.ts
|
|
16940
16821
|
import { readFile as readFile7, writeFile as writeFile3 } from "fs/promises";
|
|
16941
|
-
import
|
|
16822
|
+
import path11 from "path";
|
|
16942
16823
|
import { defineCommand as defineCommand88 } from "citty";
|
|
16943
16824
|
function setNodePrompt(canvas, nodeId, text) {
|
|
16944
16825
|
const nodes = canvas?.nodes;
|
|
@@ -16966,7 +16847,7 @@ var setPromptCommand = defineCommand88({
|
|
|
16966
16847
|
"text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
|
|
16967
16848
|
},
|
|
16968
16849
|
async run({ args }) {
|
|
16969
|
-
const filePath =
|
|
16850
|
+
const filePath = path11.resolve(String(args.file));
|
|
16970
16851
|
let canvas;
|
|
16971
16852
|
try {
|
|
16972
16853
|
canvas = JSON.parse(await readFile7(filePath, "utf8"));
|
|
@@ -16976,7 +16857,7 @@ var setPromptCommand = defineCommand88({
|
|
|
16976
16857
|
process.exit(2);
|
|
16977
16858
|
}
|
|
16978
16859
|
let text;
|
|
16979
|
-
if (args["text-file"]) text = await readFile7(
|
|
16860
|
+
if (args["text-file"]) text = await readFile7(path11.resolve(String(args["text-file"])), "utf8");
|
|
16980
16861
|
else if (args.text !== void 0) text = String(args.text);
|
|
16981
16862
|
else {
|
|
16982
16863
|
process.stderr.write(
|
|
@@ -16997,7 +16878,7 @@ var setPromptCommand = defineCommand88({
|
|
|
16997
16878
|
process.exit(2);
|
|
16998
16879
|
return;
|
|
16999
16880
|
}
|
|
17000
|
-
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated,
|
|
16881
|
+
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path11.dirname(filePath)), defaultRegistry());
|
|
17001
16882
|
if (!validation.ok) {
|
|
17002
16883
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
|
|
17003
16884
|
`);
|
|
@@ -17013,7 +16894,7 @@ var setPromptCommand = defineCommand88({
|
|
|
17013
16894
|
|
|
17014
16895
|
// src/commands/canvas/validate.ts
|
|
17015
16896
|
import { readFile as readFile8 } from "fs/promises";
|
|
17016
|
-
import
|
|
16897
|
+
import path12 from "path";
|
|
17017
16898
|
import { defineCommand as defineCommand89 } from "citty";
|
|
17018
16899
|
var validateCommand = defineCommand89({
|
|
17019
16900
|
meta: {
|
|
@@ -17022,7 +16903,7 @@ var validateCommand = defineCommand89({
|
|
|
17022
16903
|
},
|
|
17023
16904
|
args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
|
|
17024
16905
|
async run({ args }) {
|
|
17025
|
-
const filePath =
|
|
16906
|
+
const filePath = path12.resolve(String(args.file));
|
|
17026
16907
|
const raw = await readFile8(filePath, "utf8");
|
|
17027
16908
|
let parsed;
|
|
17028
16909
|
try {
|
|
@@ -17033,7 +16914,7 @@ var validateCommand = defineCommand89({
|
|
|
17033
16914
|
`);
|
|
17034
16915
|
process.exit(2);
|
|
17035
16916
|
}
|
|
17036
|
-
parsed = resolveRelativeCanvasPaths(parsed,
|
|
16917
|
+
parsed = resolveRelativeCanvasPaths(parsed, path12.dirname(filePath));
|
|
17037
16918
|
const result = await validateCanvasDeep(parsed, defaultRegistry());
|
|
17038
16919
|
if (!result.ok) {
|
|
17039
16920
|
process.stderr.write(`${JSON.stringify({ ok: false, issues: result.issues }, null, 2)}
|
|
@@ -18174,9 +18055,9 @@ async function readImageBuffer(pathOrUrl) {
|
|
|
18174
18055
|
}
|
|
18175
18056
|
return readFile10(pathOrUrl);
|
|
18176
18057
|
}
|
|
18177
|
-
async function isDirectory(
|
|
18058
|
+
async function isDirectory(path13) {
|
|
18178
18059
|
try {
|
|
18179
|
-
const s = await stat2(
|
|
18060
|
+
const s = await stat2(path13);
|
|
18180
18061
|
return s.isDirectory();
|
|
18181
18062
|
} catch {
|
|
18182
18063
|
return false;
|