@koda-sl/baker-cli 0.390.0 → 0.391.0
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 +1 -1
- package/dist/{chunk-6UE43HEQ.js → chunk-SZKTVOR7.js} +9 -1
- package/dist/chunk-SZKTVOR7.js.map +1 -0
- package/dist/cli.js +577 -491
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-6UE43HEQ.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
durationSeconds,
|
|
38
38
|
elementMentionKeywords,
|
|
39
39
|
estimateVideoCredits,
|
|
40
|
+
failedOnlyOnTransientErrors,
|
|
40
41
|
fetchExternalBytes,
|
|
41
42
|
frameRealismDirection,
|
|
42
43
|
generateCatalog,
|
|
@@ -74,7 +75,7 @@ import {
|
|
|
74
75
|
validateCanvasDeep,
|
|
75
76
|
withoutCastName,
|
|
76
77
|
ytDlpBlockSignal
|
|
77
|
-
} from "./chunk-
|
|
78
|
+
} from "./chunk-SZKTVOR7.js";
|
|
78
79
|
import {
|
|
79
80
|
NAME_CONTAINS_ARG,
|
|
80
81
|
csvOrJson,
|
|
@@ -34295,16 +34296,16 @@ async function runChatgptRead(reference, read, hintsFor4) {
|
|
|
34295
34296
|
}
|
|
34296
34297
|
}
|
|
34297
34298
|
var TRUNCATED_HINT = "These rows are the first 2,000, not the whole account \u2014 narrow the read (--campaign-id, --ad-group-id) before counting them or concluding something is missing.";
|
|
34298
|
-
async function getChatgptList(
|
|
34299
|
-
const { data, headers } = await apiGetWithHeaders(
|
|
34299
|
+
async function getChatgptList(path54, params) {
|
|
34300
|
+
const { data, headers } = await apiGetWithHeaders(path54, params);
|
|
34300
34301
|
return { data, truncated: headers.get(CHATGPT_ADS_TRUNCATED_HEADER) === "true" };
|
|
34301
34302
|
}
|
|
34302
|
-
async function runChatgptListRead(reference,
|
|
34303
|
+
async function runChatgptListRead(reference, path54, params, hintsFor4) {
|
|
34303
34304
|
let truncated = false;
|
|
34304
34305
|
await runChatgptRead(
|
|
34305
34306
|
reference,
|
|
34306
34307
|
async () => {
|
|
34307
|
-
const read = await getChatgptList(
|
|
34308
|
+
const read = await getChatgptList(path54, params);
|
|
34308
34309
|
truncated = read.truncated;
|
|
34309
34310
|
return read.data;
|
|
34310
34311
|
},
|
|
@@ -35109,8 +35110,8 @@ var MEMBER_ARGS = {
|
|
|
35109
35110
|
},
|
|
35110
35111
|
country: { type: "string", description: "Country of phones written without a +code, e.g. ES" }
|
|
35111
35112
|
};
|
|
35112
|
-
function membersFromArgs(args8,
|
|
35113
|
-
const fromFile = typeof args8.file === "string" && args8.file ? membersFromFile(
|
|
35113
|
+
function membersFromArgs(args8, readFile41 = (path54) => readFileSync2(path54, "utf8")) {
|
|
35114
|
+
const fromFile = typeof args8.file === "string" && args8.file ? membersFromFile(readFile41(args8.file)) : { emails: [], phones: [] };
|
|
35114
35115
|
const given = [...csvArg(args8.emails) ?? [], ...fromFile.emails];
|
|
35115
35116
|
const givenPhones = [...csvArg(args8.phones) ?? [], ...fromFile.phones];
|
|
35116
35117
|
const members = hashedMembers({
|
|
@@ -38484,9 +38485,9 @@ var COPY_LIST_FLAG = {
|
|
|
38484
38485
|
descriptions: "--descriptions",
|
|
38485
38486
|
longHeadlines: "--long-headlines"
|
|
38486
38487
|
};
|
|
38487
|
-
function valueAt(root,
|
|
38488
|
+
function valueAt(root, path54) {
|
|
38488
38489
|
let node = root;
|
|
38489
|
-
for (const key of
|
|
38490
|
+
for (const key of path54) {
|
|
38490
38491
|
if (node === null || typeof node !== "object") return void 0;
|
|
38491
38492
|
node = node[key];
|
|
38492
38493
|
}
|
|
@@ -38719,11 +38720,11 @@ function parseKeywordEntry(raw, defaultMatch) {
|
|
|
38719
38720
|
function rawTextEntries(value2, rawArgs) {
|
|
38720
38721
|
return repeatedValues(rawArgs, "text", value2).flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
|
|
38721
38722
|
}
|
|
38722
|
-
function rawFileEntries(
|
|
38723
|
-
if (typeof
|
|
38723
|
+
function rawFileEntries(path54) {
|
|
38724
|
+
if (typeof path54 !== "string" || path54.length === 0) {
|
|
38724
38725
|
return [];
|
|
38725
38726
|
}
|
|
38726
|
-
return readFileSync4(
|
|
38727
|
+
return readFileSync4(path54, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
38727
38728
|
}
|
|
38728
38729
|
function keywordEntries(args8, rawArgs) {
|
|
38729
38730
|
const defaultMatch = typeof args8["match-type"] === "string" ? args8["match-type"].toUpperCase() : void 0;
|
|
@@ -38746,14 +38747,14 @@ function keywordEntries(args8, rawArgs) {
|
|
|
38746
38747
|
}
|
|
38747
38748
|
return entries;
|
|
38748
38749
|
}
|
|
38749
|
-
function loadJsonFileArg(
|
|
38750
|
-
if (typeof
|
|
38750
|
+
function loadJsonFileArg(path54) {
|
|
38751
|
+
if (typeof path54 !== "string" || path54.length === 0) {
|
|
38751
38752
|
return {};
|
|
38752
38753
|
}
|
|
38753
|
-
const inline =
|
|
38754
|
-
const source = inline ? "inline JSON" :
|
|
38754
|
+
const inline = path54.trimStart().startsWith("{");
|
|
38755
|
+
const source = inline ? "inline JSON" : path54;
|
|
38755
38756
|
try {
|
|
38756
|
-
const parsed = JSON.parse(inline ?
|
|
38757
|
+
const parsed = JSON.parse(inline ? path54 : readFileSync4(path54, "utf8"));
|
|
38757
38758
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
38758
38759
|
failWriteValidation2(`${source} must contain a JSON object`);
|
|
38759
38760
|
}
|
|
@@ -38890,10 +38891,10 @@ async function stageUpdate(kind, customerId, target2, payload, hints2) {
|
|
|
38890
38891
|
async function stageTarget(kind, customerId, target2, hints2) {
|
|
38891
38892
|
await stageGoogleOp({ kind, customerId, target: target2 }, hints2);
|
|
38892
38893
|
}
|
|
38893
|
-
async function draftAction(
|
|
38894
|
+
async function draftAction(path54, body, chat) {
|
|
38894
38895
|
try {
|
|
38895
38896
|
const chatId = resolveChatId(chat);
|
|
38896
|
-
const response = await apiPost(
|
|
38897
|
+
const response = await apiPost(path54, { chatId, ...body });
|
|
38897
38898
|
writeJsonEnvelope(response);
|
|
38898
38899
|
} catch (err) {
|
|
38899
38900
|
handleGoogleError(err);
|
|
@@ -41311,14 +41312,14 @@ function mappingRowProblem(row) {
|
|
|
41311
41312
|
|
|
41312
41313
|
// ../api/src/event-sources/template.ts
|
|
41313
41314
|
var MENTION = /\{\{\s*([^{}]+?)\s*\}\}/g;
|
|
41314
|
-
function mentionOf(
|
|
41315
|
-
return `{{${
|
|
41315
|
+
function mentionOf(path54) {
|
|
41316
|
+
return `{{${path54}}}`;
|
|
41316
41317
|
}
|
|
41317
41318
|
function templateMentions(template) {
|
|
41318
41319
|
const seen = [];
|
|
41319
41320
|
for (const match of template.matchAll(MENTION)) {
|
|
41320
|
-
const
|
|
41321
|
-
if (
|
|
41321
|
+
const path54 = (match[1] ?? "").trim();
|
|
41322
|
+
if (path54 !== "" && !seen.includes(path54)) seen.push(path54);
|
|
41322
41323
|
}
|
|
41323
41324
|
return seen;
|
|
41324
41325
|
}
|
|
@@ -44673,17 +44674,17 @@ function ignoredColumnHints(columns) {
|
|
|
44673
44674
|
`Ignored ${columns.length} column(s) that are not part of a conversion: ${columns.join(", ")}. A conversion carries an order id, a date, and something to match on: a click id (gclid / gbraid / wbraid), an email, a phone, or all four of givenName + familyName + country + postalCode. Optionally a value + currency, conversionCount, and eventSource (WEB / APP / IN_STORE / PHONE / OTHER). A column named just \`Name\` is ignored on purpose \u2014 Google needs the first and last name separately, so split it before re-running.`
|
|
44674
44675
|
];
|
|
44675
44676
|
}
|
|
44676
|
-
function readRowsFile(
|
|
44677
|
-
const inline =
|
|
44677
|
+
function readRowsFile(path54) {
|
|
44678
|
+
const inline = path54.trimStart().startsWith("[") || path54.trimStart().startsWith("{");
|
|
44678
44679
|
let text2;
|
|
44679
44680
|
try {
|
|
44680
|
-
text2 = inline ?
|
|
44681
|
+
text2 = inline ? path54 : readFileSync6(path54, "utf8");
|
|
44681
44682
|
} catch (err) {
|
|
44682
|
-
failWriteValidation2(`could not read ${
|
|
44683
|
+
failWriteValidation2(`could not read ${path54}: ${err instanceof Error ? err.message : String(err)}`);
|
|
44683
44684
|
}
|
|
44684
44685
|
const parsed = parseConversionRowsText(text2);
|
|
44685
44686
|
if (!parsed.ok) {
|
|
44686
|
-
failWriteValidation2(`${inline ? "the conversions passed inline" :
|
|
44687
|
+
failWriteValidation2(`${inline ? "the conversions passed inline" : path54}: ${parsed.error}`);
|
|
44687
44688
|
}
|
|
44688
44689
|
return { rows: parsed.rows, ignoredColumns: parsed.ignoredColumns };
|
|
44689
44690
|
}
|
|
@@ -46584,19 +46585,19 @@ function failWriteValidation3(message) {
|
|
|
46584
46585
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
46585
46586
|
process.exit(1);
|
|
46586
46587
|
}
|
|
46587
|
-
function loadJsonFileArg2(
|
|
46588
|
-
if (typeof
|
|
46588
|
+
function loadJsonFileArg2(path54) {
|
|
46589
|
+
if (typeof path54 !== "string" || path54.length === 0) {
|
|
46589
46590
|
return {};
|
|
46590
46591
|
}
|
|
46591
46592
|
try {
|
|
46592
|
-
const parsed = JSON.parse(readFileSync7(
|
|
46593
|
+
const parsed = JSON.parse(readFileSync7(path54, "utf8"));
|
|
46593
46594
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
46594
|
-
failWriteValidation3(`${
|
|
46595
|
+
failWriteValidation3(`${path54} must contain a JSON object`);
|
|
46595
46596
|
}
|
|
46596
46597
|
return parsed;
|
|
46597
46598
|
} catch (err) {
|
|
46598
46599
|
if (err instanceof SyntaxError) {
|
|
46599
|
-
failWriteValidation3(`${
|
|
46600
|
+
failWriteValidation3(`${path54} is not valid JSON: ${err.message}`);
|
|
46600
46601
|
}
|
|
46601
46602
|
throw err;
|
|
46602
46603
|
}
|
|
@@ -46681,15 +46682,15 @@ function parseLocaleFlag(value2) {
|
|
|
46681
46682
|
}
|
|
46682
46683
|
return { language: match[1], country: match[2].toUpperCase() };
|
|
46683
46684
|
}
|
|
46684
|
-
function loadTargetingFileArg(
|
|
46685
|
-
if (typeof
|
|
46685
|
+
function loadTargetingFileArg(path54) {
|
|
46686
|
+
if (typeof path54 !== "string" || path54.length === 0) {
|
|
46686
46687
|
return void 0;
|
|
46687
46688
|
}
|
|
46688
|
-
const parsed = loadJsonFileArg2(
|
|
46689
|
+
const parsed = loadJsonFileArg2(path54);
|
|
46689
46690
|
const criteria = parsed.targetingCriteria ?? parsed;
|
|
46690
46691
|
if (!criteria.include) {
|
|
46691
46692
|
failWriteValidation3(
|
|
46692
|
-
`${
|
|
46693
|
+
`${path54} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
|
|
46693
46694
|
);
|
|
46694
46695
|
}
|
|
46695
46696
|
return criteria;
|
|
@@ -46724,14 +46725,14 @@ function parseCsvLine(line) {
|
|
|
46724
46725
|
cells.push(current);
|
|
46725
46726
|
return cells.map((cell3) => cell3.trim());
|
|
46726
46727
|
}
|
|
46727
|
-
function parseListFileArg(
|
|
46728
|
-
if (typeof
|
|
46728
|
+
function parseListFileArg(path54, maxRows) {
|
|
46729
|
+
if (typeof path54 !== "string" || path54.length === 0) {
|
|
46729
46730
|
return void 0;
|
|
46730
46731
|
}
|
|
46731
|
-
const raw = readFileSync7(
|
|
46732
|
+
const raw = readFileSync7(path54, "utf8");
|
|
46732
46733
|
const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
46733
46734
|
if (lines.length < 2) {
|
|
46734
|
-
failWriteValidation3(`${
|
|
46735
|
+
failWriteValidation3(`${path54} needs a header row and at least one data row`);
|
|
46735
46736
|
}
|
|
46736
46737
|
const columns = parseCsvLine(lines[0]).map((column) => column.trim());
|
|
46737
46738
|
const rows = [];
|
|
@@ -46750,7 +46751,7 @@ function parseListFileArg(path53, maxRows) {
|
|
|
46750
46751
|
}
|
|
46751
46752
|
}
|
|
46752
46753
|
if (rows.length > maxRows) {
|
|
46753
|
-
failWriteValidation3(`${
|
|
46754
|
+
failWriteValidation3(`${path54} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
|
|
46754
46755
|
}
|
|
46755
46756
|
return { columns, rows };
|
|
46756
46757
|
}
|
|
@@ -46846,11 +46847,11 @@ function readPositionals(args8) {
|
|
|
46846
46847
|
function splitIdList(raw) {
|
|
46847
46848
|
return raw.split(",").map((id) => id.trim()).filter(Boolean);
|
|
46848
46849
|
}
|
|
46849
|
-
function idsFileEntries(
|
|
46850
|
-
if (typeof
|
|
46850
|
+
function idsFileEntries(path54) {
|
|
46851
|
+
if (typeof path54 !== "string" || path54.length === 0) {
|
|
46851
46852
|
return [];
|
|
46852
46853
|
}
|
|
46853
|
-
return readFileSync7(
|
|
46854
|
+
return readFileSync7(path54, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
|
|
46854
46855
|
}
|
|
46855
46856
|
function requireTargets2(args8, entity) {
|
|
46856
46857
|
const positionals = readPositionals(args8);
|
|
@@ -49611,9 +49612,9 @@ import { defineCommand as defineCommand64 } from "citty";
|
|
|
49611
49612
|
function asRecord(value2) {
|
|
49612
49613
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2) ? value2 : void 0;
|
|
49613
49614
|
}
|
|
49614
|
-
function readPath2(row,
|
|
49615
|
+
function readPath2(row, path54) {
|
|
49615
49616
|
let current = row;
|
|
49616
|
-
for (const segment of
|
|
49617
|
+
for (const segment of path54.split(".")) {
|
|
49617
49618
|
const record = asRecord(current);
|
|
49618
49619
|
if (!record) return void 0;
|
|
49619
49620
|
current = record[segment];
|
|
@@ -49623,10 +49624,10 @@ function readPath2(row, path53) {
|
|
|
49623
49624
|
function projectFields(rows, paths) {
|
|
49624
49625
|
return rows.map((row) => {
|
|
49625
49626
|
const projected = {};
|
|
49626
|
-
for (const
|
|
49627
|
-
const value2 = readPath2(row,
|
|
49627
|
+
for (const path54 of paths) {
|
|
49628
|
+
const value2 = readPath2(row, path54);
|
|
49628
49629
|
if (value2 !== void 0) {
|
|
49629
|
-
projected[
|
|
49630
|
+
projected[path54] = value2;
|
|
49630
49631
|
}
|
|
49631
49632
|
}
|
|
49632
49633
|
return projected;
|
|
@@ -49639,16 +49640,16 @@ function shapeListRows(rows, args8, compact5) {
|
|
|
49639
49640
|
const paths = fields.split(",").map((field) => field.trim()).filter((field) => field.length > 0);
|
|
49640
49641
|
if (paths.length > 0) {
|
|
49641
49642
|
const projected = projectFields(raw, paths).map((row, i) => {
|
|
49642
|
-
const missing = paths.filter((
|
|
49643
|
+
const missing = paths.filter((path54) => row[path54] === void 0);
|
|
49643
49644
|
const source = rows[i];
|
|
49644
49645
|
if (missing.length === 0 || source === void 0) return row;
|
|
49645
49646
|
const compactRow3 = compact5.row(source);
|
|
49646
|
-
for (const
|
|
49647
|
-
if (compactRow3[
|
|
49647
|
+
for (const path54 of missing) {
|
|
49648
|
+
if (compactRow3[path54] !== void 0) row[path54] = compactRow3[path54];
|
|
49648
49649
|
}
|
|
49649
49650
|
return row;
|
|
49650
49651
|
});
|
|
49651
|
-
const unmatched = paths.filter((
|
|
49652
|
+
const unmatched = paths.filter((path54) => projected.every((row) => row[path54] === void 0));
|
|
49652
49653
|
if (projected.length === 0 || unmatched.length === 0) return { rows: projected, fields: paths };
|
|
49653
49654
|
const carried = /* @__PURE__ */ new Set([...compact5.columns, ...raw.flatMap((row) => Object.keys(row))]);
|
|
49654
49655
|
return {
|
|
@@ -51008,54 +51009,54 @@ function ruleFields(node) {
|
|
|
51008
51009
|
([key, value2]) => (key === "and" || key === "or") && Array.isArray(value2) ? value2.flatMap(ruleFields) : [key]
|
|
51009
51010
|
);
|
|
51010
51011
|
}
|
|
51011
|
-
function branchRefusal(key, value2,
|
|
51012
|
+
function branchRefusal(key, value2, path54) {
|
|
51012
51013
|
if (!Array.isArray(value2) || value2.length === 0) {
|
|
51013
|
-
return `${
|
|
51014
|
+
return `${path54}.${key} must be a non-empty array of conditions \u2014 an empty one counts every event the pixel sends`;
|
|
51014
51015
|
}
|
|
51015
51016
|
for (const [index, child] of value2.entries()) {
|
|
51016
|
-
const refusal = ruleNodeRefusal(child, `${
|
|
51017
|
+
const refusal = ruleNodeRefusal(child, `${path54}.${key}[${index}]`);
|
|
51017
51018
|
if (refusal) {
|
|
51018
51019
|
return refusal;
|
|
51019
51020
|
}
|
|
51020
51021
|
}
|
|
51021
51022
|
return void 0;
|
|
51022
51023
|
}
|
|
51023
|
-
function conditionRefusal(key, value2,
|
|
51024
|
-
return key === "and" || key === "or" ? branchRefusal(key, value2,
|
|
51024
|
+
function conditionRefusal(key, value2, path54) {
|
|
51025
|
+
return key === "and" || key === "or" ? branchRefusal(key, value2, path54) : leafRefusal(key, value2, path54);
|
|
51025
51026
|
}
|
|
51026
|
-
function ruleNodeRefusal(node,
|
|
51027
|
+
function ruleNodeRefusal(node, path54) {
|
|
51027
51028
|
if (!isRecord2(node)) {
|
|
51028
|
-
return `${
|
|
51029
|
+
return `${path54} must be an object like {"URL":{"i_contains":"/thank-you"}}`;
|
|
51029
51030
|
}
|
|
51030
51031
|
const entries = Object.entries(node);
|
|
51031
51032
|
if (entries.length === 0) {
|
|
51032
|
-
return `${
|
|
51033
|
+
return `${path54} is empty \u2014 a rule with no condition counts every event the pixel sends`;
|
|
51033
51034
|
}
|
|
51034
51035
|
if (entries.length > 1) {
|
|
51035
|
-
return `${
|
|
51036
|
+
return `${path54} names ${entries.length} conditions side by side (${entries.map(([key]) => key).join(", ")}) \u2014 Meta reads one branch or one field test per object, so combine them with {"and":[\u2026]}`;
|
|
51036
51037
|
}
|
|
51037
51038
|
for (const [key, value2] of entries) {
|
|
51038
|
-
const refusal = conditionRefusal(key, value2,
|
|
51039
|
+
const refusal = conditionRefusal(key, value2, path54);
|
|
51039
51040
|
if (refusal) {
|
|
51040
51041
|
return refusal;
|
|
51041
51042
|
}
|
|
51042
51043
|
}
|
|
51043
51044
|
return void 0;
|
|
51044
51045
|
}
|
|
51045
|
-
function leafRefusal(field, condition,
|
|
51046
|
+
function leafRefusal(field, condition, path54) {
|
|
51046
51047
|
if (!isRecord2(condition)) {
|
|
51047
|
-
return `${
|
|
51048
|
+
return `${path54}.${field} must be a test like {"i_contains":"/thank-you"}, not a bare value`;
|
|
51048
51049
|
}
|
|
51049
51050
|
const tests = Object.entries(condition);
|
|
51050
51051
|
if (tests.length === 0) {
|
|
51051
|
-
return `${
|
|
51052
|
+
return `${path54}.${field} names no test \u2014 use one of ${Object.keys(RULE_OPERATORS).join(", ")}`;
|
|
51052
51053
|
}
|
|
51053
51054
|
if (tests.length > 1) {
|
|
51054
|
-
return `${
|
|
51055
|
+
return `${path54}.${field} names ${tests.length} operators at once (${tests.map(([operator]) => operator).join(", ")}) \u2014 test a field once per condition and combine them with {"and":[\u2026]}`;
|
|
51055
51056
|
}
|
|
51056
51057
|
for (const [operator] of tests) {
|
|
51057
51058
|
if (!RULE_OPERATORS[operator]) {
|
|
51058
|
-
return `${
|
|
51059
|
+
return `${path54}.${field} uses "${operator}", which is not a Meta rule operator \u2014 use one of ${Object.keys(RULE_OPERATORS).join(", ")} (URL matching is i_contains, not contains or includes)`;
|
|
51059
51060
|
}
|
|
51060
51061
|
}
|
|
51061
51062
|
return void 0;
|
|
@@ -51771,11 +51772,11 @@ var updateStatusSchema2 = z44.enum(UPDATE_STATUSES);
|
|
|
51771
51772
|
function currencyMinimums2(currencyCode) {
|
|
51772
51773
|
return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
|
|
51773
51774
|
}
|
|
51774
|
-
function validateDailyBudgetFloor(money, ctx,
|
|
51775
|
+
function validateDailyBudgetFloor(money, ctx, path54) {
|
|
51775
51776
|
if (money?.currencyCode) {
|
|
51776
51777
|
const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
|
|
51777
51778
|
if (Number(money.amount) < min) {
|
|
51778
|
-
ctx.addIssue({ code: "custom", path:
|
|
51779
|
+
ctx.addIssue({ code: "custom", path: path54, message: `below the ${min} ${money.currencyCode} daily minimum` });
|
|
51779
51780
|
}
|
|
51780
51781
|
}
|
|
51781
51782
|
}
|
|
@@ -52693,19 +52694,19 @@ function refuseDeleteByUpdate(payload, group) {
|
|
|
52693
52694
|
);
|
|
52694
52695
|
}
|
|
52695
52696
|
}
|
|
52696
|
-
function loadJsonFileArg3(
|
|
52697
|
-
if (typeof
|
|
52697
|
+
function loadJsonFileArg3(path54) {
|
|
52698
|
+
if (typeof path54 !== "string" || path54.length === 0) {
|
|
52698
52699
|
return {};
|
|
52699
52700
|
}
|
|
52700
52701
|
try {
|
|
52701
|
-
const parsed = JSON.parse(readFileSync11(
|
|
52702
|
+
const parsed = JSON.parse(readFileSync11(path54, "utf8"));
|
|
52702
52703
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
52703
|
-
failWriteValidation4(`${
|
|
52704
|
+
failWriteValidation4(`${path54} must contain a JSON object`);
|
|
52704
52705
|
}
|
|
52705
52706
|
return parsed;
|
|
52706
52707
|
} catch (err) {
|
|
52707
52708
|
if (err instanceof SyntaxError) {
|
|
52708
|
-
failWriteValidation4(`${
|
|
52709
|
+
failWriteValidation4(`${path54} is not valid JSON: ${err.message}`);
|
|
52709
52710
|
}
|
|
52710
52711
|
throw err;
|
|
52711
52712
|
}
|
|
@@ -58476,12 +58477,12 @@ function flowExists(slug) {
|
|
|
58476
58477
|
return existsSync3(join2(flowsDir(), slug, "_data.json"));
|
|
58477
58478
|
}
|
|
58478
58479
|
function readFlowTree(slug) {
|
|
58479
|
-
const
|
|
58480
|
-
if (!existsSync3(
|
|
58480
|
+
const path54 = join2(flowsDir(), slug, "_data.json");
|
|
58481
|
+
if (!existsSync3(path54)) {
|
|
58481
58482
|
failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
|
|
58482
58483
|
}
|
|
58483
58484
|
try {
|
|
58484
|
-
return JSON.parse(readFileSync13(
|
|
58485
|
+
return JSON.parse(readFileSync13(path54, "utf-8"));
|
|
58485
58486
|
} catch (error) {
|
|
58486
58487
|
failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
|
|
58487
58488
|
}
|
|
@@ -59441,12 +59442,12 @@ function readPeopleFile(args8) {
|
|
|
59441
59442
|
}
|
|
59442
59443
|
function unreadablePeopleFile(args8) {
|
|
59443
59444
|
if (!args8["people-file"]) return void 0;
|
|
59444
|
-
const
|
|
59445
|
+
const path54 = String(args8["people-file"]);
|
|
59445
59446
|
try {
|
|
59446
|
-
readFileSync14(
|
|
59447
|
+
readFileSync14(path54, "utf8");
|
|
59447
59448
|
return void 0;
|
|
59448
59449
|
} catch {
|
|
59449
|
-
return `Couldn't read --people-file ${
|
|
59450
|
+
return `Couldn't read --people-file ${path54}. Check the path, or pass the people inline with --person.`;
|
|
59450
59451
|
}
|
|
59451
59452
|
}
|
|
59452
59453
|
function peopleLimitError(args8) {
|
|
@@ -60418,11 +60419,11 @@ function parseFieldArgs(argv) {
|
|
|
60418
60419
|
for (const pair of parseRepeated(argv, "--field")) {
|
|
60419
60420
|
const eq = pair.indexOf("=");
|
|
60420
60421
|
const field = eq === -1 ? pair : pair.slice(0, eq);
|
|
60421
|
-
const
|
|
60422
|
+
const path54 = eq === -1 ? "" : pair.slice(eq + 1).trim();
|
|
60422
60423
|
const problem = mappingRowProblem(field);
|
|
60423
60424
|
if (problem !== null) throw new ConversionArgsError(problem);
|
|
60424
|
-
if (
|
|
60425
|
-
mapping[field] =
|
|
60425
|
+
if (path54 === "") throw new ConversionArgsError(`--field ${field} needs a path: --field ${field}=<dotted.path>.`);
|
|
60426
|
+
mapping[field] = path54;
|
|
60426
60427
|
}
|
|
60427
60428
|
return mapping;
|
|
60428
60429
|
}
|
|
@@ -62024,11 +62025,11 @@ function unwrap(response) {
|
|
|
62024
62025
|
}
|
|
62025
62026
|
return response.data;
|
|
62026
62027
|
}
|
|
62027
|
-
async function readAvatars(
|
|
62028
|
-
return unwrap(await apiGet(
|
|
62028
|
+
async function readAvatars(path54, params) {
|
|
62029
|
+
return unwrap(await apiGet(path54, params));
|
|
62029
62030
|
}
|
|
62030
|
-
async function writeAvatars(
|
|
62031
|
-
return unwrap(await apiPost(
|
|
62031
|
+
async function writeAvatars(path54, body) {
|
|
62032
|
+
return unwrap(await apiPost(path54, body));
|
|
62032
62033
|
}
|
|
62033
62034
|
|
|
62034
62035
|
// src/commands/avatars/create.ts
|
|
@@ -62937,12 +62938,12 @@ function missingFontFiles(urls, available) {
|
|
|
62937
62938
|
function planFontAdoption(sources, families) {
|
|
62938
62939
|
const wanted = new Map(families.map((family) => [normalizeFamily(family), family]));
|
|
62939
62940
|
const byFamily = /* @__PURE__ */ new Map();
|
|
62940
|
-
for (const { path:
|
|
62941
|
-
const dir = posix.dirname(
|
|
62941
|
+
for (const { path: path54, source } of sources) {
|
|
62942
|
+
const dir = posix.dirname(path54);
|
|
62942
62943
|
for (const face of declaredFontFaces(source)) {
|
|
62943
62944
|
if (!wanted.has(face.family)) continue;
|
|
62944
62945
|
const perFile = byFamily.get(face.family) ?? /* @__PURE__ */ new Map();
|
|
62945
|
-
perFile.set(
|
|
62946
|
+
perFile.set(path54, [...perFile.get(path54) ?? [], rebaseFontFaceSrc(face.block, dir)]);
|
|
62946
62947
|
byFamily.set(face.family, perFile);
|
|
62947
62948
|
}
|
|
62948
62949
|
}
|
|
@@ -65041,12 +65042,12 @@ async function probeDuration(filePath) {
|
|
|
65041
65042
|
}
|
|
65042
65043
|
|
|
65043
65044
|
// src/commands/canvas/rerun.ts
|
|
65044
|
-
import
|
|
65045
|
+
import path22 from "path";
|
|
65045
65046
|
import { defineCommand as defineCommand132 } from "citty";
|
|
65046
65047
|
|
|
65047
65048
|
// src/commands/canvas/run.ts
|
|
65048
|
-
import { readFile as
|
|
65049
|
-
import
|
|
65049
|
+
import { readFile as readFile19 } from "fs/promises";
|
|
65050
|
+
import path21 from "path";
|
|
65050
65051
|
import { defineCommand as defineCommand131 } from "citty";
|
|
65051
65052
|
|
|
65052
65053
|
// src/commands/canvas/output-ratio.ts
|
|
@@ -69459,19 +69460,80 @@ async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
|
|
|
69459
69460
|
return { canvasPath: path18.resolve(resolvedTarget, manifest.canvas.path), restored, upToDate };
|
|
69460
69461
|
}
|
|
69461
69462
|
|
|
69463
|
+
// src/commands/canvas/render-budget.ts
|
|
69464
|
+
import { mkdir as mkdir6, readFile as readFile15, rm as rm5, writeFile as writeFile9 } from "fs/promises";
|
|
69465
|
+
import path19 from "path";
|
|
69466
|
+
var RENDERS_PER_ROUND = 3;
|
|
69467
|
+
function claimRender(ledger, chatId) {
|
|
69468
|
+
const current = ledger && ledger.chatId === chatId ? ledger : { chatId, renders: 0 };
|
|
69469
|
+
if (current.renders >= RENDERS_PER_ROUND) return { allowed: false, renders: current.renders, ledger: current };
|
|
69470
|
+
const next = { chatId, renders: current.renders + 1 };
|
|
69471
|
+
return { allowed: true, renders: next.renders, ledger: next };
|
|
69472
|
+
}
|
|
69473
|
+
function ledgerFile(canvasRoot, slug) {
|
|
69474
|
+
return path19.join(canvasRoot, ".render-budget", `${slug}.json`);
|
|
69475
|
+
}
|
|
69476
|
+
async function readLedger(canvasRoot, slug) {
|
|
69477
|
+
try {
|
|
69478
|
+
const raw = JSON.parse(await readFile15(ledgerFile(canvasRoot, slug), "utf8"));
|
|
69479
|
+
return typeof raw.renders === "number" ? { chatId: raw.chatId ?? null, renders: raw.renders } : null;
|
|
69480
|
+
} catch {
|
|
69481
|
+
return null;
|
|
69482
|
+
}
|
|
69483
|
+
}
|
|
69484
|
+
async function spendRender(canvasRoot, slug, chatId) {
|
|
69485
|
+
const claim = claimRender(await readLedger(canvasRoot, slug), chatId);
|
|
69486
|
+
if (claim.allowed) {
|
|
69487
|
+
try {
|
|
69488
|
+
await mkdir6(path19.dirname(ledgerFile(canvasRoot, slug)), { recursive: true });
|
|
69489
|
+
await writeFile9(ledgerFile(canvasRoot, slug), JSON.stringify(claim.ledger));
|
|
69490
|
+
} catch {
|
|
69491
|
+
}
|
|
69492
|
+
}
|
|
69493
|
+
return { allowed: claim.allowed, renders: claim.renders };
|
|
69494
|
+
}
|
|
69495
|
+
async function refundRender(canvasRoot, slug, chatId) {
|
|
69496
|
+
const ledger = await readLedger(canvasRoot, slug);
|
|
69497
|
+
if (!ledger || ledger.chatId !== chatId || ledger.renders <= 0) return;
|
|
69498
|
+
await writeFile9(ledgerFile(canvasRoot, slug), JSON.stringify({ chatId, renders: ledger.renders - 1 })).catch(
|
|
69499
|
+
() => void 0
|
|
69500
|
+
);
|
|
69501
|
+
}
|
|
69502
|
+
async function openRenderRound(canvasRoot, slug) {
|
|
69503
|
+
await rm5(ledgerFile(canvasRoot, slug), { force: true }).catch(() => void 0);
|
|
69504
|
+
}
|
|
69505
|
+
async function openRenderRoundInWorkspace(slug) {
|
|
69506
|
+
const cwd = process.cwd();
|
|
69507
|
+
await openRenderRound(canvasOutputsRoot(path19.join(cwd, "src", "creatives", slug, "canvas.json"), cwd), slug);
|
|
69508
|
+
}
|
|
69509
|
+
function isStaticAdCanvas(parsed) {
|
|
69510
|
+
const nodes = parsed?.nodes;
|
|
69511
|
+
return Array.isArray(nodes) && nodes.some((n) => n?.id === "compose_spec" && n?.type === "ingest");
|
|
69512
|
+
}
|
|
69513
|
+
function renderBudgetRefusal(slug) {
|
|
69514
|
+
return {
|
|
69515
|
+
ok: false,
|
|
69516
|
+
error: {
|
|
69517
|
+
code: "RENDER_BUDGET",
|
|
69518
|
+
message: `${slug} has been rendered ${RENDERS_PER_ROUND} times in this chat since the person last reacted to it. It is not rendered again until they do.`,
|
|
69519
|
+
fix: `Stop fixing. Show the person the best version you have (the run with the fewest blocking problems), say in one line what is still wrong, and ask whether to keep it or what to change. When they answer, record it with baker creatives feedback record --slug ${slug} --run <run_id> (or baker creatives select --slug ${slug} --run <run_id>), which opens a new round of ${RENDERS_PER_ROUND} renders. Working as a subagent: return now with the best run id and what is left.`
|
|
69520
|
+
}
|
|
69521
|
+
};
|
|
69522
|
+
}
|
|
69523
|
+
|
|
69462
69524
|
// src/commands/canvas/static-run-review.ts
|
|
69463
|
-
import { readFile as
|
|
69525
|
+
import { readFile as readFile18 } from "fs/promises";
|
|
69464
69526
|
|
|
69465
69527
|
// src/engine/nodes/local/lib/static-review.ts
|
|
69466
|
-
import { readFile as
|
|
69528
|
+
import { readFile as readFile17 } from "fs/promises";
|
|
69467
69529
|
import sharp2 from "sharp";
|
|
69468
69530
|
import { z as z52 } from "zod";
|
|
69469
69531
|
|
|
69470
69532
|
// src/engine/nodes/local/lib/render-review.ts
|
|
69471
69533
|
import { execFile as execFile2 } from "child_process";
|
|
69472
|
-
import { mkdtemp, readdir as readdir8, readFile as
|
|
69534
|
+
import { mkdtemp, readdir as readdir8, readFile as readFile16, rm as rm6 } from "fs/promises";
|
|
69473
69535
|
import { tmpdir } from "os";
|
|
69474
|
-
import
|
|
69536
|
+
import path20 from "path";
|
|
69475
69537
|
import { promisify as promisify2 } from "util";
|
|
69476
69538
|
import { z as z51 } from "zod";
|
|
69477
69539
|
var RENDER_REVIEW_MODEL = "~google/gemini-pro-latest";
|
|
@@ -69860,7 +69922,7 @@ async function askGateway(model, parts, timeoutMs, spent) {
|
|
|
69860
69922
|
async function askVideo(video, prompt, spent) {
|
|
69861
69923
|
let bytes;
|
|
69862
69924
|
try {
|
|
69863
|
-
bytes = await
|
|
69925
|
+
bytes = await readFile16(video);
|
|
69864
69926
|
} catch {
|
|
69865
69927
|
return null;
|
|
69866
69928
|
}
|
|
@@ -69897,14 +69959,14 @@ async function reviewRenderedVideo(opts) {
|
|
|
69897
69959
|
const duration = await videoDuration(opts.video);
|
|
69898
69960
|
if (duration === null) return null;
|
|
69899
69961
|
const spent = (usage) => opts.onSpend?.(usage);
|
|
69900
|
-
const dir = await mkdtemp(
|
|
69962
|
+
const dir = await mkdtemp(path20.join(tmpdir(), "baker-review-"));
|
|
69901
69963
|
try {
|
|
69902
69964
|
const grabbed = [];
|
|
69903
69965
|
for (const [i, at] of reviewFrameTimes(duration, RENDER_REVIEW_FRAMES).entries()) {
|
|
69904
|
-
const out =
|
|
69966
|
+
const out = path20.join(dir, `f${String(i).padStart(2, "0")}.jpg`);
|
|
69905
69967
|
try {
|
|
69906
69968
|
await execFileAsync2("ffmpeg", buildFrameGrabArgs(opts.video, at, out), { timeout: 3e4 });
|
|
69907
|
-
grabbed.push({ at, image: await
|
|
69969
|
+
grabbed.push({ at, image: await readFile16(out, { encoding: "base64" }) });
|
|
69908
69970
|
} catch {
|
|
69909
69971
|
}
|
|
69910
69972
|
}
|
|
@@ -69947,11 +70009,11 @@ async function reviewRenderedVideo(opts) {
|
|
|
69947
70009
|
);
|
|
69948
70010
|
return findings;
|
|
69949
70011
|
} finally {
|
|
69950
|
-
await
|
|
70012
|
+
await rm6(dir, { recursive: true, force: true });
|
|
69951
70013
|
}
|
|
69952
70014
|
}
|
|
69953
70015
|
async function reviewContextBeside(canvasPath) {
|
|
69954
|
-
const raw = await
|
|
70016
|
+
const raw = await readFile16(path20.join(path20.dirname(canvasPath), "prompt.json"), "utf-8").catch(() => null);
|
|
69955
70017
|
if (!raw) return {};
|
|
69956
70018
|
try {
|
|
69957
70019
|
const blueprint = JSON.parse(raw);
|
|
@@ -69967,12 +70029,12 @@ async function reviewContextBeside(canvasPath) {
|
|
|
69967
70029
|
}
|
|
69968
70030
|
}
|
|
69969
70031
|
async function sceneWindowsBeside(canvasPath) {
|
|
69970
|
-
const dir =
|
|
70032
|
+
const dir = path20.join(path20.dirname(canvasPath), "scenes");
|
|
69971
70033
|
const files = await readdir8(dir).catch(() => null);
|
|
69972
70034
|
if (!files) return [];
|
|
69973
70035
|
const windows = [];
|
|
69974
70036
|
for (const name of files.filter((f) => f.endsWith(".json")).sort()) {
|
|
69975
|
-
const raw = await
|
|
70037
|
+
const raw = await readFile16(path20.join(dir, name), "utf-8").catch(() => null);
|
|
69976
70038
|
if (!raw) continue;
|
|
69977
70039
|
try {
|
|
69978
70040
|
const scene = JSON.parse(raw);
|
|
@@ -70099,7 +70161,7 @@ function buildStaticReviewPrompt(opts) {
|
|
|
70099
70161
|
].join("\n");
|
|
70100
70162
|
}
|
|
70101
70163
|
async function bytesOf(source) {
|
|
70102
|
-
if (!/^https?:\/\//i.test(source)) return await
|
|
70164
|
+
if (!/^https?:\/\//i.test(source)) return await readFile17(source);
|
|
70103
70165
|
const res = await fetch(source);
|
|
70104
70166
|
if (!res.ok) throw new Error(`fetch ${res.status}`);
|
|
70105
70167
|
return Buffer.from(await res.arrayBuffer());
|
|
@@ -70204,7 +70266,7 @@ async function drawsLogo(parsed) {
|
|
|
70204
70266
|
const file = compose?.params?.path;
|
|
70205
70267
|
if (typeof file === "string") {
|
|
70206
70268
|
try {
|
|
70207
|
-
const spec = JSON.parse(await
|
|
70269
|
+
const spec = JSON.parse(await readFile18(file, "utf8"));
|
|
70208
70270
|
return spec.logo !== null && spec.logo !== void 0;
|
|
70209
70271
|
} catch {
|
|
70210
70272
|
}
|
|
@@ -70214,7 +70276,7 @@ async function drawsLogo(parsed) {
|
|
|
70214
70276
|
async function textOf(ref) {
|
|
70215
70277
|
const r = ref;
|
|
70216
70278
|
try {
|
|
70217
|
-
if (typeof r?.path === "string") return await
|
|
70279
|
+
if (typeof r?.path === "string") return await readFile18(r.path, "utf8");
|
|
70218
70280
|
if (typeof r?.url === "string") {
|
|
70219
70281
|
const res = await fetch(r.url);
|
|
70220
70282
|
return res.ok ? await res.text() : null;
|
|
@@ -70224,6 +70286,25 @@ async function textOf(ref) {
|
|
|
70224
70286
|
}
|
|
70225
70287
|
return null;
|
|
70226
70288
|
}
|
|
70289
|
+
var LAYOUT_FIX = "Overlapping or unreadable copy is a layout fix, not a new picture. In prompt.json make the line smaller (`size` in its layout region's `text`), give the line in `text_content` a `treatment` of `highlight` or `sticker` (a solid fill behind the words), or shorten it (in `text_content` and in its layout region, the same words in both). Those re-draw only the formats from the cached pictures. Moving a region (`x_pct`/`y_pct`) or editing the scene re-paints all three pictures.";
|
|
70290
|
+
function verdictHints(verdict, round4) {
|
|
70291
|
+
if (verdict === "needs_fixes") {
|
|
70292
|
+
if (round4 && round4.renders >= round4.of) {
|
|
70293
|
+
return [
|
|
70294
|
+
`NOT READY, and this was render ${round4.renders} of ${round4.of} for this creative before the person sees it: stop fixing. Show them the best version you have (this one or an earlier run), say in one line what is still wrong, and ask whether to keep it or what to change. Record their answer with baker creatives feedback record; that opens a new round.`
|
|
70295
|
+
];
|
|
70296
|
+
}
|
|
70297
|
+
return [
|
|
70298
|
+
`NOT READY: the finished ad has blocking problems. Do not present it as done. Fix them with the smallest edit and re-run${round4 ? ` (this was render ${round4.renders} of ${round4.of} before the person sees it)` : ""}.`,
|
|
70299
|
+
LAYOUT_FIX
|
|
70300
|
+
];
|
|
70301
|
+
}
|
|
70302
|
+
if (verdict === "not_reviewed") {
|
|
70303
|
+
return ["The finished ad was NOT reviewed (no model gateway key here). Look at every format yourself before showing it."];
|
|
70304
|
+
}
|
|
70305
|
+
if (verdict === "ready") return ["Reviewed every format of the finished ad: nothing to fix."];
|
|
70306
|
+
return [];
|
|
70307
|
+
}
|
|
70227
70308
|
async function reviewStaticRun(opts) {
|
|
70228
70309
|
const targets = staticReviewTargets(opts.parsed, opts.output);
|
|
70229
70310
|
if (!targets) return null;
|
|
@@ -70232,7 +70313,7 @@ async function reviewStaticRun(opts) {
|
|
|
70232
70313
|
let copy = [];
|
|
70233
70314
|
let mechanisms = [];
|
|
70234
70315
|
try {
|
|
70235
|
-
const blueprint = JSON.parse(await
|
|
70316
|
+
const blueprint = JSON.parse(await readFile18(targets.promptPath, "utf8"));
|
|
70236
70317
|
copy = copyStrings(blueprint);
|
|
70237
70318
|
mechanisms = winningMechanisms(blueprint).map((m) => m.device);
|
|
70238
70319
|
} catch {
|
|
@@ -70248,16 +70329,7 @@ async function reviewStaticRun(opts) {
|
|
|
70248
70329
|
onSpend: opts.onSpend
|
|
70249
70330
|
});
|
|
70250
70331
|
const verdict = staticVerdict(review);
|
|
70251
|
-
const hints2 =
|
|
70252
|
-
if (verdict === "needs_fixes") {
|
|
70253
|
-
hints2.push(
|
|
70254
|
-
"NOT READY: the finished ad has blocking problems. Do not present it as done. Fix them and re-run; the pictures come from the cache unless you changed what they depend on."
|
|
70255
|
-
);
|
|
70256
|
-
} else if (verdict === "not_reviewed") {
|
|
70257
|
-
hints2.push("The finished ad was NOT reviewed (no model gateway key here). Look at every format yourself before showing it.");
|
|
70258
|
-
} else if (verdict === "ready") {
|
|
70259
|
-
hints2.push("Reviewed every format of the finished ad: nothing to fix.");
|
|
70260
|
-
}
|
|
70332
|
+
const hints2 = verdictHints(verdict, opts.round ?? null);
|
|
70261
70333
|
for (const f of review ?? []) hints2.push(`${f.severity === "blocking" ? "Must fix" : "Should fix"}: ${f.what}`);
|
|
70262
70334
|
hints2.push(shapeHint(shape, copy.length));
|
|
70263
70335
|
if (judge && judge.length > 0) {
|
|
@@ -70363,9 +70435,9 @@ function stopBeforeKinds(until) {
|
|
|
70363
70435
|
return null;
|
|
70364
70436
|
}
|
|
70365
70437
|
async function executeCanvasRun(opts) {
|
|
70366
|
-
const filePath =
|
|
70438
|
+
const filePath = path21.resolve(opts.file);
|
|
70367
70439
|
const canvasRoot = canvasOutputsRoot(filePath);
|
|
70368
|
-
const raw = await
|
|
70440
|
+
const raw = await readFile19(filePath, "utf8");
|
|
70369
70441
|
let parsed;
|
|
70370
70442
|
try {
|
|
70371
70443
|
parsed = JSON.parse(raw);
|
|
@@ -70376,7 +70448,7 @@ async function executeCanvasRun(opts) {
|
|
|
70376
70448
|
process.exit(2);
|
|
70377
70449
|
}
|
|
70378
70450
|
const attemptedSlug = creativeSlugFromCanvasPath(filePath);
|
|
70379
|
-
if (attemptedSlug) await clearCreativeDirty(attemptedSlug,
|
|
70451
|
+
if (attemptedSlug) await clearCreativeDirty(attemptedSlug, path21.join(canvasRoot, ".dirty"));
|
|
70380
70452
|
const healed = await healCanvasPaths(filePath, parsed);
|
|
70381
70453
|
parsed = healed.canvas;
|
|
70382
70454
|
const canvasText = healed.text ?? raw;
|
|
@@ -70387,7 +70459,7 @@ ${describeRewrites(healed.rewrites)}
|
|
|
70387
70459
|
`
|
|
70388
70460
|
);
|
|
70389
70461
|
}
|
|
70390
|
-
parsed = resolveRelativeCanvasPaths(parsed,
|
|
70462
|
+
parsed = resolveRelativeCanvasPaths(parsed, path21.dirname(filePath));
|
|
70391
70463
|
try {
|
|
70392
70464
|
await syncStyleProjection(parsed, (line) => process.stderr.write(`${line}
|
|
70393
70465
|
`));
|
|
@@ -70476,7 +70548,7 @@ ${describeRewrites(healed.rewrites)}
|
|
|
70476
70548
|
}
|
|
70477
70549
|
}
|
|
70478
70550
|
const engine = createEngineFromEnv({
|
|
70479
|
-
cacheDir: opts.cacheDir ??
|
|
70551
|
+
cacheDir: opts.cacheDir ?? path21.join(canvasRoot, ".cache"),
|
|
70480
70552
|
outputsDir: opts.outputsDir ?? canvasRoot,
|
|
70481
70553
|
log: (line) => process.stdout.write(`${line}
|
|
70482
70554
|
`),
|
|
@@ -70497,7 +70569,7 @@ ${describeRewrites(healed.rewrites)}
|
|
|
70497
70569
|
const canvasSha = sha256Hex(Buffer.from(canvasText));
|
|
70498
70570
|
const creativeSlug = creativeSlugFromCanvasPath(filePath) ?? void 0;
|
|
70499
70571
|
const client = opts.record === false ? null : buildBackendClient();
|
|
70500
|
-
const outputsDir = opts.outputsDir ?
|
|
70572
|
+
const outputsDir = opts.outputsDir ? path21.resolve(opts.outputsDir) : canvasRoot;
|
|
70501
70573
|
const { runId, resumed, source, concurrentRunId, rendering } = await resolveRunId({
|
|
70502
70574
|
explicitRunId: opts.runId,
|
|
70503
70575
|
fresh: opts.fresh === true,
|
|
@@ -70523,6 +70595,12 @@ ${describeRewrites(healed.rewrites)}
|
|
|
70523
70595
|
);
|
|
70524
70596
|
process.exit(2);
|
|
70525
70597
|
}
|
|
70598
|
+
const round4 = creativeSlug && !resumed && isStaticAdCanvas(parsed) ? await spendRender(canvasRoot, creativeSlug, optionalChatId() ?? null) : null;
|
|
70599
|
+
if (round4 && !round4.allowed && creativeSlug) {
|
|
70600
|
+
process.stderr.write(`${JSON.stringify(renderBudgetRefusal(creativeSlug), null, 2)}
|
|
70601
|
+
`);
|
|
70602
|
+
process.exit(2);
|
|
70603
|
+
}
|
|
70526
70604
|
if (resumed) {
|
|
70527
70605
|
process.stdout.write(
|
|
70528
70606
|
`[resume] continuing interrupted run ${runId}${source === "remote" ? " (adopted from run history)" : ""} \u2014 in-flight jobs re-attach, cached nodes skip
|
|
@@ -70549,7 +70627,7 @@ ${describeRewrites(healed.rewrites)}
|
|
|
70549
70627
|
const canvasSnapshotUrl = client && creativeSlug ? await uploadRunSnapshot(client, { canvasPath: filePath, raw: canvasText, creativeSlug, parsed }) ?? void 0 : void 0;
|
|
70550
70628
|
const recordMeta = {
|
|
70551
70629
|
creativeSlug,
|
|
70552
|
-
canvasPath:
|
|
70630
|
+
canvasPath: path21.relative(path21.dirname(canvasRoot), filePath) || void 0,
|
|
70553
70631
|
canvasSha,
|
|
70554
70632
|
// The fingerprint the dashboard compares against the current source to flag
|
|
70555
70633
|
// "edited since last render". Computed from the on-disk canvas.json +
|
|
@@ -70620,7 +70698,8 @@ ${describeRewrites(healed.rewrites)}
|
|
|
70620
70698
|
parsed,
|
|
70621
70699
|
output: result.output,
|
|
70622
70700
|
outputsByNode: result.outputs_by_node,
|
|
70623
|
-
onSpend: (usage) => reviewUsage.push(usage)
|
|
70701
|
+
onSpend: (usage) => reviewUsage.push(usage),
|
|
70702
|
+
round: round4 ? { renders: round4.renders, of: RENDERS_PER_ROUND } : null
|
|
70624
70703
|
});
|
|
70625
70704
|
const hints2 = staticReview ? staticReview.hints : reviewable ? review === null ? [RENDER_REVIEW_UNAVAILABLE] : review.length === 0 ? ["Reviewed the finished video frame by frame \u2014 nothing to report."] : [
|
|
70626
70705
|
`Reviewed the finished video and found ${review.length} thing${review.length === 1 ? "" : "s"} to fix. Show the user what is wrong before you show them the ad.`,
|
|
@@ -70706,6 +70785,9 @@ ${describeRewrites(healed.rewrites)}
|
|
|
70706
70785
|
process.exit(1);
|
|
70707
70786
|
}
|
|
70708
70787
|
await clearRunMarker(outputsDir, filePath);
|
|
70788
|
+
if (round4?.allowed && creativeSlug && failedOnlyOnTransientErrors(e)) {
|
|
70789
|
+
await refundRender(canvasRoot, creativeSlug, optionalChatId() ?? null);
|
|
70790
|
+
}
|
|
70709
70791
|
if (e instanceof ValidationError) {
|
|
70710
70792
|
process.stderr.write(
|
|
70711
70793
|
`${JSON.stringify({ ok: false, error: { code: "validation", issues: e.issues } }, null, 2)}
|
|
@@ -70817,7 +70899,7 @@ var rerunCommand = defineCommand132({
|
|
|
70817
70899
|
if (latest.canvasSha && manifest.canvasSha !== latest.canvasSha) {
|
|
70818
70900
|
fail3("snapshot_mismatch", `snapshot manifest for run ${latest.runId} does not match its recorded canvas sha`);
|
|
70819
70901
|
}
|
|
70820
|
-
const targetDir =
|
|
70902
|
+
const targetDir = path22.resolve("src", "creatives", slug);
|
|
70821
70903
|
let restoredCanvasPath;
|
|
70822
70904
|
try {
|
|
70823
70905
|
const restore = await restoreRunSnapshot(manifest, targetDir, { force: args8["force-remote"] === true });
|
|
@@ -70866,8 +70948,8 @@ async function fetchManifest(url) {
|
|
|
70866
70948
|
}
|
|
70867
70949
|
|
|
70868
70950
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
70869
|
-
import { access, mkdir as
|
|
70870
|
-
import
|
|
70951
|
+
import { access, mkdir as mkdir7, readFile as readFile21, writeFile as writeFile10 } from "fs/promises";
|
|
70952
|
+
import path27 from "path";
|
|
70871
70953
|
import { defineCommand as defineCommand134 } from "citty";
|
|
70872
70954
|
|
|
70873
70955
|
// src/engine/scaffold/staticAd.ts
|
|
@@ -71051,8 +71133,8 @@ function extensionPrompt(format, legend, hasElementRefs) {
|
|
|
71051
71133
|
"{{plate_spec}}"
|
|
71052
71134
|
].join("\n");
|
|
71053
71135
|
}
|
|
71054
|
-
function ingestNode(id,
|
|
71055
|
-
return { id, type: "ingest", params: { source: "path", path:
|
|
71136
|
+
function ingestNode(id, path54, expect) {
|
|
71137
|
+
return { id, type: "ingest", params: { source: "path", path: path54, expect } };
|
|
71056
71138
|
}
|
|
71057
71139
|
function sanitizeId2(raw, fallback) {
|
|
71058
71140
|
const id = raw.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
@@ -71497,22 +71579,22 @@ function staticAdReport(input, elementsInput, opts) {
|
|
|
71497
71579
|
|
|
71498
71580
|
// src/commands/canvas/composition-path.ts
|
|
71499
71581
|
import { existsSync as existsSync5 } from "fs";
|
|
71500
|
-
import
|
|
71582
|
+
import path23 from "path";
|
|
71501
71583
|
function resolveShippedCanvasDir(name, startDir, exists3 = existsSync5, maxDepth = 8) {
|
|
71502
|
-
const rel =
|
|
71584
|
+
const rel = path23.join("canvas", name);
|
|
71503
71585
|
let dir = startDir;
|
|
71504
71586
|
for (let i = 0; i < maxDepth; i++) {
|
|
71505
|
-
const candidate =
|
|
71506
|
-
if (exists3(
|
|
71507
|
-
const parent =
|
|
71587
|
+
const candidate = path23.join(dir, rel);
|
|
71588
|
+
if (exists3(path23.join(candidate, "meta.json"))) return candidate;
|
|
71589
|
+
const parent = path23.dirname(dir);
|
|
71508
71590
|
if (parent === dir) break;
|
|
71509
71591
|
dir = parent;
|
|
71510
71592
|
}
|
|
71511
|
-
return
|
|
71593
|
+
return path23.resolve(startDir, "../../../", rel);
|
|
71512
71594
|
}
|
|
71513
71595
|
|
|
71514
71596
|
// src/commands/canvas/creative-definition.ts
|
|
71515
|
-
import
|
|
71597
|
+
import path24 from "path";
|
|
71516
71598
|
var PLATFORM_VALUES = ["meta", "google", "linkedin", "tiktok", "youtube", "x", "other"];
|
|
71517
71599
|
var FORMAT_VALUES = ["1:1", "4:5", "9:16", "16:9", "1.91:1"];
|
|
71518
71600
|
function titleFromSlug(slug) {
|
|
@@ -71560,16 +71642,16 @@ function buildCreativeDefinition(input) {
|
|
|
71560
71642
|
}
|
|
71561
71643
|
|
|
71562
71644
|
// src/commands/canvas/scaffold-static-ad-paths.ts
|
|
71563
|
-
import
|
|
71645
|
+
import path25 from "path";
|
|
71564
71646
|
function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd(), slug) {
|
|
71565
71647
|
const file = rawFile.trim();
|
|
71566
71648
|
const imageIsUrl = /^https?:\/\//i.test(file);
|
|
71567
|
-
const imageSource = imageIsUrl ? file :
|
|
71568
|
-
const outPath = out ?
|
|
71569
|
-
const blueprintPath2 =
|
|
71570
|
-
const creativeDir = slug ?
|
|
71571
|
-
const definitionPath = creativeDir ?
|
|
71572
|
-
const referencesDir = creativeDir ?
|
|
71649
|
+
const imageSource = imageIsUrl ? file : path25.resolve(cwd, file);
|
|
71650
|
+
const outPath = out ? path25.resolve(cwd, out) : slug ? path25.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path25.join(cwd, "static-ad.canvas.json") : path25.join(path25.dirname(imageSource), "static-ad.canvas.json");
|
|
71651
|
+
const blueprintPath2 = path25.join(path25.dirname(outPath), "prompt.json");
|
|
71652
|
+
const creativeDir = slug ? path25.dirname(outPath) : null;
|
|
71653
|
+
const definitionPath = creativeDir ? path25.join(creativeDir, "_definition.md") : null;
|
|
71654
|
+
const referencesDir = creativeDir ? path25.join(creativeDir, "references") : null;
|
|
71573
71655
|
return { imageIsUrl, imageSource, outPath, blueprintPath: blueprintPath2, creativeDir, definitionPath, referencesDir };
|
|
71574
71656
|
}
|
|
71575
71657
|
var SCAFFOLD_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
@@ -71579,8 +71661,8 @@ function isValidScaffoldSlug(slug) {
|
|
|
71579
71661
|
}
|
|
71580
71662
|
|
|
71581
71663
|
// src/commands/canvas/sync-definition.ts
|
|
71582
|
-
import { readdir as readdir9, readFile as
|
|
71583
|
-
import
|
|
71664
|
+
import { readdir as readdir9, readFile as readFile20, stat as stat4 } from "fs/promises";
|
|
71665
|
+
import path26 from "path";
|
|
71584
71666
|
import { defineCommand as defineCommand133 } from "citty";
|
|
71585
71667
|
|
|
71586
71668
|
// src/commands/canvas/definition-graph.ts
|
|
@@ -71673,15 +71755,15 @@ async function syncCreativeDefinitionBestEffort(input) {
|
|
|
71673
71755
|
}
|
|
71674
71756
|
}
|
|
71675
71757
|
async function resolveCanvasPath(inputPath) {
|
|
71676
|
-
const resolved =
|
|
71758
|
+
const resolved = path26.resolve(inputPath);
|
|
71677
71759
|
let dir = resolved;
|
|
71678
71760
|
try {
|
|
71679
71761
|
if ((await stat4(resolved)).isFile()) {
|
|
71680
71762
|
if (resolved.endsWith(".canvas.json")) return resolved;
|
|
71681
|
-
dir =
|
|
71763
|
+
dir = path26.dirname(resolved);
|
|
71682
71764
|
}
|
|
71683
71765
|
} catch {
|
|
71684
|
-
dir = resolved.endsWith(".canvas.json") ?
|
|
71766
|
+
dir = resolved.endsWith(".canvas.json") ? path26.dirname(resolved) : resolved;
|
|
71685
71767
|
}
|
|
71686
71768
|
let entries;
|
|
71687
71769
|
try {
|
|
@@ -71692,7 +71774,7 @@ async function resolveCanvasPath(inputPath) {
|
|
|
71692
71774
|
const canvases = entries.filter((name) => name.endsWith(".canvas.json"));
|
|
71693
71775
|
const slug = creativeSlugFromCanvasPath(`${dir}/x/`);
|
|
71694
71776
|
const chosen = (slug ? canvases.find((name) => name === `${slug}.canvas.json`) : void 0) ?? canvases[0];
|
|
71695
|
-
return chosen ?
|
|
71777
|
+
return chosen ? path26.join(dir, chosen) : null;
|
|
71696
71778
|
}
|
|
71697
71779
|
var syncDefinitionCommand = defineCommand133({
|
|
71698
71780
|
meta: {
|
|
@@ -71715,7 +71797,7 @@ var syncDefinitionCommand = defineCommand133({
|
|
|
71715
71797
|
if (!slug) return;
|
|
71716
71798
|
let canvas;
|
|
71717
71799
|
try {
|
|
71718
|
-
canvas = JSON.parse(await
|
|
71800
|
+
canvas = JSON.parse(await readFile20(canvasPath, "utf8"));
|
|
71719
71801
|
} catch {
|
|
71720
71802
|
return;
|
|
71721
71803
|
}
|
|
@@ -71756,19 +71838,19 @@ async function resolveCastAvatars(flag) {
|
|
|
71756
71838
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
71757
71839
|
var SHIPPED_STATIC_COMPOSITION = resolveShippedCanvasDir("static-ad-composition", import.meta.dirname);
|
|
71758
71840
|
async function writeStaticComposition(dir, width, height) {
|
|
71759
|
-
await
|
|
71760
|
-
const html = await
|
|
71761
|
-
await
|
|
71762
|
-
|
|
71841
|
+
await mkdir7(dir, { recursive: true });
|
|
71842
|
+
const html = await readFile21(path27.join(SHIPPED_STATIC_COMPOSITION, "index.html"), "utf8");
|
|
71843
|
+
await writeFile10(
|
|
71844
|
+
path27.join(dir, "index.html"),
|
|
71763
71845
|
html.replaceAll("__WIDTH__", String(width)).replaceAll("__HEIGHT__", String(height)),
|
|
71764
71846
|
"utf8"
|
|
71765
71847
|
);
|
|
71766
|
-
const meta = JSON.parse(await
|
|
71767
|
-
await
|
|
71848
|
+
const meta = JSON.parse(await readFile21(path27.join(SHIPPED_STATIC_COMPOSITION, "meta.json"), "utf8"));
|
|
71849
|
+
await writeFile10(path27.join(dir, "meta.json"), `${JSON.stringify({ ...meta, width, height }, null, 2)}
|
|
71768
71850
|
`, "utf8");
|
|
71769
71851
|
}
|
|
71770
71852
|
function toCanvasRelative2(canvasDir, file) {
|
|
71771
|
-
const rel =
|
|
71853
|
+
const rel = path27.relative(canvasDir, file).split(path27.sep).join("/");
|
|
71772
71854
|
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
71773
71855
|
}
|
|
71774
71856
|
async function fileExists(target2) {
|
|
@@ -71788,7 +71870,7 @@ async function uploadSourceAsReference(source, isUrl, client) {
|
|
|
71788
71870
|
throw new Error(`failed to download source image (${e instanceof Error ? e.message : String(e)})`);
|
|
71789
71871
|
}
|
|
71790
71872
|
} else {
|
|
71791
|
-
bytes = await
|
|
71873
|
+
bytes = await readFile21(source);
|
|
71792
71874
|
}
|
|
71793
71875
|
const safe = await toModelSafeImage(bytes);
|
|
71794
71876
|
const sha2562 = sha256Hex(safe.bytes);
|
|
@@ -71882,7 +71964,7 @@ DROP background extras, decorative props, generic scenery, and anything small or
|
|
|
71882
71964
|
For each kept element return: { "type": one of logo|product|person|animal|badge, "label": a short UPPER_SNAKE_CASE name (e.g. LOGO, PRODUCT, HERO_DOG, TRUSTPILOT), "description": a concrete reusable description to source/shoot the real asset (include the exact expression for a living subject, and its castable attributes \u2014 breed/species for an animal, apparent age band, apparent origin/ethnicity, and wardrobe/setting for a person \u2014 so it can be recast to fit OUR audience/market), "expression": the facial expression for a living subject or null, "reason": why it is identity-critical, "locator": the blueprint entry this element came from as { "collection": one of "subjects" | "people" | "brands_logos", "index": its 0-based position in that array } (people -> people; logos/badges -> brands_logos; products/animals/objects -> subjects). Output ONLY the JSON object.`;
|
|
71883
71965
|
async function loadAssetText(ref, label) {
|
|
71884
71966
|
const r = ref;
|
|
71885
|
-
if (typeof r?.path === "string") return
|
|
71967
|
+
if (typeof r?.path === "string") return readFile21(r.path, "utf8");
|
|
71886
71968
|
if (typeof r?.url === "string") {
|
|
71887
71969
|
const res = await fetch(r.url);
|
|
71888
71970
|
if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
|
|
@@ -72086,7 +72168,7 @@ var scaffoldStaticAdCommand = defineCommand134({
|
|
|
72086
72168
|
process.cwd(),
|
|
72087
72169
|
slug
|
|
72088
72170
|
);
|
|
72089
|
-
await
|
|
72171
|
+
await mkdir7(path27.dirname(outPath), { recursive: true });
|
|
72090
72172
|
const { describeModel, selectModel, layoutModel, genModel } = resolveModels(args8);
|
|
72091
72173
|
const cast = await resolveCastAvatars(args8.avatar);
|
|
72092
72174
|
if ("error" in cast) return fail4("avatar", cast.error);
|
|
@@ -72117,7 +72199,7 @@ var scaffoldStaticAdCommand = defineCommand134({
|
|
|
72117
72199
|
if (layout && annotated && typeof annotated === "object") {
|
|
72118
72200
|
annotated.layout = layout;
|
|
72119
72201
|
}
|
|
72120
|
-
await
|
|
72202
|
+
await writeFile10(blueprintPath2, `${JSON.stringify(annotated, null, 2)}
|
|
72121
72203
|
`, "utf8");
|
|
72122
72204
|
let canvasImagePath = imageSource;
|
|
72123
72205
|
let canvasImageIsUrl = imageIsUrl;
|
|
@@ -72136,7 +72218,7 @@ var scaffoldStaticAdCommand = defineCommand134({
|
|
|
72136
72218
|
);
|
|
72137
72219
|
if (!placementsResult.ok) return fail4("invalid_placements", placementsResult.message);
|
|
72138
72220
|
const placements = placementsResult.placements;
|
|
72139
|
-
const canvasDir =
|
|
72221
|
+
const canvasDir = path27.dirname(outPath);
|
|
72140
72222
|
const workspaceRoot = await findWorkspaceRoot(canvasDir) ?? process.cwd();
|
|
72141
72223
|
const kit = await readStaticBrandKit(workspaceRoot);
|
|
72142
72224
|
const relative = (file) => file ? toCanvasRelative2(canvasDir, file) : void 0;
|
|
@@ -72167,12 +72249,12 @@ var scaffoldStaticAdCommand = defineCommand134({
|
|
|
72167
72249
|
return fail4("scaffold", e instanceof Error ? e.message : String(e));
|
|
72168
72250
|
}
|
|
72169
72251
|
for (const layout2 of staticAdCompositions(blueprint, opts)) {
|
|
72170
|
-
await writeStaticComposition(
|
|
72252
|
+
await writeStaticComposition(path27.resolve(canvasDir, layout2.dir), layout2.width, layout2.height);
|
|
72171
72253
|
}
|
|
72172
72254
|
const specOptions = composeOptionsFor(canvas, annotated);
|
|
72173
|
-
await
|
|
72174
|
-
await
|
|
72175
|
-
const validation = await validateScaffoldedStaticAd(canvas,
|
|
72255
|
+
await writeFile10(path27.resolve(canvasDir, PLATE_SPEC_PATH), renderPlateSpec(annotated, specOptions), "utf8");
|
|
72256
|
+
await writeFile10(path27.resolve(canvasDir, COMPOSE_SPEC_PATH), renderComposeSpec(annotated, kit, specOptions), "utf8");
|
|
72257
|
+
const validation = await validateScaffoldedStaticAd(canvas, path27.dirname(outPath));
|
|
72176
72258
|
if (validation.blocking.length > 0) {
|
|
72177
72259
|
process.stderr.write(
|
|
72178
72260
|
`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.blocking } }, null, 2)}
|
|
@@ -72180,10 +72262,10 @@ var scaffoldStaticAdCommand = defineCommand134({
|
|
|
72180
72262
|
);
|
|
72181
72263
|
process.exit(2);
|
|
72182
72264
|
}
|
|
72183
|
-
await
|
|
72265
|
+
await writeFile10(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
72184
72266
|
`, "utf8");
|
|
72185
72267
|
if (definitionPath && !await fileExists(definitionPath)) {
|
|
72186
|
-
await
|
|
72268
|
+
await writeFile10(
|
|
72187
72269
|
definitionPath,
|
|
72188
72270
|
buildCreativeDefinition({
|
|
72189
72271
|
title: args8.title ? String(args8.title) : titleFromSlug(slug ?? ""),
|
|
@@ -72239,7 +72321,7 @@ var scaffoldStaticAdCommand = defineCommand134({
|
|
|
72239
72321
|
run_estimated_credits: validation.estimatedCredits
|
|
72240
72322
|
},
|
|
72241
72323
|
checklist: {
|
|
72242
|
-
edit_prompt: `Edit ${
|
|
72324
|
+
edit_prompt: `Edit ${path27.basename(blueprintPath2)} \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.`,
|
|
72243
72325
|
winning_mechanisms: report.winning_mechanisms,
|
|
72244
72326
|
rebuild_the_mechanism: "`winning_mechanisms` is WHY the source ad works. Rewrite each one for our brand in prompt.json (a rhyme stays a rhyme in our words, a before/after stays a before/after of our product); never copy the source's literal copy.",
|
|
72245
72327
|
assets_to_supply: report.elements,
|
|
@@ -72301,8 +72383,8 @@ function clientLogoMissNote(decision, domain) {
|
|
|
72301
72383
|
}
|
|
72302
72384
|
|
|
72303
72385
|
// src/commands/canvas/scaffold-ad.ts
|
|
72304
|
-
import { copyFile, cp, mkdir as
|
|
72305
|
-
import
|
|
72386
|
+
import { copyFile, cp, mkdir as mkdir8, readFile as readFile23, stat as stat5, writeFile as writeFile12 } from "fs/promises";
|
|
72387
|
+
import path29 from "path";
|
|
72306
72388
|
import { defineCommand as defineCommand135 } from "citty";
|
|
72307
72389
|
|
|
72308
72390
|
// src/engine/scaffold/ad-spec.ts
|
|
@@ -73065,8 +73147,8 @@ function injectAdBrandOverlay(indexHtml, overlayHtml, style = {}) {
|
|
|
73065
73147
|
}
|
|
73066
73148
|
|
|
73067
73149
|
// src/commands/canvas/composition-dims.ts
|
|
73068
|
-
import { readFile as
|
|
73069
|
-
import
|
|
73150
|
+
import { readFile as readFile22, writeFile as writeFile11 } from "fs/promises";
|
|
73151
|
+
import path28 from "path";
|
|
73070
73152
|
function patchCompositionMeta(metaJson, dims) {
|
|
73071
73153
|
try {
|
|
73072
73154
|
const meta = JSON.parse(metaJson);
|
|
@@ -73081,12 +73163,12 @@ function patchCompositionHtml(html, dims) {
|
|
|
73081
73163
|
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`);
|
|
73082
73164
|
}
|
|
73083
73165
|
async function stampCompositionDims(compositionDir, dims) {
|
|
73084
|
-
const metaPath =
|
|
73085
|
-
const rawMeta = await
|
|
73086
|
-
await
|
|
73087
|
-
const htmlPath =
|
|
73088
|
-
const rawHtml = await
|
|
73089
|
-
await
|
|
73166
|
+
const metaPath = path28.join(compositionDir, "meta.json");
|
|
73167
|
+
const rawMeta = await readFile22(metaPath, "utf8");
|
|
73168
|
+
await writeFile11(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
|
|
73169
|
+
const htmlPath = path28.join(compositionDir, "index.html");
|
|
73170
|
+
const rawHtml = await readFile22(htmlPath, "utf8");
|
|
73171
|
+
await writeFile11(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
|
|
73090
73172
|
}
|
|
73091
73173
|
|
|
73092
73174
|
// src/commands/canvas/scaffold-ad.ts
|
|
@@ -73170,7 +73252,7 @@ var scaffoldAdCommand = defineCommand135({
|
|
|
73170
73252
|
const specPath = args8.spec;
|
|
73171
73253
|
let parsed;
|
|
73172
73254
|
try {
|
|
73173
|
-
parsed = JSON.parse(await
|
|
73255
|
+
parsed = JSON.parse(await readFile23(specPath, "utf-8"));
|
|
73174
73256
|
} catch (e) {
|
|
73175
73257
|
writeJson({
|
|
73176
73258
|
ok: false,
|
|
@@ -73330,7 +73412,7 @@ var scaffoldAdCommand = defineCommand135({
|
|
|
73330
73412
|
] : [];
|
|
73331
73413
|
const markPath = spec.data.brand?.logo?.trim();
|
|
73332
73414
|
if (!noBrand && markPath?.toLowerCase().endsWith(".svg")) {
|
|
73333
|
-
const svg = await
|
|
73415
|
+
const svg = await readFile23(markPath, "utf-8").catch(() => null);
|
|
73334
73416
|
if (svg === null) {
|
|
73335
73417
|
writeJson({
|
|
73336
73418
|
ok: false,
|
|
@@ -73382,8 +73464,8 @@ var scaffoldAdCommand = defineCommand135({
|
|
|
73382
73464
|
videoModel: chosenVideoModel,
|
|
73383
73465
|
...avatarVoice ? { avatarVoice } : {}
|
|
73384
73466
|
});
|
|
73385
|
-
const slug = args8.slug ??
|
|
73386
|
-
const outPath = args8.out ?? (args8.slug ?
|
|
73467
|
+
const slug = args8.slug ?? path29.basename(specPath).replace(/\.[^.]+$/, "");
|
|
73468
|
+
const outPath = args8.out ?? (args8.slug ? path29.join("src/creatives", slug, `${slug}.canvas.json`) : path29.join(path29.dirname(specPath), `${slug}.canvas.json`));
|
|
73387
73469
|
let outAr;
|
|
73388
73470
|
try {
|
|
73389
73471
|
outAr = resolveAspect(void 0, spec.data.format.aspect_ratio, genAspectsFor(chosenVideoModel)).outAr;
|
|
@@ -73400,28 +73482,28 @@ var scaffoldAdCommand = defineCommand135({
|
|
|
73400
73482
|
return;
|
|
73401
73483
|
}
|
|
73402
73484
|
const outDims = canvasDims(outAr);
|
|
73403
|
-
const outDir =
|
|
73404
|
-
await
|
|
73405
|
-
const compositionDest =
|
|
73406
|
-
const captionsDest =
|
|
73485
|
+
const outDir = path29.dirname(outPath);
|
|
73486
|
+
await mkdir8(outDir, { recursive: true });
|
|
73487
|
+
const compositionDest = path29.join(outDir, "video-overlay-composition");
|
|
73488
|
+
const captionsDest = path29.join(outDir, "tiktok-captions-composition");
|
|
73407
73489
|
await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
|
|
73408
73490
|
await cp(SHIPPED_CAPTIONS_DIR, captionsDest, { recursive: true });
|
|
73409
73491
|
await stampCompositionDims(compositionDest, outDims);
|
|
73410
73492
|
await stampCompositionDims(captionsDest, outDims);
|
|
73411
|
-
const captionsIndex =
|
|
73412
|
-
await
|
|
73493
|
+
const captionsIndex = path29.join(captionsDest, "index.html");
|
|
73494
|
+
await writeFile12(captionsIndex, injectAdCaptionPlacement(await readFile23(captionsIndex, "utf-8"), outAr), "utf-8");
|
|
73413
73495
|
const face = await stageBrandDisplayFace(outDir, captionsDest);
|
|
73414
73496
|
if (face) {
|
|
73415
|
-
await
|
|
73497
|
+
await writeFile12(captionsIndex, injectAdDisplayFace(await readFile23(captionsIndex, "utf-8"), face.file), "utf-8");
|
|
73416
73498
|
}
|
|
73417
|
-
const blueprintPath2 =
|
|
73418
|
-
const blueprintStylePath =
|
|
73499
|
+
const blueprintPath2 = path29.join(outDir, "prompt.json");
|
|
73500
|
+
const blueprintStylePath = path29.join(outDir, "prompt.style.json");
|
|
73419
73501
|
await writeSceneFiles(outDir, blueprint);
|
|
73420
|
-
await
|
|
73502
|
+
await writeFile12(blueprintStylePath, renderStyleProjectionFromValue(blueprint), "utf8");
|
|
73421
73503
|
const logoPath = noBrand ? void 0 : spec.data.brand?.logo?.trim();
|
|
73422
|
-
const transcriptPath =
|
|
73504
|
+
const transcriptPath = path29.join(outDir, "transcript.json");
|
|
73423
73505
|
if (spec.data.voiceover === false) {
|
|
73424
|
-
await
|
|
73506
|
+
await writeFile12(transcriptPath, `${JSON.stringify(staticTranscript(spec.data, chosenVideoModel), null, 2)}
|
|
73425
73507
|
`, "utf-8");
|
|
73426
73508
|
}
|
|
73427
73509
|
const opts = {
|
|
@@ -73430,10 +73512,10 @@ var scaffoldAdCommand = defineCommand135({
|
|
|
73430
73512
|
// generator hands every frame a look its own model then reinterprets.
|
|
73431
73513
|
sheetModel: AD_IMAGE_MODEL,
|
|
73432
73514
|
videoModel: chosenVideoModel,
|
|
73433
|
-
overlayCompositionPath:
|
|
73434
|
-
captionsCompositionPath:
|
|
73435
|
-
blueprintPath:
|
|
73436
|
-
blueprintStylePath:
|
|
73515
|
+
overlayCompositionPath: path29.relative(outDir, compositionDest),
|
|
73516
|
+
captionsCompositionPath: path29.relative(outDir, captionsDest),
|
|
73517
|
+
blueprintPath: path29.relative(outDir, blueprintPath2),
|
|
73518
|
+
blueprintStylePath: path29.relative(outDir, blueprintStylePath),
|
|
73437
73519
|
aspect: spec.data.format.aspect_ratio,
|
|
73438
73520
|
resolution: spec.data.format.resolution,
|
|
73439
73521
|
// A beat is one clause and one card, so the cap has to clear a whole clause.
|
|
@@ -73472,17 +73554,17 @@ var scaffoldAdCommand = defineCommand135({
|
|
|
73472
73554
|
// the stock voice this exists to avoid. It is to convert the LATER extracts onto the
|
|
73473
73555
|
// first beat of the same speaker, which keeps a voice the clip really produced.
|
|
73474
73556
|
...avatar ? { keepRenderedVoice: true } : {},
|
|
73475
|
-
...spec.data.voiceover === false ? { staticTranscriptPath:
|
|
73557
|
+
...spec.data.voiceover === false ? { staticTranscriptPath: path29.relative(outDir, transcriptPath) } : {}
|
|
73476
73558
|
};
|
|
73477
73559
|
const canvas = scaffoldVideoCanvas(blueprint, adSpecCastElements(spec.data, avatar), opts);
|
|
73478
73560
|
const renderNode5 = canvas.nodes.find((n) => n.type === "hyperframe_render");
|
|
73479
|
-
const renderedDir = renderNode5 ?
|
|
73561
|
+
const renderedDir = renderNode5 ? path29.join(outDir, String(renderNode5.params?.composition ?? "")) : null;
|
|
73480
73562
|
let staged = null;
|
|
73481
73563
|
let markIsLight = false;
|
|
73482
73564
|
if (logoPath && renderedDir) {
|
|
73483
|
-
const ext =
|
|
73565
|
+
const ext = path29.extname(logoPath) || ".png";
|
|
73484
73566
|
try {
|
|
73485
|
-
await copyFile(logoPath,
|
|
73567
|
+
await copyFile(logoPath, path29.join(renderedDir, `brand-mark${ext}`));
|
|
73486
73568
|
staged = { file: `brand-mark${ext}`, alt: spec.data.brand?.name ?? "brand" };
|
|
73487
73569
|
markIsLight = await markInkIsLight(logoPath);
|
|
73488
73570
|
} catch {
|
|
@@ -73501,17 +73583,17 @@ var scaffoldAdCommand = defineCommand135({
|
|
|
73501
73583
|
plateIsDark: plateIsDark(spec.data.brand?.palette)
|
|
73502
73584
|
}) : "";
|
|
73503
73585
|
if (overlayHtml && renderedDir) {
|
|
73504
|
-
const indexPath =
|
|
73505
|
-
const html = await
|
|
73506
|
-
await
|
|
73586
|
+
const indexPath = path29.join(renderedDir, "index.html");
|
|
73587
|
+
const html = await readFile23(indexPath, "utf-8");
|
|
73588
|
+
await writeFile12(indexPath, injectAdBrandOverlay(html, overlayHtml, { aspect: outAr, ...face ? { face: AD_DISPLAY_FAMILY } : {} }), "utf-8");
|
|
73507
73589
|
}
|
|
73508
|
-
await
|
|
73509
|
-
|
|
73590
|
+
await writeFile12(
|
|
73591
|
+
path29.join(outDir, REBUILD_FILE),
|
|
73510
73592
|
`${JSON.stringify({ elements: adSpecCastElements(spec.data, avatar), opts }, null, 2)}
|
|
73511
73593
|
`,
|
|
73512
73594
|
"utf8"
|
|
73513
73595
|
);
|
|
73514
|
-
await
|
|
73596
|
+
await writeFile12(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
73515
73597
|
`, "utf-8");
|
|
73516
73598
|
writeJson({
|
|
73517
73599
|
ok: true,
|
|
@@ -73608,10 +73690,10 @@ async function fetchClientLogo() {
|
|
|
73608
73690
|
if (!response.ok) return { ok: false, reason: clientLogoMissNote({ use: false, reason: "no_hits" }, domain) };
|
|
73609
73691
|
const bytes = Buffer.from(await response.arrayBuffer());
|
|
73610
73692
|
const extension = /\.(svg|png|jpg|jpeg|webp)(?:\?|$)/i.exec(decision.url)?.[1]?.toLowerCase() ?? "svg";
|
|
73611
|
-
const dir =
|
|
73612
|
-
await
|
|
73613
|
-
const file =
|
|
73614
|
-
await
|
|
73693
|
+
const dir = path29.join(BRAND_DIR, "logos");
|
|
73694
|
+
await mkdir8(dir, { recursive: true });
|
|
73695
|
+
const file = path29.join(dir, `brand-mark.${extension}`);
|
|
73696
|
+
await writeFile12(file, bytes);
|
|
73615
73697
|
return { ok: true, path: file };
|
|
73616
73698
|
} catch {
|
|
73617
73699
|
return { ok: false, reason: clientLogoMissNote({ use: false, reason: "no_hits" }, domain) };
|
|
@@ -73629,14 +73711,14 @@ async function stageBrandDisplayFace(outDir, compositionDir) {
|
|
|
73629
73711
|
if (!face) return null;
|
|
73630
73712
|
try {
|
|
73631
73713
|
if ("path" in face.slot) {
|
|
73632
|
-
const file2 = `brand-face${
|
|
73633
|
-
await copyFile(face.slot.path,
|
|
73634
|
-
return { file: file2, label: `the font file \`${
|
|
73714
|
+
const file2 = `brand-face${path29.extname(face.slot.path).toLowerCase()}`;
|
|
73715
|
+
await copyFile(face.slot.path, path29.join(compositionDir, file2));
|
|
73716
|
+
return { file: file2, label: `the font file \`${path29.relative(root, face.slot.path)}\`` };
|
|
73635
73717
|
}
|
|
73636
73718
|
const { buffer } = await fetchExternalBytes(face.slot.url, { maxBytes: MAX_FONT_BYTES });
|
|
73637
73719
|
const extension = /\.(woff2|woff|ttf|otf)(?:\?|$)/i.exec(face.slot.url)?.[1]?.toLowerCase() ?? "woff2";
|
|
73638
73720
|
const file = `brand-face.${extension}`;
|
|
73639
|
-
await
|
|
73721
|
+
await writeFile12(path29.join(compositionDir, file), buffer);
|
|
73640
73722
|
return { file, label: `${face.family ?? "the brand's typeface"}, loaded as a web font` };
|
|
73641
73723
|
} catch {
|
|
73642
73724
|
return null;
|
|
@@ -73644,9 +73726,9 @@ async function stageBrandDisplayFace(outDir, compositionDir) {
|
|
|
73644
73726
|
}
|
|
73645
73727
|
|
|
73646
73728
|
// src/commands/canvas/scaffold-video.ts
|
|
73647
|
-
import { access as access2, cp as cp2, mkdir as
|
|
73729
|
+
import { access as access2, cp as cp2, mkdir as mkdir9, readFile as readFile26, rm as rm8, writeFile as writeFile13 } from "fs/promises";
|
|
73648
73730
|
import { tmpdir as tmpdir3 } from "os";
|
|
73649
|
-
import
|
|
73731
|
+
import path31 from "path";
|
|
73650
73732
|
import { defineCommand as defineCommand136 } from "citty";
|
|
73651
73733
|
|
|
73652
73734
|
// src/engine/scaffold/lib/model-router.ts
|
|
@@ -73686,7 +73768,7 @@ function routeVideoModel(input) {
|
|
|
73686
73768
|
|
|
73687
73769
|
// src/engine/nodes/local/lib/sceneDetect.ts
|
|
73688
73770
|
import { execFile as execFile3 } from "child_process";
|
|
73689
|
-
import { mkdtemp as mkdtemp2, readdir as readdir10, readFile as
|
|
73771
|
+
import { mkdtemp as mkdtemp2, readdir as readdir10, readFile as readFile24, rm as rm7 } from "fs/promises";
|
|
73690
73772
|
import { tmpdir as tmpdir2 } from "os";
|
|
73691
73773
|
import { join as join3 } from "path";
|
|
73692
73774
|
import { promisify as promisify3 } from "util";
|
|
@@ -73762,9 +73844,9 @@ async function runSceneDetectOnce(filePath, threshold, minSceneLenS, timeoutMs)
|
|
|
73762
73844
|
);
|
|
73763
73845
|
const csvName = (await readdir10(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
|
|
73764
73846
|
if (!csvName) return [];
|
|
73765
|
-
return parsePySceneDetectCsvCuts(await
|
|
73847
|
+
return parsePySceneDetectCsvCuts(await readFile24(join3(outDir, csvName), "utf-8"));
|
|
73766
73848
|
} finally {
|
|
73767
|
-
await
|
|
73849
|
+
await rm7(outDir, { recursive: true, force: true });
|
|
73768
73850
|
}
|
|
73769
73851
|
}
|
|
73770
73852
|
async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
|
|
@@ -73786,8 +73868,8 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
|
|
|
73786
73868
|
}
|
|
73787
73869
|
|
|
73788
73870
|
// src/commands/canvas/gitignore.ts
|
|
73789
|
-
import { appendFile, readFile as
|
|
73790
|
-
import
|
|
73871
|
+
import { appendFile, readFile as readFile25 } from "fs/promises";
|
|
73872
|
+
import path30 from "path";
|
|
73791
73873
|
function missingGitignoreEntries(existing, entries) {
|
|
73792
73874
|
const present2 = new Set(
|
|
73793
73875
|
existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
|
|
@@ -73795,10 +73877,10 @@ function missingGitignoreEntries(existing, entries) {
|
|
|
73795
73877
|
return entries.filter((e) => !present2.has(e.trim().replace(/\/+$/, "")));
|
|
73796
73878
|
}
|
|
73797
73879
|
async function ensureGitignore(dir, entries) {
|
|
73798
|
-
const file =
|
|
73880
|
+
const file = path30.join(dir, ".gitignore");
|
|
73799
73881
|
let existing;
|
|
73800
73882
|
try {
|
|
73801
|
-
existing = await
|
|
73883
|
+
existing = await readFile25(file, "utf8");
|
|
73802
73884
|
} catch {
|
|
73803
73885
|
return;
|
|
73804
73886
|
}
|
|
@@ -73837,7 +73919,7 @@ ONE PERSON, MULTIPLE LOOKS: if a single individual plays MULTIPLE personas or wa
|
|
|
73837
73919
|
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.`;
|
|
73838
73920
|
async function loadAssetText2(ref, label) {
|
|
73839
73921
|
const r = ref;
|
|
73840
|
-
if (typeof r?.path === "string") return
|
|
73922
|
+
if (typeof r?.path === "string") return readFile26(r.path, "utf8");
|
|
73841
73923
|
if (typeof r?.url === "string") {
|
|
73842
73924
|
const res = await fetch(r.url);
|
|
73843
73925
|
if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
|
|
@@ -73856,7 +73938,7 @@ async function loadTranscriptBestEffort(ref) {
|
|
|
73856
73938
|
async function stageCaptions(outDir, transcript) {
|
|
73857
73939
|
const text2 = transcript?.trim();
|
|
73858
73940
|
if (!text2 || text2 === "[]") return {};
|
|
73859
|
-
const compositionPath =
|
|
73941
|
+
const compositionPath = path31.join(outDir, "tiktok-captions-composition");
|
|
73860
73942
|
await cp2(SHIPPED_CAPTIONS_DIR2, compositionPath, { recursive: true });
|
|
73861
73943
|
return { compositionPath };
|
|
73862
73944
|
}
|
|
@@ -73905,7 +73987,7 @@ var VIDEO_EXT_BY_MIME = {
|
|
|
73905
73987
|
"video/x-matroska": ".mkv"
|
|
73906
73988
|
};
|
|
73907
73989
|
function referenceVideoExt(url, contentType) {
|
|
73908
|
-
const fromPath =
|
|
73990
|
+
const fromPath = path31.extname(new URL(url).pathname).toLowerCase();
|
|
73909
73991
|
if (fromPath && fromPath.length <= 5) return fromPath;
|
|
73910
73992
|
const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase();
|
|
73911
73993
|
return mime && VIDEO_EXT_BY_MIME[mime] || ".mp4";
|
|
@@ -73937,7 +74019,7 @@ function framesModeReport(canvas, requested) {
|
|
|
73937
74019
|
return { frames_mode: mode, ...refused ? { frames_reuse_refused: refused } : {} };
|
|
73938
74020
|
}
|
|
73939
74021
|
async function materializeReferenceVideo(fileArg2) {
|
|
73940
|
-
if (!/^https?:\/\//i.test(fileArg2)) return
|
|
74022
|
+
if (!/^https?:\/\//i.test(fileArg2)) return path31.resolve(fileArg2);
|
|
73941
74023
|
let bytes;
|
|
73942
74024
|
let contentType;
|
|
73943
74025
|
try {
|
|
@@ -73949,11 +74031,11 @@ async function materializeReferenceVideo(fileArg2) {
|
|
|
73949
74031
|
throw new Error(`failed to download reference video: ${e instanceof Error ? e.message : String(e)}`);
|
|
73950
74032
|
}
|
|
73951
74033
|
if (bytes.length === 0) throw new Error("reference video download was empty");
|
|
73952
|
-
const dest =
|
|
74034
|
+
const dest = path31.join(
|
|
73953
74035
|
tmpdir3(),
|
|
73954
74036
|
`baker-ref-${sha256Hex(bytes).slice(0, 16)}${referenceVideoExt(fileArg2, contentType)}`
|
|
73955
74037
|
);
|
|
73956
|
-
await
|
|
74038
|
+
await writeFile13(dest, bytes);
|
|
73957
74039
|
return dest;
|
|
73958
74040
|
}
|
|
73959
74041
|
function resolveSeamDedup(raw) {
|
|
@@ -74176,11 +74258,11 @@ var scaffoldVideoCommand = defineCommand136({
|
|
|
74176
74258
|
} catch (e) {
|
|
74177
74259
|
return fail5("download", e instanceof Error ? e.message : String(e));
|
|
74178
74260
|
}
|
|
74179
|
-
const base =
|
|
74180
|
-
const outPath = args8.out ?
|
|
74181
|
-
const outDir =
|
|
74182
|
-
const blueprintPath2 =
|
|
74183
|
-
const blueprintStylePath =
|
|
74261
|
+
const base = path31.basename(videoPath, path31.extname(videoPath));
|
|
74262
|
+
const outPath = args8.out ? path31.resolve(String(args8.out)) : slug ? path31.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path31.join(path31.dirname(videoPath), `${base}.video.canvas.json`);
|
|
74263
|
+
const outDir = path31.dirname(outPath);
|
|
74264
|
+
const blueprintPath2 = path31.join(outDir, "prompt.json");
|
|
74265
|
+
const blueprintStylePath = path31.join(outDir, "prompt.style.json");
|
|
74184
74266
|
const frames = args8.frames === "reuse" ? "reuse" : "generate";
|
|
74185
74267
|
const maxScenes = args8["max-scenes"] ? Number(args8["max-scenes"]) : void 0;
|
|
74186
74268
|
if (Number.isFinite(maxScenes)) {
|
|
@@ -74215,10 +74297,10 @@ var scaffoldVideoCommand = defineCommand136({
|
|
|
74215
74297
|
process.stderr.write(`\u{1F3AC} Photoreal on-camera cast detected \u2192 generating clips on ${videoModel} (dodges Seedance's real-person filter).
|
|
74216
74298
|
`);
|
|
74217
74299
|
}
|
|
74218
|
-
await
|
|
74300
|
+
await mkdir9(outDir, { recursive: true });
|
|
74219
74301
|
const annotated = annotateBlueprintWithElements(blueprint, elements);
|
|
74220
74302
|
await writeSceneFiles(outDir, annotated);
|
|
74221
|
-
await
|
|
74303
|
+
await writeFile13(blueprintStylePath, renderStyleProjectionFromValue(annotated), "utf8");
|
|
74222
74304
|
let aspect;
|
|
74223
74305
|
try {
|
|
74224
74306
|
aspect = resolveAspect(
|
|
@@ -74236,12 +74318,12 @@ var scaffoldVideoCommand = defineCommand136({
|
|
|
74236
74318
|
`
|
|
74237
74319
|
);
|
|
74238
74320
|
}
|
|
74239
|
-
const compositionDest =
|
|
74321
|
+
const compositionDest = path31.join(outDir, "video-overlay-composition");
|
|
74240
74322
|
await cp2(SHIPPED_COMPOSITION_DIR2, compositionDest, { recursive: true });
|
|
74241
74323
|
await stampCompositionDims(compositionDest, outDims);
|
|
74242
|
-
const indexPath =
|
|
74324
|
+
const indexPath = path31.join(compositionDest, "index.html");
|
|
74243
74325
|
const overlayHtml = buildOverlayHtml(blueprint, { captionsActive: Boolean(transcript) });
|
|
74244
|
-
const indexHtml = await
|
|
74326
|
+
const indexHtml = await readFile26(indexPath, "utf8");
|
|
74245
74327
|
const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
|
|
74246
74328
|
if (injected === indexHtml && overlayHtml.trim()) {
|
|
74247
74329
|
fail5(
|
|
@@ -74249,16 +74331,16 @@ var scaffoldVideoCommand = defineCommand136({
|
|
|
74249
74331
|
`video-overlay-composition/index.html is missing the <!--OVERLAYS--> marker \u2014 cannot inject the overlay layer`
|
|
74250
74332
|
);
|
|
74251
74333
|
}
|
|
74252
|
-
await
|
|
74334
|
+
await writeFile13(indexPath, injected, "utf8");
|
|
74253
74335
|
const captions = await stageCaptions(outDir, transcript);
|
|
74254
74336
|
if (captions.compositionPath) await stampCompositionDims(captions.compositionPath, outDims);
|
|
74255
74337
|
const opts = {
|
|
74256
74338
|
imageModel,
|
|
74257
74339
|
videoModel,
|
|
74258
|
-
overlayCompositionPath:
|
|
74259
|
-
captionsCompositionPath: captions.compositionPath ?
|
|
74260
|
-
blueprintPath:
|
|
74261
|
-
blueprintStylePath:
|
|
74340
|
+
overlayCompositionPath: path31.relative(outDir, compositionDest),
|
|
74341
|
+
captionsCompositionPath: captions.compositionPath ? path31.relative(outDir, captions.compositionPath) : void 0,
|
|
74342
|
+
blueprintPath: path31.relative(outDir, blueprintPath2),
|
|
74343
|
+
blueprintStylePath: path31.relative(outDir, blueprintStylePath),
|
|
74262
74344
|
frames,
|
|
74263
74345
|
ambient: Boolean(args8.ambient),
|
|
74264
74346
|
seamDedup: resolveSeamDedup(args8["seam-dedup"]),
|
|
@@ -74274,7 +74356,7 @@ var scaffoldVideoCommand = defineCommand136({
|
|
|
74274
74356
|
return fail5("scaffold", e instanceof Error ? e.message : String(e));
|
|
74275
74357
|
}
|
|
74276
74358
|
if (captions.compositionPath && !canvas.nodes.some((n) => n.id === "captions")) {
|
|
74277
|
-
await
|
|
74359
|
+
await rm8(captions.compositionPath, { recursive: true, force: true });
|
|
74278
74360
|
}
|
|
74279
74361
|
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(canvas, outDir), defaultRegistry());
|
|
74280
74362
|
if (!validation.ok) {
|
|
@@ -74283,10 +74365,10 @@ var scaffoldVideoCommand = defineCommand136({
|
|
|
74283
74365
|
todo.blocking_validation_issues = validation.issues;
|
|
74284
74366
|
meta.todo = todo;
|
|
74285
74367
|
}
|
|
74286
|
-
await
|
|
74368
|
+
await writeFile13(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
74287
74369
|
`, "utf8");
|
|
74288
|
-
await
|
|
74289
|
-
|
|
74370
|
+
await writeFile13(
|
|
74371
|
+
path31.join(outDir, REBUILD_FILE),
|
|
74290
74372
|
`${JSON.stringify({ elements, opts }, null, 2)}
|
|
74291
74373
|
`,
|
|
74292
74374
|
"utf8"
|
|
@@ -74313,9 +74395,9 @@ var scaffoldVideoCommand = defineCommand136({
|
|
|
74313
74395
|
await ensureGitignore(process.cwd(), ["canvas/", ".context/"]);
|
|
74314
74396
|
const sourceRef = videoSourceReference(blueprint, fileArg2);
|
|
74315
74397
|
if (slug) {
|
|
74316
|
-
const definitionPath =
|
|
74398
|
+
const definitionPath = path31.join(outDir, "_definition.md");
|
|
74317
74399
|
if (!await fileExists2(definitionPath)) {
|
|
74318
|
-
await
|
|
74400
|
+
await writeFile13(
|
|
74319
74401
|
definitionPath,
|
|
74320
74402
|
buildCreativeDefinition({
|
|
74321
74403
|
title: titleFromSlug(slug),
|
|
@@ -74369,7 +74451,7 @@ var scaffoldVideoCommand = defineCommand136({
|
|
|
74369
74451
|
graph: canvas.metadata?.video?.graph_stats
|
|
74370
74452
|
},
|
|
74371
74453
|
checklist: {
|
|
74372
|
-
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 \`${
|
|
74454
|
+
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 \`${path31.basename(blueprintPath2)}\`. \`baker canvas validate\`/\`run\` re-assemble the blueprint and re-flow every edited scene back into the render (and regenerate ${path31.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 ${path31.basename(blueprintStylePath)}; both are regenerated.`,
|
|
74373
74455
|
recurring_elements_to_supply: report.elements,
|
|
74374
74456
|
voices_to_confirm: report.dialogue.map((d) => ({
|
|
74375
74457
|
scene: d.scene,
|
|
@@ -74406,8 +74488,8 @@ var scaffoldVideoCommand = defineCommand136({
|
|
|
74406
74488
|
});
|
|
74407
74489
|
|
|
74408
74490
|
// src/commands/canvas/set-prompt.ts
|
|
74409
|
-
import { readFile as
|
|
74410
|
-
import
|
|
74491
|
+
import { readFile as readFile27, writeFile as writeFile14 } from "fs/promises";
|
|
74492
|
+
import path32 from "path";
|
|
74411
74493
|
import { defineCommand as defineCommand137 } from "citty";
|
|
74412
74494
|
function setNodePrompt(canvas, nodeId, text2) {
|
|
74413
74495
|
const nodes = canvas?.nodes;
|
|
@@ -74435,17 +74517,17 @@ var setPromptCommand = defineCommand137({
|
|
|
74435
74517
|
"text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
|
|
74436
74518
|
},
|
|
74437
74519
|
async run({ args: args8 }) {
|
|
74438
|
-
const filePath =
|
|
74520
|
+
const filePath = path32.resolve(String(args8.file));
|
|
74439
74521
|
let canvas;
|
|
74440
74522
|
try {
|
|
74441
|
-
canvas = JSON.parse(await
|
|
74523
|
+
canvas = JSON.parse(await readFile27(filePath, "utf8"));
|
|
74442
74524
|
} catch (e) {
|
|
74443
74525
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "parse", message: String(e) } }, null, 2)}
|
|
74444
74526
|
`);
|
|
74445
74527
|
process.exit(2);
|
|
74446
74528
|
}
|
|
74447
74529
|
let text2;
|
|
74448
|
-
if (args8["text-file"]) text2 = await
|
|
74530
|
+
if (args8["text-file"]) text2 = await readFile27(path32.resolve(String(args8["text-file"])), "utf8");
|
|
74449
74531
|
else if (args8.text !== void 0) text2 = String(args8.text);
|
|
74450
74532
|
else {
|
|
74451
74533
|
process.stderr.write(
|
|
@@ -74466,14 +74548,14 @@ var setPromptCommand = defineCommand137({
|
|
|
74466
74548
|
process.exit(2);
|
|
74467
74549
|
return;
|
|
74468
74550
|
}
|
|
74469
|
-
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated,
|
|
74551
|
+
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path32.dirname(filePath)), defaultRegistry());
|
|
74470
74552
|
if (!validation.ok) {
|
|
74471
74553
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
|
|
74472
74554
|
`);
|
|
74473
74555
|
process.exit(2);
|
|
74474
74556
|
return;
|
|
74475
74557
|
}
|
|
74476
|
-
await
|
|
74558
|
+
await writeFile14(filePath, `${JSON.stringify(updated, null, 2)}
|
|
74477
74559
|
`, "utf8");
|
|
74478
74560
|
process.stdout.write(`${JSON.stringify({ ok: true, node: String(args8.node), bytes: text2.length }, null, 2)}
|
|
74479
74561
|
`);
|
|
@@ -74481,8 +74563,8 @@ var setPromptCommand = defineCommand137({
|
|
|
74481
74563
|
});
|
|
74482
74564
|
|
|
74483
74565
|
// src/commands/canvas/validate.ts
|
|
74484
|
-
import { readFile as
|
|
74485
|
-
import
|
|
74566
|
+
import { readFile as readFile28 } from "fs/promises";
|
|
74567
|
+
import path33 from "path";
|
|
74486
74568
|
import { defineCommand as defineCommand138 } from "citty";
|
|
74487
74569
|
var validateCommand = defineCommand138({
|
|
74488
74570
|
meta: {
|
|
@@ -74491,8 +74573,8 @@ var validateCommand = defineCommand138({
|
|
|
74491
74573
|
},
|
|
74492
74574
|
args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
|
|
74493
74575
|
async run({ args: args8 }) {
|
|
74494
|
-
const filePath =
|
|
74495
|
-
const raw = await
|
|
74576
|
+
const filePath = path33.resolve(String(args8.file));
|
|
74577
|
+
const raw = await readFile28(filePath, "utf8");
|
|
74496
74578
|
let parsed;
|
|
74497
74579
|
try {
|
|
74498
74580
|
parsed = JSON.parse(raw);
|
|
@@ -74504,7 +74586,7 @@ var validateCommand = defineCommand138({
|
|
|
74504
74586
|
}
|
|
74505
74587
|
const healed = await healCanvasPaths(filePath, parsed);
|
|
74506
74588
|
parsed = healed.canvas;
|
|
74507
|
-
parsed = resolveRelativeCanvasPaths(parsed,
|
|
74589
|
+
parsed = resolveRelativeCanvasPaths(parsed, path33.dirname(filePath));
|
|
74508
74590
|
let styleProjection = "not_applicable";
|
|
74509
74591
|
try {
|
|
74510
74592
|
styleProjection = await syncStyleProjection(parsed, (line) => process.stderr.write(`${line}
|
|
@@ -75022,7 +75104,9 @@ var feedbackRecordCommand = defineCommand142({
|
|
|
75022
75104
|
);
|
|
75023
75105
|
}
|
|
75024
75106
|
try {
|
|
75025
|
-
|
|
75107
|
+
const response = await apiPost("/api/creatives/feedback/record", body);
|
|
75108
|
+
await openRenderRoundInWorkspace(args8.slug);
|
|
75109
|
+
writeJson(response);
|
|
75026
75110
|
} catch (err) {
|
|
75027
75111
|
if (err instanceof ApiError) fail6(err.code, err.message);
|
|
75028
75112
|
fail6("INTERNAL_ERROR", "Unexpected error");
|
|
@@ -75082,7 +75166,7 @@ var feedbackCommand = defineCommand143({
|
|
|
75082
75166
|
|
|
75083
75167
|
// src/commands/creatives/performance.ts
|
|
75084
75168
|
import { readFileSync as readFileSync17 } from "fs";
|
|
75085
|
-
import
|
|
75169
|
+
import path34 from "path";
|
|
75086
75170
|
import { defineCommand as defineCommand144 } from "citty";
|
|
75087
75171
|
import { parse as parseYaml } from "yaml";
|
|
75088
75172
|
var DESCRIPTION4 = `How each Baker creative is doing in the live Meta ads that show it: spend, impressions, CTR, CPC, results, cost per result, frequency and a fatigue verdict, next to the angle, hook, mechanism and dominant slot its definition set out to test. Video creatives add hookRate (percent of impressions that watched 3 seconds) and holdRate (percent of those that reached a ThruPlay). Use it before briefing new creatives, to learn which angle and mechanism to make next and which creatives to replace.
|
|
@@ -75135,7 +75219,7 @@ function definitionInsights(markdown) {
|
|
|
75135
75219
|
}
|
|
75136
75220
|
function readLocalDefinition(slug) {
|
|
75137
75221
|
try {
|
|
75138
|
-
return readFileSync17(
|
|
75222
|
+
return readFileSync17(path34.join(process.cwd(), "src", "creatives", slug, "_definition.md"), "utf8");
|
|
75139
75223
|
} catch {
|
|
75140
75224
|
return null;
|
|
75141
75225
|
}
|
|
@@ -75210,7 +75294,7 @@ var performanceCommand = defineCommand144({
|
|
|
75210
75294
|
import { defineCommand as defineCommand145 } from "citty";
|
|
75211
75295
|
|
|
75212
75296
|
// src/commands/images/api.ts
|
|
75213
|
-
import { readFile as
|
|
75297
|
+
import { readFile as readFile29 } from "fs/promises";
|
|
75214
75298
|
import { basename, extname } from "path";
|
|
75215
75299
|
var imageProcessingTimeoutMs = 18e4;
|
|
75216
75300
|
var imageReadyPollIntervalMs = 2e3;
|
|
@@ -75224,7 +75308,7 @@ var mimeMap = {
|
|
|
75224
75308
|
".avif": "image/avif"
|
|
75225
75309
|
};
|
|
75226
75310
|
var defaultImageApiDeps = {
|
|
75227
|
-
readFile:
|
|
75311
|
+
readFile: readFile29,
|
|
75228
75312
|
post: apiPost,
|
|
75229
75313
|
get: apiGet,
|
|
75230
75314
|
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
|
|
@@ -75279,7 +75363,7 @@ async function waitForReadyImage(deps, imageId, opts = {}) {
|
|
|
75279
75363
|
}
|
|
75280
75364
|
|
|
75281
75365
|
// src/commands/videos/api.ts
|
|
75282
|
-
import { readFile as
|
|
75366
|
+
import { readFile as readFile30 } from "fs/promises";
|
|
75283
75367
|
import { basename as basename2, extname as extname2 } from "path";
|
|
75284
75368
|
var videoProcessingTimeoutMs = 18e4;
|
|
75285
75369
|
var videoReadyPollIntervalMs = 3e3;
|
|
@@ -75291,7 +75375,7 @@ var mimeMap2 = {
|
|
|
75291
75375
|
".mkv": "video/x-matroska"
|
|
75292
75376
|
};
|
|
75293
75377
|
var defaultVideoApiDeps = {
|
|
75294
|
-
readFile:
|
|
75378
|
+
readFile: readFile30,
|
|
75295
75379
|
post: apiPost,
|
|
75296
75380
|
get: apiGet,
|
|
75297
75381
|
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
|
|
@@ -75559,7 +75643,9 @@ var selectCommand = defineCommand146({
|
|
|
75559
75643
|
fail7("VALIDATION_ERROR", `--${String(issue?.path[0] ?? "args")}: ${issue?.message ?? "invalid"}`);
|
|
75560
75644
|
}
|
|
75561
75645
|
try {
|
|
75562
|
-
|
|
75646
|
+
const response = await apiPost("/api/creatives/select", parsed.data);
|
|
75647
|
+
await openRenderRoundInWorkspace(args8.slug);
|
|
75648
|
+
writeJson(response);
|
|
75563
75649
|
} catch (err) {
|
|
75564
75650
|
if (err instanceof ApiError) fail7(err.code, err.message);
|
|
75565
75651
|
fail7("INTERNAL_ERROR", "Unexpected error");
|
|
@@ -75604,12 +75690,12 @@ Full guide: __tooling__/docs/tools/baker/creatives.md`
|
|
|
75604
75690
|
import { defineCommand as defineCommand148 } from "citty";
|
|
75605
75691
|
|
|
75606
75692
|
// src/commands/experiment/composition.ts
|
|
75607
|
-
import { readdir as readdir12, readFile as
|
|
75608
|
-
import
|
|
75693
|
+
import { readdir as readdir12, readFile as readFile32 } from "fs/promises";
|
|
75694
|
+
import path36 from "path";
|
|
75609
75695
|
|
|
75610
75696
|
// src/commands/landing/import-graph.ts
|
|
75611
|
-
import { readdir as readdir11, readFile as
|
|
75612
|
-
import
|
|
75697
|
+
import { readdir as readdir11, readFile as readFile31, stat as stat6 } from "fs/promises";
|
|
75698
|
+
import path35 from "path";
|
|
75613
75699
|
var SOURCE_EXT = /\.(astro|ts|tsx|js|jsx|mjs|cjs|vue|svelte)$/;
|
|
75614
75700
|
var RESOLVE_CANDIDATES = [
|
|
75615
75701
|
"",
|
|
@@ -75642,7 +75728,7 @@ async function walkFiles(dir) {
|
|
|
75642
75728
|
}
|
|
75643
75729
|
const out = [];
|
|
75644
75730
|
for (const entry of entries) {
|
|
75645
|
-
const full =
|
|
75731
|
+
const full = path35.join(dir, entry.name);
|
|
75646
75732
|
if (entry.isDirectory()) {
|
|
75647
75733
|
if (entry.name === "node_modules" || entry.name === "dist") continue;
|
|
75648
75734
|
out.push(...await walkFiles(full));
|
|
@@ -75656,8 +75742,8 @@ async function resolveSpecifier(specifier, fromFile, root) {
|
|
|
75656
75742
|
const bare = specifier.split("?")[0] ?? "";
|
|
75657
75743
|
if (bare.includes("*")) return null;
|
|
75658
75744
|
let base;
|
|
75659
|
-
if (bare.startsWith("/")) base =
|
|
75660
|
-
else if (bare.startsWith(".")) base =
|
|
75745
|
+
if (bare.startsWith("/")) base = path35.join(root, bare.slice(1));
|
|
75746
|
+
else if (bare.startsWith(".")) base = path35.resolve(path35.dirname(fromFile), bare);
|
|
75661
75747
|
else return null;
|
|
75662
75748
|
for (const suffix of RESOLVE_CANDIDATES) {
|
|
75663
75749
|
const candidate = base + suffix;
|
|
@@ -75679,13 +75765,13 @@ async function importedFiles(text2, file, root) {
|
|
|
75679
75765
|
}
|
|
75680
75766
|
async function landingGraphFiles(root, slug) {
|
|
75681
75767
|
const seen = /* @__PURE__ */ new Set();
|
|
75682
|
-
const queue = await walkFiles(
|
|
75768
|
+
const queue = await walkFiles(path35.join(root, "src/pages", slug));
|
|
75683
75769
|
while (queue.length > 0) {
|
|
75684
75770
|
const file = queue.pop();
|
|
75685
75771
|
if (file === void 0 || seen.has(file)) continue;
|
|
75686
75772
|
let text2;
|
|
75687
75773
|
try {
|
|
75688
|
-
text2 = await
|
|
75774
|
+
text2 = await readFile31(file, "utf8");
|
|
75689
75775
|
} catch {
|
|
75690
75776
|
continue;
|
|
75691
75777
|
}
|
|
@@ -75694,7 +75780,7 @@ async function landingGraphFiles(root, slug) {
|
|
|
75694
75780
|
if (!seen.has(target2)) queue.push(target2);
|
|
75695
75781
|
}
|
|
75696
75782
|
}
|
|
75697
|
-
return new Set([...seen].map((abs) =>
|
|
75783
|
+
return new Set([...seen].map((abs) => path35.relative(root, abs).split(path35.sep).join("/")));
|
|
75698
75784
|
}
|
|
75699
75785
|
|
|
75700
75786
|
// src/commands/experiment/composition.ts
|
|
@@ -75739,7 +75825,7 @@ function formsOnlyIn(these, those) {
|
|
|
75739
75825
|
}
|
|
75740
75826
|
async function knownFlows(root) {
|
|
75741
75827
|
try {
|
|
75742
|
-
const entries = await readdir12(
|
|
75828
|
+
const entries = await readdir12(path36.join(root, "src/lib/flow-engine/flows"), { withFileTypes: true });
|
|
75743
75829
|
return new Set(entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name));
|
|
75744
75830
|
} catch {
|
|
75745
75831
|
return /* @__PURE__ */ new Set();
|
|
@@ -75750,7 +75836,7 @@ async function readForms(root, slug) {
|
|
|
75750
75836
|
const [files, flows] = await Promise.all([landingGraphFiles(root, slug), knownFlows(root)]);
|
|
75751
75837
|
if (flows.size === 0) return [];
|
|
75752
75838
|
const texts = await Promise.all(
|
|
75753
|
-
[...files].filter((file) => /\.(astro|ts|tsx|js|mjs)$/.test(file)).map((file) =>
|
|
75839
|
+
[...files].filter((file) => /\.(astro|ts|tsx|js|mjs)$/.test(file)).map((file) => readFile32(path36.join(root, file), "utf8"))
|
|
75754
75840
|
);
|
|
75755
75841
|
return formsReferenced(texts, flows);
|
|
75756
75842
|
} catch {
|
|
@@ -75760,8 +75846,8 @@ async function readForms(root, slug) {
|
|
|
75760
75846
|
|
|
75761
75847
|
// src/commands/experiment/fold.ts
|
|
75762
75848
|
import { statSync } from "fs";
|
|
75763
|
-
import { cp as cp3, mkdir as
|
|
75764
|
-
import
|
|
75849
|
+
import { cp as cp3, mkdir as mkdir10, readdir as readdir13, readFile as readFile33, rm as rm9, stat as stat7, writeFile as writeFile15 } from "fs/promises";
|
|
75850
|
+
import path38 from "path";
|
|
75765
75851
|
|
|
75766
75852
|
// ../api/src/experiments/variants.ts
|
|
75767
75853
|
var VARIANT_SEPARATOR = "--";
|
|
@@ -75786,21 +75872,21 @@ function nextVariantNumber(page, existingSlugs, archived) {
|
|
|
75786
75872
|
}
|
|
75787
75873
|
|
|
75788
75874
|
// src/commands/landing/repoint.ts
|
|
75789
|
-
import
|
|
75875
|
+
import path37 from "path";
|
|
75790
75876
|
var QUOTED_RELATIVE = /(['"])(\.\.?\/[^'"]*)\1/g;
|
|
75791
75877
|
function isInside3(abs, dir) {
|
|
75792
|
-
return abs === dir || abs.startsWith(dir +
|
|
75878
|
+
return abs === dir || abs.startsWith(dir + path37.sep);
|
|
75793
75879
|
}
|
|
75794
75880
|
function toSpecifier(from, to) {
|
|
75795
|
-
const rel =
|
|
75881
|
+
const rel = path37.relative(from, to).split(path37.sep).join("/");
|
|
75796
75882
|
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
75797
75883
|
}
|
|
75798
75884
|
function repointSpecifier(spec, opts) {
|
|
75799
75885
|
if (!spec.startsWith(".")) return spec;
|
|
75800
|
-
const abs =
|
|
75886
|
+
const abs = path37.resolve(opts.fromDir, spec);
|
|
75801
75887
|
if (opts.forkedTargets?.has(abs)) {
|
|
75802
|
-
const withinControl =
|
|
75803
|
-
return toSpecifier(opts.toDir,
|
|
75888
|
+
const withinControl = path37.relative(opts.controlDir, abs);
|
|
75889
|
+
return toSpecifier(opts.toDir, path37.resolve(opts.variantDir, withinControl));
|
|
75804
75890
|
}
|
|
75805
75891
|
if (!isInside3(abs, opts.controlDir)) return spec;
|
|
75806
75892
|
return toSpecifier(opts.toDir, abs);
|
|
@@ -75812,9 +75898,9 @@ function repointFile(text2, opts) {
|
|
|
75812
75898
|
}
|
|
75813
75899
|
function repointMoved(text2, opts) {
|
|
75814
75900
|
return text2.replace(QUOTED_RELATIVE, (_match, quote, spec) => {
|
|
75815
|
-
const abs =
|
|
75816
|
-
const within =
|
|
75817
|
-
const target2 = within.startsWith("..") ||
|
|
75901
|
+
const abs = path37.resolve(opts.fromDir, spec);
|
|
75902
|
+
const within = path37.relative(opts.movedFrom, abs);
|
|
75903
|
+
const target2 = within.startsWith("..") || path37.isAbsolute(within) ? abs : path37.resolve(opts.movedTo, within);
|
|
75818
75904
|
return `${quote}${toSpecifier(opts.toDir, target2)}${quote}`;
|
|
75819
75905
|
});
|
|
75820
75906
|
}
|
|
@@ -75823,7 +75909,7 @@ function repointMoved(text2, opts) {
|
|
|
75823
75909
|
var ARCHIVE_ROOT = "src/_variants";
|
|
75824
75910
|
var QUOTED_RELATIVE2 = /(['"])(\.\.?\/[^'"]*)\1/g;
|
|
75825
75911
|
function posix2(p) {
|
|
75826
|
-
return p.split(
|
|
75912
|
+
return p.split(path38.sep).join("/");
|
|
75827
75913
|
}
|
|
75828
75914
|
async function isDir(p) {
|
|
75829
75915
|
try {
|
|
@@ -75842,39 +75928,39 @@ async function filesUnder(dir, prefix = "") {
|
|
|
75842
75928
|
const out = [];
|
|
75843
75929
|
for (const entry of entries) {
|
|
75844
75930
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
75845
|
-
if (entry.isDirectory()) out.push(...await filesUnder(
|
|
75931
|
+
if (entry.isDirectory()) out.push(...await filesUnder(path38.join(dir, entry.name), rel));
|
|
75846
75932
|
else out.push(rel);
|
|
75847
75933
|
}
|
|
75848
75934
|
return out.sort();
|
|
75849
75935
|
}
|
|
75850
75936
|
var TEXT_EXT = /\.(astro|ts|tsx|js|jsx|mjs|cjs|vue|svelte|md|json|css)$/;
|
|
75851
75937
|
function archiveRelPath(abs, base) {
|
|
75852
|
-
if (!abs.startsWith(base +
|
|
75853
|
-
return posix2(
|
|
75938
|
+
if (!abs.startsWith(base + path38.sep)) return null;
|
|
75939
|
+
return posix2(path38.relative(base, abs));
|
|
75854
75940
|
}
|
|
75855
75941
|
async function archiveArm(opts) {
|
|
75856
75942
|
const { root, armDir, pageDir, archiveDir, armSlug } = opts;
|
|
75857
75943
|
const targets = await archiveContents({ root, armDir, pageDir, armSlug });
|
|
75858
|
-
const archivedBySource = new Map([...targets].map(([rel, abs]) => [abs,
|
|
75944
|
+
const archivedBySource = new Map([...targets].map(([rel, abs]) => [abs, path38.join(archiveDir, rel)]));
|
|
75859
75945
|
const written = [];
|
|
75860
75946
|
for (const [rel, abs] of [...targets].sort()) {
|
|
75861
|
-
const dest =
|
|
75862
|
-
await
|
|
75947
|
+
const dest = path38.join(archiveDir, rel);
|
|
75948
|
+
await mkdir10(path38.dirname(dest), { recursive: true });
|
|
75863
75949
|
if (!TEXT_EXT.test(rel)) {
|
|
75864
75950
|
await cp3(abs, dest);
|
|
75865
75951
|
} else {
|
|
75866
|
-
const text2 = await
|
|
75867
|
-
await
|
|
75952
|
+
const text2 = await readFile33(abs, "utf8");
|
|
75953
|
+
await writeFile15(dest, repointIntoArchive(text2, abs, dest, archivedBySource), "utf8");
|
|
75868
75954
|
}
|
|
75869
|
-
written.push(posix2(
|
|
75955
|
+
written.push(posix2(path38.relative(root, dest)));
|
|
75870
75956
|
}
|
|
75871
75957
|
return written;
|
|
75872
75958
|
}
|
|
75873
75959
|
async function archiveContents(opts) {
|
|
75874
75960
|
const { root, armDir, pageDir, armSlug } = opts;
|
|
75875
75961
|
const graph = await landingGraphFiles(root, armSlug);
|
|
75876
|
-
const reachable = new Set([...graph].map((rel) =>
|
|
75877
|
-
for (const rel of await filesUnder(armDir)) reachable.add(
|
|
75962
|
+
const reachable = new Set([...graph].map((rel) => path38.resolve(root, rel)));
|
|
75963
|
+
for (const rel of await filesUnder(armDir)) reachable.add(path38.join(armDir, rel));
|
|
75878
75964
|
const sorted = [...reachable].sort();
|
|
75879
75965
|
const targets = /* @__PURE__ */ new Map();
|
|
75880
75966
|
for (const base of [pageDir, armDir]) {
|
|
@@ -75887,14 +75973,14 @@ async function archiveContents(opts) {
|
|
|
75887
75973
|
return targets;
|
|
75888
75974
|
}
|
|
75889
75975
|
function repointIntoArchive(text2, sourceAbs, destAbs, archivedBySource) {
|
|
75890
|
-
const fromDir =
|
|
75891
|
-
const toDir =
|
|
75976
|
+
const fromDir = path38.dirname(sourceAbs);
|
|
75977
|
+
const toDir = path38.dirname(destAbs);
|
|
75892
75978
|
return text2.replace(QUOTED_RELATIVE2, (match, quote, spec, offset) => {
|
|
75893
75979
|
const [specPath, suffix] = splitSuffix(spec);
|
|
75894
|
-
const target2 =
|
|
75980
|
+
const target2 = path38.resolve(fromDir, specPath);
|
|
75895
75981
|
const resolved = resolveInArchive(target2, archivedBySource) ?? resolveOnDisk(target2, isImportPosition(text2, offset));
|
|
75896
75982
|
if (resolved === null) return match;
|
|
75897
|
-
const rel = posix2(
|
|
75983
|
+
const rel = posix2(path38.relative(toDir, resolved));
|
|
75898
75984
|
return `${quote}${rel.startsWith(".") ? rel : `./${rel}`}${suffix}${quote}`;
|
|
75899
75985
|
});
|
|
75900
75986
|
}
|
|
@@ -75928,25 +76014,25 @@ async function promoteVariantFiles(opts) {
|
|
|
75928
76014
|
const written = [];
|
|
75929
76015
|
for (const rel of await filesUnder(variantDir)) {
|
|
75930
76016
|
if (rel === "_definition.md") continue;
|
|
75931
|
-
const from =
|
|
75932
|
-
const to =
|
|
75933
|
-
await
|
|
76017
|
+
const from = path38.join(variantDir, rel);
|
|
76018
|
+
const to = path38.join(pageDir, rel);
|
|
76019
|
+
await mkdir10(path38.dirname(to), { recursive: true });
|
|
75934
76020
|
if (!TEXT_EXT.test(rel)) {
|
|
75935
76021
|
await cp3(from, to);
|
|
75936
76022
|
} else {
|
|
75937
|
-
const text2 = await
|
|
75938
|
-
await
|
|
76023
|
+
const text2 = await readFile33(from, "utf8");
|
|
76024
|
+
await writeFile15(
|
|
75939
76025
|
to,
|
|
75940
76026
|
repointMoved(text2, {
|
|
75941
|
-
fromDir:
|
|
75942
|
-
toDir:
|
|
76027
|
+
fromDir: path38.dirname(from),
|
|
76028
|
+
toDir: path38.dirname(to),
|
|
75943
76029
|
movedFrom: variantDir,
|
|
75944
76030
|
movedTo: pageDir
|
|
75945
76031
|
}),
|
|
75946
76032
|
"utf8"
|
|
75947
76033
|
);
|
|
75948
76034
|
}
|
|
75949
|
-
written.push(posix2(
|
|
76035
|
+
written.push(posix2(path38.relative(root, to)));
|
|
75950
76036
|
}
|
|
75951
76037
|
return written;
|
|
75952
76038
|
}
|
|
@@ -75970,8 +76056,8 @@ function archiveRecord(target2, variantNumber) {
|
|
|
75970
76056
|
`;
|
|
75971
76057
|
}
|
|
75972
76058
|
async function foldExperiment(root, target2) {
|
|
75973
|
-
const pageDir =
|
|
75974
|
-
const variantDir =
|
|
76059
|
+
const pageDir = path38.resolve(root, "src/pages", target2.landingSlug);
|
|
76060
|
+
const variantDir = path38.resolve(root, "src/pages", target2.variantSlug);
|
|
75975
76061
|
const variantNumber = variantNumberOf(target2.variantSlug);
|
|
75976
76062
|
if (variantNumber === null) {
|
|
75977
76063
|
return {
|
|
@@ -75985,9 +76071,9 @@ async function foldExperiment(root, target2) {
|
|
|
75985
76071
|
return { error: `src/pages/${target2.variantSlug}/ is already gone \u2014 this test looks folded in already` };
|
|
75986
76072
|
}
|
|
75987
76073
|
const archiveKey = target2.landingInternalId ?? target2.landingSlug;
|
|
75988
|
-
const archiveDir =
|
|
75989
|
-
await
|
|
75990
|
-
await
|
|
76074
|
+
const archiveDir = path38.resolve(root, ARCHIVE_ROOT, archiveKey, String(variantNumber));
|
|
76075
|
+
await rm9(archiveDir, { recursive: true, force: true });
|
|
76076
|
+
await mkdir10(archiveDir, { recursive: true });
|
|
75991
76077
|
const written = await archiveArm({
|
|
75992
76078
|
root,
|
|
75993
76079
|
armDir: target2.survivor === "variant" ? pageDir : variantDir,
|
|
@@ -75995,16 +76081,16 @@ async function foldExperiment(root, target2) {
|
|
|
75995
76081
|
archiveDir,
|
|
75996
76082
|
armSlug: target2.survivor === "variant" ? target2.landingSlug : target2.variantSlug
|
|
75997
76083
|
});
|
|
75998
|
-
await
|
|
75999
|
-
written.push(posix2(
|
|
76084
|
+
await writeFile15(path38.join(archiveDir, "_variant.json"), archiveRecord(target2, variantNumber), "utf8");
|
|
76085
|
+
written.push(posix2(path38.join(ARCHIVE_ROOT, archiveKey, String(variantNumber), "_variant.json")));
|
|
76000
76086
|
if (target2.survivor === "variant") {
|
|
76001
76087
|
written.push(...await promoteVariantFiles({ root, pageDir, variantDir }));
|
|
76002
76088
|
}
|
|
76003
|
-
await
|
|
76089
|
+
await rm9(variantDir, { recursive: true, force: true });
|
|
76004
76090
|
return {
|
|
76005
76091
|
experimentId: target2.experimentId,
|
|
76006
76092
|
page: target2.landingSlug,
|
|
76007
|
-
archivedTo: posix2(
|
|
76093
|
+
archivedTo: posix2(path38.join(ARCHIVE_ROOT, archiveKey, String(variantNumber))),
|
|
76008
76094
|
written,
|
|
76009
76095
|
removed: [`src/pages/${target2.variantSlug}`]
|
|
76010
76096
|
};
|
|
@@ -76776,11 +76862,11 @@ function parseEvidence(raw, rawArgs) {
|
|
|
76776
76862
|
}
|
|
76777
76863
|
|
|
76778
76864
|
// src/commands/experiment/identity.ts
|
|
76779
|
-
import { readFile as
|
|
76780
|
-
import
|
|
76865
|
+
import { readFile as readFile34 } from "fs/promises";
|
|
76866
|
+
import path39 from "path";
|
|
76781
76867
|
async function readPageInternalId(root, slug) {
|
|
76782
76868
|
try {
|
|
76783
|
-
const text2 = await
|
|
76869
|
+
const text2 = await readFile34(path39.resolve(root, "src", "pages", slug, "_definition.md"), "utf8");
|
|
76784
76870
|
return /^internalId:\s*"?([^"\s]+)"?\s*$/m.exec(text2)?.[1];
|
|
76785
76871
|
} catch {
|
|
76786
76872
|
return void 0;
|
|
@@ -76788,8 +76874,8 @@ async function readPageInternalId(root, slug) {
|
|
|
76788
76874
|
}
|
|
76789
76875
|
|
|
76790
76876
|
// src/commands/experiment/measurement.ts
|
|
76791
|
-
import { readFile as
|
|
76792
|
-
import
|
|
76877
|
+
import { readFile as readFile35 } from "fs/promises";
|
|
76878
|
+
import path40 from "path";
|
|
76793
76879
|
function eventDeclarationsIn(source) {
|
|
76794
76880
|
const found = [];
|
|
76795
76881
|
const pattern = /\b(data-baker-(?:view|event))\s*=\s*["']([^"']+)["']/g;
|
|
@@ -76849,7 +76935,7 @@ function sectionsOf(files, slug) {
|
|
|
76849
76935
|
return files.filter((file) => file.startsWith(prefix) && !file.slice(prefix.length).startsWith("index."));
|
|
76850
76936
|
}
|
|
76851
76937
|
async function declarationsOf(root, files) {
|
|
76852
|
-
const sources = await Promise.all(files.map((file) =>
|
|
76938
|
+
const sources = await Promise.all(files.map((file) => readFile35(path40.join(root, file), "utf8").catch(() => "")));
|
|
76853
76939
|
return sources.flatMap(eventDeclarationsIn);
|
|
76854
76940
|
}
|
|
76855
76941
|
|
|
@@ -77901,10 +77987,10 @@ function parseValueExpression(raw) {
|
|
|
77901
77987
|
return parts.map(parsePart);
|
|
77902
77988
|
}
|
|
77903
77989
|
function trackingFieldIds() {
|
|
77904
|
-
const
|
|
77905
|
-
if (!existsSync6(
|
|
77990
|
+
const path54 = join4(flowsDir(), "..", "tracking.ts");
|
|
77991
|
+
if (!existsSync6(path54)) return null;
|
|
77906
77992
|
try {
|
|
77907
|
-
const source = readFileSync18(
|
|
77993
|
+
const source = readFileSync18(path54, "utf-8");
|
|
77908
77994
|
const block2 = source.match(/TRACKING_FIELD_IDS\s*=\s*\[([\s\S]*?)\]\s*as const/)?.[1];
|
|
77909
77995
|
if (!block2) return null;
|
|
77910
77996
|
const ids = [...block2.matchAll(/"(tracking\.[a-z0-9_]+)"/g)].map((match) => match[1]);
|
|
@@ -78478,13 +78564,13 @@ function specsFromFile(parsed) {
|
|
|
78478
78564
|
return `${destField}${type}=${entry?.value ?? ""}`;
|
|
78479
78565
|
});
|
|
78480
78566
|
}
|
|
78481
|
-
function readSpecFile(
|
|
78567
|
+
function readSpecFile(path54) {
|
|
78482
78568
|
let raw;
|
|
78483
78569
|
try {
|
|
78484
|
-
raw =
|
|
78570
|
+
raw = path54 === "-" ? readFileSync19(0, "utf-8") : readFileSync19(path54, "utf-8");
|
|
78485
78571
|
} catch (error) {
|
|
78486
78572
|
refuse(
|
|
78487
|
-
`Could not read ${
|
|
78573
|
+
`Could not read ${path54 === "-" ? "the mapping from stdin" : `"${path54}"`}: ${error instanceof Error ? error.message : String(error)}`
|
|
78488
78574
|
);
|
|
78489
78575
|
}
|
|
78490
78576
|
let parsed;
|
|
@@ -78492,7 +78578,7 @@ function readSpecFile(path53) {
|
|
|
78492
78578
|
parsed = JSON.parse(raw);
|
|
78493
78579
|
} catch (error) {
|
|
78494
78580
|
refuse(
|
|
78495
|
-
`${
|
|
78581
|
+
`${path54 === "-" ? "stdin" : `"${path54}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
78496
78582
|
'Expected { "map": { "<destField>": "<value>", \u2026 } }'
|
|
78497
78583
|
);
|
|
78498
78584
|
}
|
|
@@ -78791,21 +78877,21 @@ var ARRAY_FIELDS = [
|
|
|
78791
78877
|
"tagIds"
|
|
78792
78878
|
];
|
|
78793
78879
|
var ARRAY_OWNERS = ["", "body"];
|
|
78794
|
-
function dropUnsetOptionals(sideEffect,
|
|
78880
|
+
function dropUnsetOptionals(sideEffect, path54) {
|
|
78795
78881
|
return OPTIONAL_STRINGS.flatMap((key) => {
|
|
78796
78882
|
if (!(key in sideEffect) || sideEffect[key] !== null && sideEffect[key] !== "") return [];
|
|
78797
78883
|
delete sideEffect[key];
|
|
78798
|
-
return [{ path:
|
|
78884
|
+
return [{ path: path54, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
|
|
78799
78885
|
});
|
|
78800
78886
|
}
|
|
78801
|
-
function fillNulledArrays(target2, prefix,
|
|
78887
|
+
function fillNulledArrays(target2, prefix, path54) {
|
|
78802
78888
|
return ARRAY_FIELDS.flatMap((key) => {
|
|
78803
78889
|
if (!(key in target2) || target2[key] !== null) return [];
|
|
78804
78890
|
target2[key] = [];
|
|
78805
|
-
return [{ path:
|
|
78891
|
+
return [{ path: path54, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
|
|
78806
78892
|
});
|
|
78807
78893
|
}
|
|
78808
|
-
function tidyEmailRecipients(sideEffect,
|
|
78894
|
+
function tidyEmailRecipients(sideEffect, path54) {
|
|
78809
78895
|
if (sideEffect.type !== "email") return [];
|
|
78810
78896
|
const config = sideEffect.encryptedConfig;
|
|
78811
78897
|
if (!config || typeof config !== "object" || Array.isArray(config)) return [];
|
|
@@ -78816,7 +78902,7 @@ function tidyEmailRecipients(sideEffect, path53) {
|
|
|
78816
78902
|
const tidied = to.join(", ");
|
|
78817
78903
|
if (tidied === raw) return [];
|
|
78818
78904
|
config.email = tidied;
|
|
78819
|
-
return [{ path:
|
|
78905
|
+
return [{ path: path54, change: "tidied the recipient list's spacing and separators" }];
|
|
78820
78906
|
}
|
|
78821
78907
|
function sideEffectsOf(node) {
|
|
78822
78908
|
if (!Array.isArray(node.sideEffects)) return [];
|
|
@@ -78825,13 +78911,13 @@ function sideEffectsOf(node) {
|
|
|
78825
78911
|
);
|
|
78826
78912
|
}
|
|
78827
78913
|
function normalizeSideEffect(sideEffect, where) {
|
|
78828
|
-
const
|
|
78914
|
+
const path54 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
|
|
78829
78915
|
const arrays = ARRAY_OWNERS.flatMap((owner) => {
|
|
78830
78916
|
const target2 = owner ? sideEffect[owner] : sideEffect;
|
|
78831
78917
|
if (!target2 || typeof target2 !== "object") return [];
|
|
78832
|
-
return fillNulledArrays(target2, owner ? `${owner}.` : "",
|
|
78918
|
+
return fillNulledArrays(target2, owner ? `${owner}.` : "", path54);
|
|
78833
78919
|
});
|
|
78834
|
-
return [...dropUnsetOptionals(sideEffect,
|
|
78920
|
+
return [...dropUnsetOptionals(sideEffect, path54), ...arrays, ...tidyEmailRecipients(sideEffect, path54)];
|
|
78835
78921
|
}
|
|
78836
78922
|
function normalizeFlowTree(tree) {
|
|
78837
78923
|
const changes = [];
|
|
@@ -80223,10 +80309,10 @@ async function stageOps(ops) {
|
|
|
80223
80309
|
handleError5(err);
|
|
80224
80310
|
}
|
|
80225
80311
|
}
|
|
80226
|
-
async function draftAction2(
|
|
80312
|
+
async function draftAction2(path54, body, chat) {
|
|
80227
80313
|
const chatId = resolveChatId(chat);
|
|
80228
80314
|
try {
|
|
80229
|
-
const { data, hints: hints2 } = splitHints(await apiPost(
|
|
80315
|
+
const { data, hints: hints2 } = splitHints(await apiPost(path54, { chatId, ...body }));
|
|
80230
80316
|
writeJsonEnvelope({ ok: true, data, ...hints2.length > 0 ? { hints: hints2 } : {} });
|
|
80231
80317
|
return data;
|
|
80232
80318
|
} catch (err) {
|
|
@@ -82721,7 +82807,7 @@ function cropSprite(input, region) {
|
|
|
82721
82807
|
|
|
82722
82808
|
// src/lib/image/io.ts
|
|
82723
82809
|
import { randomBytes } from "crypto";
|
|
82724
|
-
import { glob as fsGlob, readFile as
|
|
82810
|
+
import { glob as fsGlob, readFile as readFile36, rename, stat as stat8, writeFile as writeFile16 } from "fs/promises";
|
|
82725
82811
|
import { dirname as dirname2, extname as extname3, join as join5, resolve as resolve4 } from "path";
|
|
82726
82812
|
var REMOTE_RE = /^https?:\/\//i;
|
|
82727
82813
|
var GLOB_RE = /[*?[\]{}]/;
|
|
@@ -82754,11 +82840,11 @@ async function readImageBuffer(pathOrUrl) {
|
|
|
82754
82840
|
const { buffer } = await fetchExternalBytes(pathOrUrl, { maxBytes: MAX_REMOTE_IMAGE_BYTES });
|
|
82755
82841
|
return buffer;
|
|
82756
82842
|
}
|
|
82757
|
-
return
|
|
82843
|
+
return readFile36(pathOrUrl);
|
|
82758
82844
|
}
|
|
82759
|
-
async function isDirectory(
|
|
82845
|
+
async function isDirectory(path54) {
|
|
82760
82846
|
try {
|
|
82761
|
-
const s = await stat8(
|
|
82847
|
+
const s = await stat8(path54);
|
|
82762
82848
|
return s.isDirectory();
|
|
82763
82849
|
} catch {
|
|
82764
82850
|
return false;
|
|
@@ -82777,7 +82863,7 @@ async function atomicWrite(targetPath, data) {
|
|
|
82777
82863
|
const absolute = resolve4(targetPath);
|
|
82778
82864
|
const dir = dirname2(absolute);
|
|
82779
82865
|
const tmp = join5(dir, `.baker-image-${randomBytes(8).toString("hex")}.tmp`);
|
|
82780
|
-
await
|
|
82866
|
+
await writeFile16(tmp, data);
|
|
82781
82867
|
await rename(tmp, absolute);
|
|
82782
82868
|
}
|
|
82783
82869
|
|
|
@@ -83186,13 +83272,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
|
|
|
83186
83272
|
}
|
|
83187
83273
|
function disambiguate(paths) {
|
|
83188
83274
|
const taken = /* @__PURE__ */ new Set();
|
|
83189
|
-
return paths.map((
|
|
83190
|
-
if (!taken.has(
|
|
83191
|
-
taken.add(
|
|
83192
|
-
return
|
|
83275
|
+
return paths.map((path54) => {
|
|
83276
|
+
if (!taken.has(path54)) {
|
|
83277
|
+
taken.add(path54);
|
|
83278
|
+
return path54;
|
|
83193
83279
|
}
|
|
83194
|
-
const ext = extname4(
|
|
83195
|
-
const stem =
|
|
83280
|
+
const ext = extname4(path54);
|
|
83281
|
+
const stem = path54.slice(0, path54.length - ext.length);
|
|
83196
83282
|
let n = 2;
|
|
83197
83283
|
while (taken.has(`${stem}-${n}${ext}`)) n += 1;
|
|
83198
83284
|
const unique = `${stem}-${n}${ext}`;
|
|
@@ -83318,10 +83404,10 @@ async function runDownloads(plan) {
|
|
|
83318
83404
|
const paths = disambiguate(fetched.map((item) => item.path));
|
|
83319
83405
|
const downloaded = [];
|
|
83320
83406
|
for (const [index, item] of fetched.entries()) {
|
|
83321
|
-
const
|
|
83407
|
+
const path54 = paths[index] ?? item.path;
|
|
83322
83408
|
try {
|
|
83323
|
-
await atomicWrite(
|
|
83324
|
-
downloaded.push({ input: item.input, output:
|
|
83409
|
+
await atomicWrite(path54, item.buffer);
|
|
83410
|
+
downloaded.push({ input: item.input, output: path54, bytes: item.buffer.length, contentType: item.contentType });
|
|
83325
83411
|
} catch (err) {
|
|
83326
83412
|
failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
|
|
83327
83413
|
}
|
|
@@ -86303,8 +86389,8 @@ Full guide: __tooling__/docs/tools/baker/images.md`
|
|
|
86303
86389
|
import { defineCommand as defineCommand206 } from "citty";
|
|
86304
86390
|
|
|
86305
86391
|
// src/commands/landing/critique.ts
|
|
86306
|
-
import { readdir as readdir15, readFile as
|
|
86307
|
-
import
|
|
86392
|
+
import { readdir as readdir15, readFile as readFile38, stat as stat10 } from "fs/promises";
|
|
86393
|
+
import path43 from "path";
|
|
86308
86394
|
import { defineCommand as defineCommand195 } from "citty";
|
|
86309
86395
|
|
|
86310
86396
|
// src/engine/landing/lib/constants.ts
|
|
@@ -87495,27 +87581,27 @@ function describeCounts(findings) {
|
|
|
87495
87581
|
}
|
|
87496
87582
|
|
|
87497
87583
|
// src/commands/landing/snapshot.ts
|
|
87498
|
-
import { mkdir as
|
|
87499
|
-
import
|
|
87584
|
+
import { mkdir as mkdir11, rename as rename2, writeFile as writeFile17 } from "fs/promises";
|
|
87585
|
+
import path41 from "path";
|
|
87500
87586
|
var CRITIC_VERSION = "4";
|
|
87501
87587
|
function critiqueCacheDir(projectRoot) {
|
|
87502
|
-
return
|
|
87588
|
+
return path41.join(projectRoot, ".cache", "landing-critique");
|
|
87503
87589
|
}
|
|
87504
87590
|
function snapshotPath(projectRoot, slug) {
|
|
87505
|
-
return
|
|
87591
|
+
return path41.join(critiqueCacheDir(projectRoot), `${slug}.json`);
|
|
87506
87592
|
}
|
|
87507
87593
|
async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
87508
|
-
await
|
|
87594
|
+
await mkdir11(critiqueCacheDir(projectRoot), { recursive: true });
|
|
87509
87595
|
const dest = snapshotPath(projectRoot, snapshot.slug);
|
|
87510
87596
|
const tmp = `${dest}.tmp`;
|
|
87511
|
-
await
|
|
87597
|
+
await writeFile17(tmp, `${JSON.stringify(snapshot, null, 2)}
|
|
87512
87598
|
`, "utf8");
|
|
87513
87599
|
await rename2(tmp, dest);
|
|
87514
87600
|
}
|
|
87515
87601
|
|
|
87516
87602
|
// src/commands/landing/source-version.ts
|
|
87517
|
-
import { readdir as readdir14, readFile as
|
|
87518
|
-
import
|
|
87603
|
+
import { readdir as readdir14, readFile as readFile37, stat as stat9 } from "fs/promises";
|
|
87604
|
+
import path42 from "path";
|
|
87519
87605
|
var CRITIQUED_ROOTS = ["src/pages/", "src/components/"];
|
|
87520
87606
|
async function landingSourceRelPaths(root, slug) {
|
|
87521
87607
|
const files = await landingGraphFiles(root, slug);
|
|
@@ -87524,7 +87610,7 @@ async function landingSourceRelPaths(root, slug) {
|
|
|
87524
87610
|
async function readLandingSources(root, slug) {
|
|
87525
87611
|
const rel = await landingSourceRelPaths(root, slug);
|
|
87526
87612
|
const out = [];
|
|
87527
|
-
for (const r of rel) out.push({ path: r, text: await
|
|
87613
|
+
for (const r of rel) out.push({ path: r, text: await readFile37(path42.join(root, r), "utf8") });
|
|
87528
87614
|
return out;
|
|
87529
87615
|
}
|
|
87530
87616
|
async function computeLandingSourceSha(root, slug) {
|
|
@@ -87533,7 +87619,7 @@ async function computeLandingSourceSha(root, slug) {
|
|
|
87533
87619
|
for (const r of rel) {
|
|
87534
87620
|
let bytes;
|
|
87535
87621
|
try {
|
|
87536
|
-
bytes = await
|
|
87622
|
+
bytes = await readFile37(path42.join(root, r));
|
|
87537
87623
|
} catch {
|
|
87538
87624
|
bytes = Buffer.alloc(0);
|
|
87539
87625
|
}
|
|
@@ -87543,16 +87629,16 @@ async function computeLandingSourceSha(root, slug) {
|
|
|
87543
87629
|
}
|
|
87544
87630
|
async function computeLegacyLandingSourceSha(landingDir) {
|
|
87545
87631
|
const rel = [];
|
|
87546
|
-
if (await isFile2(
|
|
87547
|
-
for (const abs of await walkAstro(
|
|
87548
|
-
rel.push(
|
|
87632
|
+
if (await isFile2(path42.join(landingDir, "index.astro"))) rel.push("index.astro");
|
|
87633
|
+
for (const abs of await walkAstro(path42.join(landingDir, "_components"))) {
|
|
87634
|
+
rel.push(path42.relative(landingDir, abs).split(path42.sep).join("/"));
|
|
87549
87635
|
}
|
|
87550
87636
|
rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
87551
87637
|
const parts = [];
|
|
87552
87638
|
for (const r of rel) {
|
|
87553
87639
|
let bytes;
|
|
87554
87640
|
try {
|
|
87555
|
-
bytes = await
|
|
87641
|
+
bytes = await readFile37(path42.join(landingDir, r));
|
|
87556
87642
|
} catch {
|
|
87557
87643
|
bytes = Buffer.alloc(0);
|
|
87558
87644
|
}
|
|
@@ -87576,7 +87662,7 @@ async function walkAstro(dir) {
|
|
|
87576
87662
|
}
|
|
87577
87663
|
const out = [];
|
|
87578
87664
|
for (const entry of entries) {
|
|
87579
|
-
const abs =
|
|
87665
|
+
const abs = path42.join(dir, entry.name);
|
|
87580
87666
|
if (entry.isDirectory()) out.push(...await walkAstro(abs));
|
|
87581
87667
|
else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
|
|
87582
87668
|
}
|
|
@@ -87637,7 +87723,7 @@ var critiqueCommand2 = defineCommand195({
|
|
|
87637
87723
|
{ availableSlugs: await listLandingSlugs(projectRoot) }
|
|
87638
87724
|
);
|
|
87639
87725
|
}
|
|
87640
|
-
if (!await isDir2(
|
|
87726
|
+
if (!await isDir2(path43.resolve(projectRoot, "src", "pages", slug))) {
|
|
87641
87727
|
fail9("NOT_FOUND", `No landing at src/pages/${slug}/`, {
|
|
87642
87728
|
availableSlugs: await listLandingSlugs(projectRoot)
|
|
87643
87729
|
});
|
|
@@ -87680,7 +87766,7 @@ async function critiqueOne(projectRoot, slug, brand, competitors) {
|
|
|
87680
87766
|
const [sources, compositionSha, sourceSha] = await Promise.all([
|
|
87681
87767
|
readLandingSources(projectRoot, slug),
|
|
87682
87768
|
computeLandingSourceSha(projectRoot, slug),
|
|
87683
|
-
computeLegacyLandingSourceSha(
|
|
87769
|
+
computeLegacyLandingSourceSha(path43.resolve(projectRoot, "src", "pages", slug))
|
|
87684
87770
|
]);
|
|
87685
87771
|
const report = critiqueLanding({ slug, sources, brand, competitors });
|
|
87686
87772
|
let snapshotFailed = false;
|
|
@@ -87700,7 +87786,7 @@ async function critiqueOne(projectRoot, slug, brand, competitors) {
|
|
|
87700
87786
|
return { slug, report, snapshotFailed };
|
|
87701
87787
|
}
|
|
87702
87788
|
async function readCompetitorNames(projectRoot) {
|
|
87703
|
-
const dir =
|
|
87789
|
+
const dir = path43.join(projectRoot, "src", "content", "competitors");
|
|
87704
87790
|
let entries;
|
|
87705
87791
|
try {
|
|
87706
87792
|
entries = (await readdir15(dir)).filter((f) => f.endsWith(".md"));
|
|
@@ -87709,9 +87795,9 @@ async function readCompetitorNames(projectRoot) {
|
|
|
87709
87795
|
}
|
|
87710
87796
|
const names = [];
|
|
87711
87797
|
for (const entry of entries) {
|
|
87712
|
-
names.push(
|
|
87798
|
+
names.push(path43.basename(entry, ".md").replace(/[-_]+/g, " "));
|
|
87713
87799
|
try {
|
|
87714
|
-
const head = (await
|
|
87800
|
+
const head = (await readFile38(path43.join(dir, entry), "utf8")).slice(0, 2e3);
|
|
87715
87801
|
const titled = /^\s*(?:title|name)\s*:\s*["']?([^"'\n]+)["']?\s*$/im.exec(head);
|
|
87716
87802
|
if (titled?.[1]) names.push(titled[1].trim());
|
|
87717
87803
|
} catch {
|
|
@@ -87721,7 +87807,7 @@ async function readCompetitorNames(projectRoot) {
|
|
|
87721
87807
|
}
|
|
87722
87808
|
async function listLandingSlugs(projectRoot) {
|
|
87723
87809
|
try {
|
|
87724
|
-
const entries = await readdir15(
|
|
87810
|
+
const entries = await readdir15(path43.join(projectRoot, "src", "pages"), { withFileTypes: true });
|
|
87725
87811
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
87726
87812
|
} catch {
|
|
87727
87813
|
return [];
|
|
@@ -87872,8 +87958,8 @@ var addCommand = defineCommand196({
|
|
|
87872
87958
|
});
|
|
87873
87959
|
|
|
87874
87960
|
// src/commands/landing/inspiration/code.ts
|
|
87875
|
-
import { mkdir as
|
|
87876
|
-
import
|
|
87961
|
+
import { mkdir as mkdir12, writeFile as writeFile18 } from "fs/promises";
|
|
87962
|
+
import path44 from "path";
|
|
87877
87963
|
import { defineCommand as defineCommand197 } from "citty";
|
|
87878
87964
|
registerSchema({
|
|
87879
87965
|
command: "landing.inspiration.code",
|
|
@@ -87896,10 +87982,10 @@ var codeCommand = defineCommand197({
|
|
|
87896
87982
|
try {
|
|
87897
87983
|
const id = args8.id;
|
|
87898
87984
|
const data = await apiGet("/api/landing-inspiration/section-code", { id });
|
|
87899
|
-
const dir =
|
|
87900
|
-
await
|
|
87901
|
-
const file =
|
|
87902
|
-
await
|
|
87985
|
+
const dir = path44.join(process.cwd(), ".baker", "inspiration", id);
|
|
87986
|
+
await mkdir12(dir, { recursive: true });
|
|
87987
|
+
const file = path44.join(dir, "section.html");
|
|
87988
|
+
await writeFile18(file, data.html);
|
|
87903
87989
|
const hints2 = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
87904
87990
|
const fidelity = fidelityHint(data.fidelity);
|
|
87905
87991
|
if (fidelity) hints2.push(fidelity);
|
|
@@ -87908,7 +87994,7 @@ var codeCommand = defineCommand197({
|
|
|
87908
87994
|
ok: true,
|
|
87909
87995
|
data: {
|
|
87910
87996
|
id,
|
|
87911
|
-
file:
|
|
87997
|
+
file: path44.relative(process.cwd(), file),
|
|
87912
87998
|
bytes: data.html.length,
|
|
87913
87999
|
fidelity: data.fidelity,
|
|
87914
88000
|
reproduction_notes: data.reproductionNotes,
|
|
@@ -88339,8 +88425,8 @@ function classifyCaptureFailure(error) {
|
|
|
88339
88425
|
}
|
|
88340
88426
|
|
|
88341
88427
|
// src/engine/landing-library/run.ts
|
|
88342
|
-
import { mkdir as
|
|
88343
|
-
import
|
|
88428
|
+
import { mkdir as mkdir13, writeFile as writeFile20 } from "fs/promises";
|
|
88429
|
+
import path46 from "path";
|
|
88344
88430
|
|
|
88345
88431
|
// ../proxy/src/preflight.ts
|
|
88346
88432
|
import http from "http";
|
|
@@ -89755,11 +89841,11 @@ async function renderBundleToPng(browser, html, viewportWidth, options = {}) {
|
|
|
89755
89841
|
}
|
|
89756
89842
|
|
|
89757
89843
|
// src/engine/landing-library/report.ts
|
|
89758
|
-
import { writeFile as
|
|
89759
|
-
import
|
|
89844
|
+
import { writeFile as writeFile19 } from "fs/promises";
|
|
89845
|
+
import path45 from "path";
|
|
89760
89846
|
async function writeCaptureReport(manifest, outDir) {
|
|
89761
|
-
const file =
|
|
89762
|
-
await
|
|
89847
|
+
const file = path45.join(outDir, "report.html");
|
|
89848
|
+
await writeFile19(file, renderReport(manifest));
|
|
89763
89849
|
return file;
|
|
89764
89850
|
}
|
|
89765
89851
|
function escapeHtml4(value2) {
|
|
@@ -89937,32 +90023,32 @@ async function reproducePage(args8) {
|
|
|
89937
90023
|
const { browser, page, outDir, pageUrl, livePageShot } = args8;
|
|
89938
90024
|
const built = await buildSectionBundle(page, "body", pageUrl).catch(() => null);
|
|
89939
90025
|
if (!built) return { bundle: null, fidelity: null };
|
|
89940
|
-
await
|
|
90026
|
+
await writeFile20(path46.join(outDir, "page.html"), built.html);
|
|
89941
90027
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width, {
|
|
89942
90028
|
wholePage: true,
|
|
89943
90029
|
timeoutMs: 6e4
|
|
89944
90030
|
});
|
|
89945
90031
|
if (!rendered || !livePageShot) return { bundle: "page.html", fidelity: null };
|
|
89946
|
-
await
|
|
90032
|
+
await writeFile20(path46.join(outDir, "page-rendered.png"), rendered);
|
|
89947
90033
|
const { score, note } = await scoreFidelity(livePageShot, rendered);
|
|
89948
90034
|
return { bundle: "page.html", fidelity: score, ...note ? { fidelityNote: note } : {} };
|
|
89949
90035
|
}
|
|
89950
90036
|
async function captureOneSection(args8) {
|
|
89951
90037
|
const { browser, page, candidate, sectionsDir, outDir, pageUrl, withCode } = args8;
|
|
89952
|
-
const dir =
|
|
89953
|
-
await
|
|
90038
|
+
const dir = path46.join(sectionsDir, String(candidate.index).padStart(2, "0"));
|
|
90039
|
+
await mkdir13(dir, { recursive: true });
|
|
89954
90040
|
const desktop = await captureSection(page, candidate);
|
|
89955
|
-
if (desktop) await
|
|
90041
|
+
if (desktop) await writeFile20(path46.join(dir, "desktop.png"), desktop);
|
|
89956
90042
|
const visualHash = desktop ? await perceptualHash(desktop) : null;
|
|
89957
90043
|
const motion = await collectMotion(page, candidate.selector);
|
|
89958
90044
|
const built = withCode ? await buildSectionBundle(page, candidate.selector, pageUrl) : null;
|
|
89959
90045
|
let fidelity = null;
|
|
89960
90046
|
let fidelityNote;
|
|
89961
90047
|
if (built) {
|
|
89962
|
-
await
|
|
90048
|
+
await writeFile20(path46.join(dir, "section.html"), built.html);
|
|
89963
90049
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width);
|
|
89964
90050
|
if (rendered && desktop) {
|
|
89965
|
-
await
|
|
90051
|
+
await writeFile20(path46.join(dir, "section-rendered.png"), rendered);
|
|
89966
90052
|
const result = await scoreFidelity(desktop, rendered);
|
|
89967
90053
|
fidelity = result.score;
|
|
89968
90054
|
fidelityNote = result.note;
|
|
@@ -89970,9 +90056,9 @@ async function captureOneSection(args8) {
|
|
|
89970
90056
|
}
|
|
89971
90057
|
return {
|
|
89972
90058
|
...candidate,
|
|
89973
|
-
desktopShot: desktop ?
|
|
90059
|
+
desktopShot: desktop ? path46.relative(outDir, path46.join(dir, "desktop.png")) : null,
|
|
89974
90060
|
mobileShot: null,
|
|
89975
|
-
bundle: built ?
|
|
90061
|
+
bundle: built ? path46.relative(outDir, path46.join(dir, "section.html")) : null,
|
|
89976
90062
|
fidelity,
|
|
89977
90063
|
...fidelityNote ? { fidelityNote } : {},
|
|
89978
90064
|
...built ? { cssStats: built.stats } : {},
|
|
@@ -89991,9 +90077,9 @@ async function captureMobileShots(args8) {
|
|
|
89991
90077
|
for (const section of sections) {
|
|
89992
90078
|
const shot = await captureSectionOnMobile(mobile.page, section);
|
|
89993
90079
|
if (!shot) continue;
|
|
89994
|
-
const file =
|
|
89995
|
-
await
|
|
89996
|
-
section.mobileShot =
|
|
90080
|
+
const file = path46.join(sectionsDir, String(section.index).padStart(2, "0"), "mobile.png");
|
|
90081
|
+
await writeFile20(file, shot);
|
|
90082
|
+
section.mobileShot = path46.relative(outDir, file);
|
|
89997
90083
|
}
|
|
89998
90084
|
} finally {
|
|
89999
90085
|
await mobile.context.close();
|
|
@@ -90008,10 +90094,10 @@ async function captureMotionTakes(args8) {
|
|
|
90008
90094
|
const filmOne = async (section) => {
|
|
90009
90095
|
const take = await captureMotionTake(browser, pageUrl, section.selector).catch(() => null);
|
|
90010
90096
|
if (!take) return;
|
|
90011
|
-
const dir =
|
|
90012
|
-
const file =
|
|
90013
|
-
await
|
|
90014
|
-
section.motionFilmstrip =
|
|
90097
|
+
const dir = path46.join(sectionsDir, String(section.index).padStart(2, "0"));
|
|
90098
|
+
const file = path46.join(dir, "motion-filmstrip.png");
|
|
90099
|
+
await writeFile20(file, take.filmstrip);
|
|
90100
|
+
section.motionFilmstrip = path46.relative(outDir, file);
|
|
90015
90101
|
log(` [${section.index}] ${section.motion.summary}`);
|
|
90016
90102
|
};
|
|
90017
90103
|
const queue = [...moving];
|
|
@@ -90068,7 +90154,7 @@ async function captureAlternateViews(args8) {
|
|
|
90068
90154
|
async function reproduceWholePage(args8) {
|
|
90069
90155
|
const { browser, page, outDir, pageUrl, withCode, log } = args8;
|
|
90070
90156
|
const fullPage = await page.screenshot({ type: "png", fullPage: true }).catch(() => null);
|
|
90071
|
-
if (fullPage) await
|
|
90157
|
+
if (fullPage) await writeFile20(path46.join(outDir, "full-page.png"), fullPage);
|
|
90072
90158
|
if (!withCode) return { bundle: null, fidelity: null };
|
|
90073
90159
|
const reproduction = await reproducePage({ browser, page, outDir, pageUrl, livePageShot: fullPage });
|
|
90074
90160
|
log(`page reproduction: ${reproduction.fidelity === null ? "unavailable" : reproduction.fidelity.toFixed(2)}`);
|
|
@@ -90139,7 +90225,7 @@ async function openViaLadder(args8) {
|
|
|
90139
90225
|
async function scrapeLanding(options) {
|
|
90140
90226
|
const timeoutMs = options.timeoutMs ?? 45e3;
|
|
90141
90227
|
const log = options.onProgress ?? (() => void 0);
|
|
90142
|
-
const sectionsDir =
|
|
90228
|
+
const sectionsDir = path46.join(options.outDir, "sections");
|
|
90143
90229
|
const nonPublic = refuseNonPublicUrl(options.url);
|
|
90144
90230
|
if (nonPublic) {
|
|
90145
90231
|
throw new BlockedPageError({
|
|
@@ -90159,7 +90245,7 @@ async function scrapeLanding(options) {
|
|
|
90159
90245
|
const withMotion = options.motion !== false && !escalated;
|
|
90160
90246
|
const renderBrowser = options.code === false ? null : await launchBrowser();
|
|
90161
90247
|
try {
|
|
90162
|
-
await
|
|
90248
|
+
await mkdir13(sectionsDir, { recursive: true });
|
|
90163
90249
|
const sections = await captureSections({
|
|
90164
90250
|
browser: renderBrowser ?? browser,
|
|
90165
90251
|
page,
|
|
@@ -90201,7 +90287,7 @@ async function scrapeLanding(options) {
|
|
|
90201
90287
|
security: prepared.security,
|
|
90202
90288
|
captureTier: tier
|
|
90203
90289
|
};
|
|
90204
|
-
await
|
|
90290
|
+
await writeFile20(path46.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
90205
90291
|
`);
|
|
90206
90292
|
if (options.report !== false) {
|
|
90207
90293
|
const reportPath = await writeCaptureReport(manifest, options.outDir);
|
|
@@ -90216,28 +90302,28 @@ async function scrapeLanding(options) {
|
|
|
90216
90302
|
|
|
90217
90303
|
// src/commands/landing/inspiration/captureOut.ts
|
|
90218
90304
|
import { existsSync as existsSync9 } from "fs";
|
|
90219
|
-
import
|
|
90305
|
+
import path47 from "path";
|
|
90220
90306
|
var SCRATCH_DIR = ".baker";
|
|
90221
90307
|
function isWithin(parent, target2) {
|
|
90222
|
-
const relative =
|
|
90223
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
90308
|
+
const relative = path47.relative(parent, target2);
|
|
90309
|
+
return relative === "" || !relative.startsWith("..") && !path47.isAbsolute(relative);
|
|
90224
90310
|
}
|
|
90225
90311
|
function findRepoRoot(from) {
|
|
90226
|
-
let dir =
|
|
90312
|
+
let dir = path47.resolve(from);
|
|
90227
90313
|
for (; ; ) {
|
|
90228
|
-
if (existsSync9(
|
|
90229
|
-
const parent =
|
|
90314
|
+
if (existsSync9(path47.join(dir, ".git"))) return dir;
|
|
90315
|
+
const parent = path47.dirname(dir);
|
|
90230
90316
|
if (parent === dir) return null;
|
|
90231
90317
|
dir = parent;
|
|
90232
90318
|
}
|
|
90233
90319
|
}
|
|
90234
90320
|
function checkCaptureOut(out, options) {
|
|
90235
90321
|
const { cwd, repoRoot } = options;
|
|
90236
|
-
const resolved =
|
|
90322
|
+
const resolved = path47.resolve(cwd, out);
|
|
90237
90323
|
if (repoRoot === null || !isWithin(repoRoot, resolved)) return { ok: true };
|
|
90238
|
-
const scratch =
|
|
90324
|
+
const scratch = path47.join(repoRoot, SCRATCH_DIR);
|
|
90239
90325
|
if (isWithin(scratch, resolved)) return { ok: true };
|
|
90240
|
-
const suggestion =
|
|
90326
|
+
const suggestion = path47.posix.join(SCRATCH_DIR, "teardowns", path47.basename(resolved) || "capture");
|
|
90241
90327
|
return {
|
|
90242
90328
|
ok: false,
|
|
90243
90329
|
error: {
|
|
@@ -90411,12 +90497,12 @@ var scrapeCommand = defineCommand200({
|
|
|
90411
90497
|
});
|
|
90412
90498
|
|
|
90413
90499
|
// src/commands/landing/inspiration/search.ts
|
|
90414
|
-
import
|
|
90500
|
+
import path49 from "path";
|
|
90415
90501
|
import { defineCommand as defineCommand201 } from "citty";
|
|
90416
90502
|
|
|
90417
90503
|
// src/commands/landing/inspiration/shot.ts
|
|
90418
|
-
import { mkdir as
|
|
90419
|
-
import
|
|
90504
|
+
import { mkdir as mkdir14, writeFile as writeFile21 } from "fs/promises";
|
|
90505
|
+
import path48 from "path";
|
|
90420
90506
|
import sharp8 from "sharp";
|
|
90421
90507
|
var READABLE_SHOT = {
|
|
90422
90508
|
maxWidth: 1440,
|
|
@@ -90442,9 +90528,9 @@ async function downloadReadableShot(url, file) {
|
|
|
90442
90528
|
const response = await fetch(url);
|
|
90443
90529
|
if (!response.ok) return null;
|
|
90444
90530
|
const shot = await toReadableShot(Buffer.from(await response.arrayBuffer()));
|
|
90445
|
-
await
|
|
90446
|
-
await
|
|
90447
|
-
return
|
|
90531
|
+
await mkdir14(path48.dirname(file), { recursive: true });
|
|
90532
|
+
await writeFile21(file, shot);
|
|
90533
|
+
return path48.relative(process.cwd(), file);
|
|
90448
90534
|
} catch {
|
|
90449
90535
|
return null;
|
|
90450
90536
|
}
|
|
@@ -90536,13 +90622,13 @@ function buildSearchBody(args8) {
|
|
|
90536
90622
|
return body;
|
|
90537
90623
|
}
|
|
90538
90624
|
async function downloadShots(results) {
|
|
90539
|
-
const dir =
|
|
90625
|
+
const dir = path49.join(process.cwd(), ".baker", "inspiration");
|
|
90540
90626
|
const saved = /* @__PURE__ */ new Map();
|
|
90541
90627
|
await Promise.all(
|
|
90542
90628
|
results.map(async (result) => {
|
|
90543
90629
|
const file = await downloadReadableShot(
|
|
90544
90630
|
result.desktopShotUrl,
|
|
90545
|
-
|
|
90631
|
+
path49.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
|
|
90546
90632
|
);
|
|
90547
90633
|
if (file) saved.set(result.id, file);
|
|
90548
90634
|
})
|
|
@@ -90770,7 +90856,7 @@ var sequencesCommand = defineCommand202({
|
|
|
90770
90856
|
});
|
|
90771
90857
|
|
|
90772
90858
|
// src/commands/landing/inspiration/view.ts
|
|
90773
|
-
import
|
|
90859
|
+
import path50 from "path";
|
|
90774
90860
|
import { defineCommand as defineCommand203 } from "citty";
|
|
90775
90861
|
registerSchema({
|
|
90776
90862
|
command: "landing.inspiration.view",
|
|
@@ -90803,12 +90889,12 @@ var viewCommand2 = defineCommand203({
|
|
|
90803
90889
|
const id = args8.id;
|
|
90804
90890
|
const data = await apiGet("/api/landing-inspiration/section", { id });
|
|
90805
90891
|
const section = data.section;
|
|
90806
|
-
const dir =
|
|
90892
|
+
const dir = path50.join(process.cwd(), ".baker", "inspiration", id);
|
|
90807
90893
|
const ext = READABLE_SHOT.extension;
|
|
90808
90894
|
const [desktop, mobile, filmstrip] = await Promise.all([
|
|
90809
|
-
downloadReadableShot(section.desktopShotUrl,
|
|
90810
|
-
downloadReadableShot(section.mobileShotUrl,
|
|
90811
|
-
downloadReadableShot(section.motionFilmstripUrl,
|
|
90895
|
+
downloadReadableShot(section.desktopShotUrl, path50.join(dir, `desktop.${ext}`)),
|
|
90896
|
+
downloadReadableShot(section.mobileShotUrl, path50.join(dir, `mobile.${ext}`)),
|
|
90897
|
+
downloadReadableShot(section.motionFilmstripUrl, path50.join(dir, `motion-filmstrip.${ext}`))
|
|
90812
90898
|
]);
|
|
90813
90899
|
const full = args8.full;
|
|
90814
90900
|
const hints2 = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
@@ -90914,8 +91000,8 @@ Full guide: __tooling__/docs/tools/baker/landing.md`
|
|
|
90914
91000
|
|
|
90915
91001
|
// src/commands/landing/variant.ts
|
|
90916
91002
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
90917
|
-
import { mkdir as
|
|
90918
|
-
import
|
|
91003
|
+
import { mkdir as mkdir15, readdir as readdir16, readFile as readFile39, stat as stat11, writeFile as writeFile22 } from "fs/promises";
|
|
91004
|
+
import path51 from "path";
|
|
90919
91005
|
import { defineCommand as defineCommand205 } from "citty";
|
|
90920
91006
|
registerSchema({
|
|
90921
91007
|
command: "landing.variant",
|
|
@@ -90942,7 +91028,7 @@ registerSchema({
|
|
|
90942
91028
|
var SLUG_RE2 = /^[a-z0-9][a-z0-9-]*$/;
|
|
90943
91029
|
async function readInternalId(definitionPath) {
|
|
90944
91030
|
try {
|
|
90945
|
-
const text2 = await
|
|
91031
|
+
const text2 = await readFile39(definitionPath, "utf8");
|
|
90946
91032
|
return /^internalId:\s*"?([^"\s]+)"?\s*$/m.exec(text2)?.[1] ?? null;
|
|
90947
91033
|
} catch {
|
|
90948
91034
|
return null;
|
|
@@ -90951,7 +91037,7 @@ async function readInternalId(definitionPath) {
|
|
|
90951
91037
|
async function archivedVariantNumbers(root, internalId) {
|
|
90952
91038
|
if (!internalId) return [];
|
|
90953
91039
|
try {
|
|
90954
|
-
const entries = await readdir16(
|
|
91040
|
+
const entries = await readdir16(path51.resolve(root, "src/_variants", internalId), { withFileTypes: true });
|
|
90955
91041
|
return entries.filter((entry) => entry.isDirectory()).map((entry) => Number(entry.name)).filter((n) => Number.isInteger(n) && n > 0);
|
|
90956
91042
|
} catch {
|
|
90957
91043
|
return [];
|
|
@@ -90981,7 +91067,7 @@ async function exists2(p) {
|
|
|
90981
91067
|
}
|
|
90982
91068
|
async function listLandingSlugs2(root) {
|
|
90983
91069
|
try {
|
|
90984
|
-
const entries = await readdir16(
|
|
91070
|
+
const entries = await readdir16(path51.resolve(root, "src", "pages"), { withFileTypes: true });
|
|
90985
91071
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
90986
91072
|
} catch {
|
|
90987
91073
|
return [];
|
|
@@ -90997,7 +91083,7 @@ async function listComponents(componentsDir, prefix = "") {
|
|
|
90997
91083
|
const out = [];
|
|
90998
91084
|
for (const entry of entries) {
|
|
90999
91085
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
91000
|
-
if (entry.isDirectory()) out.push(...await listComponents(
|
|
91086
|
+
if (entry.isDirectory()) out.push(...await listComponents(path51.join(componentsDir, entry.name), rel));
|
|
91001
91087
|
else if (entry.name.endsWith(".astro")) out.push(rel);
|
|
91002
91088
|
}
|
|
91003
91089
|
return out.sort();
|
|
@@ -91064,16 +91150,16 @@ async function resolveTargets(projectRoot, opts) {
|
|
|
91064
91150
|
`"${controlSlug}" is already a variant of "${variantOfSlug(controlSlug)}". Make the next variant of the page itself: \`baker landing variant ${variantOfSlug(controlSlug)}\`.`
|
|
91065
91151
|
);
|
|
91066
91152
|
}
|
|
91067
|
-
const controlDir =
|
|
91153
|
+
const controlDir = path51.resolve(projectRoot, "src", "pages", controlSlug);
|
|
91068
91154
|
if (!await isDir3(controlDir)) {
|
|
91069
91155
|
fail10("NOT_FOUND", `No page at src/pages/${controlSlug}/`, {
|
|
91070
91156
|
availableSlugs: await listLandingSlugs2(projectRoot)
|
|
91071
91157
|
});
|
|
91072
91158
|
}
|
|
91073
|
-
if (!await exists2(
|
|
91159
|
+
if (!await exists2(path51.join(controlDir, "index.astro"))) {
|
|
91074
91160
|
fail10("NOT_FOUND", `src/pages/${controlSlug}/index.astro is missing, so there is no page to make a variant of.`);
|
|
91075
91161
|
}
|
|
91076
|
-
const internalId = await readInternalId(
|
|
91162
|
+
const internalId = await readInternalId(path51.join(controlDir, "_definition.md"));
|
|
91077
91163
|
const variantSlugValue = variantSlug(
|
|
91078
91164
|
controlSlug,
|
|
91079
91165
|
nextVariantNumber(
|
|
@@ -91082,8 +91168,8 @@ async function resolveTargets(projectRoot, opts) {
|
|
|
91082
91168
|
await archivedVariantNumbers(projectRoot, internalId)
|
|
91083
91169
|
)
|
|
91084
91170
|
);
|
|
91085
|
-
const variantDir =
|
|
91086
|
-
const available = await listComponents(
|
|
91171
|
+
const variantDir = path51.resolve(projectRoot, "src", "pages", variantSlugValue);
|
|
91172
|
+
const available = await listComponents(path51.join(controlDir, "_components"));
|
|
91087
91173
|
const forks = [];
|
|
91088
91174
|
for (const name of parseForks(opts.fork, opts.rawArgs)) {
|
|
91089
91175
|
const resolved = resolveForkName(name, available);
|
|
@@ -91099,26 +91185,26 @@ async function resolveTargets(projectRoot, opts) {
|
|
|
91099
91185
|
}
|
|
91100
91186
|
async function writeVariant(opts) {
|
|
91101
91187
|
const { controlDir, variantDir, controlSlug, variantSlug: variantSlug2, forks } = opts;
|
|
91102
|
-
const forkedTargets = new Set(forks.map((f) =>
|
|
91188
|
+
const forkedTargets = new Set(forks.map((f) => path51.join(controlDir, "_components", f)));
|
|
91103
91189
|
const written = [];
|
|
91104
|
-
const indexText = await
|
|
91105
|
-
await
|
|
91106
|
-
await
|
|
91107
|
-
|
|
91190
|
+
const indexText = await readFile39(path51.join(controlDir, "index.astro"), "utf8");
|
|
91191
|
+
await mkdir15(variantDir, { recursive: true });
|
|
91192
|
+
await writeFile22(
|
|
91193
|
+
path51.join(variantDir, "index.astro"),
|
|
91108
91194
|
repointFile(indexText, { fromDir: controlDir, toDir: variantDir, controlDir, variantDir, forkedTargets }),
|
|
91109
91195
|
"utf8"
|
|
91110
91196
|
);
|
|
91111
91197
|
written.push(`src/pages/${variantSlug2}/index.astro`);
|
|
91112
91198
|
for (const fork of forks) {
|
|
91113
|
-
const fromFile =
|
|
91114
|
-
const toFile =
|
|
91115
|
-
const text2 = await
|
|
91116
|
-
await
|
|
91117
|
-
await
|
|
91199
|
+
const fromFile = path51.join(controlDir, "_components", fork);
|
|
91200
|
+
const toFile = path51.join(variantDir, "_components", fork);
|
|
91201
|
+
const text2 = await readFile39(fromFile, "utf8");
|
|
91202
|
+
await mkdir15(path51.dirname(toFile), { recursive: true });
|
|
91203
|
+
await writeFile22(
|
|
91118
91204
|
toFile,
|
|
91119
91205
|
repointFile(text2, {
|
|
91120
|
-
fromDir:
|
|
91121
|
-
toDir:
|
|
91206
|
+
fromDir: path51.dirname(fromFile),
|
|
91207
|
+
toDir: path51.dirname(toFile),
|
|
91122
91208
|
controlDir,
|
|
91123
91209
|
variantDir,
|
|
91124
91210
|
forkedTargets
|
|
@@ -91127,9 +91213,9 @@ async function writeVariant(opts) {
|
|
|
91127
91213
|
);
|
|
91128
91214
|
written.push(`src/pages/${variantSlug2}/_components/${fork}`);
|
|
91129
91215
|
}
|
|
91130
|
-
const controlDefinitionPath =
|
|
91216
|
+
const controlDefinitionPath = path51.join(controlDir, "_definition.md");
|
|
91131
91217
|
if (await exists2(controlDefinitionPath)) {
|
|
91132
|
-
const definition = buildVariantDefinition(await
|
|
91218
|
+
const definition = buildVariantDefinition(await readFile39(controlDefinitionPath, "utf8"), {
|
|
91133
91219
|
internalId: randomUUID2().replace(/-/g, "").slice(0, 8),
|
|
91134
91220
|
variantSlug: variantSlug2,
|
|
91135
91221
|
controlSlug,
|
|
@@ -91137,11 +91223,11 @@ async function writeVariant(opts) {
|
|
|
91137
91223
|
...opts.because ? { because: opts.because } : {},
|
|
91138
91224
|
...opts.change ? { change: opts.change } : {}
|
|
91139
91225
|
});
|
|
91140
|
-
await
|
|
91226
|
+
await writeFile22(path51.join(variantDir, "_definition.md"), definition, "utf8");
|
|
91141
91227
|
written.push(`src/pages/${variantSlug2}/_definition.md`);
|
|
91142
91228
|
}
|
|
91143
|
-
await
|
|
91144
|
-
await
|
|
91229
|
+
await mkdir15(path51.join(variantDir, "_images"), { recursive: true });
|
|
91230
|
+
await writeFile22(path51.join(variantDir, "_images", ".gitkeep"), "", "utf8");
|
|
91145
91231
|
return written;
|
|
91146
91232
|
}
|
|
91147
91233
|
var variantCommand = defineCommand205({
|
|
@@ -93492,8 +93578,8 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
|
|
|
93492
93578
|
});
|
|
93493
93579
|
|
|
93494
93580
|
// src/commands/scheduled-agents/templates.ts
|
|
93495
|
-
import { readFile as
|
|
93496
|
-
import
|
|
93581
|
+
import { readFile as readFile40 } from "fs/promises";
|
|
93582
|
+
import path52 from "path";
|
|
93497
93583
|
import { defineCommand as defineCommand227 } from "citty";
|
|
93498
93584
|
registerSchema({
|
|
93499
93585
|
command: "scheduled-agents.templates",
|
|
@@ -93600,7 +93686,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
|
|
|
93600
93686
|
}
|
|
93601
93687
|
if (save.length > 0) {
|
|
93602
93688
|
const briefFile = flag("brief-file");
|
|
93603
|
-
const brief2 = briefFile.length > 0 ? await
|
|
93689
|
+
const brief2 = briefFile.length > 0 ? await readFile40(path52.resolve(briefFile), "utf8") : flag("brief");
|
|
93604
93690
|
if (brief2.trim().length === 0) {
|
|
93605
93691
|
failValidation4("--brief-file (preferred) or --brief is required: the brief is the recipe.");
|
|
93606
93692
|
}
|
|
@@ -94240,7 +94326,7 @@ function parseImageRefs(spec) {
|
|
|
94240
94326
|
}
|
|
94241
94327
|
var defaultDeps = {
|
|
94242
94328
|
ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
|
|
94243
|
-
upload: (
|
|
94329
|
+
upload: (path54) => uploadLocalImage({ file: path54, contentType: detectImageContentType(path54), source: "uploaded" })
|
|
94244
94330
|
};
|
|
94245
94331
|
async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
94246
94332
|
const refs = parseImageRefs(spec);
|
|
@@ -94260,10 +94346,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
|
94260
94346
|
}
|
|
94261
94347
|
return { imageIds, added };
|
|
94262
94348
|
}
|
|
94263
|
-
function uploadFailure(
|
|
94349
|
+
function uploadFailure(path54) {
|
|
94264
94350
|
return (error) => {
|
|
94265
94351
|
if (error instanceof ApiError) throw error;
|
|
94266
|
-
throw new ApiError("VALIDATION_ERROR", `Could not read "${
|
|
94352
|
+
throw new ApiError("VALIDATION_ERROR", `Could not read "${path54}" as an image.`);
|
|
94267
94353
|
};
|
|
94268
94354
|
}
|
|
94269
94355
|
|
|
@@ -95363,10 +95449,10 @@ async function stageOp4(op) {
|
|
|
95363
95449
|
handleError8(err);
|
|
95364
95450
|
}
|
|
95365
95451
|
}
|
|
95366
|
-
async function draftAction3(
|
|
95452
|
+
async function draftAction3(path54, body, chat) {
|
|
95367
95453
|
const chatId = resolveChatId(chat);
|
|
95368
95454
|
try {
|
|
95369
|
-
const { data, hints: hints2 } = splitHints(await apiPost(
|
|
95455
|
+
const { data, hints: hints2 } = splitHints(await apiPost(path54, { chatId, ...body }));
|
|
95370
95456
|
writeJsonEnvelope({ ok: true, data, ...hints2.length > 0 ? { hints: hints2 } : {} });
|
|
95371
95457
|
return data;
|
|
95372
95458
|
} catch (err) {
|
|
@@ -96595,9 +96681,9 @@ var groupCommand2 = defineCommand254({
|
|
|
96595
96681
|
});
|
|
96596
96682
|
|
|
96597
96683
|
// src/commands/videos/ingest.ts
|
|
96598
|
-
import { mkdtemp as mkdtemp3, rm as
|
|
96684
|
+
import { mkdtemp as mkdtemp3, rm as rm10, stat as stat12 } from "fs/promises";
|
|
96599
96685
|
import { tmpdir as tmpdir4 } from "os";
|
|
96600
|
-
import
|
|
96686
|
+
import path53 from "path";
|
|
96601
96687
|
import { defineCommand as defineCommand255 } from "citty";
|
|
96602
96688
|
|
|
96603
96689
|
// src/lib/streamUpload.ts
|
|
@@ -96948,7 +97034,7 @@ function ingestUrl(args8) {
|
|
|
96948
97034
|
}
|
|
96949
97035
|
async function downloadThenIngest(args8, country) {
|
|
96950
97036
|
const vimeoCookie = captureVimeoCookie();
|
|
96951
|
-
const workDir = await mkdtemp3(
|
|
97037
|
+
const workDir = await mkdtemp3(path53.join(tmpdir4(), "videos-ingest-"));
|
|
96952
97038
|
try {
|
|
96953
97039
|
const probe = await probeYtDlp({ url: args8.url, country, vimeoCookie, cookieDir: workDir });
|
|
96954
97040
|
if (isAudioOnly(probe.info)) {
|
|
@@ -97005,7 +97091,7 @@ async function downloadThenIngest(args8, country) {
|
|
|
97005
97091
|
...reducedQuality ? { reducedQuality: true } : {}
|
|
97006
97092
|
};
|
|
97007
97093
|
} finally {
|
|
97008
|
-
await
|
|
97094
|
+
await rm10(workDir, { recursive: true, force: true }).catch(() => {
|
|
97009
97095
|
});
|
|
97010
97096
|
}
|
|
97011
97097
|
}
|