@koda-sl/baker-cli 0.439.0-dev.33cf4ca99 → 0.443.0-dev.edb8d8767
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 +3 -3
- package/dist/cli.js +1109 -987
- 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
|
|
@@ -73631,11 +73631,18 @@ function autoFixStep(canvas, review, opts = {}) {
|
|
|
73631
73631
|
pictureFindings.filter((f) => !BRIEF.test(withoutSize(f.what))).flatMap((f) => correctionOf(f.what) ?? [])
|
|
73632
73632
|
);
|
|
73633
73633
|
if (changes.length === 0) return sizesOnly();
|
|
73634
|
+
const flagged = new Set(blocking.flatMap((f) => sizesOf(f.what)));
|
|
73635
|
+
const keep = relaid.map((r) => r.format).filter((format) => !flagged.has(format));
|
|
73636
|
+
const remade = every.filter((size) => !keep.includes(size));
|
|
73637
|
+
const kept = {
|
|
73638
|
+
...keep.length > 0 ? { keep } : {},
|
|
73639
|
+
...remade.length > 0 ? { extend: remade } : {}
|
|
73640
|
+
};
|
|
73634
73641
|
const asked = `Change only this: ${changes.join(" ")}`;
|
|
73635
73642
|
const again = onPicture.filter((w) => persisting.has(w)).map((w) => w.replace(CATEGORY, "").replace(/\.$/, ""));
|
|
73636
73643
|
const instruction = again.length > 0 ? `${asked} The last edit was already asked for this and Image 1 still shows it, so make it clearly: ${again.join("; ")}.` : asked;
|
|
73637
73644
|
if (repeatsFailedFix(opts.lastFix, asked) || repeatsFailedFix(opts.lastFix, instruction)) return null;
|
|
73638
|
-
return { kind: "fix", instruction, ...Object.keys(edits).length > 0 ? { relay: edits } : {}, ...
|
|
73645
|
+
return { kind: "fix", instruction, ...Object.keys(edits).length > 0 ? { relay: edits } : {}, ...kept };
|
|
73639
73646
|
}
|
|
73640
73647
|
var unique = (lines) => [...new Set(lines)];
|
|
73641
73648
|
function deliveredSizes(canvas) {
|
|
@@ -73936,19 +73943,19 @@ async function variantImage(canvasRoot, runId, sha2562, fetchRecord = fetchRunRe
|
|
|
73936
73943
|
}
|
|
73937
73944
|
|
|
73938
73945
|
// src/commands/canvas/static-run-review.ts
|
|
73939
|
-
import { readFile as
|
|
73946
|
+
import { readFile as readFile24 } from "fs/promises";
|
|
73940
73947
|
|
|
73941
73948
|
// src/commands/canvas/glance-check.ts
|
|
73942
73949
|
import sharp2 from "sharp";
|
|
73943
|
-
import { z as
|
|
73950
|
+
import { z as z66 } from "zod";
|
|
73944
73951
|
|
|
73945
73952
|
// src/commands/canvas/viewer-check.ts
|
|
73946
73953
|
import { createHash as createHash4 } from "crypto";
|
|
73947
|
-
import { mkdir as
|
|
73948
|
-
import
|
|
73954
|
+
import { mkdir as mkdir9, readdir as readdir12, readFile as readFile23, stat as stat4, writeFile as writeFile14 } from "fs/promises";
|
|
73955
|
+
import path29 from "path";
|
|
73949
73956
|
import { defineCommand as defineCommand138 } from "citty";
|
|
73950
73957
|
import { parse as parseYaml3 } from "yaml";
|
|
73951
|
-
import { z as
|
|
73958
|
+
import { z as z65 } from "zod";
|
|
73952
73959
|
|
|
73953
73960
|
// src/commands/canvas/unbilled-spend.ts
|
|
73954
73961
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -74082,17 +74089,76 @@ function castingCheckFailure(answer, slug) {
|
|
|
74082
74089
|
};
|
|
74083
74090
|
}
|
|
74084
74091
|
|
|
74092
|
+
// src/commands/canvas/turn-renders.ts
|
|
74093
|
+
import { mkdir as mkdir8, readFile as readFile22, writeFile as writeFile13 } from "fs/promises";
|
|
74094
|
+
import path28 from "path";
|
|
74095
|
+
import { z as z64 } from "zod";
|
|
74096
|
+
var RUNS_PER_TURN = 2;
|
|
74097
|
+
var VIEWER_FAILS_PER_TURN = 2;
|
|
74098
|
+
var LedgerSchema = z64.object({
|
|
74099
|
+
turnId: z64.string(),
|
|
74100
|
+
creatives: z64.record(
|
|
74101
|
+
z64.string(),
|
|
74102
|
+
z64.object({ runs: z64.number().default(0), verdict: z64.string().optional(), viewerFails: z64.number().optional() })
|
|
74103
|
+
)
|
|
74104
|
+
});
|
|
74105
|
+
function turnRenderRefusal(ledger, turnId, slug) {
|
|
74106
|
+
const entry = turnId !== void 0 && ledger?.turnId === turnId ? ledger.creatives[slug] : void 0;
|
|
74107
|
+
if (!entry) return null;
|
|
74108
|
+
if (entry.verdict === "ready" || entry.verdict === "ready_with_notes") {
|
|
74109
|
+
return {
|
|
74110
|
+
code: "CONFLICT",
|
|
74111
|
+
message: `This ad is already ready in this turn (${entry.verdict}): its notes are not defects.`,
|
|
74112
|
+
fix: "Show it now. A change the person asks for comes with their next message."
|
|
74113
|
+
};
|
|
74114
|
+
}
|
|
74115
|
+
if (entry.runs >= RUNS_PER_TURN) {
|
|
74116
|
+
return {
|
|
74117
|
+
code: "CONFLICT",
|
|
74118
|
+
message: `This ad was rendered twice in this turn, its automatic corrections included, and is still not ready.`,
|
|
74119
|
+
fix: "Show the best version and say in one plain sentence what is left, in the person's words; they decide what changes next."
|
|
74120
|
+
};
|
|
74121
|
+
}
|
|
74122
|
+
return null;
|
|
74123
|
+
}
|
|
74124
|
+
function recordTurnRender(ledger, turnId, slug, verdict2) {
|
|
74125
|
+
const creatives = ledger?.turnId === turnId ? ledger.creatives : {};
|
|
74126
|
+
return { turnId, creatives: { ...creatives, [slug]: { ...creatives[slug], runs: (creatives[slug]?.runs ?? 0) + 1, verdict: verdict2 } } };
|
|
74127
|
+
}
|
|
74128
|
+
function recordViewerFail(ledger, turnId, slug) {
|
|
74129
|
+
const creatives = ledger?.turnId === turnId ? ledger.creatives : {};
|
|
74130
|
+
const entry = creatives[slug] ?? { runs: 0 };
|
|
74131
|
+
return { turnId, creatives: { ...creatives, [slug]: { ...entry, viewerFails: (entry.viewerFails ?? 0) + 1 } } };
|
|
74132
|
+
}
|
|
74133
|
+
function viewerChecksSpent(ledger, turnId, slug) {
|
|
74134
|
+
const entry = turnId !== void 0 && ledger?.turnId === turnId ? ledger.creatives[slug] : void 0;
|
|
74135
|
+
return (entry?.viewerFails ?? 0) >= VIEWER_FAILS_PER_TURN;
|
|
74136
|
+
}
|
|
74137
|
+
var ledgerPath = (cacheDir) => path28.join(cacheDir, "turn-renders.json");
|
|
74138
|
+
async function readTurnRenders(cacheDir) {
|
|
74139
|
+
try {
|
|
74140
|
+
const parsed = LedgerSchema.safeParse(JSON.parse(await readFile22(ledgerPath(cacheDir), "utf8")));
|
|
74141
|
+
return parsed.success ? parsed.data : void 0;
|
|
74142
|
+
} catch {
|
|
74143
|
+
return void 0;
|
|
74144
|
+
}
|
|
74145
|
+
}
|
|
74146
|
+
async function writeTurnRenders(cacheDir, ledger) {
|
|
74147
|
+
await mkdir8(cacheDir, { recursive: true });
|
|
74148
|
+
await writeFile13(ledgerPath(cacheDir), JSON.stringify(ledger), "utf8");
|
|
74149
|
+
}
|
|
74150
|
+
|
|
74085
74151
|
// src/commands/canvas/viewer-check.ts
|
|
74086
74152
|
var VIEWER_QUESTIONS = ["offer", "for_whom", "next_step"];
|
|
74087
74153
|
var VIEWER_CLARITY = ["clear", "guess", "unclear"];
|
|
74088
|
-
var ViewerReply =
|
|
74089
|
-
var ViewerAnswerSchema =
|
|
74154
|
+
var ViewerReply = z65.object({ answer: z65.string().default(""), clarity: z65.enum(VIEWER_CLARITY) });
|
|
74155
|
+
var ViewerAnswerSchema = z65.object({
|
|
74090
74156
|
offer: ViewerReply,
|
|
74091
74157
|
for_whom: ViewerReply,
|
|
74092
74158
|
next_step: ViewerReply,
|
|
74093
74159
|
/** The words that talk to the viewer as if they had already done something with the brand; "" when none. */
|
|
74094
|
-
presumes:
|
|
74095
|
-
missing:
|
|
74160
|
+
presumes: z65.string().default(""),
|
|
74161
|
+
missing: z65.string().default("")
|
|
74096
74162
|
});
|
|
74097
74163
|
var VIEWER_REQUIRED_CLARITY = {
|
|
74098
74164
|
offer: "clear",
|
|
@@ -74199,14 +74265,14 @@ function viewerCheckCacheKey(input, model) {
|
|
|
74199
74265
|
}
|
|
74200
74266
|
async function cachedAnswer(file) {
|
|
74201
74267
|
try {
|
|
74202
|
-
const parsed = ViewerAnswerSchema.safeParse(JSON.parse(await
|
|
74268
|
+
const parsed = ViewerAnswerSchema.safeParse(JSON.parse(await readFile23(file, "utf8")));
|
|
74203
74269
|
return parsed.success ? parsed.data : null;
|
|
74204
74270
|
} catch {
|
|
74205
74271
|
return null;
|
|
74206
74272
|
}
|
|
74207
74273
|
}
|
|
74208
74274
|
async function checkViewer(input, opts) {
|
|
74209
|
-
const file =
|
|
74275
|
+
const file = path29.join(opts.cacheDir, "viewer-check", `${viewerCheckCacheKey(input, opts.model)}.json`);
|
|
74210
74276
|
let answer = await cachedAnswer(file);
|
|
74211
74277
|
const cached = answer !== null;
|
|
74212
74278
|
const prompt = buildViewerPrompt(input);
|
|
@@ -74216,8 +74282,8 @@ async function checkViewer(input, opts) {
|
|
|
74216
74282
|
}
|
|
74217
74283
|
if (answer === null) return { status: "unavailable", input };
|
|
74218
74284
|
if (!cached) {
|
|
74219
|
-
await
|
|
74220
|
-
await
|
|
74285
|
+
await mkdir9(path29.dirname(file), { recursive: true });
|
|
74286
|
+
await writeFile14(file, `${JSON.stringify(answer, null, 2)}
|
|
74221
74287
|
`, "utf8");
|
|
74222
74288
|
}
|
|
74223
74289
|
const verdict2 = judgeViewerAnswer(answer, input.intended);
|
|
@@ -74248,7 +74314,10 @@ function viewerCheckFailure(outcome2, slug) {
|
|
|
74248
74314
|
const who = describeViewer(viewer);
|
|
74249
74315
|
const missing = outcome2.answer.missing.trim();
|
|
74250
74316
|
const unsaid = outcome2.verdict.missed.flatMap((m) => m.question === "presumes" ? [] : [QUESTION_WORDS[m.question]]);
|
|
74317
|
+
const offer = outcome2.input.intended.offer?.trim();
|
|
74318
|
+
const rewrite = offer && outcome2.verdict.missed.some((m) => m.question === "offer") ? [`Do this: make the biggest words say "${offer}", plainly, in the ad's language; keep the rest.`] : [];
|
|
74251
74319
|
const fix = [
|
|
74320
|
+
...rewrite,
|
|
74252
74321
|
...unsaid.length > 0 ? [`Seen by ${who}, the words and picture alone do not say ${unsaid.join(" or ")}.`] : [`Seen by ${who}:`],
|
|
74253
74322
|
...outcome2.verdict.missed.map(missLine),
|
|
74254
74323
|
...missing ? [`They needed: ${missing}`] : [],
|
|
@@ -74267,17 +74336,17 @@ function viewerCheckFailure(outcome2, slug) {
|
|
|
74267
74336
|
}
|
|
74268
74337
|
};
|
|
74269
74338
|
}
|
|
74270
|
-
var RecordedViewer =
|
|
74271
|
-
temperature:
|
|
74272
|
-
knows:
|
|
74273
|
-
placements:
|
|
74274
|
-
source:
|
|
74275
|
-
reasons:
|
|
74276
|
-
market:
|
|
74277
|
-
country:
|
|
74278
|
-
language:
|
|
74279
|
-
currency:
|
|
74280
|
-
address:
|
|
74339
|
+
var RecordedViewer = z65.object({
|
|
74340
|
+
temperature: z65.enum(VIEWER_TEMPERATURE_VALUES),
|
|
74341
|
+
knows: z65.array(z65.enum(VIEWER_KNOWS_VALUES)).default([]),
|
|
74342
|
+
placements: z65.array(z65.string().min(1)).default(["automatic"]),
|
|
74343
|
+
source: z65.enum(VIEWER_SOURCE_VALUES),
|
|
74344
|
+
reasons: z65.array(z65.string()).default([]),
|
|
74345
|
+
market: z65.object({
|
|
74346
|
+
country: z65.string().min(2),
|
|
74347
|
+
language: z65.string().optional(),
|
|
74348
|
+
currency: z65.string().optional(),
|
|
74349
|
+
address: z65.string().optional()
|
|
74281
74350
|
}).optional()
|
|
74282
74351
|
});
|
|
74283
74352
|
var nameKey = (text3) => wordsOf(text3).join(" ");
|
|
@@ -74336,12 +74405,12 @@ async function resolveViewerAlone(sources, lookups) {
|
|
|
74336
74405
|
notes
|
|
74337
74406
|
};
|
|
74338
74407
|
}
|
|
74339
|
-
var Frontmatter =
|
|
74340
|
-
viewer:
|
|
74341
|
-
targetAdSet:
|
|
74342
|
-
persona:
|
|
74343
|
-
awareness:
|
|
74344
|
-
angle:
|
|
74408
|
+
var Frontmatter = z65.object({
|
|
74409
|
+
viewer: z65.unknown().optional(),
|
|
74410
|
+
targetAdSet: z65.union([z65.string(), z65.number()]).transform(String).optional(),
|
|
74411
|
+
persona: z65.string().optional(),
|
|
74412
|
+
awareness: z65.enum(AWARENESS_VALUES).optional().catch(void 0),
|
|
74413
|
+
angle: z65.string().optional()
|
|
74345
74414
|
});
|
|
74346
74415
|
function frontmatterOf2(raw) {
|
|
74347
74416
|
const block2 = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1];
|
|
@@ -74354,8 +74423,8 @@ function frontmatterOf2(raw) {
|
|
|
74354
74423
|
}
|
|
74355
74424
|
}
|
|
74356
74425
|
async function readCreativeForViewerCheck(creativeDir) {
|
|
74357
|
-
const spec = readAdSpec(JSON.parse(await
|
|
74358
|
-
const definition = frontmatterOf2(await
|
|
74426
|
+
const spec = readAdSpec(JSON.parse(await readFile23(path29.join(creativeDir, "prompt.json"), "utf8")));
|
|
74427
|
+
const definition = frontmatterOf2(await readFile23(path29.join(creativeDir, "_definition.md"), "utf8").catch(() => ""));
|
|
74359
74428
|
const offer = spec.message?.trim() || definition.angle?.trim();
|
|
74360
74429
|
const persona = spec.viewer?.who.trim() || definition.persona?.trim();
|
|
74361
74430
|
const brand = spec.brand?.name?.trim();
|
|
@@ -74384,7 +74453,7 @@ function castPeople(spec) {
|
|
|
74384
74453
|
return people.length > 0 ? { people } : {};
|
|
74385
74454
|
}
|
|
74386
74455
|
async function readPersonas(workspaceRoot) {
|
|
74387
|
-
const dir =
|
|
74456
|
+
const dir = path29.join(workspaceRoot, "src", "content", "personas");
|
|
74388
74457
|
let files;
|
|
74389
74458
|
try {
|
|
74390
74459
|
files = (await readdir12(dir, { recursive: true })).filter((f) => f.endsWith(".md"));
|
|
@@ -74393,7 +74462,7 @@ async function readPersonas(workspaceRoot) {
|
|
|
74393
74462
|
}
|
|
74394
74463
|
return await Promise.all(
|
|
74395
74464
|
files.map(async (file) => {
|
|
74396
|
-
const raw = await
|
|
74465
|
+
const raw = await readFile23(path29.join(dir, file), "utf8");
|
|
74397
74466
|
const block2 = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1];
|
|
74398
74467
|
let segment;
|
|
74399
74468
|
try {
|
|
@@ -74403,7 +74472,7 @@ async function readPersonas(workspaceRoot) {
|
|
|
74403
74472
|
}
|
|
74404
74473
|
const title = raw.match(/^#\s+(.+)$/m)?.[1]?.trim();
|
|
74405
74474
|
return {
|
|
74406
|
-
slug:
|
|
74475
|
+
slug: path29.basename(file, ".md"),
|
|
74407
74476
|
...title ? { title } : {},
|
|
74408
74477
|
...typeof segment === "string" && segment.trim() ? { segment: segment.trim() } : {}
|
|
74409
74478
|
};
|
|
@@ -74442,8 +74511,8 @@ function liveViewerLookups(workspaceRoot) {
|
|
|
74442
74511
|
return {
|
|
74443
74512
|
adSetViewer: metaAdSetViewer,
|
|
74444
74513
|
personas: () => readPersonas(workspaceRoot),
|
|
74445
|
-
companyMarket: async () => marketFromCompanyDoc(await
|
|
74446
|
-
company: async () => (await
|
|
74514
|
+
companyMarket: async () => marketFromCompanyDoc(await readFile23(path29.join(workspaceRoot, "COMPANY.md"), "utf8").catch(() => null)),
|
|
74515
|
+
company: async () => (await readFile23(path29.join(workspaceRoot, "COMPANY.md"), "utf8").catch(() => "")).slice(0, 800)
|
|
74447
74516
|
};
|
|
74448
74517
|
}
|
|
74449
74518
|
function gatewayViewerModel(model, onSpend) {
|
|
@@ -74473,16 +74542,7 @@ async function viewerCheckBeforeRender(opts) {
|
|
|
74473
74542
|
const who = describeViewer(resolved.viewer);
|
|
74474
74543
|
const lines = resolved.notes.map((note) => `[viewer] ${note}`);
|
|
74475
74544
|
if (opts.skip) return { viewer: resolved.viewer, lines: [...lines, `[viewer] not asked: ${SKIP_WORDS[opts.skip]}.`] };
|
|
74476
|
-
const
|
|
74477
|
-
if (outcome2.status === "unavailable") {
|
|
74478
|
-
lines.push(
|
|
74479
|
-
`[warn] viewer_check_unavailable: the viewer model did not answer, so this ad renders without the viewer check. Read the copy as ${who} before showing it, or run \`baker canvas viewer-check ${opts.slug}\` again.`
|
|
74480
|
-
);
|
|
74481
|
-
return { viewer: resolved.viewer, lines };
|
|
74482
|
-
}
|
|
74483
|
-
if (outcome2.status === "failed") return { viewer: resolved.viewer, lines, failure: viewerCheckFailure(outcome2, opts.slug) };
|
|
74484
|
-
lines.push(`[viewer] ${who} understood it: ${outcome2.answer.offer.answer}${outcome2.cached ? " (cached)" : ""}.`);
|
|
74485
|
-
const casting = await castingCheck(
|
|
74545
|
+
const casting = (async () => castingCheck(
|
|
74486
74546
|
{
|
|
74487
74547
|
people: creative.ad.people ?? [],
|
|
74488
74548
|
company: await opts.lookups.company?.().catch(() => "") ?? "",
|
|
@@ -74490,10 +74550,26 @@ async function viewerCheckBeforeRender(opts) {
|
|
|
74490
74550
|
...creative.ad.intended.persona ? { persona: creative.ad.intended.persona } : {}
|
|
74491
74551
|
},
|
|
74492
74552
|
{ cacheDir: opts.cacheDir, model: opts.model, ask: opts.ask }
|
|
74493
|
-
);
|
|
74494
|
-
|
|
74495
|
-
if (
|
|
74496
|
-
|
|
74553
|
+
))().catch(() => ({ status: "unavailable" }));
|
|
74554
|
+
const outcome2 = await checkViewer({ ...creative.ad, viewer: resolved.viewer }, opts);
|
|
74555
|
+
if (outcome2.status === "unavailable") {
|
|
74556
|
+
lines.push(
|
|
74557
|
+
`[warn] viewer_check_unavailable: the viewer model did not answer, so this ad renders without the viewer check. Read the copy as ${who} before showing it, or run \`baker canvas viewer-check ${opts.slug}\` again.`
|
|
74558
|
+
);
|
|
74559
|
+
} else if (outcome2.status === "failed" && !opts.spent) {
|
|
74560
|
+
return { viewer: resolved.viewer, lines, failure: viewerCheckFailure(outcome2, opts.slug) };
|
|
74561
|
+
} else if (outcome2.status === "failed") {
|
|
74562
|
+
lines.push(
|
|
74563
|
+
`[viewer] this ad failed the viewer check twice in this turn, so it renders as written. Tell the person what ${who} missed: ${outcome2.verdict.missed.map(missLine).join(" ")}`
|
|
74564
|
+
);
|
|
74565
|
+
} else {
|
|
74566
|
+
lines.push(`[viewer] ${who} understood it: ${outcome2.answer.offer.answer}${outcome2.cached ? " (cached)" : ""}.`);
|
|
74567
|
+
}
|
|
74568
|
+
if (outcome2.status === "unavailable") return { viewer: resolved.viewer, lines };
|
|
74569
|
+
const cast = await casting;
|
|
74570
|
+
if (cast.status === "failed") return { viewer: resolved.viewer, lines, failure: castingCheckFailure(cast.answer, opts.slug) };
|
|
74571
|
+
if (cast.status === "passed") lines.push(`[casting] the people cast pass as real customers${cast.cached ? " (cached)" : ""}.`);
|
|
74572
|
+
if (cast.status === "unavailable") {
|
|
74497
74573
|
lines.push("[warn] casting_check_unavailable: the model did not answer; check yourself that the people cast look like this company's real customers.");
|
|
74498
74574
|
}
|
|
74499
74575
|
return { viewer: resolved.viewer, lines };
|
|
@@ -74514,11 +74590,11 @@ var ARGS5 = {
|
|
|
74514
74590
|
};
|
|
74515
74591
|
registerSchema({ command: "canvas.viewer-check", description: DESCRIPTION2, args: ARGS5 });
|
|
74516
74592
|
async function creativeDirOf(arg, workspaceRoot) {
|
|
74517
|
-
const direct =
|
|
74593
|
+
const direct = path29.resolve(arg);
|
|
74518
74594
|
try {
|
|
74519
|
-
return (await stat4(direct)).isDirectory() ? direct :
|
|
74595
|
+
return (await stat4(direct)).isDirectory() ? direct : path29.dirname(direct);
|
|
74520
74596
|
} catch {
|
|
74521
|
-
return
|
|
74597
|
+
return path29.join(workspaceRoot, "src", "creatives", arg);
|
|
74522
74598
|
}
|
|
74523
74599
|
}
|
|
74524
74600
|
var viewerCheckCommand = defineCommand138({
|
|
@@ -74528,7 +74604,7 @@ var viewerCheckCommand = defineCommand138({
|
|
|
74528
74604
|
const cwd = process.cwd();
|
|
74529
74605
|
const workspaceRoot = findWorkspaceRoot(cwd) ?? cwd;
|
|
74530
74606
|
const creativeDir = await creativeDirOf(String(args8.slug), workspaceRoot);
|
|
74531
|
-
const slug =
|
|
74607
|
+
const slug = path29.basename(creativeDir);
|
|
74532
74608
|
let creative;
|
|
74533
74609
|
try {
|
|
74534
74610
|
creative = await readCreativeForViewerCheck(creativeDir);
|
|
@@ -74545,7 +74621,7 @@ var viewerCheckCommand = defineCommand138({
|
|
|
74545
74621
|
}
|
|
74546
74622
|
const resolved = await resolveViewer(creative.sources, liveViewerLookups(workspaceRoot));
|
|
74547
74623
|
const input = { ...creative.ad, viewer: resolved.viewer };
|
|
74548
|
-
const cacheDir =
|
|
74624
|
+
const cacheDir = path29.join(workspaceRoot, "canvas", ".cache");
|
|
74549
74625
|
const spent = [];
|
|
74550
74626
|
const outcome2 = await checkViewer(input, {
|
|
74551
74627
|
cacheDir,
|
|
@@ -74562,6 +74638,19 @@ var viewerCheckCommand = defineCommand138({
|
|
|
74562
74638
|
...resolved.notes.length > 0 ? { notes: resolved.notes } : {}
|
|
74563
74639
|
} : {};
|
|
74564
74640
|
if (outcome2.status === "failed") {
|
|
74641
|
+
const turnId = optionalTurnId();
|
|
74642
|
+
const ledger = await readTurnRenders(cacheDir);
|
|
74643
|
+
if (viewerChecksSpent(ledger, turnId, slug)) {
|
|
74644
|
+
writeJson({
|
|
74645
|
+
ok: true,
|
|
74646
|
+
data: { checked: true, understood: false, viewer: who, missed: outcome2.verdict.missed, ...detail },
|
|
74647
|
+
hints: [
|
|
74648
|
+
`This ad failed the viewer check twice in this turn: render it as written (baker canvas run --slug ${slug}) and tell the person what ${who} missed: ${outcome2.verdict.missed.map(missLine).join(" ")}`
|
|
74649
|
+
]
|
|
74650
|
+
});
|
|
74651
|
+
return;
|
|
74652
|
+
}
|
|
74653
|
+
if (turnId) await writeTurnRenders(cacheDir, recordViewerFail(ledger, turnId, slug));
|
|
74565
74654
|
const failure = viewerCheckFailure(outcome2, slug);
|
|
74566
74655
|
writeJson({ ...failure, error: { ...failure.error, ...detail } });
|
|
74567
74656
|
process.exit(2);
|
|
@@ -74604,9 +74693,9 @@ var GLANCE_REQUIRED = {
|
|
|
74604
74693
|
var GLANCE_WIDTH = 400;
|
|
74605
74694
|
var GlanceAnswerSchema = ViewerAnswerSchema.extend({
|
|
74606
74695
|
/** What kind of product or service they take it for (D4). */
|
|
74607
|
-
category:
|
|
74608
|
-
stopped:
|
|
74609
|
-
me:
|
|
74696
|
+
category: z66.object({ answer: z66.string().default(""), clarity: z66.enum(["clear", "guess", "unclear"]).default("unclear") }).default({ answer: "", clarity: "unclear" }),
|
|
74697
|
+
stopped: z66.string().default(""),
|
|
74698
|
+
me: z66.enum(["yes", "no", "unsure"]).default("unsure")
|
|
74610
74699
|
});
|
|
74611
74700
|
function wherePhrase2(placements) {
|
|
74612
74701
|
const fullScreen = placements.length === 0 || placements.some((p) => /automatic|stor|reel/i.test(p));
|
|
@@ -74776,7 +74865,7 @@ function runCostHint(stats, kind) {
|
|
|
74776
74865
|
async function textOf(ref2) {
|
|
74777
74866
|
const r = ref2;
|
|
74778
74867
|
try {
|
|
74779
|
-
if (typeof r?.path === "string") return await
|
|
74868
|
+
if (typeof r?.path === "string") return await readFile24(r.path, "utf8");
|
|
74780
74869
|
if (typeof r?.url === "string") {
|
|
74781
74870
|
const res = await fetch(r.url);
|
|
74782
74871
|
return res.ok ? await res.text() : null;
|
|
@@ -74843,7 +74932,7 @@ async function reviewStaticRun(opts) {
|
|
|
74843
74932
|
let native;
|
|
74844
74933
|
let palette = [];
|
|
74845
74934
|
try {
|
|
74846
|
-
const spec = readAdSpec(JSON.parse(await
|
|
74935
|
+
const spec = readAdSpec(JSON.parse(await readFile24(targets.promptPath, "utf8")));
|
|
74847
74936
|
copy = spec.copy.map((l) => l.text);
|
|
74848
74937
|
lines = spec.copy.map((l) => ({ text: l.text, lettering: l.lettering, ...l.role ? { role: l.role } : {} }));
|
|
74849
74938
|
if (spec.native) {
|
|
@@ -74877,8 +74966,10 @@ async function reviewStaticRun(opts) {
|
|
|
74877
74966
|
() => null
|
|
74878
74967
|
);
|
|
74879
74968
|
const comparing = opts.previous ? nearIdenticalFindings(targets.images, opts.previous).catch(() => []) : Promise.resolve([]);
|
|
74969
|
+
const untouched = opts.settled ? await samePictureSizes(targets.images, opts.settled.images) : [];
|
|
74970
|
+
const changed = targets.images.filter((i) => !untouched.includes(i.format));
|
|
74880
74971
|
const review = await reviewRenderedStatic({
|
|
74881
|
-
images: targets.images,
|
|
74972
|
+
images: changed.length > 0 ? changed : targets.images,
|
|
74882
74973
|
copy,
|
|
74883
74974
|
lines,
|
|
74884
74975
|
palette,
|
|
@@ -74901,7 +74992,7 @@ async function reviewStaticRun(opts) {
|
|
|
74901
74992
|
const glance = review === null ? null : glanced;
|
|
74902
74993
|
const unchanged = review === null ? [] : compared;
|
|
74903
74994
|
const fresh = review === null ? null : [...review, ...glance ?? [], ...unchanged];
|
|
74904
|
-
const reviewed = fresh === null || !opts.settled ? fresh : carryUnchanged(fresh, opts.settled.review,
|
|
74995
|
+
const reviewed = fresh === null || !opts.settled ? fresh : carryUnchanged(fresh, opts.settled.review, untouched);
|
|
74905
74996
|
const verdict2 = staticVerdict(reviewed);
|
|
74906
74997
|
const hints2 = verdictHints(verdict2);
|
|
74907
74998
|
const sizeHints = verdict2 === "needs_fixes" ? sizeFixHints(opts.parsed, reviewed) : [];
|
|
@@ -75024,15 +75115,15 @@ function variantHints(judge, prefilter) {
|
|
|
75024
75115
|
}
|
|
75025
75116
|
|
|
75026
75117
|
// src/commands/canvas/static-spec-repair.ts
|
|
75027
|
-
import { readFile as
|
|
75118
|
+
import { readFile as readFile25, writeFile as writeFile15 } from "fs/promises";
|
|
75028
75119
|
import sharp3 from "sharp";
|
|
75029
|
-
import { z as
|
|
75030
|
-
var Answer =
|
|
75031
|
-
changed:
|
|
75032
|
-
set:
|
|
75120
|
+
import { z as z67 } from "zod";
|
|
75121
|
+
var Answer = z67.object({
|
|
75122
|
+
changed: z67.string().min(1),
|
|
75123
|
+
set: z67.array(z67.object({ path: z67.string().min(1), value: z67.unknown() }))
|
|
75033
75124
|
});
|
|
75034
|
-
function withValueAt(root,
|
|
75035
|
-
const [key, ...rest] =
|
|
75125
|
+
function withValueAt(root, path68, value2) {
|
|
75126
|
+
const [key, ...rest] = path68;
|
|
75036
75127
|
if (key === void 0) return value2;
|
|
75037
75128
|
if (Array.isArray(root)) {
|
|
75038
75129
|
const at = Number(key);
|
|
@@ -75050,7 +75141,7 @@ function printedWords(spec) {
|
|
|
75050
75141
|
function acceptRepair(before, answer) {
|
|
75051
75142
|
const parsed = Answer.safeParse(answer);
|
|
75052
75143
|
if (!parsed.success || !isAdSpec(before)) return null;
|
|
75053
|
-
const spec = parsed.data.set.reduce((at, { path:
|
|
75144
|
+
const spec = parsed.data.set.reduce((at, { path: path68, value: value2 }) => withValueAt(at, path68.split("."), value2), before);
|
|
75054
75145
|
if (!isAdSpec(spec)) return null;
|
|
75055
75146
|
const was = AdSpecSchema.safeParse(before);
|
|
75056
75147
|
const now = AdSpecSchema.safeParse(spec);
|
|
@@ -75083,7 +75174,7 @@ function buildSpecRepairPrompt(spec, findings, schema) {
|
|
|
75083
75174
|
var REPAIR_TIMEOUT_MS = 15e4;
|
|
75084
75175
|
var SHOWN_WIDTH = 768;
|
|
75085
75176
|
async function repairSpec(opts) {
|
|
75086
|
-
const text3 = await
|
|
75177
|
+
const text3 = await readFile25(opts.promptPath, "utf8").catch(() => null);
|
|
75087
75178
|
if (text3 === null) return null;
|
|
75088
75179
|
let before;
|
|
75089
75180
|
try {
|
|
@@ -75095,7 +75186,7 @@ async function repairSpec(opts) {
|
|
|
75095
75186
|
const prompt = buildSpecRepairPrompt(text3, opts.findings, adSpecJsonSchema());
|
|
75096
75187
|
const repair = acceptRepair(before, await askAboutImages(RENDER_REVIEW_MODEL, shown ? [shown] : [], prompt, opts.onSpend, REPAIR_TIMEOUT_MS));
|
|
75097
75188
|
if (!repair) return null;
|
|
75098
|
-
await
|
|
75189
|
+
await writeFile15(opts.promptPath, `${JSON.stringify(repair.spec, null, 2)}
|
|
75099
75190
|
`, "utf8");
|
|
75100
75191
|
return repair.changed;
|
|
75101
75192
|
}
|
|
@@ -75222,7 +75313,7 @@ function failPreflight3(code, message, fix) {
|
|
|
75222
75313
|
process.exit(2);
|
|
75223
75314
|
}
|
|
75224
75315
|
async function canvasFileFor(opts) {
|
|
75225
|
-
if (opts.file) return { canvasPath:
|
|
75316
|
+
if (opts.file) return { canvasPath: path30.resolve(opts.file) };
|
|
75226
75317
|
if (!opts.slug) failPreflight3("missing_canvas", "Pass the canvas file, or --slug <slug> to run a creative by name.");
|
|
75227
75318
|
const cwd = process.cwd();
|
|
75228
75319
|
try {
|
|
@@ -75243,15 +75334,27 @@ async function executeCanvasRun(opts) {
|
|
|
75243
75334
|
const canvasRoot = canvasOutputsRoot(filePath);
|
|
75244
75335
|
const creativeSlug = creativeSlugFromCanvasPath(filePath) ?? void 0;
|
|
75245
75336
|
if (creativeSlug) await clearLegacyDirtyMarker(canvasRoot, creativeSlug);
|
|
75337
|
+
const turnId = optionalTurnId();
|
|
75338
|
+
const turnCache = opts.cacheDir ?? path30.join(canvasRoot, ".cache");
|
|
75339
|
+
if (creativeSlug && (opts.autoFixRound ?? 0) === 0) {
|
|
75340
|
+
const refusal = turnRenderRefusal(await readTurnRenders(turnCache), turnId, creativeSlug);
|
|
75341
|
+
if (refusal) {
|
|
75342
|
+
process.stderr.write(
|
|
75343
|
+
`${JSON.stringify({ ok: false, error: { code: refusal.code, message: refusal.message, fix: refusal.fix } }, null, 2)}
|
|
75344
|
+
`
|
|
75345
|
+
);
|
|
75346
|
+
process.exit(2);
|
|
75347
|
+
}
|
|
75348
|
+
}
|
|
75246
75349
|
await wireProofBadges(
|
|
75247
|
-
|
|
75350
|
+
path30.join(path30.dirname(filePath), "prompt.json"),
|
|
75248
75351
|
fetchBrandLogo,
|
|
75249
75352
|
(line) => process.stderr.write(`${line}
|
|
75250
75353
|
`)
|
|
75251
75354
|
);
|
|
75252
75355
|
if ((opts.autoFixRound ?? 0) === 0) {
|
|
75253
75356
|
await wireNamedPeople(
|
|
75254
|
-
|
|
75357
|
+
path30.join(path30.dirname(filePath), "prompt.json"),
|
|
75255
75358
|
avatarOfNamedPerson,
|
|
75256
75359
|
(line) => process.stderr.write(`${line}
|
|
75257
75360
|
`)
|
|
@@ -75345,8 +75448,8 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75345
75448
|
});
|
|
75346
75449
|
const last = Object.fromEntries(
|
|
75347
75450
|
nodesWithRole(parsed, STATIC_ADAPT_FORMAT_ROLE).flatMap((n) => {
|
|
75348
|
-
const
|
|
75349
|
-
return
|
|
75451
|
+
const path68 = delivered.find((d) => d.format === formatOfAdaptId(n.id))?.path;
|
|
75452
|
+
return path68 ? [[n.id, path68]] : [];
|
|
75350
75453
|
})
|
|
75351
75454
|
);
|
|
75352
75455
|
parsed = withSizesFollowingFix(parsed, {
|
|
@@ -75354,6 +75457,12 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75354
75457
|
change: instruction.replace(/^Change only this: /, ""),
|
|
75355
75458
|
...opts.relayAlso ? { also: opts.relayAlso } : {}
|
|
75356
75459
|
});
|
|
75460
|
+
const kept = delivered.filter((d) => opts.keepSizes?.includes(d.format));
|
|
75461
|
+
if (kept.length > 0) {
|
|
75462
|
+
parsed = withDeliveredSizes(parsed, Object.fromEntries(kept.map((d) => [d.format, d.path])));
|
|
75463
|
+
process.stdout.write(`[fix] keeping ${kept.map((d) => d.format).join(", ")} as it was: nothing blocking on it
|
|
75464
|
+
`);
|
|
75465
|
+
}
|
|
75357
75466
|
if (!editInstruction) process.stdout.write(`[fix] correcting the version of run ${from}: ${instruction}
|
|
75358
75467
|
`);
|
|
75359
75468
|
}
|
|
@@ -75443,7 +75552,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75443
75552
|
process.stdout.write(`[extend] placing ${opts.extendSizes.join(", ")} clear of what covers or cuts it, its margins painted
|
|
75444
75553
|
`);
|
|
75445
75554
|
}
|
|
75446
|
-
const cacheDir = opts.cacheDir ??
|
|
75555
|
+
const cacheDir = opts.cacheDir ?? path30.join(canvasRoot, ".cache");
|
|
75447
75556
|
const engine = createEngineFromEnv({
|
|
75448
75557
|
cacheDir,
|
|
75449
75558
|
outputsDir: opts.outputsDir ?? canvasRoot,
|
|
@@ -75466,7 +75575,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75466
75575
|
const canvasSha = sha256Hex(Buffer.from(canvasText));
|
|
75467
75576
|
const sourceSha = prepared.sourceSha;
|
|
75468
75577
|
const client = opts.record === false ? null : buildBackendClient();
|
|
75469
|
-
const outputsDir = opts.outputsDir ?
|
|
75578
|
+
const outputsDir = opts.outputsDir ? path30.resolve(opts.outputsDir) : canvasRoot;
|
|
75470
75579
|
const { runId, resumed, source, sameSource, concurrentRunId, rendering } = await resolveRunId({
|
|
75471
75580
|
explicitRunId: opts.runId,
|
|
75472
75581
|
adoptedRunId: adoptRunId,
|
|
@@ -75496,7 +75605,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75496
75605
|
}
|
|
75497
75606
|
const viewerUsage = [];
|
|
75498
75607
|
const viewerCheck = creativeSlug && isStaticAdCanvas(parsed) ? await viewerCheckBeforeRender({
|
|
75499
|
-
creativeDir:
|
|
75608
|
+
creativeDir: path30.dirname(filePath),
|
|
75500
75609
|
cacheDir,
|
|
75501
75610
|
slug: creativeSlug,
|
|
75502
75611
|
skip: viewerCheckSkip({
|
|
@@ -75507,7 +75616,8 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75507
75616
|
resumed: resumed && sameSource === true,
|
|
75508
75617
|
asked: opts.skipViewerCheck === true
|
|
75509
75618
|
}),
|
|
75510
|
-
|
|
75619
|
+
spent: viewerChecksSpent(await readTurnRenders(turnCache), turnId, creativeSlug),
|
|
75620
|
+
lookups: liveViewerLookups(path30.dirname(canvasRoot)),
|
|
75511
75621
|
model: VIEWER_CHECK_MODEL,
|
|
75512
75622
|
ask: gatewayViewerModel(VIEWER_CHECK_MODEL, (usage) => viewerUsage.push(usage))
|
|
75513
75623
|
}) : null;
|
|
@@ -75515,6 +75625,9 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75515
75625
|
`);
|
|
75516
75626
|
await holdUnbilledSpend(cacheDir, viewerUsage);
|
|
75517
75627
|
if (viewerCheck?.failure) {
|
|
75628
|
+
if (creativeSlug && turnId && viewerCheck.failure.error.code === "viewer_check_failed") {
|
|
75629
|
+
await writeTurnRenders(turnCache, recordViewerFail(await readTurnRenders(turnCache), turnId, creativeSlug));
|
|
75630
|
+
}
|
|
75518
75631
|
process.stderr.write(`${JSON.stringify(viewerCheck.failure, null, 2)}
|
|
75519
75632
|
`);
|
|
75520
75633
|
process.exit(2);
|
|
@@ -75556,7 +75669,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75556
75669
|
if (snapshot && creativeSlug) await recordSnapshot(canvasRoot, creativeSlug, snapshot);
|
|
75557
75670
|
const recordMeta = {
|
|
75558
75671
|
creativeSlug,
|
|
75559
|
-
canvasPath:
|
|
75672
|
+
canvasPath: path30.relative(path30.dirname(canvasRoot), filePath) || void 0,
|
|
75560
75673
|
canvasSha,
|
|
75561
75674
|
// The fingerprint the dashboard compares against the current source to flag
|
|
75562
75675
|
// "edited since last render": the same `computeSourceSha` the edit-sync posts,
|
|
@@ -75644,7 +75757,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75644
75757
|
brief: opts.brief?.trim() || void 0,
|
|
75645
75758
|
onSpend: (usage) => reviewUsage.push(usage)
|
|
75646
75759
|
}) : null;
|
|
75647
|
-
const brandKit = reviewable ? null : await readStaticBrandKit(await findBrandRoot(
|
|
75760
|
+
const brandKit = reviewable ? null : await readStaticBrandKit(await findBrandRoot(path30.dirname(filePath)) ?? process.cwd());
|
|
75648
75761
|
const runShape = {
|
|
75649
75762
|
fix: fixInstruction !== void 0,
|
|
75650
75763
|
useVariant: opts.useVariant !== void 0,
|
|
@@ -75697,7 +75810,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75697
75810
|
] : [];
|
|
75698
75811
|
const findings = staticReview ? staticReview.review : review;
|
|
75699
75812
|
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(
|
|
75813
|
+
const definitionStatus = ready && creativeSlug ? await advanceDefinitionStatus(path30.join(path30.dirname(filePath), "_definition.md"), "rendered") : null;
|
|
75701
75814
|
if (poster) {
|
|
75702
75815
|
await flushBilled(
|
|
75703
75816
|
poster,
|
|
@@ -75772,6 +75885,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75772
75885
|
...step.kind === "fix" ? {
|
|
75773
75886
|
fix: step.instruction,
|
|
75774
75887
|
...step.relay ? { relayAlso: step.relay } : {},
|
|
75888
|
+
...step.keep ? { keepSizes: step.keep } : {},
|
|
75775
75889
|
...step.extend ? { extendSizes: step.extend } : {}
|
|
75776
75890
|
} : step.kind === "extend" ? { extendSizes: step.sizes, extendOnly: true } : step.kind === "relayout" ? {
|
|
75777
75891
|
...step.extend ? { extendSizes: step.extend } : {},
|
|
@@ -75781,28 +75895,36 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75781
75895
|
...opts.autoFixKind === "relayout" && opts.relayEdits ? { relayAsked: Object.fromEntries(Object.entries(opts.relayEdits).map(([id, asked]) => [id, [asked]])) } : {}
|
|
75782
75896
|
} : step.kind === "regenerate" ? { regenerate: step.ids.join(","), relayNote: step.note, ...step.extend ? { extendSizes: step.extend } : {} } : {}
|
|
75783
75897
|
};
|
|
75784
|
-
} else
|
|
75785
|
-
|
|
75786
|
-
|
|
75787
|
-
|
|
75788
|
-
|
|
75789
|
-
|
|
75790
|
-
|
|
75791
|
-
|
|
75792
|
-
|
|
75793
|
-
|
|
75794
|
-
|
|
75795
|
-
|
|
75796
|
-
|
|
75797
|
-
|
|
75798
|
-
|
|
75799
|
-
|
|
75800
|
-
|
|
75801
|
-
|
|
75802
|
-
|
|
75803
|
-
|
|
75898
|
+
} else {
|
|
75899
|
+
if (creativeSlug && turnId && staticReview) {
|
|
75900
|
+
await writeTurnRenders(
|
|
75901
|
+
turnCache,
|
|
75902
|
+
recordTurnRender(await readTurnRenders(turnCache), turnId, creativeSlug, staticReview.verdict)
|
|
75903
|
+
);
|
|
75904
|
+
}
|
|
75905
|
+
process.stdout.write(
|
|
75906
|
+
`${JSON.stringify(
|
|
75907
|
+
{
|
|
75908
|
+
ok: true,
|
|
75909
|
+
outputs_dir: result.outputs_dir,
|
|
75910
|
+
output: result.output,
|
|
75911
|
+
stats: result.stats,
|
|
75912
|
+
...findings ? { review: findings } : {},
|
|
75913
|
+
...staticReview?.judge?.length ? { judge: staticReview.judge } : {},
|
|
75914
|
+
...definitionStatus === "updated" ? { definition_status: "rendered" } : {},
|
|
75915
|
+
...hints2.length ? { hints: hints2 } : {},
|
|
75916
|
+
// Last, so a `| tail` keeps them: production agents cut them off and re-ran to get them back.
|
|
75917
|
+
// A static ad whose review found a blocking problem is finished but NOT ready.
|
|
75918
|
+
...staticReview ? { verdict: staticReview.verdict } : {},
|
|
75919
|
+
run_id: result.run_id,
|
|
75920
|
+
files: finalFiles(result.output, finalLabels?.labels)
|
|
75921
|
+
},
|
|
75922
|
+
null,
|
|
75923
|
+
2
|
|
75924
|
+
)}
|
|
75804
75925
|
`
|
|
75805
|
-
|
|
75926
|
+
);
|
|
75927
|
+
}
|
|
75806
75928
|
} catch (e) {
|
|
75807
75929
|
const failedPayload = async (message) => {
|
|
75808
75930
|
await readerSpend.drained();
|
|
@@ -75929,14 +76051,14 @@ var rerunCommand = defineCommand140({
|
|
|
75929
76051
|
});
|
|
75930
76052
|
|
|
75931
76053
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
75932
|
-
import { mkdir as
|
|
75933
|
-
import
|
|
76054
|
+
import { mkdir as mkdir12, readFile as readFile29, writeFile as writeFile18 } from "fs/promises";
|
|
76055
|
+
import path36 from "path";
|
|
75934
76056
|
import { defineCommand as defineCommand143 } from "citty";
|
|
75935
76057
|
|
|
75936
76058
|
// src/commands/canvas/creative-limit.ts
|
|
75937
76059
|
import { existsSync as existsSync7 } from "fs";
|
|
75938
|
-
import { mkdir as
|
|
75939
|
-
import
|
|
76060
|
+
import { mkdir as mkdir10, readdir as readdir13, writeFile as writeFile16 } from "fs/promises";
|
|
76061
|
+
import path31 from "path";
|
|
75940
76062
|
var NEW_CREATIVES_PER_CHAT = 10;
|
|
75941
76063
|
function claimNewCreative(made, slug) {
|
|
75942
76064
|
if (made.includes(slug)) return { allowed: true, count: made.length };
|
|
@@ -75944,7 +76066,7 @@ function claimNewCreative(made, slug) {
|
|
|
75944
76066
|
return { allowed: true, count: made.length + 1 };
|
|
75945
76067
|
}
|
|
75946
76068
|
function chatDir(canvasRoot, chatId) {
|
|
75947
|
-
return
|
|
76069
|
+
return path31.join(canvasRoot, ".creative-limit", (chatId ?? "no-chat").replace(/[^A-Za-z0-9_-]/g, "_"));
|
|
75948
76070
|
}
|
|
75949
76071
|
async function madeInChat(dir) {
|
|
75950
76072
|
return readdir13(dir).catch(() => []);
|
|
@@ -75956,8 +76078,8 @@ async function claimNewCreativeInWorkspace(canvasRoot, slug, chatId, definitionP
|
|
|
75956
76078
|
const claim = claimNewCreative(made, slug);
|
|
75957
76079
|
if (claim.allowed && !made.includes(slug)) {
|
|
75958
76080
|
try {
|
|
75959
|
-
await
|
|
75960
|
-
await
|
|
76081
|
+
await mkdir10(dir, { recursive: true });
|
|
76082
|
+
await writeFile16(path31.join(dir, slug), "", { flag: "wx" });
|
|
75961
76083
|
} catch {
|
|
75962
76084
|
}
|
|
75963
76085
|
}
|
|
@@ -75978,7 +76100,7 @@ async function enforceNewCreativeLimit(slug, canvasPath) {
|
|
|
75978
76100
|
canvasOutputsRoot(canvasPath),
|
|
75979
76101
|
slug,
|
|
75980
76102
|
optionalChatId() ?? null,
|
|
75981
|
-
|
|
76103
|
+
path31.join(path31.dirname(path31.resolve(canvasPath)), "_definition.md")
|
|
75982
76104
|
);
|
|
75983
76105
|
if (claim.allowed) return;
|
|
75984
76106
|
process.stderr.write(`${JSON.stringify(creativeLimitRefusal(slug), null, 2)}
|
|
@@ -75987,22 +76109,22 @@ async function enforceNewCreativeLimit(slug, canvasPath) {
|
|
|
75987
76109
|
}
|
|
75988
76110
|
|
|
75989
76111
|
// src/commands/canvas/scaffold-common.ts
|
|
75990
|
-
import { access, readFile as
|
|
76112
|
+
import { access, readFile as readFile26 } from "fs/promises";
|
|
75991
76113
|
|
|
75992
76114
|
// src/commands/canvas/composition-path.ts
|
|
75993
76115
|
import { existsSync as existsSync8 } from "fs";
|
|
75994
|
-
import
|
|
76116
|
+
import path32 from "path";
|
|
75995
76117
|
function resolveShippedCanvasDir(name, startDir, exists3 = existsSync8, maxDepth = 8) {
|
|
75996
|
-
const rel =
|
|
76118
|
+
const rel = path32.join("canvas", name);
|
|
75997
76119
|
let dir = startDir;
|
|
75998
76120
|
for (let i = 0; i < maxDepth; i++) {
|
|
75999
|
-
const candidate =
|
|
76000
|
-
if (exists3(
|
|
76001
|
-
const parent =
|
|
76121
|
+
const candidate = path32.join(dir, rel);
|
|
76122
|
+
if (exists3(path32.join(candidate, "meta.json"))) return candidate;
|
|
76123
|
+
const parent = path32.dirname(dir);
|
|
76002
76124
|
if (parent === dir) break;
|
|
76003
76125
|
dir = parent;
|
|
76004
76126
|
}
|
|
76005
|
-
return
|
|
76127
|
+
return path32.resolve(startDir, "../../../", rel);
|
|
76006
76128
|
}
|
|
76007
76129
|
|
|
76008
76130
|
// src/commands/canvas/scaffold-common.ts
|
|
@@ -76019,7 +76141,7 @@ function resolveModel(kind, requested, fallback) {
|
|
|
76019
76141
|
}
|
|
76020
76142
|
async function loadAssetText(ref2, label) {
|
|
76021
76143
|
const r = ref2;
|
|
76022
|
-
if (typeof r?.path === "string") return
|
|
76144
|
+
if (typeof r?.path === "string") return readFile26(r.path, "utf8");
|
|
76023
76145
|
if (typeof r?.url === "string") {
|
|
76024
76146
|
const res = await fetch(r.url);
|
|
76025
76147
|
if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
|
|
@@ -76167,16 +76289,16 @@ ${doc.toString()}---${raw.slice(match[0].length)}`;
|
|
|
76167
76289
|
}
|
|
76168
76290
|
|
|
76169
76291
|
// src/commands/canvas/scaffold-static-ad-paths.ts
|
|
76170
|
-
import
|
|
76292
|
+
import path33 from "path";
|
|
76171
76293
|
function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd(), slug) {
|
|
76172
76294
|
const file = rawFile.trim();
|
|
76173
76295
|
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 ?
|
|
76296
|
+
const imageSource = imageIsUrl ? file : path33.resolve(cwd, file);
|
|
76297
|
+
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");
|
|
76298
|
+
const blueprintPath2 = path33.join(path33.dirname(outPath), "prompt.json");
|
|
76299
|
+
const creativeDir = slug ? path33.dirname(outPath) : null;
|
|
76300
|
+
const definitionPath = creativeDir ? path33.join(creativeDir, "_definition.md") : null;
|
|
76301
|
+
const referencesDir = creativeDir ? path33.join(creativeDir, "references") : null;
|
|
76180
76302
|
return { imageIsUrl, imageSource, outPath, blueprintPath: blueprintPath2, creativeDir, definitionPath, referencesDir };
|
|
76181
76303
|
}
|
|
76182
76304
|
var SCAFFOLD_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
@@ -76186,8 +76308,8 @@ function isValidScaffoldSlug(slug) {
|
|
|
76186
76308
|
}
|
|
76187
76309
|
|
|
76188
76310
|
// src/commands/canvas/sync-definition.ts
|
|
76189
|
-
import { readdir as readdir14, readFile as
|
|
76190
|
-
import
|
|
76311
|
+
import { readdir as readdir14, readFile as readFile27, stat as stat5 } from "fs/promises";
|
|
76312
|
+
import path34 from "path";
|
|
76191
76313
|
import { defineCommand as defineCommand141 } from "citty";
|
|
76192
76314
|
|
|
76193
76315
|
// src/commands/canvas/definition-graph.ts
|
|
@@ -76244,14 +76366,14 @@ function buildDefinitionSyncPayload(input, graph, chatId) {
|
|
|
76244
76366
|
}
|
|
76245
76367
|
async function readDefinitionSourceFields(canvasPath) {
|
|
76246
76368
|
try {
|
|
76247
|
-
return parseDefinitionSourceFields(await
|
|
76369
|
+
return parseDefinitionSourceFields(await readFile27(path34.join(path34.dirname(canvasPath), "_definition.md"), "utf8"));
|
|
76248
76370
|
} catch {
|
|
76249
76371
|
return {};
|
|
76250
76372
|
}
|
|
76251
76373
|
}
|
|
76252
76374
|
async function readDefinitionStrategyFields(canvasPath) {
|
|
76253
76375
|
try {
|
|
76254
|
-
return parseDefinitionStrategyFields(await
|
|
76376
|
+
return parseDefinitionStrategyFields(await readFile27(path34.join(path34.dirname(canvasPath), "_definition.md"), "utf8"));
|
|
76255
76377
|
} catch {
|
|
76256
76378
|
return {};
|
|
76257
76379
|
}
|
|
@@ -76274,15 +76396,15 @@ async function syncCreativeDefinitionBestEffort(input) {
|
|
|
76274
76396
|
}
|
|
76275
76397
|
}
|
|
76276
76398
|
async function resolveCanvasPath(inputPath) {
|
|
76277
|
-
const resolved =
|
|
76399
|
+
const resolved = path34.resolve(inputPath);
|
|
76278
76400
|
let dir = resolved;
|
|
76279
76401
|
try {
|
|
76280
76402
|
if ((await stat5(resolved)).isFile()) {
|
|
76281
76403
|
if (resolved.endsWith(".canvas.json")) return resolved;
|
|
76282
|
-
dir =
|
|
76404
|
+
dir = path34.dirname(resolved);
|
|
76283
76405
|
}
|
|
76284
76406
|
} catch {
|
|
76285
|
-
dir = resolved.endsWith(".canvas.json") ?
|
|
76407
|
+
dir = resolved.endsWith(".canvas.json") ? path34.dirname(resolved) : resolved;
|
|
76286
76408
|
}
|
|
76287
76409
|
let entries;
|
|
76288
76410
|
try {
|
|
@@ -76293,7 +76415,7 @@ async function resolveCanvasPath(inputPath) {
|
|
|
76293
76415
|
const canvases = entries.filter((name) => name.endsWith(".canvas.json"));
|
|
76294
76416
|
const slug = creativeSlugFromCanvasPath(`${dir}/x/`);
|
|
76295
76417
|
const chosen = (slug ? canvases.find((name) => name === `${slug}.canvas.json`) : void 0) ?? canvases[0];
|
|
76296
|
-
return chosen ?
|
|
76418
|
+
return chosen ? path34.join(dir, chosen) : null;
|
|
76297
76419
|
}
|
|
76298
76420
|
var syncDefinitionCommand = defineCommand141({
|
|
76299
76421
|
meta: {
|
|
@@ -76563,32 +76685,32 @@ function mergeReferenceIntoBlueprint(blueprint, reference) {
|
|
|
76563
76685
|
}
|
|
76564
76686
|
|
|
76565
76687
|
// src/commands/winning-ads/fit.ts
|
|
76566
|
-
import { mkdir as
|
|
76567
|
-
import
|
|
76688
|
+
import { mkdir as mkdir11, readdir as readdir15, readFile as readFile28, writeFile as writeFile17 } from "fs/promises";
|
|
76689
|
+
import path35 from "path";
|
|
76568
76690
|
import { defineCommand as defineCommand142 } from "citty";
|
|
76569
76691
|
import sharp4 from "sharp";
|
|
76570
|
-
import { z as
|
|
76571
|
-
var Layers =
|
|
76572
|
-
disguise:
|
|
76573
|
-
voice:
|
|
76574
|
-
trigger:
|
|
76575
|
-
attention:
|
|
76576
|
-
logic:
|
|
76692
|
+
import { z as z68 } from "zod";
|
|
76693
|
+
var Layers = z68.object({
|
|
76694
|
+
disguise: z68.string().optional(),
|
|
76695
|
+
voice: z68.string().optional(),
|
|
76696
|
+
trigger: z68.string().optional(),
|
|
76697
|
+
attention: z68.string().optional(),
|
|
76698
|
+
logic: z68.string().optional()
|
|
76577
76699
|
}).strip();
|
|
76578
|
-
var FitAnswerSchema =
|
|
76700
|
+
var FitAnswerSchema = z68.object({
|
|
76579
76701
|
why_it_wins: Layers.default({}),
|
|
76580
|
-
load_bearing:
|
|
76581
|
-
|
|
76582
|
-
element:
|
|
76583
|
-
needs:
|
|
76584
|
-
have:
|
|
76585
|
-
ours:
|
|
76702
|
+
load_bearing: z68.array(
|
|
76703
|
+
z68.object({
|
|
76704
|
+
element: z68.string().trim().min(1),
|
|
76705
|
+
needs: z68.string().optional(),
|
|
76706
|
+
have: z68.enum(["have", "adapt", "missing"]),
|
|
76707
|
+
ours: z68.string().optional()
|
|
76586
76708
|
}).strip()
|
|
76587
76709
|
).default([]),
|
|
76588
|
-
verdict:
|
|
76589
|
-
transfer_note:
|
|
76590
|
-
brand_presence:
|
|
76591
|
-
recognition:
|
|
76710
|
+
verdict: z68.enum(["use", "adapt", "reject"]),
|
|
76711
|
+
transfer_note: z68.string().optional(),
|
|
76712
|
+
brand_presence: z68.enum(["native", "standard"]).optional(),
|
|
76713
|
+
recognition: z68.string().optional()
|
|
76592
76714
|
});
|
|
76593
76715
|
function buildFitPrompt(input) {
|
|
76594
76716
|
const lib = input.library;
|
|
@@ -76649,33 +76771,33 @@ var MAX_DOC_CHARS = 1500;
|
|
|
76649
76771
|
var MAX_ENTRY_CHARS = 400;
|
|
76650
76772
|
var KNOWLEDGE_COLLECTIONS = ["offers", "products", "personas"];
|
|
76651
76773
|
function readText(file) {
|
|
76652
|
-
return
|
|
76774
|
+
return readFile28(file, "utf8").catch(() => null);
|
|
76653
76775
|
}
|
|
76654
76776
|
async function readKnowledge(root) {
|
|
76655
76777
|
const parts = [];
|
|
76656
|
-
const company = await readText(
|
|
76778
|
+
const company = await readText(path35.join(root, "COMPANY.md"));
|
|
76657
76779
|
if (company) parts.push(`COMPANY.md:
|
|
76658
76780
|
${company.slice(0, MAX_DOC_CHARS)}`);
|
|
76659
|
-
const brand = await readText(
|
|
76781
|
+
const brand = await readText(path35.join(root, "src", "brand", "BRAND.md"));
|
|
76660
76782
|
if (brand) parts.push(`BRAND.md (start):
|
|
76661
76783
|
${brand.slice(0, MAX_DOC_CHARS)}`);
|
|
76662
76784
|
for (const collection of KNOWLEDGE_COLLECTIONS) {
|
|
76663
|
-
const dir =
|
|
76785
|
+
const dir = path35.join(root, "src", "content", collection);
|
|
76664
76786
|
const files = (await readdir15(dir, { recursive: true }).catch(() => [])).filter(
|
|
76665
76787
|
(f) => f.endsWith(".md")
|
|
76666
76788
|
);
|
|
76667
76789
|
for (const file of files.sort().slice(0, 12)) {
|
|
76668
|
-
const text3 = await readText(
|
|
76790
|
+
const text3 = await readText(path35.join(dir, file));
|
|
76669
76791
|
if (text3) parts.push(`${collection}/${file}:
|
|
76670
76792
|
${text3.slice(0, MAX_ENTRY_CHARS)}`);
|
|
76671
76793
|
}
|
|
76672
76794
|
}
|
|
76673
76795
|
return parts.join("\n\n");
|
|
76674
76796
|
}
|
|
76675
|
-
var ReviewSchema =
|
|
76676
|
-
reviewText:
|
|
76677
|
-
sourceType:
|
|
76678
|
-
rating:
|
|
76797
|
+
var ReviewSchema = z68.object({
|
|
76798
|
+
reviewText: z68.string(),
|
|
76799
|
+
sourceType: z68.string().optional(),
|
|
76800
|
+
rating: z68.number().optional()
|
|
76679
76801
|
});
|
|
76680
76802
|
function reviewsForFit(data) {
|
|
76681
76803
|
const list = Array.isArray(data) ? data : [];
|
|
@@ -76697,7 +76819,7 @@ async function readReviews() {
|
|
|
76697
76819
|
}
|
|
76698
76820
|
}
|
|
76699
76821
|
function fitCachePath(root, platform, adId) {
|
|
76700
|
-
return
|
|
76822
|
+
return path35.join(root, "canvas", ".cache", "fit", `${platform}-${adId.replace(/[^a-zA-Z0-9_-]/g, "_")}.json`);
|
|
76701
76823
|
}
|
|
76702
76824
|
async function readCachedFit(root, platform, adId) {
|
|
76703
76825
|
const raw = await readText(fitCachePath(root, platform, adId));
|
|
@@ -76752,8 +76874,8 @@ async function fitOnce(root, platform, adId, ask) {
|
|
|
76752
76874
|
const fit = await ask();
|
|
76753
76875
|
if (!fit) return null;
|
|
76754
76876
|
const file = fitCachePath(root, platform, adId);
|
|
76755
|
-
await
|
|
76756
|
-
await
|
|
76877
|
+
await mkdir11(path35.dirname(file), { recursive: true });
|
|
76878
|
+
await writeFile17(file, `${JSON.stringify(fit, null, 2)}
|
|
76757
76879
|
`, "utf8");
|
|
76758
76880
|
return fit;
|
|
76759
76881
|
}
|
|
@@ -76791,7 +76913,7 @@ async function fitReference(root, platform, adId) {
|
|
|
76791
76913
|
const prompt = buildFitPrompt({ library: libraryRead(ad), knowledge: knowledge2 });
|
|
76792
76914
|
const spent = [];
|
|
76793
76915
|
const answer = await askFit(jpeg, prompt, (u) => spent.push(u));
|
|
76794
|
-
await holdUnbilledSpend(
|
|
76916
|
+
await holdUnbilledSpend(path35.join(root, "canvas", ".cache"), spent);
|
|
76795
76917
|
return answer;
|
|
76796
76918
|
});
|
|
76797
76919
|
if (fit) return { ok: true, fit };
|
|
@@ -76844,7 +76966,7 @@ var fitCommand = defineCommand142({
|
|
|
76844
76966
|
|
|
76845
76967
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
76846
76968
|
function toCanvasRelative2(canvasDir, file) {
|
|
76847
|
-
const rel =
|
|
76969
|
+
const rel = path36.relative(canvasDir, file).split(path36.sep).join("/");
|
|
76848
76970
|
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
76849
76971
|
}
|
|
76850
76972
|
async function uploadSourceAsReference(source, isUrl, client) {
|
|
@@ -76856,7 +76978,7 @@ async function uploadSourceAsReference(source, isUrl, client) {
|
|
|
76856
76978
|
throw new Error(`failed to download source image (${e instanceof Error ? e.message : String(e)})`);
|
|
76857
76979
|
}
|
|
76858
76980
|
} else {
|
|
76859
|
-
bytes = await
|
|
76981
|
+
bytes = await readFile29(source);
|
|
76860
76982
|
}
|
|
76861
76983
|
const safe = await toModelSafeImage(bytes);
|
|
76862
76984
|
const sha2562 = sha256Hex(safe.bytes);
|
|
@@ -77136,25 +77258,25 @@ async function lookupReference(args8) {
|
|
|
77136
77258
|
}
|
|
77137
77259
|
async function scaffoldFromSpec(args8, slug) {
|
|
77138
77260
|
if (!slug) return fail4("missing_slug", "--spec needs --slug: an ad started from a spec is a creative.");
|
|
77139
|
-
const specPath =
|
|
77261
|
+
const specPath = path36.resolve(process.cwd(), String(args8.spec));
|
|
77140
77262
|
let started;
|
|
77141
77263
|
try {
|
|
77142
|
-
started = readAdSpec(JSON.parse(await
|
|
77264
|
+
started = readAdSpec(JSON.parse(await readFile29(specPath, "utf8")));
|
|
77143
77265
|
} catch (e) {
|
|
77144
77266
|
return fail4("invalid_spec", `${specPath} is not an ad spec: ${e instanceof Error ? e.message : String(e)}`);
|
|
77145
77267
|
}
|
|
77146
|
-
const outPath =
|
|
77147
|
-
const dir =
|
|
77148
|
-
const blueprintPath2 =
|
|
77149
|
-
const definitionPath =
|
|
77268
|
+
const outPath = path36.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`);
|
|
77269
|
+
const dir = path36.dirname(outPath);
|
|
77270
|
+
const blueprintPath2 = path36.join(dir, "prompt.json");
|
|
77271
|
+
const definitionPath = path36.join(dir, "_definition.md");
|
|
77150
77272
|
await enforceNewCreativeLimit(slug, outPath);
|
|
77151
|
-
const existingDefinition = await fileExists(definitionPath) ? await
|
|
77273
|
+
const existingDefinition = await fileExists(definitionPath) ? await readFile29(definitionPath, "utf8") : null;
|
|
77152
77274
|
const who = await scaffoldViewer(args8, existingDefinition, liveViewerLookups(process.cwd()).adSetViewer);
|
|
77153
77275
|
if (!who.ok) return fail4("invalid_viewer", who.message);
|
|
77154
|
-
await
|
|
77276
|
+
await mkdir12(dir, { recursive: true });
|
|
77155
77277
|
const workspaceRoot = await findBrandRoot(dir) ?? process.cwd();
|
|
77156
77278
|
const kit = await readStaticBrandKit(workspaceRoot);
|
|
77157
|
-
const market = who.viewer?.market ?? marketFromCompanyDoc(await
|
|
77279
|
+
const market = who.viewer?.market ?? marketFromCompanyDoc(await readFile29(path36.join(workspaceRoot, "COMPANY.md"), "utf8").catch(() => null));
|
|
77158
77280
|
const brand = brandBlock(kit);
|
|
77159
77281
|
const platform = resolvePlatform(args8.platform ? String(args8.platform) : void 0);
|
|
77160
77282
|
const placementsResult = resolvePlacements(args8.placements ? String(args8.placements) : void 0, platform);
|
|
@@ -77176,18 +77298,18 @@ async function scaffoldFromSpec(args8, slug) {
|
|
|
77176
77298
|
logo: Boolean(kit.logo),
|
|
77177
77299
|
...market ? { viewer: { ...started.viewer ?? { who: "the people this ad is for" }, market } } : {}
|
|
77178
77300
|
};
|
|
77179
|
-
await
|
|
77301
|
+
await writeFile18(blueprintPath2, `${JSON.stringify(spec, null, 2)}
|
|
77180
77302
|
`, "utf8");
|
|
77181
77303
|
const canvas = scaffoldStaticAd({}, [], { ...opts, spec });
|
|
77182
77304
|
const validation = await validateScaffoldedStaticAd(canvas, dir);
|
|
77183
77305
|
if (validation.blocking.length > 0) return fail4("validation", validation.blocking.map((i) => i.message).join("; "));
|
|
77184
|
-
await
|
|
77306
|
+
await writeFile18(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
77185
77307
|
`, "utf8");
|
|
77186
77308
|
const title = args8.title ? String(args8.title) : titleFromSlug(slug);
|
|
77187
77309
|
const recorded = who.record ? { targetAdSet: who.targetAdSet, viewer: who.viewer } : {};
|
|
77188
77310
|
if (existingDefinition === null) {
|
|
77189
77311
|
const formats = resolveFormatList(report.output_formats);
|
|
77190
|
-
await
|
|
77312
|
+
await writeFile18(definitionPath, buildCreativeDefinition({ title, kind: "static", platform, formats, ...recorded }), "utf8");
|
|
77191
77313
|
}
|
|
77192
77314
|
await syncCreativeDefinitionBestEffort({
|
|
77193
77315
|
slug,
|
|
@@ -77301,14 +77423,14 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77301
77423
|
}
|
|
77302
77424
|
const { imageIsUrl, imageSource, outPath, blueprintPath: blueprintPath2, definitionPath, referencesDir } = resolveScaffoldStaticAdPaths(sourceFile, args8.out ? String(args8.out) : void 0, process.cwd(), slug);
|
|
77303
77425
|
if (slug) await enforceNewCreativeLimit(slug, outPath);
|
|
77304
|
-
const existingDefinition = definitionPath && await fileExists(definitionPath) ? await
|
|
77426
|
+
const existingDefinition = definitionPath && await fileExists(definitionPath) ? await readFile29(definitionPath, "utf8") : null;
|
|
77305
77427
|
const who = await scaffoldViewer(args8, existingDefinition, liveViewerLookups(process.cwd()).adSetViewer);
|
|
77306
77428
|
if (!who.ok) return fail4("invalid_viewer", who.message);
|
|
77307
77429
|
const fitRoot = findWorkspaceRoot(process.cwd()) ?? process.cwd();
|
|
77308
77430
|
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
77431
|
const fitProblems = fit ? fitHints(fit, reference?.ad_id ?? "").filter((h) => h.startsWith("Pick another reference")) : [];
|
|
77310
77432
|
if (fitProblems.length > 0) return fail4("concept_does_not_fit", fitProblems.join(" "));
|
|
77311
|
-
await
|
|
77433
|
+
await mkdir12(path36.dirname(outPath), { recursive: true });
|
|
77312
77434
|
let models;
|
|
77313
77435
|
try {
|
|
77314
77436
|
models = resolveModels(args8);
|
|
@@ -77342,10 +77464,10 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77342
77464
|
const persona = existingDefinition?.match(/^persona:\s*"?([^"\n]+)"?\s*$/m)?.[1];
|
|
77343
77465
|
const elements = castAvatarsOnStaticElements(vision.elements, cast.avatars, persona);
|
|
77344
77466
|
const uncast = staticAvatarsNotCast(elements, cast.handles);
|
|
77345
|
-
const canvasDir =
|
|
77467
|
+
const canvasDir = path36.dirname(outPath);
|
|
77346
77468
|
const workspaceRoot = await findBrandRoot(canvasDir) ?? process.cwd();
|
|
77347
77469
|
const kit = await readStaticBrandKit(workspaceRoot);
|
|
77348
|
-
const market = who.viewer?.market ?? marketFromCompanyDoc(await
|
|
77470
|
+
const market = who.viewer?.market ?? marketFromCompanyDoc(await readFile29(path36.join(workspaceRoot, "COMPANY.md"), "utf8").catch(() => null));
|
|
77349
77471
|
const brand = brandBlock(kit);
|
|
77350
77472
|
let canvasImagePath = imageSource;
|
|
77351
77473
|
let canvasImageIsUrl = imageIsUrl;
|
|
@@ -77395,7 +77517,7 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77395
77517
|
...fit ? { fit: conceptFromFit(fit, reference?.ad_id ?? "") } : {},
|
|
77396
77518
|
...market ? { market } : {}
|
|
77397
77519
|
});
|
|
77398
|
-
await
|
|
77520
|
+
await writeFile18(blueprintPath2, `${JSON.stringify(spec, null, 2)}
|
|
77399
77521
|
`, "utf8");
|
|
77400
77522
|
conceptNotes = conceptProblems(spec).map(
|
|
77401
77523
|
(p) => `CONCEPT: ${p} Then \`baker canvas run\`; until then it refuses to render.`
|
|
@@ -77409,7 +77531,7 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77409
77531
|
} catch (e) {
|
|
77410
77532
|
return fail4("scaffold", e instanceof Error ? e.message : String(e));
|
|
77411
77533
|
}
|
|
77412
|
-
const validation = await validateScaffoldedStaticAd(canvas,
|
|
77534
|
+
const validation = await validateScaffoldedStaticAd(canvas, path36.dirname(outPath));
|
|
77413
77535
|
if (validation.blocking.length > 0) {
|
|
77414
77536
|
process.stderr.write(
|
|
77415
77537
|
`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.blocking } }, null, 2)}
|
|
@@ -77417,7 +77539,7 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77417
77539
|
);
|
|
77418
77540
|
process.exit(2);
|
|
77419
77541
|
}
|
|
77420
|
-
await
|
|
77542
|
+
await writeFile18(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
77421
77543
|
`, "utf8");
|
|
77422
77544
|
const sourceFields = reference ? {
|
|
77423
77545
|
sourceAdId: reference.ad_id,
|
|
@@ -77430,10 +77552,10 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77430
77552
|
} : {};
|
|
77431
77553
|
const recorded = who.record ? { targetAdSet: who.targetAdSet, viewer: who.viewer } : {};
|
|
77432
77554
|
if (definitionPath && existingDefinition !== null && who.record) {
|
|
77433
|
-
await
|
|
77555
|
+
await writeFile18(definitionPath, setDefinitionViewer(existingDefinition, recorded), "utf8");
|
|
77434
77556
|
}
|
|
77435
77557
|
if (definitionPath && existingDefinition === null) {
|
|
77436
|
-
await
|
|
77558
|
+
await writeFile18(
|
|
77437
77559
|
definitionPath,
|
|
77438
77560
|
buildCreativeDefinition({
|
|
77439
77561
|
title: args8.title ? String(args8.title) : titleFromSlug(slug ?? ""),
|
|
@@ -77506,7 +77628,7 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77506
77628
|
run_estimated_credits: validation.estimatedCredits
|
|
77507
77629
|
},
|
|
77508
77630
|
checklist: {
|
|
77509
|
-
edit_prompt: `Edit ${
|
|
77631
|
+
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
77632
|
mechanism: report.winning_mechanisms,
|
|
77511
77633
|
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
77634
|
assets_to_supply: report.elements,
|
|
@@ -77568,25 +77690,25 @@ function clientLogoMissNote(decision, domain) {
|
|
|
77568
77690
|
}
|
|
77569
77691
|
|
|
77570
77692
|
// src/commands/canvas/scaffold-ad.ts
|
|
77571
|
-
import { copyFile, cp, mkdir as
|
|
77572
|
-
import
|
|
77693
|
+
import { copyFile, cp, mkdir as mkdir13, readFile as readFile31, stat as stat6, writeFile as writeFile20 } from "fs/promises";
|
|
77694
|
+
import path38 from "path";
|
|
77573
77695
|
import { defineCommand as defineCommand144 } from "citty";
|
|
77574
77696
|
|
|
77575
77697
|
// src/engine/scaffold/ad-spec.ts
|
|
77576
|
-
import { z as
|
|
77698
|
+
import { z as z69 } from "zod";
|
|
77577
77699
|
var oneClause = (line) => (line.match(/[.!?](\s|$)/g) ?? []).length <= 1;
|
|
77578
77700
|
var isSpeech = (line) => new RegExp("\\p{L}\\p{L}", "u").test(line);
|
|
77579
|
-
var Beat =
|
|
77701
|
+
var Beat = z69.object({
|
|
77580
77702
|
/**
|
|
77581
77703
|
* The line the voice says in this beat — ONE clause, ending in its own
|
|
77582
77704
|
* punctuation. It becomes the caption card verbatim.
|
|
77583
77705
|
*/
|
|
77584
|
-
say:
|
|
77706
|
+
say: z69.string().min(1).refine(
|
|
77585
77707
|
isSpeech,
|
|
77586
77708
|
"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
77709
|
).refine(oneClause, "a beat is one clause: split a second sentence into its own beat"),
|
|
77588
77710
|
/** What is on screen while it is said, as a shot brief for the video model. */
|
|
77589
|
-
show:
|
|
77711
|
+
show: z69.string().min(1),
|
|
77590
77712
|
/**
|
|
77591
77713
|
* What this shot SOUNDS like — one effect, in words. Usually absent.
|
|
77592
77714
|
*
|
|
@@ -77603,7 +77725,7 @@ var Beat = z68.object({
|
|
|
77603
77725
|
* Describe the SOUND, not the picture: "a cordless drill tightening a bolt", not "the
|
|
77604
77726
|
* fitter works on the rail".
|
|
77605
77727
|
*/
|
|
77606
|
-
sound:
|
|
77728
|
+
sound: z69.string().min(1).max(200).optional(),
|
|
77607
77729
|
/**
|
|
77608
77730
|
* Whether this beat's subject is talking to camera.
|
|
77609
77731
|
*
|
|
@@ -77617,36 +77739,36 @@ var Beat = z68.object({
|
|
|
77617
77739
|
* way through" produced an ad with her face on screen and a narrator cast from a prose
|
|
77618
77740
|
* description reading her lines, because nothing set this flag.
|
|
77619
77741
|
*/
|
|
77620
|
-
on_camera:
|
|
77742
|
+
on_camera: z69.boolean().optional(),
|
|
77621
77743
|
/**
|
|
77622
77744
|
* Whether the ad's recurring person is in this shot. Defaults to true whenever the
|
|
77623
77745
|
* spec declares a `cast`, because an ad is usually about one person and defaulting
|
|
77624
77746
|
* the other way is how continuity gets forgotten. Set false for a beat they are not
|
|
77625
77747
|
* in — a fitter on a roof, a product close-up.
|
|
77626
77748
|
*/
|
|
77627
|
-
cast:
|
|
77749
|
+
cast: z69.boolean().optional()
|
|
77628
77750
|
});
|
|
77629
|
-
var AdSpec =
|
|
77630
|
-
format:
|
|
77631
|
-
aspect_ratio:
|
|
77632
|
-
resolution:
|
|
77751
|
+
var AdSpec = z69.object({
|
|
77752
|
+
format: z69.object({
|
|
77753
|
+
aspect_ratio: z69.string().default("9:16"),
|
|
77754
|
+
resolution: z69.string().default("720p")
|
|
77633
77755
|
}).default({ aspect_ratio: "9:16", resolution: "720p" }),
|
|
77634
|
-
brand:
|
|
77635
|
-
name:
|
|
77756
|
+
brand: z69.object({
|
|
77757
|
+
name: z69.string().optional(),
|
|
77636
77758
|
/** Figures to lift typographically — the one gold number every ad has. */
|
|
77637
|
-
highlight:
|
|
77759
|
+
highlight: z69.array(z69.string()).optional(),
|
|
77638
77760
|
/**
|
|
77639
77761
|
* Brand hex colours, most important first. They dress the brand plate and
|
|
77640
77762
|
* the type — never the scenes: pushing hex into a shot brief makes the
|
|
77641
77763
|
* model paint the whole frame that colour, which reads as a filter.
|
|
77642
77764
|
*/
|
|
77643
|
-
palette:
|
|
77765
|
+
palette: z69.array(z69.string()).optional(),
|
|
77644
77766
|
/** Repo path to the brand mark, so the CTA carries it instead of a name in a font. */
|
|
77645
|
-
logo:
|
|
77767
|
+
logo: z69.string().optional()
|
|
77646
77768
|
}).optional(),
|
|
77647
|
-
voice:
|
|
77648
|
-
description:
|
|
77649
|
-
language:
|
|
77769
|
+
voice: z69.object({
|
|
77770
|
+
description: z69.string().min(1),
|
|
77771
|
+
language: z69.string().optional()
|
|
77650
77772
|
}).optional(),
|
|
77651
77773
|
/**
|
|
77652
77774
|
* Where the ad is set, as a place a model can picture: "Spain", "Mexico",
|
|
@@ -77657,7 +77779,7 @@ var AdSpec = z68.object({
|
|
|
77657
77779
|
* houses — sash windows, slate roofs, English suburbs — in every single frame,
|
|
77658
77780
|
* from briefs that were otherwise perfectly good.
|
|
77659
77781
|
*/
|
|
77660
|
-
market:
|
|
77782
|
+
market: z69.string().min(1).optional(),
|
|
77661
77783
|
/**
|
|
77662
77784
|
* The one person the ad is about.
|
|
77663
77785
|
*
|
|
@@ -77667,7 +77789,7 @@ var AdSpec = z68.object({
|
|
|
77667
77789
|
* with five different men playing one customer, which reads as a stock-footage
|
|
77668
77790
|
* collage rather than a story.
|
|
77669
77791
|
*/
|
|
77670
|
-
cast:
|
|
77792
|
+
cast: z69.object({
|
|
77671
77793
|
/**
|
|
77672
77794
|
* A cast Avatar's handle — `baker avatars list` shows who there is.
|
|
77673
77795
|
*
|
|
@@ -77677,9 +77799,9 @@ var AdSpec = z68.object({
|
|
|
77677
77799
|
* rather than re-written per spec — re-phrasing it per render is the other reason
|
|
77678
77800
|
* a face drifts across a set.
|
|
77679
77801
|
*/
|
|
77680
|
-
avatar:
|
|
77802
|
+
avatar: z69.string().min(1).optional(),
|
|
77681
77803
|
/** Who they are, plainly: age, build, hair, wardrobe, the register of the ad. */
|
|
77682
|
-
description:
|
|
77804
|
+
description: z69.string().min(1).optional()
|
|
77683
77805
|
}).refine((c) => Boolean(c.avatar || c.description), {
|
|
77684
77806
|
message: "give `avatar` (a cast avatar's handle) or `description` \u2014 a cast with neither grounds on nothing"
|
|
77685
77807
|
}).optional(),
|
|
@@ -77696,7 +77818,7 @@ var AdSpec = z68.object({
|
|
|
77696
77818
|
* has to read clean. That is the engine's rule, not this one, and it is why the prompt
|
|
77697
77819
|
* is all this needs to carry.
|
|
77698
77820
|
*/
|
|
77699
|
-
music:
|
|
77821
|
+
music: z69.union([z69.string().min(1), z69.literal(false)]).optional(),
|
|
77700
77822
|
/**
|
|
77701
77823
|
* The place the ad keeps coming back to, held still across every shot.
|
|
77702
77824
|
*
|
|
@@ -77711,9 +77833,9 @@ var AdSpec = z68.object({
|
|
|
77711
77833
|
* is `image_generate` with extra steps — the model registry says exactly that. Generate
|
|
77712
77834
|
* the plate once with gpt-image-2 and pass its URL here.
|
|
77713
77835
|
*/
|
|
77714
|
-
set:
|
|
77715
|
-
description:
|
|
77716
|
-
image:
|
|
77836
|
+
set: z69.object({
|
|
77837
|
+
description: z69.string().min(1).max(400),
|
|
77838
|
+
image: z69.string().min(1).optional()
|
|
77717
77839
|
}).optional(),
|
|
77718
77840
|
/**
|
|
77719
77841
|
* How the ad closes.
|
|
@@ -77727,15 +77849,15 @@ var AdSpec = z68.object({
|
|
|
77727
77849
|
*
|
|
77728
77850
|
* `false` opts out, for an ad that genuinely ends on its footage.
|
|
77729
77851
|
*/
|
|
77730
|
-
end_card:
|
|
77731
|
-
|
|
77732
|
-
|
|
77852
|
+
end_card: z69.union([
|
|
77853
|
+
z69.literal(false),
|
|
77854
|
+
z69.object({
|
|
77733
77855
|
/**
|
|
77734
77856
|
* The words on the button. No default — with none the card carries the mark
|
|
77735
77857
|
* alone, because falling back to the last beat's line printed that sentence
|
|
77736
77858
|
* twice on the closing frame. See `endCardCta`.
|
|
77737
77859
|
*/
|
|
77738
|
-
cta:
|
|
77860
|
+
cta: z69.string().min(1).optional()
|
|
77739
77861
|
})
|
|
77740
77862
|
]).optional(),
|
|
77741
77863
|
/**
|
|
@@ -77755,8 +77877,8 @@ var AdSpec = z68.object({
|
|
|
77755
77877
|
* rather than from transcribing a rendered read, so the text is exact and no speech
|
|
77756
77878
|
* model is between the copy and the screen.
|
|
77757
77879
|
*/
|
|
77758
|
-
voiceover:
|
|
77759
|
-
beats:
|
|
77880
|
+
voiceover: z69.boolean().default(true),
|
|
77881
|
+
beats: z69.array(Beat).min(2)
|
|
77760
77882
|
});
|
|
77761
77883
|
function presenterIsInEveryShot(spec) {
|
|
77762
77884
|
if (!spec.cast) return false;
|
|
@@ -78283,8 +78405,8 @@ function injectAdBrandOverlay(indexHtml, overlayHtml, style = {}) {
|
|
|
78283
78405
|
}
|
|
78284
78406
|
|
|
78285
78407
|
// src/commands/canvas/composition-dims.ts
|
|
78286
|
-
import { readFile as
|
|
78287
|
-
import
|
|
78408
|
+
import { readFile as readFile30, writeFile as writeFile19 } from "fs/promises";
|
|
78409
|
+
import path37 from "path";
|
|
78288
78410
|
function patchCompositionMeta(metaJson, dims) {
|
|
78289
78411
|
try {
|
|
78290
78412
|
const meta = JSON.parse(metaJson);
|
|
@@ -78299,12 +78421,12 @@ function patchCompositionHtml(html, dims) {
|
|
|
78299
78421
|
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
78422
|
}
|
|
78301
78423
|
async function stampCompositionDims(compositionDir, dims) {
|
|
78302
|
-
const metaPath =
|
|
78303
|
-
const rawMeta = await
|
|
78304
|
-
await
|
|
78305
|
-
const htmlPath =
|
|
78306
|
-
const rawHtml = await
|
|
78307
|
-
await
|
|
78424
|
+
const metaPath = path37.join(compositionDir, "meta.json");
|
|
78425
|
+
const rawMeta = await readFile30(metaPath, "utf8");
|
|
78426
|
+
await writeFile19(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
|
|
78427
|
+
const htmlPath = path37.join(compositionDir, "index.html");
|
|
78428
|
+
const rawHtml = await readFile30(htmlPath, "utf8");
|
|
78429
|
+
await writeFile19(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
|
|
78308
78430
|
}
|
|
78309
78431
|
|
|
78310
78432
|
// src/commands/canvas/scaffold-ad.ts
|
|
@@ -78386,7 +78508,7 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78386
78508
|
const specPath = args8.spec;
|
|
78387
78509
|
let parsed;
|
|
78388
78510
|
try {
|
|
78389
|
-
parsed = JSON.parse(await
|
|
78511
|
+
parsed = JSON.parse(await readFile31(specPath, "utf-8"));
|
|
78390
78512
|
} catch (e) {
|
|
78391
78513
|
writeJson({
|
|
78392
78514
|
ok: false,
|
|
@@ -78547,7 +78669,7 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78547
78669
|
] : [];
|
|
78548
78670
|
const markPath = spec.data.brand?.logo?.trim();
|
|
78549
78671
|
if (!noBrand && markPath?.toLowerCase().endsWith(".svg")) {
|
|
78550
|
-
const svg = await
|
|
78672
|
+
const svg = await readFile31(markPath, "utf-8").catch(() => null);
|
|
78551
78673
|
if (svg === null) {
|
|
78552
78674
|
writeJson({
|
|
78553
78675
|
ok: false,
|
|
@@ -78599,8 +78721,8 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78599
78721
|
videoModel: chosenVideoModel,
|
|
78600
78722
|
...avatarVoice ? { avatarVoice } : {}
|
|
78601
78723
|
});
|
|
78602
|
-
const slug = args8.slug ??
|
|
78603
|
-
const outPath = args8.out ?? (args8.slug ?
|
|
78724
|
+
const slug = args8.slug ?? path38.basename(specPath).replace(/\.[^.]+$/, "");
|
|
78725
|
+
const outPath = args8.out ?? (args8.slug ? path38.join("src/creatives", slug, `${slug}.canvas.json`) : path38.join(path38.dirname(specPath), `${slug}.canvas.json`));
|
|
78604
78726
|
if (args8.slug) await enforceNewCreativeLimit(slug, outPath);
|
|
78605
78727
|
let outAr;
|
|
78606
78728
|
try {
|
|
@@ -78618,30 +78740,30 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78618
78740
|
return;
|
|
78619
78741
|
}
|
|
78620
78742
|
const outDims = canvasDims(outAr);
|
|
78621
|
-
const outDir =
|
|
78622
|
-
await
|
|
78623
|
-
const compositionDest =
|
|
78624
|
-
const captionsDest =
|
|
78743
|
+
const outDir = path38.dirname(outPath);
|
|
78744
|
+
await mkdir13(outDir, { recursive: true });
|
|
78745
|
+
const compositionDest = path38.join(outDir, "video-overlay-composition");
|
|
78746
|
+
const captionsDest = path38.join(outDir, "tiktok-captions-composition");
|
|
78625
78747
|
await cp(SHIPPED_OVERLAY_COMPOSITION_DIR, compositionDest, { recursive: true });
|
|
78626
78748
|
await cp(SHIPPED_CAPTIONS_DIR, captionsDest, { recursive: true });
|
|
78627
78749
|
await stampCompositionDims(compositionDest, outDims);
|
|
78628
78750
|
await stampCompositionDims(captionsDest, outDims);
|
|
78629
|
-
const captionsIndex =
|
|
78630
|
-
await
|
|
78751
|
+
const captionsIndex = path38.join(captionsDest, "index.html");
|
|
78752
|
+
await writeFile20(captionsIndex, injectAdCaptionPlacement(await readFile31(captionsIndex, "utf-8"), outAr), "utf-8");
|
|
78631
78753
|
const face = await stageBrandDisplayFace(outDir, captionsDest);
|
|
78632
78754
|
if (face) {
|
|
78633
|
-
await
|
|
78755
|
+
await writeFile20(captionsIndex, injectAdDisplayFace(await readFile31(captionsIndex, "utf-8"), face.file), "utf-8");
|
|
78634
78756
|
}
|
|
78635
|
-
const blueprintPath2 =
|
|
78636
|
-
const blueprintStylePath =
|
|
78757
|
+
const blueprintPath2 = path38.join(outDir, "prompt.json");
|
|
78758
|
+
const blueprintStylePath = path38.join(outDir, "prompt.style.json");
|
|
78637
78759
|
await writeSceneFiles(outDir, blueprint);
|
|
78638
|
-
await
|
|
78639
|
-
await
|
|
78760
|
+
await writeFile20(blueprintStylePath, renderStyleProjectionFromValue(blueprint), "utf8");
|
|
78761
|
+
await writeFile20(path38.join(outDir, AD_SPEC_FILE), `${JSON.stringify(specArtifact, null, 2)}
|
|
78640
78762
|
`, "utf8");
|
|
78641
78763
|
const logoPath = noBrand ? void 0 : spec.data.brand?.logo?.trim();
|
|
78642
|
-
const transcriptPath =
|
|
78764
|
+
const transcriptPath = path38.join(outDir, "transcript.json");
|
|
78643
78765
|
if (spec.data.voiceover === false) {
|
|
78644
|
-
await
|
|
78766
|
+
await writeFile20(transcriptPath, `${JSON.stringify(staticTranscript(spec.data, chosenVideoModel), null, 2)}
|
|
78645
78767
|
`, "utf-8");
|
|
78646
78768
|
}
|
|
78647
78769
|
const opts = {
|
|
@@ -78651,10 +78773,10 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78651
78773
|
// that stops the whole canvas. The same sheet model every other scaffold uses.
|
|
78652
78774
|
sheetModel: ACTOR_SHEET_MODEL,
|
|
78653
78775
|
videoModel: chosenVideoModel,
|
|
78654
|
-
overlayCompositionPath:
|
|
78655
|
-
captionsCompositionPath:
|
|
78656
|
-
blueprintPath:
|
|
78657
|
-
blueprintStylePath:
|
|
78776
|
+
overlayCompositionPath: path38.relative(outDir, compositionDest),
|
|
78777
|
+
captionsCompositionPath: path38.relative(outDir, captionsDest),
|
|
78778
|
+
blueprintPath: path38.relative(outDir, blueprintPath2),
|
|
78779
|
+
blueprintStylePath: path38.relative(outDir, blueprintStylePath),
|
|
78658
78780
|
aspect: spec.data.format.aspect_ratio,
|
|
78659
78781
|
resolution: spec.data.format.resolution,
|
|
78660
78782
|
// A beat is one clause and one card, so the cap has to clear a whole clause.
|
|
@@ -78693,17 +78815,17 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78693
78815
|
// the stock voice this exists to avoid. It is to convert the LATER extracts onto the
|
|
78694
78816
|
// first beat of the same speaker, which keeps a voice the clip really produced.
|
|
78695
78817
|
...avatar ? { keepRenderedVoice: true } : {},
|
|
78696
|
-
...spec.data.voiceover === false ? { staticTranscriptPath:
|
|
78818
|
+
...spec.data.voiceover === false ? { staticTranscriptPath: path38.relative(outDir, transcriptPath) } : {}
|
|
78697
78819
|
};
|
|
78698
78820
|
const canvas = scaffoldVideoCanvas(blueprint, adSpecCastElements(spec.data, avatar), opts);
|
|
78699
78821
|
const renderNode5 = canvas.nodes.find((n) => n.type === "hyperframe_render");
|
|
78700
|
-
const renderedDir = renderNode5 ?
|
|
78822
|
+
const renderedDir = renderNode5 ? path38.join(outDir, String(renderNode5.params?.composition ?? "")) : null;
|
|
78701
78823
|
let staged = null;
|
|
78702
78824
|
let markIsLight = false;
|
|
78703
78825
|
if (logoPath && renderedDir) {
|
|
78704
|
-
const ext =
|
|
78826
|
+
const ext = path38.extname(logoPath) || ".png";
|
|
78705
78827
|
try {
|
|
78706
|
-
await copyFile(logoPath,
|
|
78828
|
+
await copyFile(logoPath, path38.join(renderedDir, `brand-mark${ext}`));
|
|
78707
78829
|
staged = { file: `brand-mark${ext}`, alt: spec.data.brand?.name ?? "brand" };
|
|
78708
78830
|
markIsLight = await markInkIsLight(logoPath);
|
|
78709
78831
|
} catch {
|
|
@@ -78722,12 +78844,12 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78722
78844
|
plateIsDark: plateIsDark(spec.data.brand?.palette)
|
|
78723
78845
|
}) : "";
|
|
78724
78846
|
if (overlayHtml && renderedDir) {
|
|
78725
|
-
const indexPath =
|
|
78726
|
-
const html = await
|
|
78727
|
-
await
|
|
78847
|
+
const indexPath = path38.join(renderedDir, "index.html");
|
|
78848
|
+
const html = await readFile31(indexPath, "utf-8");
|
|
78849
|
+
await writeFile20(indexPath, injectAdBrandOverlay(html, overlayHtml, { aspect: outAr, ...face ? { face: AD_DISPLAY_FAMILY } : {} }), "utf-8");
|
|
78728
78850
|
}
|
|
78729
|
-
await
|
|
78730
|
-
|
|
78851
|
+
await writeFile20(
|
|
78852
|
+
path38.join(outDir, REBUILD_FILE),
|
|
78731
78853
|
`${JSON.stringify({ elements: adSpecCastElements(spec.data, avatar), opts }, null, 2)}
|
|
78732
78854
|
`,
|
|
78733
78855
|
"utf8"
|
|
@@ -78737,7 +78859,7 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78737
78859
|
const meta = canvas.metadata;
|
|
78738
78860
|
meta.todo = { ...meta.todo ?? {}, blocking_validation_issues: validation.issues };
|
|
78739
78861
|
}
|
|
78740
|
-
await
|
|
78862
|
+
await writeFile20(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
78741
78863
|
`, "utf-8");
|
|
78742
78864
|
if (!validation.ok) {
|
|
78743
78865
|
process.stderr.write(
|
|
@@ -78757,10 +78879,10 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78757
78879
|
return;
|
|
78758
78880
|
}
|
|
78759
78881
|
if (args8.slug) {
|
|
78760
|
-
const definitionPath =
|
|
78882
|
+
const definitionPath = path38.join(outDir, "_definition.md");
|
|
78761
78883
|
const definitionExists = await stat6(definitionPath).then(() => true, () => false);
|
|
78762
78884
|
if (!definitionExists) {
|
|
78763
|
-
await
|
|
78885
|
+
await writeFile20(
|
|
78764
78886
|
definitionPath,
|
|
78765
78887
|
buildCreativeDefinition({
|
|
78766
78888
|
title: titleFromSlug(slug),
|
|
@@ -78875,10 +78997,10 @@ async function fetchClientLogo() {
|
|
|
78875
78997
|
if (!response.ok) return { ok: false, reason: clientLogoMissNote({ use: false, reason: "no_hits" }, domain) };
|
|
78876
78998
|
const bytes = Buffer.from(await response.arrayBuffer());
|
|
78877
78999
|
const extension = /\.(svg|png|jpg|jpeg|webp)(?:\?|$)/i.exec(decision.url)?.[1]?.toLowerCase() ?? "svg";
|
|
78878
|
-
const dir =
|
|
78879
|
-
await
|
|
78880
|
-
const file =
|
|
78881
|
-
await
|
|
79000
|
+
const dir = path38.join(BRAND_DIR, "logos");
|
|
79001
|
+
await mkdir13(dir, { recursive: true });
|
|
79002
|
+
const file = path38.join(dir, `brand-mark.${extension}`);
|
|
79003
|
+
await writeFile20(file, bytes);
|
|
78882
79004
|
return { ok: true, path: file };
|
|
78883
79005
|
} catch {
|
|
78884
79006
|
return { ok: false, reason: clientLogoMissNote({ use: false, reason: "no_hits" }, domain) };
|
|
@@ -78896,14 +79018,14 @@ async function stageBrandDisplayFace(outDir, compositionDir) {
|
|
|
78896
79018
|
if (!face) return null;
|
|
78897
79019
|
try {
|
|
78898
79020
|
if ("path" in face.slot) {
|
|
78899
|
-
const file2 = `brand-face${
|
|
78900
|
-
await copyFile(face.slot.path,
|
|
78901
|
-
return { file: file2, label: `the font file \`${
|
|
79021
|
+
const file2 = `brand-face${path38.extname(face.slot.path).toLowerCase()}`;
|
|
79022
|
+
await copyFile(face.slot.path, path38.join(compositionDir, file2));
|
|
79023
|
+
return { file: file2, label: `the font file \`${path38.relative(root, face.slot.path)}\`` };
|
|
78902
79024
|
}
|
|
78903
79025
|
const { buffer } = await fetchExternalBytes(face.slot.url, { maxBytes: MAX_FONT_BYTES });
|
|
78904
79026
|
const extension = /\.(woff2|woff|ttf|otf)(?:\?|$)/i.exec(face.slot.url)?.[1]?.toLowerCase() ?? "woff2";
|
|
78905
79027
|
const file = `brand-face.${extension}`;
|
|
78906
|
-
await
|
|
79028
|
+
await writeFile20(path38.join(compositionDir, file), buffer);
|
|
78907
79029
|
return { file, label: `${face.family ?? "the brand's typeface"}, loaded as a web font` };
|
|
78908
79030
|
} catch {
|
|
78909
79031
|
return null;
|
|
@@ -78911,9 +79033,9 @@ async function stageBrandDisplayFace(outDir, compositionDir) {
|
|
|
78911
79033
|
}
|
|
78912
79034
|
|
|
78913
79035
|
// src/commands/canvas/scaffold-video.ts
|
|
78914
|
-
import { cp as cp2, mkdir as
|
|
79036
|
+
import { cp as cp2, mkdir as mkdir14, readFile as readFile34, rm as rm6, writeFile as writeFile21 } from "fs/promises";
|
|
78915
79037
|
import { tmpdir as tmpdir2 } from "os";
|
|
78916
|
-
import
|
|
79038
|
+
import path40 from "path";
|
|
78917
79039
|
import { defineCommand as defineCommand145 } from "citty";
|
|
78918
79040
|
|
|
78919
79041
|
// src/engine/scaffold/lib/model-router.ts
|
|
@@ -78953,7 +79075,7 @@ function routeVideoModel(input) {
|
|
|
78953
79075
|
|
|
78954
79076
|
// src/engine/nodes/local/lib/sceneDetect.ts
|
|
78955
79077
|
import { execFile as execFile2 } from "child_process";
|
|
78956
|
-
import { mkdtemp, readdir as readdir16, readFile as
|
|
79078
|
+
import { mkdtemp, readdir as readdir16, readFile as readFile32, rm as rm5 } from "fs/promises";
|
|
78957
79079
|
import { tmpdir } from "os";
|
|
78958
79080
|
import { join as join3 } from "path";
|
|
78959
79081
|
import { promisify as promisify2 } from "util";
|
|
@@ -79029,7 +79151,7 @@ async function runSceneDetectOnce(filePath, threshold, minSceneLenS, timeoutMs)
|
|
|
79029
79151
|
);
|
|
79030
79152
|
const csvName = (await readdir16(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
|
|
79031
79153
|
if (!csvName) return [];
|
|
79032
|
-
return parsePySceneDetectCsvCuts(await
|
|
79154
|
+
return parsePySceneDetectCsvCuts(await readFile32(join3(outDir, csvName), "utf-8"));
|
|
79033
79155
|
} finally {
|
|
79034
79156
|
await rm5(outDir, { recursive: true, force: true });
|
|
79035
79157
|
}
|
|
@@ -79053,8 +79175,8 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
|
|
|
79053
79175
|
}
|
|
79054
79176
|
|
|
79055
79177
|
// src/commands/canvas/gitignore.ts
|
|
79056
|
-
import { appendFile, readFile as
|
|
79057
|
-
import
|
|
79178
|
+
import { appendFile, readFile as readFile33 } from "fs/promises";
|
|
79179
|
+
import path39 from "path";
|
|
79058
79180
|
function missingGitignoreEntries(existing, entries) {
|
|
79059
79181
|
const present2 = new Set(
|
|
79060
79182
|
existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
|
|
@@ -79062,10 +79184,10 @@ function missingGitignoreEntries(existing, entries) {
|
|
|
79062
79184
|
return entries.filter((e) => !present2.has(e.trim().replace(/\/+$/, "")));
|
|
79063
79185
|
}
|
|
79064
79186
|
async function ensureGitignore(dir, entries) {
|
|
79065
|
-
const file =
|
|
79187
|
+
const file = path39.join(dir, ".gitignore");
|
|
79066
79188
|
let existing;
|
|
79067
79189
|
try {
|
|
79068
|
-
existing = await
|
|
79190
|
+
existing = await readFile33(file, "utf8");
|
|
79069
79191
|
} catch {
|
|
79070
79192
|
return;
|
|
79071
79193
|
}
|
|
@@ -79107,7 +79229,7 @@ async function loadTranscriptBestEffort(ref2) {
|
|
|
79107
79229
|
async function stageCaptions(outDir, transcript) {
|
|
79108
79230
|
const text3 = transcript?.trim();
|
|
79109
79231
|
if (!text3 || text3 === "[]") return {};
|
|
79110
|
-
const compositionPath =
|
|
79232
|
+
const compositionPath = path40.join(outDir, "tiktok-captions-composition");
|
|
79111
79233
|
await cp2(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
|
|
79112
79234
|
return { compositionPath };
|
|
79113
79235
|
}
|
|
@@ -79143,7 +79265,7 @@ var VIDEO_EXT_BY_MIME = {
|
|
|
79143
79265
|
"video/x-matroska": ".mkv"
|
|
79144
79266
|
};
|
|
79145
79267
|
function referenceVideoExt(url, contentType) {
|
|
79146
|
-
const fromPath =
|
|
79268
|
+
const fromPath = path40.extname(new URL(url).pathname).toLowerCase();
|
|
79147
79269
|
if (fromPath && fromPath.length <= 5) return fromPath;
|
|
79148
79270
|
const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase();
|
|
79149
79271
|
return mime && VIDEO_EXT_BY_MIME[mime] || ".mp4";
|
|
@@ -79169,7 +79291,7 @@ function framesModeReport(canvas, requested) {
|
|
|
79169
79291
|
return { frames_mode: mode, ...refused ? { frames_reuse_refused: refused } : {} };
|
|
79170
79292
|
}
|
|
79171
79293
|
async function materializeReferenceVideo(fileArg2) {
|
|
79172
|
-
if (!/^https?:\/\//i.test(fileArg2)) return
|
|
79294
|
+
if (!/^https?:\/\//i.test(fileArg2)) return path40.resolve(fileArg2);
|
|
79173
79295
|
let bytes;
|
|
79174
79296
|
let contentType;
|
|
79175
79297
|
try {
|
|
@@ -79181,11 +79303,11 @@ async function materializeReferenceVideo(fileArg2) {
|
|
|
79181
79303
|
throw new Error(`failed to download reference video: ${e instanceof Error ? e.message : String(e)}`);
|
|
79182
79304
|
}
|
|
79183
79305
|
if (bytes.length === 0) throw new Error("reference video download was empty");
|
|
79184
|
-
const dest =
|
|
79306
|
+
const dest = path40.join(
|
|
79185
79307
|
tmpdir2(),
|
|
79186
79308
|
`baker-ref-${sha256Hex(bytes).slice(0, 16)}${referenceVideoExt(fileArg2, contentType)}`
|
|
79187
79309
|
);
|
|
79188
|
-
await
|
|
79310
|
+
await writeFile21(dest, bytes);
|
|
79189
79311
|
return dest;
|
|
79190
79312
|
}
|
|
79191
79313
|
function resolveSeamDedup(raw) {
|
|
@@ -79408,12 +79530,12 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79408
79530
|
} catch (e) {
|
|
79409
79531
|
return fail4("download", e instanceof Error ? e.message : String(e));
|
|
79410
79532
|
}
|
|
79411
|
-
const base =
|
|
79412
|
-
const outPath = args8.out ?
|
|
79533
|
+
const base = path40.basename(videoPath, path40.extname(videoPath));
|
|
79534
|
+
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
79535
|
if (slug) await enforceNewCreativeLimit(slug, outPath);
|
|
79414
|
-
const outDir =
|
|
79415
|
-
const blueprintPath2 =
|
|
79416
|
-
const blueprintStylePath =
|
|
79536
|
+
const outDir = path40.dirname(outPath);
|
|
79537
|
+
const blueprintPath2 = path40.join(outDir, "prompt.json");
|
|
79538
|
+
const blueprintStylePath = path40.join(outDir, "prompt.style.json");
|
|
79417
79539
|
const frames = args8.frames === "reuse" ? "reuse" : "generate";
|
|
79418
79540
|
const maxScenes = args8["max-scenes"] ? Number(args8["max-scenes"]) : void 0;
|
|
79419
79541
|
if (Number.isFinite(maxScenes)) {
|
|
@@ -79461,10 +79583,10 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79461
79583
|
process.stderr.write(`\u{1F3AC} Photoreal on-camera cast detected \u2192 generating clips on ${videoModel} (dodges Seedance's real-person filter).
|
|
79462
79584
|
`);
|
|
79463
79585
|
}
|
|
79464
|
-
await
|
|
79586
|
+
await mkdir14(outDir, { recursive: true });
|
|
79465
79587
|
const annotated = annotateBlueprintWithElements(blueprint, elements);
|
|
79466
79588
|
await writeSceneFiles(outDir, annotated);
|
|
79467
|
-
await
|
|
79589
|
+
await writeFile21(blueprintStylePath, renderStyleProjectionFromValue(annotated), "utf8");
|
|
79468
79590
|
let aspect;
|
|
79469
79591
|
try {
|
|
79470
79592
|
aspect = resolveAspect(
|
|
@@ -79482,12 +79604,12 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79482
79604
|
`
|
|
79483
79605
|
);
|
|
79484
79606
|
}
|
|
79485
|
-
const compositionDest =
|
|
79607
|
+
const compositionDest = path40.join(outDir, "video-overlay-composition");
|
|
79486
79608
|
await cp2(SHIPPED_OVERLAY_COMPOSITION_DIR, compositionDest, { recursive: true });
|
|
79487
79609
|
await stampCompositionDims(compositionDest, outDims);
|
|
79488
|
-
const indexPath =
|
|
79610
|
+
const indexPath = path40.join(compositionDest, "index.html");
|
|
79489
79611
|
const overlayHtml = buildOverlayHtml(blueprint, { captionsActive: Boolean(transcript), videoModel });
|
|
79490
|
-
const indexHtml = await
|
|
79612
|
+
const indexHtml = await readFile34(indexPath, "utf8");
|
|
79491
79613
|
const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
|
|
79492
79614
|
if (injected === indexHtml && overlayHtml.trim()) {
|
|
79493
79615
|
fail4(
|
|
@@ -79495,16 +79617,16 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79495
79617
|
`video-overlay-composition/index.html is missing the <!--OVERLAYS--> marker \u2014 cannot inject the overlay layer`
|
|
79496
79618
|
);
|
|
79497
79619
|
}
|
|
79498
|
-
await
|
|
79620
|
+
await writeFile21(indexPath, injected, "utf8");
|
|
79499
79621
|
const captions = await stageCaptions(outDir, transcript);
|
|
79500
79622
|
if (captions.compositionPath) await stampCompositionDims(captions.compositionPath, outDims);
|
|
79501
79623
|
const opts = {
|
|
79502
79624
|
imageModel,
|
|
79503
79625
|
videoModel,
|
|
79504
|
-
overlayCompositionPath:
|
|
79505
|
-
captionsCompositionPath: captions.compositionPath ?
|
|
79506
|
-
blueprintPath:
|
|
79507
|
-
blueprintStylePath:
|
|
79626
|
+
overlayCompositionPath: path40.relative(outDir, compositionDest),
|
|
79627
|
+
captionsCompositionPath: captions.compositionPath ? path40.relative(outDir, captions.compositionPath) : void 0,
|
|
79628
|
+
blueprintPath: path40.relative(outDir, blueprintPath2),
|
|
79629
|
+
blueprintStylePath: path40.relative(outDir, blueprintStylePath),
|
|
79508
79630
|
frames,
|
|
79509
79631
|
ambient: Boolean(args8.ambient),
|
|
79510
79632
|
seamDedup: resolveSeamDedup(args8["seam-dedup"]),
|
|
@@ -79529,10 +79651,10 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79529
79651
|
todo.blocking_validation_issues = validation.issues;
|
|
79530
79652
|
meta.todo = todo;
|
|
79531
79653
|
}
|
|
79532
|
-
await
|
|
79654
|
+
await writeFile21(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
79533
79655
|
`, "utf8");
|
|
79534
|
-
await
|
|
79535
|
-
|
|
79656
|
+
await writeFile21(
|
|
79657
|
+
path40.join(outDir, REBUILD_FILE),
|
|
79536
79658
|
`${JSON.stringify({ elements, opts }, null, 2)}
|
|
79537
79659
|
`,
|
|
79538
79660
|
"utf8"
|
|
@@ -79559,9 +79681,9 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79559
79681
|
await ensureGitignore(process.cwd(), ["canvas/", ".context/"]);
|
|
79560
79682
|
const sourceRef = videoSourceReference(blueprint, fileArg2);
|
|
79561
79683
|
if (slug) {
|
|
79562
|
-
const definitionPath =
|
|
79684
|
+
const definitionPath = path40.join(outDir, "_definition.md");
|
|
79563
79685
|
if (!await fileExists(definitionPath)) {
|
|
79564
|
-
await
|
|
79686
|
+
await writeFile21(
|
|
79565
79687
|
definitionPath,
|
|
79566
79688
|
buildCreativeDefinition({
|
|
79567
79689
|
title: titleFromSlug(slug),
|
|
@@ -79615,7 +79737,7 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79615
79737
|
graph: canvas.metadata?.video?.graph_stats
|
|
79616
79738
|
},
|
|
79617
79739
|
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 \`${
|
|
79740
|
+
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
79741
|
recurring_elements_to_supply: report.elements,
|
|
79620
79742
|
voices_to_confirm: report.dialogue.map((d) => ({
|
|
79621
79743
|
scene: d.scene,
|
|
@@ -79652,8 +79774,8 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79652
79774
|
});
|
|
79653
79775
|
|
|
79654
79776
|
// src/commands/canvas/set-prompt.ts
|
|
79655
|
-
import { readFile as
|
|
79656
|
-
import
|
|
79777
|
+
import { readFile as readFile35, writeFile as writeFile22 } from "fs/promises";
|
|
79778
|
+
import path41 from "path";
|
|
79657
79779
|
import { defineCommand as defineCommand146 } from "citty";
|
|
79658
79780
|
function setNodePrompt(canvas, nodeId, text3) {
|
|
79659
79781
|
const nodes = canvas?.nodes;
|
|
@@ -79682,8 +79804,8 @@ var SetPromptError = class extends Error {
|
|
|
79682
79804
|
}
|
|
79683
79805
|
};
|
|
79684
79806
|
async function setPromptInCanvasFile(canvasPath, nodeId, text3) {
|
|
79685
|
-
const filePath =
|
|
79686
|
-
const original = await
|
|
79807
|
+
const filePath = path41.resolve(canvasPath);
|
|
79808
|
+
const original = await readFile35(filePath, "utf8");
|
|
79687
79809
|
let canvas;
|
|
79688
79810
|
try {
|
|
79689
79811
|
canvas = JSON.parse(original);
|
|
@@ -79705,7 +79827,7 @@ async function setPromptInCanvasFile(canvasPath, nodeId, text3) {
|
|
|
79705
79827
|
"projected_prompt",
|
|
79706
79828
|
`The prompt of "${nodeId}" is compiled from prompt.json on every validate and run, so setting it in the canvas would be overwritten.`,
|
|
79707
79829
|
{
|
|
79708
|
-
fix: `Edit the ad in ${
|
|
79830
|
+
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
79831
|
}
|
|
79710
79832
|
);
|
|
79711
79833
|
}
|
|
@@ -79715,9 +79837,9 @@ async function setPromptInCanvasFile(canvasPath, nodeId, text3) {
|
|
|
79715
79837
|
} catch (e) {
|
|
79716
79838
|
throw new SetPromptError("node_not_found", e instanceof Error ? e.message : String(e));
|
|
79717
79839
|
}
|
|
79718
|
-
await
|
|
79840
|
+
await writeFile22(filePath, `${JSON.stringify(updated, null, 2)}
|
|
79719
79841
|
`, "utf8");
|
|
79720
|
-
const restore = () =>
|
|
79842
|
+
const restore = () => writeFile22(filePath, original, "utf8");
|
|
79721
79843
|
try {
|
|
79722
79844
|
const prepared = await prepareCanvas(filePath);
|
|
79723
79845
|
const validation = await validateCanvasDeep(prepared.canvas, defaultRegistry());
|
|
@@ -79759,7 +79881,7 @@ var setPromptCommand = defineCommand146({
|
|
|
79759
79881
|
},
|
|
79760
79882
|
async run({ args: args8 }) {
|
|
79761
79883
|
let text3;
|
|
79762
|
-
if (args8["text-file"]) text3 = await
|
|
79884
|
+
if (args8["text-file"]) text3 = await readFile35(path41.resolve(String(args8["text-file"])), "utf8");
|
|
79763
79885
|
else if (args8.text !== void 0) text3 = String(args8.text);
|
|
79764
79886
|
else {
|
|
79765
79887
|
process.stderr.write(
|
|
@@ -79816,7 +79938,7 @@ var staleCommand = defineCommand147({
|
|
|
79816
79938
|
});
|
|
79817
79939
|
|
|
79818
79940
|
// src/commands/canvas/validate.ts
|
|
79819
|
-
import
|
|
79941
|
+
import path42 from "path";
|
|
79820
79942
|
import { defineCommand as defineCommand148 } from "citty";
|
|
79821
79943
|
var validateCommand = defineCommand148({
|
|
79822
79944
|
meta: {
|
|
@@ -79825,7 +79947,7 @@ var validateCommand = defineCommand148({
|
|
|
79825
79947
|
},
|
|
79826
79948
|
args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
|
|
79827
79949
|
async run({ args: args8 }) {
|
|
79828
|
-
const filePath =
|
|
79950
|
+
const filePath = path42.resolve(String(args8.file));
|
|
79829
79951
|
let prepared;
|
|
79830
79952
|
try {
|
|
79831
79953
|
prepared = await prepareCanvas(filePath, { gate: true, log: (line) => process.stderr.write(`${line}
|
|
@@ -80261,8 +80383,8 @@ import { defineCommand as defineCommand162 } from "citty";
|
|
|
80261
80383
|
|
|
80262
80384
|
// src/commands/creatives/brief.ts
|
|
80263
80385
|
import { readFileSync as readFileSync17 } from "fs";
|
|
80264
|
-
import { readdir as readdir17, readFile as
|
|
80265
|
-
import
|
|
80386
|
+
import { readdir as readdir17, readFile as readFile36 } from "fs/promises";
|
|
80387
|
+
import path44 from "path";
|
|
80266
80388
|
import { defineCommand as defineCommand155 } from "citty";
|
|
80267
80389
|
|
|
80268
80390
|
// src/commands/winning-ads/search.ts
|
|
@@ -80520,8 +80642,8 @@ var FLAG_FOR_FIELD = {
|
|
|
80520
80642
|
first_seen_after: "--first-seen-after",
|
|
80521
80643
|
first_seen_before: "--first-seen-before"
|
|
80522
80644
|
};
|
|
80523
|
-
function flagFor(
|
|
80524
|
-
const field = String(
|
|
80645
|
+
function flagFor(path68) {
|
|
80646
|
+
const field = String(path68[0] === "hard_filters" ? path68[1] : path68[0]);
|
|
80525
80647
|
return FLAG_FOR_FIELD[field] ?? field;
|
|
80526
80648
|
}
|
|
80527
80649
|
function schemaRejection(body) {
|
|
@@ -80932,7 +81054,7 @@ function ideasHints(report) {
|
|
|
80932
81054
|
if (next.length === 1) hints2.push(`Build: \`baker creatives make ${next[0]}\`.`);
|
|
80933
81055
|
if (next.length > 1)
|
|
80934
81056
|
hints2.push(
|
|
80935
|
-
`Build ${next.join(", ")} at once: one \`creative-producer\` per idea, all spawned in one message, each given its idea id. Never build them one after another: each render takes
|
|
81057
|
+
`Build ${next.join(", ")} at once: one \`creative-producer\` per idea, all spawned in one message in the background, each given its idea id, and handle each as it returns. Never build them one after another: each render takes minutes.`
|
|
80936
81058
|
);
|
|
80937
81059
|
return hints2;
|
|
80938
81060
|
}
|
|
@@ -80993,7 +81115,7 @@ var ideasCommand = defineCommand153({
|
|
|
80993
81115
|
|
|
80994
81116
|
// src/commands/creatives/performance.ts
|
|
80995
81117
|
import { readFileSync as readFileSync16 } from "fs";
|
|
80996
|
-
import
|
|
81118
|
+
import path43 from "path";
|
|
80997
81119
|
import { defineCommand as defineCommand154 } from "citty";
|
|
80998
81120
|
import { parse as parseYaml4 } from "yaml";
|
|
80999
81121
|
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 +81179,7 @@ function definitionInsights(markdown) {
|
|
|
81057
81179
|
}
|
|
81058
81180
|
function readLocalDefinition(slug) {
|
|
81059
81181
|
try {
|
|
81060
|
-
return readFileSync16(
|
|
81182
|
+
return readFileSync16(path43.join(process.cwd(), "src", "creatives", slug, "_definition.md"), "utf8");
|
|
81061
81183
|
} catch {
|
|
81062
81184
|
return null;
|
|
81063
81185
|
}
|
|
@@ -81169,11 +81291,11 @@ function isAvailable(value2) {
|
|
|
81169
81291
|
return !(value2 !== null && typeof value2 === "object" && "unavailable" in value2);
|
|
81170
81292
|
}
|
|
81171
81293
|
async function companyStatus(root) {
|
|
81172
|
-
const doc = await
|
|
81294
|
+
const doc = await readFile36(path44.join(root, "COMPANY.md"), "utf8").catch(() => "");
|
|
81173
81295
|
return doc.trim() && !/^#\s*Company\s*\n+\s*TODO/m.test(doc) ? "ready" : "missing";
|
|
81174
81296
|
}
|
|
81175
81297
|
async function readOffers(root) {
|
|
81176
|
-
const dir =
|
|
81298
|
+
const dir = path44.join(root, "src", "content", "offers");
|
|
81177
81299
|
const files = (await readdir17(dir, { recursive: true }).catch(() => [])).filter(
|
|
81178
81300
|
(f) => f.endsWith(".md")
|
|
81179
81301
|
);
|
|
@@ -81181,7 +81303,7 @@ async function readOffers(root) {
|
|
|
81181
81303
|
}
|
|
81182
81304
|
function readDefinition(root, slug) {
|
|
81183
81305
|
try {
|
|
81184
|
-
return readFileSync17(
|
|
81306
|
+
return readFileSync17(path44.join(root, "src", "creatives", slug, "_definition.md"), "utf8");
|
|
81185
81307
|
} catch {
|
|
81186
81308
|
return null;
|
|
81187
81309
|
}
|
|
@@ -81202,7 +81324,7 @@ function marketQuery(companyDoc) {
|
|
|
81202
81324
|
return prose.slice(0, 240);
|
|
81203
81325
|
}
|
|
81204
81326
|
async function readMarket(root) {
|
|
81205
|
-
const query = marketQuery(await
|
|
81327
|
+
const query = marketQuery(await readFile36(path44.join(root, "COMPANY.md"), "utf8").catch(() => ""));
|
|
81206
81328
|
if (!query) return null;
|
|
81207
81329
|
const search = (body) => apiPost("/api/ad-library/winners/search", body);
|
|
81208
81330
|
const followed = await search(
|
|
@@ -81269,7 +81391,7 @@ function briefHints(parts) {
|
|
|
81269
81391
|
}
|
|
81270
81392
|
if (parts.customers.thin) {
|
|
81271
81393
|
hints2.push(
|
|
81272
|
-
`Only ${parts.customers.observed} customer quotes on file: run \`baker research customers\` before writing ideas, so they stand on what customers said.`
|
|
81394
|
+
`Only ${parts.customers.observed} customer quotes on file: run \`baker research customers\` now, before writing ideas, so they stand on what customers said. It gathers reviews, Reddit and the web and writes the quotes with their sources in about a minute; never a subagent or web searches of your own for this.`
|
|
81273
81395
|
);
|
|
81274
81396
|
}
|
|
81275
81397
|
if (parts.bottleneck && isAvailable(parts.bottleneck) && parts.bottleneck.verdict === "after_click") {
|
|
@@ -81538,8 +81660,8 @@ var feedbackCommand = defineCommand157({
|
|
|
81538
81660
|
|
|
81539
81661
|
// src/commands/creatives/learn.ts
|
|
81540
81662
|
import { readFileSync as readFileSync18 } from "fs";
|
|
81541
|
-
import { readFile as
|
|
81542
|
-
import
|
|
81663
|
+
import { readFile as readFile37, writeFile as writeFile23 } from "fs/promises";
|
|
81664
|
+
import path45 from "path";
|
|
81543
81665
|
import { defineCommand as defineCommand158 } from "citty";
|
|
81544
81666
|
|
|
81545
81667
|
// src/commands/creatives/learn-model.ts
|
|
@@ -81761,14 +81883,14 @@ function learnEnvelope(learnings, written, unreadable = []) {
|
|
|
81761
81883
|
}
|
|
81762
81884
|
async function writeBack(root, learnings) {
|
|
81763
81885
|
for (const learning of learnings) {
|
|
81764
|
-
const file =
|
|
81765
|
-
const raw = await
|
|
81766
|
-
await
|
|
81886
|
+
const file = path45.join(root, IDEAS_DIR, `${learning.idea}.md`);
|
|
81887
|
+
const raw = await readFile37(file, "utf8");
|
|
81888
|
+
await writeFile23(file, setDefinitionFields(raw, { status: learning.to, result: learning.result }), "utf8");
|
|
81767
81889
|
}
|
|
81768
81890
|
}
|
|
81769
81891
|
function readDefinition2(root, slug) {
|
|
81770
81892
|
try {
|
|
81771
|
-
return readFileSync18(
|
|
81893
|
+
return readFileSync18(path45.join(root, "src", "creatives", slug, "_definition.md"), "utf8");
|
|
81772
81894
|
} catch {
|
|
81773
81895
|
return null;
|
|
81774
81896
|
}
|
|
@@ -81806,9 +81928,9 @@ var learnCommand = defineCommand158({
|
|
|
81806
81928
|
|
|
81807
81929
|
// src/commands/creatives/make.ts
|
|
81808
81930
|
import { execFile as execFile3 } from "child_process";
|
|
81809
|
-
import { mkdtemp as mkdtemp2, readFile as
|
|
81931
|
+
import { mkdtemp as mkdtemp2, readFile as readFile38, rm as rm7, writeFile as writeFile24 } from "fs/promises";
|
|
81810
81932
|
import { tmpdir as tmpdir3 } from "os";
|
|
81811
|
-
import
|
|
81933
|
+
import path46 from "path";
|
|
81812
81934
|
import { promisify as promisify3 } from "util";
|
|
81813
81935
|
import { defineCommand as defineCommand159 } from "citty";
|
|
81814
81936
|
var run2 = promisify3(execFile3);
|
|
@@ -82013,9 +82135,9 @@ async function scaffoldIdea(root, idea, args8, slug, temperature) {
|
|
|
82013
82135
|
return { scaffold, reference };
|
|
82014
82136
|
}
|
|
82015
82137
|
async function scaffoldFromIdea(idea, slug, viewerFlags) {
|
|
82016
|
-
const dir = await mkdtemp2(
|
|
82017
|
-
const specPath =
|
|
82018
|
-
await
|
|
82138
|
+
const dir = await mkdtemp2(path46.join(tmpdir3(), "idea-spec-"));
|
|
82139
|
+
const specPath = path46.join(dir, "prompt.json");
|
|
82140
|
+
await writeFile24(specPath, JSON.stringify(ideaSpec(idea, idea.who.replace(/[-_]/g, " ")), null, 2), "utf8");
|
|
82019
82141
|
try {
|
|
82020
82142
|
return await baker([
|
|
82021
82143
|
"canvas",
|
|
@@ -82033,19 +82155,19 @@ async function scaffoldFromIdea(idea, slug, viewerFlags) {
|
|
|
82033
82155
|
}
|
|
82034
82156
|
}
|
|
82035
82157
|
async function recordIdea(root, slug, idea, evidence) {
|
|
82036
|
-
const dir =
|
|
82037
|
-
const definitionPath =
|
|
82038
|
-
const definition = await
|
|
82158
|
+
const dir = path46.join(root, "src", "creatives", slug);
|
|
82159
|
+
const definitionPath = path46.join(dir, "_definition.md");
|
|
82160
|
+
const definition = await readFile38(definitionPath, "utf8").catch(() => null);
|
|
82039
82161
|
if (definition !== null) {
|
|
82040
|
-
await
|
|
82162
|
+
await writeFile24(definitionPath, setDefinitionFields(definition, { ...definitionFieldsOf(idea, evidence) }), "utf8");
|
|
82041
82163
|
}
|
|
82042
|
-
const promptPath =
|
|
82043
|
-
const prompt = isVideoIdea(idea) ? null : await
|
|
82044
|
-
if (prompt !== null) await
|
|
82164
|
+
const promptPath = path46.join(dir, "prompt.json");
|
|
82165
|
+
const prompt = isVideoIdea(idea) ? null : await readFile38(promptPath, "utf8").catch(() => null);
|
|
82166
|
+
if (prompt !== null) await writeFile24(promptPath, setSpecMessage(prompt, idea.message), "utf8").catch(() => void 0);
|
|
82045
82167
|
const unsourcedCopy = prompt === null ? [] : await draftedClaims(root, prompt);
|
|
82046
82168
|
if (idea.status === "idea") {
|
|
82047
|
-
const ideaPath =
|
|
82048
|
-
await
|
|
82169
|
+
const ideaPath = path46.join(root, IDEAS_DIR, `${idea.id}.md`);
|
|
82170
|
+
await writeFile24(ideaPath, setDefinitionFields(await readFile38(ideaPath, "utf8"), { status: "built" }), "utf8");
|
|
82049
82171
|
}
|
|
82050
82172
|
await baker(["canvas", "sync-definition", dir]);
|
|
82051
82173
|
return { dir, unsourcedCopy };
|
|
@@ -82131,7 +82253,7 @@ var makeCommand = defineCommand159({
|
|
|
82131
82253
|
reference,
|
|
82132
82254
|
...fromIdea ? { fromIdea } : {},
|
|
82133
82255
|
viewer: opts.adset ? { adset: opts.adset } : { temperature },
|
|
82134
|
-
canvasPath:
|
|
82256
|
+
canvasPath: path46.relative(root, String(scaffold.canvas_path ?? path46.join(dir, `${slug}.canvas.json`))),
|
|
82135
82257
|
scaffoldHints: Array.isArray(scaffold.hints) ? scaffold.hints : [],
|
|
82136
82258
|
unsourcedCopy
|
|
82137
82259
|
})
|
|
@@ -82144,7 +82266,7 @@ import { Buffer as Buffer2 } from "buffer";
|
|
|
82144
82266
|
import { defineCommand as defineCommand160 } from "citty";
|
|
82145
82267
|
|
|
82146
82268
|
// src/commands/images/api.ts
|
|
82147
|
-
import { readFile as
|
|
82269
|
+
import { readFile as readFile39 } from "fs/promises";
|
|
82148
82270
|
import { basename, extname } from "path";
|
|
82149
82271
|
var imageProcessingTimeoutMs = 18e4;
|
|
82150
82272
|
var imageReadyPollIntervalMs = 2e3;
|
|
@@ -82158,7 +82280,7 @@ var mimeMap = {
|
|
|
82158
82280
|
".avif": "image/avif"
|
|
82159
82281
|
};
|
|
82160
82282
|
var defaultImageApiDeps = {
|
|
82161
|
-
readFile:
|
|
82283
|
+
readFile: readFile39,
|
|
82162
82284
|
post: apiPost,
|
|
82163
82285
|
get: apiGet,
|
|
82164
82286
|
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
|
|
@@ -82224,7 +82346,7 @@ async function waitForReadyImage(deps, imageId, opts = {}) {
|
|
|
82224
82346
|
}
|
|
82225
82347
|
|
|
82226
82348
|
// src/commands/videos/api.ts
|
|
82227
|
-
import { readFile as
|
|
82349
|
+
import { readFile as readFile40 } from "fs/promises";
|
|
82228
82350
|
import { basename as basename2, extname as extname2 } from "path";
|
|
82229
82351
|
var videoProcessingTimeoutMs = 18e4;
|
|
82230
82352
|
var videoReadyPollIntervalMs = 3e3;
|
|
@@ -82236,7 +82358,7 @@ var mimeMap2 = {
|
|
|
82236
82358
|
".mkv": "video/x-matroska"
|
|
82237
82359
|
};
|
|
82238
82360
|
var defaultVideoApiDeps = {
|
|
82239
|
-
readFile:
|
|
82361
|
+
readFile: readFile40,
|
|
82240
82362
|
post: apiPost,
|
|
82241
82363
|
get: apiGet,
|
|
82242
82364
|
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
|
|
@@ -82671,7 +82793,7 @@ Full guide: __tooling__/docs/tools/baker/creatives.md`
|
|
|
82671
82793
|
});
|
|
82672
82794
|
|
|
82673
82795
|
// ../api/src/credentials/contract.ts
|
|
82674
|
-
import { z as
|
|
82796
|
+
import { z as z70 } from "zod";
|
|
82675
82797
|
|
|
82676
82798
|
// ../../.pnpm-store/v10/links/@/tldts-core/7.4.15/00637277cac46663a792d6ddb7fbb80e2995ea75c3d789d599e3f6df41962ed7/node_modules/tldts-core/dist/es6/src/domain.js
|
|
82677
82799
|
function shareSameDomainSuffix(hostname, vhost) {
|
|
@@ -83490,8 +83612,8 @@ var WHOLE_PLACEHOLDER_RE = new RegExp(`^${PLACEHOLDER_SOURCE}$`);
|
|
|
83490
83612
|
// ../api/src/credentials/contract.ts
|
|
83491
83613
|
var MAX_CREDENTIAL_VALUE_LENGTH = 16384;
|
|
83492
83614
|
var MAX_CREDENTIAL_HOSTS = 20;
|
|
83493
|
-
var credentialNameSchema =
|
|
83494
|
-
var credentialHostsSchema =
|
|
83615
|
+
var credentialNameSchema = z70.string().regex(new RegExp(`^${CREDENTIAL_NAME_PATTERN}$`), "Use lowercase letters, digits, - and _ (max 64).");
|
|
83616
|
+
var credentialHostsSchema = z70.array(z70.string()).max(MAX_CREDENTIAL_HOSTS).transform((hosts, ctx) => {
|
|
83495
83617
|
const patterns = [];
|
|
83496
83618
|
for (const host of hosts) {
|
|
83497
83619
|
const result = validateHostPattern(host);
|
|
@@ -83500,34 +83622,34 @@ var credentialHostsSchema = z69.array(z69.string()).max(MAX_CREDENTIAL_HOSTS).tr
|
|
|
83500
83622
|
}
|
|
83501
83623
|
return [...new Set(patterns)];
|
|
83502
83624
|
});
|
|
83503
|
-
var credentialSourceSchema =
|
|
83504
|
-
var credentialSummarySchema =
|
|
83505
|
-
name:
|
|
83506
|
-
label:
|
|
83507
|
-
placeholder:
|
|
83508
|
-
allowedHosts:
|
|
83625
|
+
var credentialSourceSchema = z70.enum(["user", "agent", "chat-detected", "requested"]);
|
|
83626
|
+
var credentialSummarySchema = z70.object({
|
|
83627
|
+
name: z70.string(),
|
|
83628
|
+
label: z70.string(),
|
|
83629
|
+
placeholder: z70.string(),
|
|
83630
|
+
allowedHosts: z70.array(z70.string()),
|
|
83509
83631
|
source: credentialSourceSchema,
|
|
83510
|
-
lastUsedAt:
|
|
83632
|
+
lastUsedAt: z70.number().nullable()
|
|
83511
83633
|
});
|
|
83512
|
-
var credentialsListResponseSchema =
|
|
83513
|
-
var credentialsPutRequestSchema =
|
|
83634
|
+
var credentialsListResponseSchema = z70.object({ credentials: z70.array(credentialSummarySchema) });
|
|
83635
|
+
var credentialsPutRequestSchema = z70.object({
|
|
83514
83636
|
name: credentialNameSchema,
|
|
83515
|
-
label:
|
|
83516
|
-
value:
|
|
83637
|
+
label: z70.string().trim().min(1).max(80).optional(),
|
|
83638
|
+
value: z70.string().min(1).max(MAX_CREDENTIAL_VALUE_LENGTH),
|
|
83517
83639
|
allowedHosts: credentialHostsSchema
|
|
83518
83640
|
});
|
|
83519
|
-
var credentialsPutResponseSchema =
|
|
83520
|
-
name:
|
|
83521
|
-
placeholder:
|
|
83522
|
-
replaced:
|
|
83641
|
+
var credentialsPutResponseSchema = z70.object({
|
|
83642
|
+
name: z70.string(),
|
|
83643
|
+
placeholder: z70.string(),
|
|
83644
|
+
replaced: z70.boolean()
|
|
83523
83645
|
});
|
|
83524
|
-
var credentialsDeleteRequestSchema =
|
|
83525
|
-
var credentialsDeleteResponseSchema =
|
|
83526
|
-
var brokerResolveResponseSchema =
|
|
83527
|
-
credentials:
|
|
83646
|
+
var credentialsDeleteRequestSchema = z70.object({ name: credentialNameSchema });
|
|
83647
|
+
var credentialsDeleteResponseSchema = z70.object({ name: z70.string(), deleted: z70.literal(true) });
|
|
83648
|
+
var brokerResolveResponseSchema = z70.object({
|
|
83649
|
+
credentials: z70.array(z70.object({ name: z70.string(), value: z70.string(), allowedHosts: z70.array(z70.string()) }))
|
|
83528
83650
|
});
|
|
83529
|
-
var brokerUsageRequestSchema =
|
|
83530
|
-
uses:
|
|
83651
|
+
var brokerUsageRequestSchema = z70.object({
|
|
83652
|
+
uses: z70.array(z70.object({ name: credentialNameSchema, host: z70.string().max(253) })).max(100)
|
|
83531
83653
|
});
|
|
83532
83654
|
|
|
83533
83655
|
// ../api/src/credentials/rules.ts
|
|
@@ -83878,12 +84000,12 @@ Full guide: ${DOC}`
|
|
|
83878
84000
|
import { defineCommand as defineCommand164 } from "citty";
|
|
83879
84001
|
|
|
83880
84002
|
// src/commands/experiment/composition.ts
|
|
83881
|
-
import { readdir as readdir19, readFile as
|
|
83882
|
-
import
|
|
84003
|
+
import { readdir as readdir19, readFile as readFile42 } from "fs/promises";
|
|
84004
|
+
import path48 from "path";
|
|
83883
84005
|
|
|
83884
84006
|
// src/commands/landing/import-graph.ts
|
|
83885
|
-
import { readdir as readdir18, readFile as
|
|
83886
|
-
import
|
|
84007
|
+
import { readdir as readdir18, readFile as readFile41, stat as stat7 } from "fs/promises";
|
|
84008
|
+
import path47 from "path";
|
|
83887
84009
|
var SOURCE_EXT = /\.(astro|ts|tsx|js|jsx|mjs|cjs|vue|svelte)$/;
|
|
83888
84010
|
var RESOLVE_CANDIDATES = [
|
|
83889
84011
|
"",
|
|
@@ -83916,7 +84038,7 @@ async function walkFiles(dir) {
|
|
|
83916
84038
|
}
|
|
83917
84039
|
const out = [];
|
|
83918
84040
|
for (const entry of entries) {
|
|
83919
|
-
const full =
|
|
84041
|
+
const full = path47.join(dir, entry.name);
|
|
83920
84042
|
if (entry.isDirectory()) {
|
|
83921
84043
|
if (entry.name === "node_modules" || entry.name === "dist") continue;
|
|
83922
84044
|
out.push(...await walkFiles(full));
|
|
@@ -83930,8 +84052,8 @@ async function resolveSpecifier(specifier, fromFile, root) {
|
|
|
83930
84052
|
const bare = specifier.split("?")[0] ?? "";
|
|
83931
84053
|
if (bare.includes("*")) return null;
|
|
83932
84054
|
let base;
|
|
83933
|
-
if (bare.startsWith("/")) base =
|
|
83934
|
-
else if (bare.startsWith(".")) base =
|
|
84055
|
+
if (bare.startsWith("/")) base = path47.join(root, bare.slice(1));
|
|
84056
|
+
else if (bare.startsWith(".")) base = path47.resolve(path47.dirname(fromFile), bare);
|
|
83935
84057
|
else return null;
|
|
83936
84058
|
for (const suffix of RESOLVE_CANDIDATES) {
|
|
83937
84059
|
const candidate = base + suffix;
|
|
@@ -83953,13 +84075,13 @@ async function importedFiles(text3, file, root) {
|
|
|
83953
84075
|
}
|
|
83954
84076
|
async function landingGraphFiles(root, slug) {
|
|
83955
84077
|
const seen = /* @__PURE__ */ new Set();
|
|
83956
|
-
const queue = await walkFiles(
|
|
84078
|
+
const queue = await walkFiles(path47.join(root, "src/pages", slug));
|
|
83957
84079
|
while (queue.length > 0) {
|
|
83958
84080
|
const file = queue.pop();
|
|
83959
84081
|
if (file === void 0 || seen.has(file)) continue;
|
|
83960
84082
|
let text3;
|
|
83961
84083
|
try {
|
|
83962
|
-
text3 = await
|
|
84084
|
+
text3 = await readFile41(file, "utf8");
|
|
83963
84085
|
} catch {
|
|
83964
84086
|
continue;
|
|
83965
84087
|
}
|
|
@@ -83968,7 +84090,7 @@ async function landingGraphFiles(root, slug) {
|
|
|
83968
84090
|
if (!seen.has(target2)) queue.push(target2);
|
|
83969
84091
|
}
|
|
83970
84092
|
}
|
|
83971
|
-
return new Set([...seen].map((abs) =>
|
|
84093
|
+
return new Set([...seen].map((abs) => path47.relative(root, abs).split(path47.sep).join("/")));
|
|
83972
84094
|
}
|
|
83973
84095
|
|
|
83974
84096
|
// src/commands/experiment/composition.ts
|
|
@@ -84013,7 +84135,7 @@ function formsOnlyIn(these, those) {
|
|
|
84013
84135
|
}
|
|
84014
84136
|
async function knownFlows(root) {
|
|
84015
84137
|
try {
|
|
84016
|
-
const entries = await readdir19(
|
|
84138
|
+
const entries = await readdir19(path48.join(root, "src/lib/flow-engine/flows"), { withFileTypes: true });
|
|
84017
84139
|
return new Set(entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name));
|
|
84018
84140
|
} catch {
|
|
84019
84141
|
return /* @__PURE__ */ new Set();
|
|
@@ -84024,7 +84146,7 @@ async function readForms(root, slug) {
|
|
|
84024
84146
|
const [files, flows] = await Promise.all([landingGraphFiles(root, slug), knownFlows(root)]);
|
|
84025
84147
|
if (flows.size === 0) return [];
|
|
84026
84148
|
const texts = await Promise.all(
|
|
84027
|
-
[...files].filter((file) => /\.(astro|ts|tsx|js|mjs)$/.test(file)).map((file) =>
|
|
84149
|
+
[...files].filter((file) => /\.(astro|ts|tsx|js|mjs)$/.test(file)).map((file) => readFile42(path48.join(root, file), "utf8"))
|
|
84028
84150
|
);
|
|
84029
84151
|
return formsReferenced(texts, flows);
|
|
84030
84152
|
} catch {
|
|
@@ -84034,8 +84156,8 @@ async function readForms(root, slug) {
|
|
|
84034
84156
|
|
|
84035
84157
|
// src/commands/experiment/fold.ts
|
|
84036
84158
|
import { statSync } from "fs";
|
|
84037
|
-
import { cp as cp3, mkdir as
|
|
84038
|
-
import
|
|
84159
|
+
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";
|
|
84160
|
+
import path50 from "path";
|
|
84039
84161
|
|
|
84040
84162
|
// ../api/src/experiments/variants.ts
|
|
84041
84163
|
var VARIANT_SEPARATOR = "--";
|
|
@@ -84060,21 +84182,21 @@ function nextVariantNumber(page, existingSlugs, archived) {
|
|
|
84060
84182
|
}
|
|
84061
84183
|
|
|
84062
84184
|
// src/commands/landing/repoint.ts
|
|
84063
|
-
import
|
|
84185
|
+
import path49 from "path";
|
|
84064
84186
|
var QUOTED_RELATIVE = /(['"])(\.\.?\/[^'"]*)\1/g;
|
|
84065
84187
|
function isInside3(abs, dir) {
|
|
84066
|
-
return abs === dir || abs.startsWith(dir +
|
|
84188
|
+
return abs === dir || abs.startsWith(dir + path49.sep);
|
|
84067
84189
|
}
|
|
84068
84190
|
function toSpecifier(from, to) {
|
|
84069
|
-
const rel =
|
|
84191
|
+
const rel = path49.relative(from, to).split(path49.sep).join("/");
|
|
84070
84192
|
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
84071
84193
|
}
|
|
84072
84194
|
function repointSpecifier(spec, opts) {
|
|
84073
84195
|
if (!spec.startsWith(".")) return spec;
|
|
84074
|
-
const abs =
|
|
84196
|
+
const abs = path49.resolve(opts.fromDir, spec);
|
|
84075
84197
|
if (opts.forkedTargets?.has(abs)) {
|
|
84076
|
-
const withinControl =
|
|
84077
|
-
return toSpecifier(opts.toDir,
|
|
84198
|
+
const withinControl = path49.relative(opts.controlDir, abs);
|
|
84199
|
+
return toSpecifier(opts.toDir, path49.resolve(opts.variantDir, withinControl));
|
|
84078
84200
|
}
|
|
84079
84201
|
if (!isInside3(abs, opts.controlDir)) return spec;
|
|
84080
84202
|
return toSpecifier(opts.toDir, abs);
|
|
@@ -84086,9 +84208,9 @@ function repointFile(text3, opts) {
|
|
|
84086
84208
|
}
|
|
84087
84209
|
function repointMoved(text3, opts) {
|
|
84088
84210
|
return text3.replace(QUOTED_RELATIVE, (_match, quote, spec) => {
|
|
84089
|
-
const abs =
|
|
84090
|
-
const within =
|
|
84091
|
-
const target2 = within.startsWith("..") ||
|
|
84211
|
+
const abs = path49.resolve(opts.fromDir, spec);
|
|
84212
|
+
const within = path49.relative(opts.movedFrom, abs);
|
|
84213
|
+
const target2 = within.startsWith("..") || path49.isAbsolute(within) ? abs : path49.resolve(opts.movedTo, within);
|
|
84092
84214
|
return `${quote}${toSpecifier(opts.toDir, target2)}${quote}`;
|
|
84093
84215
|
});
|
|
84094
84216
|
}
|
|
@@ -84097,7 +84219,7 @@ function repointMoved(text3, opts) {
|
|
|
84097
84219
|
var ARCHIVE_ROOT = "src/_variants";
|
|
84098
84220
|
var QUOTED_RELATIVE2 = /(['"])(\.\.?\/[^'"]*)\1/g;
|
|
84099
84221
|
function posix2(p) {
|
|
84100
|
-
return p.split(
|
|
84222
|
+
return p.split(path50.sep).join("/");
|
|
84101
84223
|
}
|
|
84102
84224
|
async function isDir(p) {
|
|
84103
84225
|
try {
|
|
@@ -84116,39 +84238,39 @@ async function filesUnder(dir, prefix = "") {
|
|
|
84116
84238
|
const out = [];
|
|
84117
84239
|
for (const entry of entries) {
|
|
84118
84240
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
84119
|
-
if (entry.isDirectory()) out.push(...await filesUnder(
|
|
84241
|
+
if (entry.isDirectory()) out.push(...await filesUnder(path50.join(dir, entry.name), rel));
|
|
84120
84242
|
else out.push(rel);
|
|
84121
84243
|
}
|
|
84122
84244
|
return out.sort();
|
|
84123
84245
|
}
|
|
84124
84246
|
var TEXT_EXT = /\.(astro|ts|tsx|js|jsx|mjs|cjs|vue|svelte|md|json|css)$/;
|
|
84125
84247
|
function archiveRelPath(abs, base) {
|
|
84126
|
-
if (!abs.startsWith(base +
|
|
84127
|
-
return posix2(
|
|
84248
|
+
if (!abs.startsWith(base + path50.sep)) return null;
|
|
84249
|
+
return posix2(path50.relative(base, abs));
|
|
84128
84250
|
}
|
|
84129
84251
|
async function archiveArm(opts) {
|
|
84130
84252
|
const { root, armDir, pageDir, archiveDir, armSlug } = opts;
|
|
84131
84253
|
const targets = await archiveContents({ root, armDir, pageDir, armSlug });
|
|
84132
|
-
const archivedBySource = new Map([...targets].map(([rel, abs]) => [abs,
|
|
84254
|
+
const archivedBySource = new Map([...targets].map(([rel, abs]) => [abs, path50.join(archiveDir, rel)]));
|
|
84133
84255
|
const written = [];
|
|
84134
84256
|
for (const [rel, abs] of [...targets].sort()) {
|
|
84135
|
-
const dest =
|
|
84136
|
-
await
|
|
84257
|
+
const dest = path50.join(archiveDir, rel);
|
|
84258
|
+
await mkdir15(path50.dirname(dest), { recursive: true });
|
|
84137
84259
|
if (!TEXT_EXT.test(rel)) {
|
|
84138
84260
|
await cp3(abs, dest);
|
|
84139
84261
|
} else {
|
|
84140
|
-
const text3 = await
|
|
84141
|
-
await
|
|
84262
|
+
const text3 = await readFile43(abs, "utf8");
|
|
84263
|
+
await writeFile25(dest, repointIntoArchive(text3, abs, dest, archivedBySource), "utf8");
|
|
84142
84264
|
}
|
|
84143
|
-
written.push(posix2(
|
|
84265
|
+
written.push(posix2(path50.relative(root, dest)));
|
|
84144
84266
|
}
|
|
84145
84267
|
return written;
|
|
84146
84268
|
}
|
|
84147
84269
|
async function archiveContents(opts) {
|
|
84148
84270
|
const { root, armDir, pageDir, armSlug } = opts;
|
|
84149
84271
|
const graph = await landingGraphFiles(root, armSlug);
|
|
84150
|
-
const reachable = new Set([...graph].map((rel) =>
|
|
84151
|
-
for (const rel of await filesUnder(armDir)) reachable.add(
|
|
84272
|
+
const reachable = new Set([...graph].map((rel) => path50.resolve(root, rel)));
|
|
84273
|
+
for (const rel of await filesUnder(armDir)) reachable.add(path50.join(armDir, rel));
|
|
84152
84274
|
const sorted = [...reachable].sort();
|
|
84153
84275
|
const targets = /* @__PURE__ */ new Map();
|
|
84154
84276
|
for (const base of [pageDir, armDir]) {
|
|
@@ -84161,14 +84283,14 @@ async function archiveContents(opts) {
|
|
|
84161
84283
|
return targets;
|
|
84162
84284
|
}
|
|
84163
84285
|
function repointIntoArchive(text3, sourceAbs, destAbs, archivedBySource) {
|
|
84164
|
-
const fromDir =
|
|
84165
|
-
const toDir =
|
|
84286
|
+
const fromDir = path50.dirname(sourceAbs);
|
|
84287
|
+
const toDir = path50.dirname(destAbs);
|
|
84166
84288
|
return text3.replace(QUOTED_RELATIVE2, (match, quote, spec, offset) => {
|
|
84167
84289
|
const [specPath, suffix] = splitSuffix(spec);
|
|
84168
|
-
const target2 =
|
|
84290
|
+
const target2 = path50.resolve(fromDir, specPath);
|
|
84169
84291
|
const resolved = resolveInArchive(target2, archivedBySource) ?? resolveOnDisk(target2, isImportPosition(text3, offset));
|
|
84170
84292
|
if (resolved === null) return match;
|
|
84171
|
-
const rel = posix2(
|
|
84293
|
+
const rel = posix2(path50.relative(toDir, resolved));
|
|
84172
84294
|
return `${quote}${rel.startsWith(".") ? rel : `./${rel}`}${suffix}${quote}`;
|
|
84173
84295
|
});
|
|
84174
84296
|
}
|
|
@@ -84202,25 +84324,25 @@ async function promoteVariantFiles(opts) {
|
|
|
84202
84324
|
const written = [];
|
|
84203
84325
|
for (const rel of await filesUnder(variantDir)) {
|
|
84204
84326
|
if (rel === "_definition.md") continue;
|
|
84205
|
-
const from =
|
|
84206
|
-
const to =
|
|
84207
|
-
await
|
|
84327
|
+
const from = path50.join(variantDir, rel);
|
|
84328
|
+
const to = path50.join(pageDir, rel);
|
|
84329
|
+
await mkdir15(path50.dirname(to), { recursive: true });
|
|
84208
84330
|
if (!TEXT_EXT.test(rel)) {
|
|
84209
84331
|
await cp3(from, to);
|
|
84210
84332
|
} else {
|
|
84211
|
-
const text3 = await
|
|
84212
|
-
await
|
|
84333
|
+
const text3 = await readFile43(from, "utf8");
|
|
84334
|
+
await writeFile25(
|
|
84213
84335
|
to,
|
|
84214
84336
|
repointMoved(text3, {
|
|
84215
|
-
fromDir:
|
|
84216
|
-
toDir:
|
|
84337
|
+
fromDir: path50.dirname(from),
|
|
84338
|
+
toDir: path50.dirname(to),
|
|
84217
84339
|
movedFrom: variantDir,
|
|
84218
84340
|
movedTo: pageDir
|
|
84219
84341
|
}),
|
|
84220
84342
|
"utf8"
|
|
84221
84343
|
);
|
|
84222
84344
|
}
|
|
84223
|
-
written.push(posix2(
|
|
84345
|
+
written.push(posix2(path50.relative(root, to)));
|
|
84224
84346
|
}
|
|
84225
84347
|
return written;
|
|
84226
84348
|
}
|
|
@@ -84244,8 +84366,8 @@ function archiveRecord(target2, variantNumber) {
|
|
|
84244
84366
|
`;
|
|
84245
84367
|
}
|
|
84246
84368
|
async function foldExperiment(root, target2) {
|
|
84247
|
-
const pageDir =
|
|
84248
|
-
const variantDir =
|
|
84369
|
+
const pageDir = path50.resolve(root, "src/pages", target2.landingSlug);
|
|
84370
|
+
const variantDir = path50.resolve(root, "src/pages", target2.variantSlug);
|
|
84249
84371
|
const variantNumber = variantNumberOf(target2.variantSlug);
|
|
84250
84372
|
if (variantNumber === null) {
|
|
84251
84373
|
return {
|
|
@@ -84259,9 +84381,9 @@ async function foldExperiment(root, target2) {
|
|
|
84259
84381
|
return { error: `src/pages/${target2.variantSlug}/ is already gone \u2014 this test looks folded in already` };
|
|
84260
84382
|
}
|
|
84261
84383
|
const archiveKey = target2.landingInternalId ?? target2.landingSlug;
|
|
84262
|
-
const archiveDir =
|
|
84384
|
+
const archiveDir = path50.resolve(root, ARCHIVE_ROOT, archiveKey, String(variantNumber));
|
|
84263
84385
|
await rm8(archiveDir, { recursive: true, force: true });
|
|
84264
|
-
await
|
|
84386
|
+
await mkdir15(archiveDir, { recursive: true });
|
|
84265
84387
|
const written = await archiveArm({
|
|
84266
84388
|
root,
|
|
84267
84389
|
armDir: target2.survivor === "variant" ? pageDir : variantDir,
|
|
@@ -84269,8 +84391,8 @@ async function foldExperiment(root, target2) {
|
|
|
84269
84391
|
archiveDir,
|
|
84270
84392
|
armSlug: target2.survivor === "variant" ? target2.landingSlug : target2.variantSlug
|
|
84271
84393
|
});
|
|
84272
|
-
await
|
|
84273
|
-
written.push(posix2(
|
|
84394
|
+
await writeFile25(path50.join(archiveDir, "_variant.json"), archiveRecord(target2, variantNumber), "utf8");
|
|
84395
|
+
written.push(posix2(path50.join(ARCHIVE_ROOT, archiveKey, String(variantNumber), "_variant.json")));
|
|
84274
84396
|
if (target2.survivor === "variant") {
|
|
84275
84397
|
written.push(...await promoteVariantFiles({ root, pageDir, variantDir }));
|
|
84276
84398
|
}
|
|
@@ -84278,29 +84400,29 @@ async function foldExperiment(root, target2) {
|
|
|
84278
84400
|
return {
|
|
84279
84401
|
experimentId: target2.experimentId,
|
|
84280
84402
|
page: target2.landingSlug,
|
|
84281
|
-
archivedTo: posix2(
|
|
84403
|
+
archivedTo: posix2(path50.join(ARCHIVE_ROOT, archiveKey, String(variantNumber))),
|
|
84282
84404
|
written,
|
|
84283
84405
|
removed: [`src/pages/${target2.variantSlug}`]
|
|
84284
84406
|
};
|
|
84285
84407
|
}
|
|
84286
84408
|
|
|
84287
84409
|
// ../api/src/experiments/composition.ts
|
|
84288
|
-
import { z as
|
|
84289
|
-
var experimentCompositionSchema =
|
|
84410
|
+
import { z as z71 } from "zod";
|
|
84411
|
+
var experimentCompositionSchema = z71.object({
|
|
84290
84412
|
/** Components the variant owns its own copy of — the thing under test. */
|
|
84291
|
-
forked:
|
|
84413
|
+
forked: z71.array(z71.string().max(300)).max(50),
|
|
84292
84414
|
/** Components both pages render from one file, so an edit reaches both arms. */
|
|
84293
|
-
shared:
|
|
84415
|
+
shared: z71.array(z71.string().max(300)).max(200)
|
|
84294
84416
|
});
|
|
84295
84417
|
|
|
84296
84418
|
// ../api/src/experiments/goal.ts
|
|
84297
|
-
import { z as
|
|
84419
|
+
import { z as z72 } from "zod";
|
|
84298
84420
|
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 =
|
|
84421
|
+
var experimentGoalSchema = z72.string().trim().min(1).max(400).refine((key) => parseEventKey(key) !== null, { message: GOAL_KEY_HELP });
|
|
84300
84422
|
|
|
84301
84423
|
// ../api/src/experiments/hypothesis.ts
|
|
84302
|
-
import { z as
|
|
84303
|
-
var experimentEvidenceSourceSchema =
|
|
84424
|
+
import { z as z73 } from "zod";
|
|
84425
|
+
var experimentEvidenceSourceSchema = z73.enum([
|
|
84304
84426
|
"baker_analytics",
|
|
84305
84427
|
"clarity",
|
|
84306
84428
|
"ads",
|
|
@@ -84310,17 +84432,17 @@ var experimentEvidenceSourceSchema = z72.enum([
|
|
|
84310
84432
|
"client",
|
|
84311
84433
|
"hunch"
|
|
84312
84434
|
]);
|
|
84313
|
-
var experimentEvidenceSchema =
|
|
84435
|
+
var experimentEvidenceSchema = z73.object({
|
|
84314
84436
|
source: experimentEvidenceSourceSchema,
|
|
84315
|
-
note:
|
|
84437
|
+
note: z73.string().min(1).max(200),
|
|
84316
84438
|
/**
|
|
84317
84439
|
* Where to go and look. Not a `z.url()` — a report inside Baker is reached by
|
|
84318
84440
|
* a relative path, and rejecting those would push every in-product citation
|
|
84319
84441
|
* into the note where nothing can link it.
|
|
84320
84442
|
*/
|
|
84321
|
-
href:
|
|
84443
|
+
href: z73.string().max(500).optional()
|
|
84322
84444
|
});
|
|
84323
|
-
var experimentHypothesisSchema =
|
|
84445
|
+
var experimentHypothesisSchema = z73.object({
|
|
84324
84446
|
/**
|
|
84325
84447
|
* The observation. The one genuinely new field, and the reason for the rest.
|
|
84326
84448
|
*
|
|
@@ -84328,7 +84450,7 @@ var experimentHypothesisSchema = z72.object({
|
|
|
84328
84450
|
* result to be evidence against — which is how a testing programme ends up as
|
|
84329
84451
|
* a list of tests instead of a list of things learned.
|
|
84330
84452
|
*/
|
|
84331
|
-
because:
|
|
84453
|
+
because: z73.string().min(1).max(300),
|
|
84332
84454
|
/**
|
|
84333
84455
|
* The change, in the client's words.
|
|
84334
84456
|
*
|
|
@@ -84336,12 +84458,12 @@ var experimentHypothesisSchema = z72.object({
|
|
|
84336
84458
|
* a person reads. The two are allowed to differ in wording and cannot differ
|
|
84337
84459
|
* in fact, because the brief shows both.
|
|
84338
84460
|
*/
|
|
84339
|
-
change:
|
|
84461
|
+
change: z73.string().min(1).max(200),
|
|
84340
84462
|
/** Which way the goal is expected to move. See the note at the top of this file. */
|
|
84341
|
-
expect:
|
|
84342
|
-
evidence:
|
|
84463
|
+
expect: z73.enum(["increase", "decrease"]).default("increase"),
|
|
84464
|
+
evidence: z73.array(experimentEvidenceSchema).max(5).optional()
|
|
84343
84465
|
});
|
|
84344
|
-
var experimentHypothesisOutcomeSchema =
|
|
84466
|
+
var experimentHypothesisOutcomeSchema = z73.enum([
|
|
84345
84467
|
"supported",
|
|
84346
84468
|
"refuted_opposite",
|
|
84347
84469
|
"refuted_no_effect",
|
|
@@ -84368,13 +84490,13 @@ function roughCount(count2) {
|
|
|
84368
84490
|
}
|
|
84369
84491
|
|
|
84370
84492
|
// ../api/src/experiments/wire.ts
|
|
84371
|
-
import { z as
|
|
84372
|
-
var slugSchema =
|
|
84373
|
-
var variantSlugSchema =
|
|
84493
|
+
import { z as z74 } from "zod";
|
|
84494
|
+
var slugSchema = z74.string().min(1).max(120).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Use a page slug, like `oferta`");
|
|
84495
|
+
var variantSlugSchema = z74.string().min(1).max(130).refine((slug) => {
|
|
84374
84496
|
const page = variantOfSlug(slug);
|
|
84375
84497
|
return page !== null && slugSchema.safeParse(page).success;
|
|
84376
84498
|
}, "Use the variant's slug, `<page>--<n>` \u2014 `baker landing variant <page>` chooses it");
|
|
84377
|
-
var experimentPlanRequestSchema =
|
|
84499
|
+
var experimentPlanRequestSchema = z74.object({
|
|
84378
84500
|
landingSlug: slugSchema,
|
|
84379
84501
|
variantSlug: variantSlugSchema,
|
|
84380
84502
|
/**
|
|
@@ -84403,7 +84525,7 @@ var experimentPlanRequestSchema = z73.object({
|
|
|
84403
84525
|
* quarter of a million visitors per arm; `plan` will say so rather than
|
|
84404
84526
|
* running it.
|
|
84405
84527
|
*/
|
|
84406
|
-
minDetectableRelativeLift:
|
|
84528
|
+
minDetectableRelativeLift: z74.number().gt(0).max(10).optional(),
|
|
84407
84529
|
/**
|
|
84408
84530
|
* The share of visitors expected to do the goal, as a fraction — `0.05` is
|
|
84409
84531
|
* 5% — for a page whose history cannot say.
|
|
@@ -84414,14 +84536,14 @@ var experimentPlanRequestSchema = z73.object({
|
|
|
84414
84536
|
* Either way the brief says which, because the sizing is a guide and never a
|
|
84415
84537
|
* finish line — the test runs until the numbers are clear.
|
|
84416
84538
|
*/
|
|
84417
|
-
baselineRate:
|
|
84539
|
+
baselineRate: z74.number().gt(0).lt(1).optional()
|
|
84418
84540
|
});
|
|
84419
|
-
var experimentTrafficSchema =
|
|
84420
|
-
var experimentBaselineSourceSchema =
|
|
84421
|
-
var experimentSizedOnSchema =
|
|
84422
|
-
visitors:
|
|
84423
|
-
convertingVisitors:
|
|
84424
|
-
windowDays:
|
|
84541
|
+
var experimentTrafficSchema = z74.enum(["none", "low", "enough"]);
|
|
84542
|
+
var experimentBaselineSourceSchema = z74.enum(["page", "company", "assumed"]);
|
|
84543
|
+
var experimentSizedOnSchema = z74.object({
|
|
84544
|
+
visitors: z74.number(),
|
|
84545
|
+
convertingVisitors: z74.number(),
|
|
84546
|
+
windowDays: z74.number()
|
|
84425
84547
|
});
|
|
84426
84548
|
var experimentStartRequestSchema = experimentPlanRequestSchema.extend({
|
|
84427
84549
|
hypothesis: experimentHypothesisSchema,
|
|
@@ -84435,8 +84557,8 @@ var experimentStartRequestSchema = experimentPlanRequestSchema.extend({
|
|
|
84435
84557
|
* loser under the wrong key. Optional because a workspace that cannot read
|
|
84436
84558
|
* its own definition must still be able to start a test.
|
|
84437
84559
|
*/
|
|
84438
|
-
landingInternalId:
|
|
84439
|
-
variantInternalId:
|
|
84560
|
+
landingInternalId: z74.string().min(1).max(64).optional(),
|
|
84561
|
+
variantInternalId: z74.string().min(1).max(64).optional(),
|
|
84440
84562
|
/**
|
|
84441
84563
|
* What the two arms are made of, read off the import graph by the caller at
|
|
84442
84564
|
* the moment the test starts.
|
|
@@ -84448,21 +84570,21 @@ var experimentStartRequestSchema = experimentPlanRequestSchema.extend({
|
|
|
84448
84570
|
*/
|
|
84449
84571
|
composition: experimentCompositionSchema.optional()
|
|
84450
84572
|
});
|
|
84451
|
-
var experimentKeepSchema =
|
|
84452
|
-
var experimentEndReasonSchema =
|
|
84453
|
-
var experimentLearningSchema =
|
|
84454
|
-
var experimentFinishRequestSchema =
|
|
84455
|
-
experimentId:
|
|
84573
|
+
var experimentKeepSchema = z74.enum(["original", "variant"]);
|
|
84574
|
+
var experimentEndReasonSchema = z74.enum(["numbers_clear", "not_settling", "page_changing", "test_flawed"]);
|
|
84575
|
+
var experimentLearningSchema = z74.string().trim().min(1).max(2e3);
|
|
84576
|
+
var experimentFinishRequestSchema = z74.object({
|
|
84577
|
+
experimentId: z74.string().min(1).max(64),
|
|
84456
84578
|
keep: experimentKeepSchema,
|
|
84457
84579
|
/** Why it is being ended. The dashboard always asks; the agent should say. */
|
|
84458
84580
|
reason: experimentEndReasonSchema.optional(),
|
|
84459
84581
|
learning: experimentLearningSchema.optional()
|
|
84460
84582
|
});
|
|
84461
|
-
var experimentPauseRequestSchema =
|
|
84462
|
-
experimentId:
|
|
84583
|
+
var experimentPauseRequestSchema = z74.object({
|
|
84584
|
+
experimentId: z74.string().min(1).max(64)
|
|
84463
84585
|
});
|
|
84464
|
-
var experimentUpdateRequestSchema =
|
|
84465
|
-
experimentId:
|
|
84586
|
+
var experimentUpdateRequestSchema = z74.object({
|
|
84587
|
+
experimentId: z74.string().min(1).max(64),
|
|
84466
84588
|
because: experimentHypothesisSchema.shape.because.optional(),
|
|
84467
84589
|
change: experimentHypothesisSchema.shape.change.optional(),
|
|
84468
84590
|
evidence: experimentHypothesisSchema.shape.evidence,
|
|
@@ -84472,26 +84594,26 @@ var experimentUpdateRequestSchema = z73.object({
|
|
|
84472
84594
|
* default still fires, so every wording-only update arrived carrying a
|
|
84473
84595
|
* direction and was refused for trying to change it.
|
|
84474
84596
|
*/
|
|
84475
|
-
expect:
|
|
84597
|
+
expect: z74.enum(["increase", "decrease"]).optional(),
|
|
84476
84598
|
/** Staged tests only. */
|
|
84477
84599
|
goal: experimentGoalSchema.optional(),
|
|
84478
84600
|
/** Staged tests only. The new version's own event, when it differs. */
|
|
84479
84601
|
variantGoal: experimentGoalSchema.optional(),
|
|
84480
84602
|
/** Staged tests only. */
|
|
84481
|
-
minDetectableRelativeLift:
|
|
84603
|
+
minDetectableRelativeLift: z74.number().gt(0).max(10).optional(),
|
|
84482
84604
|
/** Finished tests only: what the test taught, written or corrected after the fact. */
|
|
84483
84605
|
learning: experimentLearningSchema.optional()
|
|
84484
84606
|
});
|
|
84485
|
-
var experimentUpdateResponseSchema =
|
|
84607
|
+
var experimentUpdateResponseSchema = z74.object({
|
|
84486
84608
|
/** True when the test had not been published, so its whole plan was re-sized. */
|
|
84487
|
-
staged:
|
|
84488
|
-
hypothesisStatement:
|
|
84609
|
+
staged: z74.boolean(),
|
|
84610
|
+
hypothesisStatement: z74.string()
|
|
84489
84611
|
});
|
|
84490
|
-
var experimentFoldRequestSchema =
|
|
84612
|
+
var experimentFoldRequestSchema = z74.object({
|
|
84491
84613
|
/** Which test to fold. Omitted ⇒ every fold this company owes. */
|
|
84492
|
-
experimentId:
|
|
84614
|
+
experimentId: z74.string().max(64).optional(),
|
|
84493
84615
|
/** The files are written. Baker drops the promotion once the build lands. */
|
|
84494
|
-
applied:
|
|
84616
|
+
applied: z74.boolean().optional(),
|
|
84495
84617
|
/**
|
|
84496
84618
|
* Which of the owed folds were actually written, on an `applied` call.
|
|
84497
84619
|
*
|
|
@@ -84502,68 +84624,68 @@ var experimentFoldRequestSchema = z73.object({
|
|
|
84502
84624
|
* its page served the version that LOST, with nothing owed and nothing
|
|
84503
84625
|
* logged. Omitted ⇒ everything owed, which is the whole-company case.
|
|
84504
84626
|
*/
|
|
84505
|
-
experimentIds:
|
|
84627
|
+
experimentIds: z74.array(z74.string().max(64)).max(200).optional()
|
|
84506
84628
|
});
|
|
84507
|
-
var experimentFoldTargetSchema =
|
|
84508
|
-
experimentId:
|
|
84629
|
+
var experimentFoldTargetSchema = z74.object({
|
|
84630
|
+
experimentId: z74.string(),
|
|
84509
84631
|
/** The page. Keeps its slug, its identity, its history and its ads. */
|
|
84510
|
-
landingSlug:
|
|
84632
|
+
landingSlug: z74.string(),
|
|
84511
84633
|
/** The variant folder, `<page>--<n>`. Gone from `src/pages/` afterwards. */
|
|
84512
|
-
variantSlug:
|
|
84634
|
+
variantSlug: z74.string(),
|
|
84513
84635
|
/** Which variant won, and therefore which files become the page's own. */
|
|
84514
|
-
survivor:
|
|
84636
|
+
survivor: z74.union([z74.literal("control"), z74.literal("variant")]),
|
|
84515
84637
|
/** For the archive path, which is keyed on identity rather than on a slug. */
|
|
84516
|
-
landingInternalId:
|
|
84638
|
+
landingInternalId: z74.string().nullable()
|
|
84517
84639
|
});
|
|
84518
|
-
var experimentFoldResponseSchema =
|
|
84519
|
-
owed:
|
|
84640
|
+
var experimentFoldResponseSchema = z74.object({
|
|
84641
|
+
owed: z74.array(experimentFoldTargetSchema),
|
|
84520
84642
|
/** How many were recorded as written, when `applied` was set. */
|
|
84521
|
-
recorded:
|
|
84643
|
+
recorded: z74.number().optional()
|
|
84522
84644
|
});
|
|
84523
|
-
var experimentStatusRequestSchema =
|
|
84524
|
-
experimentId:
|
|
84645
|
+
var experimentStatusRequestSchema = z74.object({
|
|
84646
|
+
experimentId: z74.string().max(64).optional(),
|
|
84525
84647
|
/** Only the tests on one page, by its slug. */
|
|
84526
|
-
landingSlug:
|
|
84527
|
-
full:
|
|
84648
|
+
landingSlug: z74.string().max(200).optional(),
|
|
84649
|
+
full: z74.boolean().optional()
|
|
84528
84650
|
});
|
|
84529
|
-
var experimentHistoryRequestSchema =
|
|
84530
|
-
landingSlug:
|
|
84531
|
-
limit:
|
|
84651
|
+
var experimentHistoryRequestSchema = z74.object({
|
|
84652
|
+
landingSlug: z74.string().max(200).optional(),
|
|
84653
|
+
limit: z74.number().int().positive().max(100).optional()
|
|
84532
84654
|
});
|
|
84533
|
-
var experimentPageConversionSchema =
|
|
84655
|
+
var experimentPageConversionSchema = z74.object({
|
|
84534
84656
|
/** The event, by key — what `--goal` takes. */
|
|
84535
|
-
eventKey:
|
|
84657
|
+
eventKey: z74.string(),
|
|
84536
84658
|
/** The conversion name the company counts it under, or null when nobody has named it. */
|
|
84537
|
-
name:
|
|
84659
|
+
name: z74.string().nullable(),
|
|
84538
84660
|
/** Distinct visitors to the page who went on to do this. */
|
|
84539
|
-
convertingVisitors:
|
|
84661
|
+
convertingVisitors: z74.number()
|
|
84540
84662
|
});
|
|
84541
|
-
var experimentHorizonSchema =
|
|
84542
|
-
days:
|
|
84663
|
+
var experimentHorizonSchema = z74.object({
|
|
84664
|
+
days: z74.number(),
|
|
84543
84665
|
/** The smallest relative lift detectable in that many days. `null` when none would be. */
|
|
84544
|
-
detectableLift:
|
|
84666
|
+
detectableLift: z74.number().nullable()
|
|
84545
84667
|
});
|
|
84546
|
-
var experimentPlanResponseSchema =
|
|
84547
|
-
canRun:
|
|
84668
|
+
var experimentPlanResponseSchema = z74.object({
|
|
84669
|
+
canRun: z74.boolean(),
|
|
84548
84670
|
/** Present when `canRun` is false. Product language, ready to show. */
|
|
84549
|
-
reason:
|
|
84671
|
+
reason: z74.string().optional(),
|
|
84550
84672
|
/** The event the test would be read on. Absent when none could be chosen. */
|
|
84551
84673
|
goal: experimentGoalSchema.optional(),
|
|
84552
84674
|
/** The new version's own event, when one was given. */
|
|
84553
84675
|
variantGoal: experimentGoalSchema.optional(),
|
|
84554
84676
|
/** Every event this page's visitors went on to do over the sizing window, most common first. */
|
|
84555
|
-
conversions:
|
|
84677
|
+
conversions: z74.array(experimentPageConversionSchema),
|
|
84556
84678
|
/** Distinct visitors the page had over the sizing window. */
|
|
84557
|
-
visitors:
|
|
84558
|
-
baselineRate:
|
|
84559
|
-
minDetectableRelativeLift:
|
|
84679
|
+
visitors: z74.number(),
|
|
84680
|
+
baselineRate: z74.number(),
|
|
84681
|
+
minDetectableRelativeLift: z74.number(),
|
|
84560
84682
|
/** `null` when no amount of traffic could settle the hypothesis. */
|
|
84561
|
-
requiredPerVariant:
|
|
84683
|
+
requiredPerVariant: z74.number().nullable(),
|
|
84562
84684
|
/** Visitors a day the page has been getting, per arm once split. */
|
|
84563
|
-
dailyVisitorsPerVariant:
|
|
84685
|
+
dailyVisitorsPerVariant: z74.number(),
|
|
84564
84686
|
/** `null` when the page has no traffic to project from. */
|
|
84565
|
-
estimatedDays:
|
|
84566
|
-
horizons:
|
|
84687
|
+
estimatedDays: z74.number().nullable(),
|
|
84688
|
+
horizons: z74.array(experimentHorizonSchema),
|
|
84567
84689
|
traffic: experimentTrafficSchema,
|
|
84568
84690
|
baselineSource: experimentBaselineSourceSchema,
|
|
84569
84691
|
sizedOn: experimentSizedOnSchema,
|
|
@@ -84572,7 +84694,7 @@ var experimentPlanResponseSchema = z73.object({
|
|
|
84572
84694
|
* Product language, ready to show; absent on a page with real traffic that
|
|
84573
84695
|
* has produced the goal.
|
|
84574
84696
|
*/
|
|
84575
|
-
warning:
|
|
84697
|
+
warning: z74.string().optional(),
|
|
84576
84698
|
/**
|
|
84577
84699
|
* Every finished test on this page, newest first — see
|
|
84578
84700
|
* `experimentHistoryEntrySchema`. On the plan because the plan is the moment
|
|
@@ -84580,9 +84702,9 @@ var experimentPlanResponseSchema = z73.object({
|
|
|
84580
84702
|
* should inform it: a refuted idea proposed again, or a supported one never
|
|
84581
84703
|
* built on, is what this exists to prevent.
|
|
84582
84704
|
*/
|
|
84583
|
-
pastTests:
|
|
84705
|
+
pastTests: z74.array(z74.lazy(() => experimentHistoryEntrySchema)).default([])
|
|
84584
84706
|
});
|
|
84585
|
-
var experimentVerdictSchema =
|
|
84707
|
+
var experimentVerdictSchema = z74.enum([
|
|
84586
84708
|
"keep_running",
|
|
84587
84709
|
"winner",
|
|
84588
84710
|
"no_difference",
|
|
@@ -84609,12 +84731,12 @@ var experimentVerdictSchema = z73.enum([
|
|
|
84609
84731
|
*/
|
|
84610
84732
|
"inconclusive"
|
|
84611
84733
|
]);
|
|
84612
|
-
var experimentStatusValueSchema =
|
|
84613
|
-
var experimentArmSchema =
|
|
84614
|
-
slug:
|
|
84615
|
-
visitors:
|
|
84616
|
-
conversions:
|
|
84617
|
-
rate:
|
|
84734
|
+
var experimentStatusValueSchema = z74.enum(["running", "paused", "finished"]);
|
|
84735
|
+
var experimentArmSchema = z74.object({
|
|
84736
|
+
slug: z74.string(),
|
|
84737
|
+
visitors: z74.number(),
|
|
84738
|
+
conversions: z74.number(),
|
|
84739
|
+
rate: z74.number(),
|
|
84618
84740
|
/**
|
|
84619
84741
|
* The page's own preview shot, so a reader can see which version this is.
|
|
84620
84742
|
*
|
|
@@ -84623,7 +84745,7 @@ var experimentArmSchema = z73.object({
|
|
|
84623
84745
|
* screens cannot show a client two different "this is your offer page".
|
|
84624
84746
|
* `null` before the first capture, and on a page whose capture failed.
|
|
84625
84747
|
*/
|
|
84626
|
-
previewUrl:
|
|
84748
|
+
previewUrl: z74.string().nullable().optional(),
|
|
84627
84749
|
/**
|
|
84628
84750
|
* This version as it was when the test started — the test's own capture,
|
|
84629
84751
|
* taken off the live site once the split was being served.
|
|
@@ -84633,41 +84755,41 @@ var experimentArmSchema = z73.object({
|
|
|
84633
84755
|
* altogether for a variant that lost and was archived. This is the record;
|
|
84634
84756
|
* a card prefers it and says which of the two it is showing.
|
|
84635
84757
|
*/
|
|
84636
|
-
snapshotUrl:
|
|
84637
|
-
snapshotCapturedAt:
|
|
84638
|
-
});
|
|
84639
|
-
var experimentMetricSchema =
|
|
84640
|
-
name:
|
|
84641
|
-
control:
|
|
84642
|
-
variant:
|
|
84643
|
-
relativeLift:
|
|
84758
|
+
snapshotUrl: z74.string().nullable().optional(),
|
|
84759
|
+
snapshotCapturedAt: z74.number().nullable().optional()
|
|
84760
|
+
});
|
|
84761
|
+
var experimentMetricSchema = z74.object({
|
|
84762
|
+
name: z74.string(),
|
|
84763
|
+
control: z74.object({ conversions: z74.number(), rate: z74.number() }),
|
|
84764
|
+
variant: z74.object({ conversions: z74.number(), rate: z74.number() }),
|
|
84765
|
+
relativeLift: z74.number(),
|
|
84644
84766
|
/** The anytime-valid interval on the relative lift. */
|
|
84645
|
-
liftInterval:
|
|
84767
|
+
liftInterval: z74.tuple([z74.number(), z74.number()]),
|
|
84646
84768
|
/** `better` when the interval sits above zero, `worse` below it, `unclear` across it. */
|
|
84647
|
-
reading:
|
|
84769
|
+
reading: z74.enum(["better", "worse", "unclear"])
|
|
84648
84770
|
});
|
|
84649
|
-
var experimentTimelineArmSchema =
|
|
84650
|
-
visitors:
|
|
84651
|
-
conversions:
|
|
84652
|
-
rate:
|
|
84771
|
+
var experimentTimelineArmSchema = z74.object({
|
|
84772
|
+
visitors: z74.number(),
|
|
84773
|
+
conversions: z74.number(),
|
|
84774
|
+
rate: z74.number().nullable()
|
|
84653
84775
|
});
|
|
84654
|
-
var experimentTimelinePointSchema =
|
|
84776
|
+
var experimentTimelinePointSchema = z74.object({
|
|
84655
84777
|
/** Bucket start, ISO 8601. */
|
|
84656
|
-
bucket:
|
|
84778
|
+
bucket: z74.string(),
|
|
84657
84779
|
control: experimentTimelineArmSchema,
|
|
84658
84780
|
variant: experimentTimelineArmSchema
|
|
84659
84781
|
});
|
|
84660
|
-
var experimentStatusRowSchema =
|
|
84661
|
-
experimentId:
|
|
84662
|
-
landingSlug:
|
|
84663
|
-
variantSlug:
|
|
84782
|
+
var experimentStatusRowSchema = z74.object({
|
|
84783
|
+
experimentId: z74.string(),
|
|
84784
|
+
landingSlug: z74.string(),
|
|
84785
|
+
variantSlug: z74.string(),
|
|
84664
84786
|
hypothesis: experimentHypothesisSchema,
|
|
84665
84787
|
/**
|
|
84666
84788
|
* The hypothesis as one sentence, built from its parts plus the goal and the
|
|
84667
84789
|
* lift on this same row. Rendered backend-side so what a client reads in chat
|
|
84668
84790
|
* and what they read on the dashboard can never be two different sentences.
|
|
84669
84791
|
*/
|
|
84670
|
-
hypothesisStatement:
|
|
84792
|
+
hypothesisStatement: z74.string(),
|
|
84671
84793
|
/**
|
|
84672
84794
|
* What the test found out about the belief — derived from `verdict` and
|
|
84673
84795
|
* `winner`, never entered.
|
|
@@ -84684,28 +84806,28 @@ var experimentStatusRowSchema = z73.object({
|
|
|
84684
84806
|
* the plan because the claim is not readable without it — "leads should go up"
|
|
84685
84807
|
* is not a hypothesis, "leads should go up by at least 20%" is.
|
|
84686
84808
|
*/
|
|
84687
|
-
minDetectableRelativeLift:
|
|
84809
|
+
minDetectableRelativeLift: z74.number(),
|
|
84688
84810
|
/** The event the verdict is read from on the page, by key. */
|
|
84689
84811
|
goal: experimentGoalSchema,
|
|
84690
84812
|
/** The event the new version is read on, when it differs from `goal`. */
|
|
84691
84813
|
variantGoal: experimentGoalSchema.nullable(),
|
|
84692
84814
|
/** Both, in words a marketer reads — the Form's name, the moment. */
|
|
84693
|
-
goalLabel:
|
|
84815
|
+
goalLabel: z74.string(),
|
|
84694
84816
|
status: experimentStatusValueSchema,
|
|
84695
|
-
startedAt:
|
|
84817
|
+
startedAt: z74.number(),
|
|
84696
84818
|
/** Set while paused. The split is off and everyone sees the original. */
|
|
84697
|
-
pausedAt:
|
|
84698
|
-
finishedAt:
|
|
84819
|
+
pausedAt: z74.number().nullable(),
|
|
84820
|
+
finishedAt: z74.number().nullable(),
|
|
84699
84821
|
verdict: experimentVerdictSchema,
|
|
84700
84822
|
/** Set only on `winner`. Which version the numbers favour. */
|
|
84701
|
-
winner:
|
|
84823
|
+
winner: z74.enum(["control", "variant"]).nullable(),
|
|
84702
84824
|
/**
|
|
84703
84825
|
* Which version is live, once the test has ended. Chosen by whoever ended it,
|
|
84704
84826
|
* and allowed to disagree with `winner` — the card says so when it does.
|
|
84705
84827
|
*/
|
|
84706
|
-
survivor:
|
|
84828
|
+
survivor: z74.enum(["control", "variant"]).nullable(),
|
|
84707
84829
|
/** Set only on `invalid`. Why the numbers cannot be read. */
|
|
84708
|
-
invalidReason:
|
|
84830
|
+
invalidReason: z74.enum(["sample_ratio_mismatch", "crossover"]).nullable(),
|
|
84709
84831
|
/**
|
|
84710
84832
|
* Where the page's own content stands after the test, once it has ended.
|
|
84711
84833
|
*
|
|
@@ -84719,17 +84841,17 @@ var experimentStatusRowSchema = z73.object({
|
|
|
84719
84841
|
* version" links still show the right file, and whether there is anything
|
|
84720
84842
|
* left for a person to do.
|
|
84721
84843
|
*/
|
|
84722
|
-
fold:
|
|
84844
|
+
fold: z74.enum(["none", "owed", "landing", "done"]).nullable(),
|
|
84723
84845
|
/** What a person should be told, in one sentence, in product language. */
|
|
84724
|
-
summary:
|
|
84846
|
+
summary: z74.string(),
|
|
84725
84847
|
/** What the test taught, once somebody wrote it down. Null until then, and always null while it runs. */
|
|
84726
|
-
learning:
|
|
84848
|
+
learning: z74.string().nullable(),
|
|
84727
84849
|
/** Why it was ended, when whoever ended it said. Null while it runs. */
|
|
84728
84850
|
endReason: experimentEndReasonSchema.nullable(),
|
|
84729
84851
|
control: experimentArmSchema,
|
|
84730
84852
|
variant: experimentArmSchema,
|
|
84731
84853
|
/** Every other conversion the company counts, on the same two arms. */
|
|
84732
|
-
secondary:
|
|
84854
|
+
secondary: z74.array(experimentMetricSchema),
|
|
84733
84855
|
/**
|
|
84734
84856
|
* How close this test is to being able to say anything at all, 0 to 1.
|
|
84735
84857
|
*
|
|
@@ -84738,9 +84860,9 @@ var experimentStatusRowSchema = z73.object({
|
|
|
84738
84860
|
* measured variance says it needs, so it moves when the page gets noisier or
|
|
84739
84861
|
* steadier rather than only when visitors arrive.
|
|
84740
84862
|
*/
|
|
84741
|
-
precision:
|
|
84863
|
+
precision: z74.number(),
|
|
84742
84864
|
/** When the test could first say something. `null` with no traffic to project from. */
|
|
84743
|
-
earliestDecisionAt:
|
|
84865
|
+
earliestDecisionAt: z74.number().nullable(),
|
|
84744
84866
|
/**
|
|
84745
84867
|
* What the test is still missing before its verdict changes, if the gap it
|
|
84746
84868
|
* is seeing holds: visitors across both arms, and the conversions they bring.
|
|
@@ -84748,13 +84870,13 @@ var experimentStatusRowSchema = z73.object({
|
|
|
84748
84870
|
* divided by the page's pace. Null once it has spoken, before anyone has
|
|
84749
84871
|
* arrived, or when no amount of traffic would settle it.
|
|
84750
84872
|
*/
|
|
84751
|
-
stillNeeded:
|
|
84873
|
+
stillNeeded: z74.object({ visitors: z74.number(), conversions: z74.number() }).nullable().default(null),
|
|
84752
84874
|
/**
|
|
84753
84875
|
* True when no amount of this page's traffic would settle the comparison.
|
|
84754
84876
|
* A different thing from a page that is merely slow, and the reason to end
|
|
84755
84877
|
* a test rather than leave it running.
|
|
84756
84878
|
*/
|
|
84757
|
-
willNotConclude:
|
|
84879
|
+
willNotConclude: z74.boolean(),
|
|
84758
84880
|
/**
|
|
84759
84881
|
* How the two arms got where they are, one running total per bucket.
|
|
84760
84882
|
*
|
|
@@ -84763,74 +84885,74 @@ var experimentStatusRowSchema = z73.object({
|
|
|
84763
84885
|
* already told it in one word — the same reason everything else here is
|
|
84764
84886
|
* compact by default.
|
|
84765
84887
|
*/
|
|
84766
|
-
timeline:
|
|
84888
|
+
timeline: z74.array(experimentTimelinePointSchema).optional(),
|
|
84767
84889
|
/** Printing only. Never a reason to act — `verdict` is. */
|
|
84768
|
-
evidence:
|
|
84769
|
-
relativeLift:
|
|
84770
|
-
probabilityVariantBetter:
|
|
84890
|
+
evidence: z74.object({
|
|
84891
|
+
relativeLift: z74.number(),
|
|
84892
|
+
probabilityVariantBetter: z74.number(),
|
|
84771
84893
|
/**
|
|
84772
84894
|
* How sure the numbers are that the two versions differ at all, 0 to 1.
|
|
84773
84895
|
* Anytime-valid — the same construction the verdict is read from, so it
|
|
84774
84896
|
* holds however often it is read — and a winner is called exactly when
|
|
84775
84897
|
* it crosses 95%. The figure a card leads with.
|
|
84776
84898
|
*/
|
|
84777
|
-
confidence:
|
|
84778
|
-
controlInterval:
|
|
84779
|
-
variantInterval:
|
|
84899
|
+
confidence: z74.number(),
|
|
84900
|
+
controlInterval: z74.tuple([z74.number(), z74.number()]),
|
|
84901
|
+
variantInterval: z74.tuple([z74.number(), z74.number()]),
|
|
84780
84902
|
/** The anytime-valid interval on the relative lift — what the verdict is read from. */
|
|
84781
|
-
liftInterval:
|
|
84903
|
+
liftInterval: z74.tuple([z74.number(), z74.number()])
|
|
84782
84904
|
}).optional()
|
|
84783
84905
|
});
|
|
84784
|
-
var experimentStatusResponseSchema =
|
|
84785
|
-
experiments:
|
|
84906
|
+
var experimentStatusResponseSchema = z74.object({
|
|
84907
|
+
experiments: z74.array(experimentStatusRowSchema)
|
|
84786
84908
|
});
|
|
84787
|
-
var experimentHistoryEntrySchema =
|
|
84788
|
-
experimentId:
|
|
84789
|
-
landingSlug:
|
|
84790
|
-
variantSlug:
|
|
84791
|
-
startedAt:
|
|
84792
|
-
finishedAt:
|
|
84909
|
+
var experimentHistoryEntrySchema = z74.object({
|
|
84910
|
+
experimentId: z74.string(),
|
|
84911
|
+
landingSlug: z74.string(),
|
|
84912
|
+
variantSlug: z74.string(),
|
|
84913
|
+
startedAt: z74.number(),
|
|
84914
|
+
finishedAt: z74.number(),
|
|
84793
84915
|
/** Days the split ran, whole. */
|
|
84794
|
-
ranForDays:
|
|
84916
|
+
ranForDays: z74.number(),
|
|
84795
84917
|
hypothesis: experimentHypothesisSchema,
|
|
84796
|
-
hypothesisStatement:
|
|
84918
|
+
hypothesisStatement: z74.string(),
|
|
84797
84919
|
/** The sections the new version owned, as labels — what was actually different. */
|
|
84798
|
-
changed:
|
|
84920
|
+
changed: z74.array(z74.string()),
|
|
84799
84921
|
goal: experimentGoalSchema,
|
|
84800
84922
|
variantGoal: experimentGoalSchema.nullable(),
|
|
84801
|
-
goalLabel:
|
|
84923
|
+
goalLabel: z74.string(),
|
|
84802
84924
|
verdict: experimentVerdictSchema,
|
|
84803
84925
|
/** Which version the numbers favoured, on `winner`. */
|
|
84804
|
-
winner:
|
|
84926
|
+
winner: z74.enum(["control", "variant"]).nullable(),
|
|
84805
84927
|
/** Which version kept the traffic. */
|
|
84806
|
-
survivor:
|
|
84928
|
+
survivor: z74.enum(["control", "variant"]),
|
|
84807
84929
|
/** True when what was kept is not what the numbers favoured. */
|
|
84808
|
-
againstVerdict:
|
|
84930
|
+
againstVerdict: z74.boolean(),
|
|
84809
84931
|
hypothesisOutcome: experimentHypothesisOutcomeSchema,
|
|
84810
84932
|
/** The numbers, as they were when the test ended. */
|
|
84811
|
-
result:
|
|
84812
|
-
control:
|
|
84813
|
-
variant:
|
|
84814
|
-
relativeLift:
|
|
84815
|
-
liftInterval:
|
|
84816
|
-
confidence:
|
|
84817
|
-
secondary:
|
|
84933
|
+
result: z74.object({
|
|
84934
|
+
control: z74.object({ visitors: z74.number(), conversions: z74.number(), rate: z74.number() }),
|
|
84935
|
+
variant: z74.object({ visitors: z74.number(), conversions: z74.number(), rate: z74.number() }),
|
|
84936
|
+
relativeLift: z74.number(),
|
|
84937
|
+
liftInterval: z74.tuple([z74.number(), z74.number()]),
|
|
84938
|
+
confidence: z74.number(),
|
|
84939
|
+
secondary: z74.array(experimentMetricSchema)
|
|
84818
84940
|
}),
|
|
84819
|
-
summary:
|
|
84941
|
+
summary: z74.string(),
|
|
84820
84942
|
/** Why it was ended, when whoever ended it said. */
|
|
84821
84943
|
endReason: experimentEndReasonSchema.nullable(),
|
|
84822
84944
|
/** What the test taught, or null when nobody has written it down yet. */
|
|
84823
|
-
learning:
|
|
84945
|
+
learning: z74.string().nullable()
|
|
84824
84946
|
});
|
|
84825
|
-
var experimentHistoryResponseSchema =
|
|
84826
|
-
tests:
|
|
84947
|
+
var experimentHistoryResponseSchema = z74.object({
|
|
84948
|
+
tests: z74.array(experimentHistoryEntrySchema)
|
|
84827
84949
|
});
|
|
84828
|
-
var experimentFinishResponseSchema =
|
|
84950
|
+
var experimentFinishResponseSchema = z74.object({
|
|
84829
84951
|
verdict: experimentVerdictSchema,
|
|
84830
|
-
survivor:
|
|
84952
|
+
survivor: z74.enum(["control", "variant"]),
|
|
84831
84953
|
/** True when `keep` went against a verdict the numbers had reached. */
|
|
84832
|
-
againstVerdict:
|
|
84833
|
-
summary:
|
|
84954
|
+
againstVerdict: z74.boolean(),
|
|
84955
|
+
summary: z74.string()
|
|
84834
84956
|
});
|
|
84835
84957
|
|
|
84836
84958
|
// src/commands/experiment/hints.ts
|
|
@@ -85050,11 +85172,11 @@ function parseEvidence(raw, rawArgs) {
|
|
|
85050
85172
|
}
|
|
85051
85173
|
|
|
85052
85174
|
// src/commands/experiment/identity.ts
|
|
85053
|
-
import { readFile as
|
|
85054
|
-
import
|
|
85175
|
+
import { readFile as readFile44 } from "fs/promises";
|
|
85176
|
+
import path51 from "path";
|
|
85055
85177
|
async function readPageInternalId(root, slug) {
|
|
85056
85178
|
try {
|
|
85057
|
-
const text3 = await
|
|
85179
|
+
const text3 = await readFile44(path51.resolve(root, "src", "pages", slug, "_definition.md"), "utf8");
|
|
85058
85180
|
return /^internalId:\s*"?([^"\s]+)"?\s*$/m.exec(text3)?.[1];
|
|
85059
85181
|
} catch {
|
|
85060
85182
|
return void 0;
|
|
@@ -85062,8 +85184,8 @@ async function readPageInternalId(root, slug) {
|
|
|
85062
85184
|
}
|
|
85063
85185
|
|
|
85064
85186
|
// src/commands/experiment/measurement.ts
|
|
85065
|
-
import { readFile as
|
|
85066
|
-
import
|
|
85187
|
+
import { readFile as readFile45 } from "fs/promises";
|
|
85188
|
+
import path52 from "path";
|
|
85067
85189
|
function eventDeclarationsIn(source) {
|
|
85068
85190
|
const found = [];
|
|
85069
85191
|
const pattern = /\b(data-baker-(?:view|event))\s*=\s*["']([^"']+)["']/g;
|
|
@@ -85123,7 +85245,7 @@ function sectionsOf(files, slug) {
|
|
|
85123
85245
|
return files.filter((file) => file.startsWith(prefix) && !file.slice(prefix.length).startsWith("index."));
|
|
85124
85246
|
}
|
|
85125
85247
|
async function declarationsOf(root, files) {
|
|
85126
|
-
const sources = await Promise.all(files.map((file) =>
|
|
85248
|
+
const sources = await Promise.all(files.map((file) => readFile45(path52.join(root, file), "utf8").catch(() => "")));
|
|
85127
85249
|
return sources.flatMap(eventDeclarationsIn);
|
|
85128
85250
|
}
|
|
85129
85251
|
|
|
@@ -86545,10 +86667,10 @@ function parseValueExpression(raw) {
|
|
|
86545
86667
|
return parts.map(parsePart);
|
|
86546
86668
|
}
|
|
86547
86669
|
function trackingFieldIds() {
|
|
86548
|
-
const
|
|
86549
|
-
if (!existsSync9(
|
|
86670
|
+
const path68 = join4(flowsDir(), "..", "tracking.ts");
|
|
86671
|
+
if (!existsSync9(path68)) return null;
|
|
86550
86672
|
try {
|
|
86551
|
-
const source = readFileSync19(
|
|
86673
|
+
const source = readFileSync19(path68, "utf-8");
|
|
86552
86674
|
const block2 = source.match(/TRACKING_FIELD_IDS\s*=\s*\[([\s\S]*?)\]\s*as const/)?.[1];
|
|
86553
86675
|
if (!block2) return null;
|
|
86554
86676
|
const ids2 = [...block2.matchAll(/"(tracking\.[a-z0-9_]+)"/g)].map((match) => match[1]);
|
|
@@ -87130,13 +87252,13 @@ function specsFromFile(parsed) {
|
|
|
87130
87252
|
return `${destField}${type}=${entry?.value ?? ""}`;
|
|
87131
87253
|
});
|
|
87132
87254
|
}
|
|
87133
|
-
function readSpecFile(
|
|
87255
|
+
function readSpecFile(path68) {
|
|
87134
87256
|
let raw;
|
|
87135
87257
|
try {
|
|
87136
|
-
raw =
|
|
87258
|
+
raw = path68 === "-" ? readFileSync20(0, "utf-8") : readFileSync20(path68, "utf-8");
|
|
87137
87259
|
} catch (error) {
|
|
87138
87260
|
refuse3(
|
|
87139
|
-
`Could not read ${
|
|
87261
|
+
`Could not read ${path68 === "-" ? "the mapping from stdin" : `"${path68}"`}: ${error instanceof Error ? error.message : String(error)}`
|
|
87140
87262
|
);
|
|
87141
87263
|
}
|
|
87142
87264
|
let parsed;
|
|
@@ -87144,7 +87266,7 @@ function readSpecFile(path67) {
|
|
|
87144
87266
|
parsed = JSON.parse(raw);
|
|
87145
87267
|
} catch (error) {
|
|
87146
87268
|
refuse3(
|
|
87147
|
-
`${
|
|
87269
|
+
`${path68 === "-" ? "stdin" : `"${path68}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
87148
87270
|
'Expected { "map": { "<destField>": "<value>", \u2026 } }'
|
|
87149
87271
|
);
|
|
87150
87272
|
}
|
|
@@ -87443,21 +87565,21 @@ var ARRAY_FIELDS = [
|
|
|
87443
87565
|
"tagIds"
|
|
87444
87566
|
];
|
|
87445
87567
|
var ARRAY_OWNERS = ["", "body"];
|
|
87446
|
-
function dropUnsetOptionals(sideEffect,
|
|
87568
|
+
function dropUnsetOptionals(sideEffect, path68) {
|
|
87447
87569
|
return OPTIONAL_STRINGS.flatMap((key) => {
|
|
87448
87570
|
if (!(key in sideEffect) || sideEffect[key] !== null && sideEffect[key] !== "") return [];
|
|
87449
87571
|
delete sideEffect[key];
|
|
87450
|
-
return [{ path:
|
|
87572
|
+
return [{ path: path68, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
|
|
87451
87573
|
});
|
|
87452
87574
|
}
|
|
87453
|
-
function fillNulledArrays(target2, prefix,
|
|
87575
|
+
function fillNulledArrays(target2, prefix, path68) {
|
|
87454
87576
|
return ARRAY_FIELDS.flatMap((key) => {
|
|
87455
87577
|
if (!(key in target2) || target2[key] !== null) return [];
|
|
87456
87578
|
target2[key] = [];
|
|
87457
|
-
return [{ path:
|
|
87579
|
+
return [{ path: path68, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
|
|
87458
87580
|
});
|
|
87459
87581
|
}
|
|
87460
|
-
function tidyEmailRecipients(sideEffect,
|
|
87582
|
+
function tidyEmailRecipients(sideEffect, path68) {
|
|
87461
87583
|
if (sideEffect.type !== "email") return [];
|
|
87462
87584
|
const config = sideEffect.encryptedConfig;
|
|
87463
87585
|
if (!config || typeof config !== "object" || Array.isArray(config)) return [];
|
|
@@ -87468,7 +87590,7 @@ function tidyEmailRecipients(sideEffect, path67) {
|
|
|
87468
87590
|
const tidied = to.join(", ");
|
|
87469
87591
|
if (tidied === raw) return [];
|
|
87470
87592
|
config.email = tidied;
|
|
87471
|
-
return [{ path:
|
|
87593
|
+
return [{ path: path68, change: "tidied the recipient list's spacing and separators" }];
|
|
87472
87594
|
}
|
|
87473
87595
|
function sideEffectsOf(node) {
|
|
87474
87596
|
if (!Array.isArray(node.sideEffects)) return [];
|
|
@@ -87477,13 +87599,13 @@ function sideEffectsOf(node) {
|
|
|
87477
87599
|
);
|
|
87478
87600
|
}
|
|
87479
87601
|
function normalizeSideEffect(sideEffect, where) {
|
|
87480
|
-
const
|
|
87602
|
+
const path68 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
|
|
87481
87603
|
const arrays = ARRAY_OWNERS.flatMap((owner) => {
|
|
87482
87604
|
const target2 = owner ? sideEffect[owner] : sideEffect;
|
|
87483
87605
|
if (!target2 || typeof target2 !== "object") return [];
|
|
87484
|
-
return fillNulledArrays(target2, owner ? `${owner}.` : "",
|
|
87606
|
+
return fillNulledArrays(target2, owner ? `${owner}.` : "", path68);
|
|
87485
87607
|
});
|
|
87486
|
-
return [...dropUnsetOptionals(sideEffect,
|
|
87608
|
+
return [...dropUnsetOptionals(sideEffect, path68), ...arrays, ...tidyEmailRecipients(sideEffect, path68)];
|
|
87487
87609
|
}
|
|
87488
87610
|
function normalizeFlowTree(tree) {
|
|
87489
87611
|
const changes = [];
|
|
@@ -88258,45 +88380,45 @@ var FUNNEL_VERTICALS = [
|
|
|
88258
88380
|
];
|
|
88259
88381
|
|
|
88260
88382
|
// ../api/src/funnel/wire.ts
|
|
88261
|
-
import { z as
|
|
88383
|
+
import { z as z75 } from "zod";
|
|
88262
88384
|
var MAX_STEPS = 40;
|
|
88263
88385
|
var MAX_PER_STEP = 20;
|
|
88264
|
-
var funnelTriggerSchema =
|
|
88265
|
-
|
|
88266
|
-
id:
|
|
88267
|
-
kind:
|
|
88386
|
+
var funnelTriggerSchema = z75.discriminatedUnion("kind", [
|
|
88387
|
+
z75.object({
|
|
88388
|
+
id: z75.string().min(1).max(64),
|
|
88389
|
+
kind: z75.literal("event"),
|
|
88268
88390
|
/** The event's key, from `--moments`: `form:…`, `submit:…`, `page:…`, `exit:…`. */
|
|
88269
|
-
eventKey:
|
|
88391
|
+
eventKey: z75.string().min(1).max(320)
|
|
88270
88392
|
}),
|
|
88271
|
-
|
|
88272
|
-
id:
|
|
88273
|
-
kind:
|
|
88393
|
+
z75.object({
|
|
88394
|
+
id: z75.string().min(1).max(64),
|
|
88395
|
+
kind: z75.literal("landing"),
|
|
88274
88396
|
/** The landing's internal id — what the page stamps on its rows, never its slug. */
|
|
88275
|
-
landingId:
|
|
88397
|
+
landingId: z75.string().min(1).max(120)
|
|
88276
88398
|
}),
|
|
88277
88399
|
/** Any landing being viewed — what a funnel's first step usually listens for. */
|
|
88278
|
-
|
|
88279
|
-
id:
|
|
88280
|
-
kind:
|
|
88400
|
+
z75.object({
|
|
88401
|
+
id: z75.string().min(1).max(64),
|
|
88402
|
+
kind: z75.literal("any_landing")
|
|
88281
88403
|
})
|
|
88282
88404
|
]);
|
|
88283
|
-
var funnelStepSchema =
|
|
88405
|
+
var funnelStepSchema = z75.object({
|
|
88284
88406
|
/** Lowercase letters, digits and underscores. Stable — renaming never changes it. */
|
|
88285
|
-
key:
|
|
88407
|
+
key: z75.string().min(1).max(64),
|
|
88286
88408
|
/** The client's own words for this step. Shown everywhere; unconstrained. */
|
|
88287
|
-
displayName:
|
|
88288
|
-
stepClass:
|
|
88409
|
+
displayName: z75.string().min(1).max(120),
|
|
88410
|
+
stepClass: z75.enum(FUNNEL_STEP_CLASSES),
|
|
88289
88411
|
/** Predecessor keys. Empty means people come in here. */
|
|
88290
|
-
after:
|
|
88412
|
+
after: z75.array(z75.string().max(64)).max(MAX_STEPS).default([]),
|
|
88291
88413
|
/** What moves somebody onto this step. Any one of them is enough. */
|
|
88292
|
-
triggers:
|
|
88414
|
+
triggers: z75.array(funnelTriggerSchema).max(MAX_PER_STEP).default([])
|
|
88293
88415
|
});
|
|
88294
|
-
var funnelRequestSchema =
|
|
88295
|
-
steps:
|
|
88416
|
+
var funnelRequestSchema = z75.object({
|
|
88417
|
+
steps: z75.array(funnelStepSchema).max(MAX_STEPS).optional(),
|
|
88296
88418
|
/** One step, added or replaced by its key. Everything else is untouched. */
|
|
88297
88419
|
step: funnelStepSchema.optional(),
|
|
88298
88420
|
/** One step taken out by key; whatever came after it moves up to whatever came before it. */
|
|
88299
|
-
removeStep:
|
|
88421
|
+
removeStep: z75.string().min(1).max(64).optional(),
|
|
88300
88422
|
/**
|
|
88301
88423
|
* ONE way in added to a step, leaving the rest of that step alone.
|
|
88302
88424
|
*
|
|
@@ -88306,129 +88428,129 @@ var funnelRequestSchema = z74.object({
|
|
|
88306
88428
|
* for the thing that is actually being asked ("this Form's booking should
|
|
88307
88429
|
* count here"), and it cannot drop anything.
|
|
88308
88430
|
*/
|
|
88309
|
-
addTrigger:
|
|
88431
|
+
addTrigger: z75.object({ stepKey: z75.string().min(1).max(64), eventKey: z75.string().min(1).max(320) }).optional(),
|
|
88310
88432
|
/** ONE way in taken off a step, by the event key it listens for. */
|
|
88311
|
-
removeTrigger:
|
|
88433
|
+
removeTrigger: z75.object({ stepKey: z75.string().min(1).max(64), eventKey: z75.string().min(1).max(320) }).optional(),
|
|
88312
88434
|
/** One of the ready-made graphs, instead of stating steps. */
|
|
88313
|
-
preset:
|
|
88435
|
+
preset: z75.enum(FUNNEL_VERTICALS).optional(),
|
|
88314
88436
|
/** Also return the suggestion library and the presets. */
|
|
88315
|
-
templates:
|
|
88437
|
+
templates: z75.boolean().optional(),
|
|
88316
88438
|
/**
|
|
88317
88439
|
* Also return everything a trigger can name: every event this company's
|
|
88318
88440
|
* Forms, pages and systems have actually produced, its sources, and its
|
|
88319
88441
|
* landings. The list to pick a trigger FROM, so nobody has to guess.
|
|
88320
88442
|
*/
|
|
88321
|
-
moments:
|
|
88443
|
+
moments: z75.boolean().optional(),
|
|
88322
88444
|
/** How many days the counts beside each step cover. Default 30. */
|
|
88323
|
-
days:
|
|
88445
|
+
days: z75.number().int().min(1).max(400).optional(),
|
|
88324
88446
|
/**
|
|
88325
88447
|
* The Forms, by slug, the caller names itself when no step counts them —
|
|
88326
88448
|
* `baker funnel` reads its own workspace and names each with the key that
|
|
88327
88449
|
* places it. The backend leaves them out of its own hint, so no Form is
|
|
88328
88450
|
* named twice.
|
|
88329
88451
|
*/
|
|
88330
|
-
namedByCaller:
|
|
88452
|
+
namedByCaller: z75.array(z75.string().min(1).max(200)).max(1e3).optional()
|
|
88331
88453
|
});
|
|
88332
|
-
var funnelResponseSchema =
|
|
88333
|
-
ok:
|
|
88334
|
-
data:
|
|
88454
|
+
var funnelResponseSchema = z75.object({
|
|
88455
|
+
ok: z75.literal(true),
|
|
88456
|
+
data: z75.object({
|
|
88335
88457
|
/** The published version, or 0 when this company has never drawn a funnel. */
|
|
88336
|
-
version:
|
|
88458
|
+
version: z75.number(),
|
|
88337
88459
|
/** 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:
|
|
88460
|
+
staged: z75.boolean(),
|
|
88461
|
+
steps: z75.array(
|
|
88462
|
+
z75.object({
|
|
88463
|
+
key: z75.string(),
|
|
88464
|
+
displayName: z75.string(),
|
|
88465
|
+
stepClass: z75.string(),
|
|
88466
|
+
after: z75.array(z75.string()),
|
|
88467
|
+
triggers: z75.array(z75.record(z75.string(), z75.unknown())),
|
|
88346
88468
|
/**
|
|
88347
88469
|
* Where the people who reach this step are sent: the sends of every
|
|
88348
88470
|
* system event among its triggers. Read-only here — a send belongs to
|
|
88349
88471
|
* the event and is set with `baker analytics sends`.
|
|
88350
88472
|
*/
|
|
88351
|
-
sends:
|
|
88352
|
-
|
|
88353
|
-
id:
|
|
88354
|
-
eventKey:
|
|
88355
|
-
platform:
|
|
88356
|
-
event:
|
|
88473
|
+
sends: z75.array(
|
|
88474
|
+
z75.object({
|
|
88475
|
+
id: z75.string(),
|
|
88476
|
+
eventKey: z75.string(),
|
|
88477
|
+
platform: z75.string(),
|
|
88478
|
+
event: z75.string(),
|
|
88357
88479
|
/** What happened to it in the window, in one line. */
|
|
88358
|
-
outcome:
|
|
88480
|
+
outcome: z75.string().optional()
|
|
88359
88481
|
})
|
|
88360
88482
|
),
|
|
88361
88483
|
/** How many people reached this step in the window. Counted in people, across visits and devices. */
|
|
88362
|
-
people:
|
|
88484
|
+
people: z75.number().optional(),
|
|
88363
88485
|
/** How many reached this step or any step after it. */
|
|
88364
|
-
atLeast:
|
|
88486
|
+
atLeast: z75.number().optional(),
|
|
88365
88487
|
/** How many got as far as the step before it. Absent for a way in. */
|
|
88366
|
-
before:
|
|
88488
|
+
before: z75.number().optional(),
|
|
88367
88489
|
/** `atLeast / before`, 0–1. Absent where there is no before. */
|
|
88368
|
-
share:
|
|
88369
|
-
events:
|
|
88370
|
-
money:
|
|
88490
|
+
share: z75.number().optional(),
|
|
88491
|
+
events: z75.number().optional(),
|
|
88492
|
+
money: z75.record(z75.string(), z75.number()).optional()
|
|
88371
88493
|
})
|
|
88372
88494
|
),
|
|
88373
88495
|
/** The window `people` and friends were counted over. */
|
|
88374
|
-
window:
|
|
88496
|
+
window: z75.object({ startDate: z75.string(), endDate: z75.string(), days: z75.number() }).optional(),
|
|
88375
88497
|
/** The step that loses the most people, by key. Absent while nothing is lost. */
|
|
88376
|
-
worstStep:
|
|
88498
|
+
worstStep: z75.string().optional(),
|
|
88377
88499
|
/**
|
|
88378
88500
|
* The steps this write took off the funnel, by key. Present on every write,
|
|
88379
88501
|
* empty when nothing was dropped — the answer to "did my restatement forget
|
|
88380
88502
|
* one", given in the same turn.
|
|
88381
88503
|
*/
|
|
88382
|
-
removed:
|
|
88504
|
+
removed: z75.array(z75.string()).optional(),
|
|
88383
88505
|
/**
|
|
88384
88506
|
* What this company counts as page conversions, this chat's staged counts
|
|
88385
88507
|
* included, by event key and name. Here so a Form's ending can say it is
|
|
88386
88508
|
* already counted — the agent choosing a step's way in should use that
|
|
88387
88509
|
* same key — and outside `moments` because the plain read's hints say it too.
|
|
88388
88510
|
*/
|
|
88389
|
-
counted:
|
|
88511
|
+
counted: z75.array(z75.object({ eventKey: z75.string(), name: z75.string() })).optional(),
|
|
88390
88512
|
/** Everything a trigger can name, when asked for with `moments`. */
|
|
88391
|
-
moments:
|
|
88392
|
-
events:
|
|
88393
|
-
|
|
88394
|
-
eventKey:
|
|
88513
|
+
moments: z75.object({
|
|
88514
|
+
events: z75.array(
|
|
88515
|
+
z75.object({
|
|
88516
|
+
eventKey: z75.string(),
|
|
88395
88517
|
/** `site` for a page or Form, `systems` for the client's own server, `both` when both sent it. */
|
|
88396
|
-
origin:
|
|
88518
|
+
origin: z75.string().optional(),
|
|
88397
88519
|
/** The Form it belongs to, when it belongs to one. */
|
|
88398
|
-
flowSlug:
|
|
88399
|
-
nodeType:
|
|
88400
|
-
events:
|
|
88401
|
-
sessions:
|
|
88520
|
+
flowSlug: z75.string().optional(),
|
|
88521
|
+
nodeType: z75.string().optional(),
|
|
88522
|
+
events: z75.number(),
|
|
88523
|
+
sessions: z75.number(),
|
|
88402
88524
|
/** The name it is counted under as a page conversion, when the company counts it. Informational. */
|
|
88403
|
-
countedAs:
|
|
88525
|
+
countedAs: z75.string().optional(),
|
|
88404
88526
|
/**
|
|
88405
88527
|
* The step of the Form this happens on, in the Form's own words —
|
|
88406
88528
|
* never a node id. Present on a Form's ending read from the Form
|
|
88407
88529
|
* itself, which is the only place the name is known.
|
|
88408
88530
|
*/
|
|
88409
|
-
step:
|
|
88531
|
+
step: z75.string().optional(),
|
|
88410
88532
|
/**
|
|
88411
88533
|
* `false` on an ending a Form raises natively but nobody has fired
|
|
88412
88534
|
* yet — derived from the Form, offered at zero, exactly as a
|
|
88413
88535
|
* Source is offered before its first send. Absent means it came
|
|
88414
88536
|
* from the event store and therefore happened.
|
|
88415
88537
|
*/
|
|
88416
|
-
seen:
|
|
88538
|
+
seen: z75.boolean().optional()
|
|
88417
88539
|
})
|
|
88418
88540
|
),
|
|
88419
88541
|
/** The company's sources: a system of theirs that posts events to Baker, by the event name it raises. */
|
|
88420
|
-
sources:
|
|
88421
|
-
landings:
|
|
88542
|
+
sources: z75.array(z75.object({ eventKey: z75.string(), name: z75.string(), status: z75.string() })),
|
|
88543
|
+
landings: z75.array(z75.object({ landingId: z75.string(), title: z75.string(), slug: z75.string() }))
|
|
88422
88544
|
}).optional(),
|
|
88423
|
-
templates:
|
|
88424
|
-
|
|
88425
|
-
key:
|
|
88426
|
-
displayName:
|
|
88427
|
-
meaning:
|
|
88428
|
-
stepClass:
|
|
88545
|
+
templates: z75.array(
|
|
88546
|
+
z75.object({
|
|
88547
|
+
key: z75.string(),
|
|
88548
|
+
displayName: z75.string(),
|
|
88549
|
+
meaning: z75.string(),
|
|
88550
|
+
stepClass: z75.string()
|
|
88429
88551
|
})
|
|
88430
88552
|
).optional(),
|
|
88431
|
-
presets:
|
|
88553
|
+
presets: z75.array(z75.object({ vertical: z75.string(), label: z75.string(), description: z75.string(), steps: z75.array(z75.string()) })).optional(),
|
|
88432
88554
|
/**
|
|
88433
88555
|
* What each stage means, the test to apply, and the same stage in three
|
|
88434
88556
|
* shapes of business.
|
|
@@ -88439,17 +88561,17 @@ var funnelResponseSchema = z74.object({
|
|
|
88439
88561
|
* engaged or customer. Same source as the dashboard's stage picker, so the
|
|
88440
88562
|
* two cannot teach it differently.
|
|
88441
88563
|
*/
|
|
88442
|
-
stages:
|
|
88443
|
-
|
|
88444
|
-
stepClass:
|
|
88445
|
-
title:
|
|
88446
|
-
meaning:
|
|
88447
|
-
test:
|
|
88448
|
-
examples:
|
|
88564
|
+
stages: z75.array(
|
|
88565
|
+
z75.object({
|
|
88566
|
+
stepClass: z75.string(),
|
|
88567
|
+
title: z75.string(),
|
|
88568
|
+
meaning: z75.string(),
|
|
88569
|
+
test: z75.string(),
|
|
88570
|
+
examples: z75.object({ shop: z75.string(), selfServe: z75.string(), salesLed: z75.string() })
|
|
88449
88571
|
})
|
|
88450
88572
|
).optional()
|
|
88451
88573
|
}),
|
|
88452
|
-
hints:
|
|
88574
|
+
hints: z75.array(z75.string()).optional()
|
|
88453
88575
|
});
|
|
88454
88576
|
|
|
88455
88577
|
// src/commands/funnel/funnelArgs.ts
|
|
@@ -88989,10 +89111,10 @@ async function stageOps(ops) {
|
|
|
88989
89111
|
handleError5(err);
|
|
88990
89112
|
}
|
|
88991
89113
|
}
|
|
88992
|
-
async function draftAction2(
|
|
89114
|
+
async function draftAction2(path68, body, chat) {
|
|
88993
89115
|
const chatId = resolveChatId(chat);
|
|
88994
89116
|
try {
|
|
88995
|
-
const { data, hints: hints2 } = splitHints(await apiPost(
|
|
89117
|
+
const { data, hints: hints2 } = splitHints(await apiPost(path68, { chatId, ...body }));
|
|
88996
89118
|
writeJsonEnvelope({ ok: true, data, ...hints2.length > 0 ? { hints: hints2 } : {} });
|
|
88997
89119
|
return data;
|
|
88998
89120
|
} catch (err) {
|
|
@@ -91503,7 +91625,7 @@ function cropSprite(input, region) {
|
|
|
91503
91625
|
|
|
91504
91626
|
// src/lib/image/io.ts
|
|
91505
91627
|
import { randomBytes } from "crypto";
|
|
91506
|
-
import { glob as fsGlob, readFile as
|
|
91628
|
+
import { glob as fsGlob, readFile as readFile46, rename as rename2, stat as stat9, writeFile as writeFile26 } from "fs/promises";
|
|
91507
91629
|
import { dirname as dirname2, extname as extname3, join as join5, resolve as resolve4 } from "path";
|
|
91508
91630
|
var REMOTE_RE = /^https?:\/\//i;
|
|
91509
91631
|
var GLOB_RE = /[*?[\]{}]/;
|
|
@@ -91536,11 +91658,11 @@ async function readImageBuffer(pathOrUrl) {
|
|
|
91536
91658
|
const { buffer } = await fetchExternalBytes(pathOrUrl, { maxBytes: MAX_REMOTE_IMAGE_BYTES });
|
|
91537
91659
|
return buffer;
|
|
91538
91660
|
}
|
|
91539
|
-
return
|
|
91661
|
+
return readFile46(pathOrUrl);
|
|
91540
91662
|
}
|
|
91541
|
-
async function isDirectory(
|
|
91663
|
+
async function isDirectory(path68) {
|
|
91542
91664
|
try {
|
|
91543
|
-
const s = await stat9(
|
|
91665
|
+
const s = await stat9(path68);
|
|
91544
91666
|
return s.isDirectory();
|
|
91545
91667
|
} catch {
|
|
91546
91668
|
return false;
|
|
@@ -91559,7 +91681,7 @@ async function atomicWrite(targetPath, data) {
|
|
|
91559
91681
|
const absolute = resolve4(targetPath);
|
|
91560
91682
|
const dir = dirname2(absolute);
|
|
91561
91683
|
const tmp = join5(dir, `.baker-image-${randomBytes(8).toString("hex")}.tmp`);
|
|
91562
|
-
await
|
|
91684
|
+
await writeFile26(tmp, data);
|
|
91563
91685
|
await rename2(tmp, absolute);
|
|
91564
91686
|
}
|
|
91565
91687
|
|
|
@@ -91968,13 +92090,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
|
|
|
91968
92090
|
}
|
|
91969
92091
|
function disambiguate(paths) {
|
|
91970
92092
|
const taken = /* @__PURE__ */ new Set();
|
|
91971
|
-
return paths.map((
|
|
91972
|
-
if (!taken.has(
|
|
91973
|
-
taken.add(
|
|
91974
|
-
return
|
|
92093
|
+
return paths.map((path68) => {
|
|
92094
|
+
if (!taken.has(path68)) {
|
|
92095
|
+
taken.add(path68);
|
|
92096
|
+
return path68;
|
|
91975
92097
|
}
|
|
91976
|
-
const ext = extname4(
|
|
91977
|
-
const stem =
|
|
92098
|
+
const ext = extname4(path68);
|
|
92099
|
+
const stem = path68.slice(0, path68.length - ext.length);
|
|
91978
92100
|
let n = 2;
|
|
91979
92101
|
while (taken.has(`${stem}-${n}${ext}`)) n += 1;
|
|
91980
92102
|
const unique2 = `${stem}-${n}${ext}`;
|
|
@@ -92100,10 +92222,10 @@ async function runDownloads(plan) {
|
|
|
92100
92222
|
const paths = disambiguate(fetched.map((item) => item.path));
|
|
92101
92223
|
const downloaded = [];
|
|
92102
92224
|
for (const [index, item] of fetched.entries()) {
|
|
92103
|
-
const
|
|
92225
|
+
const path68 = paths[index] ?? item.path;
|
|
92104
92226
|
try {
|
|
92105
|
-
await atomicWrite(
|
|
92106
|
-
downloaded.push({ input: item.input, output:
|
|
92227
|
+
await atomicWrite(path68, item.buffer);
|
|
92228
|
+
downloaded.push({ input: item.input, output: path68, bytes: item.buffer.length, contentType: item.contentType });
|
|
92107
92229
|
} catch (err) {
|
|
92108
92230
|
failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
|
|
92109
92231
|
}
|
|
@@ -94998,8 +95120,8 @@ Full guide: __tooling__/docs/tools/baker/images.md`
|
|
|
94998
95120
|
import { defineCommand as defineCommand226 } from "citty";
|
|
94999
95121
|
|
|
95000
95122
|
// src/commands/landing/critique.ts
|
|
95001
|
-
import { readdir as readdir22, readFile as
|
|
95002
|
-
import
|
|
95123
|
+
import { readdir as readdir22, readFile as readFile48, stat as stat11 } from "fs/promises";
|
|
95124
|
+
import path55 from "path";
|
|
95003
95125
|
import { defineCommand as defineCommand213 } from "citty";
|
|
95004
95126
|
|
|
95005
95127
|
// src/engine/landing/lib/constants.ts
|
|
@@ -96190,27 +96312,27 @@ function describeCounts(findings) {
|
|
|
96190
96312
|
}
|
|
96191
96313
|
|
|
96192
96314
|
// src/commands/landing/snapshot.ts
|
|
96193
|
-
import { mkdir as
|
|
96194
|
-
import
|
|
96315
|
+
import { mkdir as mkdir16, rename as rename3, writeFile as writeFile27 } from "fs/promises";
|
|
96316
|
+
import path53 from "path";
|
|
96195
96317
|
var CRITIC_VERSION = "4";
|
|
96196
96318
|
function critiqueCacheDir(projectRoot) {
|
|
96197
|
-
return
|
|
96319
|
+
return path53.join(projectRoot, ".cache", "landing-critique");
|
|
96198
96320
|
}
|
|
96199
96321
|
function snapshotPath(projectRoot, slug) {
|
|
96200
|
-
return
|
|
96322
|
+
return path53.join(critiqueCacheDir(projectRoot), `${slug}.json`);
|
|
96201
96323
|
}
|
|
96202
96324
|
async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
96203
|
-
await
|
|
96325
|
+
await mkdir16(critiqueCacheDir(projectRoot), { recursive: true });
|
|
96204
96326
|
const dest = snapshotPath(projectRoot, snapshot.slug);
|
|
96205
96327
|
const tmp = `${dest}.tmp`;
|
|
96206
|
-
await
|
|
96328
|
+
await writeFile27(tmp, `${JSON.stringify(snapshot, null, 2)}
|
|
96207
96329
|
`, "utf8");
|
|
96208
96330
|
await rename3(tmp, dest);
|
|
96209
96331
|
}
|
|
96210
96332
|
|
|
96211
96333
|
// src/commands/landing/source-version.ts
|
|
96212
|
-
import { readdir as readdir21, readFile as
|
|
96213
|
-
import
|
|
96334
|
+
import { readdir as readdir21, readFile as readFile47, stat as stat10 } from "fs/promises";
|
|
96335
|
+
import path54 from "path";
|
|
96214
96336
|
var CRITIQUED_ROOTS = ["src/pages/", "src/components/"];
|
|
96215
96337
|
async function landingSourceRelPaths(root, slug) {
|
|
96216
96338
|
const files = await landingGraphFiles(root, slug);
|
|
@@ -96219,7 +96341,7 @@ async function landingSourceRelPaths(root, slug) {
|
|
|
96219
96341
|
async function readLandingSources(root, slug) {
|
|
96220
96342
|
const rel = await landingSourceRelPaths(root, slug);
|
|
96221
96343
|
const out = [];
|
|
96222
|
-
for (const r of rel) out.push({ path: r, text: await
|
|
96344
|
+
for (const r of rel) out.push({ path: r, text: await readFile47(path54.join(root, r), "utf8") });
|
|
96223
96345
|
return out;
|
|
96224
96346
|
}
|
|
96225
96347
|
async function computeLandingSourceSha(root, slug) {
|
|
@@ -96228,7 +96350,7 @@ async function computeLandingSourceSha(root, slug) {
|
|
|
96228
96350
|
for (const r of rel) {
|
|
96229
96351
|
let bytes;
|
|
96230
96352
|
try {
|
|
96231
|
-
bytes = await
|
|
96353
|
+
bytes = await readFile47(path54.join(root, r));
|
|
96232
96354
|
} catch {
|
|
96233
96355
|
bytes = Buffer.alloc(0);
|
|
96234
96356
|
}
|
|
@@ -96238,16 +96360,16 @@ async function computeLandingSourceSha(root, slug) {
|
|
|
96238
96360
|
}
|
|
96239
96361
|
async function computeLegacyLandingSourceSha(landingDir) {
|
|
96240
96362
|
const rel = [];
|
|
96241
|
-
if (await isFile2(
|
|
96242
|
-
for (const abs of await walkAstro(
|
|
96243
|
-
rel.push(
|
|
96363
|
+
if (await isFile2(path54.join(landingDir, "index.astro"))) rel.push("index.astro");
|
|
96364
|
+
for (const abs of await walkAstro(path54.join(landingDir, "_components"))) {
|
|
96365
|
+
rel.push(path54.relative(landingDir, abs).split(path54.sep).join("/"));
|
|
96244
96366
|
}
|
|
96245
96367
|
rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
96246
96368
|
const parts = [];
|
|
96247
96369
|
for (const r of rel) {
|
|
96248
96370
|
let bytes;
|
|
96249
96371
|
try {
|
|
96250
|
-
bytes = await
|
|
96372
|
+
bytes = await readFile47(path54.join(landingDir, r));
|
|
96251
96373
|
} catch {
|
|
96252
96374
|
bytes = Buffer.alloc(0);
|
|
96253
96375
|
}
|
|
@@ -96271,7 +96393,7 @@ async function walkAstro(dir) {
|
|
|
96271
96393
|
}
|
|
96272
96394
|
const out = [];
|
|
96273
96395
|
for (const entry of entries) {
|
|
96274
|
-
const abs =
|
|
96396
|
+
const abs = path54.join(dir, entry.name);
|
|
96275
96397
|
if (entry.isDirectory()) out.push(...await walkAstro(abs));
|
|
96276
96398
|
else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
|
|
96277
96399
|
}
|
|
@@ -96332,7 +96454,7 @@ var critiqueCommand2 = defineCommand213({
|
|
|
96332
96454
|
{ availableSlugs: await listLandingSlugs(projectRoot) }
|
|
96333
96455
|
);
|
|
96334
96456
|
}
|
|
96335
|
-
if (!await isDir2(
|
|
96457
|
+
if (!await isDir2(path55.resolve(projectRoot, "src", "pages", slug))) {
|
|
96336
96458
|
fail10("NOT_FOUND", `No landing at src/pages/${slug}/`, {
|
|
96337
96459
|
availableSlugs: await listLandingSlugs(projectRoot)
|
|
96338
96460
|
});
|
|
@@ -96375,7 +96497,7 @@ async function critiqueOne(projectRoot, slug, brand, competitors) {
|
|
|
96375
96497
|
const [sources, compositionSha, sourceSha] = await Promise.all([
|
|
96376
96498
|
readLandingSources(projectRoot, slug),
|
|
96377
96499
|
computeLandingSourceSha(projectRoot, slug),
|
|
96378
|
-
computeLegacyLandingSourceSha(
|
|
96500
|
+
computeLegacyLandingSourceSha(path55.resolve(projectRoot, "src", "pages", slug))
|
|
96379
96501
|
]);
|
|
96380
96502
|
const report = critiqueLanding({ slug, sources, brand, competitors });
|
|
96381
96503
|
let snapshotFailed = false;
|
|
@@ -96395,7 +96517,7 @@ async function critiqueOne(projectRoot, slug, brand, competitors) {
|
|
|
96395
96517
|
return { slug, report, snapshotFailed };
|
|
96396
96518
|
}
|
|
96397
96519
|
async function readCompetitorNames(projectRoot) {
|
|
96398
|
-
const dir =
|
|
96520
|
+
const dir = path55.join(projectRoot, "src", "content", "competitors");
|
|
96399
96521
|
let entries;
|
|
96400
96522
|
try {
|
|
96401
96523
|
entries = (await readdir22(dir)).filter((f) => f.endsWith(".md"));
|
|
@@ -96404,9 +96526,9 @@ async function readCompetitorNames(projectRoot) {
|
|
|
96404
96526
|
}
|
|
96405
96527
|
const names = [];
|
|
96406
96528
|
for (const entry of entries) {
|
|
96407
|
-
names.push(
|
|
96529
|
+
names.push(path55.basename(entry, ".md").replace(/[-_]+/g, " "));
|
|
96408
96530
|
try {
|
|
96409
|
-
const head = (await
|
|
96531
|
+
const head = (await readFile48(path55.join(dir, entry), "utf8")).slice(0, 2e3);
|
|
96410
96532
|
const titled = /^\s*(?:title|name)\s*:\s*["']?([^"'\n]+)["']?\s*$/im.exec(head);
|
|
96411
96533
|
if (titled?.[1]) names.push(titled[1].trim());
|
|
96412
96534
|
} catch {
|
|
@@ -96416,7 +96538,7 @@ async function readCompetitorNames(projectRoot) {
|
|
|
96416
96538
|
}
|
|
96417
96539
|
async function listLandingSlugs(projectRoot) {
|
|
96418
96540
|
try {
|
|
96419
|
-
const entries = await readdir22(
|
|
96541
|
+
const entries = await readdir22(path55.join(projectRoot, "src", "pages"), { withFileTypes: true });
|
|
96420
96542
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
96421
96543
|
} catch {
|
|
96422
96544
|
return [];
|
|
@@ -96540,10 +96662,10 @@ function exportStatusHints(view) {
|
|
|
96540
96662
|
}
|
|
96541
96663
|
return ["Still being prepared. Carry on with other work and check again in a minute \u2014 never sleep-loop on it."];
|
|
96542
96664
|
}
|
|
96543
|
-
function parseProfileFlag(raw,
|
|
96665
|
+
function parseProfileFlag(raw, readFile52 = (path68) => readFileSync25(path68, "utf8")) {
|
|
96544
96666
|
let parsed;
|
|
96545
96667
|
try {
|
|
96546
|
-
parsed = JSON.parse(raw.startsWith("@") ?
|
|
96668
|
+
parsed = JSON.parse(raw.startsWith("@") ? readFile52(raw.slice(1)) : raw);
|
|
96547
96669
|
} catch (error) {
|
|
96548
96670
|
return {
|
|
96549
96671
|
ok: false,
|
|
@@ -96714,8 +96836,8 @@ var addCommand = defineCommand215({
|
|
|
96714
96836
|
});
|
|
96715
96837
|
|
|
96716
96838
|
// src/commands/landing/inspiration/code.ts
|
|
96717
|
-
import { mkdir as
|
|
96718
|
-
import
|
|
96839
|
+
import { mkdir as mkdir17, writeFile as writeFile28 } from "fs/promises";
|
|
96840
|
+
import path56 from "path";
|
|
96719
96841
|
import { defineCommand as defineCommand216 } from "citty";
|
|
96720
96842
|
registerSchema({
|
|
96721
96843
|
command: "landing.inspiration.code",
|
|
@@ -96738,10 +96860,10 @@ var codeCommand = defineCommand216({
|
|
|
96738
96860
|
try {
|
|
96739
96861
|
const id = args8.id;
|
|
96740
96862
|
const data = await apiGet("/api/landing-inspiration/section-code", { id });
|
|
96741
|
-
const dir =
|
|
96742
|
-
await
|
|
96743
|
-
const file =
|
|
96744
|
-
await
|
|
96863
|
+
const dir = path56.join(process.cwd(), ".baker", "inspiration", id);
|
|
96864
|
+
await mkdir17(dir, { recursive: true });
|
|
96865
|
+
const file = path56.join(dir, "section.html");
|
|
96866
|
+
await writeFile28(file, data.html);
|
|
96745
96867
|
const hints2 = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
96746
96868
|
const fidelity = fidelityHint(data.fidelity);
|
|
96747
96869
|
if (fidelity) hints2.push(fidelity);
|
|
@@ -96750,7 +96872,7 @@ var codeCommand = defineCommand216({
|
|
|
96750
96872
|
ok: true,
|
|
96751
96873
|
data: {
|
|
96752
96874
|
id,
|
|
96753
|
-
file:
|
|
96875
|
+
file: path56.relative(process.cwd(), file),
|
|
96754
96876
|
bytes: data.html.length,
|
|
96755
96877
|
fidelity: data.fidelity,
|
|
96756
96878
|
reproduction_notes: data.reproductionNotes,
|
|
@@ -97209,8 +97331,8 @@ function classifyCaptureFailure(error) {
|
|
|
97209
97331
|
}
|
|
97210
97332
|
|
|
97211
97333
|
// src/engine/landing-library/run.ts
|
|
97212
|
-
import { mkdir as
|
|
97213
|
-
import
|
|
97334
|
+
import { mkdir as mkdir18, writeFile as writeFile30 } from "fs/promises";
|
|
97335
|
+
import path58 from "path";
|
|
97214
97336
|
|
|
97215
97337
|
// ../proxy/src/preflight.ts
|
|
97216
97338
|
import http from "http";
|
|
@@ -98627,11 +98749,11 @@ async function renderBundleToPng(browser, html, viewportWidth, options = {}) {
|
|
|
98627
98749
|
}
|
|
98628
98750
|
|
|
98629
98751
|
// src/engine/landing-library/report.ts
|
|
98630
|
-
import { writeFile as
|
|
98631
|
-
import
|
|
98752
|
+
import { writeFile as writeFile29 } from "fs/promises";
|
|
98753
|
+
import path57 from "path";
|
|
98632
98754
|
async function writeCaptureReport(manifest, outDir) {
|
|
98633
|
-
const file =
|
|
98634
|
-
await
|
|
98755
|
+
const file = path57.join(outDir, "report.html");
|
|
98756
|
+
await writeFile29(file, renderReport(manifest));
|
|
98635
98757
|
return file;
|
|
98636
98758
|
}
|
|
98637
98759
|
function escapeHtml3(value2) {
|
|
@@ -98774,32 +98896,32 @@ async function reproducePage(args8) {
|
|
|
98774
98896
|
const { browser, page, outDir, pageUrl, livePageShot } = args8;
|
|
98775
98897
|
const built = await buildSectionBundle(page, "body", pageUrl).catch(() => null);
|
|
98776
98898
|
if (!built) return { bundle: null, fidelity: null };
|
|
98777
|
-
await
|
|
98899
|
+
await writeFile30(path58.join(outDir, "page.html"), built.html);
|
|
98778
98900
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width, {
|
|
98779
98901
|
wholePage: true,
|
|
98780
98902
|
timeoutMs: 6e4
|
|
98781
98903
|
});
|
|
98782
98904
|
if (!rendered || !livePageShot) return { bundle: "page.html", fidelity: null };
|
|
98783
|
-
await
|
|
98905
|
+
await writeFile30(path58.join(outDir, "page-rendered.png"), rendered);
|
|
98784
98906
|
const { score, note } = await scoreFidelity(livePageShot, rendered);
|
|
98785
98907
|
return { bundle: "page.html", fidelity: score, ...note ? { fidelityNote: note } : {} };
|
|
98786
98908
|
}
|
|
98787
98909
|
async function captureOneSection(args8) {
|
|
98788
98910
|
const { browser, page, candidate, sectionsDir, outDir, pageUrl, withCode } = args8;
|
|
98789
|
-
const dir =
|
|
98790
|
-
await
|
|
98911
|
+
const dir = path58.join(sectionsDir, String(candidate.index).padStart(2, "0"));
|
|
98912
|
+
await mkdir18(dir, { recursive: true });
|
|
98791
98913
|
const desktop = await captureSection(page, candidate);
|
|
98792
|
-
if (desktop) await
|
|
98914
|
+
if (desktop) await writeFile30(path58.join(dir, "desktop.png"), desktop);
|
|
98793
98915
|
const visualHash = desktop ? await perceptualHash(desktop) : null;
|
|
98794
98916
|
const motion = await collectMotion(page, candidate.selector);
|
|
98795
98917
|
const built = withCode ? await buildSectionBundle(page, candidate.selector, pageUrl) : null;
|
|
98796
98918
|
let fidelity = null;
|
|
98797
98919
|
let fidelityNote;
|
|
98798
98920
|
if (built) {
|
|
98799
|
-
await
|
|
98921
|
+
await writeFile30(path58.join(dir, "section.html"), built.html);
|
|
98800
98922
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width);
|
|
98801
98923
|
if (rendered && desktop) {
|
|
98802
|
-
await
|
|
98924
|
+
await writeFile30(path58.join(dir, "section-rendered.png"), rendered);
|
|
98803
98925
|
const result = await scoreFidelity(desktop, rendered);
|
|
98804
98926
|
fidelity = result.score;
|
|
98805
98927
|
fidelityNote = result.note;
|
|
@@ -98807,9 +98929,9 @@ async function captureOneSection(args8) {
|
|
|
98807
98929
|
}
|
|
98808
98930
|
return {
|
|
98809
98931
|
...candidate,
|
|
98810
|
-
desktopShot: desktop ?
|
|
98932
|
+
desktopShot: desktop ? path58.relative(outDir, path58.join(dir, "desktop.png")) : null,
|
|
98811
98933
|
mobileShot: null,
|
|
98812
|
-
bundle: built ?
|
|
98934
|
+
bundle: built ? path58.relative(outDir, path58.join(dir, "section.html")) : null,
|
|
98813
98935
|
fidelity,
|
|
98814
98936
|
...fidelityNote ? { fidelityNote } : {},
|
|
98815
98937
|
...built ? { cssStats: built.stats } : {},
|
|
@@ -98828,9 +98950,9 @@ async function captureMobileShots(args8) {
|
|
|
98828
98950
|
for (const section2 of sections) {
|
|
98829
98951
|
const shot = await captureSectionOnMobile(mobile.page, section2);
|
|
98830
98952
|
if (!shot) continue;
|
|
98831
|
-
const file =
|
|
98832
|
-
await
|
|
98833
|
-
section2.mobileShot =
|
|
98953
|
+
const file = path58.join(sectionsDir, String(section2.index).padStart(2, "0"), "mobile.png");
|
|
98954
|
+
await writeFile30(file, shot);
|
|
98955
|
+
section2.mobileShot = path58.relative(outDir, file);
|
|
98834
98956
|
}
|
|
98835
98957
|
} finally {
|
|
98836
98958
|
await mobile.context.close();
|
|
@@ -98845,10 +98967,10 @@ async function captureMotionTakes(args8) {
|
|
|
98845
98967
|
const filmOne = async (section2) => {
|
|
98846
98968
|
const take = await captureMotionTake(browser, pageUrl, section2.selector).catch(() => null);
|
|
98847
98969
|
if (!take) return;
|
|
98848
|
-
const dir =
|
|
98849
|
-
const file =
|
|
98850
|
-
await
|
|
98851
|
-
section2.motionFilmstrip =
|
|
98970
|
+
const dir = path58.join(sectionsDir, String(section2.index).padStart(2, "0"));
|
|
98971
|
+
const file = path58.join(dir, "motion-filmstrip.png");
|
|
98972
|
+
await writeFile30(file, take.filmstrip);
|
|
98973
|
+
section2.motionFilmstrip = path58.relative(outDir, file);
|
|
98852
98974
|
log(` [${section2.index}] ${section2.motion.summary}`);
|
|
98853
98975
|
};
|
|
98854
98976
|
const queue = [...moving];
|
|
@@ -98905,7 +99027,7 @@ async function captureAlternateViews(args8) {
|
|
|
98905
99027
|
async function reproduceWholePage(args8) {
|
|
98906
99028
|
const { browser, page, outDir, pageUrl, withCode, log } = args8;
|
|
98907
99029
|
const fullPage = await page.screenshot({ type: "png", fullPage: true }).catch(() => null);
|
|
98908
|
-
if (fullPage) await
|
|
99030
|
+
if (fullPage) await writeFile30(path58.join(outDir, "full-page.png"), fullPage);
|
|
98909
99031
|
if (!withCode) return { bundle: null, fidelity: null };
|
|
98910
99032
|
const reproduction = await reproducePage({ browser, page, outDir, pageUrl, livePageShot: fullPage });
|
|
98911
99033
|
log(`page reproduction: ${reproduction.fidelity === null ? "unavailable" : reproduction.fidelity.toFixed(2)}`);
|
|
@@ -98976,7 +99098,7 @@ async function openViaLadder(args8) {
|
|
|
98976
99098
|
async function scrapeLanding(options) {
|
|
98977
99099
|
const timeoutMs = options.timeoutMs ?? 45e3;
|
|
98978
99100
|
const log = options.onProgress ?? (() => void 0);
|
|
98979
|
-
const sectionsDir =
|
|
99101
|
+
const sectionsDir = path58.join(options.outDir, "sections");
|
|
98980
99102
|
const nonPublic = refuseNonPublicUrl(options.url);
|
|
98981
99103
|
if (nonPublic) {
|
|
98982
99104
|
throw new BlockedPageError({
|
|
@@ -98996,7 +99118,7 @@ async function scrapeLanding(options) {
|
|
|
98996
99118
|
const withMotion = options.motion !== false && !escalated;
|
|
98997
99119
|
const renderBrowser = options.code === false ? null : await launchBrowser();
|
|
98998
99120
|
try {
|
|
98999
|
-
await
|
|
99121
|
+
await mkdir18(sectionsDir, { recursive: true });
|
|
99000
99122
|
const sections = await captureSections({
|
|
99001
99123
|
browser: renderBrowser ?? browser,
|
|
99002
99124
|
page,
|
|
@@ -99038,7 +99160,7 @@ async function scrapeLanding(options) {
|
|
|
99038
99160
|
security: prepared.security,
|
|
99039
99161
|
captureTier: tier
|
|
99040
99162
|
};
|
|
99041
|
-
await
|
|
99163
|
+
await writeFile30(path58.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
99042
99164
|
`);
|
|
99043
99165
|
if (options.report !== false) {
|
|
99044
99166
|
const reportPath = await writeCaptureReport(manifest, options.outDir);
|
|
@@ -99053,28 +99175,28 @@ async function scrapeLanding(options) {
|
|
|
99053
99175
|
|
|
99054
99176
|
// src/commands/landing/inspiration/captureOut.ts
|
|
99055
99177
|
import { existsSync as existsSync12 } from "fs";
|
|
99056
|
-
import
|
|
99178
|
+
import path59 from "path";
|
|
99057
99179
|
var SCRATCH_DIR = ".baker";
|
|
99058
99180
|
function isWithin(parent, target2) {
|
|
99059
|
-
const relative =
|
|
99060
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
99181
|
+
const relative = path59.relative(parent, target2);
|
|
99182
|
+
return relative === "" || !relative.startsWith("..") && !path59.isAbsolute(relative);
|
|
99061
99183
|
}
|
|
99062
99184
|
function findRepoRoot(from) {
|
|
99063
|
-
let dir =
|
|
99185
|
+
let dir = path59.resolve(from);
|
|
99064
99186
|
for (; ; ) {
|
|
99065
|
-
if (existsSync12(
|
|
99066
|
-
const parent =
|
|
99187
|
+
if (existsSync12(path59.join(dir, ".git"))) return dir;
|
|
99188
|
+
const parent = path59.dirname(dir);
|
|
99067
99189
|
if (parent === dir) return null;
|
|
99068
99190
|
dir = parent;
|
|
99069
99191
|
}
|
|
99070
99192
|
}
|
|
99071
99193
|
function checkCaptureOut(out, options) {
|
|
99072
99194
|
const { cwd, repoRoot } = options;
|
|
99073
|
-
const resolved =
|
|
99195
|
+
const resolved = path59.resolve(cwd, out);
|
|
99074
99196
|
if (repoRoot === null || !isWithin(repoRoot, resolved)) return { ok: true };
|
|
99075
|
-
const scratch =
|
|
99197
|
+
const scratch = path59.join(repoRoot, SCRATCH_DIR);
|
|
99076
99198
|
if (isWithin(scratch, resolved)) return { ok: true };
|
|
99077
|
-
const suggestion =
|
|
99199
|
+
const suggestion = path59.posix.join(SCRATCH_DIR, "teardowns", path59.basename(resolved) || "capture");
|
|
99078
99200
|
return {
|
|
99079
99201
|
ok: false,
|
|
99080
99202
|
error: {
|
|
@@ -99248,12 +99370,12 @@ var scrapeCommand = defineCommand220({
|
|
|
99248
99370
|
});
|
|
99249
99371
|
|
|
99250
99372
|
// src/commands/landing/inspiration/search.ts
|
|
99251
|
-
import
|
|
99373
|
+
import path61 from "path";
|
|
99252
99374
|
import { defineCommand as defineCommand221 } from "citty";
|
|
99253
99375
|
|
|
99254
99376
|
// src/commands/landing/inspiration/shot.ts
|
|
99255
|
-
import { mkdir as
|
|
99256
|
-
import
|
|
99377
|
+
import { mkdir as mkdir19, writeFile as writeFile31 } from "fs/promises";
|
|
99378
|
+
import path60 from "path";
|
|
99257
99379
|
import sharp9 from "sharp";
|
|
99258
99380
|
var READABLE_SHOT = {
|
|
99259
99381
|
maxWidth: 1440,
|
|
@@ -99279,9 +99401,9 @@ async function downloadReadableShot(url, file) {
|
|
|
99279
99401
|
const response = await fetch(url);
|
|
99280
99402
|
if (!response.ok) return null;
|
|
99281
99403
|
const shot = await toReadableShot(Buffer.from(await response.arrayBuffer()));
|
|
99282
|
-
await
|
|
99283
|
-
await
|
|
99284
|
-
return
|
|
99404
|
+
await mkdir19(path60.dirname(file), { recursive: true });
|
|
99405
|
+
await writeFile31(file, shot);
|
|
99406
|
+
return path60.relative(process.cwd(), file);
|
|
99285
99407
|
} catch {
|
|
99286
99408
|
return null;
|
|
99287
99409
|
}
|
|
@@ -99373,13 +99495,13 @@ function buildSearchBody2(args8) {
|
|
|
99373
99495
|
return body;
|
|
99374
99496
|
}
|
|
99375
99497
|
async function downloadShots(results) {
|
|
99376
|
-
const dir =
|
|
99498
|
+
const dir = path61.join(process.cwd(), ".baker", "inspiration");
|
|
99377
99499
|
const saved = /* @__PURE__ */ new Map();
|
|
99378
99500
|
await Promise.all(
|
|
99379
99501
|
results.map(async (result) => {
|
|
99380
99502
|
const file = await downloadReadableShot(
|
|
99381
99503
|
result.desktopShotUrl,
|
|
99382
|
-
|
|
99504
|
+
path61.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
|
|
99383
99505
|
);
|
|
99384
99506
|
if (file) saved.set(result.id, file);
|
|
99385
99507
|
})
|
|
@@ -99607,7 +99729,7 @@ var sequencesCommand = defineCommand222({
|
|
|
99607
99729
|
});
|
|
99608
99730
|
|
|
99609
99731
|
// src/commands/landing/inspiration/view.ts
|
|
99610
|
-
import
|
|
99732
|
+
import path62 from "path";
|
|
99611
99733
|
import { defineCommand as defineCommand223 } from "citty";
|
|
99612
99734
|
registerSchema({
|
|
99613
99735
|
command: "landing.inspiration.view",
|
|
@@ -99640,12 +99762,12 @@ var viewCommand2 = defineCommand223({
|
|
|
99640
99762
|
const id = args8.id;
|
|
99641
99763
|
const data = await apiGet("/api/landing-inspiration/section", { id });
|
|
99642
99764
|
const section2 = data.section;
|
|
99643
|
-
const dir =
|
|
99765
|
+
const dir = path62.join(process.cwd(), ".baker", "inspiration", id);
|
|
99644
99766
|
const ext = READABLE_SHOT.extension;
|
|
99645
99767
|
const [desktop, mobile, filmstrip] = await Promise.all([
|
|
99646
|
-
downloadReadableShot(section2.desktopShotUrl,
|
|
99647
|
-
downloadReadableShot(section2.mobileShotUrl,
|
|
99648
|
-
downloadReadableShot(section2.motionFilmstripUrl,
|
|
99768
|
+
downloadReadableShot(section2.desktopShotUrl, path62.join(dir, `desktop.${ext}`)),
|
|
99769
|
+
downloadReadableShot(section2.mobileShotUrl, path62.join(dir, `mobile.${ext}`)),
|
|
99770
|
+
downloadReadableShot(section2.motionFilmstripUrl, path62.join(dir, `motion-filmstrip.${ext}`))
|
|
99649
99771
|
]);
|
|
99650
99772
|
const full = args8.full;
|
|
99651
99773
|
const hints2 = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
@@ -99753,8 +99875,8 @@ Full guide: __tooling__/docs/tools/baker/landing.md`
|
|
|
99753
99875
|
|
|
99754
99876
|
// src/commands/landing/variant.ts
|
|
99755
99877
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
99756
|
-
import { mkdir as
|
|
99757
|
-
import
|
|
99878
|
+
import { mkdir as mkdir20, readdir as readdir23, readFile as readFile49, stat as stat12, writeFile as writeFile32 } from "fs/promises";
|
|
99879
|
+
import path63 from "path";
|
|
99758
99880
|
import { defineCommand as defineCommand225 } from "citty";
|
|
99759
99881
|
registerSchema({
|
|
99760
99882
|
command: "landing.variant",
|
|
@@ -99781,7 +99903,7 @@ registerSchema({
|
|
|
99781
99903
|
var SLUG_RE2 = /^[a-z0-9][a-z0-9-]*$/;
|
|
99782
99904
|
async function readInternalId(definitionPath) {
|
|
99783
99905
|
try {
|
|
99784
|
-
const text3 = await
|
|
99906
|
+
const text3 = await readFile49(definitionPath, "utf8");
|
|
99785
99907
|
return /^internalId:\s*"?([^"\s]+)"?\s*$/m.exec(text3)?.[1] ?? null;
|
|
99786
99908
|
} catch {
|
|
99787
99909
|
return null;
|
|
@@ -99790,7 +99912,7 @@ async function readInternalId(definitionPath) {
|
|
|
99790
99912
|
async function archivedVariantNumbers(root, internalId) {
|
|
99791
99913
|
if (!internalId) return [];
|
|
99792
99914
|
try {
|
|
99793
|
-
const entries = await readdir23(
|
|
99915
|
+
const entries = await readdir23(path63.resolve(root, "src/_variants", internalId), { withFileTypes: true });
|
|
99794
99916
|
return entries.filter((entry) => entry.isDirectory()).map((entry) => Number(entry.name)).filter((n) => Number.isInteger(n) && n > 0);
|
|
99795
99917
|
} catch {
|
|
99796
99918
|
return [];
|
|
@@ -99820,7 +99942,7 @@ async function exists2(p) {
|
|
|
99820
99942
|
}
|
|
99821
99943
|
async function listLandingSlugs2(root) {
|
|
99822
99944
|
try {
|
|
99823
|
-
const entries = await readdir23(
|
|
99945
|
+
const entries = await readdir23(path63.resolve(root, "src", "pages"), { withFileTypes: true });
|
|
99824
99946
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
99825
99947
|
} catch {
|
|
99826
99948
|
return [];
|
|
@@ -99836,7 +99958,7 @@ async function listComponents(componentsDir, prefix = "") {
|
|
|
99836
99958
|
const out = [];
|
|
99837
99959
|
for (const entry of entries) {
|
|
99838
99960
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
99839
|
-
if (entry.isDirectory()) out.push(...await listComponents(
|
|
99961
|
+
if (entry.isDirectory()) out.push(...await listComponents(path63.join(componentsDir, entry.name), rel));
|
|
99840
99962
|
else if (entry.name.endsWith(".astro")) out.push(rel);
|
|
99841
99963
|
}
|
|
99842
99964
|
return out.sort();
|
|
@@ -99903,16 +100025,16 @@ async function resolveTargets(projectRoot, opts) {
|
|
|
99903
100025
|
`"${controlSlug}" is already a variant of "${variantOfSlug(controlSlug)}". Make the next variant of the page itself: \`baker landing variant ${variantOfSlug(controlSlug)}\`.`
|
|
99904
100026
|
);
|
|
99905
100027
|
}
|
|
99906
|
-
const controlDir =
|
|
100028
|
+
const controlDir = path63.resolve(projectRoot, "src", "pages", controlSlug);
|
|
99907
100029
|
if (!await isDir3(controlDir)) {
|
|
99908
100030
|
fail11("NOT_FOUND", `No page at src/pages/${controlSlug}/`, {
|
|
99909
100031
|
availableSlugs: await listLandingSlugs2(projectRoot)
|
|
99910
100032
|
});
|
|
99911
100033
|
}
|
|
99912
|
-
if (!await exists2(
|
|
100034
|
+
if (!await exists2(path63.join(controlDir, "index.astro"))) {
|
|
99913
100035
|
fail11("NOT_FOUND", `src/pages/${controlSlug}/index.astro is missing, so there is no page to make a variant of.`);
|
|
99914
100036
|
}
|
|
99915
|
-
const internalId = await readInternalId(
|
|
100037
|
+
const internalId = await readInternalId(path63.join(controlDir, "_definition.md"));
|
|
99916
100038
|
const variantSlugValue = variantSlug(
|
|
99917
100039
|
controlSlug,
|
|
99918
100040
|
nextVariantNumber(
|
|
@@ -99921,8 +100043,8 @@ async function resolveTargets(projectRoot, opts) {
|
|
|
99921
100043
|
await archivedVariantNumbers(projectRoot, internalId)
|
|
99922
100044
|
)
|
|
99923
100045
|
);
|
|
99924
|
-
const variantDir =
|
|
99925
|
-
const available = await listComponents(
|
|
100046
|
+
const variantDir = path63.resolve(projectRoot, "src", "pages", variantSlugValue);
|
|
100047
|
+
const available = await listComponents(path63.join(controlDir, "_components"));
|
|
99926
100048
|
const forks = [];
|
|
99927
100049
|
for (const name of parseForks(opts.fork, opts.rawArgs)) {
|
|
99928
100050
|
const resolved = resolveForkName(name, available);
|
|
@@ -99938,26 +100060,26 @@ async function resolveTargets(projectRoot, opts) {
|
|
|
99938
100060
|
}
|
|
99939
100061
|
async function writeVariant(opts) {
|
|
99940
100062
|
const { controlDir, variantDir, controlSlug, variantSlug: variantSlug2, forks } = opts;
|
|
99941
|
-
const forkedTargets = new Set(forks.map((f) =>
|
|
100063
|
+
const forkedTargets = new Set(forks.map((f) => path63.join(controlDir, "_components", f)));
|
|
99942
100064
|
const written = [];
|
|
99943
|
-
const indexText = await
|
|
99944
|
-
await
|
|
99945
|
-
await
|
|
99946
|
-
|
|
100065
|
+
const indexText = await readFile49(path63.join(controlDir, "index.astro"), "utf8");
|
|
100066
|
+
await mkdir20(variantDir, { recursive: true });
|
|
100067
|
+
await writeFile32(
|
|
100068
|
+
path63.join(variantDir, "index.astro"),
|
|
99947
100069
|
repointFile(indexText, { fromDir: controlDir, toDir: variantDir, controlDir, variantDir, forkedTargets }),
|
|
99948
100070
|
"utf8"
|
|
99949
100071
|
);
|
|
99950
100072
|
written.push(`src/pages/${variantSlug2}/index.astro`);
|
|
99951
100073
|
for (const fork of forks) {
|
|
99952
|
-
const fromFile =
|
|
99953
|
-
const toFile =
|
|
99954
|
-
const text3 = await
|
|
99955
|
-
await
|
|
99956
|
-
await
|
|
100074
|
+
const fromFile = path63.join(controlDir, "_components", fork);
|
|
100075
|
+
const toFile = path63.join(variantDir, "_components", fork);
|
|
100076
|
+
const text3 = await readFile49(fromFile, "utf8");
|
|
100077
|
+
await mkdir20(path63.dirname(toFile), { recursive: true });
|
|
100078
|
+
await writeFile32(
|
|
99957
100079
|
toFile,
|
|
99958
100080
|
repointFile(text3, {
|
|
99959
|
-
fromDir:
|
|
99960
|
-
toDir:
|
|
100081
|
+
fromDir: path63.dirname(fromFile),
|
|
100082
|
+
toDir: path63.dirname(toFile),
|
|
99961
100083
|
controlDir,
|
|
99962
100084
|
variantDir,
|
|
99963
100085
|
forkedTargets
|
|
@@ -99966,9 +100088,9 @@ async function writeVariant(opts) {
|
|
|
99966
100088
|
);
|
|
99967
100089
|
written.push(`src/pages/${variantSlug2}/_components/${fork}`);
|
|
99968
100090
|
}
|
|
99969
|
-
const controlDefinitionPath =
|
|
100091
|
+
const controlDefinitionPath = path63.join(controlDir, "_definition.md");
|
|
99970
100092
|
if (await exists2(controlDefinitionPath)) {
|
|
99971
|
-
const definition = buildVariantDefinition(await
|
|
100093
|
+
const definition = buildVariantDefinition(await readFile49(controlDefinitionPath, "utf8"), {
|
|
99972
100094
|
internalId: randomUUID4().replace(/-/g, "").slice(0, 8),
|
|
99973
100095
|
variantSlug: variantSlug2,
|
|
99974
100096
|
controlSlug,
|
|
@@ -99976,11 +100098,11 @@ async function writeVariant(opts) {
|
|
|
99976
100098
|
...opts.because ? { because: opts.because } : {},
|
|
99977
100099
|
...opts.change ? { change: opts.change } : {}
|
|
99978
100100
|
});
|
|
99979
|
-
await
|
|
100101
|
+
await writeFile32(path63.join(variantDir, "_definition.md"), definition, "utf8");
|
|
99980
100102
|
written.push(`src/pages/${variantSlug2}/_definition.md`);
|
|
99981
100103
|
}
|
|
99982
|
-
await
|
|
99983
|
-
await
|
|
100104
|
+
await mkdir20(path63.join(variantDir, "_images"), { recursive: true });
|
|
100105
|
+
await writeFile32(path63.join(variantDir, "_images", ".gitkeep"), "", "utf8");
|
|
99984
100106
|
return written;
|
|
99985
100107
|
}
|
|
99986
100108
|
var variantCommand = defineCommand225({
|
|
@@ -100085,28 +100207,28 @@ Subcommands:
|
|
|
100085
100207
|
});
|
|
100086
100208
|
|
|
100087
100209
|
// ../api/src/mcp-catalog.ts
|
|
100088
|
-
import { z as
|
|
100210
|
+
import { z as z76 } from "zod";
|
|
100089
100211
|
var MCP_CATALOG_DEFAULT_LIMIT = 8;
|
|
100090
100212
|
var MCP_CATALOG_DESCRIPTION_MAX = 160;
|
|
100091
|
-
var mcpCatalogRequestSchema =
|
|
100092
|
-
search:
|
|
100093
|
-
limit:
|
|
100213
|
+
var mcpCatalogRequestSchema = z76.object({
|
|
100214
|
+
search: z76.string().trim().min(1, "Provide --search").max(100),
|
|
100215
|
+
limit: z76.coerce.number().int().min(1).max(20).default(MCP_CATALOG_DEFAULT_LIMIT)
|
|
100094
100216
|
});
|
|
100095
|
-
var mcpCatalogConnectSchema =
|
|
100096
|
-
var mcpCatalogEntrySchema =
|
|
100217
|
+
var mcpCatalogConnectSchema = z76.enum(["sign-in", "key"]);
|
|
100218
|
+
var mcpCatalogEntrySchema = z76.object({
|
|
100097
100219
|
/** The toolkit slug — what request_connection `{ app }` takes. */
|
|
100098
|
-
app:
|
|
100099
|
-
name:
|
|
100100
|
-
description:
|
|
100220
|
+
app: z76.string(),
|
|
100221
|
+
name: z76.string(),
|
|
100222
|
+
description: z76.string().max(MCP_CATALOG_DESCRIPTION_MAX),
|
|
100101
100223
|
connect: mcpCatalogConnectSchema,
|
|
100102
|
-
connected:
|
|
100224
|
+
connected: z76.boolean()
|
|
100103
100225
|
});
|
|
100104
|
-
var mcpCatalogResponseSchema =
|
|
100105
|
-
apps:
|
|
100226
|
+
var mcpCatalogResponseSchema = z76.object({
|
|
100227
|
+
apps: z76.array(mcpCatalogEntrySchema),
|
|
100106
100228
|
/** The company has managed apps switched off: nothing here can be connected, so `apps` is empty. */
|
|
100107
|
-
managedDisabled:
|
|
100229
|
+
managedDisabled: z76.boolean().optional(),
|
|
100108
100230
|
/** Baker's own connections this search matched — ask for these with `{ platform }`, never a managed copy. */
|
|
100109
|
-
platforms:
|
|
100231
|
+
platforms: z76.array(z76.string()).optional()
|
|
100110
100232
|
});
|
|
100111
100233
|
|
|
100112
100234
|
// src/commands/mcp/index.ts
|
|
@@ -101327,11 +101449,11 @@ var countriesCommand = defineCommand237({
|
|
|
101327
101449
|
});
|
|
101328
101450
|
|
|
101329
101451
|
// src/commands/research/customers.ts
|
|
101330
|
-
import { mkdir as
|
|
101331
|
-
import
|
|
101452
|
+
import { mkdir as mkdir21, readdir as readdir24, readFile as readFile50, writeFile as writeFile33 } from "fs/promises";
|
|
101453
|
+
import path64 from "path";
|
|
101332
101454
|
import { defineCommand as defineCommand239 } from "citty";
|
|
101333
101455
|
import { parse as parseYaml5 } from "yaml";
|
|
101334
|
-
import { z as
|
|
101456
|
+
import { z as z77 } from "zod";
|
|
101335
101457
|
|
|
101336
101458
|
// src/commands/research/reddit.ts
|
|
101337
101459
|
import { defineCommand as defineCommand238 } from "citty";
|
|
@@ -101743,13 +101865,13 @@ var PLATFORM_NAMES = {
|
|
|
101743
101865
|
app_store: "App Store",
|
|
101744
101866
|
google_play: "Google Play"
|
|
101745
101867
|
};
|
|
101746
|
-
var LibraryReviewSchema =
|
|
101747
|
-
_id:
|
|
101748
|
-
reviewText:
|
|
101749
|
-
sourceType:
|
|
101750
|
-
reviewerName:
|
|
101751
|
-
rating:
|
|
101752
|
-
reviewDate:
|
|
101868
|
+
var LibraryReviewSchema = z77.object({
|
|
101869
|
+
_id: z77.string().min(1),
|
|
101870
|
+
reviewText: z77.string(),
|
|
101871
|
+
sourceType: z77.string().optional(),
|
|
101872
|
+
reviewerName: z77.string().optional(),
|
|
101873
|
+
rating: z77.number().optional(),
|
|
101874
|
+
reviewDate: z77.number().optional()
|
|
101753
101875
|
});
|
|
101754
101876
|
function dayOf(ms) {
|
|
101755
101877
|
const date = new Date(ms);
|
|
@@ -101832,13 +101954,13 @@ function buildCustomersPrompt(sources, focus) {
|
|
|
101832
101954
|
...sources.map((source, i) => `[${i + 1}] ${source.text}`)
|
|
101833
101955
|
].join("\n");
|
|
101834
101956
|
}
|
|
101835
|
-
var AnswerSchema =
|
|
101836
|
-
var CandidateSchema =
|
|
101837
|
-
source:
|
|
101838
|
-
quote:
|
|
101839
|
-
type:
|
|
101840
|
-
force:
|
|
101841
|
-
persona:
|
|
101957
|
+
var AnswerSchema = z77.object({ insights: z77.array(z77.unknown()) });
|
|
101958
|
+
var CandidateSchema = z77.object({
|
|
101959
|
+
source: z77.coerce.number().int().positive(),
|
|
101960
|
+
quote: z77.string().trim().min(1),
|
|
101961
|
+
type: z77.enum(INSIGHT_TYPES).catch("other"),
|
|
101962
|
+
force: z77.enum(FORCES).optional().catch(void 0),
|
|
101963
|
+
persona: z77.string().trim().optional().catch(void 0)
|
|
101842
101964
|
});
|
|
101843
101965
|
function candidatesFrom(answer) {
|
|
101844
101966
|
const parsed = AnswerSchema.safeParse(answer);
|
|
@@ -101880,7 +102002,7 @@ function customersHints(input) {
|
|
|
101880
102002
|
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
102003
|
return written < FEW_OBSERVED ? [`Only ${written} observed. ${widen}`, cite] : [cite];
|
|
101882
102004
|
}
|
|
101883
|
-
var INSIGHTS_DIR2 =
|
|
102005
|
+
var INSIGHTS_DIR2 = path64.join("src", "content", "insights");
|
|
101884
102006
|
var BATCH_CHARS = 24e3;
|
|
101885
102007
|
var MAX_BATCHES = 5;
|
|
101886
102008
|
var WEB_PAGES = 3;
|
|
@@ -101929,7 +102051,7 @@ var ARGS15 = {
|
|
|
101929
102051
|
}
|
|
101930
102052
|
};
|
|
101931
102053
|
registerSchema({ command: "research.customers", description: DESCRIPTION13, args: ARGS15 });
|
|
101932
|
-
var QuoteFrontmatter =
|
|
102054
|
+
var QuoteFrontmatter = z77.object({ quote: z77.string() });
|
|
101933
102055
|
function quoteOf(raw) {
|
|
101934
102056
|
const front = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1];
|
|
101935
102057
|
if (!front) return "";
|
|
@@ -101945,12 +102067,12 @@ async function readExistingInsights(dir) {
|
|
|
101945
102067
|
return await Promise.all(
|
|
101946
102068
|
files.map(async (file) => ({
|
|
101947
102069
|
id: file.slice(0, -3),
|
|
101948
|
-
quote: quoteOf(await
|
|
102070
|
+
quote: quoteOf(await readFile50(path64.join(dir, file), "utf8").catch(() => ""))
|
|
101949
102071
|
}))
|
|
101950
102072
|
);
|
|
101951
102073
|
}
|
|
101952
102074
|
async function productSlugs(root) {
|
|
101953
|
-
const files = await readdir24(
|
|
102075
|
+
const files = await readdir24(path64.join(root, "src", "content", "products")).catch(() => []);
|
|
101954
102076
|
return files.filter((f) => f.endsWith(".md")).map((f) => f.slice(0, -3));
|
|
101955
102077
|
}
|
|
101956
102078
|
async function gatherTestimonials(focus) {
|
|
@@ -101973,7 +102095,7 @@ async function gatherReddit(query) {
|
|
|
101973
102095
|
const items = answers.flatMap((a) => a.status === "fulfilled" ? a.value.results : []).filter((item) => !seen.has(item.id) && Boolean(seen.add(item.id)));
|
|
101974
102096
|
return sourcesFromReddit(items);
|
|
101975
102097
|
}
|
|
101976
|
-
var ExploreAnswer =
|
|
102098
|
+
var ExploreAnswer = z77.object({ sources: z77.array(z77.object({ url: z77.string() })) });
|
|
101977
102099
|
async function resolveGroundingRedirect(url) {
|
|
101978
102100
|
if (!URL.canParse(url) || new URL(url).host !== "vertexaisearch.cloud.google.com") return url;
|
|
101979
102101
|
try {
|
|
@@ -102050,7 +102172,7 @@ async function classifyAll(sources, focus, root) {
|
|
|
102050
102172
|
const batches = batchSources(sources, BATCH_CHARS).slice(0, MAX_BATCHES);
|
|
102051
102173
|
const spent = [];
|
|
102052
102174
|
const answers = await Promise.all(batches.map((batch) => classifyBatch(batch, focus, (u) => spent.push(u))));
|
|
102053
|
-
await holdUnbilledSpend(
|
|
102175
|
+
await holdUnbilledSpend(path64.join(root, "canvas", ".cache"), spent);
|
|
102054
102176
|
if (batches.length > 0 && answers.every((a) => a === null)) return null;
|
|
102055
102177
|
const result = { kept: [], dropped: 0, tooLong: 0 };
|
|
102056
102178
|
batches.forEach((batch, i) => {
|
|
@@ -102063,11 +102185,11 @@ async function classifyAll(sources, focus, root) {
|
|
|
102063
102185
|
}
|
|
102064
102186
|
async function writeInsights(dir, writes, opts) {
|
|
102065
102187
|
if (writes.length === 0) return [];
|
|
102066
|
-
await
|
|
102188
|
+
await mkdir21(dir, { recursive: true });
|
|
102067
102189
|
const written = [];
|
|
102068
102190
|
for (const entry of writes) {
|
|
102069
102191
|
const file = renderInsightFile(entry.insight, opts);
|
|
102070
|
-
const ok = await
|
|
102192
|
+
const ok = await writeFile33(path64.join(dir, `${entry.id}.md`), file, { encoding: "utf8", flag: "wx" }).then(
|
|
102071
102193
|
() => true,
|
|
102072
102194
|
() => false
|
|
102073
102195
|
);
|
|
@@ -102118,7 +102240,7 @@ var customersCommand = defineCommand239({
|
|
|
102118
102240
|
"Run it again. Do not write insights by hand: an insight is only a quote this command found in its source."
|
|
102119
102241
|
);
|
|
102120
102242
|
}
|
|
102121
|
-
const dir =
|
|
102243
|
+
const dir = path64.join(root, INSIGHTS_DIR2);
|
|
102122
102244
|
const plan = planInsightWrites(gated.kept, await readExistingInsights(dir), limit);
|
|
102123
102245
|
const today2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
102124
102246
|
const shown = dryRun ? plan.writes : await writeInsights(dir, plan.writes, { today: today2, ...product ? { product } : {} });
|
|
@@ -103720,8 +103842,8 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
|
|
|
103720
103842
|
});
|
|
103721
103843
|
|
|
103722
103844
|
// src/commands/scheduled-agents/templates.ts
|
|
103723
|
-
import { readFile as
|
|
103724
|
-
import
|
|
103845
|
+
import { readFile as readFile51 } from "fs/promises";
|
|
103846
|
+
import path65 from "path";
|
|
103725
103847
|
import { defineCommand as defineCommand256 } from "citty";
|
|
103726
103848
|
registerSchema({
|
|
103727
103849
|
command: "scheduled-agents.templates",
|
|
@@ -103828,7 +103950,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
|
|
|
103828
103950
|
}
|
|
103829
103951
|
if (save.length > 0) {
|
|
103830
103952
|
const briefFile = flag2("brief-file");
|
|
103831
|
-
const brief2 = briefFile.length > 0 ? await
|
|
103953
|
+
const brief2 = briefFile.length > 0 ? await readFile51(path65.resolve(briefFile), "utf8") : flag2("brief");
|
|
103832
103954
|
if (brief2.trim().length === 0) {
|
|
103833
103955
|
failValidation6("--brief-file (preferred) or --brief is required: the brief is the recipe.");
|
|
103834
103956
|
}
|
|
@@ -104373,8 +104495,8 @@ function onOff(flag2, value2) {
|
|
|
104373
104495
|
if (value2 === "off" || value2 === "false") return false;
|
|
104374
104496
|
throw new SiteArgsError(`--${flag2} must be on or off`);
|
|
104375
104497
|
}
|
|
104376
|
-
function parseProfile(raw,
|
|
104377
|
-
const text3 = raw.startsWith("@") ?
|
|
104498
|
+
function parseProfile(raw, readFile52) {
|
|
104499
|
+
const text3 = raw.startsWith("@") ? readFile52(raw.slice(1)) : raw;
|
|
104378
104500
|
let parsed;
|
|
104379
104501
|
try {
|
|
104380
104502
|
parsed = JSON.parse(text3);
|
|
@@ -104386,7 +104508,7 @@ function parseProfile(raw, readFile51) {
|
|
|
104386
104508
|
}
|
|
104387
104509
|
return parsed;
|
|
104388
104510
|
}
|
|
104389
|
-
function parseSetArgs(args8,
|
|
104511
|
+
function parseSetArgs(args8, readFile52) {
|
|
104390
104512
|
const inputs = [];
|
|
104391
104513
|
if (args8.consent !== void 0) {
|
|
104392
104514
|
inputs.push({ setting: "consent", mode: oneOf3("consent", args8.consent, SITE_CONSENT_MODES) });
|
|
@@ -104419,7 +104541,7 @@ function parseSetArgs(args8, readFile51) {
|
|
|
104419
104541
|
inputs.push({ setting: "timezone", timezone: args8.timezone });
|
|
104420
104542
|
}
|
|
104421
104543
|
if (args8["export-profile"] !== void 0) {
|
|
104422
|
-
inputs.push({ setting: "exportProfile", profile: parseProfile(args8["export-profile"],
|
|
104544
|
+
inputs.push({ setting: "exportProfile", profile: parseProfile(args8["export-profile"], readFile52) });
|
|
104423
104545
|
}
|
|
104424
104546
|
if (inputs.length === 0) {
|
|
104425
104547
|
throw new SiteArgsError(
|
|
@@ -104610,7 +104732,7 @@ var setCommand = defineCommand264({
|
|
|
104610
104732
|
timezone: args8.timezone,
|
|
104611
104733
|
"export-profile": args8["export-profile"]
|
|
104612
104734
|
},
|
|
104613
|
-
(
|
|
104735
|
+
(path68) => readFileSync26(path68, "utf8")
|
|
104614
104736
|
);
|
|
104615
104737
|
const body = { chatId: requireChatId(), inputs };
|
|
104616
104738
|
writeJson(await apiPost("/api/site/stage", body));
|
|
@@ -104892,7 +105014,7 @@ function parseImageRefs(spec) {
|
|
|
104892
105014
|
}
|
|
104893
105015
|
var defaultDeps = {
|
|
104894
105016
|
ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
|
|
104895
|
-
upload: (
|
|
105017
|
+
upload: (path68) => uploadLocalImage({ file: path68, contentType: detectImageContentType(path68), source: "uploaded" })
|
|
104896
105018
|
};
|
|
104897
105019
|
async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
104898
105020
|
const refs = parseImageRefs(spec);
|
|
@@ -104912,10 +105034,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
|
104912
105034
|
}
|
|
104913
105035
|
return { imageIds, added };
|
|
104914
105036
|
}
|
|
104915
|
-
function uploadFailure(
|
|
105037
|
+
function uploadFailure(path68) {
|
|
104916
105038
|
return (error) => {
|
|
104917
105039
|
if (error instanceof ApiError) throw error;
|
|
104918
|
-
throw new ApiError("VALIDATION_ERROR", `Could not read "${
|
|
105040
|
+
throw new ApiError("VALIDATION_ERROR", `Could not read "${path68}" as an image.`);
|
|
104919
105041
|
};
|
|
104920
105042
|
}
|
|
104921
105043
|
|
|
@@ -106015,10 +106137,10 @@ async function stageOp4(op) {
|
|
|
106015
106137
|
handleError8(err);
|
|
106016
106138
|
}
|
|
106017
106139
|
}
|
|
106018
|
-
async function draftAction3(
|
|
106140
|
+
async function draftAction3(path68, body, chat) {
|
|
106019
106141
|
const chatId = resolveChatId(chat);
|
|
106020
106142
|
try {
|
|
106021
|
-
const { data, hints: hints2 } = splitHints(await apiPost(
|
|
106143
|
+
const { data, hints: hints2 } = splitHints(await apiPost(path68, { chatId, ...body }));
|
|
106022
106144
|
writeJsonEnvelope({ ok: true, data, ...hints2.length > 0 ? { hints: hints2 } : {} });
|
|
106023
106145
|
return data;
|
|
106024
106146
|
} catch (err) {
|
|
@@ -107580,9 +107702,9 @@ Example: baker videos describe j571abc123 --description "Customer explaining how
|
|
|
107580
107702
|
|
|
107581
107703
|
// src/commands/videos/fetch.ts
|
|
107582
107704
|
import { constants } from "fs";
|
|
107583
|
-
import { copyFile as copyFile2, mkdir as
|
|
107705
|
+
import { copyFile as copyFile2, mkdir as mkdir22, mkdtemp as mkdtemp3, rm as rm9 } from "fs/promises";
|
|
107584
107706
|
import { tmpdir as tmpdir4 } from "os";
|
|
107585
|
-
import
|
|
107707
|
+
import path66 from "path";
|
|
107586
107708
|
import { defineCommand as defineCommand293 } from "citty";
|
|
107587
107709
|
|
|
107588
107710
|
// src/commands/videos/videoPlatforms.ts
|
|
@@ -107750,10 +107872,10 @@ var fetchCommand2 = defineCommand293({
|
|
|
107750
107872
|
}
|
|
107751
107873
|
});
|
|
107752
107874
|
async function fetchVideo(args8) {
|
|
107753
|
-
const outDir =
|
|
107754
|
-
const workDir = await mkdtemp3(
|
|
107875
|
+
const outDir = path66.resolve(args8.out);
|
|
107876
|
+
const workDir = await mkdtemp3(path66.join(tmpdir4(), "videos-fetch-"));
|
|
107755
107877
|
try {
|
|
107756
|
-
await
|
|
107878
|
+
await mkdir22(outDir, { recursive: true });
|
|
107757
107879
|
const vimeoCookie = captureVimeoCookie();
|
|
107758
107880
|
const probe = await probeYtDlp({ url: args8.url, vimeoCookie, cookieDir: workDir });
|
|
107759
107881
|
if (isAudioOnly(probe.info)) {
|
|
@@ -107796,7 +107918,7 @@ function fileStem(info) {
|
|
|
107796
107918
|
}
|
|
107797
107919
|
async function placeInto(outDir, source, stem) {
|
|
107798
107920
|
for (let n = 1; ; n++) {
|
|
107799
|
-
const target2 =
|
|
107921
|
+
const target2 = path66.join(outDir, `${stem}${n === 1 ? "" : `-${n}`}${path66.extname(source)}`);
|
|
107800
107922
|
try {
|
|
107801
107923
|
await copyFile2(source, target2, constants.COPYFILE_EXCL);
|
|
107802
107924
|
return target2;
|
|
@@ -107883,7 +108005,7 @@ var groupCommand2 = defineCommand295({
|
|
|
107883
108005
|
// src/commands/videos/ingest.ts
|
|
107884
108006
|
import { mkdtemp as mkdtemp4, rm as rm10, stat as stat13 } from "fs/promises";
|
|
107885
108007
|
import { tmpdir as tmpdir5 } from "os";
|
|
107886
|
-
import
|
|
108008
|
+
import path67 from "path";
|
|
107887
108009
|
import { defineCommand as defineCommand296 } from "citty";
|
|
107888
108010
|
|
|
107889
108011
|
// src/lib/streamUpload.ts
|
|
@@ -108123,7 +108245,7 @@ function ingestUrl(args8) {
|
|
|
108123
108245
|
}
|
|
108124
108246
|
async function downloadThenIngest(args8, country) {
|
|
108125
108247
|
const vimeoCookie = captureVimeoCookie();
|
|
108126
|
-
const workDir = await mkdtemp4(
|
|
108248
|
+
const workDir = await mkdtemp4(path67.join(tmpdir5(), "videos-ingest-"));
|
|
108127
108249
|
try {
|
|
108128
108250
|
const probe = await probeYtDlp({ url: args8.url, country, vimeoCookie, cookieDir: workDir });
|
|
108129
108251
|
if (isAudioOnly(probe.info)) {
|