@koda-sl/baker-cli 0.439.0-dev.33cf4ca99 → 0.440.0-dev.dab0aaba9
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/cli.js +980 -914
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -35511,16 +35511,16 @@ async function runChatgptRead(reference, read, hintsFor4) {
|
|
|
35511
35511
|
}
|
|
35512
35512
|
}
|
|
35513
35513
|
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.";
|
|
35514
|
-
async function getChatgptList(
|
|
35515
|
-
const { data, headers } = await apiGetWithHeaders(
|
|
35514
|
+
async function getChatgptList(path68, params) {
|
|
35515
|
+
const { data, headers } = await apiGetWithHeaders(path68, params);
|
|
35516
35516
|
return { data, truncated: headers.get(CHATGPT_ADS_TRUNCATED_HEADER) === "true" };
|
|
35517
35517
|
}
|
|
35518
|
-
async function runChatgptListRead(reference,
|
|
35518
|
+
async function runChatgptListRead(reference, path68, params, hintsFor4) {
|
|
35519
35519
|
let truncated = false;
|
|
35520
35520
|
await runChatgptRead(
|
|
35521
35521
|
reference,
|
|
35522
35522
|
async () => {
|
|
35523
|
-
const read = await getChatgptList(
|
|
35523
|
+
const read = await getChatgptList(path68, params);
|
|
35524
35524
|
truncated = read.truncated;
|
|
35525
35525
|
return read.data;
|
|
35526
35526
|
},
|
|
@@ -36325,8 +36325,8 @@ var MEMBER_ARGS = {
|
|
|
36325
36325
|
},
|
|
36326
36326
|
country: { type: "string", description: "Country of phones written without a +code, e.g. ES" }
|
|
36327
36327
|
};
|
|
36328
|
-
function membersFromArgs(args8,
|
|
36329
|
-
const fromFile = typeof args8.file === "string" && args8.file ? membersFromFile(
|
|
36328
|
+
function membersFromArgs(args8, readFile52 = (path68) => readFileSync2(path68, "utf8")) {
|
|
36329
|
+
const fromFile = typeof args8.file === "string" && args8.file ? membersFromFile(readFile52(args8.file)) : { emails: [], phones: [] };
|
|
36330
36330
|
const given = [...csvArg(args8.emails) ?? [], ...fromFile.emails];
|
|
36331
36331
|
const givenPhones = [...csvArg(args8.phones) ?? [], ...fromFile.phones];
|
|
36332
36332
|
const members = hashedMembers({
|
|
@@ -39718,9 +39718,9 @@ var COPY_LIST_FLAG = {
|
|
|
39718
39718
|
descriptions: "--descriptions",
|
|
39719
39719
|
longHeadlines: "--long-headlines"
|
|
39720
39720
|
};
|
|
39721
|
-
function valueAt(root,
|
|
39721
|
+
function valueAt(root, path68) {
|
|
39722
39722
|
let node = root;
|
|
39723
|
-
for (const key of
|
|
39723
|
+
for (const key of path68) {
|
|
39724
39724
|
if (node === null || typeof node !== "object") return void 0;
|
|
39725
39725
|
node = node[key];
|
|
39726
39726
|
}
|
|
@@ -39953,11 +39953,11 @@ function parseKeywordEntry(raw, defaultMatch) {
|
|
|
39953
39953
|
function rawTextEntries(value2, rawArgs) {
|
|
39954
39954
|
return repeatedValues(rawArgs, "text", value2).flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
|
|
39955
39955
|
}
|
|
39956
|
-
function rawFileEntries(
|
|
39957
|
-
if (typeof
|
|
39956
|
+
function rawFileEntries(path68) {
|
|
39957
|
+
if (typeof path68 !== "string" || path68.length === 0) {
|
|
39958
39958
|
return [];
|
|
39959
39959
|
}
|
|
39960
|
-
return readFileSync4(
|
|
39960
|
+
return readFileSync4(path68, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
39961
39961
|
}
|
|
39962
39962
|
function keywordEntries(args8, rawArgs) {
|
|
39963
39963
|
const defaultMatch = typeof args8["match-type"] === "string" ? args8["match-type"].toUpperCase() : void 0;
|
|
@@ -39980,14 +39980,14 @@ function keywordEntries(args8, rawArgs) {
|
|
|
39980
39980
|
}
|
|
39981
39981
|
return entries;
|
|
39982
39982
|
}
|
|
39983
|
-
function loadJsonFileArg(
|
|
39984
|
-
if (typeof
|
|
39983
|
+
function loadJsonFileArg(path68) {
|
|
39984
|
+
if (typeof path68 !== "string" || path68.length === 0) {
|
|
39985
39985
|
return {};
|
|
39986
39986
|
}
|
|
39987
|
-
const inline =
|
|
39988
|
-
const source = inline ? "inline JSON" :
|
|
39987
|
+
const inline = path68.trimStart().startsWith("{");
|
|
39988
|
+
const source = inline ? "inline JSON" : path68;
|
|
39989
39989
|
try {
|
|
39990
|
-
const parsed = JSON.parse(inline ?
|
|
39990
|
+
const parsed = JSON.parse(inline ? path68 : readFileSync4(path68, "utf8"));
|
|
39991
39991
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
39992
39992
|
failWriteValidation2(`${source} must contain a JSON object`);
|
|
39993
39993
|
}
|
|
@@ -40124,10 +40124,10 @@ async function stageUpdate(kind, customerId, target2, payload, hints2) {
|
|
|
40124
40124
|
async function stageTarget(kind, customerId, target2, hints2) {
|
|
40125
40125
|
await stageGoogleOp({ kind, customerId, target: target2 }, hints2);
|
|
40126
40126
|
}
|
|
40127
|
-
async function draftAction(
|
|
40127
|
+
async function draftAction(path68, body, chat) {
|
|
40128
40128
|
try {
|
|
40129
40129
|
const chatId = resolveChatId(chat);
|
|
40130
|
-
const response = await apiPost(
|
|
40130
|
+
const response = await apiPost(path68, { chatId, ...body });
|
|
40131
40131
|
writeJsonEnvelope(response);
|
|
40132
40132
|
} catch (err) {
|
|
40133
40133
|
handleGoogleError(err);
|
|
@@ -42547,14 +42547,14 @@ function mappingRowProblem(row2) {
|
|
|
42547
42547
|
|
|
42548
42548
|
// ../api/src/event-sources/template.ts
|
|
42549
42549
|
var MENTION = /\{\{\s*([^{}]+?)\s*\}\}/g;
|
|
42550
|
-
function mentionOf(
|
|
42551
|
-
return `{{${
|
|
42550
|
+
function mentionOf(path68) {
|
|
42551
|
+
return `{{${path68}}}`;
|
|
42552
42552
|
}
|
|
42553
42553
|
function templateMentions(template) {
|
|
42554
42554
|
const seen = [];
|
|
42555
42555
|
for (const match of template.matchAll(MENTION)) {
|
|
42556
|
-
const
|
|
42557
|
-
if (
|
|
42556
|
+
const path68 = (match[1] ?? "").trim();
|
|
42557
|
+
if (path68 !== "" && !seen.includes(path68)) seen.push(path68);
|
|
42558
42558
|
}
|
|
42559
42559
|
return seen;
|
|
42560
42560
|
}
|
|
@@ -45951,17 +45951,17 @@ function ignoredColumnHints(columns) {
|
|
|
45951
45951
|
`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.`
|
|
45952
45952
|
];
|
|
45953
45953
|
}
|
|
45954
|
-
function readRowsFile(
|
|
45955
|
-
const inline =
|
|
45954
|
+
function readRowsFile(path68) {
|
|
45955
|
+
const inline = path68.trimStart().startsWith("[") || path68.trimStart().startsWith("{");
|
|
45956
45956
|
let text3;
|
|
45957
45957
|
try {
|
|
45958
|
-
text3 = inline ?
|
|
45958
|
+
text3 = inline ? path68 : readFileSync6(path68, "utf8");
|
|
45959
45959
|
} catch (err) {
|
|
45960
|
-
failWriteValidation2(`could not read ${
|
|
45960
|
+
failWriteValidation2(`could not read ${path68}: ${err instanceof Error ? err.message : String(err)}`);
|
|
45961
45961
|
}
|
|
45962
45962
|
const parsed = parseConversionRowsText(text3);
|
|
45963
45963
|
if (!parsed.ok) {
|
|
45964
|
-
failWriteValidation2(`${inline ? "the conversions passed inline" :
|
|
45964
|
+
failWriteValidation2(`${inline ? "the conversions passed inline" : path68}: ${parsed.error}`);
|
|
45965
45965
|
}
|
|
45966
45966
|
return { rows: parsed.rows, ignoredColumns: parsed.ignoredColumns };
|
|
45967
45967
|
}
|
|
@@ -47862,19 +47862,19 @@ function failWriteValidation3(message) {
|
|
|
47862
47862
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
47863
47863
|
process.exit(1);
|
|
47864
47864
|
}
|
|
47865
|
-
function loadJsonFileArg2(
|
|
47866
|
-
if (typeof
|
|
47865
|
+
function loadJsonFileArg2(path68) {
|
|
47866
|
+
if (typeof path68 !== "string" || path68.length === 0) {
|
|
47867
47867
|
return {};
|
|
47868
47868
|
}
|
|
47869
47869
|
try {
|
|
47870
|
-
const parsed = JSON.parse(readFileSync7(
|
|
47870
|
+
const parsed = JSON.parse(readFileSync7(path68, "utf8"));
|
|
47871
47871
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
47872
|
-
failWriteValidation3(`${
|
|
47872
|
+
failWriteValidation3(`${path68} must contain a JSON object`);
|
|
47873
47873
|
}
|
|
47874
47874
|
return parsed;
|
|
47875
47875
|
} catch (err) {
|
|
47876
47876
|
if (err instanceof SyntaxError) {
|
|
47877
|
-
failWriteValidation3(`${
|
|
47877
|
+
failWriteValidation3(`${path68} is not valid JSON: ${err.message}`);
|
|
47878
47878
|
}
|
|
47879
47879
|
throw err;
|
|
47880
47880
|
}
|
|
@@ -47959,15 +47959,15 @@ function parseLocaleFlag(value2) {
|
|
|
47959
47959
|
}
|
|
47960
47960
|
return { language: match[1], country: match[2].toUpperCase() };
|
|
47961
47961
|
}
|
|
47962
|
-
function loadTargetingFileArg(
|
|
47963
|
-
if (typeof
|
|
47962
|
+
function loadTargetingFileArg(path68) {
|
|
47963
|
+
if (typeof path68 !== "string" || path68.length === 0) {
|
|
47964
47964
|
return void 0;
|
|
47965
47965
|
}
|
|
47966
|
-
const parsed = loadJsonFileArg2(
|
|
47966
|
+
const parsed = loadJsonFileArg2(path68);
|
|
47967
47967
|
const criteria = parsed.targetingCriteria ?? parsed;
|
|
47968
47968
|
if (!criteria.include) {
|
|
47969
47969
|
failWriteValidation3(
|
|
47970
|
-
`${
|
|
47970
|
+
`${path68} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
|
|
47971
47971
|
);
|
|
47972
47972
|
}
|
|
47973
47973
|
return criteria;
|
|
@@ -48002,14 +48002,14 @@ function parseCsvLine(line) {
|
|
|
48002
48002
|
cells.push(current);
|
|
48003
48003
|
return cells.map((cell3) => cell3.trim());
|
|
48004
48004
|
}
|
|
48005
|
-
function parseListFileArg(
|
|
48006
|
-
if (typeof
|
|
48005
|
+
function parseListFileArg(path68, maxRows) {
|
|
48006
|
+
if (typeof path68 !== "string" || path68.length === 0) {
|
|
48007
48007
|
return void 0;
|
|
48008
48008
|
}
|
|
48009
|
-
const raw = readFileSync7(
|
|
48009
|
+
const raw = readFileSync7(path68, "utf8");
|
|
48010
48010
|
const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
48011
48011
|
if (lines.length < 2) {
|
|
48012
|
-
failWriteValidation3(`${
|
|
48012
|
+
failWriteValidation3(`${path68} needs a header row and at least one data row`);
|
|
48013
48013
|
}
|
|
48014
48014
|
const columns = parseCsvLine(lines[0]).map((column) => column.trim());
|
|
48015
48015
|
const rows = [];
|
|
@@ -48028,7 +48028,7 @@ function parseListFileArg(path67, maxRows) {
|
|
|
48028
48028
|
}
|
|
48029
48029
|
}
|
|
48030
48030
|
if (rows.length > maxRows) {
|
|
48031
|
-
failWriteValidation3(`${
|
|
48031
|
+
failWriteValidation3(`${path68} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
|
|
48032
48032
|
}
|
|
48033
48033
|
return { columns, rows };
|
|
48034
48034
|
}
|
|
@@ -48124,11 +48124,11 @@ function readPositionals(args8) {
|
|
|
48124
48124
|
function splitIdList(raw) {
|
|
48125
48125
|
return raw.split(",").map((id) => id.trim()).filter(Boolean);
|
|
48126
48126
|
}
|
|
48127
|
-
function idsFileEntries(
|
|
48128
|
-
if (typeof
|
|
48127
|
+
function idsFileEntries(path68) {
|
|
48128
|
+
if (typeof path68 !== "string" || path68.length === 0) {
|
|
48129
48129
|
return [];
|
|
48130
48130
|
}
|
|
48131
|
-
return readFileSync7(
|
|
48131
|
+
return readFileSync7(path68, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
|
|
48132
48132
|
}
|
|
48133
48133
|
function requireTargets2(args8, entity) {
|
|
48134
48134
|
const positionals = readPositionals(args8);
|
|
@@ -52230,54 +52230,54 @@ function ruleFields(node) {
|
|
|
52230
52230
|
([key, value2]) => (key === "and" || key === "or") && Array.isArray(value2) ? value2.flatMap(ruleFields) : [key]
|
|
52231
52231
|
);
|
|
52232
52232
|
}
|
|
52233
|
-
function branchRefusal(key, value2,
|
|
52233
|
+
function branchRefusal(key, value2, path68) {
|
|
52234
52234
|
if (!Array.isArray(value2) || value2.length === 0) {
|
|
52235
|
-
return `${
|
|
52235
|
+
return `${path68}.${key} must be a non-empty array of conditions \u2014 an empty one counts every event the pixel sends`;
|
|
52236
52236
|
}
|
|
52237
52237
|
for (const [index, child] of value2.entries()) {
|
|
52238
|
-
const refusal = ruleNodeRefusal(child, `${
|
|
52238
|
+
const refusal = ruleNodeRefusal(child, `${path68}.${key}[${index}]`);
|
|
52239
52239
|
if (refusal) {
|
|
52240
52240
|
return refusal;
|
|
52241
52241
|
}
|
|
52242
52242
|
}
|
|
52243
52243
|
return void 0;
|
|
52244
52244
|
}
|
|
52245
|
-
function conditionRefusal(key, value2,
|
|
52246
|
-
return key === "and" || key === "or" ? branchRefusal(key, value2,
|
|
52245
|
+
function conditionRefusal(key, value2, path68) {
|
|
52246
|
+
return key === "and" || key === "or" ? branchRefusal(key, value2, path68) : leafRefusal(key, value2, path68);
|
|
52247
52247
|
}
|
|
52248
|
-
function ruleNodeRefusal(node,
|
|
52248
|
+
function ruleNodeRefusal(node, path68) {
|
|
52249
52249
|
if (!isRecord2(node)) {
|
|
52250
|
-
return `${
|
|
52250
|
+
return `${path68} must be an object like {"URL":{"i_contains":"/thank-you"}}`;
|
|
52251
52251
|
}
|
|
52252
52252
|
const entries = Object.entries(node);
|
|
52253
52253
|
if (entries.length === 0) {
|
|
52254
|
-
return `${
|
|
52254
|
+
return `${path68} is empty \u2014 a rule with no condition counts every event the pixel sends`;
|
|
52255
52255
|
}
|
|
52256
52256
|
if (entries.length > 1) {
|
|
52257
|
-
return `${
|
|
52257
|
+
return `${path68} 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]}`;
|
|
52258
52258
|
}
|
|
52259
52259
|
for (const [key, value2] of entries) {
|
|
52260
|
-
const refusal = conditionRefusal(key, value2,
|
|
52260
|
+
const refusal = conditionRefusal(key, value2, path68);
|
|
52261
52261
|
if (refusal) {
|
|
52262
52262
|
return refusal;
|
|
52263
52263
|
}
|
|
52264
52264
|
}
|
|
52265
52265
|
return void 0;
|
|
52266
52266
|
}
|
|
52267
|
-
function leafRefusal(field, condition,
|
|
52267
|
+
function leafRefusal(field, condition, path68) {
|
|
52268
52268
|
if (!isRecord2(condition)) {
|
|
52269
|
-
return `${
|
|
52269
|
+
return `${path68}.${field} must be a test like {"i_contains":"/thank-you"}, not a bare value`;
|
|
52270
52270
|
}
|
|
52271
52271
|
const tests = Object.entries(condition);
|
|
52272
52272
|
if (tests.length === 0) {
|
|
52273
|
-
return `${
|
|
52273
|
+
return `${path68}.${field} names no test \u2014 use one of ${Object.keys(RULE_OPERATORS).join(", ")}`;
|
|
52274
52274
|
}
|
|
52275
52275
|
if (tests.length > 1) {
|
|
52276
|
-
return `${
|
|
52276
|
+
return `${path68}.${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]}`;
|
|
52277
52277
|
}
|
|
52278
52278
|
for (const [operator] of tests) {
|
|
52279
52279
|
if (!RULE_OPERATORS[operator]) {
|
|
52280
|
-
return `${
|
|
52280
|
+
return `${path68}.${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)`;
|
|
52281
52281
|
}
|
|
52282
52282
|
}
|
|
52283
52283
|
return void 0;
|
|
@@ -52993,11 +52993,11 @@ var updateStatusSchema2 = z52.enum(UPDATE_STATUSES);
|
|
|
52993
52993
|
function currencyMinimums2(currencyCode) {
|
|
52994
52994
|
return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
|
|
52995
52995
|
}
|
|
52996
|
-
function validateDailyBudgetFloor(money, ctx,
|
|
52996
|
+
function validateDailyBudgetFloor(money, ctx, path68) {
|
|
52997
52997
|
if (money?.currencyCode) {
|
|
52998
52998
|
const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
|
|
52999
52999
|
if (Number(money.amount) < min) {
|
|
53000
|
-
ctx.addIssue({ code: "custom", path:
|
|
53000
|
+
ctx.addIssue({ code: "custom", path: path68, message: `below the ${min} ${money.currencyCode} daily minimum` });
|
|
53001
53001
|
}
|
|
53002
53002
|
}
|
|
53003
53003
|
}
|
|
@@ -53915,19 +53915,19 @@ function refuseDeleteByUpdate(payload, group) {
|
|
|
53915
53915
|
);
|
|
53916
53916
|
}
|
|
53917
53917
|
}
|
|
53918
|
-
function loadJsonFileArg3(
|
|
53919
|
-
if (typeof
|
|
53918
|
+
function loadJsonFileArg3(path68) {
|
|
53919
|
+
if (typeof path68 !== "string" || path68.length === 0) {
|
|
53920
53920
|
return {};
|
|
53921
53921
|
}
|
|
53922
53922
|
try {
|
|
53923
|
-
const parsed = JSON.parse(readFileSync11(
|
|
53923
|
+
const parsed = JSON.parse(readFileSync11(path68, "utf8"));
|
|
53924
53924
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
53925
|
-
failWriteValidation4(`${
|
|
53925
|
+
failWriteValidation4(`${path68} must contain a JSON object`);
|
|
53926
53926
|
}
|
|
53927
53927
|
return parsed;
|
|
53928
53928
|
} catch (err) {
|
|
53929
53929
|
if (err instanceof SyntaxError) {
|
|
53930
|
-
failWriteValidation4(`${
|
|
53930
|
+
failWriteValidation4(`${path68} is not valid JSON: ${err.message}`);
|
|
53931
53931
|
}
|
|
53932
53932
|
throw err;
|
|
53933
53933
|
}
|
|
@@ -54829,9 +54829,9 @@ var listArgs6 = {
|
|
|
54829
54829
|
},
|
|
54830
54830
|
...META_LIST_SHAPE_ARGS
|
|
54831
54831
|
};
|
|
54832
|
-
async function readOrEmpty(
|
|
54832
|
+
async function readOrEmpty(path68, params, label, hints2) {
|
|
54833
54833
|
try {
|
|
54834
|
-
return await apiGet(
|
|
54834
|
+
return await apiGet(path68, params);
|
|
54835
54835
|
} catch (err) {
|
|
54836
54836
|
const message = err instanceof Error ? err.message : "unknown error";
|
|
54837
54837
|
hints2.push(`Could not read the account's ${label} (${message}); the viewer was read without them.`);
|
|
@@ -59774,12 +59774,12 @@ function flowExists(slug) {
|
|
|
59774
59774
|
return existsSync3(join2(flowsDir(), slug, "_data.json"));
|
|
59775
59775
|
}
|
|
59776
59776
|
function readFlowTree(slug) {
|
|
59777
|
-
const
|
|
59778
|
-
if (!existsSync3(
|
|
59777
|
+
const path68 = join2(flowsDir(), slug, "_data.json");
|
|
59778
|
+
if (!existsSync3(path68)) {
|
|
59779
59779
|
failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
|
|
59780
59780
|
}
|
|
59781
59781
|
try {
|
|
59782
|
-
return JSON.parse(readFileSync13(
|
|
59782
|
+
return JSON.parse(readFileSync13(path68, "utf-8"));
|
|
59783
59783
|
} catch (error) {
|
|
59784
59784
|
failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
|
|
59785
59785
|
}
|
|
@@ -60756,12 +60756,12 @@ function readPeopleFile(args8) {
|
|
|
60756
60756
|
}
|
|
60757
60757
|
function unreadablePeopleFile(args8) {
|
|
60758
60758
|
if (!args8["people-file"]) return void 0;
|
|
60759
|
-
const
|
|
60759
|
+
const path68 = String(args8["people-file"]);
|
|
60760
60760
|
try {
|
|
60761
|
-
readFileSync14(
|
|
60761
|
+
readFileSync14(path68, "utf8");
|
|
60762
60762
|
return void 0;
|
|
60763
60763
|
} catch {
|
|
60764
|
-
return `Couldn't read --people-file ${
|
|
60764
|
+
return `Couldn't read --people-file ${path68}. Check the path, or pass the people inline with --person.`;
|
|
60765
60765
|
}
|
|
60766
60766
|
}
|
|
60767
60767
|
function peopleLimitError(args8) {
|
|
@@ -61733,11 +61733,11 @@ function parseFieldArgs(argv) {
|
|
|
61733
61733
|
for (const pair of parseRepeated(argv, "--field")) {
|
|
61734
61734
|
const eq = pair.indexOf("=");
|
|
61735
61735
|
const field = eq === -1 ? pair : pair.slice(0, eq);
|
|
61736
|
-
const
|
|
61736
|
+
const path68 = eq === -1 ? "" : pair.slice(eq + 1).trim();
|
|
61737
61737
|
const problem = mappingRowProblem(field);
|
|
61738
61738
|
if (problem !== null) throw new ConversionArgsError(problem);
|
|
61739
|
-
if (
|
|
61740
|
-
mapping[field] =
|
|
61739
|
+
if (path68 === "") throw new ConversionArgsError(`--field ${field} needs a path: --field ${field}=<dotted.path>.`);
|
|
61740
|
+
mapping[field] = path68;
|
|
61741
61741
|
}
|
|
61742
61742
|
return mapping;
|
|
61743
61743
|
}
|
|
@@ -63410,11 +63410,11 @@ function unwrap(response) {
|
|
|
63410
63410
|
}
|
|
63411
63411
|
return response.data;
|
|
63412
63412
|
}
|
|
63413
|
-
async function readAvatars(
|
|
63414
|
-
return unwrap(await apiGet(
|
|
63413
|
+
async function readAvatars(path68, params) {
|
|
63414
|
+
return unwrap(await apiGet(path68, params));
|
|
63415
63415
|
}
|
|
63416
|
-
async function writeAvatars(
|
|
63417
|
-
return unwrap(await apiPost(
|
|
63416
|
+
async function writeAvatars(path68, body, opts) {
|
|
63417
|
+
return unwrap(await apiPost(path68, body, opts));
|
|
63418
63418
|
}
|
|
63419
63419
|
|
|
63420
63420
|
// src/commands/avatars/create.ts
|
|
@@ -64528,12 +64528,12 @@ function missingFontFiles(urls, available) {
|
|
|
64528
64528
|
function planFontAdoption(sources, families) {
|
|
64529
64529
|
const wanted = new Map(families.map((family) => [normalizeFamily(family), family]));
|
|
64530
64530
|
const byFamily = /* @__PURE__ */ new Map();
|
|
64531
|
-
for (const { path:
|
|
64532
|
-
const dir = posix.dirname(
|
|
64531
|
+
for (const { path: path68, source } of sources) {
|
|
64532
|
+
const dir = posix.dirname(path68);
|
|
64533
64533
|
for (const face of declaredFontFaces(source)) {
|
|
64534
64534
|
if (!wanted.has(face.family)) continue;
|
|
64535
64535
|
const perFile = byFamily.get(face.family) ?? /* @__PURE__ */ new Map();
|
|
64536
|
-
perFile.set(
|
|
64536
|
+
perFile.set(path68, [...perFile.get(path68) ?? [], rebaseFontFaceSrc(face.block, dir)]);
|
|
64537
64537
|
byFamily.set(face.family, perFile);
|
|
64538
64538
|
}
|
|
64539
64539
|
}
|
|
@@ -67202,8 +67202,8 @@ function logoComposited(opts) {
|
|
|
67202
67202
|
}
|
|
67203
67203
|
var MAX_TAKES = 3;
|
|
67204
67204
|
var UNCOMPILED = "compiled from prompt.json";
|
|
67205
|
-
function ingestNode(id,
|
|
67206
|
-
return { id, type: "ingest", params: { source: "path", path:
|
|
67205
|
+
function ingestNode(id, path68, expect) {
|
|
67206
|
+
return { id, type: "ingest", params: { source: "path", path: path68, expect } };
|
|
67207
67207
|
}
|
|
67208
67208
|
function aspectRatio(blueprint, opts) {
|
|
67209
67209
|
const requested = opts.aspectRatio ?? blueprint.meta?.estimated_aspect_ratio;
|
|
@@ -72113,7 +72113,7 @@ async function probeDuration(filePath) {
|
|
|
72113
72113
|
import { defineCommand as defineCommand140 } from "citty";
|
|
72114
72114
|
|
|
72115
72115
|
// src/commands/canvas/run.ts
|
|
72116
|
-
import
|
|
72116
|
+
import path30 from "path";
|
|
72117
72117
|
import { defineCommand as defineCommand139 } from "citty";
|
|
72118
72118
|
|
|
72119
72119
|
// src/commands/canvas/definition-status.ts
|
|
@@ -75031,8 +75031,8 @@ var Answer = z66.object({
|
|
|
75031
75031
|
changed: z66.string().min(1),
|
|
75032
75032
|
set: z66.array(z66.object({ path: z66.string().min(1), value: z66.unknown() }))
|
|
75033
75033
|
});
|
|
75034
|
-
function withValueAt(root,
|
|
75035
|
-
const [key, ...rest] =
|
|
75034
|
+
function withValueAt(root, path68, value2) {
|
|
75035
|
+
const [key, ...rest] = path68;
|
|
75036
75036
|
if (key === void 0) return value2;
|
|
75037
75037
|
if (Array.isArray(root)) {
|
|
75038
75038
|
const at = Number(key);
|
|
@@ -75050,7 +75050,7 @@ function printedWords(spec) {
|
|
|
75050
75050
|
function acceptRepair(before, answer) {
|
|
75051
75051
|
const parsed = Answer.safeParse(answer);
|
|
75052
75052
|
if (!parsed.success || !isAdSpec(before)) return null;
|
|
75053
|
-
const spec = parsed.data.set.reduce((at, { path:
|
|
75053
|
+
const spec = parsed.data.set.reduce((at, { path: path68, value: value2 }) => withValueAt(at, path68.split("."), value2), before);
|
|
75054
75054
|
if (!isAdSpec(spec)) return null;
|
|
75055
75055
|
const was = AdSpecSchema.safeParse(before);
|
|
75056
75056
|
const now = AdSpecSchema.safeParse(spec);
|
|
@@ -75100,6 +75100,52 @@ async function repairSpec(opts) {
|
|
|
75100
75100
|
return repair.changed;
|
|
75101
75101
|
}
|
|
75102
75102
|
|
|
75103
|
+
// src/commands/canvas/turn-renders.ts
|
|
75104
|
+
import { mkdir as mkdir9, readFile as readFile25, writeFile as writeFile15 } from "fs/promises";
|
|
75105
|
+
import path29 from "path";
|
|
75106
|
+
import { z as z67 } from "zod";
|
|
75107
|
+
var RUNS_PER_TURN = 2;
|
|
75108
|
+
var LedgerSchema = z67.object({
|
|
75109
|
+
turnId: z67.string(),
|
|
75110
|
+
creatives: z67.record(z67.string(), z67.object({ runs: z67.number(), verdict: z67.string().optional() }))
|
|
75111
|
+
});
|
|
75112
|
+
function turnRenderRefusal(ledger, turnId, slug) {
|
|
75113
|
+
const entry = turnId !== void 0 && ledger?.turnId === turnId ? ledger.creatives[slug] : void 0;
|
|
75114
|
+
if (!entry) return null;
|
|
75115
|
+
if (entry.verdict === "ready" || entry.verdict === "ready_with_notes") {
|
|
75116
|
+
return {
|
|
75117
|
+
code: "CONFLICT",
|
|
75118
|
+
message: `This ad is already ready in this turn (${entry.verdict}): its notes are not defects.`,
|
|
75119
|
+
fix: "Show it now. A change the person asks for comes with their next message."
|
|
75120
|
+
};
|
|
75121
|
+
}
|
|
75122
|
+
if (entry.runs >= RUNS_PER_TURN) {
|
|
75123
|
+
return {
|
|
75124
|
+
code: "CONFLICT",
|
|
75125
|
+
message: `This ad was rendered twice in this turn, its automatic corrections included, and is still not ready.`,
|
|
75126
|
+
fix: "Show the best version and say in one plain sentence what is left, in the person's words; they decide what changes next."
|
|
75127
|
+
};
|
|
75128
|
+
}
|
|
75129
|
+
return null;
|
|
75130
|
+
}
|
|
75131
|
+
function recordTurnRender(ledger, turnId, slug, verdict2) {
|
|
75132
|
+
const creatives = ledger?.turnId === turnId ? ledger.creatives : {};
|
|
75133
|
+
return { turnId, creatives: { ...creatives, [slug]: { runs: (creatives[slug]?.runs ?? 0) + 1, verdict: verdict2 } } };
|
|
75134
|
+
}
|
|
75135
|
+
var ledgerPath = (cacheDir) => path29.join(cacheDir, "turn-renders.json");
|
|
75136
|
+
async function readTurnRenders(cacheDir) {
|
|
75137
|
+
try {
|
|
75138
|
+
const parsed = LedgerSchema.safeParse(JSON.parse(await readFile25(ledgerPath(cacheDir), "utf8")));
|
|
75139
|
+
return parsed.success ? parsed.data : void 0;
|
|
75140
|
+
} catch {
|
|
75141
|
+
return void 0;
|
|
75142
|
+
}
|
|
75143
|
+
}
|
|
75144
|
+
async function writeTurnRenders(cacheDir, ledger) {
|
|
75145
|
+
await mkdir9(cacheDir, { recursive: true });
|
|
75146
|
+
await writeFile15(ledgerPath(cacheDir), JSON.stringify(ledger), "utf8");
|
|
75147
|
+
}
|
|
75148
|
+
|
|
75103
75149
|
// src/commands/canvas/run.ts
|
|
75104
75150
|
var OPERATOR = "Operator only, agents never need it.";
|
|
75105
75151
|
var runCommand = defineCommand139({
|
|
@@ -75222,7 +75268,7 @@ function failPreflight3(code, message, fix) {
|
|
|
75222
75268
|
process.exit(2);
|
|
75223
75269
|
}
|
|
75224
75270
|
async function canvasFileFor(opts) {
|
|
75225
|
-
if (opts.file) return { canvasPath:
|
|
75271
|
+
if (opts.file) return { canvasPath: path30.resolve(opts.file) };
|
|
75226
75272
|
if (!opts.slug) failPreflight3("missing_canvas", "Pass the canvas file, or --slug <slug> to run a creative by name.");
|
|
75227
75273
|
const cwd = process.cwd();
|
|
75228
75274
|
try {
|
|
@@ -75243,15 +75289,27 @@ async function executeCanvasRun(opts) {
|
|
|
75243
75289
|
const canvasRoot = canvasOutputsRoot(filePath);
|
|
75244
75290
|
const creativeSlug = creativeSlugFromCanvasPath(filePath) ?? void 0;
|
|
75245
75291
|
if (creativeSlug) await clearLegacyDirtyMarker(canvasRoot, creativeSlug);
|
|
75292
|
+
const turnId = optionalTurnId();
|
|
75293
|
+
const turnCache = opts.cacheDir ?? path30.join(canvasRoot, ".cache");
|
|
75294
|
+
if (creativeSlug && (opts.autoFixRound ?? 0) === 0) {
|
|
75295
|
+
const refusal = turnRenderRefusal(await readTurnRenders(turnCache), turnId, creativeSlug);
|
|
75296
|
+
if (refusal) {
|
|
75297
|
+
process.stderr.write(
|
|
75298
|
+
`${JSON.stringify({ ok: false, error: { code: refusal.code, message: refusal.message, fix: refusal.fix } }, null, 2)}
|
|
75299
|
+
`
|
|
75300
|
+
);
|
|
75301
|
+
process.exit(2);
|
|
75302
|
+
}
|
|
75303
|
+
}
|
|
75246
75304
|
await wireProofBadges(
|
|
75247
|
-
|
|
75305
|
+
path30.join(path30.dirname(filePath), "prompt.json"),
|
|
75248
75306
|
fetchBrandLogo,
|
|
75249
75307
|
(line) => process.stderr.write(`${line}
|
|
75250
75308
|
`)
|
|
75251
75309
|
);
|
|
75252
75310
|
if ((opts.autoFixRound ?? 0) === 0) {
|
|
75253
75311
|
await wireNamedPeople(
|
|
75254
|
-
|
|
75312
|
+
path30.join(path30.dirname(filePath), "prompt.json"),
|
|
75255
75313
|
avatarOfNamedPerson,
|
|
75256
75314
|
(line) => process.stderr.write(`${line}
|
|
75257
75315
|
`)
|
|
@@ -75345,8 +75403,8 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75345
75403
|
});
|
|
75346
75404
|
const last = Object.fromEntries(
|
|
75347
75405
|
nodesWithRole(parsed, STATIC_ADAPT_FORMAT_ROLE).flatMap((n) => {
|
|
75348
|
-
const
|
|
75349
|
-
return
|
|
75406
|
+
const path68 = delivered.find((d) => d.format === formatOfAdaptId(n.id))?.path;
|
|
75407
|
+
return path68 ? [[n.id, path68]] : [];
|
|
75350
75408
|
})
|
|
75351
75409
|
);
|
|
75352
75410
|
parsed = withSizesFollowingFix(parsed, {
|
|
@@ -75443,7 +75501,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75443
75501
|
process.stdout.write(`[extend] placing ${opts.extendSizes.join(", ")} clear of what covers or cuts it, its margins painted
|
|
75444
75502
|
`);
|
|
75445
75503
|
}
|
|
75446
|
-
const cacheDir = opts.cacheDir ??
|
|
75504
|
+
const cacheDir = opts.cacheDir ?? path30.join(canvasRoot, ".cache");
|
|
75447
75505
|
const engine = createEngineFromEnv({
|
|
75448
75506
|
cacheDir,
|
|
75449
75507
|
outputsDir: opts.outputsDir ?? canvasRoot,
|
|
@@ -75466,7 +75524,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75466
75524
|
const canvasSha = sha256Hex(Buffer.from(canvasText));
|
|
75467
75525
|
const sourceSha = prepared.sourceSha;
|
|
75468
75526
|
const client = opts.record === false ? null : buildBackendClient();
|
|
75469
|
-
const outputsDir = opts.outputsDir ?
|
|
75527
|
+
const outputsDir = opts.outputsDir ? path30.resolve(opts.outputsDir) : canvasRoot;
|
|
75470
75528
|
const { runId, resumed, source, sameSource, concurrentRunId, rendering } = await resolveRunId({
|
|
75471
75529
|
explicitRunId: opts.runId,
|
|
75472
75530
|
adoptedRunId: adoptRunId,
|
|
@@ -75496,7 +75554,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75496
75554
|
}
|
|
75497
75555
|
const viewerUsage = [];
|
|
75498
75556
|
const viewerCheck = creativeSlug && isStaticAdCanvas(parsed) ? await viewerCheckBeforeRender({
|
|
75499
|
-
creativeDir:
|
|
75557
|
+
creativeDir: path30.dirname(filePath),
|
|
75500
75558
|
cacheDir,
|
|
75501
75559
|
slug: creativeSlug,
|
|
75502
75560
|
skip: viewerCheckSkip({
|
|
@@ -75507,7 +75565,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75507
75565
|
resumed: resumed && sameSource === true,
|
|
75508
75566
|
asked: opts.skipViewerCheck === true
|
|
75509
75567
|
}),
|
|
75510
|
-
lookups: liveViewerLookups(
|
|
75568
|
+
lookups: liveViewerLookups(path30.dirname(canvasRoot)),
|
|
75511
75569
|
model: VIEWER_CHECK_MODEL,
|
|
75512
75570
|
ask: gatewayViewerModel(VIEWER_CHECK_MODEL, (usage) => viewerUsage.push(usage))
|
|
75513
75571
|
}) : null;
|
|
@@ -75556,7 +75614,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75556
75614
|
if (snapshot && creativeSlug) await recordSnapshot(canvasRoot, creativeSlug, snapshot);
|
|
75557
75615
|
const recordMeta = {
|
|
75558
75616
|
creativeSlug,
|
|
75559
|
-
canvasPath:
|
|
75617
|
+
canvasPath: path30.relative(path30.dirname(canvasRoot), filePath) || void 0,
|
|
75560
75618
|
canvasSha,
|
|
75561
75619
|
// The fingerprint the dashboard compares against the current source to flag
|
|
75562
75620
|
// "edited since last render": the same `computeSourceSha` the edit-sync posts,
|
|
@@ -75644,7 +75702,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75644
75702
|
brief: opts.brief?.trim() || void 0,
|
|
75645
75703
|
onSpend: (usage) => reviewUsage.push(usage)
|
|
75646
75704
|
}) : null;
|
|
75647
|
-
const brandKit = reviewable ? null : await readStaticBrandKit(await findBrandRoot(
|
|
75705
|
+
const brandKit = reviewable ? null : await readStaticBrandKit(await findBrandRoot(path30.dirname(filePath)) ?? process.cwd());
|
|
75648
75706
|
const runShape = {
|
|
75649
75707
|
fix: fixInstruction !== void 0,
|
|
75650
75708
|
useVariant: opts.useVariant !== void 0,
|
|
@@ -75697,7 +75755,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75697
75755
|
] : [];
|
|
75698
75756
|
const findings = staticReview ? staticReview.review : review;
|
|
75699
75757
|
const ready = staticReview ? staticReview.verdict === "ready" || staticReview.verdict === "ready_with_notes" : review !== null && !review.some((f) => f.severity === "blocking");
|
|
75700
|
-
const definitionStatus = ready && creativeSlug ? await advanceDefinitionStatus(
|
|
75758
|
+
const definitionStatus = ready && creativeSlug ? await advanceDefinitionStatus(path30.join(path30.dirname(filePath), "_definition.md"), "rendered") : null;
|
|
75701
75759
|
if (poster) {
|
|
75702
75760
|
await flushBilled(
|
|
75703
75761
|
poster,
|
|
@@ -75781,28 +75839,36 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75781
75839
|
...opts.autoFixKind === "relayout" && opts.relayEdits ? { relayAsked: Object.fromEntries(Object.entries(opts.relayEdits).map(([id, asked]) => [id, [asked]])) } : {}
|
|
75782
75840
|
} : step.kind === "regenerate" ? { regenerate: step.ids.join(","), relayNote: step.note, ...step.extend ? { extendSizes: step.extend } : {} } : {}
|
|
75783
75841
|
};
|
|
75784
|
-
} else
|
|
75785
|
-
|
|
75786
|
-
|
|
75787
|
-
|
|
75788
|
-
|
|
75789
|
-
|
|
75790
|
-
|
|
75791
|
-
|
|
75792
|
-
|
|
75793
|
-
|
|
75794
|
-
|
|
75795
|
-
|
|
75796
|
-
|
|
75797
|
-
|
|
75798
|
-
|
|
75799
|
-
|
|
75800
|
-
|
|
75801
|
-
|
|
75802
|
-
|
|
75803
|
-
|
|
75842
|
+
} else {
|
|
75843
|
+
if (creativeSlug && turnId && staticReview) {
|
|
75844
|
+
await writeTurnRenders(
|
|
75845
|
+
turnCache,
|
|
75846
|
+
recordTurnRender(await readTurnRenders(turnCache), turnId, creativeSlug, staticReview.verdict)
|
|
75847
|
+
);
|
|
75848
|
+
}
|
|
75849
|
+
process.stdout.write(
|
|
75850
|
+
`${JSON.stringify(
|
|
75851
|
+
{
|
|
75852
|
+
ok: true,
|
|
75853
|
+
outputs_dir: result.outputs_dir,
|
|
75854
|
+
output: result.output,
|
|
75855
|
+
stats: result.stats,
|
|
75856
|
+
...findings ? { review: findings } : {},
|
|
75857
|
+
...staticReview?.judge?.length ? { judge: staticReview.judge } : {},
|
|
75858
|
+
...definitionStatus === "updated" ? { definition_status: "rendered" } : {},
|
|
75859
|
+
...hints2.length ? { hints: hints2 } : {},
|
|
75860
|
+
// Last, so a `| tail` keeps them: production agents cut them off and re-ran to get them back.
|
|
75861
|
+
// A static ad whose review found a blocking problem is finished but NOT ready.
|
|
75862
|
+
...staticReview ? { verdict: staticReview.verdict } : {},
|
|
75863
|
+
run_id: result.run_id,
|
|
75864
|
+
files: finalFiles(result.output, finalLabels?.labels)
|
|
75865
|
+
},
|
|
75866
|
+
null,
|
|
75867
|
+
2
|
|
75868
|
+
)}
|
|
75804
75869
|
`
|
|
75805
|
-
|
|
75870
|
+
);
|
|
75871
|
+
}
|
|
75806
75872
|
} catch (e) {
|
|
75807
75873
|
const failedPayload = async (message) => {
|
|
75808
75874
|
await readerSpend.drained();
|
|
@@ -75929,14 +75995,14 @@ var rerunCommand = defineCommand140({
|
|
|
75929
75995
|
});
|
|
75930
75996
|
|
|
75931
75997
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
75932
|
-
import { mkdir as
|
|
75933
|
-
import
|
|
75998
|
+
import { mkdir as mkdir12, readFile as readFile29, writeFile as writeFile18 } from "fs/promises";
|
|
75999
|
+
import path36 from "path";
|
|
75934
76000
|
import { defineCommand as defineCommand143 } from "citty";
|
|
75935
76001
|
|
|
75936
76002
|
// src/commands/canvas/creative-limit.ts
|
|
75937
76003
|
import { existsSync as existsSync7 } from "fs";
|
|
75938
|
-
import { mkdir as
|
|
75939
|
-
import
|
|
76004
|
+
import { mkdir as mkdir10, readdir as readdir13, writeFile as writeFile16 } from "fs/promises";
|
|
76005
|
+
import path31 from "path";
|
|
75940
76006
|
var NEW_CREATIVES_PER_CHAT = 10;
|
|
75941
76007
|
function claimNewCreative(made, slug) {
|
|
75942
76008
|
if (made.includes(slug)) return { allowed: true, count: made.length };
|
|
@@ -75944,7 +76010,7 @@ function claimNewCreative(made, slug) {
|
|
|
75944
76010
|
return { allowed: true, count: made.length + 1 };
|
|
75945
76011
|
}
|
|
75946
76012
|
function chatDir(canvasRoot, chatId) {
|
|
75947
|
-
return
|
|
76013
|
+
return path31.join(canvasRoot, ".creative-limit", (chatId ?? "no-chat").replace(/[^A-Za-z0-9_-]/g, "_"));
|
|
75948
76014
|
}
|
|
75949
76015
|
async function madeInChat(dir) {
|
|
75950
76016
|
return readdir13(dir).catch(() => []);
|
|
@@ -75956,8 +76022,8 @@ async function claimNewCreativeInWorkspace(canvasRoot, slug, chatId, definitionP
|
|
|
75956
76022
|
const claim = claimNewCreative(made, slug);
|
|
75957
76023
|
if (claim.allowed && !made.includes(slug)) {
|
|
75958
76024
|
try {
|
|
75959
|
-
await
|
|
75960
|
-
await
|
|
76025
|
+
await mkdir10(dir, { recursive: true });
|
|
76026
|
+
await writeFile16(path31.join(dir, slug), "", { flag: "wx" });
|
|
75961
76027
|
} catch {
|
|
75962
76028
|
}
|
|
75963
76029
|
}
|
|
@@ -75978,7 +76044,7 @@ async function enforceNewCreativeLimit(slug, canvasPath) {
|
|
|
75978
76044
|
canvasOutputsRoot(canvasPath),
|
|
75979
76045
|
slug,
|
|
75980
76046
|
optionalChatId() ?? null,
|
|
75981
|
-
|
|
76047
|
+
path31.join(path31.dirname(path31.resolve(canvasPath)), "_definition.md")
|
|
75982
76048
|
);
|
|
75983
76049
|
if (claim.allowed) return;
|
|
75984
76050
|
process.stderr.write(`${JSON.stringify(creativeLimitRefusal(slug), null, 2)}
|
|
@@ -75987,22 +76053,22 @@ async function enforceNewCreativeLimit(slug, canvasPath) {
|
|
|
75987
76053
|
}
|
|
75988
76054
|
|
|
75989
76055
|
// src/commands/canvas/scaffold-common.ts
|
|
75990
|
-
import { access, readFile as
|
|
76056
|
+
import { access, readFile as readFile26 } from "fs/promises";
|
|
75991
76057
|
|
|
75992
76058
|
// src/commands/canvas/composition-path.ts
|
|
75993
76059
|
import { existsSync as existsSync8 } from "fs";
|
|
75994
|
-
import
|
|
76060
|
+
import path32 from "path";
|
|
75995
76061
|
function resolveShippedCanvasDir(name, startDir, exists3 = existsSync8, maxDepth = 8) {
|
|
75996
|
-
const rel =
|
|
76062
|
+
const rel = path32.join("canvas", name);
|
|
75997
76063
|
let dir = startDir;
|
|
75998
76064
|
for (let i = 0; i < maxDepth; i++) {
|
|
75999
|
-
const candidate =
|
|
76000
|
-
if (exists3(
|
|
76001
|
-
const parent =
|
|
76065
|
+
const candidate = path32.join(dir, rel);
|
|
76066
|
+
if (exists3(path32.join(candidate, "meta.json"))) return candidate;
|
|
76067
|
+
const parent = path32.dirname(dir);
|
|
76002
76068
|
if (parent === dir) break;
|
|
76003
76069
|
dir = parent;
|
|
76004
76070
|
}
|
|
76005
|
-
return
|
|
76071
|
+
return path32.resolve(startDir, "../../../", rel);
|
|
76006
76072
|
}
|
|
76007
76073
|
|
|
76008
76074
|
// src/commands/canvas/scaffold-common.ts
|
|
@@ -76019,7 +76085,7 @@ function resolveModel(kind, requested, fallback) {
|
|
|
76019
76085
|
}
|
|
76020
76086
|
async function loadAssetText(ref2, label) {
|
|
76021
76087
|
const r = ref2;
|
|
76022
|
-
if (typeof r?.path === "string") return
|
|
76088
|
+
if (typeof r?.path === "string") return readFile26(r.path, "utf8");
|
|
76023
76089
|
if (typeof r?.url === "string") {
|
|
76024
76090
|
const res = await fetch(r.url);
|
|
76025
76091
|
if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
|
|
@@ -76167,16 +76233,16 @@ ${doc.toString()}---${raw.slice(match[0].length)}`;
|
|
|
76167
76233
|
}
|
|
76168
76234
|
|
|
76169
76235
|
// src/commands/canvas/scaffold-static-ad-paths.ts
|
|
76170
|
-
import
|
|
76236
|
+
import path33 from "path";
|
|
76171
76237
|
function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd(), slug) {
|
|
76172
76238
|
const file = rawFile.trim();
|
|
76173
76239
|
const imageIsUrl = /^https?:\/\//i.test(file);
|
|
76174
|
-
const imageSource = imageIsUrl ? file :
|
|
76175
|
-
const outPath = out ?
|
|
76176
|
-
const blueprintPath2 =
|
|
76177
|
-
const creativeDir = slug ?
|
|
76178
|
-
const definitionPath = creativeDir ?
|
|
76179
|
-
const referencesDir = creativeDir ?
|
|
76240
|
+
const imageSource = imageIsUrl ? file : path33.resolve(cwd, file);
|
|
76241
|
+
const outPath = out ? path33.resolve(cwd, out) : slug ? path33.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path33.join(cwd, "static-ad.canvas.json") : path33.join(path33.dirname(imageSource), "static-ad.canvas.json");
|
|
76242
|
+
const blueprintPath2 = path33.join(path33.dirname(outPath), "prompt.json");
|
|
76243
|
+
const creativeDir = slug ? path33.dirname(outPath) : null;
|
|
76244
|
+
const definitionPath = creativeDir ? path33.join(creativeDir, "_definition.md") : null;
|
|
76245
|
+
const referencesDir = creativeDir ? path33.join(creativeDir, "references") : null;
|
|
76180
76246
|
return { imageIsUrl, imageSource, outPath, blueprintPath: blueprintPath2, creativeDir, definitionPath, referencesDir };
|
|
76181
76247
|
}
|
|
76182
76248
|
var SCAFFOLD_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
@@ -76186,8 +76252,8 @@ function isValidScaffoldSlug(slug) {
|
|
|
76186
76252
|
}
|
|
76187
76253
|
|
|
76188
76254
|
// src/commands/canvas/sync-definition.ts
|
|
76189
|
-
import { readdir as readdir14, readFile as
|
|
76190
|
-
import
|
|
76255
|
+
import { readdir as readdir14, readFile as readFile27, stat as stat5 } from "fs/promises";
|
|
76256
|
+
import path34 from "path";
|
|
76191
76257
|
import { defineCommand as defineCommand141 } from "citty";
|
|
76192
76258
|
|
|
76193
76259
|
// src/commands/canvas/definition-graph.ts
|
|
@@ -76244,14 +76310,14 @@ function buildDefinitionSyncPayload(input, graph, chatId) {
|
|
|
76244
76310
|
}
|
|
76245
76311
|
async function readDefinitionSourceFields(canvasPath) {
|
|
76246
76312
|
try {
|
|
76247
|
-
return parseDefinitionSourceFields(await
|
|
76313
|
+
return parseDefinitionSourceFields(await readFile27(path34.join(path34.dirname(canvasPath), "_definition.md"), "utf8"));
|
|
76248
76314
|
} catch {
|
|
76249
76315
|
return {};
|
|
76250
76316
|
}
|
|
76251
76317
|
}
|
|
76252
76318
|
async function readDefinitionStrategyFields(canvasPath) {
|
|
76253
76319
|
try {
|
|
76254
|
-
return parseDefinitionStrategyFields(await
|
|
76320
|
+
return parseDefinitionStrategyFields(await readFile27(path34.join(path34.dirname(canvasPath), "_definition.md"), "utf8"));
|
|
76255
76321
|
} catch {
|
|
76256
76322
|
return {};
|
|
76257
76323
|
}
|
|
@@ -76274,15 +76340,15 @@ async function syncCreativeDefinitionBestEffort(input) {
|
|
|
76274
76340
|
}
|
|
76275
76341
|
}
|
|
76276
76342
|
async function resolveCanvasPath(inputPath) {
|
|
76277
|
-
const resolved =
|
|
76343
|
+
const resolved = path34.resolve(inputPath);
|
|
76278
76344
|
let dir = resolved;
|
|
76279
76345
|
try {
|
|
76280
76346
|
if ((await stat5(resolved)).isFile()) {
|
|
76281
76347
|
if (resolved.endsWith(".canvas.json")) return resolved;
|
|
76282
|
-
dir =
|
|
76348
|
+
dir = path34.dirname(resolved);
|
|
76283
76349
|
}
|
|
76284
76350
|
} catch {
|
|
76285
|
-
dir = resolved.endsWith(".canvas.json") ?
|
|
76351
|
+
dir = resolved.endsWith(".canvas.json") ? path34.dirname(resolved) : resolved;
|
|
76286
76352
|
}
|
|
76287
76353
|
let entries;
|
|
76288
76354
|
try {
|
|
@@ -76293,7 +76359,7 @@ async function resolveCanvasPath(inputPath) {
|
|
|
76293
76359
|
const canvases = entries.filter((name) => name.endsWith(".canvas.json"));
|
|
76294
76360
|
const slug = creativeSlugFromCanvasPath(`${dir}/x/`);
|
|
76295
76361
|
const chosen = (slug ? canvases.find((name) => name === `${slug}.canvas.json`) : void 0) ?? canvases[0];
|
|
76296
|
-
return chosen ?
|
|
76362
|
+
return chosen ? path34.join(dir, chosen) : null;
|
|
76297
76363
|
}
|
|
76298
76364
|
var syncDefinitionCommand = defineCommand141({
|
|
76299
76365
|
meta: {
|
|
@@ -76563,32 +76629,32 @@ function mergeReferenceIntoBlueprint(blueprint, reference) {
|
|
|
76563
76629
|
}
|
|
76564
76630
|
|
|
76565
76631
|
// src/commands/winning-ads/fit.ts
|
|
76566
|
-
import { mkdir as
|
|
76567
|
-
import
|
|
76632
|
+
import { mkdir as mkdir11, readdir as readdir15, readFile as readFile28, writeFile as writeFile17 } from "fs/promises";
|
|
76633
|
+
import path35 from "path";
|
|
76568
76634
|
import { defineCommand as defineCommand142 } from "citty";
|
|
76569
76635
|
import sharp4 from "sharp";
|
|
76570
|
-
import { z as
|
|
76571
|
-
var Layers =
|
|
76572
|
-
disguise:
|
|
76573
|
-
voice:
|
|
76574
|
-
trigger:
|
|
76575
|
-
attention:
|
|
76576
|
-
logic:
|
|
76636
|
+
import { z as z68 } from "zod";
|
|
76637
|
+
var Layers = z68.object({
|
|
76638
|
+
disguise: z68.string().optional(),
|
|
76639
|
+
voice: z68.string().optional(),
|
|
76640
|
+
trigger: z68.string().optional(),
|
|
76641
|
+
attention: z68.string().optional(),
|
|
76642
|
+
logic: z68.string().optional()
|
|
76577
76643
|
}).strip();
|
|
76578
|
-
var FitAnswerSchema =
|
|
76644
|
+
var FitAnswerSchema = z68.object({
|
|
76579
76645
|
why_it_wins: Layers.default({}),
|
|
76580
|
-
load_bearing:
|
|
76581
|
-
|
|
76582
|
-
element:
|
|
76583
|
-
needs:
|
|
76584
|
-
have:
|
|
76585
|
-
ours:
|
|
76646
|
+
load_bearing: z68.array(
|
|
76647
|
+
z68.object({
|
|
76648
|
+
element: z68.string().trim().min(1),
|
|
76649
|
+
needs: z68.string().optional(),
|
|
76650
|
+
have: z68.enum(["have", "adapt", "missing"]),
|
|
76651
|
+
ours: z68.string().optional()
|
|
76586
76652
|
}).strip()
|
|
76587
76653
|
).default([]),
|
|
76588
|
-
verdict:
|
|
76589
|
-
transfer_note:
|
|
76590
|
-
brand_presence:
|
|
76591
|
-
recognition:
|
|
76654
|
+
verdict: z68.enum(["use", "adapt", "reject"]),
|
|
76655
|
+
transfer_note: z68.string().optional(),
|
|
76656
|
+
brand_presence: z68.enum(["native", "standard"]).optional(),
|
|
76657
|
+
recognition: z68.string().optional()
|
|
76592
76658
|
});
|
|
76593
76659
|
function buildFitPrompt(input) {
|
|
76594
76660
|
const lib = input.library;
|
|
@@ -76649,33 +76715,33 @@ var MAX_DOC_CHARS = 1500;
|
|
|
76649
76715
|
var MAX_ENTRY_CHARS = 400;
|
|
76650
76716
|
var KNOWLEDGE_COLLECTIONS = ["offers", "products", "personas"];
|
|
76651
76717
|
function readText(file) {
|
|
76652
|
-
return
|
|
76718
|
+
return readFile28(file, "utf8").catch(() => null);
|
|
76653
76719
|
}
|
|
76654
76720
|
async function readKnowledge(root) {
|
|
76655
76721
|
const parts = [];
|
|
76656
|
-
const company = await readText(
|
|
76722
|
+
const company = await readText(path35.join(root, "COMPANY.md"));
|
|
76657
76723
|
if (company) parts.push(`COMPANY.md:
|
|
76658
76724
|
${company.slice(0, MAX_DOC_CHARS)}`);
|
|
76659
|
-
const brand = await readText(
|
|
76725
|
+
const brand = await readText(path35.join(root, "src", "brand", "BRAND.md"));
|
|
76660
76726
|
if (brand) parts.push(`BRAND.md (start):
|
|
76661
76727
|
${brand.slice(0, MAX_DOC_CHARS)}`);
|
|
76662
76728
|
for (const collection of KNOWLEDGE_COLLECTIONS) {
|
|
76663
|
-
const dir =
|
|
76729
|
+
const dir = path35.join(root, "src", "content", collection);
|
|
76664
76730
|
const files = (await readdir15(dir, { recursive: true }).catch(() => [])).filter(
|
|
76665
76731
|
(f) => f.endsWith(".md")
|
|
76666
76732
|
);
|
|
76667
76733
|
for (const file of files.sort().slice(0, 12)) {
|
|
76668
|
-
const text3 = await readText(
|
|
76734
|
+
const text3 = await readText(path35.join(dir, file));
|
|
76669
76735
|
if (text3) parts.push(`${collection}/${file}:
|
|
76670
76736
|
${text3.slice(0, MAX_ENTRY_CHARS)}`);
|
|
76671
76737
|
}
|
|
76672
76738
|
}
|
|
76673
76739
|
return parts.join("\n\n");
|
|
76674
76740
|
}
|
|
76675
|
-
var ReviewSchema =
|
|
76676
|
-
reviewText:
|
|
76677
|
-
sourceType:
|
|
76678
|
-
rating:
|
|
76741
|
+
var ReviewSchema = z68.object({
|
|
76742
|
+
reviewText: z68.string(),
|
|
76743
|
+
sourceType: z68.string().optional(),
|
|
76744
|
+
rating: z68.number().optional()
|
|
76679
76745
|
});
|
|
76680
76746
|
function reviewsForFit(data) {
|
|
76681
76747
|
const list = Array.isArray(data) ? data : [];
|
|
@@ -76697,7 +76763,7 @@ async function readReviews() {
|
|
|
76697
76763
|
}
|
|
76698
76764
|
}
|
|
76699
76765
|
function fitCachePath(root, platform, adId) {
|
|
76700
|
-
return
|
|
76766
|
+
return path35.join(root, "canvas", ".cache", "fit", `${platform}-${adId.replace(/[^a-zA-Z0-9_-]/g, "_")}.json`);
|
|
76701
76767
|
}
|
|
76702
76768
|
async function readCachedFit(root, platform, adId) {
|
|
76703
76769
|
const raw = await readText(fitCachePath(root, platform, adId));
|
|
@@ -76752,8 +76818,8 @@ async function fitOnce(root, platform, adId, ask) {
|
|
|
76752
76818
|
const fit = await ask();
|
|
76753
76819
|
if (!fit) return null;
|
|
76754
76820
|
const file = fitCachePath(root, platform, adId);
|
|
76755
|
-
await
|
|
76756
|
-
await
|
|
76821
|
+
await mkdir11(path35.dirname(file), { recursive: true });
|
|
76822
|
+
await writeFile17(file, `${JSON.stringify(fit, null, 2)}
|
|
76757
76823
|
`, "utf8");
|
|
76758
76824
|
return fit;
|
|
76759
76825
|
}
|
|
@@ -76791,7 +76857,7 @@ async function fitReference(root, platform, adId) {
|
|
|
76791
76857
|
const prompt = buildFitPrompt({ library: libraryRead(ad), knowledge: knowledge2 });
|
|
76792
76858
|
const spent = [];
|
|
76793
76859
|
const answer = await askFit(jpeg, prompt, (u) => spent.push(u));
|
|
76794
|
-
await holdUnbilledSpend(
|
|
76860
|
+
await holdUnbilledSpend(path35.join(root, "canvas", ".cache"), spent);
|
|
76795
76861
|
return answer;
|
|
76796
76862
|
});
|
|
76797
76863
|
if (fit) return { ok: true, fit };
|
|
@@ -76844,7 +76910,7 @@ var fitCommand = defineCommand142({
|
|
|
76844
76910
|
|
|
76845
76911
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
76846
76912
|
function toCanvasRelative2(canvasDir, file) {
|
|
76847
|
-
const rel =
|
|
76913
|
+
const rel = path36.relative(canvasDir, file).split(path36.sep).join("/");
|
|
76848
76914
|
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
76849
76915
|
}
|
|
76850
76916
|
async function uploadSourceAsReference(source, isUrl, client) {
|
|
@@ -76856,7 +76922,7 @@ async function uploadSourceAsReference(source, isUrl, client) {
|
|
|
76856
76922
|
throw new Error(`failed to download source image (${e instanceof Error ? e.message : String(e)})`);
|
|
76857
76923
|
}
|
|
76858
76924
|
} else {
|
|
76859
|
-
bytes = await
|
|
76925
|
+
bytes = await readFile29(source);
|
|
76860
76926
|
}
|
|
76861
76927
|
const safe = await toModelSafeImage(bytes);
|
|
76862
76928
|
const sha2562 = sha256Hex(safe.bytes);
|
|
@@ -77136,25 +77202,25 @@ async function lookupReference(args8) {
|
|
|
77136
77202
|
}
|
|
77137
77203
|
async function scaffoldFromSpec(args8, slug) {
|
|
77138
77204
|
if (!slug) return fail4("missing_slug", "--spec needs --slug: an ad started from a spec is a creative.");
|
|
77139
|
-
const specPath =
|
|
77205
|
+
const specPath = path36.resolve(process.cwd(), String(args8.spec));
|
|
77140
77206
|
let started;
|
|
77141
77207
|
try {
|
|
77142
|
-
started = readAdSpec(JSON.parse(await
|
|
77208
|
+
started = readAdSpec(JSON.parse(await readFile29(specPath, "utf8")));
|
|
77143
77209
|
} catch (e) {
|
|
77144
77210
|
return fail4("invalid_spec", `${specPath} is not an ad spec: ${e instanceof Error ? e.message : String(e)}`);
|
|
77145
77211
|
}
|
|
77146
|
-
const outPath =
|
|
77147
|
-
const dir =
|
|
77148
|
-
const blueprintPath2 =
|
|
77149
|
-
const definitionPath =
|
|
77212
|
+
const outPath = path36.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`);
|
|
77213
|
+
const dir = path36.dirname(outPath);
|
|
77214
|
+
const blueprintPath2 = path36.join(dir, "prompt.json");
|
|
77215
|
+
const definitionPath = path36.join(dir, "_definition.md");
|
|
77150
77216
|
await enforceNewCreativeLimit(slug, outPath);
|
|
77151
|
-
const existingDefinition = await fileExists(definitionPath) ? await
|
|
77217
|
+
const existingDefinition = await fileExists(definitionPath) ? await readFile29(definitionPath, "utf8") : null;
|
|
77152
77218
|
const who = await scaffoldViewer(args8, existingDefinition, liveViewerLookups(process.cwd()).adSetViewer);
|
|
77153
77219
|
if (!who.ok) return fail4("invalid_viewer", who.message);
|
|
77154
|
-
await
|
|
77220
|
+
await mkdir12(dir, { recursive: true });
|
|
77155
77221
|
const workspaceRoot = await findBrandRoot(dir) ?? process.cwd();
|
|
77156
77222
|
const kit = await readStaticBrandKit(workspaceRoot);
|
|
77157
|
-
const market = who.viewer?.market ?? marketFromCompanyDoc(await
|
|
77223
|
+
const market = who.viewer?.market ?? marketFromCompanyDoc(await readFile29(path36.join(workspaceRoot, "COMPANY.md"), "utf8").catch(() => null));
|
|
77158
77224
|
const brand = brandBlock(kit);
|
|
77159
77225
|
const platform = resolvePlatform(args8.platform ? String(args8.platform) : void 0);
|
|
77160
77226
|
const placementsResult = resolvePlacements(args8.placements ? String(args8.placements) : void 0, platform);
|
|
@@ -77176,18 +77242,18 @@ async function scaffoldFromSpec(args8, slug) {
|
|
|
77176
77242
|
logo: Boolean(kit.logo),
|
|
77177
77243
|
...market ? { viewer: { ...started.viewer ?? { who: "the people this ad is for" }, market } } : {}
|
|
77178
77244
|
};
|
|
77179
|
-
await
|
|
77245
|
+
await writeFile18(blueprintPath2, `${JSON.stringify(spec, null, 2)}
|
|
77180
77246
|
`, "utf8");
|
|
77181
77247
|
const canvas = scaffoldStaticAd({}, [], { ...opts, spec });
|
|
77182
77248
|
const validation = await validateScaffoldedStaticAd(canvas, dir);
|
|
77183
77249
|
if (validation.blocking.length > 0) return fail4("validation", validation.blocking.map((i) => i.message).join("; "));
|
|
77184
|
-
await
|
|
77250
|
+
await writeFile18(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
77185
77251
|
`, "utf8");
|
|
77186
77252
|
const title = args8.title ? String(args8.title) : titleFromSlug(slug);
|
|
77187
77253
|
const recorded = who.record ? { targetAdSet: who.targetAdSet, viewer: who.viewer } : {};
|
|
77188
77254
|
if (existingDefinition === null) {
|
|
77189
77255
|
const formats = resolveFormatList(report.output_formats);
|
|
77190
|
-
await
|
|
77256
|
+
await writeFile18(definitionPath, buildCreativeDefinition({ title, kind: "static", platform, formats, ...recorded }), "utf8");
|
|
77191
77257
|
}
|
|
77192
77258
|
await syncCreativeDefinitionBestEffort({
|
|
77193
77259
|
slug,
|
|
@@ -77301,14 +77367,14 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77301
77367
|
}
|
|
77302
77368
|
const { imageIsUrl, imageSource, outPath, blueprintPath: blueprintPath2, definitionPath, referencesDir } = resolveScaffoldStaticAdPaths(sourceFile, args8.out ? String(args8.out) : void 0, process.cwd(), slug);
|
|
77303
77369
|
if (slug) await enforceNewCreativeLimit(slug, outPath);
|
|
77304
|
-
const existingDefinition = definitionPath && await fileExists(definitionPath) ? await
|
|
77370
|
+
const existingDefinition = definitionPath && await fileExists(definitionPath) ? await readFile29(definitionPath, "utf8") : null;
|
|
77305
77371
|
const who = await scaffoldViewer(args8, existingDefinition, liveViewerLookups(process.cwd()).adSetViewer);
|
|
77306
77372
|
if (!who.ok) return fail4("invalid_viewer", who.message);
|
|
77307
77373
|
const fitRoot = findWorkspaceRoot(process.cwd()) ?? process.cwd();
|
|
77308
77374
|
const fit = reference || lookup.status === "unavailable" ? await readCachedFit(fitRoot, reference?.platform ?? (lookup.status === "unavailable" ? lookup.platform : "meta"), reference?.ad_id ?? (lookup.status === "unavailable" ? lookup.adId : "")) : null;
|
|
77309
77375
|
const fitProblems = fit ? fitHints(fit, reference?.ad_id ?? "").filter((h) => h.startsWith("Pick another reference")) : [];
|
|
77310
77376
|
if (fitProblems.length > 0) return fail4("concept_does_not_fit", fitProblems.join(" "));
|
|
77311
|
-
await
|
|
77377
|
+
await mkdir12(path36.dirname(outPath), { recursive: true });
|
|
77312
77378
|
let models;
|
|
77313
77379
|
try {
|
|
77314
77380
|
models = resolveModels(args8);
|
|
@@ -77342,10 +77408,10 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77342
77408
|
const persona = existingDefinition?.match(/^persona:\s*"?([^"\n]+)"?\s*$/m)?.[1];
|
|
77343
77409
|
const elements = castAvatarsOnStaticElements(vision.elements, cast.avatars, persona);
|
|
77344
77410
|
const uncast = staticAvatarsNotCast(elements, cast.handles);
|
|
77345
|
-
const canvasDir =
|
|
77411
|
+
const canvasDir = path36.dirname(outPath);
|
|
77346
77412
|
const workspaceRoot = await findBrandRoot(canvasDir) ?? process.cwd();
|
|
77347
77413
|
const kit = await readStaticBrandKit(workspaceRoot);
|
|
77348
|
-
const market = who.viewer?.market ?? marketFromCompanyDoc(await
|
|
77414
|
+
const market = who.viewer?.market ?? marketFromCompanyDoc(await readFile29(path36.join(workspaceRoot, "COMPANY.md"), "utf8").catch(() => null));
|
|
77349
77415
|
const brand = brandBlock(kit);
|
|
77350
77416
|
let canvasImagePath = imageSource;
|
|
77351
77417
|
let canvasImageIsUrl = imageIsUrl;
|
|
@@ -77395,7 +77461,7 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77395
77461
|
...fit ? { fit: conceptFromFit(fit, reference?.ad_id ?? "") } : {},
|
|
77396
77462
|
...market ? { market } : {}
|
|
77397
77463
|
});
|
|
77398
|
-
await
|
|
77464
|
+
await writeFile18(blueprintPath2, `${JSON.stringify(spec, null, 2)}
|
|
77399
77465
|
`, "utf8");
|
|
77400
77466
|
conceptNotes = conceptProblems(spec).map(
|
|
77401
77467
|
(p) => `CONCEPT: ${p} Then \`baker canvas run\`; until then it refuses to render.`
|
|
@@ -77409,7 +77475,7 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77409
77475
|
} catch (e) {
|
|
77410
77476
|
return fail4("scaffold", e instanceof Error ? e.message : String(e));
|
|
77411
77477
|
}
|
|
77412
|
-
const validation = await validateScaffoldedStaticAd(canvas,
|
|
77478
|
+
const validation = await validateScaffoldedStaticAd(canvas, path36.dirname(outPath));
|
|
77413
77479
|
if (validation.blocking.length > 0) {
|
|
77414
77480
|
process.stderr.write(
|
|
77415
77481
|
`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.blocking } }, null, 2)}
|
|
@@ -77417,7 +77483,7 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77417
77483
|
);
|
|
77418
77484
|
process.exit(2);
|
|
77419
77485
|
}
|
|
77420
|
-
await
|
|
77486
|
+
await writeFile18(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
77421
77487
|
`, "utf8");
|
|
77422
77488
|
const sourceFields = reference ? {
|
|
77423
77489
|
sourceAdId: reference.ad_id,
|
|
@@ -77430,10 +77496,10 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77430
77496
|
} : {};
|
|
77431
77497
|
const recorded = who.record ? { targetAdSet: who.targetAdSet, viewer: who.viewer } : {};
|
|
77432
77498
|
if (definitionPath && existingDefinition !== null && who.record) {
|
|
77433
|
-
await
|
|
77499
|
+
await writeFile18(definitionPath, setDefinitionViewer(existingDefinition, recorded), "utf8");
|
|
77434
77500
|
}
|
|
77435
77501
|
if (definitionPath && existingDefinition === null) {
|
|
77436
|
-
await
|
|
77502
|
+
await writeFile18(
|
|
77437
77503
|
definitionPath,
|
|
77438
77504
|
buildCreativeDefinition({
|
|
77439
77505
|
title: args8.title ? String(args8.title) : titleFromSlug(slug ?? ""),
|
|
@@ -77506,7 +77572,7 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77506
77572
|
run_estimated_credits: validation.estimatedCredits
|
|
77507
77573
|
},
|
|
77508
77574
|
checklist: {
|
|
77509
|
-
edit_prompt: `Edit ${
|
|
77575
|
+
edit_prompt: `Edit ${path36.basename(blueprintPath2)}: it is the ad spec, read from your image, to rewrite into the ad you want. \`viewer\` (who sees it and what they already know), \`message\` (the one thing it tells them), \`copy\` (each line, \`in_scene\` or \`designed\`), \`scene\`, \`references\` (one picture each, by role), \`colors\` (each tied to an object, from the brand) and an optional \`direction\`. You never write a model prompt: every render prompt is compiled from this file and shown on its node.${reference ? " Its `reference` block is the library's read of the source ad (container, layout, mechanism, offer, text blocks): keep it as the account of why the ad won, and rebuild it for our brand." : ""}`,
|
|
77510
77576
|
mechanism: report.winning_mechanisms,
|
|
77511
77577
|
rebuild_the_mechanism: "`mechanism` is WHY the source ad works. Rebuild each device for our brand in prompt.json's copy and scene (a rhyme stays a rhyme in our words, a before/after stays a before/after of our product); never copy the source's literal copy.",
|
|
77512
77578
|
assets_to_supply: report.elements,
|
|
@@ -77568,25 +77634,25 @@ function clientLogoMissNote(decision, domain) {
|
|
|
77568
77634
|
}
|
|
77569
77635
|
|
|
77570
77636
|
// src/commands/canvas/scaffold-ad.ts
|
|
77571
|
-
import { copyFile, cp, mkdir as
|
|
77572
|
-
import
|
|
77637
|
+
import { copyFile, cp, mkdir as mkdir13, readFile as readFile31, stat as stat6, writeFile as writeFile20 } from "fs/promises";
|
|
77638
|
+
import path38 from "path";
|
|
77573
77639
|
import { defineCommand as defineCommand144 } from "citty";
|
|
77574
77640
|
|
|
77575
77641
|
// src/engine/scaffold/ad-spec.ts
|
|
77576
|
-
import { z as
|
|
77642
|
+
import { z as z69 } from "zod";
|
|
77577
77643
|
var oneClause = (line) => (line.match(/[.!?](\s|$)/g) ?? []).length <= 1;
|
|
77578
77644
|
var isSpeech = (line) => new RegExp("\\p{L}\\p{L}", "u").test(line);
|
|
77579
|
-
var Beat =
|
|
77645
|
+
var Beat = z69.object({
|
|
77580
77646
|
/**
|
|
77581
77647
|
* The line the voice says in this beat — ONE clause, ending in its own
|
|
77582
77648
|
* punctuation. It becomes the caption card verbatim.
|
|
77583
77649
|
*/
|
|
77584
|
-
say:
|
|
77650
|
+
say: z69.string().min(1).refine(
|
|
77585
77651
|
isSpeech,
|
|
77586
77652
|
"a beat's line has to be words someone says \u2014 a dot or a dash is not a line, and an ad with no voiceover is not supported yet"
|
|
77587
77653
|
).refine(oneClause, "a beat is one clause: split a second sentence into its own beat"),
|
|
77588
77654
|
/** What is on screen while it is said, as a shot brief for the video model. */
|
|
77589
|
-
show:
|
|
77655
|
+
show: z69.string().min(1),
|
|
77590
77656
|
/**
|
|
77591
77657
|
* What this shot SOUNDS like — one effect, in words. Usually absent.
|
|
77592
77658
|
*
|
|
@@ -77603,7 +77669,7 @@ var Beat = z68.object({
|
|
|
77603
77669
|
* Describe the SOUND, not the picture: "a cordless drill tightening a bolt", not "the
|
|
77604
77670
|
* fitter works on the rail".
|
|
77605
77671
|
*/
|
|
77606
|
-
sound:
|
|
77672
|
+
sound: z69.string().min(1).max(200).optional(),
|
|
77607
77673
|
/**
|
|
77608
77674
|
* Whether this beat's subject is talking to camera.
|
|
77609
77675
|
*
|
|
@@ -77617,36 +77683,36 @@ var Beat = z68.object({
|
|
|
77617
77683
|
* way through" produced an ad with her face on screen and a narrator cast from a prose
|
|
77618
77684
|
* description reading her lines, because nothing set this flag.
|
|
77619
77685
|
*/
|
|
77620
|
-
on_camera:
|
|
77686
|
+
on_camera: z69.boolean().optional(),
|
|
77621
77687
|
/**
|
|
77622
77688
|
* Whether the ad's recurring person is in this shot. Defaults to true whenever the
|
|
77623
77689
|
* spec declares a `cast`, because an ad is usually about one person and defaulting
|
|
77624
77690
|
* the other way is how continuity gets forgotten. Set false for a beat they are not
|
|
77625
77691
|
* in — a fitter on a roof, a product close-up.
|
|
77626
77692
|
*/
|
|
77627
|
-
cast:
|
|
77693
|
+
cast: z69.boolean().optional()
|
|
77628
77694
|
});
|
|
77629
|
-
var AdSpec =
|
|
77630
|
-
format:
|
|
77631
|
-
aspect_ratio:
|
|
77632
|
-
resolution:
|
|
77695
|
+
var AdSpec = z69.object({
|
|
77696
|
+
format: z69.object({
|
|
77697
|
+
aspect_ratio: z69.string().default("9:16"),
|
|
77698
|
+
resolution: z69.string().default("720p")
|
|
77633
77699
|
}).default({ aspect_ratio: "9:16", resolution: "720p" }),
|
|
77634
|
-
brand:
|
|
77635
|
-
name:
|
|
77700
|
+
brand: z69.object({
|
|
77701
|
+
name: z69.string().optional(),
|
|
77636
77702
|
/** Figures to lift typographically — the one gold number every ad has. */
|
|
77637
|
-
highlight:
|
|
77703
|
+
highlight: z69.array(z69.string()).optional(),
|
|
77638
77704
|
/**
|
|
77639
77705
|
* Brand hex colours, most important first. They dress the brand plate and
|
|
77640
77706
|
* the type — never the scenes: pushing hex into a shot brief makes the
|
|
77641
77707
|
* model paint the whole frame that colour, which reads as a filter.
|
|
77642
77708
|
*/
|
|
77643
|
-
palette:
|
|
77709
|
+
palette: z69.array(z69.string()).optional(),
|
|
77644
77710
|
/** Repo path to the brand mark, so the CTA carries it instead of a name in a font. */
|
|
77645
|
-
logo:
|
|
77711
|
+
logo: z69.string().optional()
|
|
77646
77712
|
}).optional(),
|
|
77647
|
-
voice:
|
|
77648
|
-
description:
|
|
77649
|
-
language:
|
|
77713
|
+
voice: z69.object({
|
|
77714
|
+
description: z69.string().min(1),
|
|
77715
|
+
language: z69.string().optional()
|
|
77650
77716
|
}).optional(),
|
|
77651
77717
|
/**
|
|
77652
77718
|
* Where the ad is set, as a place a model can picture: "Spain", "Mexico",
|
|
@@ -77657,7 +77723,7 @@ var AdSpec = z68.object({
|
|
|
77657
77723
|
* houses — sash windows, slate roofs, English suburbs — in every single frame,
|
|
77658
77724
|
* from briefs that were otherwise perfectly good.
|
|
77659
77725
|
*/
|
|
77660
|
-
market:
|
|
77726
|
+
market: z69.string().min(1).optional(),
|
|
77661
77727
|
/**
|
|
77662
77728
|
* The one person the ad is about.
|
|
77663
77729
|
*
|
|
@@ -77667,7 +77733,7 @@ var AdSpec = z68.object({
|
|
|
77667
77733
|
* with five different men playing one customer, which reads as a stock-footage
|
|
77668
77734
|
* collage rather than a story.
|
|
77669
77735
|
*/
|
|
77670
|
-
cast:
|
|
77736
|
+
cast: z69.object({
|
|
77671
77737
|
/**
|
|
77672
77738
|
* A cast Avatar's handle — `baker avatars list` shows who there is.
|
|
77673
77739
|
*
|
|
@@ -77677,9 +77743,9 @@ var AdSpec = z68.object({
|
|
|
77677
77743
|
* rather than re-written per spec — re-phrasing it per render is the other reason
|
|
77678
77744
|
* a face drifts across a set.
|
|
77679
77745
|
*/
|
|
77680
|
-
avatar:
|
|
77746
|
+
avatar: z69.string().min(1).optional(),
|
|
77681
77747
|
/** Who they are, plainly: age, build, hair, wardrobe, the register of the ad. */
|
|
77682
|
-
description:
|
|
77748
|
+
description: z69.string().min(1).optional()
|
|
77683
77749
|
}).refine((c) => Boolean(c.avatar || c.description), {
|
|
77684
77750
|
message: "give `avatar` (a cast avatar's handle) or `description` \u2014 a cast with neither grounds on nothing"
|
|
77685
77751
|
}).optional(),
|
|
@@ -77696,7 +77762,7 @@ var AdSpec = z68.object({
|
|
|
77696
77762
|
* has to read clean. That is the engine's rule, not this one, and it is why the prompt
|
|
77697
77763
|
* is all this needs to carry.
|
|
77698
77764
|
*/
|
|
77699
|
-
music:
|
|
77765
|
+
music: z69.union([z69.string().min(1), z69.literal(false)]).optional(),
|
|
77700
77766
|
/**
|
|
77701
77767
|
* The place the ad keeps coming back to, held still across every shot.
|
|
77702
77768
|
*
|
|
@@ -77711,9 +77777,9 @@ var AdSpec = z68.object({
|
|
|
77711
77777
|
* is `image_generate` with extra steps — the model registry says exactly that. Generate
|
|
77712
77778
|
* the plate once with gpt-image-2 and pass its URL here.
|
|
77713
77779
|
*/
|
|
77714
|
-
set:
|
|
77715
|
-
description:
|
|
77716
|
-
image:
|
|
77780
|
+
set: z69.object({
|
|
77781
|
+
description: z69.string().min(1).max(400),
|
|
77782
|
+
image: z69.string().min(1).optional()
|
|
77717
77783
|
}).optional(),
|
|
77718
77784
|
/**
|
|
77719
77785
|
* How the ad closes.
|
|
@@ -77727,15 +77793,15 @@ var AdSpec = z68.object({
|
|
|
77727
77793
|
*
|
|
77728
77794
|
* `false` opts out, for an ad that genuinely ends on its footage.
|
|
77729
77795
|
*/
|
|
77730
|
-
end_card:
|
|
77731
|
-
|
|
77732
|
-
|
|
77796
|
+
end_card: z69.union([
|
|
77797
|
+
z69.literal(false),
|
|
77798
|
+
z69.object({
|
|
77733
77799
|
/**
|
|
77734
77800
|
* The words on the button. No default — with none the card carries the mark
|
|
77735
77801
|
* alone, because falling back to the last beat's line printed that sentence
|
|
77736
77802
|
* twice on the closing frame. See `endCardCta`.
|
|
77737
77803
|
*/
|
|
77738
|
-
cta:
|
|
77804
|
+
cta: z69.string().min(1).optional()
|
|
77739
77805
|
})
|
|
77740
77806
|
]).optional(),
|
|
77741
77807
|
/**
|
|
@@ -77755,8 +77821,8 @@ var AdSpec = z68.object({
|
|
|
77755
77821
|
* rather than from transcribing a rendered read, so the text is exact and no speech
|
|
77756
77822
|
* model is between the copy and the screen.
|
|
77757
77823
|
*/
|
|
77758
|
-
voiceover:
|
|
77759
|
-
beats:
|
|
77824
|
+
voiceover: z69.boolean().default(true),
|
|
77825
|
+
beats: z69.array(Beat).min(2)
|
|
77760
77826
|
});
|
|
77761
77827
|
function presenterIsInEveryShot(spec) {
|
|
77762
77828
|
if (!spec.cast) return false;
|
|
@@ -78283,8 +78349,8 @@ function injectAdBrandOverlay(indexHtml, overlayHtml, style = {}) {
|
|
|
78283
78349
|
}
|
|
78284
78350
|
|
|
78285
78351
|
// src/commands/canvas/composition-dims.ts
|
|
78286
|
-
import { readFile as
|
|
78287
|
-
import
|
|
78352
|
+
import { readFile as readFile30, writeFile as writeFile19 } from "fs/promises";
|
|
78353
|
+
import path37 from "path";
|
|
78288
78354
|
function patchCompositionMeta(metaJson, dims) {
|
|
78289
78355
|
try {
|
|
78290
78356
|
const meta = JSON.parse(metaJson);
|
|
@@ -78299,12 +78365,12 @@ function patchCompositionHtml(html, dims) {
|
|
|
78299
78365
|
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`);
|
|
78300
78366
|
}
|
|
78301
78367
|
async function stampCompositionDims(compositionDir, dims) {
|
|
78302
|
-
const metaPath =
|
|
78303
|
-
const rawMeta = await
|
|
78304
|
-
await
|
|
78305
|
-
const htmlPath =
|
|
78306
|
-
const rawHtml = await
|
|
78307
|
-
await
|
|
78368
|
+
const metaPath = path37.join(compositionDir, "meta.json");
|
|
78369
|
+
const rawMeta = await readFile30(metaPath, "utf8");
|
|
78370
|
+
await writeFile19(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
|
|
78371
|
+
const htmlPath = path37.join(compositionDir, "index.html");
|
|
78372
|
+
const rawHtml = await readFile30(htmlPath, "utf8");
|
|
78373
|
+
await writeFile19(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
|
|
78308
78374
|
}
|
|
78309
78375
|
|
|
78310
78376
|
// src/commands/canvas/scaffold-ad.ts
|
|
@@ -78386,7 +78452,7 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78386
78452
|
const specPath = args8.spec;
|
|
78387
78453
|
let parsed;
|
|
78388
78454
|
try {
|
|
78389
|
-
parsed = JSON.parse(await
|
|
78455
|
+
parsed = JSON.parse(await readFile31(specPath, "utf-8"));
|
|
78390
78456
|
} catch (e) {
|
|
78391
78457
|
writeJson({
|
|
78392
78458
|
ok: false,
|
|
@@ -78547,7 +78613,7 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78547
78613
|
] : [];
|
|
78548
78614
|
const markPath = spec.data.brand?.logo?.trim();
|
|
78549
78615
|
if (!noBrand && markPath?.toLowerCase().endsWith(".svg")) {
|
|
78550
|
-
const svg = await
|
|
78616
|
+
const svg = await readFile31(markPath, "utf-8").catch(() => null);
|
|
78551
78617
|
if (svg === null) {
|
|
78552
78618
|
writeJson({
|
|
78553
78619
|
ok: false,
|
|
@@ -78599,8 +78665,8 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78599
78665
|
videoModel: chosenVideoModel,
|
|
78600
78666
|
...avatarVoice ? { avatarVoice } : {}
|
|
78601
78667
|
});
|
|
78602
|
-
const slug = args8.slug ??
|
|
78603
|
-
const outPath = args8.out ?? (args8.slug ?
|
|
78668
|
+
const slug = args8.slug ?? path38.basename(specPath).replace(/\.[^.]+$/, "");
|
|
78669
|
+
const outPath = args8.out ?? (args8.slug ? path38.join("src/creatives", slug, `${slug}.canvas.json`) : path38.join(path38.dirname(specPath), `${slug}.canvas.json`));
|
|
78604
78670
|
if (args8.slug) await enforceNewCreativeLimit(slug, outPath);
|
|
78605
78671
|
let outAr;
|
|
78606
78672
|
try {
|
|
@@ -78618,30 +78684,30 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78618
78684
|
return;
|
|
78619
78685
|
}
|
|
78620
78686
|
const outDims = canvasDims(outAr);
|
|
78621
|
-
const outDir =
|
|
78622
|
-
await
|
|
78623
|
-
const compositionDest =
|
|
78624
|
-
const captionsDest =
|
|
78687
|
+
const outDir = path38.dirname(outPath);
|
|
78688
|
+
await mkdir13(outDir, { recursive: true });
|
|
78689
|
+
const compositionDest = path38.join(outDir, "video-overlay-composition");
|
|
78690
|
+
const captionsDest = path38.join(outDir, "tiktok-captions-composition");
|
|
78625
78691
|
await cp(SHIPPED_OVERLAY_COMPOSITION_DIR, compositionDest, { recursive: true });
|
|
78626
78692
|
await cp(SHIPPED_CAPTIONS_DIR, captionsDest, { recursive: true });
|
|
78627
78693
|
await stampCompositionDims(compositionDest, outDims);
|
|
78628
78694
|
await stampCompositionDims(captionsDest, outDims);
|
|
78629
|
-
const captionsIndex =
|
|
78630
|
-
await
|
|
78695
|
+
const captionsIndex = path38.join(captionsDest, "index.html");
|
|
78696
|
+
await writeFile20(captionsIndex, injectAdCaptionPlacement(await readFile31(captionsIndex, "utf-8"), outAr), "utf-8");
|
|
78631
78697
|
const face = await stageBrandDisplayFace(outDir, captionsDest);
|
|
78632
78698
|
if (face) {
|
|
78633
|
-
await
|
|
78699
|
+
await writeFile20(captionsIndex, injectAdDisplayFace(await readFile31(captionsIndex, "utf-8"), face.file), "utf-8");
|
|
78634
78700
|
}
|
|
78635
|
-
const blueprintPath2 =
|
|
78636
|
-
const blueprintStylePath =
|
|
78701
|
+
const blueprintPath2 = path38.join(outDir, "prompt.json");
|
|
78702
|
+
const blueprintStylePath = path38.join(outDir, "prompt.style.json");
|
|
78637
78703
|
await writeSceneFiles(outDir, blueprint);
|
|
78638
|
-
await
|
|
78639
|
-
await
|
|
78704
|
+
await writeFile20(blueprintStylePath, renderStyleProjectionFromValue(blueprint), "utf8");
|
|
78705
|
+
await writeFile20(path38.join(outDir, AD_SPEC_FILE), `${JSON.stringify(specArtifact, null, 2)}
|
|
78640
78706
|
`, "utf8");
|
|
78641
78707
|
const logoPath = noBrand ? void 0 : spec.data.brand?.logo?.trim();
|
|
78642
|
-
const transcriptPath =
|
|
78708
|
+
const transcriptPath = path38.join(outDir, "transcript.json");
|
|
78643
78709
|
if (spec.data.voiceover === false) {
|
|
78644
|
-
await
|
|
78710
|
+
await writeFile20(transcriptPath, `${JSON.stringify(staticTranscript(spec.data, chosenVideoModel), null, 2)}
|
|
78645
78711
|
`, "utf-8");
|
|
78646
78712
|
}
|
|
78647
78713
|
const opts = {
|
|
@@ -78651,10 +78717,10 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78651
78717
|
// that stops the whole canvas. The same sheet model every other scaffold uses.
|
|
78652
78718
|
sheetModel: ACTOR_SHEET_MODEL,
|
|
78653
78719
|
videoModel: chosenVideoModel,
|
|
78654
|
-
overlayCompositionPath:
|
|
78655
|
-
captionsCompositionPath:
|
|
78656
|
-
blueprintPath:
|
|
78657
|
-
blueprintStylePath:
|
|
78720
|
+
overlayCompositionPath: path38.relative(outDir, compositionDest),
|
|
78721
|
+
captionsCompositionPath: path38.relative(outDir, captionsDest),
|
|
78722
|
+
blueprintPath: path38.relative(outDir, blueprintPath2),
|
|
78723
|
+
blueprintStylePath: path38.relative(outDir, blueprintStylePath),
|
|
78658
78724
|
aspect: spec.data.format.aspect_ratio,
|
|
78659
78725
|
resolution: spec.data.format.resolution,
|
|
78660
78726
|
// A beat is one clause and one card, so the cap has to clear a whole clause.
|
|
@@ -78693,17 +78759,17 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78693
78759
|
// the stock voice this exists to avoid. It is to convert the LATER extracts onto the
|
|
78694
78760
|
// first beat of the same speaker, which keeps a voice the clip really produced.
|
|
78695
78761
|
...avatar ? { keepRenderedVoice: true } : {},
|
|
78696
|
-
...spec.data.voiceover === false ? { staticTranscriptPath:
|
|
78762
|
+
...spec.data.voiceover === false ? { staticTranscriptPath: path38.relative(outDir, transcriptPath) } : {}
|
|
78697
78763
|
};
|
|
78698
78764
|
const canvas = scaffoldVideoCanvas(blueprint, adSpecCastElements(spec.data, avatar), opts);
|
|
78699
78765
|
const renderNode5 = canvas.nodes.find((n) => n.type === "hyperframe_render");
|
|
78700
|
-
const renderedDir = renderNode5 ?
|
|
78766
|
+
const renderedDir = renderNode5 ? path38.join(outDir, String(renderNode5.params?.composition ?? "")) : null;
|
|
78701
78767
|
let staged = null;
|
|
78702
78768
|
let markIsLight = false;
|
|
78703
78769
|
if (logoPath && renderedDir) {
|
|
78704
|
-
const ext =
|
|
78770
|
+
const ext = path38.extname(logoPath) || ".png";
|
|
78705
78771
|
try {
|
|
78706
|
-
await copyFile(logoPath,
|
|
78772
|
+
await copyFile(logoPath, path38.join(renderedDir, `brand-mark${ext}`));
|
|
78707
78773
|
staged = { file: `brand-mark${ext}`, alt: spec.data.brand?.name ?? "brand" };
|
|
78708
78774
|
markIsLight = await markInkIsLight(logoPath);
|
|
78709
78775
|
} catch {
|
|
@@ -78722,12 +78788,12 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78722
78788
|
plateIsDark: plateIsDark(spec.data.brand?.palette)
|
|
78723
78789
|
}) : "";
|
|
78724
78790
|
if (overlayHtml && renderedDir) {
|
|
78725
|
-
const indexPath =
|
|
78726
|
-
const html = await
|
|
78727
|
-
await
|
|
78791
|
+
const indexPath = path38.join(renderedDir, "index.html");
|
|
78792
|
+
const html = await readFile31(indexPath, "utf-8");
|
|
78793
|
+
await writeFile20(indexPath, injectAdBrandOverlay(html, overlayHtml, { aspect: outAr, ...face ? { face: AD_DISPLAY_FAMILY } : {} }), "utf-8");
|
|
78728
78794
|
}
|
|
78729
|
-
await
|
|
78730
|
-
|
|
78795
|
+
await writeFile20(
|
|
78796
|
+
path38.join(outDir, REBUILD_FILE),
|
|
78731
78797
|
`${JSON.stringify({ elements: adSpecCastElements(spec.data, avatar), opts }, null, 2)}
|
|
78732
78798
|
`,
|
|
78733
78799
|
"utf8"
|
|
@@ -78737,7 +78803,7 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78737
78803
|
const meta = canvas.metadata;
|
|
78738
78804
|
meta.todo = { ...meta.todo ?? {}, blocking_validation_issues: validation.issues };
|
|
78739
78805
|
}
|
|
78740
|
-
await
|
|
78806
|
+
await writeFile20(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
78741
78807
|
`, "utf-8");
|
|
78742
78808
|
if (!validation.ok) {
|
|
78743
78809
|
process.stderr.write(
|
|
@@ -78757,10 +78823,10 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78757
78823
|
return;
|
|
78758
78824
|
}
|
|
78759
78825
|
if (args8.slug) {
|
|
78760
|
-
const definitionPath =
|
|
78826
|
+
const definitionPath = path38.join(outDir, "_definition.md");
|
|
78761
78827
|
const definitionExists = await stat6(definitionPath).then(() => true, () => false);
|
|
78762
78828
|
if (!definitionExists) {
|
|
78763
|
-
await
|
|
78829
|
+
await writeFile20(
|
|
78764
78830
|
definitionPath,
|
|
78765
78831
|
buildCreativeDefinition({
|
|
78766
78832
|
title: titleFromSlug(slug),
|
|
@@ -78875,10 +78941,10 @@ async function fetchClientLogo() {
|
|
|
78875
78941
|
if (!response.ok) return { ok: false, reason: clientLogoMissNote({ use: false, reason: "no_hits" }, domain) };
|
|
78876
78942
|
const bytes = Buffer.from(await response.arrayBuffer());
|
|
78877
78943
|
const extension = /\.(svg|png|jpg|jpeg|webp)(?:\?|$)/i.exec(decision.url)?.[1]?.toLowerCase() ?? "svg";
|
|
78878
|
-
const dir =
|
|
78879
|
-
await
|
|
78880
|
-
const file =
|
|
78881
|
-
await
|
|
78944
|
+
const dir = path38.join(BRAND_DIR, "logos");
|
|
78945
|
+
await mkdir13(dir, { recursive: true });
|
|
78946
|
+
const file = path38.join(dir, `brand-mark.${extension}`);
|
|
78947
|
+
await writeFile20(file, bytes);
|
|
78882
78948
|
return { ok: true, path: file };
|
|
78883
78949
|
} catch {
|
|
78884
78950
|
return { ok: false, reason: clientLogoMissNote({ use: false, reason: "no_hits" }, domain) };
|
|
@@ -78896,14 +78962,14 @@ async function stageBrandDisplayFace(outDir, compositionDir) {
|
|
|
78896
78962
|
if (!face) return null;
|
|
78897
78963
|
try {
|
|
78898
78964
|
if ("path" in face.slot) {
|
|
78899
|
-
const file2 = `brand-face${
|
|
78900
|
-
await copyFile(face.slot.path,
|
|
78901
|
-
return { file: file2, label: `the font file \`${
|
|
78965
|
+
const file2 = `brand-face${path38.extname(face.slot.path).toLowerCase()}`;
|
|
78966
|
+
await copyFile(face.slot.path, path38.join(compositionDir, file2));
|
|
78967
|
+
return { file: file2, label: `the font file \`${path38.relative(root, face.slot.path)}\`` };
|
|
78902
78968
|
}
|
|
78903
78969
|
const { buffer } = await fetchExternalBytes(face.slot.url, { maxBytes: MAX_FONT_BYTES });
|
|
78904
78970
|
const extension = /\.(woff2|woff|ttf|otf)(?:\?|$)/i.exec(face.slot.url)?.[1]?.toLowerCase() ?? "woff2";
|
|
78905
78971
|
const file = `brand-face.${extension}`;
|
|
78906
|
-
await
|
|
78972
|
+
await writeFile20(path38.join(compositionDir, file), buffer);
|
|
78907
78973
|
return { file, label: `${face.family ?? "the brand's typeface"}, loaded as a web font` };
|
|
78908
78974
|
} catch {
|
|
78909
78975
|
return null;
|
|
@@ -78911,9 +78977,9 @@ async function stageBrandDisplayFace(outDir, compositionDir) {
|
|
|
78911
78977
|
}
|
|
78912
78978
|
|
|
78913
78979
|
// src/commands/canvas/scaffold-video.ts
|
|
78914
|
-
import { cp as cp2, mkdir as
|
|
78980
|
+
import { cp as cp2, mkdir as mkdir14, readFile as readFile34, rm as rm6, writeFile as writeFile21 } from "fs/promises";
|
|
78915
78981
|
import { tmpdir as tmpdir2 } from "os";
|
|
78916
|
-
import
|
|
78982
|
+
import path40 from "path";
|
|
78917
78983
|
import { defineCommand as defineCommand145 } from "citty";
|
|
78918
78984
|
|
|
78919
78985
|
// src/engine/scaffold/lib/model-router.ts
|
|
@@ -78953,7 +79019,7 @@ function routeVideoModel(input) {
|
|
|
78953
79019
|
|
|
78954
79020
|
// src/engine/nodes/local/lib/sceneDetect.ts
|
|
78955
79021
|
import { execFile as execFile2 } from "child_process";
|
|
78956
|
-
import { mkdtemp, readdir as readdir16, readFile as
|
|
79022
|
+
import { mkdtemp, readdir as readdir16, readFile as readFile32, rm as rm5 } from "fs/promises";
|
|
78957
79023
|
import { tmpdir } from "os";
|
|
78958
79024
|
import { join as join3 } from "path";
|
|
78959
79025
|
import { promisify as promisify2 } from "util";
|
|
@@ -79029,7 +79095,7 @@ async function runSceneDetectOnce(filePath, threshold, minSceneLenS, timeoutMs)
|
|
|
79029
79095
|
);
|
|
79030
79096
|
const csvName = (await readdir16(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
|
|
79031
79097
|
if (!csvName) return [];
|
|
79032
|
-
return parsePySceneDetectCsvCuts(await
|
|
79098
|
+
return parsePySceneDetectCsvCuts(await readFile32(join3(outDir, csvName), "utf-8"));
|
|
79033
79099
|
} finally {
|
|
79034
79100
|
await rm5(outDir, { recursive: true, force: true });
|
|
79035
79101
|
}
|
|
@@ -79053,8 +79119,8 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
|
|
|
79053
79119
|
}
|
|
79054
79120
|
|
|
79055
79121
|
// src/commands/canvas/gitignore.ts
|
|
79056
|
-
import { appendFile, readFile as
|
|
79057
|
-
import
|
|
79122
|
+
import { appendFile, readFile as readFile33 } from "fs/promises";
|
|
79123
|
+
import path39 from "path";
|
|
79058
79124
|
function missingGitignoreEntries(existing, entries) {
|
|
79059
79125
|
const present2 = new Set(
|
|
79060
79126
|
existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
|
|
@@ -79062,10 +79128,10 @@ function missingGitignoreEntries(existing, entries) {
|
|
|
79062
79128
|
return entries.filter((e) => !present2.has(e.trim().replace(/\/+$/, "")));
|
|
79063
79129
|
}
|
|
79064
79130
|
async function ensureGitignore(dir, entries) {
|
|
79065
|
-
const file =
|
|
79131
|
+
const file = path39.join(dir, ".gitignore");
|
|
79066
79132
|
let existing;
|
|
79067
79133
|
try {
|
|
79068
|
-
existing = await
|
|
79134
|
+
existing = await readFile33(file, "utf8");
|
|
79069
79135
|
} catch {
|
|
79070
79136
|
return;
|
|
79071
79137
|
}
|
|
@@ -79107,7 +79173,7 @@ async function loadTranscriptBestEffort(ref2) {
|
|
|
79107
79173
|
async function stageCaptions(outDir, transcript) {
|
|
79108
79174
|
const text3 = transcript?.trim();
|
|
79109
79175
|
if (!text3 || text3 === "[]") return {};
|
|
79110
|
-
const compositionPath =
|
|
79176
|
+
const compositionPath = path40.join(outDir, "tiktok-captions-composition");
|
|
79111
79177
|
await cp2(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
|
|
79112
79178
|
return { compositionPath };
|
|
79113
79179
|
}
|
|
@@ -79143,7 +79209,7 @@ var VIDEO_EXT_BY_MIME = {
|
|
|
79143
79209
|
"video/x-matroska": ".mkv"
|
|
79144
79210
|
};
|
|
79145
79211
|
function referenceVideoExt(url, contentType) {
|
|
79146
|
-
const fromPath =
|
|
79212
|
+
const fromPath = path40.extname(new URL(url).pathname).toLowerCase();
|
|
79147
79213
|
if (fromPath && fromPath.length <= 5) return fromPath;
|
|
79148
79214
|
const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase();
|
|
79149
79215
|
return mime && VIDEO_EXT_BY_MIME[mime] || ".mp4";
|
|
@@ -79169,7 +79235,7 @@ function framesModeReport(canvas, requested) {
|
|
|
79169
79235
|
return { frames_mode: mode, ...refused ? { frames_reuse_refused: refused } : {} };
|
|
79170
79236
|
}
|
|
79171
79237
|
async function materializeReferenceVideo(fileArg2) {
|
|
79172
|
-
if (!/^https?:\/\//i.test(fileArg2)) return
|
|
79238
|
+
if (!/^https?:\/\//i.test(fileArg2)) return path40.resolve(fileArg2);
|
|
79173
79239
|
let bytes;
|
|
79174
79240
|
let contentType;
|
|
79175
79241
|
try {
|
|
@@ -79181,11 +79247,11 @@ async function materializeReferenceVideo(fileArg2) {
|
|
|
79181
79247
|
throw new Error(`failed to download reference video: ${e instanceof Error ? e.message : String(e)}`);
|
|
79182
79248
|
}
|
|
79183
79249
|
if (bytes.length === 0) throw new Error("reference video download was empty");
|
|
79184
|
-
const dest =
|
|
79250
|
+
const dest = path40.join(
|
|
79185
79251
|
tmpdir2(),
|
|
79186
79252
|
`baker-ref-${sha256Hex(bytes).slice(0, 16)}${referenceVideoExt(fileArg2, contentType)}`
|
|
79187
79253
|
);
|
|
79188
|
-
await
|
|
79254
|
+
await writeFile21(dest, bytes);
|
|
79189
79255
|
return dest;
|
|
79190
79256
|
}
|
|
79191
79257
|
function resolveSeamDedup(raw) {
|
|
@@ -79408,12 +79474,12 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79408
79474
|
} catch (e) {
|
|
79409
79475
|
return fail4("download", e instanceof Error ? e.message : String(e));
|
|
79410
79476
|
}
|
|
79411
|
-
const base =
|
|
79412
|
-
const outPath = args8.out ?
|
|
79477
|
+
const base = path40.basename(videoPath, path40.extname(videoPath));
|
|
79478
|
+
const outPath = args8.out ? path40.resolve(String(args8.out)) : slug ? path40.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path40.join(path40.dirname(videoPath), `${base}.video.canvas.json`);
|
|
79413
79479
|
if (slug) await enforceNewCreativeLimit(slug, outPath);
|
|
79414
|
-
const outDir =
|
|
79415
|
-
const blueprintPath2 =
|
|
79416
|
-
const blueprintStylePath =
|
|
79480
|
+
const outDir = path40.dirname(outPath);
|
|
79481
|
+
const blueprintPath2 = path40.join(outDir, "prompt.json");
|
|
79482
|
+
const blueprintStylePath = path40.join(outDir, "prompt.style.json");
|
|
79417
79483
|
const frames = args8.frames === "reuse" ? "reuse" : "generate";
|
|
79418
79484
|
const maxScenes = args8["max-scenes"] ? Number(args8["max-scenes"]) : void 0;
|
|
79419
79485
|
if (Number.isFinite(maxScenes)) {
|
|
@@ -79461,10 +79527,10 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79461
79527
|
process.stderr.write(`\u{1F3AC} Photoreal on-camera cast detected \u2192 generating clips on ${videoModel} (dodges Seedance's real-person filter).
|
|
79462
79528
|
`);
|
|
79463
79529
|
}
|
|
79464
|
-
await
|
|
79530
|
+
await mkdir14(outDir, { recursive: true });
|
|
79465
79531
|
const annotated = annotateBlueprintWithElements(blueprint, elements);
|
|
79466
79532
|
await writeSceneFiles(outDir, annotated);
|
|
79467
|
-
await
|
|
79533
|
+
await writeFile21(blueprintStylePath, renderStyleProjectionFromValue(annotated), "utf8");
|
|
79468
79534
|
let aspect;
|
|
79469
79535
|
try {
|
|
79470
79536
|
aspect = resolveAspect(
|
|
@@ -79482,12 +79548,12 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79482
79548
|
`
|
|
79483
79549
|
);
|
|
79484
79550
|
}
|
|
79485
|
-
const compositionDest =
|
|
79551
|
+
const compositionDest = path40.join(outDir, "video-overlay-composition");
|
|
79486
79552
|
await cp2(SHIPPED_OVERLAY_COMPOSITION_DIR, compositionDest, { recursive: true });
|
|
79487
79553
|
await stampCompositionDims(compositionDest, outDims);
|
|
79488
|
-
const indexPath =
|
|
79554
|
+
const indexPath = path40.join(compositionDest, "index.html");
|
|
79489
79555
|
const overlayHtml = buildOverlayHtml(blueprint, { captionsActive: Boolean(transcript), videoModel });
|
|
79490
|
-
const indexHtml = await
|
|
79556
|
+
const indexHtml = await readFile34(indexPath, "utf8");
|
|
79491
79557
|
const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
|
|
79492
79558
|
if (injected === indexHtml && overlayHtml.trim()) {
|
|
79493
79559
|
fail4(
|
|
@@ -79495,16 +79561,16 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79495
79561
|
`video-overlay-composition/index.html is missing the <!--OVERLAYS--> marker \u2014 cannot inject the overlay layer`
|
|
79496
79562
|
);
|
|
79497
79563
|
}
|
|
79498
|
-
await
|
|
79564
|
+
await writeFile21(indexPath, injected, "utf8");
|
|
79499
79565
|
const captions = await stageCaptions(outDir, transcript);
|
|
79500
79566
|
if (captions.compositionPath) await stampCompositionDims(captions.compositionPath, outDims);
|
|
79501
79567
|
const opts = {
|
|
79502
79568
|
imageModel,
|
|
79503
79569
|
videoModel,
|
|
79504
|
-
overlayCompositionPath:
|
|
79505
|
-
captionsCompositionPath: captions.compositionPath ?
|
|
79506
|
-
blueprintPath:
|
|
79507
|
-
blueprintStylePath:
|
|
79570
|
+
overlayCompositionPath: path40.relative(outDir, compositionDest),
|
|
79571
|
+
captionsCompositionPath: captions.compositionPath ? path40.relative(outDir, captions.compositionPath) : void 0,
|
|
79572
|
+
blueprintPath: path40.relative(outDir, blueprintPath2),
|
|
79573
|
+
blueprintStylePath: path40.relative(outDir, blueprintStylePath),
|
|
79508
79574
|
frames,
|
|
79509
79575
|
ambient: Boolean(args8.ambient),
|
|
79510
79576
|
seamDedup: resolveSeamDedup(args8["seam-dedup"]),
|
|
@@ -79529,10 +79595,10 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79529
79595
|
todo.blocking_validation_issues = validation.issues;
|
|
79530
79596
|
meta.todo = todo;
|
|
79531
79597
|
}
|
|
79532
|
-
await
|
|
79598
|
+
await writeFile21(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
79533
79599
|
`, "utf8");
|
|
79534
|
-
await
|
|
79535
|
-
|
|
79600
|
+
await writeFile21(
|
|
79601
|
+
path40.join(outDir, REBUILD_FILE),
|
|
79536
79602
|
`${JSON.stringify({ elements, opts }, null, 2)}
|
|
79537
79603
|
`,
|
|
79538
79604
|
"utf8"
|
|
@@ -79559,9 +79625,9 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79559
79625
|
await ensureGitignore(process.cwd(), ["canvas/", ".context/"]);
|
|
79560
79626
|
const sourceRef = videoSourceReference(blueprint, fileArg2);
|
|
79561
79627
|
if (slug) {
|
|
79562
|
-
const definitionPath =
|
|
79628
|
+
const definitionPath = path40.join(outDir, "_definition.md");
|
|
79563
79629
|
if (!await fileExists(definitionPath)) {
|
|
79564
|
-
await
|
|
79630
|
+
await writeFile21(
|
|
79565
79631
|
definitionPath,
|
|
79566
79632
|
buildCreativeDefinition({
|
|
79567
79633
|
title: titleFromSlug(slug),
|
|
@@ -79615,7 +79681,7 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79615
79681
|
graph: canvas.metadata?.video?.graph_stats
|
|
79616
79682
|
},
|
|
79617
79683
|
checklist: {
|
|
79618
|
-
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 \`${
|
|
79684
|
+
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 \`${path40.basename(blueprintPath2)}\`. \`baker canvas validate\`/\`run\` re-assemble the blueprint and re-flow every edited scene back into the render (and regenerate ${path40.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 ${path40.basename(blueprintStylePath)}; both are regenerated.`,
|
|
79619
79685
|
recurring_elements_to_supply: report.elements,
|
|
79620
79686
|
voices_to_confirm: report.dialogue.map((d) => ({
|
|
79621
79687
|
scene: d.scene,
|
|
@@ -79652,8 +79718,8 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79652
79718
|
});
|
|
79653
79719
|
|
|
79654
79720
|
// src/commands/canvas/set-prompt.ts
|
|
79655
|
-
import { readFile as
|
|
79656
|
-
import
|
|
79721
|
+
import { readFile as readFile35, writeFile as writeFile22 } from "fs/promises";
|
|
79722
|
+
import path41 from "path";
|
|
79657
79723
|
import { defineCommand as defineCommand146 } from "citty";
|
|
79658
79724
|
function setNodePrompt(canvas, nodeId, text3) {
|
|
79659
79725
|
const nodes = canvas?.nodes;
|
|
@@ -79682,8 +79748,8 @@ var SetPromptError = class extends Error {
|
|
|
79682
79748
|
}
|
|
79683
79749
|
};
|
|
79684
79750
|
async function setPromptInCanvasFile(canvasPath, nodeId, text3) {
|
|
79685
|
-
const filePath =
|
|
79686
|
-
const original = await
|
|
79751
|
+
const filePath = path41.resolve(canvasPath);
|
|
79752
|
+
const original = await readFile35(filePath, "utf8");
|
|
79687
79753
|
let canvas;
|
|
79688
79754
|
try {
|
|
79689
79755
|
canvas = JSON.parse(original);
|
|
@@ -79705,7 +79771,7 @@ async function setPromptInCanvasFile(canvasPath, nodeId, text3) {
|
|
|
79705
79771
|
"projected_prompt",
|
|
79706
79772
|
`The prompt of "${nodeId}" is compiled from prompt.json on every validate and run, so setting it in the canvas would be overwritten.`,
|
|
79707
79773
|
{
|
|
79708
|
-
fix: `Edit the ad in ${
|
|
79774
|
+
fix: `Edit the ad in ${path41.join(path41.dirname(filePath), "prompt.json")} (its scene, copy, references or direction), then run \`baker canvas validate\` to see the compiled prompt on the node.`
|
|
79709
79775
|
}
|
|
79710
79776
|
);
|
|
79711
79777
|
}
|
|
@@ -79715,9 +79781,9 @@ async function setPromptInCanvasFile(canvasPath, nodeId, text3) {
|
|
|
79715
79781
|
} catch (e) {
|
|
79716
79782
|
throw new SetPromptError("node_not_found", e instanceof Error ? e.message : String(e));
|
|
79717
79783
|
}
|
|
79718
|
-
await
|
|
79784
|
+
await writeFile22(filePath, `${JSON.stringify(updated, null, 2)}
|
|
79719
79785
|
`, "utf8");
|
|
79720
|
-
const restore = () =>
|
|
79786
|
+
const restore = () => writeFile22(filePath, original, "utf8");
|
|
79721
79787
|
try {
|
|
79722
79788
|
const prepared = await prepareCanvas(filePath);
|
|
79723
79789
|
const validation = await validateCanvasDeep(prepared.canvas, defaultRegistry());
|
|
@@ -79759,7 +79825,7 @@ var setPromptCommand = defineCommand146({
|
|
|
79759
79825
|
},
|
|
79760
79826
|
async run({ args: args8 }) {
|
|
79761
79827
|
let text3;
|
|
79762
|
-
if (args8["text-file"]) text3 = await
|
|
79828
|
+
if (args8["text-file"]) text3 = await readFile35(path41.resolve(String(args8["text-file"])), "utf8");
|
|
79763
79829
|
else if (args8.text !== void 0) text3 = String(args8.text);
|
|
79764
79830
|
else {
|
|
79765
79831
|
process.stderr.write(
|
|
@@ -79816,7 +79882,7 @@ var staleCommand = defineCommand147({
|
|
|
79816
79882
|
});
|
|
79817
79883
|
|
|
79818
79884
|
// src/commands/canvas/validate.ts
|
|
79819
|
-
import
|
|
79885
|
+
import path42 from "path";
|
|
79820
79886
|
import { defineCommand as defineCommand148 } from "citty";
|
|
79821
79887
|
var validateCommand = defineCommand148({
|
|
79822
79888
|
meta: {
|
|
@@ -79825,7 +79891,7 @@ var validateCommand = defineCommand148({
|
|
|
79825
79891
|
},
|
|
79826
79892
|
args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
|
|
79827
79893
|
async run({ args: args8 }) {
|
|
79828
|
-
const filePath =
|
|
79894
|
+
const filePath = path42.resolve(String(args8.file));
|
|
79829
79895
|
let prepared;
|
|
79830
79896
|
try {
|
|
79831
79897
|
prepared = await prepareCanvas(filePath, { gate: true, log: (line) => process.stderr.write(`${line}
|
|
@@ -80261,8 +80327,8 @@ import { defineCommand as defineCommand162 } from "citty";
|
|
|
80261
80327
|
|
|
80262
80328
|
// src/commands/creatives/brief.ts
|
|
80263
80329
|
import { readFileSync as readFileSync17 } from "fs";
|
|
80264
|
-
import { readdir as readdir17, readFile as
|
|
80265
|
-
import
|
|
80330
|
+
import { readdir as readdir17, readFile as readFile36 } from "fs/promises";
|
|
80331
|
+
import path44 from "path";
|
|
80266
80332
|
import { defineCommand as defineCommand155 } from "citty";
|
|
80267
80333
|
|
|
80268
80334
|
// src/commands/winning-ads/search.ts
|
|
@@ -80520,8 +80586,8 @@ var FLAG_FOR_FIELD = {
|
|
|
80520
80586
|
first_seen_after: "--first-seen-after",
|
|
80521
80587
|
first_seen_before: "--first-seen-before"
|
|
80522
80588
|
};
|
|
80523
|
-
function flagFor(
|
|
80524
|
-
const field = String(
|
|
80589
|
+
function flagFor(path68) {
|
|
80590
|
+
const field = String(path68[0] === "hard_filters" ? path68[1] : path68[0]);
|
|
80525
80591
|
return FLAG_FOR_FIELD[field] ?? field;
|
|
80526
80592
|
}
|
|
80527
80593
|
function schemaRejection(body) {
|
|
@@ -80993,7 +81059,7 @@ var ideasCommand = defineCommand153({
|
|
|
80993
81059
|
|
|
80994
81060
|
// src/commands/creatives/performance.ts
|
|
80995
81061
|
import { readFileSync as readFileSync16 } from "fs";
|
|
80996
|
-
import
|
|
81062
|
+
import path43 from "path";
|
|
80997
81063
|
import { defineCommand as defineCommand154 } from "citty";
|
|
80998
81064
|
import { parse as parseYaml4 } from "yaml";
|
|
80999
81065
|
var DESCRIPTION5 = `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.
|
|
@@ -81057,7 +81123,7 @@ function definitionInsights(markdown) {
|
|
|
81057
81123
|
}
|
|
81058
81124
|
function readLocalDefinition(slug) {
|
|
81059
81125
|
try {
|
|
81060
|
-
return readFileSync16(
|
|
81126
|
+
return readFileSync16(path43.join(process.cwd(), "src", "creatives", slug, "_definition.md"), "utf8");
|
|
81061
81127
|
} catch {
|
|
81062
81128
|
return null;
|
|
81063
81129
|
}
|
|
@@ -81169,11 +81235,11 @@ function isAvailable(value2) {
|
|
|
81169
81235
|
return !(value2 !== null && typeof value2 === "object" && "unavailable" in value2);
|
|
81170
81236
|
}
|
|
81171
81237
|
async function companyStatus(root) {
|
|
81172
|
-
const doc = await
|
|
81238
|
+
const doc = await readFile36(path44.join(root, "COMPANY.md"), "utf8").catch(() => "");
|
|
81173
81239
|
return doc.trim() && !/^#\s*Company\s*\n+\s*TODO/m.test(doc) ? "ready" : "missing";
|
|
81174
81240
|
}
|
|
81175
81241
|
async function readOffers(root) {
|
|
81176
|
-
const dir =
|
|
81242
|
+
const dir = path44.join(root, "src", "content", "offers");
|
|
81177
81243
|
const files = (await readdir17(dir, { recursive: true }).catch(() => [])).filter(
|
|
81178
81244
|
(f) => f.endsWith(".md")
|
|
81179
81245
|
);
|
|
@@ -81181,7 +81247,7 @@ async function readOffers(root) {
|
|
|
81181
81247
|
}
|
|
81182
81248
|
function readDefinition(root, slug) {
|
|
81183
81249
|
try {
|
|
81184
|
-
return readFileSync17(
|
|
81250
|
+
return readFileSync17(path44.join(root, "src", "creatives", slug, "_definition.md"), "utf8");
|
|
81185
81251
|
} catch {
|
|
81186
81252
|
return null;
|
|
81187
81253
|
}
|
|
@@ -81202,7 +81268,7 @@ function marketQuery(companyDoc) {
|
|
|
81202
81268
|
return prose.slice(0, 240);
|
|
81203
81269
|
}
|
|
81204
81270
|
async function readMarket(root) {
|
|
81205
|
-
const query = marketQuery(await
|
|
81271
|
+
const query = marketQuery(await readFile36(path44.join(root, "COMPANY.md"), "utf8").catch(() => ""));
|
|
81206
81272
|
if (!query) return null;
|
|
81207
81273
|
const search = (body) => apiPost("/api/ad-library/winners/search", body);
|
|
81208
81274
|
const followed = await search(
|
|
@@ -81538,8 +81604,8 @@ var feedbackCommand = defineCommand157({
|
|
|
81538
81604
|
|
|
81539
81605
|
// src/commands/creatives/learn.ts
|
|
81540
81606
|
import { readFileSync as readFileSync18 } from "fs";
|
|
81541
|
-
import { readFile as
|
|
81542
|
-
import
|
|
81607
|
+
import { readFile as readFile37, writeFile as writeFile23 } from "fs/promises";
|
|
81608
|
+
import path45 from "path";
|
|
81543
81609
|
import { defineCommand as defineCommand158 } from "citty";
|
|
81544
81610
|
|
|
81545
81611
|
// src/commands/creatives/learn-model.ts
|
|
@@ -81761,14 +81827,14 @@ function learnEnvelope(learnings, written, unreadable = []) {
|
|
|
81761
81827
|
}
|
|
81762
81828
|
async function writeBack(root, learnings) {
|
|
81763
81829
|
for (const learning of learnings) {
|
|
81764
|
-
const file =
|
|
81765
|
-
const raw = await
|
|
81766
|
-
await
|
|
81830
|
+
const file = path45.join(root, IDEAS_DIR, `${learning.idea}.md`);
|
|
81831
|
+
const raw = await readFile37(file, "utf8");
|
|
81832
|
+
await writeFile23(file, setDefinitionFields(raw, { status: learning.to, result: learning.result }), "utf8");
|
|
81767
81833
|
}
|
|
81768
81834
|
}
|
|
81769
81835
|
function readDefinition2(root, slug) {
|
|
81770
81836
|
try {
|
|
81771
|
-
return readFileSync18(
|
|
81837
|
+
return readFileSync18(path45.join(root, "src", "creatives", slug, "_definition.md"), "utf8");
|
|
81772
81838
|
} catch {
|
|
81773
81839
|
return null;
|
|
81774
81840
|
}
|
|
@@ -81806,9 +81872,9 @@ var learnCommand = defineCommand158({
|
|
|
81806
81872
|
|
|
81807
81873
|
// src/commands/creatives/make.ts
|
|
81808
81874
|
import { execFile as execFile3 } from "child_process";
|
|
81809
|
-
import { mkdtemp as mkdtemp2, readFile as
|
|
81875
|
+
import { mkdtemp as mkdtemp2, readFile as readFile38, rm as rm7, writeFile as writeFile24 } from "fs/promises";
|
|
81810
81876
|
import { tmpdir as tmpdir3 } from "os";
|
|
81811
|
-
import
|
|
81877
|
+
import path46 from "path";
|
|
81812
81878
|
import { promisify as promisify3 } from "util";
|
|
81813
81879
|
import { defineCommand as defineCommand159 } from "citty";
|
|
81814
81880
|
var run2 = promisify3(execFile3);
|
|
@@ -82013,9 +82079,9 @@ async function scaffoldIdea(root, idea, args8, slug, temperature) {
|
|
|
82013
82079
|
return { scaffold, reference };
|
|
82014
82080
|
}
|
|
82015
82081
|
async function scaffoldFromIdea(idea, slug, viewerFlags) {
|
|
82016
|
-
const dir = await mkdtemp2(
|
|
82017
|
-
const specPath =
|
|
82018
|
-
await
|
|
82082
|
+
const dir = await mkdtemp2(path46.join(tmpdir3(), "idea-spec-"));
|
|
82083
|
+
const specPath = path46.join(dir, "prompt.json");
|
|
82084
|
+
await writeFile24(specPath, JSON.stringify(ideaSpec(idea, idea.who.replace(/[-_]/g, " ")), null, 2), "utf8");
|
|
82019
82085
|
try {
|
|
82020
82086
|
return await baker([
|
|
82021
82087
|
"canvas",
|
|
@@ -82033,19 +82099,19 @@ async function scaffoldFromIdea(idea, slug, viewerFlags) {
|
|
|
82033
82099
|
}
|
|
82034
82100
|
}
|
|
82035
82101
|
async function recordIdea(root, slug, idea, evidence) {
|
|
82036
|
-
const dir =
|
|
82037
|
-
const definitionPath =
|
|
82038
|
-
const definition = await
|
|
82102
|
+
const dir = path46.join(root, "src", "creatives", slug);
|
|
82103
|
+
const definitionPath = path46.join(dir, "_definition.md");
|
|
82104
|
+
const definition = await readFile38(definitionPath, "utf8").catch(() => null);
|
|
82039
82105
|
if (definition !== null) {
|
|
82040
|
-
await
|
|
82106
|
+
await writeFile24(definitionPath, setDefinitionFields(definition, { ...definitionFieldsOf(idea, evidence) }), "utf8");
|
|
82041
82107
|
}
|
|
82042
|
-
const promptPath =
|
|
82043
|
-
const prompt = isVideoIdea(idea) ? null : await
|
|
82044
|
-
if (prompt !== null) await
|
|
82108
|
+
const promptPath = path46.join(dir, "prompt.json");
|
|
82109
|
+
const prompt = isVideoIdea(idea) ? null : await readFile38(promptPath, "utf8").catch(() => null);
|
|
82110
|
+
if (prompt !== null) await writeFile24(promptPath, setSpecMessage(prompt, idea.message), "utf8").catch(() => void 0);
|
|
82045
82111
|
const unsourcedCopy = prompt === null ? [] : await draftedClaims(root, prompt);
|
|
82046
82112
|
if (idea.status === "idea") {
|
|
82047
|
-
const ideaPath =
|
|
82048
|
-
await
|
|
82113
|
+
const ideaPath = path46.join(root, IDEAS_DIR, `${idea.id}.md`);
|
|
82114
|
+
await writeFile24(ideaPath, setDefinitionFields(await readFile38(ideaPath, "utf8"), { status: "built" }), "utf8");
|
|
82049
82115
|
}
|
|
82050
82116
|
await baker(["canvas", "sync-definition", dir]);
|
|
82051
82117
|
return { dir, unsourcedCopy };
|
|
@@ -82131,7 +82197,7 @@ var makeCommand = defineCommand159({
|
|
|
82131
82197
|
reference,
|
|
82132
82198
|
...fromIdea ? { fromIdea } : {},
|
|
82133
82199
|
viewer: opts.adset ? { adset: opts.adset } : { temperature },
|
|
82134
|
-
canvasPath:
|
|
82200
|
+
canvasPath: path46.relative(root, String(scaffold.canvas_path ?? path46.join(dir, `${slug}.canvas.json`))),
|
|
82135
82201
|
scaffoldHints: Array.isArray(scaffold.hints) ? scaffold.hints : [],
|
|
82136
82202
|
unsourcedCopy
|
|
82137
82203
|
})
|
|
@@ -82144,7 +82210,7 @@ import { Buffer as Buffer2 } from "buffer";
|
|
|
82144
82210
|
import { defineCommand as defineCommand160 } from "citty";
|
|
82145
82211
|
|
|
82146
82212
|
// src/commands/images/api.ts
|
|
82147
|
-
import { readFile as
|
|
82213
|
+
import { readFile as readFile39 } from "fs/promises";
|
|
82148
82214
|
import { basename, extname } from "path";
|
|
82149
82215
|
var imageProcessingTimeoutMs = 18e4;
|
|
82150
82216
|
var imageReadyPollIntervalMs = 2e3;
|
|
@@ -82158,7 +82224,7 @@ var mimeMap = {
|
|
|
82158
82224
|
".avif": "image/avif"
|
|
82159
82225
|
};
|
|
82160
82226
|
var defaultImageApiDeps = {
|
|
82161
|
-
readFile:
|
|
82227
|
+
readFile: readFile39,
|
|
82162
82228
|
post: apiPost,
|
|
82163
82229
|
get: apiGet,
|
|
82164
82230
|
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
|
|
@@ -82224,7 +82290,7 @@ async function waitForReadyImage(deps, imageId, opts = {}) {
|
|
|
82224
82290
|
}
|
|
82225
82291
|
|
|
82226
82292
|
// src/commands/videos/api.ts
|
|
82227
|
-
import { readFile as
|
|
82293
|
+
import { readFile as readFile40 } from "fs/promises";
|
|
82228
82294
|
import { basename as basename2, extname as extname2 } from "path";
|
|
82229
82295
|
var videoProcessingTimeoutMs = 18e4;
|
|
82230
82296
|
var videoReadyPollIntervalMs = 3e3;
|
|
@@ -82236,7 +82302,7 @@ var mimeMap2 = {
|
|
|
82236
82302
|
".mkv": "video/x-matroska"
|
|
82237
82303
|
};
|
|
82238
82304
|
var defaultVideoApiDeps = {
|
|
82239
|
-
readFile:
|
|
82305
|
+
readFile: readFile40,
|
|
82240
82306
|
post: apiPost,
|
|
82241
82307
|
get: apiGet,
|
|
82242
82308
|
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
|
|
@@ -82671,7 +82737,7 @@ Full guide: __tooling__/docs/tools/baker/creatives.md`
|
|
|
82671
82737
|
});
|
|
82672
82738
|
|
|
82673
82739
|
// ../api/src/credentials/contract.ts
|
|
82674
|
-
import { z as
|
|
82740
|
+
import { z as z70 } from "zod";
|
|
82675
82741
|
|
|
82676
82742
|
// ../../.pnpm-store/v10/links/@/tldts-core/7.4.15/00637277cac46663a792d6ddb7fbb80e2995ea75c3d789d599e3f6df41962ed7/node_modules/tldts-core/dist/es6/src/domain.js
|
|
82677
82743
|
function shareSameDomainSuffix(hostname, vhost) {
|
|
@@ -83490,8 +83556,8 @@ var WHOLE_PLACEHOLDER_RE = new RegExp(`^${PLACEHOLDER_SOURCE}$`);
|
|
|
83490
83556
|
// ../api/src/credentials/contract.ts
|
|
83491
83557
|
var MAX_CREDENTIAL_VALUE_LENGTH = 16384;
|
|
83492
83558
|
var MAX_CREDENTIAL_HOSTS = 20;
|
|
83493
|
-
var credentialNameSchema =
|
|
83494
|
-
var credentialHostsSchema =
|
|
83559
|
+
var credentialNameSchema = z70.string().regex(new RegExp(`^${CREDENTIAL_NAME_PATTERN}$`), "Use lowercase letters, digits, - and _ (max 64).");
|
|
83560
|
+
var credentialHostsSchema = z70.array(z70.string()).max(MAX_CREDENTIAL_HOSTS).transform((hosts, ctx) => {
|
|
83495
83561
|
const patterns = [];
|
|
83496
83562
|
for (const host of hosts) {
|
|
83497
83563
|
const result = validateHostPattern(host);
|
|
@@ -83500,34 +83566,34 @@ var credentialHostsSchema = z69.array(z69.string()).max(MAX_CREDENTIAL_HOSTS).tr
|
|
|
83500
83566
|
}
|
|
83501
83567
|
return [...new Set(patterns)];
|
|
83502
83568
|
});
|
|
83503
|
-
var credentialSourceSchema =
|
|
83504
|
-
var credentialSummarySchema =
|
|
83505
|
-
name:
|
|
83506
|
-
label:
|
|
83507
|
-
placeholder:
|
|
83508
|
-
allowedHosts:
|
|
83569
|
+
var credentialSourceSchema = z70.enum(["user", "agent", "chat-detected", "requested"]);
|
|
83570
|
+
var credentialSummarySchema = z70.object({
|
|
83571
|
+
name: z70.string(),
|
|
83572
|
+
label: z70.string(),
|
|
83573
|
+
placeholder: z70.string(),
|
|
83574
|
+
allowedHosts: z70.array(z70.string()),
|
|
83509
83575
|
source: credentialSourceSchema,
|
|
83510
|
-
lastUsedAt:
|
|
83576
|
+
lastUsedAt: z70.number().nullable()
|
|
83511
83577
|
});
|
|
83512
|
-
var credentialsListResponseSchema =
|
|
83513
|
-
var credentialsPutRequestSchema =
|
|
83578
|
+
var credentialsListResponseSchema = z70.object({ credentials: z70.array(credentialSummarySchema) });
|
|
83579
|
+
var credentialsPutRequestSchema = z70.object({
|
|
83514
83580
|
name: credentialNameSchema,
|
|
83515
|
-
label:
|
|
83516
|
-
value:
|
|
83581
|
+
label: z70.string().trim().min(1).max(80).optional(),
|
|
83582
|
+
value: z70.string().min(1).max(MAX_CREDENTIAL_VALUE_LENGTH),
|
|
83517
83583
|
allowedHosts: credentialHostsSchema
|
|
83518
83584
|
});
|
|
83519
|
-
var credentialsPutResponseSchema =
|
|
83520
|
-
name:
|
|
83521
|
-
placeholder:
|
|
83522
|
-
replaced:
|
|
83585
|
+
var credentialsPutResponseSchema = z70.object({
|
|
83586
|
+
name: z70.string(),
|
|
83587
|
+
placeholder: z70.string(),
|
|
83588
|
+
replaced: z70.boolean()
|
|
83523
83589
|
});
|
|
83524
|
-
var credentialsDeleteRequestSchema =
|
|
83525
|
-
var credentialsDeleteResponseSchema =
|
|
83526
|
-
var brokerResolveResponseSchema =
|
|
83527
|
-
credentials:
|
|
83590
|
+
var credentialsDeleteRequestSchema = z70.object({ name: credentialNameSchema });
|
|
83591
|
+
var credentialsDeleteResponseSchema = z70.object({ name: z70.string(), deleted: z70.literal(true) });
|
|
83592
|
+
var brokerResolveResponseSchema = z70.object({
|
|
83593
|
+
credentials: z70.array(z70.object({ name: z70.string(), value: z70.string(), allowedHosts: z70.array(z70.string()) }))
|
|
83528
83594
|
});
|
|
83529
|
-
var brokerUsageRequestSchema =
|
|
83530
|
-
uses:
|
|
83595
|
+
var brokerUsageRequestSchema = z70.object({
|
|
83596
|
+
uses: z70.array(z70.object({ name: credentialNameSchema, host: z70.string().max(253) })).max(100)
|
|
83531
83597
|
});
|
|
83532
83598
|
|
|
83533
83599
|
// ../api/src/credentials/rules.ts
|
|
@@ -83878,12 +83944,12 @@ Full guide: ${DOC}`
|
|
|
83878
83944
|
import { defineCommand as defineCommand164 } from "citty";
|
|
83879
83945
|
|
|
83880
83946
|
// src/commands/experiment/composition.ts
|
|
83881
|
-
import { readdir as readdir19, readFile as
|
|
83882
|
-
import
|
|
83947
|
+
import { readdir as readdir19, readFile as readFile42 } from "fs/promises";
|
|
83948
|
+
import path48 from "path";
|
|
83883
83949
|
|
|
83884
83950
|
// src/commands/landing/import-graph.ts
|
|
83885
|
-
import { readdir as readdir18, readFile as
|
|
83886
|
-
import
|
|
83951
|
+
import { readdir as readdir18, readFile as readFile41, stat as stat7 } from "fs/promises";
|
|
83952
|
+
import path47 from "path";
|
|
83887
83953
|
var SOURCE_EXT = /\.(astro|ts|tsx|js|jsx|mjs|cjs|vue|svelte)$/;
|
|
83888
83954
|
var RESOLVE_CANDIDATES = [
|
|
83889
83955
|
"",
|
|
@@ -83916,7 +83982,7 @@ async function walkFiles(dir) {
|
|
|
83916
83982
|
}
|
|
83917
83983
|
const out = [];
|
|
83918
83984
|
for (const entry of entries) {
|
|
83919
|
-
const full =
|
|
83985
|
+
const full = path47.join(dir, entry.name);
|
|
83920
83986
|
if (entry.isDirectory()) {
|
|
83921
83987
|
if (entry.name === "node_modules" || entry.name === "dist") continue;
|
|
83922
83988
|
out.push(...await walkFiles(full));
|
|
@@ -83930,8 +83996,8 @@ async function resolveSpecifier(specifier, fromFile, root) {
|
|
|
83930
83996
|
const bare = specifier.split("?")[0] ?? "";
|
|
83931
83997
|
if (bare.includes("*")) return null;
|
|
83932
83998
|
let base;
|
|
83933
|
-
if (bare.startsWith("/")) base =
|
|
83934
|
-
else if (bare.startsWith(".")) base =
|
|
83999
|
+
if (bare.startsWith("/")) base = path47.join(root, bare.slice(1));
|
|
84000
|
+
else if (bare.startsWith(".")) base = path47.resolve(path47.dirname(fromFile), bare);
|
|
83935
84001
|
else return null;
|
|
83936
84002
|
for (const suffix of RESOLVE_CANDIDATES) {
|
|
83937
84003
|
const candidate = base + suffix;
|
|
@@ -83953,13 +84019,13 @@ async function importedFiles(text3, file, root) {
|
|
|
83953
84019
|
}
|
|
83954
84020
|
async function landingGraphFiles(root, slug) {
|
|
83955
84021
|
const seen = /* @__PURE__ */ new Set();
|
|
83956
|
-
const queue = await walkFiles(
|
|
84022
|
+
const queue = await walkFiles(path47.join(root, "src/pages", slug));
|
|
83957
84023
|
while (queue.length > 0) {
|
|
83958
84024
|
const file = queue.pop();
|
|
83959
84025
|
if (file === void 0 || seen.has(file)) continue;
|
|
83960
84026
|
let text3;
|
|
83961
84027
|
try {
|
|
83962
|
-
text3 = await
|
|
84028
|
+
text3 = await readFile41(file, "utf8");
|
|
83963
84029
|
} catch {
|
|
83964
84030
|
continue;
|
|
83965
84031
|
}
|
|
@@ -83968,7 +84034,7 @@ async function landingGraphFiles(root, slug) {
|
|
|
83968
84034
|
if (!seen.has(target2)) queue.push(target2);
|
|
83969
84035
|
}
|
|
83970
84036
|
}
|
|
83971
|
-
return new Set([...seen].map((abs) =>
|
|
84037
|
+
return new Set([...seen].map((abs) => path47.relative(root, abs).split(path47.sep).join("/")));
|
|
83972
84038
|
}
|
|
83973
84039
|
|
|
83974
84040
|
// src/commands/experiment/composition.ts
|
|
@@ -84013,7 +84079,7 @@ function formsOnlyIn(these, those) {
|
|
|
84013
84079
|
}
|
|
84014
84080
|
async function knownFlows(root) {
|
|
84015
84081
|
try {
|
|
84016
|
-
const entries = await readdir19(
|
|
84082
|
+
const entries = await readdir19(path48.join(root, "src/lib/flow-engine/flows"), { withFileTypes: true });
|
|
84017
84083
|
return new Set(entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name));
|
|
84018
84084
|
} catch {
|
|
84019
84085
|
return /* @__PURE__ */ new Set();
|
|
@@ -84024,7 +84090,7 @@ async function readForms(root, slug) {
|
|
|
84024
84090
|
const [files, flows] = await Promise.all([landingGraphFiles(root, slug), knownFlows(root)]);
|
|
84025
84091
|
if (flows.size === 0) return [];
|
|
84026
84092
|
const texts = await Promise.all(
|
|
84027
|
-
[...files].filter((file) => /\.(astro|ts|tsx|js|mjs)$/.test(file)).map((file) =>
|
|
84093
|
+
[...files].filter((file) => /\.(astro|ts|tsx|js|mjs)$/.test(file)).map((file) => readFile42(path48.join(root, file), "utf8"))
|
|
84028
84094
|
);
|
|
84029
84095
|
return formsReferenced(texts, flows);
|
|
84030
84096
|
} catch {
|
|
@@ -84034,8 +84100,8 @@ async function readForms(root, slug) {
|
|
|
84034
84100
|
|
|
84035
84101
|
// src/commands/experiment/fold.ts
|
|
84036
84102
|
import { statSync } from "fs";
|
|
84037
|
-
import { cp as cp3, mkdir as
|
|
84038
|
-
import
|
|
84103
|
+
import { cp as cp3, mkdir as mkdir15, readdir as readdir20, readFile as readFile43, rm as rm8, stat as stat8, writeFile as writeFile25 } from "fs/promises";
|
|
84104
|
+
import path50 from "path";
|
|
84039
84105
|
|
|
84040
84106
|
// ../api/src/experiments/variants.ts
|
|
84041
84107
|
var VARIANT_SEPARATOR = "--";
|
|
@@ -84060,21 +84126,21 @@ function nextVariantNumber(page, existingSlugs, archived) {
|
|
|
84060
84126
|
}
|
|
84061
84127
|
|
|
84062
84128
|
// src/commands/landing/repoint.ts
|
|
84063
|
-
import
|
|
84129
|
+
import path49 from "path";
|
|
84064
84130
|
var QUOTED_RELATIVE = /(['"])(\.\.?\/[^'"]*)\1/g;
|
|
84065
84131
|
function isInside3(abs, dir) {
|
|
84066
|
-
return abs === dir || abs.startsWith(dir +
|
|
84132
|
+
return abs === dir || abs.startsWith(dir + path49.sep);
|
|
84067
84133
|
}
|
|
84068
84134
|
function toSpecifier(from, to) {
|
|
84069
|
-
const rel =
|
|
84135
|
+
const rel = path49.relative(from, to).split(path49.sep).join("/");
|
|
84070
84136
|
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
84071
84137
|
}
|
|
84072
84138
|
function repointSpecifier(spec, opts) {
|
|
84073
84139
|
if (!spec.startsWith(".")) return spec;
|
|
84074
|
-
const abs =
|
|
84140
|
+
const abs = path49.resolve(opts.fromDir, spec);
|
|
84075
84141
|
if (opts.forkedTargets?.has(abs)) {
|
|
84076
|
-
const withinControl =
|
|
84077
|
-
return toSpecifier(opts.toDir,
|
|
84142
|
+
const withinControl = path49.relative(opts.controlDir, abs);
|
|
84143
|
+
return toSpecifier(opts.toDir, path49.resolve(opts.variantDir, withinControl));
|
|
84078
84144
|
}
|
|
84079
84145
|
if (!isInside3(abs, opts.controlDir)) return spec;
|
|
84080
84146
|
return toSpecifier(opts.toDir, abs);
|
|
@@ -84086,9 +84152,9 @@ function repointFile(text3, opts) {
|
|
|
84086
84152
|
}
|
|
84087
84153
|
function repointMoved(text3, opts) {
|
|
84088
84154
|
return text3.replace(QUOTED_RELATIVE, (_match, quote, spec) => {
|
|
84089
|
-
const abs =
|
|
84090
|
-
const within =
|
|
84091
|
-
const target2 = within.startsWith("..") ||
|
|
84155
|
+
const abs = path49.resolve(opts.fromDir, spec);
|
|
84156
|
+
const within = path49.relative(opts.movedFrom, abs);
|
|
84157
|
+
const target2 = within.startsWith("..") || path49.isAbsolute(within) ? abs : path49.resolve(opts.movedTo, within);
|
|
84092
84158
|
return `${quote}${toSpecifier(opts.toDir, target2)}${quote}`;
|
|
84093
84159
|
});
|
|
84094
84160
|
}
|
|
@@ -84097,7 +84163,7 @@ function repointMoved(text3, opts) {
|
|
|
84097
84163
|
var ARCHIVE_ROOT = "src/_variants";
|
|
84098
84164
|
var QUOTED_RELATIVE2 = /(['"])(\.\.?\/[^'"]*)\1/g;
|
|
84099
84165
|
function posix2(p) {
|
|
84100
|
-
return p.split(
|
|
84166
|
+
return p.split(path50.sep).join("/");
|
|
84101
84167
|
}
|
|
84102
84168
|
async function isDir(p) {
|
|
84103
84169
|
try {
|
|
@@ -84116,39 +84182,39 @@ async function filesUnder(dir, prefix = "") {
|
|
|
84116
84182
|
const out = [];
|
|
84117
84183
|
for (const entry of entries) {
|
|
84118
84184
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
84119
|
-
if (entry.isDirectory()) out.push(...await filesUnder(
|
|
84185
|
+
if (entry.isDirectory()) out.push(...await filesUnder(path50.join(dir, entry.name), rel));
|
|
84120
84186
|
else out.push(rel);
|
|
84121
84187
|
}
|
|
84122
84188
|
return out.sort();
|
|
84123
84189
|
}
|
|
84124
84190
|
var TEXT_EXT = /\.(astro|ts|tsx|js|jsx|mjs|cjs|vue|svelte|md|json|css)$/;
|
|
84125
84191
|
function archiveRelPath(abs, base) {
|
|
84126
|
-
if (!abs.startsWith(base +
|
|
84127
|
-
return posix2(
|
|
84192
|
+
if (!abs.startsWith(base + path50.sep)) return null;
|
|
84193
|
+
return posix2(path50.relative(base, abs));
|
|
84128
84194
|
}
|
|
84129
84195
|
async function archiveArm(opts) {
|
|
84130
84196
|
const { root, armDir, pageDir, archiveDir, armSlug } = opts;
|
|
84131
84197
|
const targets = await archiveContents({ root, armDir, pageDir, armSlug });
|
|
84132
|
-
const archivedBySource = new Map([...targets].map(([rel, abs]) => [abs,
|
|
84198
|
+
const archivedBySource = new Map([...targets].map(([rel, abs]) => [abs, path50.join(archiveDir, rel)]));
|
|
84133
84199
|
const written = [];
|
|
84134
84200
|
for (const [rel, abs] of [...targets].sort()) {
|
|
84135
|
-
const dest =
|
|
84136
|
-
await
|
|
84201
|
+
const dest = path50.join(archiveDir, rel);
|
|
84202
|
+
await mkdir15(path50.dirname(dest), { recursive: true });
|
|
84137
84203
|
if (!TEXT_EXT.test(rel)) {
|
|
84138
84204
|
await cp3(abs, dest);
|
|
84139
84205
|
} else {
|
|
84140
|
-
const text3 = await
|
|
84141
|
-
await
|
|
84206
|
+
const text3 = await readFile43(abs, "utf8");
|
|
84207
|
+
await writeFile25(dest, repointIntoArchive(text3, abs, dest, archivedBySource), "utf8");
|
|
84142
84208
|
}
|
|
84143
|
-
written.push(posix2(
|
|
84209
|
+
written.push(posix2(path50.relative(root, dest)));
|
|
84144
84210
|
}
|
|
84145
84211
|
return written;
|
|
84146
84212
|
}
|
|
84147
84213
|
async function archiveContents(opts) {
|
|
84148
84214
|
const { root, armDir, pageDir, armSlug } = opts;
|
|
84149
84215
|
const graph = await landingGraphFiles(root, armSlug);
|
|
84150
|
-
const reachable = new Set([...graph].map((rel) =>
|
|
84151
|
-
for (const rel of await filesUnder(armDir)) reachable.add(
|
|
84216
|
+
const reachable = new Set([...graph].map((rel) => path50.resolve(root, rel)));
|
|
84217
|
+
for (const rel of await filesUnder(armDir)) reachable.add(path50.join(armDir, rel));
|
|
84152
84218
|
const sorted = [...reachable].sort();
|
|
84153
84219
|
const targets = /* @__PURE__ */ new Map();
|
|
84154
84220
|
for (const base of [pageDir, armDir]) {
|
|
@@ -84161,14 +84227,14 @@ async function archiveContents(opts) {
|
|
|
84161
84227
|
return targets;
|
|
84162
84228
|
}
|
|
84163
84229
|
function repointIntoArchive(text3, sourceAbs, destAbs, archivedBySource) {
|
|
84164
|
-
const fromDir =
|
|
84165
|
-
const toDir =
|
|
84230
|
+
const fromDir = path50.dirname(sourceAbs);
|
|
84231
|
+
const toDir = path50.dirname(destAbs);
|
|
84166
84232
|
return text3.replace(QUOTED_RELATIVE2, (match, quote, spec, offset) => {
|
|
84167
84233
|
const [specPath, suffix] = splitSuffix(spec);
|
|
84168
|
-
const target2 =
|
|
84234
|
+
const target2 = path50.resolve(fromDir, specPath);
|
|
84169
84235
|
const resolved = resolveInArchive(target2, archivedBySource) ?? resolveOnDisk(target2, isImportPosition(text3, offset));
|
|
84170
84236
|
if (resolved === null) return match;
|
|
84171
|
-
const rel = posix2(
|
|
84237
|
+
const rel = posix2(path50.relative(toDir, resolved));
|
|
84172
84238
|
return `${quote}${rel.startsWith(".") ? rel : `./${rel}`}${suffix}${quote}`;
|
|
84173
84239
|
});
|
|
84174
84240
|
}
|
|
@@ -84202,25 +84268,25 @@ async function promoteVariantFiles(opts) {
|
|
|
84202
84268
|
const written = [];
|
|
84203
84269
|
for (const rel of await filesUnder(variantDir)) {
|
|
84204
84270
|
if (rel === "_definition.md") continue;
|
|
84205
|
-
const from =
|
|
84206
|
-
const to =
|
|
84207
|
-
await
|
|
84271
|
+
const from = path50.join(variantDir, rel);
|
|
84272
|
+
const to = path50.join(pageDir, rel);
|
|
84273
|
+
await mkdir15(path50.dirname(to), { recursive: true });
|
|
84208
84274
|
if (!TEXT_EXT.test(rel)) {
|
|
84209
84275
|
await cp3(from, to);
|
|
84210
84276
|
} else {
|
|
84211
|
-
const text3 = await
|
|
84212
|
-
await
|
|
84277
|
+
const text3 = await readFile43(from, "utf8");
|
|
84278
|
+
await writeFile25(
|
|
84213
84279
|
to,
|
|
84214
84280
|
repointMoved(text3, {
|
|
84215
|
-
fromDir:
|
|
84216
|
-
toDir:
|
|
84281
|
+
fromDir: path50.dirname(from),
|
|
84282
|
+
toDir: path50.dirname(to),
|
|
84217
84283
|
movedFrom: variantDir,
|
|
84218
84284
|
movedTo: pageDir
|
|
84219
84285
|
}),
|
|
84220
84286
|
"utf8"
|
|
84221
84287
|
);
|
|
84222
84288
|
}
|
|
84223
|
-
written.push(posix2(
|
|
84289
|
+
written.push(posix2(path50.relative(root, to)));
|
|
84224
84290
|
}
|
|
84225
84291
|
return written;
|
|
84226
84292
|
}
|
|
@@ -84244,8 +84310,8 @@ function archiveRecord(target2, variantNumber) {
|
|
|
84244
84310
|
`;
|
|
84245
84311
|
}
|
|
84246
84312
|
async function foldExperiment(root, target2) {
|
|
84247
|
-
const pageDir =
|
|
84248
|
-
const variantDir =
|
|
84313
|
+
const pageDir = path50.resolve(root, "src/pages", target2.landingSlug);
|
|
84314
|
+
const variantDir = path50.resolve(root, "src/pages", target2.variantSlug);
|
|
84249
84315
|
const variantNumber = variantNumberOf(target2.variantSlug);
|
|
84250
84316
|
if (variantNumber === null) {
|
|
84251
84317
|
return {
|
|
@@ -84259,9 +84325,9 @@ async function foldExperiment(root, target2) {
|
|
|
84259
84325
|
return { error: `src/pages/${target2.variantSlug}/ is already gone \u2014 this test looks folded in already` };
|
|
84260
84326
|
}
|
|
84261
84327
|
const archiveKey = target2.landingInternalId ?? target2.landingSlug;
|
|
84262
|
-
const archiveDir =
|
|
84328
|
+
const archiveDir = path50.resolve(root, ARCHIVE_ROOT, archiveKey, String(variantNumber));
|
|
84263
84329
|
await rm8(archiveDir, { recursive: true, force: true });
|
|
84264
|
-
await
|
|
84330
|
+
await mkdir15(archiveDir, { recursive: true });
|
|
84265
84331
|
const written = await archiveArm({
|
|
84266
84332
|
root,
|
|
84267
84333
|
armDir: target2.survivor === "variant" ? pageDir : variantDir,
|
|
@@ -84269,8 +84335,8 @@ async function foldExperiment(root, target2) {
|
|
|
84269
84335
|
archiveDir,
|
|
84270
84336
|
armSlug: target2.survivor === "variant" ? target2.landingSlug : target2.variantSlug
|
|
84271
84337
|
});
|
|
84272
|
-
await
|
|
84273
|
-
written.push(posix2(
|
|
84338
|
+
await writeFile25(path50.join(archiveDir, "_variant.json"), archiveRecord(target2, variantNumber), "utf8");
|
|
84339
|
+
written.push(posix2(path50.join(ARCHIVE_ROOT, archiveKey, String(variantNumber), "_variant.json")));
|
|
84274
84340
|
if (target2.survivor === "variant") {
|
|
84275
84341
|
written.push(...await promoteVariantFiles({ root, pageDir, variantDir }));
|
|
84276
84342
|
}
|
|
@@ -84278,29 +84344,29 @@ async function foldExperiment(root, target2) {
|
|
|
84278
84344
|
return {
|
|
84279
84345
|
experimentId: target2.experimentId,
|
|
84280
84346
|
page: target2.landingSlug,
|
|
84281
|
-
archivedTo: posix2(
|
|
84347
|
+
archivedTo: posix2(path50.join(ARCHIVE_ROOT, archiveKey, String(variantNumber))),
|
|
84282
84348
|
written,
|
|
84283
84349
|
removed: [`src/pages/${target2.variantSlug}`]
|
|
84284
84350
|
};
|
|
84285
84351
|
}
|
|
84286
84352
|
|
|
84287
84353
|
// ../api/src/experiments/composition.ts
|
|
84288
|
-
import { z as
|
|
84289
|
-
var experimentCompositionSchema =
|
|
84354
|
+
import { z as z71 } from "zod";
|
|
84355
|
+
var experimentCompositionSchema = z71.object({
|
|
84290
84356
|
/** Components the variant owns its own copy of — the thing under test. */
|
|
84291
|
-
forked:
|
|
84357
|
+
forked: z71.array(z71.string().max(300)).max(50),
|
|
84292
84358
|
/** Components both pages render from one file, so an edit reaches both arms. */
|
|
84293
|
-
shared:
|
|
84359
|
+
shared: z71.array(z71.string().max(300)).max(200)
|
|
84294
84360
|
});
|
|
84295
84361
|
|
|
84296
84362
|
// ../api/src/experiments/goal.ts
|
|
84297
|
-
import { z as
|
|
84363
|
+
import { z as z72 } from "zod";
|
|
84298
84364
|
var GOAL_KEY_HELP = "A goal is one event, by its key \u2014 `form:<flow>:<node>:<trigger>`, `submit:<flow>`, `page:<name>` or `exit:<host>`. `baker analytics conversions --candidates` lists the ones these pages produce";
|
|
84299
|
-
var experimentGoalSchema =
|
|
84365
|
+
var experimentGoalSchema = z72.string().trim().min(1).max(400).refine((key) => parseEventKey(key) !== null, { message: GOAL_KEY_HELP });
|
|
84300
84366
|
|
|
84301
84367
|
// ../api/src/experiments/hypothesis.ts
|
|
84302
|
-
import { z as
|
|
84303
|
-
var experimentEvidenceSourceSchema =
|
|
84368
|
+
import { z as z73 } from "zod";
|
|
84369
|
+
var experimentEvidenceSourceSchema = z73.enum([
|
|
84304
84370
|
"baker_analytics",
|
|
84305
84371
|
"clarity",
|
|
84306
84372
|
"ads",
|
|
@@ -84310,17 +84376,17 @@ var experimentEvidenceSourceSchema = z72.enum([
|
|
|
84310
84376
|
"client",
|
|
84311
84377
|
"hunch"
|
|
84312
84378
|
]);
|
|
84313
|
-
var experimentEvidenceSchema =
|
|
84379
|
+
var experimentEvidenceSchema = z73.object({
|
|
84314
84380
|
source: experimentEvidenceSourceSchema,
|
|
84315
|
-
note:
|
|
84381
|
+
note: z73.string().min(1).max(200),
|
|
84316
84382
|
/**
|
|
84317
84383
|
* Where to go and look. Not a `z.url()` — a report inside Baker is reached by
|
|
84318
84384
|
* a relative path, and rejecting those would push every in-product citation
|
|
84319
84385
|
* into the note where nothing can link it.
|
|
84320
84386
|
*/
|
|
84321
|
-
href:
|
|
84387
|
+
href: z73.string().max(500).optional()
|
|
84322
84388
|
});
|
|
84323
|
-
var experimentHypothesisSchema =
|
|
84389
|
+
var experimentHypothesisSchema = z73.object({
|
|
84324
84390
|
/**
|
|
84325
84391
|
* The observation. The one genuinely new field, and the reason for the rest.
|
|
84326
84392
|
*
|
|
@@ -84328,7 +84394,7 @@ var experimentHypothesisSchema = z72.object({
|
|
|
84328
84394
|
* result to be evidence against — which is how a testing programme ends up as
|
|
84329
84395
|
* a list of tests instead of a list of things learned.
|
|
84330
84396
|
*/
|
|
84331
|
-
because:
|
|
84397
|
+
because: z73.string().min(1).max(300),
|
|
84332
84398
|
/**
|
|
84333
84399
|
* The change, in the client's words.
|
|
84334
84400
|
*
|
|
@@ -84336,12 +84402,12 @@ var experimentHypothesisSchema = z72.object({
|
|
|
84336
84402
|
* a person reads. The two are allowed to differ in wording and cannot differ
|
|
84337
84403
|
* in fact, because the brief shows both.
|
|
84338
84404
|
*/
|
|
84339
|
-
change:
|
|
84405
|
+
change: z73.string().min(1).max(200),
|
|
84340
84406
|
/** Which way the goal is expected to move. See the note at the top of this file. */
|
|
84341
|
-
expect:
|
|
84342
|
-
evidence:
|
|
84407
|
+
expect: z73.enum(["increase", "decrease"]).default("increase"),
|
|
84408
|
+
evidence: z73.array(experimentEvidenceSchema).max(5).optional()
|
|
84343
84409
|
});
|
|
84344
|
-
var experimentHypothesisOutcomeSchema =
|
|
84410
|
+
var experimentHypothesisOutcomeSchema = z73.enum([
|
|
84345
84411
|
"supported",
|
|
84346
84412
|
"refuted_opposite",
|
|
84347
84413
|
"refuted_no_effect",
|
|
@@ -84368,13 +84434,13 @@ function roughCount(count2) {
|
|
|
84368
84434
|
}
|
|
84369
84435
|
|
|
84370
84436
|
// ../api/src/experiments/wire.ts
|
|
84371
|
-
import { z as
|
|
84372
|
-
var slugSchema =
|
|
84373
|
-
var variantSlugSchema =
|
|
84437
|
+
import { z as z74 } from "zod";
|
|
84438
|
+
var slugSchema = z74.string().min(1).max(120).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Use a page slug, like `oferta`");
|
|
84439
|
+
var variantSlugSchema = z74.string().min(1).max(130).refine((slug) => {
|
|
84374
84440
|
const page = variantOfSlug(slug);
|
|
84375
84441
|
return page !== null && slugSchema.safeParse(page).success;
|
|
84376
84442
|
}, "Use the variant's slug, `<page>--<n>` \u2014 `baker landing variant <page>` chooses it");
|
|
84377
|
-
var experimentPlanRequestSchema =
|
|
84443
|
+
var experimentPlanRequestSchema = z74.object({
|
|
84378
84444
|
landingSlug: slugSchema,
|
|
84379
84445
|
variantSlug: variantSlugSchema,
|
|
84380
84446
|
/**
|
|
@@ -84403,7 +84469,7 @@ var experimentPlanRequestSchema = z73.object({
|
|
|
84403
84469
|
* quarter of a million visitors per arm; `plan` will say so rather than
|
|
84404
84470
|
* running it.
|
|
84405
84471
|
*/
|
|
84406
|
-
minDetectableRelativeLift:
|
|
84472
|
+
minDetectableRelativeLift: z74.number().gt(0).max(10).optional(),
|
|
84407
84473
|
/**
|
|
84408
84474
|
* The share of visitors expected to do the goal, as a fraction — `0.05` is
|
|
84409
84475
|
* 5% — for a page whose history cannot say.
|
|
@@ -84414,14 +84480,14 @@ var experimentPlanRequestSchema = z73.object({
|
|
|
84414
84480
|
* Either way the brief says which, because the sizing is a guide and never a
|
|
84415
84481
|
* finish line — the test runs until the numbers are clear.
|
|
84416
84482
|
*/
|
|
84417
|
-
baselineRate:
|
|
84483
|
+
baselineRate: z74.number().gt(0).lt(1).optional()
|
|
84418
84484
|
});
|
|
84419
|
-
var experimentTrafficSchema =
|
|
84420
|
-
var experimentBaselineSourceSchema =
|
|
84421
|
-
var experimentSizedOnSchema =
|
|
84422
|
-
visitors:
|
|
84423
|
-
convertingVisitors:
|
|
84424
|
-
windowDays:
|
|
84485
|
+
var experimentTrafficSchema = z74.enum(["none", "low", "enough"]);
|
|
84486
|
+
var experimentBaselineSourceSchema = z74.enum(["page", "company", "assumed"]);
|
|
84487
|
+
var experimentSizedOnSchema = z74.object({
|
|
84488
|
+
visitors: z74.number(),
|
|
84489
|
+
convertingVisitors: z74.number(),
|
|
84490
|
+
windowDays: z74.number()
|
|
84425
84491
|
});
|
|
84426
84492
|
var experimentStartRequestSchema = experimentPlanRequestSchema.extend({
|
|
84427
84493
|
hypothesis: experimentHypothesisSchema,
|
|
@@ -84435,8 +84501,8 @@ var experimentStartRequestSchema = experimentPlanRequestSchema.extend({
|
|
|
84435
84501
|
* loser under the wrong key. Optional because a workspace that cannot read
|
|
84436
84502
|
* its own definition must still be able to start a test.
|
|
84437
84503
|
*/
|
|
84438
|
-
landingInternalId:
|
|
84439
|
-
variantInternalId:
|
|
84504
|
+
landingInternalId: z74.string().min(1).max(64).optional(),
|
|
84505
|
+
variantInternalId: z74.string().min(1).max(64).optional(),
|
|
84440
84506
|
/**
|
|
84441
84507
|
* What the two arms are made of, read off the import graph by the caller at
|
|
84442
84508
|
* the moment the test starts.
|
|
@@ -84448,21 +84514,21 @@ var experimentStartRequestSchema = experimentPlanRequestSchema.extend({
|
|
|
84448
84514
|
*/
|
|
84449
84515
|
composition: experimentCompositionSchema.optional()
|
|
84450
84516
|
});
|
|
84451
|
-
var experimentKeepSchema =
|
|
84452
|
-
var experimentEndReasonSchema =
|
|
84453
|
-
var experimentLearningSchema =
|
|
84454
|
-
var experimentFinishRequestSchema =
|
|
84455
|
-
experimentId:
|
|
84517
|
+
var experimentKeepSchema = z74.enum(["original", "variant"]);
|
|
84518
|
+
var experimentEndReasonSchema = z74.enum(["numbers_clear", "not_settling", "page_changing", "test_flawed"]);
|
|
84519
|
+
var experimentLearningSchema = z74.string().trim().min(1).max(2e3);
|
|
84520
|
+
var experimentFinishRequestSchema = z74.object({
|
|
84521
|
+
experimentId: z74.string().min(1).max(64),
|
|
84456
84522
|
keep: experimentKeepSchema,
|
|
84457
84523
|
/** Why it is being ended. The dashboard always asks; the agent should say. */
|
|
84458
84524
|
reason: experimentEndReasonSchema.optional(),
|
|
84459
84525
|
learning: experimentLearningSchema.optional()
|
|
84460
84526
|
});
|
|
84461
|
-
var experimentPauseRequestSchema =
|
|
84462
|
-
experimentId:
|
|
84527
|
+
var experimentPauseRequestSchema = z74.object({
|
|
84528
|
+
experimentId: z74.string().min(1).max(64)
|
|
84463
84529
|
});
|
|
84464
|
-
var experimentUpdateRequestSchema =
|
|
84465
|
-
experimentId:
|
|
84530
|
+
var experimentUpdateRequestSchema = z74.object({
|
|
84531
|
+
experimentId: z74.string().min(1).max(64),
|
|
84466
84532
|
because: experimentHypothesisSchema.shape.because.optional(),
|
|
84467
84533
|
change: experimentHypothesisSchema.shape.change.optional(),
|
|
84468
84534
|
evidence: experimentHypothesisSchema.shape.evidence,
|
|
@@ -84472,26 +84538,26 @@ var experimentUpdateRequestSchema = z73.object({
|
|
|
84472
84538
|
* default still fires, so every wording-only update arrived carrying a
|
|
84473
84539
|
* direction and was refused for trying to change it.
|
|
84474
84540
|
*/
|
|
84475
|
-
expect:
|
|
84541
|
+
expect: z74.enum(["increase", "decrease"]).optional(),
|
|
84476
84542
|
/** Staged tests only. */
|
|
84477
84543
|
goal: experimentGoalSchema.optional(),
|
|
84478
84544
|
/** Staged tests only. The new version's own event, when it differs. */
|
|
84479
84545
|
variantGoal: experimentGoalSchema.optional(),
|
|
84480
84546
|
/** Staged tests only. */
|
|
84481
|
-
minDetectableRelativeLift:
|
|
84547
|
+
minDetectableRelativeLift: z74.number().gt(0).max(10).optional(),
|
|
84482
84548
|
/** Finished tests only: what the test taught, written or corrected after the fact. */
|
|
84483
84549
|
learning: experimentLearningSchema.optional()
|
|
84484
84550
|
});
|
|
84485
|
-
var experimentUpdateResponseSchema =
|
|
84551
|
+
var experimentUpdateResponseSchema = z74.object({
|
|
84486
84552
|
/** True when the test had not been published, so its whole plan was re-sized. */
|
|
84487
|
-
staged:
|
|
84488
|
-
hypothesisStatement:
|
|
84553
|
+
staged: z74.boolean(),
|
|
84554
|
+
hypothesisStatement: z74.string()
|
|
84489
84555
|
});
|
|
84490
|
-
var experimentFoldRequestSchema =
|
|
84556
|
+
var experimentFoldRequestSchema = z74.object({
|
|
84491
84557
|
/** Which test to fold. Omitted ⇒ every fold this company owes. */
|
|
84492
|
-
experimentId:
|
|
84558
|
+
experimentId: z74.string().max(64).optional(),
|
|
84493
84559
|
/** The files are written. Baker drops the promotion once the build lands. */
|
|
84494
|
-
applied:
|
|
84560
|
+
applied: z74.boolean().optional(),
|
|
84495
84561
|
/**
|
|
84496
84562
|
* Which of the owed folds were actually written, on an `applied` call.
|
|
84497
84563
|
*
|
|
@@ -84502,68 +84568,68 @@ var experimentFoldRequestSchema = z73.object({
|
|
|
84502
84568
|
* its page served the version that LOST, with nothing owed and nothing
|
|
84503
84569
|
* logged. Omitted ⇒ everything owed, which is the whole-company case.
|
|
84504
84570
|
*/
|
|
84505
|
-
experimentIds:
|
|
84571
|
+
experimentIds: z74.array(z74.string().max(64)).max(200).optional()
|
|
84506
84572
|
});
|
|
84507
|
-
var experimentFoldTargetSchema =
|
|
84508
|
-
experimentId:
|
|
84573
|
+
var experimentFoldTargetSchema = z74.object({
|
|
84574
|
+
experimentId: z74.string(),
|
|
84509
84575
|
/** The page. Keeps its slug, its identity, its history and its ads. */
|
|
84510
|
-
landingSlug:
|
|
84576
|
+
landingSlug: z74.string(),
|
|
84511
84577
|
/** The variant folder, `<page>--<n>`. Gone from `src/pages/` afterwards. */
|
|
84512
|
-
variantSlug:
|
|
84578
|
+
variantSlug: z74.string(),
|
|
84513
84579
|
/** Which variant won, and therefore which files become the page's own. */
|
|
84514
|
-
survivor:
|
|
84580
|
+
survivor: z74.union([z74.literal("control"), z74.literal("variant")]),
|
|
84515
84581
|
/** For the archive path, which is keyed on identity rather than on a slug. */
|
|
84516
|
-
landingInternalId:
|
|
84582
|
+
landingInternalId: z74.string().nullable()
|
|
84517
84583
|
});
|
|
84518
|
-
var experimentFoldResponseSchema =
|
|
84519
|
-
owed:
|
|
84584
|
+
var experimentFoldResponseSchema = z74.object({
|
|
84585
|
+
owed: z74.array(experimentFoldTargetSchema),
|
|
84520
84586
|
/** How many were recorded as written, when `applied` was set. */
|
|
84521
|
-
recorded:
|
|
84587
|
+
recorded: z74.number().optional()
|
|
84522
84588
|
});
|
|
84523
|
-
var experimentStatusRequestSchema =
|
|
84524
|
-
experimentId:
|
|
84589
|
+
var experimentStatusRequestSchema = z74.object({
|
|
84590
|
+
experimentId: z74.string().max(64).optional(),
|
|
84525
84591
|
/** Only the tests on one page, by its slug. */
|
|
84526
|
-
landingSlug:
|
|
84527
|
-
full:
|
|
84592
|
+
landingSlug: z74.string().max(200).optional(),
|
|
84593
|
+
full: z74.boolean().optional()
|
|
84528
84594
|
});
|
|
84529
|
-
var experimentHistoryRequestSchema =
|
|
84530
|
-
landingSlug:
|
|
84531
|
-
limit:
|
|
84595
|
+
var experimentHistoryRequestSchema = z74.object({
|
|
84596
|
+
landingSlug: z74.string().max(200).optional(),
|
|
84597
|
+
limit: z74.number().int().positive().max(100).optional()
|
|
84532
84598
|
});
|
|
84533
|
-
var experimentPageConversionSchema =
|
|
84599
|
+
var experimentPageConversionSchema = z74.object({
|
|
84534
84600
|
/** The event, by key — what `--goal` takes. */
|
|
84535
|
-
eventKey:
|
|
84601
|
+
eventKey: z74.string(),
|
|
84536
84602
|
/** The conversion name the company counts it under, or null when nobody has named it. */
|
|
84537
|
-
name:
|
|
84603
|
+
name: z74.string().nullable(),
|
|
84538
84604
|
/** Distinct visitors to the page who went on to do this. */
|
|
84539
|
-
convertingVisitors:
|
|
84605
|
+
convertingVisitors: z74.number()
|
|
84540
84606
|
});
|
|
84541
|
-
var experimentHorizonSchema =
|
|
84542
|
-
days:
|
|
84607
|
+
var experimentHorizonSchema = z74.object({
|
|
84608
|
+
days: z74.number(),
|
|
84543
84609
|
/** The smallest relative lift detectable in that many days. `null` when none would be. */
|
|
84544
|
-
detectableLift:
|
|
84610
|
+
detectableLift: z74.number().nullable()
|
|
84545
84611
|
});
|
|
84546
|
-
var experimentPlanResponseSchema =
|
|
84547
|
-
canRun:
|
|
84612
|
+
var experimentPlanResponseSchema = z74.object({
|
|
84613
|
+
canRun: z74.boolean(),
|
|
84548
84614
|
/** Present when `canRun` is false. Product language, ready to show. */
|
|
84549
|
-
reason:
|
|
84615
|
+
reason: z74.string().optional(),
|
|
84550
84616
|
/** The event the test would be read on. Absent when none could be chosen. */
|
|
84551
84617
|
goal: experimentGoalSchema.optional(),
|
|
84552
84618
|
/** The new version's own event, when one was given. */
|
|
84553
84619
|
variantGoal: experimentGoalSchema.optional(),
|
|
84554
84620
|
/** Every event this page's visitors went on to do over the sizing window, most common first. */
|
|
84555
|
-
conversions:
|
|
84621
|
+
conversions: z74.array(experimentPageConversionSchema),
|
|
84556
84622
|
/** Distinct visitors the page had over the sizing window. */
|
|
84557
|
-
visitors:
|
|
84558
|
-
baselineRate:
|
|
84559
|
-
minDetectableRelativeLift:
|
|
84623
|
+
visitors: z74.number(),
|
|
84624
|
+
baselineRate: z74.number(),
|
|
84625
|
+
minDetectableRelativeLift: z74.number(),
|
|
84560
84626
|
/** `null` when no amount of traffic could settle the hypothesis. */
|
|
84561
|
-
requiredPerVariant:
|
|
84627
|
+
requiredPerVariant: z74.number().nullable(),
|
|
84562
84628
|
/** Visitors a day the page has been getting, per arm once split. */
|
|
84563
|
-
dailyVisitorsPerVariant:
|
|
84629
|
+
dailyVisitorsPerVariant: z74.number(),
|
|
84564
84630
|
/** `null` when the page has no traffic to project from. */
|
|
84565
|
-
estimatedDays:
|
|
84566
|
-
horizons:
|
|
84631
|
+
estimatedDays: z74.number().nullable(),
|
|
84632
|
+
horizons: z74.array(experimentHorizonSchema),
|
|
84567
84633
|
traffic: experimentTrafficSchema,
|
|
84568
84634
|
baselineSource: experimentBaselineSourceSchema,
|
|
84569
84635
|
sizedOn: experimentSizedOnSchema,
|
|
@@ -84572,7 +84638,7 @@ var experimentPlanResponseSchema = z73.object({
|
|
|
84572
84638
|
* Product language, ready to show; absent on a page with real traffic that
|
|
84573
84639
|
* has produced the goal.
|
|
84574
84640
|
*/
|
|
84575
|
-
warning:
|
|
84641
|
+
warning: z74.string().optional(),
|
|
84576
84642
|
/**
|
|
84577
84643
|
* Every finished test on this page, newest first — see
|
|
84578
84644
|
* `experimentHistoryEntrySchema`. On the plan because the plan is the moment
|
|
@@ -84580,9 +84646,9 @@ var experimentPlanResponseSchema = z73.object({
|
|
|
84580
84646
|
* should inform it: a refuted idea proposed again, or a supported one never
|
|
84581
84647
|
* built on, is what this exists to prevent.
|
|
84582
84648
|
*/
|
|
84583
|
-
pastTests:
|
|
84649
|
+
pastTests: z74.array(z74.lazy(() => experimentHistoryEntrySchema)).default([])
|
|
84584
84650
|
});
|
|
84585
|
-
var experimentVerdictSchema =
|
|
84651
|
+
var experimentVerdictSchema = z74.enum([
|
|
84586
84652
|
"keep_running",
|
|
84587
84653
|
"winner",
|
|
84588
84654
|
"no_difference",
|
|
@@ -84609,12 +84675,12 @@ var experimentVerdictSchema = z73.enum([
|
|
|
84609
84675
|
*/
|
|
84610
84676
|
"inconclusive"
|
|
84611
84677
|
]);
|
|
84612
|
-
var experimentStatusValueSchema =
|
|
84613
|
-
var experimentArmSchema =
|
|
84614
|
-
slug:
|
|
84615
|
-
visitors:
|
|
84616
|
-
conversions:
|
|
84617
|
-
rate:
|
|
84678
|
+
var experimentStatusValueSchema = z74.enum(["running", "paused", "finished"]);
|
|
84679
|
+
var experimentArmSchema = z74.object({
|
|
84680
|
+
slug: z74.string(),
|
|
84681
|
+
visitors: z74.number(),
|
|
84682
|
+
conversions: z74.number(),
|
|
84683
|
+
rate: z74.number(),
|
|
84618
84684
|
/**
|
|
84619
84685
|
* The page's own preview shot, so a reader can see which version this is.
|
|
84620
84686
|
*
|
|
@@ -84623,7 +84689,7 @@ var experimentArmSchema = z73.object({
|
|
|
84623
84689
|
* screens cannot show a client two different "this is your offer page".
|
|
84624
84690
|
* `null` before the first capture, and on a page whose capture failed.
|
|
84625
84691
|
*/
|
|
84626
|
-
previewUrl:
|
|
84692
|
+
previewUrl: z74.string().nullable().optional(),
|
|
84627
84693
|
/**
|
|
84628
84694
|
* This version as it was when the test started — the test's own capture,
|
|
84629
84695
|
* taken off the live site once the split was being served.
|
|
@@ -84633,41 +84699,41 @@ var experimentArmSchema = z73.object({
|
|
|
84633
84699
|
* altogether for a variant that lost and was archived. This is the record;
|
|
84634
84700
|
* a card prefers it and says which of the two it is showing.
|
|
84635
84701
|
*/
|
|
84636
|
-
snapshotUrl:
|
|
84637
|
-
snapshotCapturedAt:
|
|
84638
|
-
});
|
|
84639
|
-
var experimentMetricSchema =
|
|
84640
|
-
name:
|
|
84641
|
-
control:
|
|
84642
|
-
variant:
|
|
84643
|
-
relativeLift:
|
|
84702
|
+
snapshotUrl: z74.string().nullable().optional(),
|
|
84703
|
+
snapshotCapturedAt: z74.number().nullable().optional()
|
|
84704
|
+
});
|
|
84705
|
+
var experimentMetricSchema = z74.object({
|
|
84706
|
+
name: z74.string(),
|
|
84707
|
+
control: z74.object({ conversions: z74.number(), rate: z74.number() }),
|
|
84708
|
+
variant: z74.object({ conversions: z74.number(), rate: z74.number() }),
|
|
84709
|
+
relativeLift: z74.number(),
|
|
84644
84710
|
/** The anytime-valid interval on the relative lift. */
|
|
84645
|
-
liftInterval:
|
|
84711
|
+
liftInterval: z74.tuple([z74.number(), z74.number()]),
|
|
84646
84712
|
/** `better` when the interval sits above zero, `worse` below it, `unclear` across it. */
|
|
84647
|
-
reading:
|
|
84713
|
+
reading: z74.enum(["better", "worse", "unclear"])
|
|
84648
84714
|
});
|
|
84649
|
-
var experimentTimelineArmSchema =
|
|
84650
|
-
visitors:
|
|
84651
|
-
conversions:
|
|
84652
|
-
rate:
|
|
84715
|
+
var experimentTimelineArmSchema = z74.object({
|
|
84716
|
+
visitors: z74.number(),
|
|
84717
|
+
conversions: z74.number(),
|
|
84718
|
+
rate: z74.number().nullable()
|
|
84653
84719
|
});
|
|
84654
|
-
var experimentTimelinePointSchema =
|
|
84720
|
+
var experimentTimelinePointSchema = z74.object({
|
|
84655
84721
|
/** Bucket start, ISO 8601. */
|
|
84656
|
-
bucket:
|
|
84722
|
+
bucket: z74.string(),
|
|
84657
84723
|
control: experimentTimelineArmSchema,
|
|
84658
84724
|
variant: experimentTimelineArmSchema
|
|
84659
84725
|
});
|
|
84660
|
-
var experimentStatusRowSchema =
|
|
84661
|
-
experimentId:
|
|
84662
|
-
landingSlug:
|
|
84663
|
-
variantSlug:
|
|
84726
|
+
var experimentStatusRowSchema = z74.object({
|
|
84727
|
+
experimentId: z74.string(),
|
|
84728
|
+
landingSlug: z74.string(),
|
|
84729
|
+
variantSlug: z74.string(),
|
|
84664
84730
|
hypothesis: experimentHypothesisSchema,
|
|
84665
84731
|
/**
|
|
84666
84732
|
* The hypothesis as one sentence, built from its parts plus the goal and the
|
|
84667
84733
|
* lift on this same row. Rendered backend-side so what a client reads in chat
|
|
84668
84734
|
* and what they read on the dashboard can never be two different sentences.
|
|
84669
84735
|
*/
|
|
84670
|
-
hypothesisStatement:
|
|
84736
|
+
hypothesisStatement: z74.string(),
|
|
84671
84737
|
/**
|
|
84672
84738
|
* What the test found out about the belief — derived from `verdict` and
|
|
84673
84739
|
* `winner`, never entered.
|
|
@@ -84684,28 +84750,28 @@ var experimentStatusRowSchema = z73.object({
|
|
|
84684
84750
|
* the plan because the claim is not readable without it — "leads should go up"
|
|
84685
84751
|
* is not a hypothesis, "leads should go up by at least 20%" is.
|
|
84686
84752
|
*/
|
|
84687
|
-
minDetectableRelativeLift:
|
|
84753
|
+
minDetectableRelativeLift: z74.number(),
|
|
84688
84754
|
/** The event the verdict is read from on the page, by key. */
|
|
84689
84755
|
goal: experimentGoalSchema,
|
|
84690
84756
|
/** The event the new version is read on, when it differs from `goal`. */
|
|
84691
84757
|
variantGoal: experimentGoalSchema.nullable(),
|
|
84692
84758
|
/** Both, in words a marketer reads — the Form's name, the moment. */
|
|
84693
|
-
goalLabel:
|
|
84759
|
+
goalLabel: z74.string(),
|
|
84694
84760
|
status: experimentStatusValueSchema,
|
|
84695
|
-
startedAt:
|
|
84761
|
+
startedAt: z74.number(),
|
|
84696
84762
|
/** Set while paused. The split is off and everyone sees the original. */
|
|
84697
|
-
pausedAt:
|
|
84698
|
-
finishedAt:
|
|
84763
|
+
pausedAt: z74.number().nullable(),
|
|
84764
|
+
finishedAt: z74.number().nullable(),
|
|
84699
84765
|
verdict: experimentVerdictSchema,
|
|
84700
84766
|
/** Set only on `winner`. Which version the numbers favour. */
|
|
84701
|
-
winner:
|
|
84767
|
+
winner: z74.enum(["control", "variant"]).nullable(),
|
|
84702
84768
|
/**
|
|
84703
84769
|
* Which version is live, once the test has ended. Chosen by whoever ended it,
|
|
84704
84770
|
* and allowed to disagree with `winner` — the card says so when it does.
|
|
84705
84771
|
*/
|
|
84706
|
-
survivor:
|
|
84772
|
+
survivor: z74.enum(["control", "variant"]).nullable(),
|
|
84707
84773
|
/** Set only on `invalid`. Why the numbers cannot be read. */
|
|
84708
|
-
invalidReason:
|
|
84774
|
+
invalidReason: z74.enum(["sample_ratio_mismatch", "crossover"]).nullable(),
|
|
84709
84775
|
/**
|
|
84710
84776
|
* Where the page's own content stands after the test, once it has ended.
|
|
84711
84777
|
*
|
|
@@ -84719,17 +84785,17 @@ var experimentStatusRowSchema = z73.object({
|
|
|
84719
84785
|
* version" links still show the right file, and whether there is anything
|
|
84720
84786
|
* left for a person to do.
|
|
84721
84787
|
*/
|
|
84722
|
-
fold:
|
|
84788
|
+
fold: z74.enum(["none", "owed", "landing", "done"]).nullable(),
|
|
84723
84789
|
/** What a person should be told, in one sentence, in product language. */
|
|
84724
|
-
summary:
|
|
84790
|
+
summary: z74.string(),
|
|
84725
84791
|
/** What the test taught, once somebody wrote it down. Null until then, and always null while it runs. */
|
|
84726
|
-
learning:
|
|
84792
|
+
learning: z74.string().nullable(),
|
|
84727
84793
|
/** Why it was ended, when whoever ended it said. Null while it runs. */
|
|
84728
84794
|
endReason: experimentEndReasonSchema.nullable(),
|
|
84729
84795
|
control: experimentArmSchema,
|
|
84730
84796
|
variant: experimentArmSchema,
|
|
84731
84797
|
/** Every other conversion the company counts, on the same two arms. */
|
|
84732
|
-
secondary:
|
|
84798
|
+
secondary: z74.array(experimentMetricSchema),
|
|
84733
84799
|
/**
|
|
84734
84800
|
* How close this test is to being able to say anything at all, 0 to 1.
|
|
84735
84801
|
*
|
|
@@ -84738,9 +84804,9 @@ var experimentStatusRowSchema = z73.object({
|
|
|
84738
84804
|
* measured variance says it needs, so it moves when the page gets noisier or
|
|
84739
84805
|
* steadier rather than only when visitors arrive.
|
|
84740
84806
|
*/
|
|
84741
|
-
precision:
|
|
84807
|
+
precision: z74.number(),
|
|
84742
84808
|
/** When the test could first say something. `null` with no traffic to project from. */
|
|
84743
|
-
earliestDecisionAt:
|
|
84809
|
+
earliestDecisionAt: z74.number().nullable(),
|
|
84744
84810
|
/**
|
|
84745
84811
|
* What the test is still missing before its verdict changes, if the gap it
|
|
84746
84812
|
* is seeing holds: visitors across both arms, and the conversions they bring.
|
|
@@ -84748,13 +84814,13 @@ var experimentStatusRowSchema = z73.object({
|
|
|
84748
84814
|
* divided by the page's pace. Null once it has spoken, before anyone has
|
|
84749
84815
|
* arrived, or when no amount of traffic would settle it.
|
|
84750
84816
|
*/
|
|
84751
|
-
stillNeeded:
|
|
84817
|
+
stillNeeded: z74.object({ visitors: z74.number(), conversions: z74.number() }).nullable().default(null),
|
|
84752
84818
|
/**
|
|
84753
84819
|
* True when no amount of this page's traffic would settle the comparison.
|
|
84754
84820
|
* A different thing from a page that is merely slow, and the reason to end
|
|
84755
84821
|
* a test rather than leave it running.
|
|
84756
84822
|
*/
|
|
84757
|
-
willNotConclude:
|
|
84823
|
+
willNotConclude: z74.boolean(),
|
|
84758
84824
|
/**
|
|
84759
84825
|
* How the two arms got where they are, one running total per bucket.
|
|
84760
84826
|
*
|
|
@@ -84763,74 +84829,74 @@ var experimentStatusRowSchema = z73.object({
|
|
|
84763
84829
|
* already told it in one word — the same reason everything else here is
|
|
84764
84830
|
* compact by default.
|
|
84765
84831
|
*/
|
|
84766
|
-
timeline:
|
|
84832
|
+
timeline: z74.array(experimentTimelinePointSchema).optional(),
|
|
84767
84833
|
/** Printing only. Never a reason to act — `verdict` is. */
|
|
84768
|
-
evidence:
|
|
84769
|
-
relativeLift:
|
|
84770
|
-
probabilityVariantBetter:
|
|
84834
|
+
evidence: z74.object({
|
|
84835
|
+
relativeLift: z74.number(),
|
|
84836
|
+
probabilityVariantBetter: z74.number(),
|
|
84771
84837
|
/**
|
|
84772
84838
|
* How sure the numbers are that the two versions differ at all, 0 to 1.
|
|
84773
84839
|
* Anytime-valid — the same construction the verdict is read from, so it
|
|
84774
84840
|
* holds however often it is read — and a winner is called exactly when
|
|
84775
84841
|
* it crosses 95%. The figure a card leads with.
|
|
84776
84842
|
*/
|
|
84777
|
-
confidence:
|
|
84778
|
-
controlInterval:
|
|
84779
|
-
variantInterval:
|
|
84843
|
+
confidence: z74.number(),
|
|
84844
|
+
controlInterval: z74.tuple([z74.number(), z74.number()]),
|
|
84845
|
+
variantInterval: z74.tuple([z74.number(), z74.number()]),
|
|
84780
84846
|
/** The anytime-valid interval on the relative lift — what the verdict is read from. */
|
|
84781
|
-
liftInterval:
|
|
84847
|
+
liftInterval: z74.tuple([z74.number(), z74.number()])
|
|
84782
84848
|
}).optional()
|
|
84783
84849
|
});
|
|
84784
|
-
var experimentStatusResponseSchema =
|
|
84785
|
-
experiments:
|
|
84850
|
+
var experimentStatusResponseSchema = z74.object({
|
|
84851
|
+
experiments: z74.array(experimentStatusRowSchema)
|
|
84786
84852
|
});
|
|
84787
|
-
var experimentHistoryEntrySchema =
|
|
84788
|
-
experimentId:
|
|
84789
|
-
landingSlug:
|
|
84790
|
-
variantSlug:
|
|
84791
|
-
startedAt:
|
|
84792
|
-
finishedAt:
|
|
84853
|
+
var experimentHistoryEntrySchema = z74.object({
|
|
84854
|
+
experimentId: z74.string(),
|
|
84855
|
+
landingSlug: z74.string(),
|
|
84856
|
+
variantSlug: z74.string(),
|
|
84857
|
+
startedAt: z74.number(),
|
|
84858
|
+
finishedAt: z74.number(),
|
|
84793
84859
|
/** Days the split ran, whole. */
|
|
84794
|
-
ranForDays:
|
|
84860
|
+
ranForDays: z74.number(),
|
|
84795
84861
|
hypothesis: experimentHypothesisSchema,
|
|
84796
|
-
hypothesisStatement:
|
|
84862
|
+
hypothesisStatement: z74.string(),
|
|
84797
84863
|
/** The sections the new version owned, as labels — what was actually different. */
|
|
84798
|
-
changed:
|
|
84864
|
+
changed: z74.array(z74.string()),
|
|
84799
84865
|
goal: experimentGoalSchema,
|
|
84800
84866
|
variantGoal: experimentGoalSchema.nullable(),
|
|
84801
|
-
goalLabel:
|
|
84867
|
+
goalLabel: z74.string(),
|
|
84802
84868
|
verdict: experimentVerdictSchema,
|
|
84803
84869
|
/** Which version the numbers favoured, on `winner`. */
|
|
84804
|
-
winner:
|
|
84870
|
+
winner: z74.enum(["control", "variant"]).nullable(),
|
|
84805
84871
|
/** Which version kept the traffic. */
|
|
84806
|
-
survivor:
|
|
84872
|
+
survivor: z74.enum(["control", "variant"]),
|
|
84807
84873
|
/** True when what was kept is not what the numbers favoured. */
|
|
84808
|
-
againstVerdict:
|
|
84874
|
+
againstVerdict: z74.boolean(),
|
|
84809
84875
|
hypothesisOutcome: experimentHypothesisOutcomeSchema,
|
|
84810
84876
|
/** The numbers, as they were when the test ended. */
|
|
84811
|
-
result:
|
|
84812
|
-
control:
|
|
84813
|
-
variant:
|
|
84814
|
-
relativeLift:
|
|
84815
|
-
liftInterval:
|
|
84816
|
-
confidence:
|
|
84817
|
-
secondary:
|
|
84877
|
+
result: z74.object({
|
|
84878
|
+
control: z74.object({ visitors: z74.number(), conversions: z74.number(), rate: z74.number() }),
|
|
84879
|
+
variant: z74.object({ visitors: z74.number(), conversions: z74.number(), rate: z74.number() }),
|
|
84880
|
+
relativeLift: z74.number(),
|
|
84881
|
+
liftInterval: z74.tuple([z74.number(), z74.number()]),
|
|
84882
|
+
confidence: z74.number(),
|
|
84883
|
+
secondary: z74.array(experimentMetricSchema)
|
|
84818
84884
|
}),
|
|
84819
|
-
summary:
|
|
84885
|
+
summary: z74.string(),
|
|
84820
84886
|
/** Why it was ended, when whoever ended it said. */
|
|
84821
84887
|
endReason: experimentEndReasonSchema.nullable(),
|
|
84822
84888
|
/** What the test taught, or null when nobody has written it down yet. */
|
|
84823
|
-
learning:
|
|
84889
|
+
learning: z74.string().nullable()
|
|
84824
84890
|
});
|
|
84825
|
-
var experimentHistoryResponseSchema =
|
|
84826
|
-
tests:
|
|
84891
|
+
var experimentHistoryResponseSchema = z74.object({
|
|
84892
|
+
tests: z74.array(experimentHistoryEntrySchema)
|
|
84827
84893
|
});
|
|
84828
|
-
var experimentFinishResponseSchema =
|
|
84894
|
+
var experimentFinishResponseSchema = z74.object({
|
|
84829
84895
|
verdict: experimentVerdictSchema,
|
|
84830
|
-
survivor:
|
|
84896
|
+
survivor: z74.enum(["control", "variant"]),
|
|
84831
84897
|
/** True when `keep` went against a verdict the numbers had reached. */
|
|
84832
|
-
againstVerdict:
|
|
84833
|
-
summary:
|
|
84898
|
+
againstVerdict: z74.boolean(),
|
|
84899
|
+
summary: z74.string()
|
|
84834
84900
|
});
|
|
84835
84901
|
|
|
84836
84902
|
// src/commands/experiment/hints.ts
|
|
@@ -85050,11 +85116,11 @@ function parseEvidence(raw, rawArgs) {
|
|
|
85050
85116
|
}
|
|
85051
85117
|
|
|
85052
85118
|
// src/commands/experiment/identity.ts
|
|
85053
|
-
import { readFile as
|
|
85054
|
-
import
|
|
85119
|
+
import { readFile as readFile44 } from "fs/promises";
|
|
85120
|
+
import path51 from "path";
|
|
85055
85121
|
async function readPageInternalId(root, slug) {
|
|
85056
85122
|
try {
|
|
85057
|
-
const text3 = await
|
|
85123
|
+
const text3 = await readFile44(path51.resolve(root, "src", "pages", slug, "_definition.md"), "utf8");
|
|
85058
85124
|
return /^internalId:\s*"?([^"\s]+)"?\s*$/m.exec(text3)?.[1];
|
|
85059
85125
|
} catch {
|
|
85060
85126
|
return void 0;
|
|
@@ -85062,8 +85128,8 @@ async function readPageInternalId(root, slug) {
|
|
|
85062
85128
|
}
|
|
85063
85129
|
|
|
85064
85130
|
// src/commands/experiment/measurement.ts
|
|
85065
|
-
import { readFile as
|
|
85066
|
-
import
|
|
85131
|
+
import { readFile as readFile45 } from "fs/promises";
|
|
85132
|
+
import path52 from "path";
|
|
85067
85133
|
function eventDeclarationsIn(source) {
|
|
85068
85134
|
const found = [];
|
|
85069
85135
|
const pattern = /\b(data-baker-(?:view|event))\s*=\s*["']([^"']+)["']/g;
|
|
@@ -85123,7 +85189,7 @@ function sectionsOf(files, slug) {
|
|
|
85123
85189
|
return files.filter((file) => file.startsWith(prefix) && !file.slice(prefix.length).startsWith("index."));
|
|
85124
85190
|
}
|
|
85125
85191
|
async function declarationsOf(root, files) {
|
|
85126
|
-
const sources = await Promise.all(files.map((file) =>
|
|
85192
|
+
const sources = await Promise.all(files.map((file) => readFile45(path52.join(root, file), "utf8").catch(() => "")));
|
|
85127
85193
|
return sources.flatMap(eventDeclarationsIn);
|
|
85128
85194
|
}
|
|
85129
85195
|
|
|
@@ -86545,10 +86611,10 @@ function parseValueExpression(raw) {
|
|
|
86545
86611
|
return parts.map(parsePart);
|
|
86546
86612
|
}
|
|
86547
86613
|
function trackingFieldIds() {
|
|
86548
|
-
const
|
|
86549
|
-
if (!existsSync9(
|
|
86614
|
+
const path68 = join4(flowsDir(), "..", "tracking.ts");
|
|
86615
|
+
if (!existsSync9(path68)) return null;
|
|
86550
86616
|
try {
|
|
86551
|
-
const source = readFileSync19(
|
|
86617
|
+
const source = readFileSync19(path68, "utf-8");
|
|
86552
86618
|
const block2 = source.match(/TRACKING_FIELD_IDS\s*=\s*\[([\s\S]*?)\]\s*as const/)?.[1];
|
|
86553
86619
|
if (!block2) return null;
|
|
86554
86620
|
const ids2 = [...block2.matchAll(/"(tracking\.[a-z0-9_]+)"/g)].map((match) => match[1]);
|
|
@@ -87130,13 +87196,13 @@ function specsFromFile(parsed) {
|
|
|
87130
87196
|
return `${destField}${type}=${entry?.value ?? ""}`;
|
|
87131
87197
|
});
|
|
87132
87198
|
}
|
|
87133
|
-
function readSpecFile(
|
|
87199
|
+
function readSpecFile(path68) {
|
|
87134
87200
|
let raw;
|
|
87135
87201
|
try {
|
|
87136
|
-
raw =
|
|
87202
|
+
raw = path68 === "-" ? readFileSync20(0, "utf-8") : readFileSync20(path68, "utf-8");
|
|
87137
87203
|
} catch (error) {
|
|
87138
87204
|
refuse3(
|
|
87139
|
-
`Could not read ${
|
|
87205
|
+
`Could not read ${path68 === "-" ? "the mapping from stdin" : `"${path68}"`}: ${error instanceof Error ? error.message : String(error)}`
|
|
87140
87206
|
);
|
|
87141
87207
|
}
|
|
87142
87208
|
let parsed;
|
|
@@ -87144,7 +87210,7 @@ function readSpecFile(path67) {
|
|
|
87144
87210
|
parsed = JSON.parse(raw);
|
|
87145
87211
|
} catch (error) {
|
|
87146
87212
|
refuse3(
|
|
87147
|
-
`${
|
|
87213
|
+
`${path68 === "-" ? "stdin" : `"${path68}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
87148
87214
|
'Expected { "map": { "<destField>": "<value>", \u2026 } }'
|
|
87149
87215
|
);
|
|
87150
87216
|
}
|
|
@@ -87443,21 +87509,21 @@ var ARRAY_FIELDS = [
|
|
|
87443
87509
|
"tagIds"
|
|
87444
87510
|
];
|
|
87445
87511
|
var ARRAY_OWNERS = ["", "body"];
|
|
87446
|
-
function dropUnsetOptionals(sideEffect,
|
|
87512
|
+
function dropUnsetOptionals(sideEffect, path68) {
|
|
87447
87513
|
return OPTIONAL_STRINGS.flatMap((key) => {
|
|
87448
87514
|
if (!(key in sideEffect) || sideEffect[key] !== null && sideEffect[key] !== "") return [];
|
|
87449
87515
|
delete sideEffect[key];
|
|
87450
|
-
return [{ path:
|
|
87516
|
+
return [{ path: path68, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
|
|
87451
87517
|
});
|
|
87452
87518
|
}
|
|
87453
|
-
function fillNulledArrays(target2, prefix,
|
|
87519
|
+
function fillNulledArrays(target2, prefix, path68) {
|
|
87454
87520
|
return ARRAY_FIELDS.flatMap((key) => {
|
|
87455
87521
|
if (!(key in target2) || target2[key] !== null) return [];
|
|
87456
87522
|
target2[key] = [];
|
|
87457
|
-
return [{ path:
|
|
87523
|
+
return [{ path: path68, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
|
|
87458
87524
|
});
|
|
87459
87525
|
}
|
|
87460
|
-
function tidyEmailRecipients(sideEffect,
|
|
87526
|
+
function tidyEmailRecipients(sideEffect, path68) {
|
|
87461
87527
|
if (sideEffect.type !== "email") return [];
|
|
87462
87528
|
const config = sideEffect.encryptedConfig;
|
|
87463
87529
|
if (!config || typeof config !== "object" || Array.isArray(config)) return [];
|
|
@@ -87468,7 +87534,7 @@ function tidyEmailRecipients(sideEffect, path67) {
|
|
|
87468
87534
|
const tidied = to.join(", ");
|
|
87469
87535
|
if (tidied === raw) return [];
|
|
87470
87536
|
config.email = tidied;
|
|
87471
|
-
return [{ path:
|
|
87537
|
+
return [{ path: path68, change: "tidied the recipient list's spacing and separators" }];
|
|
87472
87538
|
}
|
|
87473
87539
|
function sideEffectsOf(node) {
|
|
87474
87540
|
if (!Array.isArray(node.sideEffects)) return [];
|
|
@@ -87477,13 +87543,13 @@ function sideEffectsOf(node) {
|
|
|
87477
87543
|
);
|
|
87478
87544
|
}
|
|
87479
87545
|
function normalizeSideEffect(sideEffect, where) {
|
|
87480
|
-
const
|
|
87546
|
+
const path68 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
|
|
87481
87547
|
const arrays = ARRAY_OWNERS.flatMap((owner) => {
|
|
87482
87548
|
const target2 = owner ? sideEffect[owner] : sideEffect;
|
|
87483
87549
|
if (!target2 || typeof target2 !== "object") return [];
|
|
87484
|
-
return fillNulledArrays(target2, owner ? `${owner}.` : "",
|
|
87550
|
+
return fillNulledArrays(target2, owner ? `${owner}.` : "", path68);
|
|
87485
87551
|
});
|
|
87486
|
-
return [...dropUnsetOptionals(sideEffect,
|
|
87552
|
+
return [...dropUnsetOptionals(sideEffect, path68), ...arrays, ...tidyEmailRecipients(sideEffect, path68)];
|
|
87487
87553
|
}
|
|
87488
87554
|
function normalizeFlowTree(tree) {
|
|
87489
87555
|
const changes = [];
|
|
@@ -88258,45 +88324,45 @@ var FUNNEL_VERTICALS = [
|
|
|
88258
88324
|
];
|
|
88259
88325
|
|
|
88260
88326
|
// ../api/src/funnel/wire.ts
|
|
88261
|
-
import { z as
|
|
88327
|
+
import { z as z75 } from "zod";
|
|
88262
88328
|
var MAX_STEPS = 40;
|
|
88263
88329
|
var MAX_PER_STEP = 20;
|
|
88264
|
-
var funnelTriggerSchema =
|
|
88265
|
-
|
|
88266
|
-
id:
|
|
88267
|
-
kind:
|
|
88330
|
+
var funnelTriggerSchema = z75.discriminatedUnion("kind", [
|
|
88331
|
+
z75.object({
|
|
88332
|
+
id: z75.string().min(1).max(64),
|
|
88333
|
+
kind: z75.literal("event"),
|
|
88268
88334
|
/** The event's key, from `--moments`: `form:…`, `submit:…`, `page:…`, `exit:…`. */
|
|
88269
|
-
eventKey:
|
|
88335
|
+
eventKey: z75.string().min(1).max(320)
|
|
88270
88336
|
}),
|
|
88271
|
-
|
|
88272
|
-
id:
|
|
88273
|
-
kind:
|
|
88337
|
+
z75.object({
|
|
88338
|
+
id: z75.string().min(1).max(64),
|
|
88339
|
+
kind: z75.literal("landing"),
|
|
88274
88340
|
/** The landing's internal id — what the page stamps on its rows, never its slug. */
|
|
88275
|
-
landingId:
|
|
88341
|
+
landingId: z75.string().min(1).max(120)
|
|
88276
88342
|
}),
|
|
88277
88343
|
/** Any landing being viewed — what a funnel's first step usually listens for. */
|
|
88278
|
-
|
|
88279
|
-
id:
|
|
88280
|
-
kind:
|
|
88344
|
+
z75.object({
|
|
88345
|
+
id: z75.string().min(1).max(64),
|
|
88346
|
+
kind: z75.literal("any_landing")
|
|
88281
88347
|
})
|
|
88282
88348
|
]);
|
|
88283
|
-
var funnelStepSchema =
|
|
88349
|
+
var funnelStepSchema = z75.object({
|
|
88284
88350
|
/** Lowercase letters, digits and underscores. Stable — renaming never changes it. */
|
|
88285
|
-
key:
|
|
88351
|
+
key: z75.string().min(1).max(64),
|
|
88286
88352
|
/** The client's own words for this step. Shown everywhere; unconstrained. */
|
|
88287
|
-
displayName:
|
|
88288
|
-
stepClass:
|
|
88353
|
+
displayName: z75.string().min(1).max(120),
|
|
88354
|
+
stepClass: z75.enum(FUNNEL_STEP_CLASSES),
|
|
88289
88355
|
/** Predecessor keys. Empty means people come in here. */
|
|
88290
|
-
after:
|
|
88356
|
+
after: z75.array(z75.string().max(64)).max(MAX_STEPS).default([]),
|
|
88291
88357
|
/** What moves somebody onto this step. Any one of them is enough. */
|
|
88292
|
-
triggers:
|
|
88358
|
+
triggers: z75.array(funnelTriggerSchema).max(MAX_PER_STEP).default([])
|
|
88293
88359
|
});
|
|
88294
|
-
var funnelRequestSchema =
|
|
88295
|
-
steps:
|
|
88360
|
+
var funnelRequestSchema = z75.object({
|
|
88361
|
+
steps: z75.array(funnelStepSchema).max(MAX_STEPS).optional(),
|
|
88296
88362
|
/** One step, added or replaced by its key. Everything else is untouched. */
|
|
88297
88363
|
step: funnelStepSchema.optional(),
|
|
88298
88364
|
/** One step taken out by key; whatever came after it moves up to whatever came before it. */
|
|
88299
|
-
removeStep:
|
|
88365
|
+
removeStep: z75.string().min(1).max(64).optional(),
|
|
88300
88366
|
/**
|
|
88301
88367
|
* ONE way in added to a step, leaving the rest of that step alone.
|
|
88302
88368
|
*
|
|
@@ -88306,129 +88372,129 @@ var funnelRequestSchema = z74.object({
|
|
|
88306
88372
|
* for the thing that is actually being asked ("this Form's booking should
|
|
88307
88373
|
* count here"), and it cannot drop anything.
|
|
88308
88374
|
*/
|
|
88309
|
-
addTrigger:
|
|
88375
|
+
addTrigger: z75.object({ stepKey: z75.string().min(1).max(64), eventKey: z75.string().min(1).max(320) }).optional(),
|
|
88310
88376
|
/** ONE way in taken off a step, by the event key it listens for. */
|
|
88311
|
-
removeTrigger:
|
|
88377
|
+
removeTrigger: z75.object({ stepKey: z75.string().min(1).max(64), eventKey: z75.string().min(1).max(320) }).optional(),
|
|
88312
88378
|
/** One of the ready-made graphs, instead of stating steps. */
|
|
88313
|
-
preset:
|
|
88379
|
+
preset: z75.enum(FUNNEL_VERTICALS).optional(),
|
|
88314
88380
|
/** Also return the suggestion library and the presets. */
|
|
88315
|
-
templates:
|
|
88381
|
+
templates: z75.boolean().optional(),
|
|
88316
88382
|
/**
|
|
88317
88383
|
* Also return everything a trigger can name: every event this company's
|
|
88318
88384
|
* Forms, pages and systems have actually produced, its sources, and its
|
|
88319
88385
|
* landings. The list to pick a trigger FROM, so nobody has to guess.
|
|
88320
88386
|
*/
|
|
88321
|
-
moments:
|
|
88387
|
+
moments: z75.boolean().optional(),
|
|
88322
88388
|
/** How many days the counts beside each step cover. Default 30. */
|
|
88323
|
-
days:
|
|
88389
|
+
days: z75.number().int().min(1).max(400).optional(),
|
|
88324
88390
|
/**
|
|
88325
88391
|
* The Forms, by slug, the caller names itself when no step counts them —
|
|
88326
88392
|
* `baker funnel` reads its own workspace and names each with the key that
|
|
88327
88393
|
* places it. The backend leaves them out of its own hint, so no Form is
|
|
88328
88394
|
* named twice.
|
|
88329
88395
|
*/
|
|
88330
|
-
namedByCaller:
|
|
88396
|
+
namedByCaller: z75.array(z75.string().min(1).max(200)).max(1e3).optional()
|
|
88331
88397
|
});
|
|
88332
|
-
var funnelResponseSchema =
|
|
88333
|
-
ok:
|
|
88334
|
-
data:
|
|
88398
|
+
var funnelResponseSchema = z75.object({
|
|
88399
|
+
ok: z75.literal(true),
|
|
88400
|
+
data: z75.object({
|
|
88335
88401
|
/** The published version, or 0 when this company has never drawn a funnel. */
|
|
88336
|
-
version:
|
|
88402
|
+
version: z75.number(),
|
|
88337
88403
|
/** Whether `steps` is a proposal waiting on this chat's publish rather than what is live. */
|
|
88338
|
-
staged:
|
|
88339
|
-
steps:
|
|
88340
|
-
|
|
88341
|
-
key:
|
|
88342
|
-
displayName:
|
|
88343
|
-
stepClass:
|
|
88344
|
-
after:
|
|
88345
|
-
triggers:
|
|
88404
|
+
staged: z75.boolean(),
|
|
88405
|
+
steps: z75.array(
|
|
88406
|
+
z75.object({
|
|
88407
|
+
key: z75.string(),
|
|
88408
|
+
displayName: z75.string(),
|
|
88409
|
+
stepClass: z75.string(),
|
|
88410
|
+
after: z75.array(z75.string()),
|
|
88411
|
+
triggers: z75.array(z75.record(z75.string(), z75.unknown())),
|
|
88346
88412
|
/**
|
|
88347
88413
|
* Where the people who reach this step are sent: the sends of every
|
|
88348
88414
|
* system event among its triggers. Read-only here — a send belongs to
|
|
88349
88415
|
* the event and is set with `baker analytics sends`.
|
|
88350
88416
|
*/
|
|
88351
|
-
sends:
|
|
88352
|
-
|
|
88353
|
-
id:
|
|
88354
|
-
eventKey:
|
|
88355
|
-
platform:
|
|
88356
|
-
event:
|
|
88417
|
+
sends: z75.array(
|
|
88418
|
+
z75.object({
|
|
88419
|
+
id: z75.string(),
|
|
88420
|
+
eventKey: z75.string(),
|
|
88421
|
+
platform: z75.string(),
|
|
88422
|
+
event: z75.string(),
|
|
88357
88423
|
/** What happened to it in the window, in one line. */
|
|
88358
|
-
outcome:
|
|
88424
|
+
outcome: z75.string().optional()
|
|
88359
88425
|
})
|
|
88360
88426
|
),
|
|
88361
88427
|
/** How many people reached this step in the window. Counted in people, across visits and devices. */
|
|
88362
|
-
people:
|
|
88428
|
+
people: z75.number().optional(),
|
|
88363
88429
|
/** How many reached this step or any step after it. */
|
|
88364
|
-
atLeast:
|
|
88430
|
+
atLeast: z75.number().optional(),
|
|
88365
88431
|
/** How many got as far as the step before it. Absent for a way in. */
|
|
88366
|
-
before:
|
|
88432
|
+
before: z75.number().optional(),
|
|
88367
88433
|
/** `atLeast / before`, 0–1. Absent where there is no before. */
|
|
88368
|
-
share:
|
|
88369
|
-
events:
|
|
88370
|
-
money:
|
|
88434
|
+
share: z75.number().optional(),
|
|
88435
|
+
events: z75.number().optional(),
|
|
88436
|
+
money: z75.record(z75.string(), z75.number()).optional()
|
|
88371
88437
|
})
|
|
88372
88438
|
),
|
|
88373
88439
|
/** The window `people` and friends were counted over. */
|
|
88374
|
-
window:
|
|
88440
|
+
window: z75.object({ startDate: z75.string(), endDate: z75.string(), days: z75.number() }).optional(),
|
|
88375
88441
|
/** The step that loses the most people, by key. Absent while nothing is lost. */
|
|
88376
|
-
worstStep:
|
|
88442
|
+
worstStep: z75.string().optional(),
|
|
88377
88443
|
/**
|
|
88378
88444
|
* The steps this write took off the funnel, by key. Present on every write,
|
|
88379
88445
|
* empty when nothing was dropped — the answer to "did my restatement forget
|
|
88380
88446
|
* one", given in the same turn.
|
|
88381
88447
|
*/
|
|
88382
|
-
removed:
|
|
88448
|
+
removed: z75.array(z75.string()).optional(),
|
|
88383
88449
|
/**
|
|
88384
88450
|
* What this company counts as page conversions, this chat's staged counts
|
|
88385
88451
|
* included, by event key and name. Here so a Form's ending can say it is
|
|
88386
88452
|
* already counted — the agent choosing a step's way in should use that
|
|
88387
88453
|
* same key — and outside `moments` because the plain read's hints say it too.
|
|
88388
88454
|
*/
|
|
88389
|
-
counted:
|
|
88455
|
+
counted: z75.array(z75.object({ eventKey: z75.string(), name: z75.string() })).optional(),
|
|
88390
88456
|
/** Everything a trigger can name, when asked for with `moments`. */
|
|
88391
|
-
moments:
|
|
88392
|
-
events:
|
|
88393
|
-
|
|
88394
|
-
eventKey:
|
|
88457
|
+
moments: z75.object({
|
|
88458
|
+
events: z75.array(
|
|
88459
|
+
z75.object({
|
|
88460
|
+
eventKey: z75.string(),
|
|
88395
88461
|
/** `site` for a page or Form, `systems` for the client's own server, `both` when both sent it. */
|
|
88396
|
-
origin:
|
|
88462
|
+
origin: z75.string().optional(),
|
|
88397
88463
|
/** The Form it belongs to, when it belongs to one. */
|
|
88398
|
-
flowSlug:
|
|
88399
|
-
nodeType:
|
|
88400
|
-
events:
|
|
88401
|
-
sessions:
|
|
88464
|
+
flowSlug: z75.string().optional(),
|
|
88465
|
+
nodeType: z75.string().optional(),
|
|
88466
|
+
events: z75.number(),
|
|
88467
|
+
sessions: z75.number(),
|
|
88402
88468
|
/** The name it is counted under as a page conversion, when the company counts it. Informational. */
|
|
88403
|
-
countedAs:
|
|
88469
|
+
countedAs: z75.string().optional(),
|
|
88404
88470
|
/**
|
|
88405
88471
|
* The step of the Form this happens on, in the Form's own words —
|
|
88406
88472
|
* never a node id. Present on a Form's ending read from the Form
|
|
88407
88473
|
* itself, which is the only place the name is known.
|
|
88408
88474
|
*/
|
|
88409
|
-
step:
|
|
88475
|
+
step: z75.string().optional(),
|
|
88410
88476
|
/**
|
|
88411
88477
|
* `false` on an ending a Form raises natively but nobody has fired
|
|
88412
88478
|
* yet — derived from the Form, offered at zero, exactly as a
|
|
88413
88479
|
* Source is offered before its first send. Absent means it came
|
|
88414
88480
|
* from the event store and therefore happened.
|
|
88415
88481
|
*/
|
|
88416
|
-
seen:
|
|
88482
|
+
seen: z75.boolean().optional()
|
|
88417
88483
|
})
|
|
88418
88484
|
),
|
|
88419
88485
|
/** The company's sources: a system of theirs that posts events to Baker, by the event name it raises. */
|
|
88420
|
-
sources:
|
|
88421
|
-
landings:
|
|
88486
|
+
sources: z75.array(z75.object({ eventKey: z75.string(), name: z75.string(), status: z75.string() })),
|
|
88487
|
+
landings: z75.array(z75.object({ landingId: z75.string(), title: z75.string(), slug: z75.string() }))
|
|
88422
88488
|
}).optional(),
|
|
88423
|
-
templates:
|
|
88424
|
-
|
|
88425
|
-
key:
|
|
88426
|
-
displayName:
|
|
88427
|
-
meaning:
|
|
88428
|
-
stepClass:
|
|
88489
|
+
templates: z75.array(
|
|
88490
|
+
z75.object({
|
|
88491
|
+
key: z75.string(),
|
|
88492
|
+
displayName: z75.string(),
|
|
88493
|
+
meaning: z75.string(),
|
|
88494
|
+
stepClass: z75.string()
|
|
88429
88495
|
})
|
|
88430
88496
|
).optional(),
|
|
88431
|
-
presets:
|
|
88497
|
+
presets: z75.array(z75.object({ vertical: z75.string(), label: z75.string(), description: z75.string(), steps: z75.array(z75.string()) })).optional(),
|
|
88432
88498
|
/**
|
|
88433
88499
|
* What each stage means, the test to apply, and the same stage in three
|
|
88434
88500
|
* shapes of business.
|
|
@@ -88439,17 +88505,17 @@ var funnelResponseSchema = z74.object({
|
|
|
88439
88505
|
* engaged or customer. Same source as the dashboard's stage picker, so the
|
|
88440
88506
|
* two cannot teach it differently.
|
|
88441
88507
|
*/
|
|
88442
|
-
stages:
|
|
88443
|
-
|
|
88444
|
-
stepClass:
|
|
88445
|
-
title:
|
|
88446
|
-
meaning:
|
|
88447
|
-
test:
|
|
88448
|
-
examples:
|
|
88508
|
+
stages: z75.array(
|
|
88509
|
+
z75.object({
|
|
88510
|
+
stepClass: z75.string(),
|
|
88511
|
+
title: z75.string(),
|
|
88512
|
+
meaning: z75.string(),
|
|
88513
|
+
test: z75.string(),
|
|
88514
|
+
examples: z75.object({ shop: z75.string(), selfServe: z75.string(), salesLed: z75.string() })
|
|
88449
88515
|
})
|
|
88450
88516
|
).optional()
|
|
88451
88517
|
}),
|
|
88452
|
-
hints:
|
|
88518
|
+
hints: z75.array(z75.string()).optional()
|
|
88453
88519
|
});
|
|
88454
88520
|
|
|
88455
88521
|
// src/commands/funnel/funnelArgs.ts
|
|
@@ -88989,10 +89055,10 @@ async function stageOps(ops) {
|
|
|
88989
89055
|
handleError5(err);
|
|
88990
89056
|
}
|
|
88991
89057
|
}
|
|
88992
|
-
async function draftAction2(
|
|
89058
|
+
async function draftAction2(path68, body, chat) {
|
|
88993
89059
|
const chatId = resolveChatId(chat);
|
|
88994
89060
|
try {
|
|
88995
|
-
const { data, hints: hints2 } = splitHints(await apiPost(
|
|
89061
|
+
const { data, hints: hints2 } = splitHints(await apiPost(path68, { chatId, ...body }));
|
|
88996
89062
|
writeJsonEnvelope({ ok: true, data, ...hints2.length > 0 ? { hints: hints2 } : {} });
|
|
88997
89063
|
return data;
|
|
88998
89064
|
} catch (err) {
|
|
@@ -91503,7 +91569,7 @@ function cropSprite(input, region) {
|
|
|
91503
91569
|
|
|
91504
91570
|
// src/lib/image/io.ts
|
|
91505
91571
|
import { randomBytes } from "crypto";
|
|
91506
|
-
import { glob as fsGlob, readFile as
|
|
91572
|
+
import { glob as fsGlob, readFile as readFile46, rename as rename2, stat as stat9, writeFile as writeFile26 } from "fs/promises";
|
|
91507
91573
|
import { dirname as dirname2, extname as extname3, join as join5, resolve as resolve4 } from "path";
|
|
91508
91574
|
var REMOTE_RE = /^https?:\/\//i;
|
|
91509
91575
|
var GLOB_RE = /[*?[\]{}]/;
|
|
@@ -91536,11 +91602,11 @@ async function readImageBuffer(pathOrUrl) {
|
|
|
91536
91602
|
const { buffer } = await fetchExternalBytes(pathOrUrl, { maxBytes: MAX_REMOTE_IMAGE_BYTES });
|
|
91537
91603
|
return buffer;
|
|
91538
91604
|
}
|
|
91539
|
-
return
|
|
91605
|
+
return readFile46(pathOrUrl);
|
|
91540
91606
|
}
|
|
91541
|
-
async function isDirectory(
|
|
91607
|
+
async function isDirectory(path68) {
|
|
91542
91608
|
try {
|
|
91543
|
-
const s = await stat9(
|
|
91609
|
+
const s = await stat9(path68);
|
|
91544
91610
|
return s.isDirectory();
|
|
91545
91611
|
} catch {
|
|
91546
91612
|
return false;
|
|
@@ -91559,7 +91625,7 @@ async function atomicWrite(targetPath, data) {
|
|
|
91559
91625
|
const absolute = resolve4(targetPath);
|
|
91560
91626
|
const dir = dirname2(absolute);
|
|
91561
91627
|
const tmp = join5(dir, `.baker-image-${randomBytes(8).toString("hex")}.tmp`);
|
|
91562
|
-
await
|
|
91628
|
+
await writeFile26(tmp, data);
|
|
91563
91629
|
await rename2(tmp, absolute);
|
|
91564
91630
|
}
|
|
91565
91631
|
|
|
@@ -91968,13 +92034,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
|
|
|
91968
92034
|
}
|
|
91969
92035
|
function disambiguate(paths) {
|
|
91970
92036
|
const taken = /* @__PURE__ */ new Set();
|
|
91971
|
-
return paths.map((
|
|
91972
|
-
if (!taken.has(
|
|
91973
|
-
taken.add(
|
|
91974
|
-
return
|
|
92037
|
+
return paths.map((path68) => {
|
|
92038
|
+
if (!taken.has(path68)) {
|
|
92039
|
+
taken.add(path68);
|
|
92040
|
+
return path68;
|
|
91975
92041
|
}
|
|
91976
|
-
const ext = extname4(
|
|
91977
|
-
const stem =
|
|
92042
|
+
const ext = extname4(path68);
|
|
92043
|
+
const stem = path68.slice(0, path68.length - ext.length);
|
|
91978
92044
|
let n = 2;
|
|
91979
92045
|
while (taken.has(`${stem}-${n}${ext}`)) n += 1;
|
|
91980
92046
|
const unique2 = `${stem}-${n}${ext}`;
|
|
@@ -92100,10 +92166,10 @@ async function runDownloads(plan) {
|
|
|
92100
92166
|
const paths = disambiguate(fetched.map((item) => item.path));
|
|
92101
92167
|
const downloaded = [];
|
|
92102
92168
|
for (const [index, item] of fetched.entries()) {
|
|
92103
|
-
const
|
|
92169
|
+
const path68 = paths[index] ?? item.path;
|
|
92104
92170
|
try {
|
|
92105
|
-
await atomicWrite(
|
|
92106
|
-
downloaded.push({ input: item.input, output:
|
|
92171
|
+
await atomicWrite(path68, item.buffer);
|
|
92172
|
+
downloaded.push({ input: item.input, output: path68, bytes: item.buffer.length, contentType: item.contentType });
|
|
92107
92173
|
} catch (err) {
|
|
92108
92174
|
failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
|
|
92109
92175
|
}
|
|
@@ -94998,8 +95064,8 @@ Full guide: __tooling__/docs/tools/baker/images.md`
|
|
|
94998
95064
|
import { defineCommand as defineCommand226 } from "citty";
|
|
94999
95065
|
|
|
95000
95066
|
// src/commands/landing/critique.ts
|
|
95001
|
-
import { readdir as readdir22, readFile as
|
|
95002
|
-
import
|
|
95067
|
+
import { readdir as readdir22, readFile as readFile48, stat as stat11 } from "fs/promises";
|
|
95068
|
+
import path55 from "path";
|
|
95003
95069
|
import { defineCommand as defineCommand213 } from "citty";
|
|
95004
95070
|
|
|
95005
95071
|
// src/engine/landing/lib/constants.ts
|
|
@@ -96190,27 +96256,27 @@ function describeCounts(findings) {
|
|
|
96190
96256
|
}
|
|
96191
96257
|
|
|
96192
96258
|
// src/commands/landing/snapshot.ts
|
|
96193
|
-
import { mkdir as
|
|
96194
|
-
import
|
|
96259
|
+
import { mkdir as mkdir16, rename as rename3, writeFile as writeFile27 } from "fs/promises";
|
|
96260
|
+
import path53 from "path";
|
|
96195
96261
|
var CRITIC_VERSION = "4";
|
|
96196
96262
|
function critiqueCacheDir(projectRoot) {
|
|
96197
|
-
return
|
|
96263
|
+
return path53.join(projectRoot, ".cache", "landing-critique");
|
|
96198
96264
|
}
|
|
96199
96265
|
function snapshotPath(projectRoot, slug) {
|
|
96200
|
-
return
|
|
96266
|
+
return path53.join(critiqueCacheDir(projectRoot), `${slug}.json`);
|
|
96201
96267
|
}
|
|
96202
96268
|
async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
96203
|
-
await
|
|
96269
|
+
await mkdir16(critiqueCacheDir(projectRoot), { recursive: true });
|
|
96204
96270
|
const dest = snapshotPath(projectRoot, snapshot.slug);
|
|
96205
96271
|
const tmp = `${dest}.tmp`;
|
|
96206
|
-
await
|
|
96272
|
+
await writeFile27(tmp, `${JSON.stringify(snapshot, null, 2)}
|
|
96207
96273
|
`, "utf8");
|
|
96208
96274
|
await rename3(tmp, dest);
|
|
96209
96275
|
}
|
|
96210
96276
|
|
|
96211
96277
|
// src/commands/landing/source-version.ts
|
|
96212
|
-
import { readdir as readdir21, readFile as
|
|
96213
|
-
import
|
|
96278
|
+
import { readdir as readdir21, readFile as readFile47, stat as stat10 } from "fs/promises";
|
|
96279
|
+
import path54 from "path";
|
|
96214
96280
|
var CRITIQUED_ROOTS = ["src/pages/", "src/components/"];
|
|
96215
96281
|
async function landingSourceRelPaths(root, slug) {
|
|
96216
96282
|
const files = await landingGraphFiles(root, slug);
|
|
@@ -96219,7 +96285,7 @@ async function landingSourceRelPaths(root, slug) {
|
|
|
96219
96285
|
async function readLandingSources(root, slug) {
|
|
96220
96286
|
const rel = await landingSourceRelPaths(root, slug);
|
|
96221
96287
|
const out = [];
|
|
96222
|
-
for (const r of rel) out.push({ path: r, text: await
|
|
96288
|
+
for (const r of rel) out.push({ path: r, text: await readFile47(path54.join(root, r), "utf8") });
|
|
96223
96289
|
return out;
|
|
96224
96290
|
}
|
|
96225
96291
|
async function computeLandingSourceSha(root, slug) {
|
|
@@ -96228,7 +96294,7 @@ async function computeLandingSourceSha(root, slug) {
|
|
|
96228
96294
|
for (const r of rel) {
|
|
96229
96295
|
let bytes;
|
|
96230
96296
|
try {
|
|
96231
|
-
bytes = await
|
|
96297
|
+
bytes = await readFile47(path54.join(root, r));
|
|
96232
96298
|
} catch {
|
|
96233
96299
|
bytes = Buffer.alloc(0);
|
|
96234
96300
|
}
|
|
@@ -96238,16 +96304,16 @@ async function computeLandingSourceSha(root, slug) {
|
|
|
96238
96304
|
}
|
|
96239
96305
|
async function computeLegacyLandingSourceSha(landingDir) {
|
|
96240
96306
|
const rel = [];
|
|
96241
|
-
if (await isFile2(
|
|
96242
|
-
for (const abs of await walkAstro(
|
|
96243
|
-
rel.push(
|
|
96307
|
+
if (await isFile2(path54.join(landingDir, "index.astro"))) rel.push("index.astro");
|
|
96308
|
+
for (const abs of await walkAstro(path54.join(landingDir, "_components"))) {
|
|
96309
|
+
rel.push(path54.relative(landingDir, abs).split(path54.sep).join("/"));
|
|
96244
96310
|
}
|
|
96245
96311
|
rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
96246
96312
|
const parts = [];
|
|
96247
96313
|
for (const r of rel) {
|
|
96248
96314
|
let bytes;
|
|
96249
96315
|
try {
|
|
96250
|
-
bytes = await
|
|
96316
|
+
bytes = await readFile47(path54.join(landingDir, r));
|
|
96251
96317
|
} catch {
|
|
96252
96318
|
bytes = Buffer.alloc(0);
|
|
96253
96319
|
}
|
|
@@ -96271,7 +96337,7 @@ async function walkAstro(dir) {
|
|
|
96271
96337
|
}
|
|
96272
96338
|
const out = [];
|
|
96273
96339
|
for (const entry of entries) {
|
|
96274
|
-
const abs =
|
|
96340
|
+
const abs = path54.join(dir, entry.name);
|
|
96275
96341
|
if (entry.isDirectory()) out.push(...await walkAstro(abs));
|
|
96276
96342
|
else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
|
|
96277
96343
|
}
|
|
@@ -96332,7 +96398,7 @@ var critiqueCommand2 = defineCommand213({
|
|
|
96332
96398
|
{ availableSlugs: await listLandingSlugs(projectRoot) }
|
|
96333
96399
|
);
|
|
96334
96400
|
}
|
|
96335
|
-
if (!await isDir2(
|
|
96401
|
+
if (!await isDir2(path55.resolve(projectRoot, "src", "pages", slug))) {
|
|
96336
96402
|
fail10("NOT_FOUND", `No landing at src/pages/${slug}/`, {
|
|
96337
96403
|
availableSlugs: await listLandingSlugs(projectRoot)
|
|
96338
96404
|
});
|
|
@@ -96375,7 +96441,7 @@ async function critiqueOne(projectRoot, slug, brand, competitors) {
|
|
|
96375
96441
|
const [sources, compositionSha, sourceSha] = await Promise.all([
|
|
96376
96442
|
readLandingSources(projectRoot, slug),
|
|
96377
96443
|
computeLandingSourceSha(projectRoot, slug),
|
|
96378
|
-
computeLegacyLandingSourceSha(
|
|
96444
|
+
computeLegacyLandingSourceSha(path55.resolve(projectRoot, "src", "pages", slug))
|
|
96379
96445
|
]);
|
|
96380
96446
|
const report = critiqueLanding({ slug, sources, brand, competitors });
|
|
96381
96447
|
let snapshotFailed = false;
|
|
@@ -96395,7 +96461,7 @@ async function critiqueOne(projectRoot, slug, brand, competitors) {
|
|
|
96395
96461
|
return { slug, report, snapshotFailed };
|
|
96396
96462
|
}
|
|
96397
96463
|
async function readCompetitorNames(projectRoot) {
|
|
96398
|
-
const dir =
|
|
96464
|
+
const dir = path55.join(projectRoot, "src", "content", "competitors");
|
|
96399
96465
|
let entries;
|
|
96400
96466
|
try {
|
|
96401
96467
|
entries = (await readdir22(dir)).filter((f) => f.endsWith(".md"));
|
|
@@ -96404,9 +96470,9 @@ async function readCompetitorNames(projectRoot) {
|
|
|
96404
96470
|
}
|
|
96405
96471
|
const names = [];
|
|
96406
96472
|
for (const entry of entries) {
|
|
96407
|
-
names.push(
|
|
96473
|
+
names.push(path55.basename(entry, ".md").replace(/[-_]+/g, " "));
|
|
96408
96474
|
try {
|
|
96409
|
-
const head = (await
|
|
96475
|
+
const head = (await readFile48(path55.join(dir, entry), "utf8")).slice(0, 2e3);
|
|
96410
96476
|
const titled = /^\s*(?:title|name)\s*:\s*["']?([^"'\n]+)["']?\s*$/im.exec(head);
|
|
96411
96477
|
if (titled?.[1]) names.push(titled[1].trim());
|
|
96412
96478
|
} catch {
|
|
@@ -96416,7 +96482,7 @@ async function readCompetitorNames(projectRoot) {
|
|
|
96416
96482
|
}
|
|
96417
96483
|
async function listLandingSlugs(projectRoot) {
|
|
96418
96484
|
try {
|
|
96419
|
-
const entries = await readdir22(
|
|
96485
|
+
const entries = await readdir22(path55.join(projectRoot, "src", "pages"), { withFileTypes: true });
|
|
96420
96486
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
96421
96487
|
} catch {
|
|
96422
96488
|
return [];
|
|
@@ -96540,10 +96606,10 @@ function exportStatusHints(view) {
|
|
|
96540
96606
|
}
|
|
96541
96607
|
return ["Still being prepared. Carry on with other work and check again in a minute \u2014 never sleep-loop on it."];
|
|
96542
96608
|
}
|
|
96543
|
-
function parseProfileFlag(raw,
|
|
96609
|
+
function parseProfileFlag(raw, readFile52 = (path68) => readFileSync25(path68, "utf8")) {
|
|
96544
96610
|
let parsed;
|
|
96545
96611
|
try {
|
|
96546
|
-
parsed = JSON.parse(raw.startsWith("@") ?
|
|
96612
|
+
parsed = JSON.parse(raw.startsWith("@") ? readFile52(raw.slice(1)) : raw);
|
|
96547
96613
|
} catch (error) {
|
|
96548
96614
|
return {
|
|
96549
96615
|
ok: false,
|
|
@@ -96714,8 +96780,8 @@ var addCommand = defineCommand215({
|
|
|
96714
96780
|
});
|
|
96715
96781
|
|
|
96716
96782
|
// src/commands/landing/inspiration/code.ts
|
|
96717
|
-
import { mkdir as
|
|
96718
|
-
import
|
|
96783
|
+
import { mkdir as mkdir17, writeFile as writeFile28 } from "fs/promises";
|
|
96784
|
+
import path56 from "path";
|
|
96719
96785
|
import { defineCommand as defineCommand216 } from "citty";
|
|
96720
96786
|
registerSchema({
|
|
96721
96787
|
command: "landing.inspiration.code",
|
|
@@ -96738,10 +96804,10 @@ var codeCommand = defineCommand216({
|
|
|
96738
96804
|
try {
|
|
96739
96805
|
const id = args8.id;
|
|
96740
96806
|
const data = await apiGet("/api/landing-inspiration/section-code", { id });
|
|
96741
|
-
const dir =
|
|
96742
|
-
await
|
|
96743
|
-
const file =
|
|
96744
|
-
await
|
|
96807
|
+
const dir = path56.join(process.cwd(), ".baker", "inspiration", id);
|
|
96808
|
+
await mkdir17(dir, { recursive: true });
|
|
96809
|
+
const file = path56.join(dir, "section.html");
|
|
96810
|
+
await writeFile28(file, data.html);
|
|
96745
96811
|
const hints2 = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
96746
96812
|
const fidelity = fidelityHint(data.fidelity);
|
|
96747
96813
|
if (fidelity) hints2.push(fidelity);
|
|
@@ -96750,7 +96816,7 @@ var codeCommand = defineCommand216({
|
|
|
96750
96816
|
ok: true,
|
|
96751
96817
|
data: {
|
|
96752
96818
|
id,
|
|
96753
|
-
file:
|
|
96819
|
+
file: path56.relative(process.cwd(), file),
|
|
96754
96820
|
bytes: data.html.length,
|
|
96755
96821
|
fidelity: data.fidelity,
|
|
96756
96822
|
reproduction_notes: data.reproductionNotes,
|
|
@@ -97209,8 +97275,8 @@ function classifyCaptureFailure(error) {
|
|
|
97209
97275
|
}
|
|
97210
97276
|
|
|
97211
97277
|
// src/engine/landing-library/run.ts
|
|
97212
|
-
import { mkdir as
|
|
97213
|
-
import
|
|
97278
|
+
import { mkdir as mkdir18, writeFile as writeFile30 } from "fs/promises";
|
|
97279
|
+
import path58 from "path";
|
|
97214
97280
|
|
|
97215
97281
|
// ../proxy/src/preflight.ts
|
|
97216
97282
|
import http from "http";
|
|
@@ -98627,11 +98693,11 @@ async function renderBundleToPng(browser, html, viewportWidth, options = {}) {
|
|
|
98627
98693
|
}
|
|
98628
98694
|
|
|
98629
98695
|
// src/engine/landing-library/report.ts
|
|
98630
|
-
import { writeFile as
|
|
98631
|
-
import
|
|
98696
|
+
import { writeFile as writeFile29 } from "fs/promises";
|
|
98697
|
+
import path57 from "path";
|
|
98632
98698
|
async function writeCaptureReport(manifest, outDir) {
|
|
98633
|
-
const file =
|
|
98634
|
-
await
|
|
98699
|
+
const file = path57.join(outDir, "report.html");
|
|
98700
|
+
await writeFile29(file, renderReport(manifest));
|
|
98635
98701
|
return file;
|
|
98636
98702
|
}
|
|
98637
98703
|
function escapeHtml3(value2) {
|
|
@@ -98774,32 +98840,32 @@ async function reproducePage(args8) {
|
|
|
98774
98840
|
const { browser, page, outDir, pageUrl, livePageShot } = args8;
|
|
98775
98841
|
const built = await buildSectionBundle(page, "body", pageUrl).catch(() => null);
|
|
98776
98842
|
if (!built) return { bundle: null, fidelity: null };
|
|
98777
|
-
await
|
|
98843
|
+
await writeFile30(path58.join(outDir, "page.html"), built.html);
|
|
98778
98844
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width, {
|
|
98779
98845
|
wholePage: true,
|
|
98780
98846
|
timeoutMs: 6e4
|
|
98781
98847
|
});
|
|
98782
98848
|
if (!rendered || !livePageShot) return { bundle: "page.html", fidelity: null };
|
|
98783
|
-
await
|
|
98849
|
+
await writeFile30(path58.join(outDir, "page-rendered.png"), rendered);
|
|
98784
98850
|
const { score, note } = await scoreFidelity(livePageShot, rendered);
|
|
98785
98851
|
return { bundle: "page.html", fidelity: score, ...note ? { fidelityNote: note } : {} };
|
|
98786
98852
|
}
|
|
98787
98853
|
async function captureOneSection(args8) {
|
|
98788
98854
|
const { browser, page, candidate, sectionsDir, outDir, pageUrl, withCode } = args8;
|
|
98789
|
-
const dir =
|
|
98790
|
-
await
|
|
98855
|
+
const dir = path58.join(sectionsDir, String(candidate.index).padStart(2, "0"));
|
|
98856
|
+
await mkdir18(dir, { recursive: true });
|
|
98791
98857
|
const desktop = await captureSection(page, candidate);
|
|
98792
|
-
if (desktop) await
|
|
98858
|
+
if (desktop) await writeFile30(path58.join(dir, "desktop.png"), desktop);
|
|
98793
98859
|
const visualHash = desktop ? await perceptualHash(desktop) : null;
|
|
98794
98860
|
const motion = await collectMotion(page, candidate.selector);
|
|
98795
98861
|
const built = withCode ? await buildSectionBundle(page, candidate.selector, pageUrl) : null;
|
|
98796
98862
|
let fidelity = null;
|
|
98797
98863
|
let fidelityNote;
|
|
98798
98864
|
if (built) {
|
|
98799
|
-
await
|
|
98865
|
+
await writeFile30(path58.join(dir, "section.html"), built.html);
|
|
98800
98866
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width);
|
|
98801
98867
|
if (rendered && desktop) {
|
|
98802
|
-
await
|
|
98868
|
+
await writeFile30(path58.join(dir, "section-rendered.png"), rendered);
|
|
98803
98869
|
const result = await scoreFidelity(desktop, rendered);
|
|
98804
98870
|
fidelity = result.score;
|
|
98805
98871
|
fidelityNote = result.note;
|
|
@@ -98807,9 +98873,9 @@ async function captureOneSection(args8) {
|
|
|
98807
98873
|
}
|
|
98808
98874
|
return {
|
|
98809
98875
|
...candidate,
|
|
98810
|
-
desktopShot: desktop ?
|
|
98876
|
+
desktopShot: desktop ? path58.relative(outDir, path58.join(dir, "desktop.png")) : null,
|
|
98811
98877
|
mobileShot: null,
|
|
98812
|
-
bundle: built ?
|
|
98878
|
+
bundle: built ? path58.relative(outDir, path58.join(dir, "section.html")) : null,
|
|
98813
98879
|
fidelity,
|
|
98814
98880
|
...fidelityNote ? { fidelityNote } : {},
|
|
98815
98881
|
...built ? { cssStats: built.stats } : {},
|
|
@@ -98828,9 +98894,9 @@ async function captureMobileShots(args8) {
|
|
|
98828
98894
|
for (const section2 of sections) {
|
|
98829
98895
|
const shot = await captureSectionOnMobile(mobile.page, section2);
|
|
98830
98896
|
if (!shot) continue;
|
|
98831
|
-
const file =
|
|
98832
|
-
await
|
|
98833
|
-
section2.mobileShot =
|
|
98897
|
+
const file = path58.join(sectionsDir, String(section2.index).padStart(2, "0"), "mobile.png");
|
|
98898
|
+
await writeFile30(file, shot);
|
|
98899
|
+
section2.mobileShot = path58.relative(outDir, file);
|
|
98834
98900
|
}
|
|
98835
98901
|
} finally {
|
|
98836
98902
|
await mobile.context.close();
|
|
@@ -98845,10 +98911,10 @@ async function captureMotionTakes(args8) {
|
|
|
98845
98911
|
const filmOne = async (section2) => {
|
|
98846
98912
|
const take = await captureMotionTake(browser, pageUrl, section2.selector).catch(() => null);
|
|
98847
98913
|
if (!take) return;
|
|
98848
|
-
const dir =
|
|
98849
|
-
const file =
|
|
98850
|
-
await
|
|
98851
|
-
section2.motionFilmstrip =
|
|
98914
|
+
const dir = path58.join(sectionsDir, String(section2.index).padStart(2, "0"));
|
|
98915
|
+
const file = path58.join(dir, "motion-filmstrip.png");
|
|
98916
|
+
await writeFile30(file, take.filmstrip);
|
|
98917
|
+
section2.motionFilmstrip = path58.relative(outDir, file);
|
|
98852
98918
|
log(` [${section2.index}] ${section2.motion.summary}`);
|
|
98853
98919
|
};
|
|
98854
98920
|
const queue = [...moving];
|
|
@@ -98905,7 +98971,7 @@ async function captureAlternateViews(args8) {
|
|
|
98905
98971
|
async function reproduceWholePage(args8) {
|
|
98906
98972
|
const { browser, page, outDir, pageUrl, withCode, log } = args8;
|
|
98907
98973
|
const fullPage = await page.screenshot({ type: "png", fullPage: true }).catch(() => null);
|
|
98908
|
-
if (fullPage) await
|
|
98974
|
+
if (fullPage) await writeFile30(path58.join(outDir, "full-page.png"), fullPage);
|
|
98909
98975
|
if (!withCode) return { bundle: null, fidelity: null };
|
|
98910
98976
|
const reproduction = await reproducePage({ browser, page, outDir, pageUrl, livePageShot: fullPage });
|
|
98911
98977
|
log(`page reproduction: ${reproduction.fidelity === null ? "unavailable" : reproduction.fidelity.toFixed(2)}`);
|
|
@@ -98976,7 +99042,7 @@ async function openViaLadder(args8) {
|
|
|
98976
99042
|
async function scrapeLanding(options) {
|
|
98977
99043
|
const timeoutMs = options.timeoutMs ?? 45e3;
|
|
98978
99044
|
const log = options.onProgress ?? (() => void 0);
|
|
98979
|
-
const sectionsDir =
|
|
99045
|
+
const sectionsDir = path58.join(options.outDir, "sections");
|
|
98980
99046
|
const nonPublic = refuseNonPublicUrl(options.url);
|
|
98981
99047
|
if (nonPublic) {
|
|
98982
99048
|
throw new BlockedPageError({
|
|
@@ -98996,7 +99062,7 @@ async function scrapeLanding(options) {
|
|
|
98996
99062
|
const withMotion = options.motion !== false && !escalated;
|
|
98997
99063
|
const renderBrowser = options.code === false ? null : await launchBrowser();
|
|
98998
99064
|
try {
|
|
98999
|
-
await
|
|
99065
|
+
await mkdir18(sectionsDir, { recursive: true });
|
|
99000
99066
|
const sections = await captureSections({
|
|
99001
99067
|
browser: renderBrowser ?? browser,
|
|
99002
99068
|
page,
|
|
@@ -99038,7 +99104,7 @@ async function scrapeLanding(options) {
|
|
|
99038
99104
|
security: prepared.security,
|
|
99039
99105
|
captureTier: tier
|
|
99040
99106
|
};
|
|
99041
|
-
await
|
|
99107
|
+
await writeFile30(path58.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
99042
99108
|
`);
|
|
99043
99109
|
if (options.report !== false) {
|
|
99044
99110
|
const reportPath = await writeCaptureReport(manifest, options.outDir);
|
|
@@ -99053,28 +99119,28 @@ async function scrapeLanding(options) {
|
|
|
99053
99119
|
|
|
99054
99120
|
// src/commands/landing/inspiration/captureOut.ts
|
|
99055
99121
|
import { existsSync as existsSync12 } from "fs";
|
|
99056
|
-
import
|
|
99122
|
+
import path59 from "path";
|
|
99057
99123
|
var SCRATCH_DIR = ".baker";
|
|
99058
99124
|
function isWithin(parent, target2) {
|
|
99059
|
-
const relative =
|
|
99060
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
99125
|
+
const relative = path59.relative(parent, target2);
|
|
99126
|
+
return relative === "" || !relative.startsWith("..") && !path59.isAbsolute(relative);
|
|
99061
99127
|
}
|
|
99062
99128
|
function findRepoRoot(from) {
|
|
99063
|
-
let dir =
|
|
99129
|
+
let dir = path59.resolve(from);
|
|
99064
99130
|
for (; ; ) {
|
|
99065
|
-
if (existsSync12(
|
|
99066
|
-
const parent =
|
|
99131
|
+
if (existsSync12(path59.join(dir, ".git"))) return dir;
|
|
99132
|
+
const parent = path59.dirname(dir);
|
|
99067
99133
|
if (parent === dir) return null;
|
|
99068
99134
|
dir = parent;
|
|
99069
99135
|
}
|
|
99070
99136
|
}
|
|
99071
99137
|
function checkCaptureOut(out, options) {
|
|
99072
99138
|
const { cwd, repoRoot } = options;
|
|
99073
|
-
const resolved =
|
|
99139
|
+
const resolved = path59.resolve(cwd, out);
|
|
99074
99140
|
if (repoRoot === null || !isWithin(repoRoot, resolved)) return { ok: true };
|
|
99075
|
-
const scratch =
|
|
99141
|
+
const scratch = path59.join(repoRoot, SCRATCH_DIR);
|
|
99076
99142
|
if (isWithin(scratch, resolved)) return { ok: true };
|
|
99077
|
-
const suggestion =
|
|
99143
|
+
const suggestion = path59.posix.join(SCRATCH_DIR, "teardowns", path59.basename(resolved) || "capture");
|
|
99078
99144
|
return {
|
|
99079
99145
|
ok: false,
|
|
99080
99146
|
error: {
|
|
@@ -99248,12 +99314,12 @@ var scrapeCommand = defineCommand220({
|
|
|
99248
99314
|
});
|
|
99249
99315
|
|
|
99250
99316
|
// src/commands/landing/inspiration/search.ts
|
|
99251
|
-
import
|
|
99317
|
+
import path61 from "path";
|
|
99252
99318
|
import { defineCommand as defineCommand221 } from "citty";
|
|
99253
99319
|
|
|
99254
99320
|
// src/commands/landing/inspiration/shot.ts
|
|
99255
|
-
import { mkdir as
|
|
99256
|
-
import
|
|
99321
|
+
import { mkdir as mkdir19, writeFile as writeFile31 } from "fs/promises";
|
|
99322
|
+
import path60 from "path";
|
|
99257
99323
|
import sharp9 from "sharp";
|
|
99258
99324
|
var READABLE_SHOT = {
|
|
99259
99325
|
maxWidth: 1440,
|
|
@@ -99279,9 +99345,9 @@ async function downloadReadableShot(url, file) {
|
|
|
99279
99345
|
const response = await fetch(url);
|
|
99280
99346
|
if (!response.ok) return null;
|
|
99281
99347
|
const shot = await toReadableShot(Buffer.from(await response.arrayBuffer()));
|
|
99282
|
-
await
|
|
99283
|
-
await
|
|
99284
|
-
return
|
|
99348
|
+
await mkdir19(path60.dirname(file), { recursive: true });
|
|
99349
|
+
await writeFile31(file, shot);
|
|
99350
|
+
return path60.relative(process.cwd(), file);
|
|
99285
99351
|
} catch {
|
|
99286
99352
|
return null;
|
|
99287
99353
|
}
|
|
@@ -99373,13 +99439,13 @@ function buildSearchBody2(args8) {
|
|
|
99373
99439
|
return body;
|
|
99374
99440
|
}
|
|
99375
99441
|
async function downloadShots(results) {
|
|
99376
|
-
const dir =
|
|
99442
|
+
const dir = path61.join(process.cwd(), ".baker", "inspiration");
|
|
99377
99443
|
const saved = /* @__PURE__ */ new Map();
|
|
99378
99444
|
await Promise.all(
|
|
99379
99445
|
results.map(async (result) => {
|
|
99380
99446
|
const file = await downloadReadableShot(
|
|
99381
99447
|
result.desktopShotUrl,
|
|
99382
|
-
|
|
99448
|
+
path61.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
|
|
99383
99449
|
);
|
|
99384
99450
|
if (file) saved.set(result.id, file);
|
|
99385
99451
|
})
|
|
@@ -99607,7 +99673,7 @@ var sequencesCommand = defineCommand222({
|
|
|
99607
99673
|
});
|
|
99608
99674
|
|
|
99609
99675
|
// src/commands/landing/inspiration/view.ts
|
|
99610
|
-
import
|
|
99676
|
+
import path62 from "path";
|
|
99611
99677
|
import { defineCommand as defineCommand223 } from "citty";
|
|
99612
99678
|
registerSchema({
|
|
99613
99679
|
command: "landing.inspiration.view",
|
|
@@ -99640,12 +99706,12 @@ var viewCommand2 = defineCommand223({
|
|
|
99640
99706
|
const id = args8.id;
|
|
99641
99707
|
const data = await apiGet("/api/landing-inspiration/section", { id });
|
|
99642
99708
|
const section2 = data.section;
|
|
99643
|
-
const dir =
|
|
99709
|
+
const dir = path62.join(process.cwd(), ".baker", "inspiration", id);
|
|
99644
99710
|
const ext = READABLE_SHOT.extension;
|
|
99645
99711
|
const [desktop, mobile, filmstrip] = await Promise.all([
|
|
99646
|
-
downloadReadableShot(section2.desktopShotUrl,
|
|
99647
|
-
downloadReadableShot(section2.mobileShotUrl,
|
|
99648
|
-
downloadReadableShot(section2.motionFilmstripUrl,
|
|
99712
|
+
downloadReadableShot(section2.desktopShotUrl, path62.join(dir, `desktop.${ext}`)),
|
|
99713
|
+
downloadReadableShot(section2.mobileShotUrl, path62.join(dir, `mobile.${ext}`)),
|
|
99714
|
+
downloadReadableShot(section2.motionFilmstripUrl, path62.join(dir, `motion-filmstrip.${ext}`))
|
|
99649
99715
|
]);
|
|
99650
99716
|
const full = args8.full;
|
|
99651
99717
|
const hints2 = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
@@ -99753,8 +99819,8 @@ Full guide: __tooling__/docs/tools/baker/landing.md`
|
|
|
99753
99819
|
|
|
99754
99820
|
// src/commands/landing/variant.ts
|
|
99755
99821
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
99756
|
-
import { mkdir as
|
|
99757
|
-
import
|
|
99822
|
+
import { mkdir as mkdir20, readdir as readdir23, readFile as readFile49, stat as stat12, writeFile as writeFile32 } from "fs/promises";
|
|
99823
|
+
import path63 from "path";
|
|
99758
99824
|
import { defineCommand as defineCommand225 } from "citty";
|
|
99759
99825
|
registerSchema({
|
|
99760
99826
|
command: "landing.variant",
|
|
@@ -99781,7 +99847,7 @@ registerSchema({
|
|
|
99781
99847
|
var SLUG_RE2 = /^[a-z0-9][a-z0-9-]*$/;
|
|
99782
99848
|
async function readInternalId(definitionPath) {
|
|
99783
99849
|
try {
|
|
99784
|
-
const text3 = await
|
|
99850
|
+
const text3 = await readFile49(definitionPath, "utf8");
|
|
99785
99851
|
return /^internalId:\s*"?([^"\s]+)"?\s*$/m.exec(text3)?.[1] ?? null;
|
|
99786
99852
|
} catch {
|
|
99787
99853
|
return null;
|
|
@@ -99790,7 +99856,7 @@ async function readInternalId(definitionPath) {
|
|
|
99790
99856
|
async function archivedVariantNumbers(root, internalId) {
|
|
99791
99857
|
if (!internalId) return [];
|
|
99792
99858
|
try {
|
|
99793
|
-
const entries = await readdir23(
|
|
99859
|
+
const entries = await readdir23(path63.resolve(root, "src/_variants", internalId), { withFileTypes: true });
|
|
99794
99860
|
return entries.filter((entry) => entry.isDirectory()).map((entry) => Number(entry.name)).filter((n) => Number.isInteger(n) && n > 0);
|
|
99795
99861
|
} catch {
|
|
99796
99862
|
return [];
|
|
@@ -99820,7 +99886,7 @@ async function exists2(p) {
|
|
|
99820
99886
|
}
|
|
99821
99887
|
async function listLandingSlugs2(root) {
|
|
99822
99888
|
try {
|
|
99823
|
-
const entries = await readdir23(
|
|
99889
|
+
const entries = await readdir23(path63.resolve(root, "src", "pages"), { withFileTypes: true });
|
|
99824
99890
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
99825
99891
|
} catch {
|
|
99826
99892
|
return [];
|
|
@@ -99836,7 +99902,7 @@ async function listComponents(componentsDir, prefix = "") {
|
|
|
99836
99902
|
const out = [];
|
|
99837
99903
|
for (const entry of entries) {
|
|
99838
99904
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
99839
|
-
if (entry.isDirectory()) out.push(...await listComponents(
|
|
99905
|
+
if (entry.isDirectory()) out.push(...await listComponents(path63.join(componentsDir, entry.name), rel));
|
|
99840
99906
|
else if (entry.name.endsWith(".astro")) out.push(rel);
|
|
99841
99907
|
}
|
|
99842
99908
|
return out.sort();
|
|
@@ -99903,16 +99969,16 @@ async function resolveTargets(projectRoot, opts) {
|
|
|
99903
99969
|
`"${controlSlug}" is already a variant of "${variantOfSlug(controlSlug)}". Make the next variant of the page itself: \`baker landing variant ${variantOfSlug(controlSlug)}\`.`
|
|
99904
99970
|
);
|
|
99905
99971
|
}
|
|
99906
|
-
const controlDir =
|
|
99972
|
+
const controlDir = path63.resolve(projectRoot, "src", "pages", controlSlug);
|
|
99907
99973
|
if (!await isDir3(controlDir)) {
|
|
99908
99974
|
fail11("NOT_FOUND", `No page at src/pages/${controlSlug}/`, {
|
|
99909
99975
|
availableSlugs: await listLandingSlugs2(projectRoot)
|
|
99910
99976
|
});
|
|
99911
99977
|
}
|
|
99912
|
-
if (!await exists2(
|
|
99978
|
+
if (!await exists2(path63.join(controlDir, "index.astro"))) {
|
|
99913
99979
|
fail11("NOT_FOUND", `src/pages/${controlSlug}/index.astro is missing, so there is no page to make a variant of.`);
|
|
99914
99980
|
}
|
|
99915
|
-
const internalId = await readInternalId(
|
|
99981
|
+
const internalId = await readInternalId(path63.join(controlDir, "_definition.md"));
|
|
99916
99982
|
const variantSlugValue = variantSlug(
|
|
99917
99983
|
controlSlug,
|
|
99918
99984
|
nextVariantNumber(
|
|
@@ -99921,8 +99987,8 @@ async function resolveTargets(projectRoot, opts) {
|
|
|
99921
99987
|
await archivedVariantNumbers(projectRoot, internalId)
|
|
99922
99988
|
)
|
|
99923
99989
|
);
|
|
99924
|
-
const variantDir =
|
|
99925
|
-
const available = await listComponents(
|
|
99990
|
+
const variantDir = path63.resolve(projectRoot, "src", "pages", variantSlugValue);
|
|
99991
|
+
const available = await listComponents(path63.join(controlDir, "_components"));
|
|
99926
99992
|
const forks = [];
|
|
99927
99993
|
for (const name of parseForks(opts.fork, opts.rawArgs)) {
|
|
99928
99994
|
const resolved = resolveForkName(name, available);
|
|
@@ -99938,26 +100004,26 @@ async function resolveTargets(projectRoot, opts) {
|
|
|
99938
100004
|
}
|
|
99939
100005
|
async function writeVariant(opts) {
|
|
99940
100006
|
const { controlDir, variantDir, controlSlug, variantSlug: variantSlug2, forks } = opts;
|
|
99941
|
-
const forkedTargets = new Set(forks.map((f) =>
|
|
100007
|
+
const forkedTargets = new Set(forks.map((f) => path63.join(controlDir, "_components", f)));
|
|
99942
100008
|
const written = [];
|
|
99943
|
-
const indexText = await
|
|
99944
|
-
await
|
|
99945
|
-
await
|
|
99946
|
-
|
|
100009
|
+
const indexText = await readFile49(path63.join(controlDir, "index.astro"), "utf8");
|
|
100010
|
+
await mkdir20(variantDir, { recursive: true });
|
|
100011
|
+
await writeFile32(
|
|
100012
|
+
path63.join(variantDir, "index.astro"),
|
|
99947
100013
|
repointFile(indexText, { fromDir: controlDir, toDir: variantDir, controlDir, variantDir, forkedTargets }),
|
|
99948
100014
|
"utf8"
|
|
99949
100015
|
);
|
|
99950
100016
|
written.push(`src/pages/${variantSlug2}/index.astro`);
|
|
99951
100017
|
for (const fork of forks) {
|
|
99952
|
-
const fromFile =
|
|
99953
|
-
const toFile =
|
|
99954
|
-
const text3 = await
|
|
99955
|
-
await
|
|
99956
|
-
await
|
|
100018
|
+
const fromFile = path63.join(controlDir, "_components", fork);
|
|
100019
|
+
const toFile = path63.join(variantDir, "_components", fork);
|
|
100020
|
+
const text3 = await readFile49(fromFile, "utf8");
|
|
100021
|
+
await mkdir20(path63.dirname(toFile), { recursive: true });
|
|
100022
|
+
await writeFile32(
|
|
99957
100023
|
toFile,
|
|
99958
100024
|
repointFile(text3, {
|
|
99959
|
-
fromDir:
|
|
99960
|
-
toDir:
|
|
100025
|
+
fromDir: path63.dirname(fromFile),
|
|
100026
|
+
toDir: path63.dirname(toFile),
|
|
99961
100027
|
controlDir,
|
|
99962
100028
|
variantDir,
|
|
99963
100029
|
forkedTargets
|
|
@@ -99966,9 +100032,9 @@ async function writeVariant(opts) {
|
|
|
99966
100032
|
);
|
|
99967
100033
|
written.push(`src/pages/${variantSlug2}/_components/${fork}`);
|
|
99968
100034
|
}
|
|
99969
|
-
const controlDefinitionPath =
|
|
100035
|
+
const controlDefinitionPath = path63.join(controlDir, "_definition.md");
|
|
99970
100036
|
if (await exists2(controlDefinitionPath)) {
|
|
99971
|
-
const definition = buildVariantDefinition(await
|
|
100037
|
+
const definition = buildVariantDefinition(await readFile49(controlDefinitionPath, "utf8"), {
|
|
99972
100038
|
internalId: randomUUID4().replace(/-/g, "").slice(0, 8),
|
|
99973
100039
|
variantSlug: variantSlug2,
|
|
99974
100040
|
controlSlug,
|
|
@@ -99976,11 +100042,11 @@ async function writeVariant(opts) {
|
|
|
99976
100042
|
...opts.because ? { because: opts.because } : {},
|
|
99977
100043
|
...opts.change ? { change: opts.change } : {}
|
|
99978
100044
|
});
|
|
99979
|
-
await
|
|
100045
|
+
await writeFile32(path63.join(variantDir, "_definition.md"), definition, "utf8");
|
|
99980
100046
|
written.push(`src/pages/${variantSlug2}/_definition.md`);
|
|
99981
100047
|
}
|
|
99982
|
-
await
|
|
99983
|
-
await
|
|
100048
|
+
await mkdir20(path63.join(variantDir, "_images"), { recursive: true });
|
|
100049
|
+
await writeFile32(path63.join(variantDir, "_images", ".gitkeep"), "", "utf8");
|
|
99984
100050
|
return written;
|
|
99985
100051
|
}
|
|
99986
100052
|
var variantCommand = defineCommand225({
|
|
@@ -100085,28 +100151,28 @@ Subcommands:
|
|
|
100085
100151
|
});
|
|
100086
100152
|
|
|
100087
100153
|
// ../api/src/mcp-catalog.ts
|
|
100088
|
-
import { z as
|
|
100154
|
+
import { z as z76 } from "zod";
|
|
100089
100155
|
var MCP_CATALOG_DEFAULT_LIMIT = 8;
|
|
100090
100156
|
var MCP_CATALOG_DESCRIPTION_MAX = 160;
|
|
100091
|
-
var mcpCatalogRequestSchema =
|
|
100092
|
-
search:
|
|
100093
|
-
limit:
|
|
100157
|
+
var mcpCatalogRequestSchema = z76.object({
|
|
100158
|
+
search: z76.string().trim().min(1, "Provide --search").max(100),
|
|
100159
|
+
limit: z76.coerce.number().int().min(1).max(20).default(MCP_CATALOG_DEFAULT_LIMIT)
|
|
100094
100160
|
});
|
|
100095
|
-
var mcpCatalogConnectSchema =
|
|
100096
|
-
var mcpCatalogEntrySchema =
|
|
100161
|
+
var mcpCatalogConnectSchema = z76.enum(["sign-in", "key"]);
|
|
100162
|
+
var mcpCatalogEntrySchema = z76.object({
|
|
100097
100163
|
/** The toolkit slug — what request_connection `{ app }` takes. */
|
|
100098
|
-
app:
|
|
100099
|
-
name:
|
|
100100
|
-
description:
|
|
100164
|
+
app: z76.string(),
|
|
100165
|
+
name: z76.string(),
|
|
100166
|
+
description: z76.string().max(MCP_CATALOG_DESCRIPTION_MAX),
|
|
100101
100167
|
connect: mcpCatalogConnectSchema,
|
|
100102
|
-
connected:
|
|
100168
|
+
connected: z76.boolean()
|
|
100103
100169
|
});
|
|
100104
|
-
var mcpCatalogResponseSchema =
|
|
100105
|
-
apps:
|
|
100170
|
+
var mcpCatalogResponseSchema = z76.object({
|
|
100171
|
+
apps: z76.array(mcpCatalogEntrySchema),
|
|
100106
100172
|
/** The company has managed apps switched off: nothing here can be connected, so `apps` is empty. */
|
|
100107
|
-
managedDisabled:
|
|
100173
|
+
managedDisabled: z76.boolean().optional(),
|
|
100108
100174
|
/** Baker's own connections this search matched — ask for these with `{ platform }`, never a managed copy. */
|
|
100109
|
-
platforms:
|
|
100175
|
+
platforms: z76.array(z76.string()).optional()
|
|
100110
100176
|
});
|
|
100111
100177
|
|
|
100112
100178
|
// src/commands/mcp/index.ts
|
|
@@ -101327,11 +101393,11 @@ var countriesCommand = defineCommand237({
|
|
|
101327
101393
|
});
|
|
101328
101394
|
|
|
101329
101395
|
// src/commands/research/customers.ts
|
|
101330
|
-
import { mkdir as
|
|
101331
|
-
import
|
|
101396
|
+
import { mkdir as mkdir21, readdir as readdir24, readFile as readFile50, writeFile as writeFile33 } from "fs/promises";
|
|
101397
|
+
import path64 from "path";
|
|
101332
101398
|
import { defineCommand as defineCommand239 } from "citty";
|
|
101333
101399
|
import { parse as parseYaml5 } from "yaml";
|
|
101334
|
-
import { z as
|
|
101400
|
+
import { z as z77 } from "zod";
|
|
101335
101401
|
|
|
101336
101402
|
// src/commands/research/reddit.ts
|
|
101337
101403
|
import { defineCommand as defineCommand238 } from "citty";
|
|
@@ -101743,13 +101809,13 @@ var PLATFORM_NAMES = {
|
|
|
101743
101809
|
app_store: "App Store",
|
|
101744
101810
|
google_play: "Google Play"
|
|
101745
101811
|
};
|
|
101746
|
-
var LibraryReviewSchema =
|
|
101747
|
-
_id:
|
|
101748
|
-
reviewText:
|
|
101749
|
-
sourceType:
|
|
101750
|
-
reviewerName:
|
|
101751
|
-
rating:
|
|
101752
|
-
reviewDate:
|
|
101812
|
+
var LibraryReviewSchema = z77.object({
|
|
101813
|
+
_id: z77.string().min(1),
|
|
101814
|
+
reviewText: z77.string(),
|
|
101815
|
+
sourceType: z77.string().optional(),
|
|
101816
|
+
reviewerName: z77.string().optional(),
|
|
101817
|
+
rating: z77.number().optional(),
|
|
101818
|
+
reviewDate: z77.number().optional()
|
|
101753
101819
|
});
|
|
101754
101820
|
function dayOf(ms) {
|
|
101755
101821
|
const date = new Date(ms);
|
|
@@ -101832,13 +101898,13 @@ function buildCustomersPrompt(sources, focus) {
|
|
|
101832
101898
|
...sources.map((source, i) => `[${i + 1}] ${source.text}`)
|
|
101833
101899
|
].join("\n");
|
|
101834
101900
|
}
|
|
101835
|
-
var AnswerSchema =
|
|
101836
|
-
var CandidateSchema =
|
|
101837
|
-
source:
|
|
101838
|
-
quote:
|
|
101839
|
-
type:
|
|
101840
|
-
force:
|
|
101841
|
-
persona:
|
|
101901
|
+
var AnswerSchema = z77.object({ insights: z77.array(z77.unknown()) });
|
|
101902
|
+
var CandidateSchema = z77.object({
|
|
101903
|
+
source: z77.coerce.number().int().positive(),
|
|
101904
|
+
quote: z77.string().trim().min(1),
|
|
101905
|
+
type: z77.enum(INSIGHT_TYPES).catch("other"),
|
|
101906
|
+
force: z77.enum(FORCES).optional().catch(void 0),
|
|
101907
|
+
persona: z77.string().trim().optional().catch(void 0)
|
|
101842
101908
|
});
|
|
101843
101909
|
function candidatesFrom(answer) {
|
|
101844
101910
|
const parsed = AnswerSchema.safeParse(answer);
|
|
@@ -101880,7 +101946,7 @@ function customersHints(input) {
|
|
|
101880
101946
|
const cite = `Cite these by id in an idea's \`evidence\` (src/creatives/_ideas/<idea>.md); write the ad from the quote as said.`;
|
|
101881
101947
|
return written < FEW_OBSERVED ? [`Only ${written} observed. ${widen}`, cite] : [cite];
|
|
101882
101948
|
}
|
|
101883
|
-
var INSIGHTS_DIR2 =
|
|
101949
|
+
var INSIGHTS_DIR2 = path64.join("src", "content", "insights");
|
|
101884
101950
|
var BATCH_CHARS = 24e3;
|
|
101885
101951
|
var MAX_BATCHES = 5;
|
|
101886
101952
|
var WEB_PAGES = 3;
|
|
@@ -101929,7 +101995,7 @@ var ARGS15 = {
|
|
|
101929
101995
|
}
|
|
101930
101996
|
};
|
|
101931
101997
|
registerSchema({ command: "research.customers", description: DESCRIPTION13, args: ARGS15 });
|
|
101932
|
-
var QuoteFrontmatter =
|
|
101998
|
+
var QuoteFrontmatter = z77.object({ quote: z77.string() });
|
|
101933
101999
|
function quoteOf(raw) {
|
|
101934
102000
|
const front = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1];
|
|
101935
102001
|
if (!front) return "";
|
|
@@ -101945,12 +102011,12 @@ async function readExistingInsights(dir) {
|
|
|
101945
102011
|
return await Promise.all(
|
|
101946
102012
|
files.map(async (file) => ({
|
|
101947
102013
|
id: file.slice(0, -3),
|
|
101948
|
-
quote: quoteOf(await
|
|
102014
|
+
quote: quoteOf(await readFile50(path64.join(dir, file), "utf8").catch(() => ""))
|
|
101949
102015
|
}))
|
|
101950
102016
|
);
|
|
101951
102017
|
}
|
|
101952
102018
|
async function productSlugs(root) {
|
|
101953
|
-
const files = await readdir24(
|
|
102019
|
+
const files = await readdir24(path64.join(root, "src", "content", "products")).catch(() => []);
|
|
101954
102020
|
return files.filter((f) => f.endsWith(".md")).map((f) => f.slice(0, -3));
|
|
101955
102021
|
}
|
|
101956
102022
|
async function gatherTestimonials(focus) {
|
|
@@ -101973,7 +102039,7 @@ async function gatherReddit(query) {
|
|
|
101973
102039
|
const items = answers.flatMap((a) => a.status === "fulfilled" ? a.value.results : []).filter((item) => !seen.has(item.id) && Boolean(seen.add(item.id)));
|
|
101974
102040
|
return sourcesFromReddit(items);
|
|
101975
102041
|
}
|
|
101976
|
-
var ExploreAnswer =
|
|
102042
|
+
var ExploreAnswer = z77.object({ sources: z77.array(z77.object({ url: z77.string() })) });
|
|
101977
102043
|
async function resolveGroundingRedirect(url) {
|
|
101978
102044
|
if (!URL.canParse(url) || new URL(url).host !== "vertexaisearch.cloud.google.com") return url;
|
|
101979
102045
|
try {
|
|
@@ -102050,7 +102116,7 @@ async function classifyAll(sources, focus, root) {
|
|
|
102050
102116
|
const batches = batchSources(sources, BATCH_CHARS).slice(0, MAX_BATCHES);
|
|
102051
102117
|
const spent = [];
|
|
102052
102118
|
const answers = await Promise.all(batches.map((batch) => classifyBatch(batch, focus, (u) => spent.push(u))));
|
|
102053
|
-
await holdUnbilledSpend(
|
|
102119
|
+
await holdUnbilledSpend(path64.join(root, "canvas", ".cache"), spent);
|
|
102054
102120
|
if (batches.length > 0 && answers.every((a) => a === null)) return null;
|
|
102055
102121
|
const result = { kept: [], dropped: 0, tooLong: 0 };
|
|
102056
102122
|
batches.forEach((batch, i) => {
|
|
@@ -102063,11 +102129,11 @@ async function classifyAll(sources, focus, root) {
|
|
|
102063
102129
|
}
|
|
102064
102130
|
async function writeInsights(dir, writes, opts) {
|
|
102065
102131
|
if (writes.length === 0) return [];
|
|
102066
|
-
await
|
|
102132
|
+
await mkdir21(dir, { recursive: true });
|
|
102067
102133
|
const written = [];
|
|
102068
102134
|
for (const entry of writes) {
|
|
102069
102135
|
const file = renderInsightFile(entry.insight, opts);
|
|
102070
|
-
const ok = await
|
|
102136
|
+
const ok = await writeFile33(path64.join(dir, `${entry.id}.md`), file, { encoding: "utf8", flag: "wx" }).then(
|
|
102071
102137
|
() => true,
|
|
102072
102138
|
() => false
|
|
102073
102139
|
);
|
|
@@ -102118,7 +102184,7 @@ var customersCommand = defineCommand239({
|
|
|
102118
102184
|
"Run it again. Do not write insights by hand: an insight is only a quote this command found in its source."
|
|
102119
102185
|
);
|
|
102120
102186
|
}
|
|
102121
|
-
const dir =
|
|
102187
|
+
const dir = path64.join(root, INSIGHTS_DIR2);
|
|
102122
102188
|
const plan = planInsightWrites(gated.kept, await readExistingInsights(dir), limit);
|
|
102123
102189
|
const today2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
102124
102190
|
const shown = dryRun ? plan.writes : await writeInsights(dir, plan.writes, { today: today2, ...product ? { product } : {} });
|
|
@@ -103720,8 +103786,8 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
|
|
|
103720
103786
|
});
|
|
103721
103787
|
|
|
103722
103788
|
// src/commands/scheduled-agents/templates.ts
|
|
103723
|
-
import { readFile as
|
|
103724
|
-
import
|
|
103789
|
+
import { readFile as readFile51 } from "fs/promises";
|
|
103790
|
+
import path65 from "path";
|
|
103725
103791
|
import { defineCommand as defineCommand256 } from "citty";
|
|
103726
103792
|
registerSchema({
|
|
103727
103793
|
command: "scheduled-agents.templates",
|
|
@@ -103828,7 +103894,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
|
|
|
103828
103894
|
}
|
|
103829
103895
|
if (save.length > 0) {
|
|
103830
103896
|
const briefFile = flag2("brief-file");
|
|
103831
|
-
const brief2 = briefFile.length > 0 ? await
|
|
103897
|
+
const brief2 = briefFile.length > 0 ? await readFile51(path65.resolve(briefFile), "utf8") : flag2("brief");
|
|
103832
103898
|
if (brief2.trim().length === 0) {
|
|
103833
103899
|
failValidation6("--brief-file (preferred) or --brief is required: the brief is the recipe.");
|
|
103834
103900
|
}
|
|
@@ -104373,8 +104439,8 @@ function onOff(flag2, value2) {
|
|
|
104373
104439
|
if (value2 === "off" || value2 === "false") return false;
|
|
104374
104440
|
throw new SiteArgsError(`--${flag2} must be on or off`);
|
|
104375
104441
|
}
|
|
104376
|
-
function parseProfile(raw,
|
|
104377
|
-
const text3 = raw.startsWith("@") ?
|
|
104442
|
+
function parseProfile(raw, readFile52) {
|
|
104443
|
+
const text3 = raw.startsWith("@") ? readFile52(raw.slice(1)) : raw;
|
|
104378
104444
|
let parsed;
|
|
104379
104445
|
try {
|
|
104380
104446
|
parsed = JSON.parse(text3);
|
|
@@ -104386,7 +104452,7 @@ function parseProfile(raw, readFile51) {
|
|
|
104386
104452
|
}
|
|
104387
104453
|
return parsed;
|
|
104388
104454
|
}
|
|
104389
|
-
function parseSetArgs(args8,
|
|
104455
|
+
function parseSetArgs(args8, readFile52) {
|
|
104390
104456
|
const inputs = [];
|
|
104391
104457
|
if (args8.consent !== void 0) {
|
|
104392
104458
|
inputs.push({ setting: "consent", mode: oneOf3("consent", args8.consent, SITE_CONSENT_MODES) });
|
|
@@ -104419,7 +104485,7 @@ function parseSetArgs(args8, readFile51) {
|
|
|
104419
104485
|
inputs.push({ setting: "timezone", timezone: args8.timezone });
|
|
104420
104486
|
}
|
|
104421
104487
|
if (args8["export-profile"] !== void 0) {
|
|
104422
|
-
inputs.push({ setting: "exportProfile", profile: parseProfile(args8["export-profile"],
|
|
104488
|
+
inputs.push({ setting: "exportProfile", profile: parseProfile(args8["export-profile"], readFile52) });
|
|
104423
104489
|
}
|
|
104424
104490
|
if (inputs.length === 0) {
|
|
104425
104491
|
throw new SiteArgsError(
|
|
@@ -104610,7 +104676,7 @@ var setCommand = defineCommand264({
|
|
|
104610
104676
|
timezone: args8.timezone,
|
|
104611
104677
|
"export-profile": args8["export-profile"]
|
|
104612
104678
|
},
|
|
104613
|
-
(
|
|
104679
|
+
(path68) => readFileSync26(path68, "utf8")
|
|
104614
104680
|
);
|
|
104615
104681
|
const body = { chatId: requireChatId(), inputs };
|
|
104616
104682
|
writeJson(await apiPost("/api/site/stage", body));
|
|
@@ -104892,7 +104958,7 @@ function parseImageRefs(spec) {
|
|
|
104892
104958
|
}
|
|
104893
104959
|
var defaultDeps = {
|
|
104894
104960
|
ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
|
|
104895
|
-
upload: (
|
|
104961
|
+
upload: (path68) => uploadLocalImage({ file: path68, contentType: detectImageContentType(path68), source: "uploaded" })
|
|
104896
104962
|
};
|
|
104897
104963
|
async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
104898
104964
|
const refs = parseImageRefs(spec);
|
|
@@ -104912,10 +104978,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
|
104912
104978
|
}
|
|
104913
104979
|
return { imageIds, added };
|
|
104914
104980
|
}
|
|
104915
|
-
function uploadFailure(
|
|
104981
|
+
function uploadFailure(path68) {
|
|
104916
104982
|
return (error) => {
|
|
104917
104983
|
if (error instanceof ApiError) throw error;
|
|
104918
|
-
throw new ApiError("VALIDATION_ERROR", `Could not read "${
|
|
104984
|
+
throw new ApiError("VALIDATION_ERROR", `Could not read "${path68}" as an image.`);
|
|
104919
104985
|
};
|
|
104920
104986
|
}
|
|
104921
104987
|
|
|
@@ -106015,10 +106081,10 @@ async function stageOp4(op) {
|
|
|
106015
106081
|
handleError8(err);
|
|
106016
106082
|
}
|
|
106017
106083
|
}
|
|
106018
|
-
async function draftAction3(
|
|
106084
|
+
async function draftAction3(path68, body, chat) {
|
|
106019
106085
|
const chatId = resolveChatId(chat);
|
|
106020
106086
|
try {
|
|
106021
|
-
const { data, hints: hints2 } = splitHints(await apiPost(
|
|
106087
|
+
const { data, hints: hints2 } = splitHints(await apiPost(path68, { chatId, ...body }));
|
|
106022
106088
|
writeJsonEnvelope({ ok: true, data, ...hints2.length > 0 ? { hints: hints2 } : {} });
|
|
106023
106089
|
return data;
|
|
106024
106090
|
} catch (err) {
|
|
@@ -107580,9 +107646,9 @@ Example: baker videos describe j571abc123 --description "Customer explaining how
|
|
|
107580
107646
|
|
|
107581
107647
|
// src/commands/videos/fetch.ts
|
|
107582
107648
|
import { constants } from "fs";
|
|
107583
|
-
import { copyFile as copyFile2, mkdir as
|
|
107649
|
+
import { copyFile as copyFile2, mkdir as mkdir22, mkdtemp as mkdtemp3, rm as rm9 } from "fs/promises";
|
|
107584
107650
|
import { tmpdir as tmpdir4 } from "os";
|
|
107585
|
-
import
|
|
107651
|
+
import path66 from "path";
|
|
107586
107652
|
import { defineCommand as defineCommand293 } from "citty";
|
|
107587
107653
|
|
|
107588
107654
|
// src/commands/videos/videoPlatforms.ts
|
|
@@ -107750,10 +107816,10 @@ var fetchCommand2 = defineCommand293({
|
|
|
107750
107816
|
}
|
|
107751
107817
|
});
|
|
107752
107818
|
async function fetchVideo(args8) {
|
|
107753
|
-
const outDir =
|
|
107754
|
-
const workDir = await mkdtemp3(
|
|
107819
|
+
const outDir = path66.resolve(args8.out);
|
|
107820
|
+
const workDir = await mkdtemp3(path66.join(tmpdir4(), "videos-fetch-"));
|
|
107755
107821
|
try {
|
|
107756
|
-
await
|
|
107822
|
+
await mkdir22(outDir, { recursive: true });
|
|
107757
107823
|
const vimeoCookie = captureVimeoCookie();
|
|
107758
107824
|
const probe = await probeYtDlp({ url: args8.url, vimeoCookie, cookieDir: workDir });
|
|
107759
107825
|
if (isAudioOnly(probe.info)) {
|
|
@@ -107796,7 +107862,7 @@ function fileStem(info) {
|
|
|
107796
107862
|
}
|
|
107797
107863
|
async function placeInto(outDir, source, stem) {
|
|
107798
107864
|
for (let n = 1; ; n++) {
|
|
107799
|
-
const target2 =
|
|
107865
|
+
const target2 = path66.join(outDir, `${stem}${n === 1 ? "" : `-${n}`}${path66.extname(source)}`);
|
|
107800
107866
|
try {
|
|
107801
107867
|
await copyFile2(source, target2, constants.COPYFILE_EXCL);
|
|
107802
107868
|
return target2;
|
|
@@ -107883,7 +107949,7 @@ var groupCommand2 = defineCommand295({
|
|
|
107883
107949
|
// src/commands/videos/ingest.ts
|
|
107884
107950
|
import { mkdtemp as mkdtemp4, rm as rm10, stat as stat13 } from "fs/promises";
|
|
107885
107951
|
import { tmpdir as tmpdir5 } from "os";
|
|
107886
|
-
import
|
|
107952
|
+
import path67 from "path";
|
|
107887
107953
|
import { defineCommand as defineCommand296 } from "citty";
|
|
107888
107954
|
|
|
107889
107955
|
// src/lib/streamUpload.ts
|
|
@@ -108123,7 +108189,7 @@ function ingestUrl(args8) {
|
|
|
108123
108189
|
}
|
|
108124
108190
|
async function downloadThenIngest(args8, country) {
|
|
108125
108191
|
const vimeoCookie = captureVimeoCookie();
|
|
108126
|
-
const workDir = await mkdtemp4(
|
|
108192
|
+
const workDir = await mkdtemp4(path67.join(tmpdir5(), "videos-ingest-"));
|
|
108127
108193
|
try {
|
|
108128
108194
|
const probe = await probeYtDlp({ url: args8.url, country, vimeoCookie, cookieDir: workDir });
|
|
108129
108195
|
if (isAudioOnly(probe.info)) {
|