@koda-sl/baker-cli 0.434.0-dev.42b55fa66 → 0.440.0-dev.dab0aaba9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/{chunk-RFZYRG2Q.js → chunk-32UJLGZX.js} +3 -3
- package/dist/{chunk-GCTR4MDE.js → chunk-5DJPTQU5.js} +2 -2
- package/dist/{chunk-MMTPMFFH.js → chunk-APTNJ2EY.js} +19 -12
- package/dist/chunk-APTNJ2EY.js.map +1 -0
- package/dist/{chunk-PHLFNYGQ.js → chunk-KP33GIDZ.js} +4 -4
- package/dist/{chunk-FQOKJYKN.js → chunk-PR3CXBAT.js} +5 -1
- package/dist/chunk-PR3CXBAT.js.map +1 -0
- package/dist/{chunk-Q6F5UNOU.js → chunk-UKHD423A.js} +3 -3
- package/dist/cli.js +1119 -947
- package/dist/cli.js.map +1 -1
- package/dist/{client-GS7T7ETG.js → client-JM3QBGR2.js} +4 -4
- package/dist/engine/index.js +3 -3
- package/dist/{env-SL22S6CM.js → env-WIAWLTDN.js} +4 -2
- package/dist/{output-OFDKEPPJ.js → output-UGMWOFQR.js} +5 -5
- package/dist/{shared-XR54KFMH.js → shared-DJTAT7CM.js} +6 -6
- package/package.json +1 -1
- package/dist/chunk-FQOKJYKN.js.map +0 -1
- package/dist/chunk-MMTPMFFH.js.map +0 -1
- /package/dist/{chunk-RFZYRG2Q.js.map → chunk-32UJLGZX.js.map} +0 -0
- /package/dist/{chunk-GCTR4MDE.js.map → chunk-5DJPTQU5.js.map} +0 -0
- /package/dist/{chunk-PHLFNYGQ.js.map → chunk-KP33GIDZ.js.map} +0 -0
- /package/dist/{chunk-Q6F5UNOU.js.map → chunk-UKHD423A.js.map} +0 -0
- /package/dist/{client-GS7T7ETG.js.map → client-JM3QBGR2.js.map} +0 -0
- /package/dist/{env-SL22S6CM.js.map → env-WIAWLTDN.js.map} +0 -0
- /package/dist/{output-OFDKEPPJ.js.map → output-UGMWOFQR.js.map} +0 -0
- /package/dist/{shared-XR54KFMH.js.map → shared-DJTAT7CM.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -198,7 +198,7 @@ import {
|
|
|
198
198
|
withoutCastName,
|
|
199
199
|
wordCount,
|
|
200
200
|
ytDlpBlockSignal
|
|
201
|
-
} from "./chunk-
|
|
201
|
+
} from "./chunk-APTNJ2EY.js";
|
|
202
202
|
import {
|
|
203
203
|
NAME_CONTAINS_ARG,
|
|
204
204
|
csvOrJson,
|
|
@@ -209,7 +209,7 @@ import {
|
|
|
209
209
|
resolveEffectiveStatus,
|
|
210
210
|
statusFilterHints,
|
|
211
211
|
todayIso
|
|
212
|
-
} from "./chunk-
|
|
212
|
+
} from "./chunk-KP33GIDZ.js";
|
|
213
213
|
import {
|
|
214
214
|
buildQueryCacheKey,
|
|
215
215
|
cacheGet,
|
|
@@ -225,19 +225,19 @@ import {
|
|
|
225
225
|
writeAdsJson,
|
|
226
226
|
writeAdsOutput,
|
|
227
227
|
writeJsonEnvelope
|
|
228
|
-
} from "./chunk-
|
|
228
|
+
} from "./chunk-32UJLGZX.js";
|
|
229
229
|
import {
|
|
230
230
|
ApiError,
|
|
231
231
|
apiGet,
|
|
232
232
|
apiGetWithHeaders,
|
|
233
233
|
apiPost,
|
|
234
234
|
validateConvexId
|
|
235
|
-
} from "./chunk-
|
|
235
|
+
} from "./chunk-UKHD423A.js";
|
|
236
236
|
import {
|
|
237
237
|
installExitRecord,
|
|
238
238
|
installStreamTaps,
|
|
239
239
|
logInvocation
|
|
240
|
-
} from "./chunk-
|
|
240
|
+
} from "./chunk-5DJPTQU5.js";
|
|
241
241
|
import {
|
|
242
242
|
__commonJS,
|
|
243
243
|
__esm,
|
|
@@ -261,6 +261,7 @@ import {
|
|
|
261
261
|
nextPlannedRoute,
|
|
262
262
|
optionalChatId,
|
|
263
263
|
optionalConnection,
|
|
264
|
+
optionalTurnId,
|
|
264
265
|
outputFlagForCommand,
|
|
265
266
|
plannedRoutes,
|
|
266
267
|
refuseNonPublicUrl,
|
|
@@ -275,7 +276,7 @@ import {
|
|
|
275
276
|
winningAdNormalizer,
|
|
276
277
|
writeJson,
|
|
277
278
|
writeOutput
|
|
278
|
-
} from "./chunk-
|
|
279
|
+
} from "./chunk-PR3CXBAT.js";
|
|
279
280
|
|
|
280
281
|
// ../../.pnpm-store/v10/links/@/i18n-iso-countries/7.14.0/5e6699c273c94e2e87598072b13ced7dbfb8ba9153290de3d03d5f711e81fac0/node_modules/i18n-iso-countries/langs/af.json
|
|
281
282
|
var require_af = __commonJS({
|
|
@@ -35510,16 +35511,16 @@ async function runChatgptRead(reference, read, hintsFor4) {
|
|
|
35510
35511
|
}
|
|
35511
35512
|
}
|
|
35512
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.";
|
|
35513
|
-
async function getChatgptList(
|
|
35514
|
-
const { data, headers } = await apiGetWithHeaders(
|
|
35514
|
+
async function getChatgptList(path68, params) {
|
|
35515
|
+
const { data, headers } = await apiGetWithHeaders(path68, params);
|
|
35515
35516
|
return { data, truncated: headers.get(CHATGPT_ADS_TRUNCATED_HEADER) === "true" };
|
|
35516
35517
|
}
|
|
35517
|
-
async function runChatgptListRead(reference,
|
|
35518
|
+
async function runChatgptListRead(reference, path68, params, hintsFor4) {
|
|
35518
35519
|
let truncated = false;
|
|
35519
35520
|
await runChatgptRead(
|
|
35520
35521
|
reference,
|
|
35521
35522
|
async () => {
|
|
35522
|
-
const read = await getChatgptList(
|
|
35523
|
+
const read = await getChatgptList(path68, params);
|
|
35523
35524
|
truncated = read.truncated;
|
|
35524
35525
|
return read.data;
|
|
35525
35526
|
},
|
|
@@ -36324,8 +36325,8 @@ var MEMBER_ARGS = {
|
|
|
36324
36325
|
},
|
|
36325
36326
|
country: { type: "string", description: "Country of phones written without a +code, e.g. ES" }
|
|
36326
36327
|
};
|
|
36327
|
-
function membersFromArgs(args8,
|
|
36328
|
-
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: [] };
|
|
36329
36330
|
const given = [...csvArg(args8.emails) ?? [], ...fromFile.emails];
|
|
36330
36331
|
const givenPhones = [...csvArg(args8.phones) ?? [], ...fromFile.phones];
|
|
36331
36332
|
const members = hashedMembers({
|
|
@@ -39717,9 +39718,9 @@ var COPY_LIST_FLAG = {
|
|
|
39717
39718
|
descriptions: "--descriptions",
|
|
39718
39719
|
longHeadlines: "--long-headlines"
|
|
39719
39720
|
};
|
|
39720
|
-
function valueAt(root,
|
|
39721
|
+
function valueAt(root, path68) {
|
|
39721
39722
|
let node = root;
|
|
39722
|
-
for (const key of
|
|
39723
|
+
for (const key of path68) {
|
|
39723
39724
|
if (node === null || typeof node !== "object") return void 0;
|
|
39724
39725
|
node = node[key];
|
|
39725
39726
|
}
|
|
@@ -39952,11 +39953,11 @@ function parseKeywordEntry(raw, defaultMatch) {
|
|
|
39952
39953
|
function rawTextEntries(value2, rawArgs) {
|
|
39953
39954
|
return repeatedValues(rawArgs, "text", value2).flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
|
|
39954
39955
|
}
|
|
39955
|
-
function rawFileEntries(
|
|
39956
|
-
if (typeof
|
|
39956
|
+
function rawFileEntries(path68) {
|
|
39957
|
+
if (typeof path68 !== "string" || path68.length === 0) {
|
|
39957
39958
|
return [];
|
|
39958
39959
|
}
|
|
39959
|
-
return readFileSync4(
|
|
39960
|
+
return readFileSync4(path68, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
39960
39961
|
}
|
|
39961
39962
|
function keywordEntries(args8, rawArgs) {
|
|
39962
39963
|
const defaultMatch = typeof args8["match-type"] === "string" ? args8["match-type"].toUpperCase() : void 0;
|
|
@@ -39979,14 +39980,14 @@ function keywordEntries(args8, rawArgs) {
|
|
|
39979
39980
|
}
|
|
39980
39981
|
return entries;
|
|
39981
39982
|
}
|
|
39982
|
-
function loadJsonFileArg(
|
|
39983
|
-
if (typeof
|
|
39983
|
+
function loadJsonFileArg(path68) {
|
|
39984
|
+
if (typeof path68 !== "string" || path68.length === 0) {
|
|
39984
39985
|
return {};
|
|
39985
39986
|
}
|
|
39986
|
-
const inline =
|
|
39987
|
-
const source = inline ? "inline JSON" :
|
|
39987
|
+
const inline = path68.trimStart().startsWith("{");
|
|
39988
|
+
const source = inline ? "inline JSON" : path68;
|
|
39988
39989
|
try {
|
|
39989
|
-
const parsed = JSON.parse(inline ?
|
|
39990
|
+
const parsed = JSON.parse(inline ? path68 : readFileSync4(path68, "utf8"));
|
|
39990
39991
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
39991
39992
|
failWriteValidation2(`${source} must contain a JSON object`);
|
|
39992
39993
|
}
|
|
@@ -40123,10 +40124,10 @@ async function stageUpdate(kind, customerId, target2, payload, hints2) {
|
|
|
40123
40124
|
async function stageTarget(kind, customerId, target2, hints2) {
|
|
40124
40125
|
await stageGoogleOp({ kind, customerId, target: target2 }, hints2);
|
|
40125
40126
|
}
|
|
40126
|
-
async function draftAction(
|
|
40127
|
+
async function draftAction(path68, body, chat) {
|
|
40127
40128
|
try {
|
|
40128
40129
|
const chatId = resolveChatId(chat);
|
|
40129
|
-
const response = await apiPost(
|
|
40130
|
+
const response = await apiPost(path68, { chatId, ...body });
|
|
40130
40131
|
writeJsonEnvelope(response);
|
|
40131
40132
|
} catch (err) {
|
|
40132
40133
|
handleGoogleError(err);
|
|
@@ -42546,14 +42547,14 @@ function mappingRowProblem(row2) {
|
|
|
42546
42547
|
|
|
42547
42548
|
// ../api/src/event-sources/template.ts
|
|
42548
42549
|
var MENTION = /\{\{\s*([^{}]+?)\s*\}\}/g;
|
|
42549
|
-
function mentionOf(
|
|
42550
|
-
return `{{${
|
|
42550
|
+
function mentionOf(path68) {
|
|
42551
|
+
return `{{${path68}}}`;
|
|
42551
42552
|
}
|
|
42552
42553
|
function templateMentions(template) {
|
|
42553
42554
|
const seen = [];
|
|
42554
42555
|
for (const match of template.matchAll(MENTION)) {
|
|
42555
|
-
const
|
|
42556
|
-
if (
|
|
42556
|
+
const path68 = (match[1] ?? "").trim();
|
|
42557
|
+
if (path68 !== "" && !seen.includes(path68)) seen.push(path68);
|
|
42557
42558
|
}
|
|
42558
42559
|
return seen;
|
|
42559
42560
|
}
|
|
@@ -45950,17 +45951,17 @@ function ignoredColumnHints(columns) {
|
|
|
45950
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.`
|
|
45951
45952
|
];
|
|
45952
45953
|
}
|
|
45953
|
-
function readRowsFile(
|
|
45954
|
-
const inline =
|
|
45954
|
+
function readRowsFile(path68) {
|
|
45955
|
+
const inline = path68.trimStart().startsWith("[") || path68.trimStart().startsWith("{");
|
|
45955
45956
|
let text3;
|
|
45956
45957
|
try {
|
|
45957
|
-
text3 = inline ?
|
|
45958
|
+
text3 = inline ? path68 : readFileSync6(path68, "utf8");
|
|
45958
45959
|
} catch (err) {
|
|
45959
|
-
failWriteValidation2(`could not read ${
|
|
45960
|
+
failWriteValidation2(`could not read ${path68}: ${err instanceof Error ? err.message : String(err)}`);
|
|
45960
45961
|
}
|
|
45961
45962
|
const parsed = parseConversionRowsText(text3);
|
|
45962
45963
|
if (!parsed.ok) {
|
|
45963
|
-
failWriteValidation2(`${inline ? "the conversions passed inline" :
|
|
45964
|
+
failWriteValidation2(`${inline ? "the conversions passed inline" : path68}: ${parsed.error}`);
|
|
45964
45965
|
}
|
|
45965
45966
|
return { rows: parsed.rows, ignoredColumns: parsed.ignoredColumns };
|
|
45966
45967
|
}
|
|
@@ -47861,19 +47862,19 @@ function failWriteValidation3(message) {
|
|
|
47861
47862
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
47862
47863
|
process.exit(1);
|
|
47863
47864
|
}
|
|
47864
|
-
function loadJsonFileArg2(
|
|
47865
|
-
if (typeof
|
|
47865
|
+
function loadJsonFileArg2(path68) {
|
|
47866
|
+
if (typeof path68 !== "string" || path68.length === 0) {
|
|
47866
47867
|
return {};
|
|
47867
47868
|
}
|
|
47868
47869
|
try {
|
|
47869
|
-
const parsed = JSON.parse(readFileSync7(
|
|
47870
|
+
const parsed = JSON.parse(readFileSync7(path68, "utf8"));
|
|
47870
47871
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
47871
|
-
failWriteValidation3(`${
|
|
47872
|
+
failWriteValidation3(`${path68} must contain a JSON object`);
|
|
47872
47873
|
}
|
|
47873
47874
|
return parsed;
|
|
47874
47875
|
} catch (err) {
|
|
47875
47876
|
if (err instanceof SyntaxError) {
|
|
47876
|
-
failWriteValidation3(`${
|
|
47877
|
+
failWriteValidation3(`${path68} is not valid JSON: ${err.message}`);
|
|
47877
47878
|
}
|
|
47878
47879
|
throw err;
|
|
47879
47880
|
}
|
|
@@ -47958,15 +47959,15 @@ function parseLocaleFlag(value2) {
|
|
|
47958
47959
|
}
|
|
47959
47960
|
return { language: match[1], country: match[2].toUpperCase() };
|
|
47960
47961
|
}
|
|
47961
|
-
function loadTargetingFileArg(
|
|
47962
|
-
if (typeof
|
|
47962
|
+
function loadTargetingFileArg(path68) {
|
|
47963
|
+
if (typeof path68 !== "string" || path68.length === 0) {
|
|
47963
47964
|
return void 0;
|
|
47964
47965
|
}
|
|
47965
|
-
const parsed = loadJsonFileArg2(
|
|
47966
|
+
const parsed = loadJsonFileArg2(path68);
|
|
47966
47967
|
const criteria = parsed.targetingCriteria ?? parsed;
|
|
47967
47968
|
if (!criteria.include) {
|
|
47968
47969
|
failWriteValidation3(
|
|
47969
|
-
`${
|
|
47970
|
+
`${path68} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
|
|
47970
47971
|
);
|
|
47971
47972
|
}
|
|
47972
47973
|
return criteria;
|
|
@@ -48001,14 +48002,14 @@ function parseCsvLine(line) {
|
|
|
48001
48002
|
cells.push(current);
|
|
48002
48003
|
return cells.map((cell3) => cell3.trim());
|
|
48003
48004
|
}
|
|
48004
|
-
function parseListFileArg(
|
|
48005
|
-
if (typeof
|
|
48005
|
+
function parseListFileArg(path68, maxRows) {
|
|
48006
|
+
if (typeof path68 !== "string" || path68.length === 0) {
|
|
48006
48007
|
return void 0;
|
|
48007
48008
|
}
|
|
48008
|
-
const raw = readFileSync7(
|
|
48009
|
+
const raw = readFileSync7(path68, "utf8");
|
|
48009
48010
|
const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
48010
48011
|
if (lines.length < 2) {
|
|
48011
|
-
failWriteValidation3(`${
|
|
48012
|
+
failWriteValidation3(`${path68} needs a header row and at least one data row`);
|
|
48012
48013
|
}
|
|
48013
48014
|
const columns = parseCsvLine(lines[0]).map((column) => column.trim());
|
|
48014
48015
|
const rows = [];
|
|
@@ -48027,7 +48028,7 @@ function parseListFileArg(path67, maxRows) {
|
|
|
48027
48028
|
}
|
|
48028
48029
|
}
|
|
48029
48030
|
if (rows.length > maxRows) {
|
|
48030
|
-
failWriteValidation3(`${
|
|
48031
|
+
failWriteValidation3(`${path68} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
|
|
48031
48032
|
}
|
|
48032
48033
|
return { columns, rows };
|
|
48033
48034
|
}
|
|
@@ -48123,11 +48124,11 @@ function readPositionals(args8) {
|
|
|
48123
48124
|
function splitIdList(raw) {
|
|
48124
48125
|
return raw.split(",").map((id) => id.trim()).filter(Boolean);
|
|
48125
48126
|
}
|
|
48126
|
-
function idsFileEntries(
|
|
48127
|
-
if (typeof
|
|
48127
|
+
function idsFileEntries(path68) {
|
|
48128
|
+
if (typeof path68 !== "string" || path68.length === 0) {
|
|
48128
48129
|
return [];
|
|
48129
48130
|
}
|
|
48130
|
-
return readFileSync7(
|
|
48131
|
+
return readFileSync7(path68, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
|
|
48131
48132
|
}
|
|
48132
48133
|
function requireTargets2(args8, entity) {
|
|
48133
48134
|
const positionals = readPositionals(args8);
|
|
@@ -52229,54 +52230,54 @@ function ruleFields(node) {
|
|
|
52229
52230
|
([key, value2]) => (key === "and" || key === "or") && Array.isArray(value2) ? value2.flatMap(ruleFields) : [key]
|
|
52230
52231
|
);
|
|
52231
52232
|
}
|
|
52232
|
-
function branchRefusal(key, value2,
|
|
52233
|
+
function branchRefusal(key, value2, path68) {
|
|
52233
52234
|
if (!Array.isArray(value2) || value2.length === 0) {
|
|
52234
|
-
return `${
|
|
52235
|
+
return `${path68}.${key} must be a non-empty array of conditions \u2014 an empty one counts every event the pixel sends`;
|
|
52235
52236
|
}
|
|
52236
52237
|
for (const [index, child] of value2.entries()) {
|
|
52237
|
-
const refusal = ruleNodeRefusal(child, `${
|
|
52238
|
+
const refusal = ruleNodeRefusal(child, `${path68}.${key}[${index}]`);
|
|
52238
52239
|
if (refusal) {
|
|
52239
52240
|
return refusal;
|
|
52240
52241
|
}
|
|
52241
52242
|
}
|
|
52242
52243
|
return void 0;
|
|
52243
52244
|
}
|
|
52244
|
-
function conditionRefusal(key, value2,
|
|
52245
|
-
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);
|
|
52246
52247
|
}
|
|
52247
|
-
function ruleNodeRefusal(node,
|
|
52248
|
+
function ruleNodeRefusal(node, path68) {
|
|
52248
52249
|
if (!isRecord2(node)) {
|
|
52249
|
-
return `${
|
|
52250
|
+
return `${path68} must be an object like {"URL":{"i_contains":"/thank-you"}}`;
|
|
52250
52251
|
}
|
|
52251
52252
|
const entries = Object.entries(node);
|
|
52252
52253
|
if (entries.length === 0) {
|
|
52253
|
-
return `${
|
|
52254
|
+
return `${path68} is empty \u2014 a rule with no condition counts every event the pixel sends`;
|
|
52254
52255
|
}
|
|
52255
52256
|
if (entries.length > 1) {
|
|
52256
|
-
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]}`;
|
|
52257
52258
|
}
|
|
52258
52259
|
for (const [key, value2] of entries) {
|
|
52259
|
-
const refusal = conditionRefusal(key, value2,
|
|
52260
|
+
const refusal = conditionRefusal(key, value2, path68);
|
|
52260
52261
|
if (refusal) {
|
|
52261
52262
|
return refusal;
|
|
52262
52263
|
}
|
|
52263
52264
|
}
|
|
52264
52265
|
return void 0;
|
|
52265
52266
|
}
|
|
52266
|
-
function leafRefusal(field, condition,
|
|
52267
|
+
function leafRefusal(field, condition, path68) {
|
|
52267
52268
|
if (!isRecord2(condition)) {
|
|
52268
|
-
return `${
|
|
52269
|
+
return `${path68}.${field} must be a test like {"i_contains":"/thank-you"}, not a bare value`;
|
|
52269
52270
|
}
|
|
52270
52271
|
const tests = Object.entries(condition);
|
|
52271
52272
|
if (tests.length === 0) {
|
|
52272
|
-
return `${
|
|
52273
|
+
return `${path68}.${field} names no test \u2014 use one of ${Object.keys(RULE_OPERATORS).join(", ")}`;
|
|
52273
52274
|
}
|
|
52274
52275
|
if (tests.length > 1) {
|
|
52275
|
-
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]}`;
|
|
52276
52277
|
}
|
|
52277
52278
|
for (const [operator] of tests) {
|
|
52278
52279
|
if (!RULE_OPERATORS[operator]) {
|
|
52279
|
-
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)`;
|
|
52280
52281
|
}
|
|
52281
52282
|
}
|
|
52282
52283
|
return void 0;
|
|
@@ -52992,11 +52993,11 @@ var updateStatusSchema2 = z52.enum(UPDATE_STATUSES);
|
|
|
52992
52993
|
function currencyMinimums2(currencyCode) {
|
|
52993
52994
|
return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
|
|
52994
52995
|
}
|
|
52995
|
-
function validateDailyBudgetFloor(money, ctx,
|
|
52996
|
+
function validateDailyBudgetFloor(money, ctx, path68) {
|
|
52996
52997
|
if (money?.currencyCode) {
|
|
52997
52998
|
const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
|
|
52998
52999
|
if (Number(money.amount) < min) {
|
|
52999
|
-
ctx.addIssue({ code: "custom", path:
|
|
53000
|
+
ctx.addIssue({ code: "custom", path: path68, message: `below the ${min} ${money.currencyCode} daily minimum` });
|
|
53000
53001
|
}
|
|
53001
53002
|
}
|
|
53002
53003
|
}
|
|
@@ -53914,19 +53915,19 @@ function refuseDeleteByUpdate(payload, group) {
|
|
|
53914
53915
|
);
|
|
53915
53916
|
}
|
|
53916
53917
|
}
|
|
53917
|
-
function loadJsonFileArg3(
|
|
53918
|
-
if (typeof
|
|
53918
|
+
function loadJsonFileArg3(path68) {
|
|
53919
|
+
if (typeof path68 !== "string" || path68.length === 0) {
|
|
53919
53920
|
return {};
|
|
53920
53921
|
}
|
|
53921
53922
|
try {
|
|
53922
|
-
const parsed = JSON.parse(readFileSync11(
|
|
53923
|
+
const parsed = JSON.parse(readFileSync11(path68, "utf8"));
|
|
53923
53924
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
53924
|
-
failWriteValidation4(`${
|
|
53925
|
+
failWriteValidation4(`${path68} must contain a JSON object`);
|
|
53925
53926
|
}
|
|
53926
53927
|
return parsed;
|
|
53927
53928
|
} catch (err) {
|
|
53928
53929
|
if (err instanceof SyntaxError) {
|
|
53929
|
-
failWriteValidation4(`${
|
|
53930
|
+
failWriteValidation4(`${path68} is not valid JSON: ${err.message}`);
|
|
53930
53931
|
}
|
|
53931
53932
|
throw err;
|
|
53932
53933
|
}
|
|
@@ -54504,10 +54505,10 @@ Example: baker ads meta ads retag 120252158038400247 --url-tags "utm_source={{si
|
|
|
54504
54505
|
}
|
|
54505
54506
|
},
|
|
54506
54507
|
run: async ({ args: args8 }) => {
|
|
54507
|
-
const { apiPost: apiPost2 } = await import("./client-
|
|
54508
|
-
const { requireChatId: requireChatId2 } = await import("./env-
|
|
54509
|
-
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-
|
|
54510
|
-
const { handleMetaError: handleMetaError2 } = await import("./shared-
|
|
54508
|
+
const { apiPost: apiPost2 } = await import("./client-JM3QBGR2.js");
|
|
54509
|
+
const { requireChatId: requireChatId2 } = await import("./env-WIAWLTDN.js");
|
|
54510
|
+
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-UGMWOFQR.js");
|
|
54511
|
+
const { handleMetaError: handleMetaError2 } = await import("./shared-DJTAT7CM.js");
|
|
54511
54512
|
try {
|
|
54512
54513
|
const accountId = bareAccountId2(args8);
|
|
54513
54514
|
const urlTags = typeof args8["url-tags"] === "string" ? args8["url-tags"] : void 0;
|
|
@@ -54658,10 +54659,10 @@ function duplicateCommand2(entity, label) {
|
|
|
54658
54659
|
replace: { type: "boolean", description: "Pause the original once the copy publishes" }
|
|
54659
54660
|
},
|
|
54660
54661
|
run: async ({ args: args8 }) => {
|
|
54661
|
-
const { apiPost: apiPost2 } = await import("./client-
|
|
54662
|
-
const { requireChatId: requireChatId2 } = await import("./env-
|
|
54663
|
-
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-
|
|
54664
|
-
const { handleMetaError: handleMetaError2 } = await import("./shared-
|
|
54662
|
+
const { apiPost: apiPost2 } = await import("./client-JM3QBGR2.js");
|
|
54663
|
+
const { requireChatId: requireChatId2 } = await import("./env-WIAWLTDN.js");
|
|
54664
|
+
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-UGMWOFQR.js");
|
|
54665
|
+
const { handleMetaError: handleMetaError2 } = await import("./shared-DJTAT7CM.js");
|
|
54665
54666
|
try {
|
|
54666
54667
|
const accountId = bareAccountId2(args8);
|
|
54667
54668
|
const chatId = requireChatId2();
|
|
@@ -54828,9 +54829,9 @@ var listArgs6 = {
|
|
|
54828
54829
|
},
|
|
54829
54830
|
...META_LIST_SHAPE_ARGS
|
|
54830
54831
|
};
|
|
54831
|
-
async function readOrEmpty(
|
|
54832
|
+
async function readOrEmpty(path68, params, label, hints2) {
|
|
54832
54833
|
try {
|
|
54833
|
-
return await apiGet(
|
|
54834
|
+
return await apiGet(path68, params);
|
|
54834
54835
|
} catch (err) {
|
|
54835
54836
|
const message = err instanceof Error ? err.message : "unknown error";
|
|
54836
54837
|
hints2.push(`Could not read the account's ${label} (${message}); the viewer was read without them.`);
|
|
@@ -59773,12 +59774,12 @@ function flowExists(slug) {
|
|
|
59773
59774
|
return existsSync3(join2(flowsDir(), slug, "_data.json"));
|
|
59774
59775
|
}
|
|
59775
59776
|
function readFlowTree(slug) {
|
|
59776
|
-
const
|
|
59777
|
-
if (!existsSync3(
|
|
59777
|
+
const path68 = join2(flowsDir(), slug, "_data.json");
|
|
59778
|
+
if (!existsSync3(path68)) {
|
|
59778
59779
|
failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
|
|
59779
59780
|
}
|
|
59780
59781
|
try {
|
|
59781
|
-
return JSON.parse(readFileSync13(
|
|
59782
|
+
return JSON.parse(readFileSync13(path68, "utf-8"));
|
|
59782
59783
|
} catch (error) {
|
|
59783
59784
|
failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
|
|
59784
59785
|
}
|
|
@@ -60755,12 +60756,12 @@ function readPeopleFile(args8) {
|
|
|
60755
60756
|
}
|
|
60756
60757
|
function unreadablePeopleFile(args8) {
|
|
60757
60758
|
if (!args8["people-file"]) return void 0;
|
|
60758
|
-
const
|
|
60759
|
+
const path68 = String(args8["people-file"]);
|
|
60759
60760
|
try {
|
|
60760
|
-
readFileSync14(
|
|
60761
|
+
readFileSync14(path68, "utf8");
|
|
60761
60762
|
return void 0;
|
|
60762
60763
|
} catch {
|
|
60763
|
-
return `Couldn't read --people-file ${
|
|
60764
|
+
return `Couldn't read --people-file ${path68}. Check the path, or pass the people inline with --person.`;
|
|
60764
60765
|
}
|
|
60765
60766
|
}
|
|
60766
60767
|
function peopleLimitError(args8) {
|
|
@@ -61732,11 +61733,11 @@ function parseFieldArgs(argv) {
|
|
|
61732
61733
|
for (const pair of parseRepeated(argv, "--field")) {
|
|
61733
61734
|
const eq = pair.indexOf("=");
|
|
61734
61735
|
const field = eq === -1 ? pair : pair.slice(0, eq);
|
|
61735
|
-
const
|
|
61736
|
+
const path68 = eq === -1 ? "" : pair.slice(eq + 1).trim();
|
|
61736
61737
|
const problem = mappingRowProblem(field);
|
|
61737
61738
|
if (problem !== null) throw new ConversionArgsError(problem);
|
|
61738
|
-
if (
|
|
61739
|
-
mapping[field] =
|
|
61739
|
+
if (path68 === "") throw new ConversionArgsError(`--field ${field} needs a path: --field ${field}=<dotted.path>.`);
|
|
61740
|
+
mapping[field] = path68;
|
|
61740
61741
|
}
|
|
61741
61742
|
return mapping;
|
|
61742
61743
|
}
|
|
@@ -63409,11 +63410,11 @@ function unwrap(response) {
|
|
|
63409
63410
|
}
|
|
63410
63411
|
return response.data;
|
|
63411
63412
|
}
|
|
63412
|
-
async function readAvatars(
|
|
63413
|
-
return unwrap(await apiGet(
|
|
63413
|
+
async function readAvatars(path68, params) {
|
|
63414
|
+
return unwrap(await apiGet(path68, params));
|
|
63414
63415
|
}
|
|
63415
|
-
async function writeAvatars(
|
|
63416
|
-
return unwrap(await apiPost(
|
|
63416
|
+
async function writeAvatars(path68, body, opts) {
|
|
63417
|
+
return unwrap(await apiPost(path68, body, opts));
|
|
63417
63418
|
}
|
|
63418
63419
|
|
|
63419
63420
|
// src/commands/avatars/create.ts
|
|
@@ -64527,12 +64528,12 @@ function missingFontFiles(urls, available) {
|
|
|
64527
64528
|
function planFontAdoption(sources, families) {
|
|
64528
64529
|
const wanted = new Map(families.map((family) => [normalizeFamily(family), family]));
|
|
64529
64530
|
const byFamily = /* @__PURE__ */ new Map();
|
|
64530
|
-
for (const { path:
|
|
64531
|
-
const dir = posix.dirname(
|
|
64531
|
+
for (const { path: path68, source } of sources) {
|
|
64532
|
+
const dir = posix.dirname(path68);
|
|
64532
64533
|
for (const face of declaredFontFaces(source)) {
|
|
64533
64534
|
if (!wanted.has(face.family)) continue;
|
|
64534
64535
|
const perFile = byFamily.get(face.family) ?? /* @__PURE__ */ new Map();
|
|
64535
|
-
perFile.set(
|
|
64536
|
+
perFile.set(path68, [...perFile.get(path68) ?? [], rebaseFontFaceSrc(face.block, dir)]);
|
|
64536
64537
|
byFamily.set(face.family, perFile);
|
|
64537
64538
|
}
|
|
64538
64539
|
}
|
|
@@ -67201,8 +67202,8 @@ function logoComposited(opts) {
|
|
|
67201
67202
|
}
|
|
67202
67203
|
var MAX_TAKES = 3;
|
|
67203
67204
|
var UNCOMPILED = "compiled from prompt.json";
|
|
67204
|
-
function ingestNode(id,
|
|
67205
|
-
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 } };
|
|
67206
67207
|
}
|
|
67207
67208
|
function aspectRatio(blueprint, opts) {
|
|
67208
67209
|
const requested = opts.aspectRatio ?? blueprint.meta?.estimated_aspect_ratio;
|
|
@@ -67633,10 +67634,63 @@ function figureSourced(match, knowledge2) {
|
|
|
67633
67634
|
return new RegExp(`${number2}[^.!?\\n]{0,60}?${stem}|${stem}[^.!?\\n]{0,60}?${number2}`, "iu").test(knowledge2);
|
|
67634
67635
|
});
|
|
67635
67636
|
}
|
|
67637
|
+
var NUMBER_WORDS = {
|
|
67638
|
+
one: 1,
|
|
67639
|
+
two: 2,
|
|
67640
|
+
three: 3,
|
|
67641
|
+
four: 4,
|
|
67642
|
+
five: 5,
|
|
67643
|
+
six: 6,
|
|
67644
|
+
seven: 7,
|
|
67645
|
+
eight: 8,
|
|
67646
|
+
nine: 9,
|
|
67647
|
+
ten: 10,
|
|
67648
|
+
eleven: 11,
|
|
67649
|
+
twelve: 12,
|
|
67650
|
+
fifteen: 15,
|
|
67651
|
+
twenty: 20,
|
|
67652
|
+
thirty: 30,
|
|
67653
|
+
forty: 40,
|
|
67654
|
+
fifty: 50,
|
|
67655
|
+
sixty: 60,
|
|
67656
|
+
ninety: 90,
|
|
67657
|
+
hundred: 100,
|
|
67658
|
+
un: 1,
|
|
67659
|
+
uno: 1,
|
|
67660
|
+
una: 1,
|
|
67661
|
+
dos: 2,
|
|
67662
|
+
tres: 3,
|
|
67663
|
+
cuatro: 4,
|
|
67664
|
+
cinco: 5,
|
|
67665
|
+
seis: 6,
|
|
67666
|
+
siete: 7,
|
|
67667
|
+
ocho: 8,
|
|
67668
|
+
nueve: 9,
|
|
67669
|
+
diez: 10,
|
|
67670
|
+
once: 11,
|
|
67671
|
+
doce: 12,
|
|
67672
|
+
quince: 15,
|
|
67673
|
+
veinte: 20,
|
|
67674
|
+
treinta: 30,
|
|
67675
|
+
cuarenta: 40,
|
|
67676
|
+
cincuenta: 50,
|
|
67677
|
+
sesenta: 60,
|
|
67678
|
+
noventa: 90,
|
|
67679
|
+
cien: 100
|
|
67680
|
+
};
|
|
67681
|
+
var NUMBER_WORD = new RegExp(`(?<![\\p{L}])(${Object.keys(NUMBER_WORDS).join("|")})(?![\\p{L}])`, "giu");
|
|
67682
|
+
function withDigits(text3) {
|
|
67683
|
+
return text3.replace(NUMBER_WORD, (word) => String(NUMBER_WORDS[word.toLowerCase()]));
|
|
67684
|
+
}
|
|
67685
|
+
var SUPERLATIVE = new RegExp("(?<![\\p{L}])(the only|only one|#\\s?1|number one|n[\xBA\xB0o]\\.?\\s?1|the (?:best|fastest|cheapest|biggest|largest|lowest|highest|leading)|fastest|cheapest|el \xFAnico|la \xFAnica|los \xFAnicos|las \xFAnicas|el mejor|la mejor|los mejores|el m\xE1s \\p{L}+|la m\xE1s \\p{L}+|n\xFAmero 1|l\xEDder)(?![\\p{L}])", "giu");
|
|
67636
67686
|
function unsourcedFigureLines(spec, knowledge2) {
|
|
67637
|
-
|
|
67638
|
-
|
|
67639
|
-
)
|
|
67687
|
+
const records = withDigits(knowledge2);
|
|
67688
|
+
const lower = knowledge2.toLowerCase();
|
|
67689
|
+
return spec.copy.flatMap((line) => {
|
|
67690
|
+
const figure = [...withDigits(line.text).matchAll(CLAIMED_FIGURE)].some((m) => !figureSourced(m, records));
|
|
67691
|
+
const superlative = [...line.text.matchAll(SUPERLATIVE)].some((m) => !lower.includes(m[0].toLowerCase()));
|
|
67692
|
+
return figure || superlative ? [line.text] : [];
|
|
67693
|
+
});
|
|
67640
67694
|
}
|
|
67641
67695
|
function proofProblems(spec, knowledge2) {
|
|
67642
67696
|
const problems = [];
|
|
@@ -67658,7 +67712,7 @@ function proofProblems(spec, knowledge2) {
|
|
|
67658
67712
|
}
|
|
67659
67713
|
for (const text3 of unsourcedFigureLines(spec, knowledge2)) {
|
|
67660
67714
|
problems.push(
|
|
67661
|
-
`"${text3}" promises a figure the company's records (src/content, COMPANY.md) do not hold: say only what they record, or drop
|
|
67715
|
+
`"${text3}" promises a figure or a superlative ("the only", "the fastest") the company's records (src/content, COMPANY.md) do not hold: say only what they record, or drop it.`
|
|
67662
67716
|
);
|
|
67663
67717
|
}
|
|
67664
67718
|
return problems;
|
|
@@ -72059,7 +72113,7 @@ async function probeDuration(filePath) {
|
|
|
72059
72113
|
import { defineCommand as defineCommand140 } from "citty";
|
|
72060
72114
|
|
|
72061
72115
|
// src/commands/canvas/run.ts
|
|
72062
|
-
import
|
|
72116
|
+
import path30 from "path";
|
|
72063
72117
|
import { defineCommand as defineCommand139 } from "citty";
|
|
72064
72118
|
|
|
72065
72119
|
// src/commands/canvas/definition-status.ts
|
|
@@ -74819,6 +74873,10 @@ async function reviewStaticRun(opts) {
|
|
|
74819
74873
|
const shape = staticRunShape(opts.parsed);
|
|
74820
74874
|
const placements = canvasPlacements(opts.parsed) ?? safeAreaPlacements(opts.viewer);
|
|
74821
74875
|
const masterFormat = staticMasterFormat(opts.parsed) ?? void 0;
|
|
74876
|
+
const glancing = opts.glance === false ? Promise.resolve(null) : glanceAtFinished(targets.images, opts.viewer ?? STRANGER, intended, opts.glance ?? gatewayGlanceModel(opts.onSpend)).catch(
|
|
74877
|
+
() => null
|
|
74878
|
+
);
|
|
74879
|
+
const comparing = opts.previous ? nearIdenticalFindings(targets.images, opts.previous).catch(() => []) : Promise.resolve([]);
|
|
74822
74880
|
const review = await reviewRenderedStatic({
|
|
74823
74881
|
images: targets.images,
|
|
74824
74882
|
copy,
|
|
@@ -74839,8 +74897,9 @@ async function reviewStaticRun(opts) {
|
|
|
74839
74897
|
...opts.viewer ? { viewer: describeViewer(opts.viewer) } : {},
|
|
74840
74898
|
onSpend: opts.onSpend
|
|
74841
74899
|
});
|
|
74842
|
-
const
|
|
74843
|
-
const
|
|
74900
|
+
const [glanced, compared] = await Promise.all([glancing, comparing]);
|
|
74901
|
+
const glance = review === null ? null : glanced;
|
|
74902
|
+
const unchanged = review === null ? [] : compared;
|
|
74844
74903
|
const fresh = review === null ? null : [...review, ...glance ?? [], ...unchanged];
|
|
74845
74904
|
const reviewed = fresh === null || !opts.settled ? fresh : carryUnchanged(fresh, opts.settled.review, await samePictureSizes(targets.images, opts.settled.images));
|
|
74846
74905
|
const verdict2 = staticVerdict(reviewed);
|
|
@@ -74972,8 +75031,8 @@ var Answer = z66.object({
|
|
|
74972
75031
|
changed: z66.string().min(1),
|
|
74973
75032
|
set: z66.array(z66.object({ path: z66.string().min(1), value: z66.unknown() }))
|
|
74974
75033
|
});
|
|
74975
|
-
function withValueAt(root,
|
|
74976
|
-
const [key, ...rest] =
|
|
75034
|
+
function withValueAt(root, path68, value2) {
|
|
75035
|
+
const [key, ...rest] = path68;
|
|
74977
75036
|
if (key === void 0) return value2;
|
|
74978
75037
|
if (Array.isArray(root)) {
|
|
74979
75038
|
const at = Number(key);
|
|
@@ -74991,7 +75050,7 @@ function printedWords(spec) {
|
|
|
74991
75050
|
function acceptRepair(before, answer) {
|
|
74992
75051
|
const parsed = Answer.safeParse(answer);
|
|
74993
75052
|
if (!parsed.success || !isAdSpec(before)) return null;
|
|
74994
|
-
const spec = parsed.data.set.reduce((at, { path:
|
|
75053
|
+
const spec = parsed.data.set.reduce((at, { path: path68, value: value2 }) => withValueAt(at, path68.split("."), value2), before);
|
|
74995
75054
|
if (!isAdSpec(spec)) return null;
|
|
74996
75055
|
const was = AdSpecSchema.safeParse(before);
|
|
74997
75056
|
const now = AdSpecSchema.safeParse(spec);
|
|
@@ -75041,6 +75100,52 @@ async function repairSpec(opts) {
|
|
|
75041
75100
|
return repair.changed;
|
|
75042
75101
|
}
|
|
75043
75102
|
|
|
75103
|
+
// src/commands/canvas/turn-renders.ts
|
|
75104
|
+
import { mkdir as mkdir9, readFile as readFile25, writeFile as writeFile15 } from "fs/promises";
|
|
75105
|
+
import path29 from "path";
|
|
75106
|
+
import { z as z67 } from "zod";
|
|
75107
|
+
var RUNS_PER_TURN = 2;
|
|
75108
|
+
var LedgerSchema = z67.object({
|
|
75109
|
+
turnId: z67.string(),
|
|
75110
|
+
creatives: z67.record(z67.string(), z67.object({ runs: z67.number(), verdict: z67.string().optional() }))
|
|
75111
|
+
});
|
|
75112
|
+
function turnRenderRefusal(ledger, turnId, slug) {
|
|
75113
|
+
const entry = turnId !== void 0 && ledger?.turnId === turnId ? ledger.creatives[slug] : void 0;
|
|
75114
|
+
if (!entry) return null;
|
|
75115
|
+
if (entry.verdict === "ready" || entry.verdict === "ready_with_notes") {
|
|
75116
|
+
return {
|
|
75117
|
+
code: "CONFLICT",
|
|
75118
|
+
message: `This ad is already ready in this turn (${entry.verdict}): its notes are not defects.`,
|
|
75119
|
+
fix: "Show it now. A change the person asks for comes with their next message."
|
|
75120
|
+
};
|
|
75121
|
+
}
|
|
75122
|
+
if (entry.runs >= RUNS_PER_TURN) {
|
|
75123
|
+
return {
|
|
75124
|
+
code: "CONFLICT",
|
|
75125
|
+
message: `This ad was rendered twice in this turn, its automatic corrections included, and is still not ready.`,
|
|
75126
|
+
fix: "Show the best version and say in one plain sentence what is left, in the person's words; they decide what changes next."
|
|
75127
|
+
};
|
|
75128
|
+
}
|
|
75129
|
+
return null;
|
|
75130
|
+
}
|
|
75131
|
+
function recordTurnRender(ledger, turnId, slug, verdict2) {
|
|
75132
|
+
const creatives = ledger?.turnId === turnId ? ledger.creatives : {};
|
|
75133
|
+
return { turnId, creatives: { ...creatives, [slug]: { runs: (creatives[slug]?.runs ?? 0) + 1, verdict: verdict2 } } };
|
|
75134
|
+
}
|
|
75135
|
+
var ledgerPath = (cacheDir) => path29.join(cacheDir, "turn-renders.json");
|
|
75136
|
+
async function readTurnRenders(cacheDir) {
|
|
75137
|
+
try {
|
|
75138
|
+
const parsed = LedgerSchema.safeParse(JSON.parse(await readFile25(ledgerPath(cacheDir), "utf8")));
|
|
75139
|
+
return parsed.success ? parsed.data : void 0;
|
|
75140
|
+
} catch {
|
|
75141
|
+
return void 0;
|
|
75142
|
+
}
|
|
75143
|
+
}
|
|
75144
|
+
async function writeTurnRenders(cacheDir, ledger) {
|
|
75145
|
+
await mkdir9(cacheDir, { recursive: true });
|
|
75146
|
+
await writeFile15(ledgerPath(cacheDir), JSON.stringify(ledger), "utf8");
|
|
75147
|
+
}
|
|
75148
|
+
|
|
75044
75149
|
// src/commands/canvas/run.ts
|
|
75045
75150
|
var OPERATOR = "Operator only, agents never need it.";
|
|
75046
75151
|
var runCommand = defineCommand139({
|
|
@@ -75163,7 +75268,7 @@ function failPreflight3(code, message, fix) {
|
|
|
75163
75268
|
process.exit(2);
|
|
75164
75269
|
}
|
|
75165
75270
|
async function canvasFileFor(opts) {
|
|
75166
|
-
if (opts.file) return { canvasPath:
|
|
75271
|
+
if (opts.file) return { canvasPath: path30.resolve(opts.file) };
|
|
75167
75272
|
if (!opts.slug) failPreflight3("missing_canvas", "Pass the canvas file, or --slug <slug> to run a creative by name.");
|
|
75168
75273
|
const cwd = process.cwd();
|
|
75169
75274
|
try {
|
|
@@ -75184,15 +75289,27 @@ async function executeCanvasRun(opts) {
|
|
|
75184
75289
|
const canvasRoot = canvasOutputsRoot(filePath);
|
|
75185
75290
|
const creativeSlug = creativeSlugFromCanvasPath(filePath) ?? void 0;
|
|
75186
75291
|
if (creativeSlug) await clearLegacyDirtyMarker(canvasRoot, creativeSlug);
|
|
75292
|
+
const turnId = optionalTurnId();
|
|
75293
|
+
const turnCache = opts.cacheDir ?? path30.join(canvasRoot, ".cache");
|
|
75294
|
+
if (creativeSlug && (opts.autoFixRound ?? 0) === 0) {
|
|
75295
|
+
const refusal = turnRenderRefusal(await readTurnRenders(turnCache), turnId, creativeSlug);
|
|
75296
|
+
if (refusal) {
|
|
75297
|
+
process.stderr.write(
|
|
75298
|
+
`${JSON.stringify({ ok: false, error: { code: refusal.code, message: refusal.message, fix: refusal.fix } }, null, 2)}
|
|
75299
|
+
`
|
|
75300
|
+
);
|
|
75301
|
+
process.exit(2);
|
|
75302
|
+
}
|
|
75303
|
+
}
|
|
75187
75304
|
await wireProofBadges(
|
|
75188
|
-
|
|
75305
|
+
path30.join(path30.dirname(filePath), "prompt.json"),
|
|
75189
75306
|
fetchBrandLogo,
|
|
75190
75307
|
(line) => process.stderr.write(`${line}
|
|
75191
75308
|
`)
|
|
75192
75309
|
);
|
|
75193
75310
|
if ((opts.autoFixRound ?? 0) === 0) {
|
|
75194
75311
|
await wireNamedPeople(
|
|
75195
|
-
|
|
75312
|
+
path30.join(path30.dirname(filePath), "prompt.json"),
|
|
75196
75313
|
avatarOfNamedPerson,
|
|
75197
75314
|
(line) => process.stderr.write(`${line}
|
|
75198
75315
|
`)
|
|
@@ -75286,8 +75403,8 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75286
75403
|
});
|
|
75287
75404
|
const last = Object.fromEntries(
|
|
75288
75405
|
nodesWithRole(parsed, STATIC_ADAPT_FORMAT_ROLE).flatMap((n) => {
|
|
75289
|
-
const
|
|
75290
|
-
return
|
|
75406
|
+
const path68 = delivered.find((d) => d.format === formatOfAdaptId(n.id))?.path;
|
|
75407
|
+
return path68 ? [[n.id, path68]] : [];
|
|
75291
75408
|
})
|
|
75292
75409
|
);
|
|
75293
75410
|
parsed = withSizesFollowingFix(parsed, {
|
|
@@ -75384,7 +75501,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75384
75501
|
process.stdout.write(`[extend] placing ${opts.extendSizes.join(", ")} clear of what covers or cuts it, its margins painted
|
|
75385
75502
|
`);
|
|
75386
75503
|
}
|
|
75387
|
-
const cacheDir = opts.cacheDir ??
|
|
75504
|
+
const cacheDir = opts.cacheDir ?? path30.join(canvasRoot, ".cache");
|
|
75388
75505
|
const engine = createEngineFromEnv({
|
|
75389
75506
|
cacheDir,
|
|
75390
75507
|
outputsDir: opts.outputsDir ?? canvasRoot,
|
|
@@ -75407,7 +75524,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75407
75524
|
const canvasSha = sha256Hex(Buffer.from(canvasText));
|
|
75408
75525
|
const sourceSha = prepared.sourceSha;
|
|
75409
75526
|
const client = opts.record === false ? null : buildBackendClient();
|
|
75410
|
-
const outputsDir = opts.outputsDir ?
|
|
75527
|
+
const outputsDir = opts.outputsDir ? path30.resolve(opts.outputsDir) : canvasRoot;
|
|
75411
75528
|
const { runId, resumed, source, sameSource, concurrentRunId, rendering } = await resolveRunId({
|
|
75412
75529
|
explicitRunId: opts.runId,
|
|
75413
75530
|
adoptedRunId: adoptRunId,
|
|
@@ -75437,7 +75554,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75437
75554
|
}
|
|
75438
75555
|
const viewerUsage = [];
|
|
75439
75556
|
const viewerCheck = creativeSlug && isStaticAdCanvas(parsed) ? await viewerCheckBeforeRender({
|
|
75440
|
-
creativeDir:
|
|
75557
|
+
creativeDir: path30.dirname(filePath),
|
|
75441
75558
|
cacheDir,
|
|
75442
75559
|
slug: creativeSlug,
|
|
75443
75560
|
skip: viewerCheckSkip({
|
|
@@ -75448,7 +75565,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75448
75565
|
resumed: resumed && sameSource === true,
|
|
75449
75566
|
asked: opts.skipViewerCheck === true
|
|
75450
75567
|
}),
|
|
75451
|
-
lookups: liveViewerLookups(
|
|
75568
|
+
lookups: liveViewerLookups(path30.dirname(canvasRoot)),
|
|
75452
75569
|
model: VIEWER_CHECK_MODEL,
|
|
75453
75570
|
ask: gatewayViewerModel(VIEWER_CHECK_MODEL, (usage) => viewerUsage.push(usage))
|
|
75454
75571
|
}) : null;
|
|
@@ -75497,7 +75614,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75497
75614
|
if (snapshot && creativeSlug) await recordSnapshot(canvasRoot, creativeSlug, snapshot);
|
|
75498
75615
|
const recordMeta = {
|
|
75499
75616
|
creativeSlug,
|
|
75500
|
-
canvasPath:
|
|
75617
|
+
canvasPath: path30.relative(path30.dirname(canvasRoot), filePath) || void 0,
|
|
75501
75618
|
canvasSha,
|
|
75502
75619
|
// The fingerprint the dashboard compares against the current source to flag
|
|
75503
75620
|
// "edited since last render": the same `computeSourceSha` the edit-sync posts,
|
|
@@ -75585,7 +75702,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75585
75702
|
brief: opts.brief?.trim() || void 0,
|
|
75586
75703
|
onSpend: (usage) => reviewUsage.push(usage)
|
|
75587
75704
|
}) : null;
|
|
75588
|
-
const brandKit = reviewable ? null : await readStaticBrandKit(await findBrandRoot(
|
|
75705
|
+
const brandKit = reviewable ? null : await readStaticBrandKit(await findBrandRoot(path30.dirname(filePath)) ?? process.cwd());
|
|
75589
75706
|
const runShape = {
|
|
75590
75707
|
fix: fixInstruction !== void 0,
|
|
75591
75708
|
useVariant: opts.useVariant !== void 0,
|
|
@@ -75638,7 +75755,7 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75638
75755
|
] : [];
|
|
75639
75756
|
const findings = staticReview ? staticReview.review : review;
|
|
75640
75757
|
const ready = staticReview ? staticReview.verdict === "ready" || staticReview.verdict === "ready_with_notes" : review !== null && !review.some((f) => f.severity === "blocking");
|
|
75641
|
-
const definitionStatus = ready && creativeSlug ? await advanceDefinitionStatus(
|
|
75758
|
+
const definitionStatus = ready && creativeSlug ? await advanceDefinitionStatus(path30.join(path30.dirname(filePath), "_definition.md"), "rendered") : null;
|
|
75642
75759
|
if (poster) {
|
|
75643
75760
|
await flushBilled(
|
|
75644
75761
|
poster,
|
|
@@ -75722,28 +75839,36 @@ ${describeRewrites(prepared.rewrites)}
|
|
|
75722
75839
|
...opts.autoFixKind === "relayout" && opts.relayEdits ? { relayAsked: Object.fromEntries(Object.entries(opts.relayEdits).map(([id, asked]) => [id, [asked]])) } : {}
|
|
75723
75840
|
} : step.kind === "regenerate" ? { regenerate: step.ids.join(","), relayNote: step.note, ...step.extend ? { extendSizes: step.extend } : {} } : {}
|
|
75724
75841
|
};
|
|
75725
|
-
} else
|
|
75726
|
-
|
|
75727
|
-
|
|
75728
|
-
|
|
75729
|
-
|
|
75730
|
-
|
|
75731
|
-
|
|
75732
|
-
|
|
75733
|
-
|
|
75734
|
-
|
|
75735
|
-
|
|
75736
|
-
|
|
75737
|
-
|
|
75738
|
-
|
|
75739
|
-
|
|
75740
|
-
|
|
75741
|
-
|
|
75742
|
-
|
|
75743
|
-
|
|
75744
|
-
|
|
75842
|
+
} else {
|
|
75843
|
+
if (creativeSlug && turnId && staticReview) {
|
|
75844
|
+
await writeTurnRenders(
|
|
75845
|
+
turnCache,
|
|
75846
|
+
recordTurnRender(await readTurnRenders(turnCache), turnId, creativeSlug, staticReview.verdict)
|
|
75847
|
+
);
|
|
75848
|
+
}
|
|
75849
|
+
process.stdout.write(
|
|
75850
|
+
`${JSON.stringify(
|
|
75851
|
+
{
|
|
75852
|
+
ok: true,
|
|
75853
|
+
outputs_dir: result.outputs_dir,
|
|
75854
|
+
output: result.output,
|
|
75855
|
+
stats: result.stats,
|
|
75856
|
+
...findings ? { review: findings } : {},
|
|
75857
|
+
...staticReview?.judge?.length ? { judge: staticReview.judge } : {},
|
|
75858
|
+
...definitionStatus === "updated" ? { definition_status: "rendered" } : {},
|
|
75859
|
+
...hints2.length ? { hints: hints2 } : {},
|
|
75860
|
+
// Last, so a `| tail` keeps them: production agents cut them off and re-ran to get them back.
|
|
75861
|
+
// A static ad whose review found a blocking problem is finished but NOT ready.
|
|
75862
|
+
...staticReview ? { verdict: staticReview.verdict } : {},
|
|
75863
|
+
run_id: result.run_id,
|
|
75864
|
+
files: finalFiles(result.output, finalLabels?.labels)
|
|
75865
|
+
},
|
|
75866
|
+
null,
|
|
75867
|
+
2
|
|
75868
|
+
)}
|
|
75745
75869
|
`
|
|
75746
|
-
|
|
75870
|
+
);
|
|
75871
|
+
}
|
|
75747
75872
|
} catch (e) {
|
|
75748
75873
|
const failedPayload = async (message) => {
|
|
75749
75874
|
await readerSpend.drained();
|
|
@@ -75870,14 +75995,14 @@ var rerunCommand = defineCommand140({
|
|
|
75870
75995
|
});
|
|
75871
75996
|
|
|
75872
75997
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
75873
|
-
import { mkdir as
|
|
75874
|
-
import
|
|
75998
|
+
import { mkdir as mkdir12, readFile as readFile29, writeFile as writeFile18 } from "fs/promises";
|
|
75999
|
+
import path36 from "path";
|
|
75875
76000
|
import { defineCommand as defineCommand143 } from "citty";
|
|
75876
76001
|
|
|
75877
76002
|
// src/commands/canvas/creative-limit.ts
|
|
75878
76003
|
import { existsSync as existsSync7 } from "fs";
|
|
75879
|
-
import { mkdir as
|
|
75880
|
-
import
|
|
76004
|
+
import { mkdir as mkdir10, readdir as readdir13, writeFile as writeFile16 } from "fs/promises";
|
|
76005
|
+
import path31 from "path";
|
|
75881
76006
|
var NEW_CREATIVES_PER_CHAT = 10;
|
|
75882
76007
|
function claimNewCreative(made, slug) {
|
|
75883
76008
|
if (made.includes(slug)) return { allowed: true, count: made.length };
|
|
@@ -75885,7 +76010,7 @@ function claimNewCreative(made, slug) {
|
|
|
75885
76010
|
return { allowed: true, count: made.length + 1 };
|
|
75886
76011
|
}
|
|
75887
76012
|
function chatDir(canvasRoot, chatId) {
|
|
75888
|
-
return
|
|
76013
|
+
return path31.join(canvasRoot, ".creative-limit", (chatId ?? "no-chat").replace(/[^A-Za-z0-9_-]/g, "_"));
|
|
75889
76014
|
}
|
|
75890
76015
|
async function madeInChat(dir) {
|
|
75891
76016
|
return readdir13(dir).catch(() => []);
|
|
@@ -75897,8 +76022,8 @@ async function claimNewCreativeInWorkspace(canvasRoot, slug, chatId, definitionP
|
|
|
75897
76022
|
const claim = claimNewCreative(made, slug);
|
|
75898
76023
|
if (claim.allowed && !made.includes(slug)) {
|
|
75899
76024
|
try {
|
|
75900
|
-
await
|
|
75901
|
-
await
|
|
76025
|
+
await mkdir10(dir, { recursive: true });
|
|
76026
|
+
await writeFile16(path31.join(dir, slug), "", { flag: "wx" });
|
|
75902
76027
|
} catch {
|
|
75903
76028
|
}
|
|
75904
76029
|
}
|
|
@@ -75919,7 +76044,7 @@ async function enforceNewCreativeLimit(slug, canvasPath) {
|
|
|
75919
76044
|
canvasOutputsRoot(canvasPath),
|
|
75920
76045
|
slug,
|
|
75921
76046
|
optionalChatId() ?? null,
|
|
75922
|
-
|
|
76047
|
+
path31.join(path31.dirname(path31.resolve(canvasPath)), "_definition.md")
|
|
75923
76048
|
);
|
|
75924
76049
|
if (claim.allowed) return;
|
|
75925
76050
|
process.stderr.write(`${JSON.stringify(creativeLimitRefusal(slug), null, 2)}
|
|
@@ -75928,22 +76053,22 @@ async function enforceNewCreativeLimit(slug, canvasPath) {
|
|
|
75928
76053
|
}
|
|
75929
76054
|
|
|
75930
76055
|
// src/commands/canvas/scaffold-common.ts
|
|
75931
|
-
import { access, readFile as
|
|
76056
|
+
import { access, readFile as readFile26 } from "fs/promises";
|
|
75932
76057
|
|
|
75933
76058
|
// src/commands/canvas/composition-path.ts
|
|
75934
76059
|
import { existsSync as existsSync8 } from "fs";
|
|
75935
|
-
import
|
|
76060
|
+
import path32 from "path";
|
|
75936
76061
|
function resolveShippedCanvasDir(name, startDir, exists3 = existsSync8, maxDepth = 8) {
|
|
75937
|
-
const rel =
|
|
76062
|
+
const rel = path32.join("canvas", name);
|
|
75938
76063
|
let dir = startDir;
|
|
75939
76064
|
for (let i = 0; i < maxDepth; i++) {
|
|
75940
|
-
const candidate =
|
|
75941
|
-
if (exists3(
|
|
75942
|
-
const parent =
|
|
76065
|
+
const candidate = path32.join(dir, rel);
|
|
76066
|
+
if (exists3(path32.join(candidate, "meta.json"))) return candidate;
|
|
76067
|
+
const parent = path32.dirname(dir);
|
|
75943
76068
|
if (parent === dir) break;
|
|
75944
76069
|
dir = parent;
|
|
75945
76070
|
}
|
|
75946
|
-
return
|
|
76071
|
+
return path32.resolve(startDir, "../../../", rel);
|
|
75947
76072
|
}
|
|
75948
76073
|
|
|
75949
76074
|
// src/commands/canvas/scaffold-common.ts
|
|
@@ -75960,7 +76085,7 @@ function resolveModel(kind, requested, fallback) {
|
|
|
75960
76085
|
}
|
|
75961
76086
|
async function loadAssetText(ref2, label) {
|
|
75962
76087
|
const r = ref2;
|
|
75963
|
-
if (typeof r?.path === "string") return
|
|
76088
|
+
if (typeof r?.path === "string") return readFile26(r.path, "utf8");
|
|
75964
76089
|
if (typeof r?.url === "string") {
|
|
75965
76090
|
const res = await fetch(r.url);
|
|
75966
76091
|
if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
|
|
@@ -76108,16 +76233,16 @@ ${doc.toString()}---${raw.slice(match[0].length)}`;
|
|
|
76108
76233
|
}
|
|
76109
76234
|
|
|
76110
76235
|
// src/commands/canvas/scaffold-static-ad-paths.ts
|
|
76111
|
-
import
|
|
76236
|
+
import path33 from "path";
|
|
76112
76237
|
function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd(), slug) {
|
|
76113
76238
|
const file = rawFile.trim();
|
|
76114
76239
|
const imageIsUrl = /^https?:\/\//i.test(file);
|
|
76115
|
-
const imageSource = imageIsUrl ? file :
|
|
76116
|
-
const outPath = out ?
|
|
76117
|
-
const blueprintPath2 =
|
|
76118
|
-
const creativeDir = slug ?
|
|
76119
|
-
const definitionPath = creativeDir ?
|
|
76120
|
-
const referencesDir = creativeDir ?
|
|
76240
|
+
const imageSource = imageIsUrl ? file : path33.resolve(cwd, file);
|
|
76241
|
+
const outPath = out ? path33.resolve(cwd, out) : slug ? path33.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path33.join(cwd, "static-ad.canvas.json") : path33.join(path33.dirname(imageSource), "static-ad.canvas.json");
|
|
76242
|
+
const blueprintPath2 = path33.join(path33.dirname(outPath), "prompt.json");
|
|
76243
|
+
const creativeDir = slug ? path33.dirname(outPath) : null;
|
|
76244
|
+
const definitionPath = creativeDir ? path33.join(creativeDir, "_definition.md") : null;
|
|
76245
|
+
const referencesDir = creativeDir ? path33.join(creativeDir, "references") : null;
|
|
76121
76246
|
return { imageIsUrl, imageSource, outPath, blueprintPath: blueprintPath2, creativeDir, definitionPath, referencesDir };
|
|
76122
76247
|
}
|
|
76123
76248
|
var SCAFFOLD_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
@@ -76127,8 +76252,8 @@ function isValidScaffoldSlug(slug) {
|
|
|
76127
76252
|
}
|
|
76128
76253
|
|
|
76129
76254
|
// src/commands/canvas/sync-definition.ts
|
|
76130
|
-
import { readdir as readdir14, readFile as
|
|
76131
|
-
import
|
|
76255
|
+
import { readdir as readdir14, readFile as readFile27, stat as stat5 } from "fs/promises";
|
|
76256
|
+
import path34 from "path";
|
|
76132
76257
|
import { defineCommand as defineCommand141 } from "citty";
|
|
76133
76258
|
|
|
76134
76259
|
// src/commands/canvas/definition-graph.ts
|
|
@@ -76185,14 +76310,14 @@ function buildDefinitionSyncPayload(input, graph, chatId) {
|
|
|
76185
76310
|
}
|
|
76186
76311
|
async function readDefinitionSourceFields(canvasPath) {
|
|
76187
76312
|
try {
|
|
76188
|
-
return parseDefinitionSourceFields(await
|
|
76313
|
+
return parseDefinitionSourceFields(await readFile27(path34.join(path34.dirname(canvasPath), "_definition.md"), "utf8"));
|
|
76189
76314
|
} catch {
|
|
76190
76315
|
return {};
|
|
76191
76316
|
}
|
|
76192
76317
|
}
|
|
76193
76318
|
async function readDefinitionStrategyFields(canvasPath) {
|
|
76194
76319
|
try {
|
|
76195
|
-
return parseDefinitionStrategyFields(await
|
|
76320
|
+
return parseDefinitionStrategyFields(await readFile27(path34.join(path34.dirname(canvasPath), "_definition.md"), "utf8"));
|
|
76196
76321
|
} catch {
|
|
76197
76322
|
return {};
|
|
76198
76323
|
}
|
|
@@ -76215,15 +76340,15 @@ async function syncCreativeDefinitionBestEffort(input) {
|
|
|
76215
76340
|
}
|
|
76216
76341
|
}
|
|
76217
76342
|
async function resolveCanvasPath(inputPath) {
|
|
76218
|
-
const resolved =
|
|
76343
|
+
const resolved = path34.resolve(inputPath);
|
|
76219
76344
|
let dir = resolved;
|
|
76220
76345
|
try {
|
|
76221
76346
|
if ((await stat5(resolved)).isFile()) {
|
|
76222
76347
|
if (resolved.endsWith(".canvas.json")) return resolved;
|
|
76223
|
-
dir =
|
|
76348
|
+
dir = path34.dirname(resolved);
|
|
76224
76349
|
}
|
|
76225
76350
|
} catch {
|
|
76226
|
-
dir = resolved.endsWith(".canvas.json") ?
|
|
76351
|
+
dir = resolved.endsWith(".canvas.json") ? path34.dirname(resolved) : resolved;
|
|
76227
76352
|
}
|
|
76228
76353
|
let entries;
|
|
76229
76354
|
try {
|
|
@@ -76234,7 +76359,7 @@ async function resolveCanvasPath(inputPath) {
|
|
|
76234
76359
|
const canvases = entries.filter((name) => name.endsWith(".canvas.json"));
|
|
76235
76360
|
const slug = creativeSlugFromCanvasPath(`${dir}/x/`);
|
|
76236
76361
|
const chosen = (slug ? canvases.find((name) => name === `${slug}.canvas.json`) : void 0) ?? canvases[0];
|
|
76237
|
-
return chosen ?
|
|
76362
|
+
return chosen ? path34.join(dir, chosen) : null;
|
|
76238
76363
|
}
|
|
76239
76364
|
var syncDefinitionCommand = defineCommand141({
|
|
76240
76365
|
meta: {
|
|
@@ -76504,32 +76629,32 @@ function mergeReferenceIntoBlueprint(blueprint, reference) {
|
|
|
76504
76629
|
}
|
|
76505
76630
|
|
|
76506
76631
|
// src/commands/winning-ads/fit.ts
|
|
76507
|
-
import { mkdir as
|
|
76508
|
-
import
|
|
76632
|
+
import { mkdir as mkdir11, readdir as readdir15, readFile as readFile28, writeFile as writeFile17 } from "fs/promises";
|
|
76633
|
+
import path35 from "path";
|
|
76509
76634
|
import { defineCommand as defineCommand142 } from "citty";
|
|
76510
76635
|
import sharp4 from "sharp";
|
|
76511
|
-
import { z as
|
|
76512
|
-
var Layers =
|
|
76513
|
-
disguise:
|
|
76514
|
-
voice:
|
|
76515
|
-
trigger:
|
|
76516
|
-
attention:
|
|
76517
|
-
logic:
|
|
76636
|
+
import { z as z68 } from "zod";
|
|
76637
|
+
var Layers = z68.object({
|
|
76638
|
+
disguise: z68.string().optional(),
|
|
76639
|
+
voice: z68.string().optional(),
|
|
76640
|
+
trigger: z68.string().optional(),
|
|
76641
|
+
attention: z68.string().optional(),
|
|
76642
|
+
logic: z68.string().optional()
|
|
76518
76643
|
}).strip();
|
|
76519
|
-
var FitAnswerSchema =
|
|
76644
|
+
var FitAnswerSchema = z68.object({
|
|
76520
76645
|
why_it_wins: Layers.default({}),
|
|
76521
|
-
load_bearing:
|
|
76522
|
-
|
|
76523
|
-
element:
|
|
76524
|
-
needs:
|
|
76525
|
-
have:
|
|
76526
|
-
ours:
|
|
76646
|
+
load_bearing: z68.array(
|
|
76647
|
+
z68.object({
|
|
76648
|
+
element: z68.string().trim().min(1),
|
|
76649
|
+
needs: z68.string().optional(),
|
|
76650
|
+
have: z68.enum(["have", "adapt", "missing"]),
|
|
76651
|
+
ours: z68.string().optional()
|
|
76527
76652
|
}).strip()
|
|
76528
76653
|
).default([]),
|
|
76529
|
-
verdict:
|
|
76530
|
-
transfer_note:
|
|
76531
|
-
brand_presence:
|
|
76532
|
-
recognition:
|
|
76654
|
+
verdict: z68.enum(["use", "adapt", "reject"]),
|
|
76655
|
+
transfer_note: z68.string().optional(),
|
|
76656
|
+
brand_presence: z68.enum(["native", "standard"]).optional(),
|
|
76657
|
+
recognition: z68.string().optional()
|
|
76533
76658
|
});
|
|
76534
76659
|
function buildFitPrompt(input) {
|
|
76535
76660
|
const lib = input.library;
|
|
@@ -76590,33 +76715,33 @@ var MAX_DOC_CHARS = 1500;
|
|
|
76590
76715
|
var MAX_ENTRY_CHARS = 400;
|
|
76591
76716
|
var KNOWLEDGE_COLLECTIONS = ["offers", "products", "personas"];
|
|
76592
76717
|
function readText(file) {
|
|
76593
|
-
return
|
|
76718
|
+
return readFile28(file, "utf8").catch(() => null);
|
|
76594
76719
|
}
|
|
76595
76720
|
async function readKnowledge(root) {
|
|
76596
76721
|
const parts = [];
|
|
76597
|
-
const company = await readText(
|
|
76722
|
+
const company = await readText(path35.join(root, "COMPANY.md"));
|
|
76598
76723
|
if (company) parts.push(`COMPANY.md:
|
|
76599
76724
|
${company.slice(0, MAX_DOC_CHARS)}`);
|
|
76600
|
-
const brand = await readText(
|
|
76725
|
+
const brand = await readText(path35.join(root, "src", "brand", "BRAND.md"));
|
|
76601
76726
|
if (brand) parts.push(`BRAND.md (start):
|
|
76602
76727
|
${brand.slice(0, MAX_DOC_CHARS)}`);
|
|
76603
76728
|
for (const collection of KNOWLEDGE_COLLECTIONS) {
|
|
76604
|
-
const dir =
|
|
76729
|
+
const dir = path35.join(root, "src", "content", collection);
|
|
76605
76730
|
const files = (await readdir15(dir, { recursive: true }).catch(() => [])).filter(
|
|
76606
76731
|
(f) => f.endsWith(".md")
|
|
76607
76732
|
);
|
|
76608
76733
|
for (const file of files.sort().slice(0, 12)) {
|
|
76609
|
-
const text3 = await readText(
|
|
76734
|
+
const text3 = await readText(path35.join(dir, file));
|
|
76610
76735
|
if (text3) parts.push(`${collection}/${file}:
|
|
76611
76736
|
${text3.slice(0, MAX_ENTRY_CHARS)}`);
|
|
76612
76737
|
}
|
|
76613
76738
|
}
|
|
76614
76739
|
return parts.join("\n\n");
|
|
76615
76740
|
}
|
|
76616
|
-
var ReviewSchema =
|
|
76617
|
-
reviewText:
|
|
76618
|
-
sourceType:
|
|
76619
|
-
rating:
|
|
76741
|
+
var ReviewSchema = z68.object({
|
|
76742
|
+
reviewText: z68.string(),
|
|
76743
|
+
sourceType: z68.string().optional(),
|
|
76744
|
+
rating: z68.number().optional()
|
|
76620
76745
|
});
|
|
76621
76746
|
function reviewsForFit(data) {
|
|
76622
76747
|
const list = Array.isArray(data) ? data : [];
|
|
@@ -76638,7 +76763,7 @@ async function readReviews() {
|
|
|
76638
76763
|
}
|
|
76639
76764
|
}
|
|
76640
76765
|
function fitCachePath(root, platform, adId) {
|
|
76641
|
-
return
|
|
76766
|
+
return path35.join(root, "canvas", ".cache", "fit", `${platform}-${adId.replace(/[^a-zA-Z0-9_-]/g, "_")}.json`);
|
|
76642
76767
|
}
|
|
76643
76768
|
async function readCachedFit(root, platform, adId) {
|
|
76644
76769
|
const raw = await readText(fitCachePath(root, platform, adId));
|
|
@@ -76693,8 +76818,8 @@ async function fitOnce(root, platform, adId, ask) {
|
|
|
76693
76818
|
const fit = await ask();
|
|
76694
76819
|
if (!fit) return null;
|
|
76695
76820
|
const file = fitCachePath(root, platform, adId);
|
|
76696
|
-
await
|
|
76697
|
-
await
|
|
76821
|
+
await mkdir11(path35.dirname(file), { recursive: true });
|
|
76822
|
+
await writeFile17(file, `${JSON.stringify(fit, null, 2)}
|
|
76698
76823
|
`, "utf8");
|
|
76699
76824
|
return fit;
|
|
76700
76825
|
}
|
|
@@ -76732,7 +76857,7 @@ async function fitReference(root, platform, adId) {
|
|
|
76732
76857
|
const prompt = buildFitPrompt({ library: libraryRead(ad), knowledge: knowledge2 });
|
|
76733
76858
|
const spent = [];
|
|
76734
76859
|
const answer = await askFit(jpeg, prompt, (u) => spent.push(u));
|
|
76735
|
-
await holdUnbilledSpend(
|
|
76860
|
+
await holdUnbilledSpend(path35.join(root, "canvas", ".cache"), spent);
|
|
76736
76861
|
return answer;
|
|
76737
76862
|
});
|
|
76738
76863
|
if (fit) return { ok: true, fit };
|
|
@@ -76785,7 +76910,7 @@ var fitCommand = defineCommand142({
|
|
|
76785
76910
|
|
|
76786
76911
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
76787
76912
|
function toCanvasRelative2(canvasDir, file) {
|
|
76788
|
-
const rel =
|
|
76913
|
+
const rel = path36.relative(canvasDir, file).split(path36.sep).join("/");
|
|
76789
76914
|
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
76790
76915
|
}
|
|
76791
76916
|
async function uploadSourceAsReference(source, isUrl, client) {
|
|
@@ -76797,7 +76922,7 @@ async function uploadSourceAsReference(source, isUrl, client) {
|
|
|
76797
76922
|
throw new Error(`failed to download source image (${e instanceof Error ? e.message : String(e)})`);
|
|
76798
76923
|
}
|
|
76799
76924
|
} else {
|
|
76800
|
-
bytes = await
|
|
76925
|
+
bytes = await readFile29(source);
|
|
76801
76926
|
}
|
|
76802
76927
|
const safe = await toModelSafeImage(bytes);
|
|
76803
76928
|
const sha2562 = sha256Hex(safe.bytes);
|
|
@@ -77077,25 +77202,25 @@ async function lookupReference(args8) {
|
|
|
77077
77202
|
}
|
|
77078
77203
|
async function scaffoldFromSpec(args8, slug) {
|
|
77079
77204
|
if (!slug) return fail4("missing_slug", "--spec needs --slug: an ad started from a spec is a creative.");
|
|
77080
|
-
const specPath =
|
|
77205
|
+
const specPath = path36.resolve(process.cwd(), String(args8.spec));
|
|
77081
77206
|
let started;
|
|
77082
77207
|
try {
|
|
77083
|
-
started = readAdSpec(JSON.parse(await
|
|
77208
|
+
started = readAdSpec(JSON.parse(await readFile29(specPath, "utf8")));
|
|
77084
77209
|
} catch (e) {
|
|
77085
77210
|
return fail4("invalid_spec", `${specPath} is not an ad spec: ${e instanceof Error ? e.message : String(e)}`);
|
|
77086
77211
|
}
|
|
77087
|
-
const outPath =
|
|
77088
|
-
const dir =
|
|
77089
|
-
const blueprintPath2 =
|
|
77090
|
-
const definitionPath =
|
|
77212
|
+
const outPath = path36.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`);
|
|
77213
|
+
const dir = path36.dirname(outPath);
|
|
77214
|
+
const blueprintPath2 = path36.join(dir, "prompt.json");
|
|
77215
|
+
const definitionPath = path36.join(dir, "_definition.md");
|
|
77091
77216
|
await enforceNewCreativeLimit(slug, outPath);
|
|
77092
|
-
const existingDefinition = await fileExists(definitionPath) ? await
|
|
77217
|
+
const existingDefinition = await fileExists(definitionPath) ? await readFile29(definitionPath, "utf8") : null;
|
|
77093
77218
|
const who = await scaffoldViewer(args8, existingDefinition, liveViewerLookups(process.cwd()).adSetViewer);
|
|
77094
77219
|
if (!who.ok) return fail4("invalid_viewer", who.message);
|
|
77095
|
-
await
|
|
77220
|
+
await mkdir12(dir, { recursive: true });
|
|
77096
77221
|
const workspaceRoot = await findBrandRoot(dir) ?? process.cwd();
|
|
77097
77222
|
const kit = await readStaticBrandKit(workspaceRoot);
|
|
77098
|
-
const market = who.viewer?.market ?? marketFromCompanyDoc(await
|
|
77223
|
+
const market = who.viewer?.market ?? marketFromCompanyDoc(await readFile29(path36.join(workspaceRoot, "COMPANY.md"), "utf8").catch(() => null));
|
|
77099
77224
|
const brand = brandBlock(kit);
|
|
77100
77225
|
const platform = resolvePlatform(args8.platform ? String(args8.platform) : void 0);
|
|
77101
77226
|
const placementsResult = resolvePlacements(args8.placements ? String(args8.placements) : void 0, platform);
|
|
@@ -77117,18 +77242,18 @@ async function scaffoldFromSpec(args8, slug) {
|
|
|
77117
77242
|
logo: Boolean(kit.logo),
|
|
77118
77243
|
...market ? { viewer: { ...started.viewer ?? { who: "the people this ad is for" }, market } } : {}
|
|
77119
77244
|
};
|
|
77120
|
-
await
|
|
77245
|
+
await writeFile18(blueprintPath2, `${JSON.stringify(spec, null, 2)}
|
|
77121
77246
|
`, "utf8");
|
|
77122
77247
|
const canvas = scaffoldStaticAd({}, [], { ...opts, spec });
|
|
77123
77248
|
const validation = await validateScaffoldedStaticAd(canvas, dir);
|
|
77124
77249
|
if (validation.blocking.length > 0) return fail4("validation", validation.blocking.map((i) => i.message).join("; "));
|
|
77125
|
-
await
|
|
77250
|
+
await writeFile18(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
77126
77251
|
`, "utf8");
|
|
77127
77252
|
const title = args8.title ? String(args8.title) : titleFromSlug(slug);
|
|
77128
77253
|
const recorded = who.record ? { targetAdSet: who.targetAdSet, viewer: who.viewer } : {};
|
|
77129
77254
|
if (existingDefinition === null) {
|
|
77130
77255
|
const formats = resolveFormatList(report.output_formats);
|
|
77131
|
-
await
|
|
77256
|
+
await writeFile18(definitionPath, buildCreativeDefinition({ title, kind: "static", platform, formats, ...recorded }), "utf8");
|
|
77132
77257
|
}
|
|
77133
77258
|
await syncCreativeDefinitionBestEffort({
|
|
77134
77259
|
slug,
|
|
@@ -77242,14 +77367,14 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77242
77367
|
}
|
|
77243
77368
|
const { imageIsUrl, imageSource, outPath, blueprintPath: blueprintPath2, definitionPath, referencesDir } = resolveScaffoldStaticAdPaths(sourceFile, args8.out ? String(args8.out) : void 0, process.cwd(), slug);
|
|
77244
77369
|
if (slug) await enforceNewCreativeLimit(slug, outPath);
|
|
77245
|
-
const existingDefinition = definitionPath && await fileExists(definitionPath) ? await
|
|
77370
|
+
const existingDefinition = definitionPath && await fileExists(definitionPath) ? await readFile29(definitionPath, "utf8") : null;
|
|
77246
77371
|
const who = await scaffoldViewer(args8, existingDefinition, liveViewerLookups(process.cwd()).adSetViewer);
|
|
77247
77372
|
if (!who.ok) return fail4("invalid_viewer", who.message);
|
|
77248
77373
|
const fitRoot = findWorkspaceRoot(process.cwd()) ?? process.cwd();
|
|
77249
77374
|
const fit = reference || lookup.status === "unavailable" ? await readCachedFit(fitRoot, reference?.platform ?? (lookup.status === "unavailable" ? lookup.platform : "meta"), reference?.ad_id ?? (lookup.status === "unavailable" ? lookup.adId : "")) : null;
|
|
77250
77375
|
const fitProblems = fit ? fitHints(fit, reference?.ad_id ?? "").filter((h) => h.startsWith("Pick another reference")) : [];
|
|
77251
77376
|
if (fitProblems.length > 0) return fail4("concept_does_not_fit", fitProblems.join(" "));
|
|
77252
|
-
await
|
|
77377
|
+
await mkdir12(path36.dirname(outPath), { recursive: true });
|
|
77253
77378
|
let models;
|
|
77254
77379
|
try {
|
|
77255
77380
|
models = resolveModels(args8);
|
|
@@ -77283,10 +77408,10 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77283
77408
|
const persona = existingDefinition?.match(/^persona:\s*"?([^"\n]+)"?\s*$/m)?.[1];
|
|
77284
77409
|
const elements = castAvatarsOnStaticElements(vision.elements, cast.avatars, persona);
|
|
77285
77410
|
const uncast = staticAvatarsNotCast(elements, cast.handles);
|
|
77286
|
-
const canvasDir =
|
|
77411
|
+
const canvasDir = path36.dirname(outPath);
|
|
77287
77412
|
const workspaceRoot = await findBrandRoot(canvasDir) ?? process.cwd();
|
|
77288
77413
|
const kit = await readStaticBrandKit(workspaceRoot);
|
|
77289
|
-
const market = who.viewer?.market ?? marketFromCompanyDoc(await
|
|
77414
|
+
const market = who.viewer?.market ?? marketFromCompanyDoc(await readFile29(path36.join(workspaceRoot, "COMPANY.md"), "utf8").catch(() => null));
|
|
77290
77415
|
const brand = brandBlock(kit);
|
|
77291
77416
|
let canvasImagePath = imageSource;
|
|
77292
77417
|
let canvasImageIsUrl = imageIsUrl;
|
|
@@ -77336,7 +77461,7 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77336
77461
|
...fit ? { fit: conceptFromFit(fit, reference?.ad_id ?? "") } : {},
|
|
77337
77462
|
...market ? { market } : {}
|
|
77338
77463
|
});
|
|
77339
|
-
await
|
|
77464
|
+
await writeFile18(blueprintPath2, `${JSON.stringify(spec, null, 2)}
|
|
77340
77465
|
`, "utf8");
|
|
77341
77466
|
conceptNotes = conceptProblems(spec).map(
|
|
77342
77467
|
(p) => `CONCEPT: ${p} Then \`baker canvas run\`; until then it refuses to render.`
|
|
@@ -77350,7 +77475,7 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77350
77475
|
} catch (e) {
|
|
77351
77476
|
return fail4("scaffold", e instanceof Error ? e.message : String(e));
|
|
77352
77477
|
}
|
|
77353
|
-
const validation = await validateScaffoldedStaticAd(canvas,
|
|
77478
|
+
const validation = await validateScaffoldedStaticAd(canvas, path36.dirname(outPath));
|
|
77354
77479
|
if (validation.blocking.length > 0) {
|
|
77355
77480
|
process.stderr.write(
|
|
77356
77481
|
`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.blocking } }, null, 2)}
|
|
@@ -77358,7 +77483,7 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77358
77483
|
);
|
|
77359
77484
|
process.exit(2);
|
|
77360
77485
|
}
|
|
77361
|
-
await
|
|
77486
|
+
await writeFile18(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
77362
77487
|
`, "utf8");
|
|
77363
77488
|
const sourceFields = reference ? {
|
|
77364
77489
|
sourceAdId: reference.ad_id,
|
|
@@ -77371,10 +77496,10 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77371
77496
|
} : {};
|
|
77372
77497
|
const recorded = who.record ? { targetAdSet: who.targetAdSet, viewer: who.viewer } : {};
|
|
77373
77498
|
if (definitionPath && existingDefinition !== null && who.record) {
|
|
77374
|
-
await
|
|
77499
|
+
await writeFile18(definitionPath, setDefinitionViewer(existingDefinition, recorded), "utf8");
|
|
77375
77500
|
}
|
|
77376
77501
|
if (definitionPath && existingDefinition === null) {
|
|
77377
|
-
await
|
|
77502
|
+
await writeFile18(
|
|
77378
77503
|
definitionPath,
|
|
77379
77504
|
buildCreativeDefinition({
|
|
77380
77505
|
title: args8.title ? String(args8.title) : titleFromSlug(slug ?? ""),
|
|
@@ -77447,7 +77572,7 @@ var scaffoldStaticAdCommand = defineCommand143({
|
|
|
77447
77572
|
run_estimated_credits: validation.estimatedCredits
|
|
77448
77573
|
},
|
|
77449
77574
|
checklist: {
|
|
77450
|
-
edit_prompt: `Edit ${
|
|
77575
|
+
edit_prompt: `Edit ${path36.basename(blueprintPath2)}: it is the ad spec, read from your image, to rewrite into the ad you want. \`viewer\` (who sees it and what they already know), \`message\` (the one thing it tells them), \`copy\` (each line, \`in_scene\` or \`designed\`), \`scene\`, \`references\` (one picture each, by role), \`colors\` (each tied to an object, from the brand) and an optional \`direction\`. You never write a model prompt: every render prompt is compiled from this file and shown on its node.${reference ? " Its `reference` block is the library's read of the source ad (container, layout, mechanism, offer, text blocks): keep it as the account of why the ad won, and rebuild it for our brand." : ""}`,
|
|
77451
77576
|
mechanism: report.winning_mechanisms,
|
|
77452
77577
|
rebuild_the_mechanism: "`mechanism` is WHY the source ad works. Rebuild each device for our brand in prompt.json's copy and scene (a rhyme stays a rhyme in our words, a before/after stays a before/after of our product); never copy the source's literal copy.",
|
|
77453
77578
|
assets_to_supply: report.elements,
|
|
@@ -77509,25 +77634,25 @@ function clientLogoMissNote(decision, domain) {
|
|
|
77509
77634
|
}
|
|
77510
77635
|
|
|
77511
77636
|
// src/commands/canvas/scaffold-ad.ts
|
|
77512
|
-
import { copyFile, cp, mkdir as
|
|
77513
|
-
import
|
|
77637
|
+
import { copyFile, cp, mkdir as mkdir13, readFile as readFile31, stat as stat6, writeFile as writeFile20 } from "fs/promises";
|
|
77638
|
+
import path38 from "path";
|
|
77514
77639
|
import { defineCommand as defineCommand144 } from "citty";
|
|
77515
77640
|
|
|
77516
77641
|
// src/engine/scaffold/ad-spec.ts
|
|
77517
|
-
import { z as
|
|
77642
|
+
import { z as z69 } from "zod";
|
|
77518
77643
|
var oneClause = (line) => (line.match(/[.!?](\s|$)/g) ?? []).length <= 1;
|
|
77519
77644
|
var isSpeech = (line) => new RegExp("\\p{L}\\p{L}", "u").test(line);
|
|
77520
|
-
var Beat =
|
|
77645
|
+
var Beat = z69.object({
|
|
77521
77646
|
/**
|
|
77522
77647
|
* The line the voice says in this beat — ONE clause, ending in its own
|
|
77523
77648
|
* punctuation. It becomes the caption card verbatim.
|
|
77524
77649
|
*/
|
|
77525
|
-
say:
|
|
77650
|
+
say: z69.string().min(1).refine(
|
|
77526
77651
|
isSpeech,
|
|
77527
77652
|
"a beat's line has to be words someone says \u2014 a dot or a dash is not a line, and an ad with no voiceover is not supported yet"
|
|
77528
77653
|
).refine(oneClause, "a beat is one clause: split a second sentence into its own beat"),
|
|
77529
77654
|
/** What is on screen while it is said, as a shot brief for the video model. */
|
|
77530
|
-
show:
|
|
77655
|
+
show: z69.string().min(1),
|
|
77531
77656
|
/**
|
|
77532
77657
|
* What this shot SOUNDS like — one effect, in words. Usually absent.
|
|
77533
77658
|
*
|
|
@@ -77544,7 +77669,7 @@ var Beat = z68.object({
|
|
|
77544
77669
|
* Describe the SOUND, not the picture: "a cordless drill tightening a bolt", not "the
|
|
77545
77670
|
* fitter works on the rail".
|
|
77546
77671
|
*/
|
|
77547
|
-
sound:
|
|
77672
|
+
sound: z69.string().min(1).max(200).optional(),
|
|
77548
77673
|
/**
|
|
77549
77674
|
* Whether this beat's subject is talking to camera.
|
|
77550
77675
|
*
|
|
@@ -77558,36 +77683,36 @@ var Beat = z68.object({
|
|
|
77558
77683
|
* way through" produced an ad with her face on screen and a narrator cast from a prose
|
|
77559
77684
|
* description reading her lines, because nothing set this flag.
|
|
77560
77685
|
*/
|
|
77561
|
-
on_camera:
|
|
77686
|
+
on_camera: z69.boolean().optional(),
|
|
77562
77687
|
/**
|
|
77563
77688
|
* Whether the ad's recurring person is in this shot. Defaults to true whenever the
|
|
77564
77689
|
* spec declares a `cast`, because an ad is usually about one person and defaulting
|
|
77565
77690
|
* the other way is how continuity gets forgotten. Set false for a beat they are not
|
|
77566
77691
|
* in — a fitter on a roof, a product close-up.
|
|
77567
77692
|
*/
|
|
77568
|
-
cast:
|
|
77693
|
+
cast: z69.boolean().optional()
|
|
77569
77694
|
});
|
|
77570
|
-
var AdSpec =
|
|
77571
|
-
format:
|
|
77572
|
-
aspect_ratio:
|
|
77573
|
-
resolution:
|
|
77695
|
+
var AdSpec = z69.object({
|
|
77696
|
+
format: z69.object({
|
|
77697
|
+
aspect_ratio: z69.string().default("9:16"),
|
|
77698
|
+
resolution: z69.string().default("720p")
|
|
77574
77699
|
}).default({ aspect_ratio: "9:16", resolution: "720p" }),
|
|
77575
|
-
brand:
|
|
77576
|
-
name:
|
|
77700
|
+
brand: z69.object({
|
|
77701
|
+
name: z69.string().optional(),
|
|
77577
77702
|
/** Figures to lift typographically — the one gold number every ad has. */
|
|
77578
|
-
highlight:
|
|
77703
|
+
highlight: z69.array(z69.string()).optional(),
|
|
77579
77704
|
/**
|
|
77580
77705
|
* Brand hex colours, most important first. They dress the brand plate and
|
|
77581
77706
|
* the type — never the scenes: pushing hex into a shot brief makes the
|
|
77582
77707
|
* model paint the whole frame that colour, which reads as a filter.
|
|
77583
77708
|
*/
|
|
77584
|
-
palette:
|
|
77709
|
+
palette: z69.array(z69.string()).optional(),
|
|
77585
77710
|
/** Repo path to the brand mark, so the CTA carries it instead of a name in a font. */
|
|
77586
|
-
logo:
|
|
77711
|
+
logo: z69.string().optional()
|
|
77587
77712
|
}).optional(),
|
|
77588
|
-
voice:
|
|
77589
|
-
description:
|
|
77590
|
-
language:
|
|
77713
|
+
voice: z69.object({
|
|
77714
|
+
description: z69.string().min(1),
|
|
77715
|
+
language: z69.string().optional()
|
|
77591
77716
|
}).optional(),
|
|
77592
77717
|
/**
|
|
77593
77718
|
* Where the ad is set, as a place a model can picture: "Spain", "Mexico",
|
|
@@ -77598,7 +77723,7 @@ var AdSpec = z68.object({
|
|
|
77598
77723
|
* houses — sash windows, slate roofs, English suburbs — in every single frame,
|
|
77599
77724
|
* from briefs that were otherwise perfectly good.
|
|
77600
77725
|
*/
|
|
77601
|
-
market:
|
|
77726
|
+
market: z69.string().min(1).optional(),
|
|
77602
77727
|
/**
|
|
77603
77728
|
* The one person the ad is about.
|
|
77604
77729
|
*
|
|
@@ -77608,7 +77733,7 @@ var AdSpec = z68.object({
|
|
|
77608
77733
|
* with five different men playing one customer, which reads as a stock-footage
|
|
77609
77734
|
* collage rather than a story.
|
|
77610
77735
|
*/
|
|
77611
|
-
cast:
|
|
77736
|
+
cast: z69.object({
|
|
77612
77737
|
/**
|
|
77613
77738
|
* A cast Avatar's handle — `baker avatars list` shows who there is.
|
|
77614
77739
|
*
|
|
@@ -77618,9 +77743,9 @@ var AdSpec = z68.object({
|
|
|
77618
77743
|
* rather than re-written per spec — re-phrasing it per render is the other reason
|
|
77619
77744
|
* a face drifts across a set.
|
|
77620
77745
|
*/
|
|
77621
|
-
avatar:
|
|
77746
|
+
avatar: z69.string().min(1).optional(),
|
|
77622
77747
|
/** Who they are, plainly: age, build, hair, wardrobe, the register of the ad. */
|
|
77623
|
-
description:
|
|
77748
|
+
description: z69.string().min(1).optional()
|
|
77624
77749
|
}).refine((c) => Boolean(c.avatar || c.description), {
|
|
77625
77750
|
message: "give `avatar` (a cast avatar's handle) or `description` \u2014 a cast with neither grounds on nothing"
|
|
77626
77751
|
}).optional(),
|
|
@@ -77637,7 +77762,7 @@ var AdSpec = z68.object({
|
|
|
77637
77762
|
* has to read clean. That is the engine's rule, not this one, and it is why the prompt
|
|
77638
77763
|
* is all this needs to carry.
|
|
77639
77764
|
*/
|
|
77640
|
-
music:
|
|
77765
|
+
music: z69.union([z69.string().min(1), z69.literal(false)]).optional(),
|
|
77641
77766
|
/**
|
|
77642
77767
|
* The place the ad keeps coming back to, held still across every shot.
|
|
77643
77768
|
*
|
|
@@ -77652,9 +77777,9 @@ var AdSpec = z68.object({
|
|
|
77652
77777
|
* is `image_generate` with extra steps — the model registry says exactly that. Generate
|
|
77653
77778
|
* the plate once with gpt-image-2 and pass its URL here.
|
|
77654
77779
|
*/
|
|
77655
|
-
set:
|
|
77656
|
-
description:
|
|
77657
|
-
image:
|
|
77780
|
+
set: z69.object({
|
|
77781
|
+
description: z69.string().min(1).max(400),
|
|
77782
|
+
image: z69.string().min(1).optional()
|
|
77658
77783
|
}).optional(),
|
|
77659
77784
|
/**
|
|
77660
77785
|
* How the ad closes.
|
|
@@ -77668,15 +77793,15 @@ var AdSpec = z68.object({
|
|
|
77668
77793
|
*
|
|
77669
77794
|
* `false` opts out, for an ad that genuinely ends on its footage.
|
|
77670
77795
|
*/
|
|
77671
|
-
end_card:
|
|
77672
|
-
|
|
77673
|
-
|
|
77796
|
+
end_card: z69.union([
|
|
77797
|
+
z69.literal(false),
|
|
77798
|
+
z69.object({
|
|
77674
77799
|
/**
|
|
77675
77800
|
* The words on the button. No default — with none the card carries the mark
|
|
77676
77801
|
* alone, because falling back to the last beat's line printed that sentence
|
|
77677
77802
|
* twice on the closing frame. See `endCardCta`.
|
|
77678
77803
|
*/
|
|
77679
|
-
cta:
|
|
77804
|
+
cta: z69.string().min(1).optional()
|
|
77680
77805
|
})
|
|
77681
77806
|
]).optional(),
|
|
77682
77807
|
/**
|
|
@@ -77696,8 +77821,8 @@ var AdSpec = z68.object({
|
|
|
77696
77821
|
* rather than from transcribing a rendered read, so the text is exact and no speech
|
|
77697
77822
|
* model is between the copy and the screen.
|
|
77698
77823
|
*/
|
|
77699
|
-
voiceover:
|
|
77700
|
-
beats:
|
|
77824
|
+
voiceover: z69.boolean().default(true),
|
|
77825
|
+
beats: z69.array(Beat).min(2)
|
|
77701
77826
|
});
|
|
77702
77827
|
function presenterIsInEveryShot(spec) {
|
|
77703
77828
|
if (!spec.cast) return false;
|
|
@@ -78224,8 +78349,8 @@ function injectAdBrandOverlay(indexHtml, overlayHtml, style = {}) {
|
|
|
78224
78349
|
}
|
|
78225
78350
|
|
|
78226
78351
|
// src/commands/canvas/composition-dims.ts
|
|
78227
|
-
import { readFile as
|
|
78228
|
-
import
|
|
78352
|
+
import { readFile as readFile30, writeFile as writeFile19 } from "fs/promises";
|
|
78353
|
+
import path37 from "path";
|
|
78229
78354
|
function patchCompositionMeta(metaJson, dims) {
|
|
78230
78355
|
try {
|
|
78231
78356
|
const meta = JSON.parse(metaJson);
|
|
@@ -78240,12 +78365,12 @@ function patchCompositionHtml(html, dims) {
|
|
|
78240
78365
|
return html.replace(/(<meta\s+name="viewport"\s+content="width=)\d+(,\s*height=)\d+(")/i, `$1${dims.w}$2${dims.h}$3`).replace(/(width:\s*)\d+(px;\s*height:\s*)\d+(px;)/i, `$1${dims.w}$2${dims.h}$3`).replace(/(data-width=")\d+(")/i, `$1${dims.w}$2`).replace(/(data-height=")\d+(")/i, `$1${dims.h}$2`);
|
|
78241
78366
|
}
|
|
78242
78367
|
async function stampCompositionDims(compositionDir, dims) {
|
|
78243
|
-
const metaPath =
|
|
78244
|
-
const rawMeta = await
|
|
78245
|
-
await
|
|
78246
|
-
const htmlPath =
|
|
78247
|
-
const rawHtml = await
|
|
78248
|
-
await
|
|
78368
|
+
const metaPath = path37.join(compositionDir, "meta.json");
|
|
78369
|
+
const rawMeta = await readFile30(metaPath, "utf8");
|
|
78370
|
+
await writeFile19(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
|
|
78371
|
+
const htmlPath = path37.join(compositionDir, "index.html");
|
|
78372
|
+
const rawHtml = await readFile30(htmlPath, "utf8");
|
|
78373
|
+
await writeFile19(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
|
|
78249
78374
|
}
|
|
78250
78375
|
|
|
78251
78376
|
// src/commands/canvas/scaffold-ad.ts
|
|
@@ -78327,7 +78452,7 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78327
78452
|
const specPath = args8.spec;
|
|
78328
78453
|
let parsed;
|
|
78329
78454
|
try {
|
|
78330
|
-
parsed = JSON.parse(await
|
|
78455
|
+
parsed = JSON.parse(await readFile31(specPath, "utf-8"));
|
|
78331
78456
|
} catch (e) {
|
|
78332
78457
|
writeJson({
|
|
78333
78458
|
ok: false,
|
|
@@ -78488,7 +78613,7 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78488
78613
|
] : [];
|
|
78489
78614
|
const markPath = spec.data.brand?.logo?.trim();
|
|
78490
78615
|
if (!noBrand && markPath?.toLowerCase().endsWith(".svg")) {
|
|
78491
|
-
const svg = await
|
|
78616
|
+
const svg = await readFile31(markPath, "utf-8").catch(() => null);
|
|
78492
78617
|
if (svg === null) {
|
|
78493
78618
|
writeJson({
|
|
78494
78619
|
ok: false,
|
|
@@ -78540,8 +78665,8 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78540
78665
|
videoModel: chosenVideoModel,
|
|
78541
78666
|
...avatarVoice ? { avatarVoice } : {}
|
|
78542
78667
|
});
|
|
78543
|
-
const slug = args8.slug ??
|
|
78544
|
-
const outPath = args8.out ?? (args8.slug ?
|
|
78668
|
+
const slug = args8.slug ?? path38.basename(specPath).replace(/\.[^.]+$/, "");
|
|
78669
|
+
const outPath = args8.out ?? (args8.slug ? path38.join("src/creatives", slug, `${slug}.canvas.json`) : path38.join(path38.dirname(specPath), `${slug}.canvas.json`));
|
|
78545
78670
|
if (args8.slug) await enforceNewCreativeLimit(slug, outPath);
|
|
78546
78671
|
let outAr;
|
|
78547
78672
|
try {
|
|
@@ -78559,30 +78684,30 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78559
78684
|
return;
|
|
78560
78685
|
}
|
|
78561
78686
|
const outDims = canvasDims(outAr);
|
|
78562
|
-
const outDir =
|
|
78563
|
-
await
|
|
78564
|
-
const compositionDest =
|
|
78565
|
-
const captionsDest =
|
|
78687
|
+
const outDir = path38.dirname(outPath);
|
|
78688
|
+
await mkdir13(outDir, { recursive: true });
|
|
78689
|
+
const compositionDest = path38.join(outDir, "video-overlay-composition");
|
|
78690
|
+
const captionsDest = path38.join(outDir, "tiktok-captions-composition");
|
|
78566
78691
|
await cp(SHIPPED_OVERLAY_COMPOSITION_DIR, compositionDest, { recursive: true });
|
|
78567
78692
|
await cp(SHIPPED_CAPTIONS_DIR, captionsDest, { recursive: true });
|
|
78568
78693
|
await stampCompositionDims(compositionDest, outDims);
|
|
78569
78694
|
await stampCompositionDims(captionsDest, outDims);
|
|
78570
|
-
const captionsIndex =
|
|
78571
|
-
await
|
|
78695
|
+
const captionsIndex = path38.join(captionsDest, "index.html");
|
|
78696
|
+
await writeFile20(captionsIndex, injectAdCaptionPlacement(await readFile31(captionsIndex, "utf-8"), outAr), "utf-8");
|
|
78572
78697
|
const face = await stageBrandDisplayFace(outDir, captionsDest);
|
|
78573
78698
|
if (face) {
|
|
78574
|
-
await
|
|
78699
|
+
await writeFile20(captionsIndex, injectAdDisplayFace(await readFile31(captionsIndex, "utf-8"), face.file), "utf-8");
|
|
78575
78700
|
}
|
|
78576
|
-
const blueprintPath2 =
|
|
78577
|
-
const blueprintStylePath =
|
|
78701
|
+
const blueprintPath2 = path38.join(outDir, "prompt.json");
|
|
78702
|
+
const blueprintStylePath = path38.join(outDir, "prompt.style.json");
|
|
78578
78703
|
await writeSceneFiles(outDir, blueprint);
|
|
78579
|
-
await
|
|
78580
|
-
await
|
|
78704
|
+
await writeFile20(blueprintStylePath, renderStyleProjectionFromValue(blueprint), "utf8");
|
|
78705
|
+
await writeFile20(path38.join(outDir, AD_SPEC_FILE), `${JSON.stringify(specArtifact, null, 2)}
|
|
78581
78706
|
`, "utf8");
|
|
78582
78707
|
const logoPath = noBrand ? void 0 : spec.data.brand?.logo?.trim();
|
|
78583
|
-
const transcriptPath =
|
|
78708
|
+
const transcriptPath = path38.join(outDir, "transcript.json");
|
|
78584
78709
|
if (spec.data.voiceover === false) {
|
|
78585
|
-
await
|
|
78710
|
+
await writeFile20(transcriptPath, `${JSON.stringify(staticTranscript(spec.data, chosenVideoModel), null, 2)}
|
|
78586
78711
|
`, "utf-8");
|
|
78587
78712
|
}
|
|
78588
78713
|
const opts = {
|
|
@@ -78592,10 +78717,10 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78592
78717
|
// that stops the whole canvas. The same sheet model every other scaffold uses.
|
|
78593
78718
|
sheetModel: ACTOR_SHEET_MODEL,
|
|
78594
78719
|
videoModel: chosenVideoModel,
|
|
78595
|
-
overlayCompositionPath:
|
|
78596
|
-
captionsCompositionPath:
|
|
78597
|
-
blueprintPath:
|
|
78598
|
-
blueprintStylePath:
|
|
78720
|
+
overlayCompositionPath: path38.relative(outDir, compositionDest),
|
|
78721
|
+
captionsCompositionPath: path38.relative(outDir, captionsDest),
|
|
78722
|
+
blueprintPath: path38.relative(outDir, blueprintPath2),
|
|
78723
|
+
blueprintStylePath: path38.relative(outDir, blueprintStylePath),
|
|
78599
78724
|
aspect: spec.data.format.aspect_ratio,
|
|
78600
78725
|
resolution: spec.data.format.resolution,
|
|
78601
78726
|
// A beat is one clause and one card, so the cap has to clear a whole clause.
|
|
@@ -78634,17 +78759,17 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78634
78759
|
// the stock voice this exists to avoid. It is to convert the LATER extracts onto the
|
|
78635
78760
|
// first beat of the same speaker, which keeps a voice the clip really produced.
|
|
78636
78761
|
...avatar ? { keepRenderedVoice: true } : {},
|
|
78637
|
-
...spec.data.voiceover === false ? { staticTranscriptPath:
|
|
78762
|
+
...spec.data.voiceover === false ? { staticTranscriptPath: path38.relative(outDir, transcriptPath) } : {}
|
|
78638
78763
|
};
|
|
78639
78764
|
const canvas = scaffoldVideoCanvas(blueprint, adSpecCastElements(spec.data, avatar), opts);
|
|
78640
78765
|
const renderNode5 = canvas.nodes.find((n) => n.type === "hyperframe_render");
|
|
78641
|
-
const renderedDir = renderNode5 ?
|
|
78766
|
+
const renderedDir = renderNode5 ? path38.join(outDir, String(renderNode5.params?.composition ?? "")) : null;
|
|
78642
78767
|
let staged = null;
|
|
78643
78768
|
let markIsLight = false;
|
|
78644
78769
|
if (logoPath && renderedDir) {
|
|
78645
|
-
const ext =
|
|
78770
|
+
const ext = path38.extname(logoPath) || ".png";
|
|
78646
78771
|
try {
|
|
78647
|
-
await copyFile(logoPath,
|
|
78772
|
+
await copyFile(logoPath, path38.join(renderedDir, `brand-mark${ext}`));
|
|
78648
78773
|
staged = { file: `brand-mark${ext}`, alt: spec.data.brand?.name ?? "brand" };
|
|
78649
78774
|
markIsLight = await markInkIsLight(logoPath);
|
|
78650
78775
|
} catch {
|
|
@@ -78663,12 +78788,12 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78663
78788
|
plateIsDark: plateIsDark(spec.data.brand?.palette)
|
|
78664
78789
|
}) : "";
|
|
78665
78790
|
if (overlayHtml && renderedDir) {
|
|
78666
|
-
const indexPath =
|
|
78667
|
-
const html = await
|
|
78668
|
-
await
|
|
78791
|
+
const indexPath = path38.join(renderedDir, "index.html");
|
|
78792
|
+
const html = await readFile31(indexPath, "utf-8");
|
|
78793
|
+
await writeFile20(indexPath, injectAdBrandOverlay(html, overlayHtml, { aspect: outAr, ...face ? { face: AD_DISPLAY_FAMILY } : {} }), "utf-8");
|
|
78669
78794
|
}
|
|
78670
|
-
await
|
|
78671
|
-
|
|
78795
|
+
await writeFile20(
|
|
78796
|
+
path38.join(outDir, REBUILD_FILE),
|
|
78672
78797
|
`${JSON.stringify({ elements: adSpecCastElements(spec.data, avatar), opts }, null, 2)}
|
|
78673
78798
|
`,
|
|
78674
78799
|
"utf8"
|
|
@@ -78678,7 +78803,7 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78678
78803
|
const meta = canvas.metadata;
|
|
78679
78804
|
meta.todo = { ...meta.todo ?? {}, blocking_validation_issues: validation.issues };
|
|
78680
78805
|
}
|
|
78681
|
-
await
|
|
78806
|
+
await writeFile20(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
78682
78807
|
`, "utf-8");
|
|
78683
78808
|
if (!validation.ok) {
|
|
78684
78809
|
process.stderr.write(
|
|
@@ -78698,10 +78823,10 @@ var scaffoldAdCommand = defineCommand144({
|
|
|
78698
78823
|
return;
|
|
78699
78824
|
}
|
|
78700
78825
|
if (args8.slug) {
|
|
78701
|
-
const definitionPath =
|
|
78826
|
+
const definitionPath = path38.join(outDir, "_definition.md");
|
|
78702
78827
|
const definitionExists = await stat6(definitionPath).then(() => true, () => false);
|
|
78703
78828
|
if (!definitionExists) {
|
|
78704
|
-
await
|
|
78829
|
+
await writeFile20(
|
|
78705
78830
|
definitionPath,
|
|
78706
78831
|
buildCreativeDefinition({
|
|
78707
78832
|
title: titleFromSlug(slug),
|
|
@@ -78816,10 +78941,10 @@ async function fetchClientLogo() {
|
|
|
78816
78941
|
if (!response.ok) return { ok: false, reason: clientLogoMissNote({ use: false, reason: "no_hits" }, domain) };
|
|
78817
78942
|
const bytes = Buffer.from(await response.arrayBuffer());
|
|
78818
78943
|
const extension = /\.(svg|png|jpg|jpeg|webp)(?:\?|$)/i.exec(decision.url)?.[1]?.toLowerCase() ?? "svg";
|
|
78819
|
-
const dir =
|
|
78820
|
-
await
|
|
78821
|
-
const file =
|
|
78822
|
-
await
|
|
78944
|
+
const dir = path38.join(BRAND_DIR, "logos");
|
|
78945
|
+
await mkdir13(dir, { recursive: true });
|
|
78946
|
+
const file = path38.join(dir, `brand-mark.${extension}`);
|
|
78947
|
+
await writeFile20(file, bytes);
|
|
78823
78948
|
return { ok: true, path: file };
|
|
78824
78949
|
} catch {
|
|
78825
78950
|
return { ok: false, reason: clientLogoMissNote({ use: false, reason: "no_hits" }, domain) };
|
|
@@ -78837,14 +78962,14 @@ async function stageBrandDisplayFace(outDir, compositionDir) {
|
|
|
78837
78962
|
if (!face) return null;
|
|
78838
78963
|
try {
|
|
78839
78964
|
if ("path" in face.slot) {
|
|
78840
|
-
const file2 = `brand-face${
|
|
78841
|
-
await copyFile(face.slot.path,
|
|
78842
|
-
return { file: file2, label: `the font file \`${
|
|
78965
|
+
const file2 = `brand-face${path38.extname(face.slot.path).toLowerCase()}`;
|
|
78966
|
+
await copyFile(face.slot.path, path38.join(compositionDir, file2));
|
|
78967
|
+
return { file: file2, label: `the font file \`${path38.relative(root, face.slot.path)}\`` };
|
|
78843
78968
|
}
|
|
78844
78969
|
const { buffer } = await fetchExternalBytes(face.slot.url, { maxBytes: MAX_FONT_BYTES });
|
|
78845
78970
|
const extension = /\.(woff2|woff|ttf|otf)(?:\?|$)/i.exec(face.slot.url)?.[1]?.toLowerCase() ?? "woff2";
|
|
78846
78971
|
const file = `brand-face.${extension}`;
|
|
78847
|
-
await
|
|
78972
|
+
await writeFile20(path38.join(compositionDir, file), buffer);
|
|
78848
78973
|
return { file, label: `${face.family ?? "the brand's typeface"}, loaded as a web font` };
|
|
78849
78974
|
} catch {
|
|
78850
78975
|
return null;
|
|
@@ -78852,9 +78977,9 @@ async function stageBrandDisplayFace(outDir, compositionDir) {
|
|
|
78852
78977
|
}
|
|
78853
78978
|
|
|
78854
78979
|
// src/commands/canvas/scaffold-video.ts
|
|
78855
|
-
import { cp as cp2, mkdir as
|
|
78980
|
+
import { cp as cp2, mkdir as mkdir14, readFile as readFile34, rm as rm6, writeFile as writeFile21 } from "fs/promises";
|
|
78856
78981
|
import { tmpdir as tmpdir2 } from "os";
|
|
78857
|
-
import
|
|
78982
|
+
import path40 from "path";
|
|
78858
78983
|
import { defineCommand as defineCommand145 } from "citty";
|
|
78859
78984
|
|
|
78860
78985
|
// src/engine/scaffold/lib/model-router.ts
|
|
@@ -78894,7 +79019,7 @@ function routeVideoModel(input) {
|
|
|
78894
79019
|
|
|
78895
79020
|
// src/engine/nodes/local/lib/sceneDetect.ts
|
|
78896
79021
|
import { execFile as execFile2 } from "child_process";
|
|
78897
|
-
import { mkdtemp, readdir as readdir16, readFile as
|
|
79022
|
+
import { mkdtemp, readdir as readdir16, readFile as readFile32, rm as rm5 } from "fs/promises";
|
|
78898
79023
|
import { tmpdir } from "os";
|
|
78899
79024
|
import { join as join3 } from "path";
|
|
78900
79025
|
import { promisify as promisify2 } from "util";
|
|
@@ -78970,7 +79095,7 @@ async function runSceneDetectOnce(filePath, threshold, minSceneLenS, timeoutMs)
|
|
|
78970
79095
|
);
|
|
78971
79096
|
const csvName = (await readdir16(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
|
|
78972
79097
|
if (!csvName) return [];
|
|
78973
|
-
return parsePySceneDetectCsvCuts(await
|
|
79098
|
+
return parsePySceneDetectCsvCuts(await readFile32(join3(outDir, csvName), "utf-8"));
|
|
78974
79099
|
} finally {
|
|
78975
79100
|
await rm5(outDir, { recursive: true, force: true });
|
|
78976
79101
|
}
|
|
@@ -78994,8 +79119,8 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
|
|
|
78994
79119
|
}
|
|
78995
79120
|
|
|
78996
79121
|
// src/commands/canvas/gitignore.ts
|
|
78997
|
-
import { appendFile, readFile as
|
|
78998
|
-
import
|
|
79122
|
+
import { appendFile, readFile as readFile33 } from "fs/promises";
|
|
79123
|
+
import path39 from "path";
|
|
78999
79124
|
function missingGitignoreEntries(existing, entries) {
|
|
79000
79125
|
const present2 = new Set(
|
|
79001
79126
|
existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
|
|
@@ -79003,10 +79128,10 @@ function missingGitignoreEntries(existing, entries) {
|
|
|
79003
79128
|
return entries.filter((e) => !present2.has(e.trim().replace(/\/+$/, "")));
|
|
79004
79129
|
}
|
|
79005
79130
|
async function ensureGitignore(dir, entries) {
|
|
79006
|
-
const file =
|
|
79131
|
+
const file = path39.join(dir, ".gitignore");
|
|
79007
79132
|
let existing;
|
|
79008
79133
|
try {
|
|
79009
|
-
existing = await
|
|
79134
|
+
existing = await readFile33(file, "utf8");
|
|
79010
79135
|
} catch {
|
|
79011
79136
|
return;
|
|
79012
79137
|
}
|
|
@@ -79048,7 +79173,7 @@ async function loadTranscriptBestEffort(ref2) {
|
|
|
79048
79173
|
async function stageCaptions(outDir, transcript) {
|
|
79049
79174
|
const text3 = transcript?.trim();
|
|
79050
79175
|
if (!text3 || text3 === "[]") return {};
|
|
79051
|
-
const compositionPath =
|
|
79176
|
+
const compositionPath = path40.join(outDir, "tiktok-captions-composition");
|
|
79052
79177
|
await cp2(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
|
|
79053
79178
|
return { compositionPath };
|
|
79054
79179
|
}
|
|
@@ -79084,7 +79209,7 @@ var VIDEO_EXT_BY_MIME = {
|
|
|
79084
79209
|
"video/x-matroska": ".mkv"
|
|
79085
79210
|
};
|
|
79086
79211
|
function referenceVideoExt(url, contentType) {
|
|
79087
|
-
const fromPath =
|
|
79212
|
+
const fromPath = path40.extname(new URL(url).pathname).toLowerCase();
|
|
79088
79213
|
if (fromPath && fromPath.length <= 5) return fromPath;
|
|
79089
79214
|
const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase();
|
|
79090
79215
|
return mime && VIDEO_EXT_BY_MIME[mime] || ".mp4";
|
|
@@ -79110,7 +79235,7 @@ function framesModeReport(canvas, requested) {
|
|
|
79110
79235
|
return { frames_mode: mode, ...refused ? { frames_reuse_refused: refused } : {} };
|
|
79111
79236
|
}
|
|
79112
79237
|
async function materializeReferenceVideo(fileArg2) {
|
|
79113
|
-
if (!/^https?:\/\//i.test(fileArg2)) return
|
|
79238
|
+
if (!/^https?:\/\//i.test(fileArg2)) return path40.resolve(fileArg2);
|
|
79114
79239
|
let bytes;
|
|
79115
79240
|
let contentType;
|
|
79116
79241
|
try {
|
|
@@ -79122,11 +79247,11 @@ async function materializeReferenceVideo(fileArg2) {
|
|
|
79122
79247
|
throw new Error(`failed to download reference video: ${e instanceof Error ? e.message : String(e)}`);
|
|
79123
79248
|
}
|
|
79124
79249
|
if (bytes.length === 0) throw new Error("reference video download was empty");
|
|
79125
|
-
const dest =
|
|
79250
|
+
const dest = path40.join(
|
|
79126
79251
|
tmpdir2(),
|
|
79127
79252
|
`baker-ref-${sha256Hex(bytes).slice(0, 16)}${referenceVideoExt(fileArg2, contentType)}`
|
|
79128
79253
|
);
|
|
79129
|
-
await
|
|
79254
|
+
await writeFile21(dest, bytes);
|
|
79130
79255
|
return dest;
|
|
79131
79256
|
}
|
|
79132
79257
|
function resolveSeamDedup(raw) {
|
|
@@ -79349,12 +79474,12 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79349
79474
|
} catch (e) {
|
|
79350
79475
|
return fail4("download", e instanceof Error ? e.message : String(e));
|
|
79351
79476
|
}
|
|
79352
|
-
const base =
|
|
79353
|
-
const outPath = args8.out ?
|
|
79477
|
+
const base = path40.basename(videoPath, path40.extname(videoPath));
|
|
79478
|
+
const outPath = args8.out ? path40.resolve(String(args8.out)) : slug ? path40.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path40.join(path40.dirname(videoPath), `${base}.video.canvas.json`);
|
|
79354
79479
|
if (slug) await enforceNewCreativeLimit(slug, outPath);
|
|
79355
|
-
const outDir =
|
|
79356
|
-
const blueprintPath2 =
|
|
79357
|
-
const blueprintStylePath =
|
|
79480
|
+
const outDir = path40.dirname(outPath);
|
|
79481
|
+
const blueprintPath2 = path40.join(outDir, "prompt.json");
|
|
79482
|
+
const blueprintStylePath = path40.join(outDir, "prompt.style.json");
|
|
79358
79483
|
const frames = args8.frames === "reuse" ? "reuse" : "generate";
|
|
79359
79484
|
const maxScenes = args8["max-scenes"] ? Number(args8["max-scenes"]) : void 0;
|
|
79360
79485
|
if (Number.isFinite(maxScenes)) {
|
|
@@ -79402,10 +79527,10 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79402
79527
|
process.stderr.write(`\u{1F3AC} Photoreal on-camera cast detected \u2192 generating clips on ${videoModel} (dodges Seedance's real-person filter).
|
|
79403
79528
|
`);
|
|
79404
79529
|
}
|
|
79405
|
-
await
|
|
79530
|
+
await mkdir14(outDir, { recursive: true });
|
|
79406
79531
|
const annotated = annotateBlueprintWithElements(blueprint, elements);
|
|
79407
79532
|
await writeSceneFiles(outDir, annotated);
|
|
79408
|
-
await
|
|
79533
|
+
await writeFile21(blueprintStylePath, renderStyleProjectionFromValue(annotated), "utf8");
|
|
79409
79534
|
let aspect;
|
|
79410
79535
|
try {
|
|
79411
79536
|
aspect = resolveAspect(
|
|
@@ -79423,12 +79548,12 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79423
79548
|
`
|
|
79424
79549
|
);
|
|
79425
79550
|
}
|
|
79426
|
-
const compositionDest =
|
|
79551
|
+
const compositionDest = path40.join(outDir, "video-overlay-composition");
|
|
79427
79552
|
await cp2(SHIPPED_OVERLAY_COMPOSITION_DIR, compositionDest, { recursive: true });
|
|
79428
79553
|
await stampCompositionDims(compositionDest, outDims);
|
|
79429
|
-
const indexPath =
|
|
79554
|
+
const indexPath = path40.join(compositionDest, "index.html");
|
|
79430
79555
|
const overlayHtml = buildOverlayHtml(blueprint, { captionsActive: Boolean(transcript), videoModel });
|
|
79431
|
-
const indexHtml = await
|
|
79556
|
+
const indexHtml = await readFile34(indexPath, "utf8");
|
|
79432
79557
|
const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
|
|
79433
79558
|
if (injected === indexHtml && overlayHtml.trim()) {
|
|
79434
79559
|
fail4(
|
|
@@ -79436,16 +79561,16 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79436
79561
|
`video-overlay-composition/index.html is missing the <!--OVERLAYS--> marker \u2014 cannot inject the overlay layer`
|
|
79437
79562
|
);
|
|
79438
79563
|
}
|
|
79439
|
-
await
|
|
79564
|
+
await writeFile21(indexPath, injected, "utf8");
|
|
79440
79565
|
const captions = await stageCaptions(outDir, transcript);
|
|
79441
79566
|
if (captions.compositionPath) await stampCompositionDims(captions.compositionPath, outDims);
|
|
79442
79567
|
const opts = {
|
|
79443
79568
|
imageModel,
|
|
79444
79569
|
videoModel,
|
|
79445
|
-
overlayCompositionPath:
|
|
79446
|
-
captionsCompositionPath: captions.compositionPath ?
|
|
79447
|
-
blueprintPath:
|
|
79448
|
-
blueprintStylePath:
|
|
79570
|
+
overlayCompositionPath: path40.relative(outDir, compositionDest),
|
|
79571
|
+
captionsCompositionPath: captions.compositionPath ? path40.relative(outDir, captions.compositionPath) : void 0,
|
|
79572
|
+
blueprintPath: path40.relative(outDir, blueprintPath2),
|
|
79573
|
+
blueprintStylePath: path40.relative(outDir, blueprintStylePath),
|
|
79449
79574
|
frames,
|
|
79450
79575
|
ambient: Boolean(args8.ambient),
|
|
79451
79576
|
seamDedup: resolveSeamDedup(args8["seam-dedup"]),
|
|
@@ -79470,10 +79595,10 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79470
79595
|
todo.blocking_validation_issues = validation.issues;
|
|
79471
79596
|
meta.todo = todo;
|
|
79472
79597
|
}
|
|
79473
|
-
await
|
|
79598
|
+
await writeFile21(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
79474
79599
|
`, "utf8");
|
|
79475
|
-
await
|
|
79476
|
-
|
|
79600
|
+
await writeFile21(
|
|
79601
|
+
path40.join(outDir, REBUILD_FILE),
|
|
79477
79602
|
`${JSON.stringify({ elements, opts }, null, 2)}
|
|
79478
79603
|
`,
|
|
79479
79604
|
"utf8"
|
|
@@ -79500,9 +79625,9 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79500
79625
|
await ensureGitignore(process.cwd(), ["canvas/", ".context/"]);
|
|
79501
79626
|
const sourceRef = videoSourceReference(blueprint, fileArg2);
|
|
79502
79627
|
if (slug) {
|
|
79503
|
-
const definitionPath =
|
|
79628
|
+
const definitionPath = path40.join(outDir, "_definition.md");
|
|
79504
79629
|
if (!await fileExists(definitionPath)) {
|
|
79505
|
-
await
|
|
79630
|
+
await writeFile21(
|
|
79506
79631
|
definitionPath,
|
|
79507
79632
|
buildCreativeDefinition({
|
|
79508
79633
|
title: titleFromSlug(slug),
|
|
@@ -79556,7 +79681,7 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79556
79681
|
graph: canvas.metadata?.video?.graph_stats
|
|
79557
79682
|
},
|
|
79558
79683
|
checklist: {
|
|
79559
|
-
edit_prompt: `The blueprint is split so you edit ONE small file at a time \u2014 never a giant one. Per-scene content (a scene's dialogue, action, frame prompts, overlays) lives in \`scenes/sNN.json\` \u2014 edit the single scene you want to change. Global cast/palette/brand/copy lives in \`${
|
|
79684
|
+
edit_prompt: `The blueprint is split so you edit ONE small file at a time \u2014 never a giant one. Per-scene content (a scene's dialogue, action, frame prompts, overlays) lives in \`scenes/sNN.json\` \u2014 edit the single scene you want to change. Global cast/palette/brand/copy lives in \`${path40.basename(blueprintPath2)}\`. \`baker canvas validate\`/\`run\` re-assemble the blueprint and re-flow every edited scene back into the render (and regenerate ${path40.basename(blueprintStylePath)}, the projection each frame's target_blueprint reads) \u2014 so your scene edits reach the render automatically. Never hand-edit the inlined node prompts in the canvas or the derived ${path40.basename(blueprintStylePath)}; both are regenerated.`,
|
|
79560
79685
|
recurring_elements_to_supply: report.elements,
|
|
79561
79686
|
voices_to_confirm: report.dialogue.map((d) => ({
|
|
79562
79687
|
scene: d.scene,
|
|
@@ -79593,8 +79718,8 @@ var scaffoldVideoCommand = defineCommand145({
|
|
|
79593
79718
|
});
|
|
79594
79719
|
|
|
79595
79720
|
// src/commands/canvas/set-prompt.ts
|
|
79596
|
-
import { readFile as
|
|
79597
|
-
import
|
|
79721
|
+
import { readFile as readFile35, writeFile as writeFile22 } from "fs/promises";
|
|
79722
|
+
import path41 from "path";
|
|
79598
79723
|
import { defineCommand as defineCommand146 } from "citty";
|
|
79599
79724
|
function setNodePrompt(canvas, nodeId, text3) {
|
|
79600
79725
|
const nodes = canvas?.nodes;
|
|
@@ -79623,8 +79748,8 @@ var SetPromptError = class extends Error {
|
|
|
79623
79748
|
}
|
|
79624
79749
|
};
|
|
79625
79750
|
async function setPromptInCanvasFile(canvasPath, nodeId, text3) {
|
|
79626
|
-
const filePath =
|
|
79627
|
-
const original = await
|
|
79751
|
+
const filePath = path41.resolve(canvasPath);
|
|
79752
|
+
const original = await readFile35(filePath, "utf8");
|
|
79628
79753
|
let canvas;
|
|
79629
79754
|
try {
|
|
79630
79755
|
canvas = JSON.parse(original);
|
|
@@ -79646,7 +79771,7 @@ async function setPromptInCanvasFile(canvasPath, nodeId, text3) {
|
|
|
79646
79771
|
"projected_prompt",
|
|
79647
79772
|
`The prompt of "${nodeId}" is compiled from prompt.json on every validate and run, so setting it in the canvas would be overwritten.`,
|
|
79648
79773
|
{
|
|
79649
|
-
fix: `Edit the ad in ${
|
|
79774
|
+
fix: `Edit the ad in ${path41.join(path41.dirname(filePath), "prompt.json")} (its scene, copy, references or direction), then run \`baker canvas validate\` to see the compiled prompt on the node.`
|
|
79650
79775
|
}
|
|
79651
79776
|
);
|
|
79652
79777
|
}
|
|
@@ -79656,9 +79781,9 @@ async function setPromptInCanvasFile(canvasPath, nodeId, text3) {
|
|
|
79656
79781
|
} catch (e) {
|
|
79657
79782
|
throw new SetPromptError("node_not_found", e instanceof Error ? e.message : String(e));
|
|
79658
79783
|
}
|
|
79659
|
-
await
|
|
79784
|
+
await writeFile22(filePath, `${JSON.stringify(updated, null, 2)}
|
|
79660
79785
|
`, "utf8");
|
|
79661
|
-
const restore = () =>
|
|
79786
|
+
const restore = () => writeFile22(filePath, original, "utf8");
|
|
79662
79787
|
try {
|
|
79663
79788
|
const prepared = await prepareCanvas(filePath);
|
|
79664
79789
|
const validation = await validateCanvasDeep(prepared.canvas, defaultRegistry());
|
|
@@ -79700,7 +79825,7 @@ var setPromptCommand = defineCommand146({
|
|
|
79700
79825
|
},
|
|
79701
79826
|
async run({ args: args8 }) {
|
|
79702
79827
|
let text3;
|
|
79703
|
-
if (args8["text-file"]) text3 = await
|
|
79828
|
+
if (args8["text-file"]) text3 = await readFile35(path41.resolve(String(args8["text-file"])), "utf8");
|
|
79704
79829
|
else if (args8.text !== void 0) text3 = String(args8.text);
|
|
79705
79830
|
else {
|
|
79706
79831
|
process.stderr.write(
|
|
@@ -79757,7 +79882,7 @@ var staleCommand = defineCommand147({
|
|
|
79757
79882
|
});
|
|
79758
79883
|
|
|
79759
79884
|
// src/commands/canvas/validate.ts
|
|
79760
|
-
import
|
|
79885
|
+
import path42 from "path";
|
|
79761
79886
|
import { defineCommand as defineCommand148 } from "citty";
|
|
79762
79887
|
var validateCommand = defineCommand148({
|
|
79763
79888
|
meta: {
|
|
@@ -79766,7 +79891,7 @@ var validateCommand = defineCommand148({
|
|
|
79766
79891
|
},
|
|
79767
79892
|
args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
|
|
79768
79893
|
async run({ args: args8 }) {
|
|
79769
|
-
const filePath =
|
|
79894
|
+
const filePath = path42.resolve(String(args8.file));
|
|
79770
79895
|
let prepared;
|
|
79771
79896
|
try {
|
|
79772
79897
|
prepared = await prepareCanvas(filePath, { gate: true, log: (line) => process.stderr.write(`${line}
|
|
@@ -80202,8 +80327,8 @@ import { defineCommand as defineCommand162 } from "citty";
|
|
|
80202
80327
|
|
|
80203
80328
|
// src/commands/creatives/brief.ts
|
|
80204
80329
|
import { readFileSync as readFileSync17 } from "fs";
|
|
80205
|
-
import { readdir as readdir17, readFile as
|
|
80206
|
-
import
|
|
80330
|
+
import { readdir as readdir17, readFile as readFile36 } from "fs/promises";
|
|
80331
|
+
import path44 from "path";
|
|
80207
80332
|
import { defineCommand as defineCommand155 } from "citty";
|
|
80208
80333
|
|
|
80209
80334
|
// src/commands/winning-ads/search.ts
|
|
@@ -80461,8 +80586,8 @@ var FLAG_FOR_FIELD = {
|
|
|
80461
80586
|
first_seen_after: "--first-seen-after",
|
|
80462
80587
|
first_seen_before: "--first-seen-before"
|
|
80463
80588
|
};
|
|
80464
|
-
function flagFor(
|
|
80465
|
-
const field = String(
|
|
80589
|
+
function flagFor(path68) {
|
|
80590
|
+
const field = String(path68[0] === "hard_filters" ? path68[1] : path68[0]);
|
|
80466
80591
|
return FLAG_FOR_FIELD[field] ?? field;
|
|
80467
80592
|
}
|
|
80468
80593
|
function schemaRejection(body) {
|
|
@@ -80878,7 +81003,9 @@ function ideasHints(report) {
|
|
|
80878
81003
|
return hints2;
|
|
80879
81004
|
}
|
|
80880
81005
|
function buildNext(ideas, count2 = BUILD_NEXT) {
|
|
80881
|
-
|
|
81006
|
+
const ready = ideas.filter((i) => i.status === "idea" && i.problems.length === 0);
|
|
81007
|
+
const firstBet = ready.find((i) => i.evidence === "explore");
|
|
81008
|
+
return ready.filter((i) => i.evidence !== "explore" || i === firstBet).slice(0, count2).map((i) => i.id);
|
|
80882
81009
|
}
|
|
80883
81010
|
function compactIdea(check) {
|
|
80884
81011
|
return {
|
|
@@ -80932,7 +81059,7 @@ var ideasCommand = defineCommand153({
|
|
|
80932
81059
|
|
|
80933
81060
|
// src/commands/creatives/performance.ts
|
|
80934
81061
|
import { readFileSync as readFileSync16 } from "fs";
|
|
80935
|
-
import
|
|
81062
|
+
import path43 from "path";
|
|
80936
81063
|
import { defineCommand as defineCommand154 } from "citty";
|
|
80937
81064
|
import { parse as parseYaml4 } from "yaml";
|
|
80938
81065
|
var DESCRIPTION5 = `How each Baker creative is doing in the live Meta ads that show it: spend, impressions, CTR, CPC, results, cost per result, frequency and a fatigue verdict, next to the angle, hook, mechanism and dominant slot its definition set out to test. Video creatives add hookRate (percent of impressions that watched 3 seconds) and holdRate (percent of those that reached a ThruPlay). Use it before briefing new creatives, to learn which angle and mechanism to make next and which creatives to replace.
|
|
@@ -80996,7 +81123,7 @@ function definitionInsights(markdown) {
|
|
|
80996
81123
|
}
|
|
80997
81124
|
function readLocalDefinition(slug) {
|
|
80998
81125
|
try {
|
|
80999
|
-
return readFileSync16(
|
|
81126
|
+
return readFileSync16(path43.join(process.cwd(), "src", "creatives", slug, "_definition.md"), "utf8");
|
|
81000
81127
|
} catch {
|
|
81001
81128
|
return null;
|
|
81002
81129
|
}
|
|
@@ -81108,11 +81235,11 @@ function isAvailable(value2) {
|
|
|
81108
81235
|
return !(value2 !== null && typeof value2 === "object" && "unavailable" in value2);
|
|
81109
81236
|
}
|
|
81110
81237
|
async function companyStatus(root) {
|
|
81111
|
-
const doc = await
|
|
81238
|
+
const doc = await readFile36(path44.join(root, "COMPANY.md"), "utf8").catch(() => "");
|
|
81112
81239
|
return doc.trim() && !/^#\s*Company\s*\n+\s*TODO/m.test(doc) ? "ready" : "missing";
|
|
81113
81240
|
}
|
|
81114
81241
|
async function readOffers(root) {
|
|
81115
|
-
const dir =
|
|
81242
|
+
const dir = path44.join(root, "src", "content", "offers");
|
|
81116
81243
|
const files = (await readdir17(dir, { recursive: true }).catch(() => [])).filter(
|
|
81117
81244
|
(f) => f.endsWith(".md")
|
|
81118
81245
|
);
|
|
@@ -81120,7 +81247,7 @@ async function readOffers(root) {
|
|
|
81120
81247
|
}
|
|
81121
81248
|
function readDefinition(root, slug) {
|
|
81122
81249
|
try {
|
|
81123
|
-
return readFileSync17(
|
|
81250
|
+
return readFileSync17(path44.join(root, "src", "creatives", slug, "_definition.md"), "utf8");
|
|
81124
81251
|
} catch {
|
|
81125
81252
|
return null;
|
|
81126
81253
|
}
|
|
@@ -81141,7 +81268,7 @@ function marketQuery(companyDoc) {
|
|
|
81141
81268
|
return prose.slice(0, 240);
|
|
81142
81269
|
}
|
|
81143
81270
|
async function readMarket(root) {
|
|
81144
|
-
const query = marketQuery(await
|
|
81271
|
+
const query = marketQuery(await readFile36(path44.join(root, "COMPANY.md"), "utf8").catch(() => ""));
|
|
81145
81272
|
if (!query) return null;
|
|
81146
81273
|
const search = (body) => apiPost("/api/ad-library/winners/search", body);
|
|
81147
81274
|
const followed = await search(
|
|
@@ -81477,8 +81604,8 @@ var feedbackCommand = defineCommand157({
|
|
|
81477
81604
|
|
|
81478
81605
|
// src/commands/creatives/learn.ts
|
|
81479
81606
|
import { readFileSync as readFileSync18 } from "fs";
|
|
81480
|
-
import { readFile as
|
|
81481
|
-
import
|
|
81607
|
+
import { readFile as readFile37, writeFile as writeFile23 } from "fs/promises";
|
|
81608
|
+
import path45 from "path";
|
|
81482
81609
|
import { defineCommand as defineCommand158 } from "citty";
|
|
81483
81610
|
|
|
81484
81611
|
// src/commands/creatives/learn-model.ts
|
|
@@ -81700,14 +81827,14 @@ function learnEnvelope(learnings, written, unreadable = []) {
|
|
|
81700
81827
|
}
|
|
81701
81828
|
async function writeBack(root, learnings) {
|
|
81702
81829
|
for (const learning of learnings) {
|
|
81703
|
-
const file =
|
|
81704
|
-
const raw = await
|
|
81705
|
-
await
|
|
81830
|
+
const file = path45.join(root, IDEAS_DIR, `${learning.idea}.md`);
|
|
81831
|
+
const raw = await readFile37(file, "utf8");
|
|
81832
|
+
await writeFile23(file, setDefinitionFields(raw, { status: learning.to, result: learning.result }), "utf8");
|
|
81706
81833
|
}
|
|
81707
81834
|
}
|
|
81708
81835
|
function readDefinition2(root, slug) {
|
|
81709
81836
|
try {
|
|
81710
|
-
return readFileSync18(
|
|
81837
|
+
return readFileSync18(path45.join(root, "src", "creatives", slug, "_definition.md"), "utf8");
|
|
81711
81838
|
} catch {
|
|
81712
81839
|
return null;
|
|
81713
81840
|
}
|
|
@@ -81745,9 +81872,9 @@ var learnCommand = defineCommand158({
|
|
|
81745
81872
|
|
|
81746
81873
|
// src/commands/creatives/make.ts
|
|
81747
81874
|
import { execFile as execFile3 } from "child_process";
|
|
81748
|
-
import { mkdtemp as mkdtemp2, readFile as
|
|
81875
|
+
import { mkdtemp as mkdtemp2, readFile as readFile38, rm as rm7, writeFile as writeFile24 } from "fs/promises";
|
|
81749
81876
|
import { tmpdir as tmpdir3 } from "os";
|
|
81750
|
-
import
|
|
81877
|
+
import path46 from "path";
|
|
81751
81878
|
import { promisify as promisify3 } from "util";
|
|
81752
81879
|
import { defineCommand as defineCommand159 } from "citty";
|
|
81753
81880
|
var run2 = promisify3(execFile3);
|
|
@@ -81827,30 +81954,37 @@ async function viewerTemperature(root, adSet, awareness) {
|
|
|
81827
81954
|
return viewerFromKnowledge({ personas: [], awareness }).temperature;
|
|
81828
81955
|
}
|
|
81829
81956
|
var asPlatform = (value2) => value2 === "linkedin" || value2 === "tiktok" ? value2 : "meta";
|
|
81830
|
-
|
|
81831
|
-
|
|
81832
|
-
|
|
81957
|
+
function liveReferenceDeps(root) {
|
|
81958
|
+
return {
|
|
81959
|
+
search: async (body) => (await apiPost("/api/ad-library/winners/search", body, {
|
|
81833
81960
|
timeoutMs: SEARCH_TIMEOUT_MS2
|
|
81834
|
-
}).catch(() => null)
|
|
81835
|
-
|
|
81961
|
+
}).catch(() => null))?.results ?? [],
|
|
81962
|
+
fit: (platform, adId) => fitReference(root, platform, adId)
|
|
81963
|
+
};
|
|
81964
|
+
}
|
|
81965
|
+
async function findCandidates(searches, deps) {
|
|
81966
|
+
const results = await Promise.all(searches.map((search) => deps.search(search).catch(() => [])));
|
|
81967
|
+
for (const found of results) {
|
|
81968
|
+
const candidates = found.filter((r) => r.ad_id).slice(0, REFERENCE_CANDIDATES);
|
|
81836
81969
|
if (candidates.length > 0) return candidates;
|
|
81837
81970
|
}
|
|
81838
81971
|
return [];
|
|
81839
81972
|
}
|
|
81840
|
-
async function pickReference(root, searches) {
|
|
81841
|
-
const candidates = (await findCandidates(searches)).slice(0, FITS_TRIED);
|
|
81973
|
+
async function pickReference(root, searches, deps = liveReferenceDeps(root)) {
|
|
81974
|
+
const candidates = (await findCandidates(searches, deps)).slice(0, FITS_TRIED);
|
|
81975
|
+
const fits = await Promise.all(candidates.map((c) => deps.fit(asPlatform(c.platform), String(c.ad_id))));
|
|
81842
81976
|
let unchecked = null;
|
|
81843
81977
|
let checked = 0;
|
|
81844
81978
|
for (const [index, candidate] of candidates.entries()) {
|
|
81845
|
-
const
|
|
81846
|
-
|
|
81979
|
+
const fit = fits[index];
|
|
81980
|
+
if (!fit) continue;
|
|
81847
81981
|
if (!fit.ok) unchecked = fit.error;
|
|
81848
81982
|
else checked += 1;
|
|
81849
81983
|
if (!(fit.ok && fitsIdea(fit.fit))) continue;
|
|
81850
81984
|
const layers = fit.fit.why_it_wins;
|
|
81851
81985
|
return {
|
|
81852
81986
|
adId: String(candidate.ad_id),
|
|
81853
|
-
platform,
|
|
81987
|
+
platform: asPlatform(candidate.platform),
|
|
81854
81988
|
...candidate.advertiser_id ? { advertiserId: candidate.advertiser_id } : {},
|
|
81855
81989
|
why: [layers.disguise, layers.trigger, layers.logic].filter(Boolean).join("; "),
|
|
81856
81990
|
tried: index + 1
|
|
@@ -81945,9 +82079,9 @@ async function scaffoldIdea(root, idea, args8, slug, temperature) {
|
|
|
81945
82079
|
return { scaffold, reference };
|
|
81946
82080
|
}
|
|
81947
82081
|
async function scaffoldFromIdea(idea, slug, viewerFlags) {
|
|
81948
|
-
const dir = await mkdtemp2(
|
|
81949
|
-
const specPath =
|
|
81950
|
-
await
|
|
82082
|
+
const dir = await mkdtemp2(path46.join(tmpdir3(), "idea-spec-"));
|
|
82083
|
+
const specPath = path46.join(dir, "prompt.json");
|
|
82084
|
+
await writeFile24(specPath, JSON.stringify(ideaSpec(idea, idea.who.replace(/[-_]/g, " ")), null, 2), "utf8");
|
|
81951
82085
|
try {
|
|
81952
82086
|
return await baker([
|
|
81953
82087
|
"canvas",
|
|
@@ -81965,19 +82099,19 @@ async function scaffoldFromIdea(idea, slug, viewerFlags) {
|
|
|
81965
82099
|
}
|
|
81966
82100
|
}
|
|
81967
82101
|
async function recordIdea(root, slug, idea, evidence) {
|
|
81968
|
-
const dir =
|
|
81969
|
-
const definitionPath =
|
|
81970
|
-
const definition = await
|
|
82102
|
+
const dir = path46.join(root, "src", "creatives", slug);
|
|
82103
|
+
const definitionPath = path46.join(dir, "_definition.md");
|
|
82104
|
+
const definition = await readFile38(definitionPath, "utf8").catch(() => null);
|
|
81971
82105
|
if (definition !== null) {
|
|
81972
|
-
await
|
|
82106
|
+
await writeFile24(definitionPath, setDefinitionFields(definition, { ...definitionFieldsOf(idea, evidence) }), "utf8");
|
|
81973
82107
|
}
|
|
81974
|
-
const promptPath =
|
|
81975
|
-
const prompt = isVideoIdea(idea) ? null : await
|
|
81976
|
-
if (prompt !== null) await
|
|
82108
|
+
const promptPath = path46.join(dir, "prompt.json");
|
|
82109
|
+
const prompt = isVideoIdea(idea) ? null : await readFile38(promptPath, "utf8").catch(() => null);
|
|
82110
|
+
if (prompt !== null) await writeFile24(promptPath, setSpecMessage(prompt, idea.message), "utf8").catch(() => void 0);
|
|
81977
82111
|
const unsourcedCopy = prompt === null ? [] : await draftedClaims(root, prompt);
|
|
81978
82112
|
if (idea.status === "idea") {
|
|
81979
|
-
const ideaPath =
|
|
81980
|
-
await
|
|
82113
|
+
const ideaPath = path46.join(root, IDEAS_DIR, `${idea.id}.md`);
|
|
82114
|
+
await writeFile24(ideaPath, setDefinitionFields(await readFile38(ideaPath, "utf8"), { status: "built" }), "utf8");
|
|
81981
82115
|
}
|
|
81982
82116
|
await baker(["canvas", "sync-definition", dir]);
|
|
81983
82117
|
return { dir, unsourcedCopy };
|
|
@@ -82063,7 +82197,7 @@ var makeCommand = defineCommand159({
|
|
|
82063
82197
|
reference,
|
|
82064
82198
|
...fromIdea ? { fromIdea } : {},
|
|
82065
82199
|
viewer: opts.adset ? { adset: opts.adset } : { temperature },
|
|
82066
|
-
canvasPath:
|
|
82200
|
+
canvasPath: path46.relative(root, String(scaffold.canvas_path ?? path46.join(dir, `${slug}.canvas.json`))),
|
|
82067
82201
|
scaffoldHints: Array.isArray(scaffold.hints) ? scaffold.hints : [],
|
|
82068
82202
|
unsourcedCopy
|
|
82069
82203
|
})
|
|
@@ -82076,7 +82210,7 @@ import { Buffer as Buffer2 } from "buffer";
|
|
|
82076
82210
|
import { defineCommand as defineCommand160 } from "citty";
|
|
82077
82211
|
|
|
82078
82212
|
// src/commands/images/api.ts
|
|
82079
|
-
import { readFile as
|
|
82213
|
+
import { readFile as readFile39 } from "fs/promises";
|
|
82080
82214
|
import { basename, extname } from "path";
|
|
82081
82215
|
var imageProcessingTimeoutMs = 18e4;
|
|
82082
82216
|
var imageReadyPollIntervalMs = 2e3;
|
|
@@ -82090,7 +82224,7 @@ var mimeMap = {
|
|
|
82090
82224
|
".avif": "image/avif"
|
|
82091
82225
|
};
|
|
82092
82226
|
var defaultImageApiDeps = {
|
|
82093
|
-
readFile:
|
|
82227
|
+
readFile: readFile39,
|
|
82094
82228
|
post: apiPost,
|
|
82095
82229
|
get: apiGet,
|
|
82096
82230
|
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
|
|
@@ -82156,7 +82290,7 @@ async function waitForReadyImage(deps, imageId, opts = {}) {
|
|
|
82156
82290
|
}
|
|
82157
82291
|
|
|
82158
82292
|
// src/commands/videos/api.ts
|
|
82159
|
-
import { readFile as
|
|
82293
|
+
import { readFile as readFile40 } from "fs/promises";
|
|
82160
82294
|
import { basename as basename2, extname as extname2 } from "path";
|
|
82161
82295
|
var videoProcessingTimeoutMs = 18e4;
|
|
82162
82296
|
var videoReadyPollIntervalMs = 3e3;
|
|
@@ -82168,7 +82302,7 @@ var mimeMap2 = {
|
|
|
82168
82302
|
".mkv": "video/x-matroska"
|
|
82169
82303
|
};
|
|
82170
82304
|
var defaultVideoApiDeps = {
|
|
82171
|
-
readFile:
|
|
82305
|
+
readFile: readFile40,
|
|
82172
82306
|
post: apiPost,
|
|
82173
82307
|
get: apiGet,
|
|
82174
82308
|
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
|
|
@@ -82261,6 +82395,11 @@ var ARGS13 = {
|
|
|
82261
82395
|
type: "string",
|
|
82262
82396
|
description: "With --run-id and no file: which of the run's outputs to save when it made several, by label (e.g. 4:5), slot, or number (1 = first).",
|
|
82263
82397
|
required: false
|
|
82398
|
+
},
|
|
82399
|
+
approved: {
|
|
82400
|
+
type: "boolean",
|
|
82401
|
+
description: `The person's own message in this turn asked to save it ("make it and keep it"). Needed only for a version rendered in this same turn, which they have not seen yet; never pass it on your own judgment.`,
|
|
82402
|
+
required: false
|
|
82264
82403
|
}
|
|
82265
82404
|
};
|
|
82266
82405
|
registerSchema({ command: "creatives.publish", description: DESCRIPTION11, args: ARGS13 });
|
|
@@ -82332,6 +82471,23 @@ function chooseOutput(run3, choice) {
|
|
|
82332
82471
|
return found;
|
|
82333
82472
|
}
|
|
82334
82473
|
var EXTENSION = { "image/png": ".png", "image/jpeg": ".jpg", "image/webp": ".webp" };
|
|
82474
|
+
function turnStartedAt(turnId) {
|
|
82475
|
+
const match = turnId?.match(/:(\d+)$/);
|
|
82476
|
+
return match ? Number(match[1]) : void 0;
|
|
82477
|
+
}
|
|
82478
|
+
function refuseUnseen(run3, turnId, approved) {
|
|
82479
|
+
const started = turnStartedAt(turnId);
|
|
82480
|
+
if (approved || started === void 0 || run3.createdAt < started) return;
|
|
82481
|
+
throw new ApiError(
|
|
82482
|
+
"VALIDATION_ERROR",
|
|
82483
|
+
`Run ${run3.runId} was rendered in this turn, so the person has not seen it yet. Show it and save it once they approve it in a later message; pass --approved only when their own message asked to save it.`
|
|
82484
|
+
);
|
|
82485
|
+
}
|
|
82486
|
+
async function refuseUnseenFile(args8, deps) {
|
|
82487
|
+
if (!args8.runId || args8.turnId === void 0 || args8.approved === true) return;
|
|
82488
|
+
const run3 = await fetchRunRecord(args8.runId, deps.post);
|
|
82489
|
+
if (run3) refuseUnseen(run3, args8.turnId, false);
|
|
82490
|
+
}
|
|
82335
82491
|
async function uploadRunOutput(args8, deps) {
|
|
82336
82492
|
const run3 = await fetchRunRecord(args8.runId, deps.post);
|
|
82337
82493
|
if (!run3) {
|
|
@@ -82340,6 +82496,7 @@ async function uploadRunOutput(args8, deps) {
|
|
|
82340
82496
|
`No run "${args8.runId}" in this company's run history. Pass the image file instead.`
|
|
82341
82497
|
);
|
|
82342
82498
|
}
|
|
82499
|
+
refuseUnseen(run3, args8.turnId, args8.approved);
|
|
82343
82500
|
const chosen = chooseOutput(run3, args8.output);
|
|
82344
82501
|
const bytes = await (deps.download ?? downloadBytes)(String(chosen.url));
|
|
82345
82502
|
if (sha256Hex(bytes) !== chosen.sha256) {
|
|
@@ -82369,6 +82526,7 @@ async function publishCreative(args8, deps = defaultImageApiDeps) {
|
|
|
82369
82526
|
const descriptionContext = args8.context ?? `Static ad creative: ${title}`;
|
|
82370
82527
|
let upload;
|
|
82371
82528
|
if (args8.file) {
|
|
82529
|
+
await refuseUnseenFile(args8, deps);
|
|
82372
82530
|
upload = await uploadLocalImage(
|
|
82373
82531
|
{
|
|
82374
82532
|
file: args8.file,
|
|
@@ -82379,7 +82537,16 @@ async function publishCreative(args8, deps = defaultImageApiDeps) {
|
|
|
82379
82537
|
deps
|
|
82380
82538
|
);
|
|
82381
82539
|
} else if (args8.runId) {
|
|
82382
|
-
upload = await uploadRunOutput(
|
|
82540
|
+
upload = await uploadRunOutput(
|
|
82541
|
+
{
|
|
82542
|
+
runId: args8.runId,
|
|
82543
|
+
output: args8.output,
|
|
82544
|
+
descriptionContext,
|
|
82545
|
+
turnId: args8.turnId,
|
|
82546
|
+
approved: args8.approved === true
|
|
82547
|
+
},
|
|
82548
|
+
deps
|
|
82549
|
+
);
|
|
82383
82550
|
} else {
|
|
82384
82551
|
throw new ApiError("VALIDATION_ERROR", "Pass the image file, or --run-id to save the run's own output");
|
|
82385
82552
|
}
|
|
@@ -82465,7 +82632,12 @@ var publishCommand = defineCommand160({
|
|
|
82465
82632
|
writeJson(await publishVideoCreative({ ...publishArgs, file }));
|
|
82466
82633
|
return;
|
|
82467
82634
|
}
|
|
82468
|
-
const data = await publishCreative({
|
|
82635
|
+
const data = await publishCreative({
|
|
82636
|
+
...publishArgs,
|
|
82637
|
+
output: args8.output,
|
|
82638
|
+
turnId: optionalTurnId(),
|
|
82639
|
+
approved: args8.approved === true
|
|
82640
|
+
});
|
|
82469
82641
|
writeJson({ ok: true, data });
|
|
82470
82642
|
} catch (err) {
|
|
82471
82643
|
if (err instanceof ApiError) {
|
|
@@ -82565,7 +82737,7 @@ Full guide: __tooling__/docs/tools/baker/creatives.md`
|
|
|
82565
82737
|
});
|
|
82566
82738
|
|
|
82567
82739
|
// ../api/src/credentials/contract.ts
|
|
82568
|
-
import { z as
|
|
82740
|
+
import { z as z70 } from "zod";
|
|
82569
82741
|
|
|
82570
82742
|
// ../../.pnpm-store/v10/links/@/tldts-core/7.4.15/00637277cac46663a792d6ddb7fbb80e2995ea75c3d789d599e3f6df41962ed7/node_modules/tldts-core/dist/es6/src/domain.js
|
|
82571
82743
|
function shareSameDomainSuffix(hostname, vhost) {
|
|
@@ -83384,8 +83556,8 @@ var WHOLE_PLACEHOLDER_RE = new RegExp(`^${PLACEHOLDER_SOURCE}$`);
|
|
|
83384
83556
|
// ../api/src/credentials/contract.ts
|
|
83385
83557
|
var MAX_CREDENTIAL_VALUE_LENGTH = 16384;
|
|
83386
83558
|
var MAX_CREDENTIAL_HOSTS = 20;
|
|
83387
|
-
var credentialNameSchema =
|
|
83388
|
-
var credentialHostsSchema =
|
|
83559
|
+
var credentialNameSchema = z70.string().regex(new RegExp(`^${CREDENTIAL_NAME_PATTERN}$`), "Use lowercase letters, digits, - and _ (max 64).");
|
|
83560
|
+
var credentialHostsSchema = z70.array(z70.string()).max(MAX_CREDENTIAL_HOSTS).transform((hosts, ctx) => {
|
|
83389
83561
|
const patterns = [];
|
|
83390
83562
|
for (const host of hosts) {
|
|
83391
83563
|
const result = validateHostPattern(host);
|
|
@@ -83394,34 +83566,34 @@ var credentialHostsSchema = z69.array(z69.string()).max(MAX_CREDENTIAL_HOSTS).tr
|
|
|
83394
83566
|
}
|
|
83395
83567
|
return [...new Set(patterns)];
|
|
83396
83568
|
});
|
|
83397
|
-
var credentialSourceSchema =
|
|
83398
|
-
var credentialSummarySchema =
|
|
83399
|
-
name:
|
|
83400
|
-
label:
|
|
83401
|
-
placeholder:
|
|
83402
|
-
allowedHosts:
|
|
83569
|
+
var credentialSourceSchema = z70.enum(["user", "agent", "chat-detected", "requested"]);
|
|
83570
|
+
var credentialSummarySchema = z70.object({
|
|
83571
|
+
name: z70.string(),
|
|
83572
|
+
label: z70.string(),
|
|
83573
|
+
placeholder: z70.string(),
|
|
83574
|
+
allowedHosts: z70.array(z70.string()),
|
|
83403
83575
|
source: credentialSourceSchema,
|
|
83404
|
-
lastUsedAt:
|
|
83576
|
+
lastUsedAt: z70.number().nullable()
|
|
83405
83577
|
});
|
|
83406
|
-
var credentialsListResponseSchema =
|
|
83407
|
-
var credentialsPutRequestSchema =
|
|
83578
|
+
var credentialsListResponseSchema = z70.object({ credentials: z70.array(credentialSummarySchema) });
|
|
83579
|
+
var credentialsPutRequestSchema = z70.object({
|
|
83408
83580
|
name: credentialNameSchema,
|
|
83409
|
-
label:
|
|
83410
|
-
value:
|
|
83581
|
+
label: z70.string().trim().min(1).max(80).optional(),
|
|
83582
|
+
value: z70.string().min(1).max(MAX_CREDENTIAL_VALUE_LENGTH),
|
|
83411
83583
|
allowedHosts: credentialHostsSchema
|
|
83412
83584
|
});
|
|
83413
|
-
var credentialsPutResponseSchema =
|
|
83414
|
-
name:
|
|
83415
|
-
placeholder:
|
|
83416
|
-
replaced:
|
|
83585
|
+
var credentialsPutResponseSchema = z70.object({
|
|
83586
|
+
name: z70.string(),
|
|
83587
|
+
placeholder: z70.string(),
|
|
83588
|
+
replaced: z70.boolean()
|
|
83417
83589
|
});
|
|
83418
|
-
var credentialsDeleteRequestSchema =
|
|
83419
|
-
var credentialsDeleteResponseSchema =
|
|
83420
|
-
var brokerResolveResponseSchema =
|
|
83421
|
-
credentials:
|
|
83590
|
+
var credentialsDeleteRequestSchema = z70.object({ name: credentialNameSchema });
|
|
83591
|
+
var credentialsDeleteResponseSchema = z70.object({ name: z70.string(), deleted: z70.literal(true) });
|
|
83592
|
+
var brokerResolveResponseSchema = z70.object({
|
|
83593
|
+
credentials: z70.array(z70.object({ name: z70.string(), value: z70.string(), allowedHosts: z70.array(z70.string()) }))
|
|
83422
83594
|
});
|
|
83423
|
-
var brokerUsageRequestSchema =
|
|
83424
|
-
uses:
|
|
83595
|
+
var brokerUsageRequestSchema = z70.object({
|
|
83596
|
+
uses: z70.array(z70.object({ name: credentialNameSchema, host: z70.string().max(253) })).max(100)
|
|
83425
83597
|
});
|
|
83426
83598
|
|
|
83427
83599
|
// ../api/src/credentials/rules.ts
|
|
@@ -83772,12 +83944,12 @@ Full guide: ${DOC}`
|
|
|
83772
83944
|
import { defineCommand as defineCommand164 } from "citty";
|
|
83773
83945
|
|
|
83774
83946
|
// src/commands/experiment/composition.ts
|
|
83775
|
-
import { readdir as readdir19, readFile as
|
|
83776
|
-
import
|
|
83947
|
+
import { readdir as readdir19, readFile as readFile42 } from "fs/promises";
|
|
83948
|
+
import path48 from "path";
|
|
83777
83949
|
|
|
83778
83950
|
// src/commands/landing/import-graph.ts
|
|
83779
|
-
import { readdir as readdir18, readFile as
|
|
83780
|
-
import
|
|
83951
|
+
import { readdir as readdir18, readFile as readFile41, stat as stat7 } from "fs/promises";
|
|
83952
|
+
import path47 from "path";
|
|
83781
83953
|
var SOURCE_EXT = /\.(astro|ts|tsx|js|jsx|mjs|cjs|vue|svelte)$/;
|
|
83782
83954
|
var RESOLVE_CANDIDATES = [
|
|
83783
83955
|
"",
|
|
@@ -83810,7 +83982,7 @@ async function walkFiles(dir) {
|
|
|
83810
83982
|
}
|
|
83811
83983
|
const out = [];
|
|
83812
83984
|
for (const entry of entries) {
|
|
83813
|
-
const full =
|
|
83985
|
+
const full = path47.join(dir, entry.name);
|
|
83814
83986
|
if (entry.isDirectory()) {
|
|
83815
83987
|
if (entry.name === "node_modules" || entry.name === "dist") continue;
|
|
83816
83988
|
out.push(...await walkFiles(full));
|
|
@@ -83824,8 +83996,8 @@ async function resolveSpecifier(specifier, fromFile, root) {
|
|
|
83824
83996
|
const bare = specifier.split("?")[0] ?? "";
|
|
83825
83997
|
if (bare.includes("*")) return null;
|
|
83826
83998
|
let base;
|
|
83827
|
-
if (bare.startsWith("/")) base =
|
|
83828
|
-
else if (bare.startsWith(".")) base =
|
|
83999
|
+
if (bare.startsWith("/")) base = path47.join(root, bare.slice(1));
|
|
84000
|
+
else if (bare.startsWith(".")) base = path47.resolve(path47.dirname(fromFile), bare);
|
|
83829
84001
|
else return null;
|
|
83830
84002
|
for (const suffix of RESOLVE_CANDIDATES) {
|
|
83831
84003
|
const candidate = base + suffix;
|
|
@@ -83847,13 +84019,13 @@ async function importedFiles(text3, file, root) {
|
|
|
83847
84019
|
}
|
|
83848
84020
|
async function landingGraphFiles(root, slug) {
|
|
83849
84021
|
const seen = /* @__PURE__ */ new Set();
|
|
83850
|
-
const queue = await walkFiles(
|
|
84022
|
+
const queue = await walkFiles(path47.join(root, "src/pages", slug));
|
|
83851
84023
|
while (queue.length > 0) {
|
|
83852
84024
|
const file = queue.pop();
|
|
83853
84025
|
if (file === void 0 || seen.has(file)) continue;
|
|
83854
84026
|
let text3;
|
|
83855
84027
|
try {
|
|
83856
|
-
text3 = await
|
|
84028
|
+
text3 = await readFile41(file, "utf8");
|
|
83857
84029
|
} catch {
|
|
83858
84030
|
continue;
|
|
83859
84031
|
}
|
|
@@ -83862,7 +84034,7 @@ async function landingGraphFiles(root, slug) {
|
|
|
83862
84034
|
if (!seen.has(target2)) queue.push(target2);
|
|
83863
84035
|
}
|
|
83864
84036
|
}
|
|
83865
|
-
return new Set([...seen].map((abs) =>
|
|
84037
|
+
return new Set([...seen].map((abs) => path47.relative(root, abs).split(path47.sep).join("/")));
|
|
83866
84038
|
}
|
|
83867
84039
|
|
|
83868
84040
|
// src/commands/experiment/composition.ts
|
|
@@ -83907,7 +84079,7 @@ function formsOnlyIn(these, those) {
|
|
|
83907
84079
|
}
|
|
83908
84080
|
async function knownFlows(root) {
|
|
83909
84081
|
try {
|
|
83910
|
-
const entries = await readdir19(
|
|
84082
|
+
const entries = await readdir19(path48.join(root, "src/lib/flow-engine/flows"), { withFileTypes: true });
|
|
83911
84083
|
return new Set(entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name));
|
|
83912
84084
|
} catch {
|
|
83913
84085
|
return /* @__PURE__ */ new Set();
|
|
@@ -83918,7 +84090,7 @@ async function readForms(root, slug) {
|
|
|
83918
84090
|
const [files, flows] = await Promise.all([landingGraphFiles(root, slug), knownFlows(root)]);
|
|
83919
84091
|
if (flows.size === 0) return [];
|
|
83920
84092
|
const texts = await Promise.all(
|
|
83921
|
-
[...files].filter((file) => /\.(astro|ts|tsx|js|mjs)$/.test(file)).map((file) =>
|
|
84093
|
+
[...files].filter((file) => /\.(astro|ts|tsx|js|mjs)$/.test(file)).map((file) => readFile42(path48.join(root, file), "utf8"))
|
|
83922
84094
|
);
|
|
83923
84095
|
return formsReferenced(texts, flows);
|
|
83924
84096
|
} catch {
|
|
@@ -83928,8 +84100,8 @@ async function readForms(root, slug) {
|
|
|
83928
84100
|
|
|
83929
84101
|
// src/commands/experiment/fold.ts
|
|
83930
84102
|
import { statSync } from "fs";
|
|
83931
|
-
import { cp as cp3, mkdir as
|
|
83932
|
-
import
|
|
84103
|
+
import { cp as cp3, mkdir as mkdir15, readdir as readdir20, readFile as readFile43, rm as rm8, stat as stat8, writeFile as writeFile25 } from "fs/promises";
|
|
84104
|
+
import path50 from "path";
|
|
83933
84105
|
|
|
83934
84106
|
// ../api/src/experiments/variants.ts
|
|
83935
84107
|
var VARIANT_SEPARATOR = "--";
|
|
@@ -83954,21 +84126,21 @@ function nextVariantNumber(page, existingSlugs, archived) {
|
|
|
83954
84126
|
}
|
|
83955
84127
|
|
|
83956
84128
|
// src/commands/landing/repoint.ts
|
|
83957
|
-
import
|
|
84129
|
+
import path49 from "path";
|
|
83958
84130
|
var QUOTED_RELATIVE = /(['"])(\.\.?\/[^'"]*)\1/g;
|
|
83959
84131
|
function isInside3(abs, dir) {
|
|
83960
|
-
return abs === dir || abs.startsWith(dir +
|
|
84132
|
+
return abs === dir || abs.startsWith(dir + path49.sep);
|
|
83961
84133
|
}
|
|
83962
84134
|
function toSpecifier(from, to) {
|
|
83963
|
-
const rel =
|
|
84135
|
+
const rel = path49.relative(from, to).split(path49.sep).join("/");
|
|
83964
84136
|
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
83965
84137
|
}
|
|
83966
84138
|
function repointSpecifier(spec, opts) {
|
|
83967
84139
|
if (!spec.startsWith(".")) return spec;
|
|
83968
|
-
const abs =
|
|
84140
|
+
const abs = path49.resolve(opts.fromDir, spec);
|
|
83969
84141
|
if (opts.forkedTargets?.has(abs)) {
|
|
83970
|
-
const withinControl =
|
|
83971
|
-
return toSpecifier(opts.toDir,
|
|
84142
|
+
const withinControl = path49.relative(opts.controlDir, abs);
|
|
84143
|
+
return toSpecifier(opts.toDir, path49.resolve(opts.variantDir, withinControl));
|
|
83972
84144
|
}
|
|
83973
84145
|
if (!isInside3(abs, opts.controlDir)) return spec;
|
|
83974
84146
|
return toSpecifier(opts.toDir, abs);
|
|
@@ -83980,9 +84152,9 @@ function repointFile(text3, opts) {
|
|
|
83980
84152
|
}
|
|
83981
84153
|
function repointMoved(text3, opts) {
|
|
83982
84154
|
return text3.replace(QUOTED_RELATIVE, (_match, quote, spec) => {
|
|
83983
|
-
const abs =
|
|
83984
|
-
const within =
|
|
83985
|
-
const target2 = within.startsWith("..") ||
|
|
84155
|
+
const abs = path49.resolve(opts.fromDir, spec);
|
|
84156
|
+
const within = path49.relative(opts.movedFrom, abs);
|
|
84157
|
+
const target2 = within.startsWith("..") || path49.isAbsolute(within) ? abs : path49.resolve(opts.movedTo, within);
|
|
83986
84158
|
return `${quote}${toSpecifier(opts.toDir, target2)}${quote}`;
|
|
83987
84159
|
});
|
|
83988
84160
|
}
|
|
@@ -83991,7 +84163,7 @@ function repointMoved(text3, opts) {
|
|
|
83991
84163
|
var ARCHIVE_ROOT = "src/_variants";
|
|
83992
84164
|
var QUOTED_RELATIVE2 = /(['"])(\.\.?\/[^'"]*)\1/g;
|
|
83993
84165
|
function posix2(p) {
|
|
83994
|
-
return p.split(
|
|
84166
|
+
return p.split(path50.sep).join("/");
|
|
83995
84167
|
}
|
|
83996
84168
|
async function isDir(p) {
|
|
83997
84169
|
try {
|
|
@@ -84010,39 +84182,39 @@ async function filesUnder(dir, prefix = "") {
|
|
|
84010
84182
|
const out = [];
|
|
84011
84183
|
for (const entry of entries) {
|
|
84012
84184
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
84013
|
-
if (entry.isDirectory()) out.push(...await filesUnder(
|
|
84185
|
+
if (entry.isDirectory()) out.push(...await filesUnder(path50.join(dir, entry.name), rel));
|
|
84014
84186
|
else out.push(rel);
|
|
84015
84187
|
}
|
|
84016
84188
|
return out.sort();
|
|
84017
84189
|
}
|
|
84018
84190
|
var TEXT_EXT = /\.(astro|ts|tsx|js|jsx|mjs|cjs|vue|svelte|md|json|css)$/;
|
|
84019
84191
|
function archiveRelPath(abs, base) {
|
|
84020
|
-
if (!abs.startsWith(base +
|
|
84021
|
-
return posix2(
|
|
84192
|
+
if (!abs.startsWith(base + path50.sep)) return null;
|
|
84193
|
+
return posix2(path50.relative(base, abs));
|
|
84022
84194
|
}
|
|
84023
84195
|
async function archiveArm(opts) {
|
|
84024
84196
|
const { root, armDir, pageDir, archiveDir, armSlug } = opts;
|
|
84025
84197
|
const targets = await archiveContents({ root, armDir, pageDir, armSlug });
|
|
84026
|
-
const archivedBySource = new Map([...targets].map(([rel, abs]) => [abs,
|
|
84198
|
+
const archivedBySource = new Map([...targets].map(([rel, abs]) => [abs, path50.join(archiveDir, rel)]));
|
|
84027
84199
|
const written = [];
|
|
84028
84200
|
for (const [rel, abs] of [...targets].sort()) {
|
|
84029
|
-
const dest =
|
|
84030
|
-
await
|
|
84201
|
+
const dest = path50.join(archiveDir, rel);
|
|
84202
|
+
await mkdir15(path50.dirname(dest), { recursive: true });
|
|
84031
84203
|
if (!TEXT_EXT.test(rel)) {
|
|
84032
84204
|
await cp3(abs, dest);
|
|
84033
84205
|
} else {
|
|
84034
|
-
const text3 = await
|
|
84035
|
-
await
|
|
84206
|
+
const text3 = await readFile43(abs, "utf8");
|
|
84207
|
+
await writeFile25(dest, repointIntoArchive(text3, abs, dest, archivedBySource), "utf8");
|
|
84036
84208
|
}
|
|
84037
|
-
written.push(posix2(
|
|
84209
|
+
written.push(posix2(path50.relative(root, dest)));
|
|
84038
84210
|
}
|
|
84039
84211
|
return written;
|
|
84040
84212
|
}
|
|
84041
84213
|
async function archiveContents(opts) {
|
|
84042
84214
|
const { root, armDir, pageDir, armSlug } = opts;
|
|
84043
84215
|
const graph = await landingGraphFiles(root, armSlug);
|
|
84044
|
-
const reachable = new Set([...graph].map((rel) =>
|
|
84045
|
-
for (const rel of await filesUnder(armDir)) reachable.add(
|
|
84216
|
+
const reachable = new Set([...graph].map((rel) => path50.resolve(root, rel)));
|
|
84217
|
+
for (const rel of await filesUnder(armDir)) reachable.add(path50.join(armDir, rel));
|
|
84046
84218
|
const sorted = [...reachable].sort();
|
|
84047
84219
|
const targets = /* @__PURE__ */ new Map();
|
|
84048
84220
|
for (const base of [pageDir, armDir]) {
|
|
@@ -84055,14 +84227,14 @@ async function archiveContents(opts) {
|
|
|
84055
84227
|
return targets;
|
|
84056
84228
|
}
|
|
84057
84229
|
function repointIntoArchive(text3, sourceAbs, destAbs, archivedBySource) {
|
|
84058
|
-
const fromDir =
|
|
84059
|
-
const toDir =
|
|
84230
|
+
const fromDir = path50.dirname(sourceAbs);
|
|
84231
|
+
const toDir = path50.dirname(destAbs);
|
|
84060
84232
|
return text3.replace(QUOTED_RELATIVE2, (match, quote, spec, offset) => {
|
|
84061
84233
|
const [specPath, suffix] = splitSuffix(spec);
|
|
84062
|
-
const target2 =
|
|
84234
|
+
const target2 = path50.resolve(fromDir, specPath);
|
|
84063
84235
|
const resolved = resolveInArchive(target2, archivedBySource) ?? resolveOnDisk(target2, isImportPosition(text3, offset));
|
|
84064
84236
|
if (resolved === null) return match;
|
|
84065
|
-
const rel = posix2(
|
|
84237
|
+
const rel = posix2(path50.relative(toDir, resolved));
|
|
84066
84238
|
return `${quote}${rel.startsWith(".") ? rel : `./${rel}`}${suffix}${quote}`;
|
|
84067
84239
|
});
|
|
84068
84240
|
}
|
|
@@ -84096,25 +84268,25 @@ async function promoteVariantFiles(opts) {
|
|
|
84096
84268
|
const written = [];
|
|
84097
84269
|
for (const rel of await filesUnder(variantDir)) {
|
|
84098
84270
|
if (rel === "_definition.md") continue;
|
|
84099
|
-
const from =
|
|
84100
|
-
const to =
|
|
84101
|
-
await
|
|
84271
|
+
const from = path50.join(variantDir, rel);
|
|
84272
|
+
const to = path50.join(pageDir, rel);
|
|
84273
|
+
await mkdir15(path50.dirname(to), { recursive: true });
|
|
84102
84274
|
if (!TEXT_EXT.test(rel)) {
|
|
84103
84275
|
await cp3(from, to);
|
|
84104
84276
|
} else {
|
|
84105
|
-
const text3 = await
|
|
84106
|
-
await
|
|
84277
|
+
const text3 = await readFile43(from, "utf8");
|
|
84278
|
+
await writeFile25(
|
|
84107
84279
|
to,
|
|
84108
84280
|
repointMoved(text3, {
|
|
84109
|
-
fromDir:
|
|
84110
|
-
toDir:
|
|
84281
|
+
fromDir: path50.dirname(from),
|
|
84282
|
+
toDir: path50.dirname(to),
|
|
84111
84283
|
movedFrom: variantDir,
|
|
84112
84284
|
movedTo: pageDir
|
|
84113
84285
|
}),
|
|
84114
84286
|
"utf8"
|
|
84115
84287
|
);
|
|
84116
84288
|
}
|
|
84117
|
-
written.push(posix2(
|
|
84289
|
+
written.push(posix2(path50.relative(root, to)));
|
|
84118
84290
|
}
|
|
84119
84291
|
return written;
|
|
84120
84292
|
}
|
|
@@ -84138,8 +84310,8 @@ function archiveRecord(target2, variantNumber) {
|
|
|
84138
84310
|
`;
|
|
84139
84311
|
}
|
|
84140
84312
|
async function foldExperiment(root, target2) {
|
|
84141
|
-
const pageDir =
|
|
84142
|
-
const variantDir =
|
|
84313
|
+
const pageDir = path50.resolve(root, "src/pages", target2.landingSlug);
|
|
84314
|
+
const variantDir = path50.resolve(root, "src/pages", target2.variantSlug);
|
|
84143
84315
|
const variantNumber = variantNumberOf(target2.variantSlug);
|
|
84144
84316
|
if (variantNumber === null) {
|
|
84145
84317
|
return {
|
|
@@ -84153,9 +84325,9 @@ async function foldExperiment(root, target2) {
|
|
|
84153
84325
|
return { error: `src/pages/${target2.variantSlug}/ is already gone \u2014 this test looks folded in already` };
|
|
84154
84326
|
}
|
|
84155
84327
|
const archiveKey = target2.landingInternalId ?? target2.landingSlug;
|
|
84156
|
-
const archiveDir =
|
|
84328
|
+
const archiveDir = path50.resolve(root, ARCHIVE_ROOT, archiveKey, String(variantNumber));
|
|
84157
84329
|
await rm8(archiveDir, { recursive: true, force: true });
|
|
84158
|
-
await
|
|
84330
|
+
await mkdir15(archiveDir, { recursive: true });
|
|
84159
84331
|
const written = await archiveArm({
|
|
84160
84332
|
root,
|
|
84161
84333
|
armDir: target2.survivor === "variant" ? pageDir : variantDir,
|
|
@@ -84163,8 +84335,8 @@ async function foldExperiment(root, target2) {
|
|
|
84163
84335
|
archiveDir,
|
|
84164
84336
|
armSlug: target2.survivor === "variant" ? target2.landingSlug : target2.variantSlug
|
|
84165
84337
|
});
|
|
84166
|
-
await
|
|
84167
|
-
written.push(posix2(
|
|
84338
|
+
await writeFile25(path50.join(archiveDir, "_variant.json"), archiveRecord(target2, variantNumber), "utf8");
|
|
84339
|
+
written.push(posix2(path50.join(ARCHIVE_ROOT, archiveKey, String(variantNumber), "_variant.json")));
|
|
84168
84340
|
if (target2.survivor === "variant") {
|
|
84169
84341
|
written.push(...await promoteVariantFiles({ root, pageDir, variantDir }));
|
|
84170
84342
|
}
|
|
@@ -84172,29 +84344,29 @@ async function foldExperiment(root, target2) {
|
|
|
84172
84344
|
return {
|
|
84173
84345
|
experimentId: target2.experimentId,
|
|
84174
84346
|
page: target2.landingSlug,
|
|
84175
|
-
archivedTo: posix2(
|
|
84347
|
+
archivedTo: posix2(path50.join(ARCHIVE_ROOT, archiveKey, String(variantNumber))),
|
|
84176
84348
|
written,
|
|
84177
84349
|
removed: [`src/pages/${target2.variantSlug}`]
|
|
84178
84350
|
};
|
|
84179
84351
|
}
|
|
84180
84352
|
|
|
84181
84353
|
// ../api/src/experiments/composition.ts
|
|
84182
|
-
import { z as
|
|
84183
|
-
var experimentCompositionSchema =
|
|
84354
|
+
import { z as z71 } from "zod";
|
|
84355
|
+
var experimentCompositionSchema = z71.object({
|
|
84184
84356
|
/** Components the variant owns its own copy of — the thing under test. */
|
|
84185
|
-
forked:
|
|
84357
|
+
forked: z71.array(z71.string().max(300)).max(50),
|
|
84186
84358
|
/** Components both pages render from one file, so an edit reaches both arms. */
|
|
84187
|
-
shared:
|
|
84359
|
+
shared: z71.array(z71.string().max(300)).max(200)
|
|
84188
84360
|
});
|
|
84189
84361
|
|
|
84190
84362
|
// ../api/src/experiments/goal.ts
|
|
84191
|
-
import { z as
|
|
84363
|
+
import { z as z72 } from "zod";
|
|
84192
84364
|
var GOAL_KEY_HELP = "A goal is one event, by its key \u2014 `form:<flow>:<node>:<trigger>`, `submit:<flow>`, `page:<name>` or `exit:<host>`. `baker analytics conversions --candidates` lists the ones these pages produce";
|
|
84193
|
-
var experimentGoalSchema =
|
|
84365
|
+
var experimentGoalSchema = z72.string().trim().min(1).max(400).refine((key) => parseEventKey(key) !== null, { message: GOAL_KEY_HELP });
|
|
84194
84366
|
|
|
84195
84367
|
// ../api/src/experiments/hypothesis.ts
|
|
84196
|
-
import { z as
|
|
84197
|
-
var experimentEvidenceSourceSchema =
|
|
84368
|
+
import { z as z73 } from "zod";
|
|
84369
|
+
var experimentEvidenceSourceSchema = z73.enum([
|
|
84198
84370
|
"baker_analytics",
|
|
84199
84371
|
"clarity",
|
|
84200
84372
|
"ads",
|
|
@@ -84204,17 +84376,17 @@ var experimentEvidenceSourceSchema = z72.enum([
|
|
|
84204
84376
|
"client",
|
|
84205
84377
|
"hunch"
|
|
84206
84378
|
]);
|
|
84207
|
-
var experimentEvidenceSchema =
|
|
84379
|
+
var experimentEvidenceSchema = z73.object({
|
|
84208
84380
|
source: experimentEvidenceSourceSchema,
|
|
84209
|
-
note:
|
|
84381
|
+
note: z73.string().min(1).max(200),
|
|
84210
84382
|
/**
|
|
84211
84383
|
* Where to go and look. Not a `z.url()` — a report inside Baker is reached by
|
|
84212
84384
|
* a relative path, and rejecting those would push every in-product citation
|
|
84213
84385
|
* into the note where nothing can link it.
|
|
84214
84386
|
*/
|
|
84215
|
-
href:
|
|
84387
|
+
href: z73.string().max(500).optional()
|
|
84216
84388
|
});
|
|
84217
|
-
var experimentHypothesisSchema =
|
|
84389
|
+
var experimentHypothesisSchema = z73.object({
|
|
84218
84390
|
/**
|
|
84219
84391
|
* The observation. The one genuinely new field, and the reason for the rest.
|
|
84220
84392
|
*
|
|
@@ -84222,7 +84394,7 @@ var experimentHypothesisSchema = z72.object({
|
|
|
84222
84394
|
* result to be evidence against — which is how a testing programme ends up as
|
|
84223
84395
|
* a list of tests instead of a list of things learned.
|
|
84224
84396
|
*/
|
|
84225
|
-
because:
|
|
84397
|
+
because: z73.string().min(1).max(300),
|
|
84226
84398
|
/**
|
|
84227
84399
|
* The change, in the client's words.
|
|
84228
84400
|
*
|
|
@@ -84230,12 +84402,12 @@ var experimentHypothesisSchema = z72.object({
|
|
|
84230
84402
|
* a person reads. The two are allowed to differ in wording and cannot differ
|
|
84231
84403
|
* in fact, because the brief shows both.
|
|
84232
84404
|
*/
|
|
84233
|
-
change:
|
|
84405
|
+
change: z73.string().min(1).max(200),
|
|
84234
84406
|
/** Which way the goal is expected to move. See the note at the top of this file. */
|
|
84235
|
-
expect:
|
|
84236
|
-
evidence:
|
|
84407
|
+
expect: z73.enum(["increase", "decrease"]).default("increase"),
|
|
84408
|
+
evidence: z73.array(experimentEvidenceSchema).max(5).optional()
|
|
84237
84409
|
});
|
|
84238
|
-
var experimentHypothesisOutcomeSchema =
|
|
84410
|
+
var experimentHypothesisOutcomeSchema = z73.enum([
|
|
84239
84411
|
"supported",
|
|
84240
84412
|
"refuted_opposite",
|
|
84241
84413
|
"refuted_no_effect",
|
|
@@ -84262,13 +84434,13 @@ function roughCount(count2) {
|
|
|
84262
84434
|
}
|
|
84263
84435
|
|
|
84264
84436
|
// ../api/src/experiments/wire.ts
|
|
84265
|
-
import { z as
|
|
84266
|
-
var slugSchema =
|
|
84267
|
-
var variantSlugSchema =
|
|
84437
|
+
import { z as z74 } from "zod";
|
|
84438
|
+
var slugSchema = z74.string().min(1).max(120).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Use a page slug, like `oferta`");
|
|
84439
|
+
var variantSlugSchema = z74.string().min(1).max(130).refine((slug) => {
|
|
84268
84440
|
const page = variantOfSlug(slug);
|
|
84269
84441
|
return page !== null && slugSchema.safeParse(page).success;
|
|
84270
84442
|
}, "Use the variant's slug, `<page>--<n>` \u2014 `baker landing variant <page>` chooses it");
|
|
84271
|
-
var experimentPlanRequestSchema =
|
|
84443
|
+
var experimentPlanRequestSchema = z74.object({
|
|
84272
84444
|
landingSlug: slugSchema,
|
|
84273
84445
|
variantSlug: variantSlugSchema,
|
|
84274
84446
|
/**
|
|
@@ -84297,7 +84469,7 @@ var experimentPlanRequestSchema = z73.object({
|
|
|
84297
84469
|
* quarter of a million visitors per arm; `plan` will say so rather than
|
|
84298
84470
|
* running it.
|
|
84299
84471
|
*/
|
|
84300
|
-
minDetectableRelativeLift:
|
|
84472
|
+
minDetectableRelativeLift: z74.number().gt(0).max(10).optional(),
|
|
84301
84473
|
/**
|
|
84302
84474
|
* The share of visitors expected to do the goal, as a fraction — `0.05` is
|
|
84303
84475
|
* 5% — for a page whose history cannot say.
|
|
@@ -84308,14 +84480,14 @@ var experimentPlanRequestSchema = z73.object({
|
|
|
84308
84480
|
* Either way the brief says which, because the sizing is a guide and never a
|
|
84309
84481
|
* finish line — the test runs until the numbers are clear.
|
|
84310
84482
|
*/
|
|
84311
|
-
baselineRate:
|
|
84483
|
+
baselineRate: z74.number().gt(0).lt(1).optional()
|
|
84312
84484
|
});
|
|
84313
|
-
var experimentTrafficSchema =
|
|
84314
|
-
var experimentBaselineSourceSchema =
|
|
84315
|
-
var experimentSizedOnSchema =
|
|
84316
|
-
visitors:
|
|
84317
|
-
convertingVisitors:
|
|
84318
|
-
windowDays:
|
|
84485
|
+
var experimentTrafficSchema = z74.enum(["none", "low", "enough"]);
|
|
84486
|
+
var experimentBaselineSourceSchema = z74.enum(["page", "company", "assumed"]);
|
|
84487
|
+
var experimentSizedOnSchema = z74.object({
|
|
84488
|
+
visitors: z74.number(),
|
|
84489
|
+
convertingVisitors: z74.number(),
|
|
84490
|
+
windowDays: z74.number()
|
|
84319
84491
|
});
|
|
84320
84492
|
var experimentStartRequestSchema = experimentPlanRequestSchema.extend({
|
|
84321
84493
|
hypothesis: experimentHypothesisSchema,
|
|
@@ -84329,8 +84501,8 @@ var experimentStartRequestSchema = experimentPlanRequestSchema.extend({
|
|
|
84329
84501
|
* loser under the wrong key. Optional because a workspace that cannot read
|
|
84330
84502
|
* its own definition must still be able to start a test.
|
|
84331
84503
|
*/
|
|
84332
|
-
landingInternalId:
|
|
84333
|
-
variantInternalId:
|
|
84504
|
+
landingInternalId: z74.string().min(1).max(64).optional(),
|
|
84505
|
+
variantInternalId: z74.string().min(1).max(64).optional(),
|
|
84334
84506
|
/**
|
|
84335
84507
|
* What the two arms are made of, read off the import graph by the caller at
|
|
84336
84508
|
* the moment the test starts.
|
|
@@ -84342,21 +84514,21 @@ var experimentStartRequestSchema = experimentPlanRequestSchema.extend({
|
|
|
84342
84514
|
*/
|
|
84343
84515
|
composition: experimentCompositionSchema.optional()
|
|
84344
84516
|
});
|
|
84345
|
-
var experimentKeepSchema =
|
|
84346
|
-
var experimentEndReasonSchema =
|
|
84347
|
-
var experimentLearningSchema =
|
|
84348
|
-
var experimentFinishRequestSchema =
|
|
84349
|
-
experimentId:
|
|
84517
|
+
var experimentKeepSchema = z74.enum(["original", "variant"]);
|
|
84518
|
+
var experimentEndReasonSchema = z74.enum(["numbers_clear", "not_settling", "page_changing", "test_flawed"]);
|
|
84519
|
+
var experimentLearningSchema = z74.string().trim().min(1).max(2e3);
|
|
84520
|
+
var experimentFinishRequestSchema = z74.object({
|
|
84521
|
+
experimentId: z74.string().min(1).max(64),
|
|
84350
84522
|
keep: experimentKeepSchema,
|
|
84351
84523
|
/** Why it is being ended. The dashboard always asks; the agent should say. */
|
|
84352
84524
|
reason: experimentEndReasonSchema.optional(),
|
|
84353
84525
|
learning: experimentLearningSchema.optional()
|
|
84354
84526
|
});
|
|
84355
|
-
var experimentPauseRequestSchema =
|
|
84356
|
-
experimentId:
|
|
84527
|
+
var experimentPauseRequestSchema = z74.object({
|
|
84528
|
+
experimentId: z74.string().min(1).max(64)
|
|
84357
84529
|
});
|
|
84358
|
-
var experimentUpdateRequestSchema =
|
|
84359
|
-
experimentId:
|
|
84530
|
+
var experimentUpdateRequestSchema = z74.object({
|
|
84531
|
+
experimentId: z74.string().min(1).max(64),
|
|
84360
84532
|
because: experimentHypothesisSchema.shape.because.optional(),
|
|
84361
84533
|
change: experimentHypothesisSchema.shape.change.optional(),
|
|
84362
84534
|
evidence: experimentHypothesisSchema.shape.evidence,
|
|
@@ -84366,26 +84538,26 @@ var experimentUpdateRequestSchema = z73.object({
|
|
|
84366
84538
|
* default still fires, so every wording-only update arrived carrying a
|
|
84367
84539
|
* direction and was refused for trying to change it.
|
|
84368
84540
|
*/
|
|
84369
|
-
expect:
|
|
84541
|
+
expect: z74.enum(["increase", "decrease"]).optional(),
|
|
84370
84542
|
/** Staged tests only. */
|
|
84371
84543
|
goal: experimentGoalSchema.optional(),
|
|
84372
84544
|
/** Staged tests only. The new version's own event, when it differs. */
|
|
84373
84545
|
variantGoal: experimentGoalSchema.optional(),
|
|
84374
84546
|
/** Staged tests only. */
|
|
84375
|
-
minDetectableRelativeLift:
|
|
84547
|
+
minDetectableRelativeLift: z74.number().gt(0).max(10).optional(),
|
|
84376
84548
|
/** Finished tests only: what the test taught, written or corrected after the fact. */
|
|
84377
84549
|
learning: experimentLearningSchema.optional()
|
|
84378
84550
|
});
|
|
84379
|
-
var experimentUpdateResponseSchema =
|
|
84551
|
+
var experimentUpdateResponseSchema = z74.object({
|
|
84380
84552
|
/** True when the test had not been published, so its whole plan was re-sized. */
|
|
84381
|
-
staged:
|
|
84382
|
-
hypothesisStatement:
|
|
84553
|
+
staged: z74.boolean(),
|
|
84554
|
+
hypothesisStatement: z74.string()
|
|
84383
84555
|
});
|
|
84384
|
-
var experimentFoldRequestSchema =
|
|
84556
|
+
var experimentFoldRequestSchema = z74.object({
|
|
84385
84557
|
/** Which test to fold. Omitted ⇒ every fold this company owes. */
|
|
84386
|
-
experimentId:
|
|
84558
|
+
experimentId: z74.string().max(64).optional(),
|
|
84387
84559
|
/** The files are written. Baker drops the promotion once the build lands. */
|
|
84388
|
-
applied:
|
|
84560
|
+
applied: z74.boolean().optional(),
|
|
84389
84561
|
/**
|
|
84390
84562
|
* Which of the owed folds were actually written, on an `applied` call.
|
|
84391
84563
|
*
|
|
@@ -84396,68 +84568,68 @@ var experimentFoldRequestSchema = z73.object({
|
|
|
84396
84568
|
* its page served the version that LOST, with nothing owed and nothing
|
|
84397
84569
|
* logged. Omitted ⇒ everything owed, which is the whole-company case.
|
|
84398
84570
|
*/
|
|
84399
|
-
experimentIds:
|
|
84571
|
+
experimentIds: z74.array(z74.string().max(64)).max(200).optional()
|
|
84400
84572
|
});
|
|
84401
|
-
var experimentFoldTargetSchema =
|
|
84402
|
-
experimentId:
|
|
84573
|
+
var experimentFoldTargetSchema = z74.object({
|
|
84574
|
+
experimentId: z74.string(),
|
|
84403
84575
|
/** The page. Keeps its slug, its identity, its history and its ads. */
|
|
84404
|
-
landingSlug:
|
|
84576
|
+
landingSlug: z74.string(),
|
|
84405
84577
|
/** The variant folder, `<page>--<n>`. Gone from `src/pages/` afterwards. */
|
|
84406
|
-
variantSlug:
|
|
84578
|
+
variantSlug: z74.string(),
|
|
84407
84579
|
/** Which variant won, and therefore which files become the page's own. */
|
|
84408
|
-
survivor:
|
|
84580
|
+
survivor: z74.union([z74.literal("control"), z74.literal("variant")]),
|
|
84409
84581
|
/** For the archive path, which is keyed on identity rather than on a slug. */
|
|
84410
|
-
landingInternalId:
|
|
84582
|
+
landingInternalId: z74.string().nullable()
|
|
84411
84583
|
});
|
|
84412
|
-
var experimentFoldResponseSchema =
|
|
84413
|
-
owed:
|
|
84584
|
+
var experimentFoldResponseSchema = z74.object({
|
|
84585
|
+
owed: z74.array(experimentFoldTargetSchema),
|
|
84414
84586
|
/** How many were recorded as written, when `applied` was set. */
|
|
84415
|
-
recorded:
|
|
84587
|
+
recorded: z74.number().optional()
|
|
84416
84588
|
});
|
|
84417
|
-
var experimentStatusRequestSchema =
|
|
84418
|
-
experimentId:
|
|
84589
|
+
var experimentStatusRequestSchema = z74.object({
|
|
84590
|
+
experimentId: z74.string().max(64).optional(),
|
|
84419
84591
|
/** Only the tests on one page, by its slug. */
|
|
84420
|
-
landingSlug:
|
|
84421
|
-
full:
|
|
84592
|
+
landingSlug: z74.string().max(200).optional(),
|
|
84593
|
+
full: z74.boolean().optional()
|
|
84422
84594
|
});
|
|
84423
|
-
var experimentHistoryRequestSchema =
|
|
84424
|
-
landingSlug:
|
|
84425
|
-
limit:
|
|
84595
|
+
var experimentHistoryRequestSchema = z74.object({
|
|
84596
|
+
landingSlug: z74.string().max(200).optional(),
|
|
84597
|
+
limit: z74.number().int().positive().max(100).optional()
|
|
84426
84598
|
});
|
|
84427
|
-
var experimentPageConversionSchema =
|
|
84599
|
+
var experimentPageConversionSchema = z74.object({
|
|
84428
84600
|
/** The event, by key — what `--goal` takes. */
|
|
84429
|
-
eventKey:
|
|
84601
|
+
eventKey: z74.string(),
|
|
84430
84602
|
/** The conversion name the company counts it under, or null when nobody has named it. */
|
|
84431
|
-
name:
|
|
84603
|
+
name: z74.string().nullable(),
|
|
84432
84604
|
/** Distinct visitors to the page who went on to do this. */
|
|
84433
|
-
convertingVisitors:
|
|
84605
|
+
convertingVisitors: z74.number()
|
|
84434
84606
|
});
|
|
84435
|
-
var experimentHorizonSchema =
|
|
84436
|
-
days:
|
|
84607
|
+
var experimentHorizonSchema = z74.object({
|
|
84608
|
+
days: z74.number(),
|
|
84437
84609
|
/** The smallest relative lift detectable in that many days. `null` when none would be. */
|
|
84438
|
-
detectableLift:
|
|
84610
|
+
detectableLift: z74.number().nullable()
|
|
84439
84611
|
});
|
|
84440
|
-
var experimentPlanResponseSchema =
|
|
84441
|
-
canRun:
|
|
84612
|
+
var experimentPlanResponseSchema = z74.object({
|
|
84613
|
+
canRun: z74.boolean(),
|
|
84442
84614
|
/** Present when `canRun` is false. Product language, ready to show. */
|
|
84443
|
-
reason:
|
|
84615
|
+
reason: z74.string().optional(),
|
|
84444
84616
|
/** The event the test would be read on. Absent when none could be chosen. */
|
|
84445
84617
|
goal: experimentGoalSchema.optional(),
|
|
84446
84618
|
/** The new version's own event, when one was given. */
|
|
84447
84619
|
variantGoal: experimentGoalSchema.optional(),
|
|
84448
84620
|
/** Every event this page's visitors went on to do over the sizing window, most common first. */
|
|
84449
|
-
conversions:
|
|
84621
|
+
conversions: z74.array(experimentPageConversionSchema),
|
|
84450
84622
|
/** Distinct visitors the page had over the sizing window. */
|
|
84451
|
-
visitors:
|
|
84452
|
-
baselineRate:
|
|
84453
|
-
minDetectableRelativeLift:
|
|
84623
|
+
visitors: z74.number(),
|
|
84624
|
+
baselineRate: z74.number(),
|
|
84625
|
+
minDetectableRelativeLift: z74.number(),
|
|
84454
84626
|
/** `null` when no amount of traffic could settle the hypothesis. */
|
|
84455
|
-
requiredPerVariant:
|
|
84627
|
+
requiredPerVariant: z74.number().nullable(),
|
|
84456
84628
|
/** Visitors a day the page has been getting, per arm once split. */
|
|
84457
|
-
dailyVisitorsPerVariant:
|
|
84629
|
+
dailyVisitorsPerVariant: z74.number(),
|
|
84458
84630
|
/** `null` when the page has no traffic to project from. */
|
|
84459
|
-
estimatedDays:
|
|
84460
|
-
horizons:
|
|
84631
|
+
estimatedDays: z74.number().nullable(),
|
|
84632
|
+
horizons: z74.array(experimentHorizonSchema),
|
|
84461
84633
|
traffic: experimentTrafficSchema,
|
|
84462
84634
|
baselineSource: experimentBaselineSourceSchema,
|
|
84463
84635
|
sizedOn: experimentSizedOnSchema,
|
|
@@ -84466,7 +84638,7 @@ var experimentPlanResponseSchema = z73.object({
|
|
|
84466
84638
|
* Product language, ready to show; absent on a page with real traffic that
|
|
84467
84639
|
* has produced the goal.
|
|
84468
84640
|
*/
|
|
84469
|
-
warning:
|
|
84641
|
+
warning: z74.string().optional(),
|
|
84470
84642
|
/**
|
|
84471
84643
|
* Every finished test on this page, newest first — see
|
|
84472
84644
|
* `experimentHistoryEntrySchema`. On the plan because the plan is the moment
|
|
@@ -84474,9 +84646,9 @@ var experimentPlanResponseSchema = z73.object({
|
|
|
84474
84646
|
* should inform it: a refuted idea proposed again, or a supported one never
|
|
84475
84647
|
* built on, is what this exists to prevent.
|
|
84476
84648
|
*/
|
|
84477
|
-
pastTests:
|
|
84649
|
+
pastTests: z74.array(z74.lazy(() => experimentHistoryEntrySchema)).default([])
|
|
84478
84650
|
});
|
|
84479
|
-
var experimentVerdictSchema =
|
|
84651
|
+
var experimentVerdictSchema = z74.enum([
|
|
84480
84652
|
"keep_running",
|
|
84481
84653
|
"winner",
|
|
84482
84654
|
"no_difference",
|
|
@@ -84503,12 +84675,12 @@ var experimentVerdictSchema = z73.enum([
|
|
|
84503
84675
|
*/
|
|
84504
84676
|
"inconclusive"
|
|
84505
84677
|
]);
|
|
84506
|
-
var experimentStatusValueSchema =
|
|
84507
|
-
var experimentArmSchema =
|
|
84508
|
-
slug:
|
|
84509
|
-
visitors:
|
|
84510
|
-
conversions:
|
|
84511
|
-
rate:
|
|
84678
|
+
var experimentStatusValueSchema = z74.enum(["running", "paused", "finished"]);
|
|
84679
|
+
var experimentArmSchema = z74.object({
|
|
84680
|
+
slug: z74.string(),
|
|
84681
|
+
visitors: z74.number(),
|
|
84682
|
+
conversions: z74.number(),
|
|
84683
|
+
rate: z74.number(),
|
|
84512
84684
|
/**
|
|
84513
84685
|
* The page's own preview shot, so a reader can see which version this is.
|
|
84514
84686
|
*
|
|
@@ -84517,7 +84689,7 @@ var experimentArmSchema = z73.object({
|
|
|
84517
84689
|
* screens cannot show a client two different "this is your offer page".
|
|
84518
84690
|
* `null` before the first capture, and on a page whose capture failed.
|
|
84519
84691
|
*/
|
|
84520
|
-
previewUrl:
|
|
84692
|
+
previewUrl: z74.string().nullable().optional(),
|
|
84521
84693
|
/**
|
|
84522
84694
|
* This version as it was when the test started — the test's own capture,
|
|
84523
84695
|
* taken off the live site once the split was being served.
|
|
@@ -84527,41 +84699,41 @@ var experimentArmSchema = z73.object({
|
|
|
84527
84699
|
* altogether for a variant that lost and was archived. This is the record;
|
|
84528
84700
|
* a card prefers it and says which of the two it is showing.
|
|
84529
84701
|
*/
|
|
84530
|
-
snapshotUrl:
|
|
84531
|
-
snapshotCapturedAt:
|
|
84532
|
-
});
|
|
84533
|
-
var experimentMetricSchema =
|
|
84534
|
-
name:
|
|
84535
|
-
control:
|
|
84536
|
-
variant:
|
|
84537
|
-
relativeLift:
|
|
84702
|
+
snapshotUrl: z74.string().nullable().optional(),
|
|
84703
|
+
snapshotCapturedAt: z74.number().nullable().optional()
|
|
84704
|
+
});
|
|
84705
|
+
var experimentMetricSchema = z74.object({
|
|
84706
|
+
name: z74.string(),
|
|
84707
|
+
control: z74.object({ conversions: z74.number(), rate: z74.number() }),
|
|
84708
|
+
variant: z74.object({ conversions: z74.number(), rate: z74.number() }),
|
|
84709
|
+
relativeLift: z74.number(),
|
|
84538
84710
|
/** The anytime-valid interval on the relative lift. */
|
|
84539
|
-
liftInterval:
|
|
84711
|
+
liftInterval: z74.tuple([z74.number(), z74.number()]),
|
|
84540
84712
|
/** `better` when the interval sits above zero, `worse` below it, `unclear` across it. */
|
|
84541
|
-
reading:
|
|
84713
|
+
reading: z74.enum(["better", "worse", "unclear"])
|
|
84542
84714
|
});
|
|
84543
|
-
var experimentTimelineArmSchema =
|
|
84544
|
-
visitors:
|
|
84545
|
-
conversions:
|
|
84546
|
-
rate:
|
|
84715
|
+
var experimentTimelineArmSchema = z74.object({
|
|
84716
|
+
visitors: z74.number(),
|
|
84717
|
+
conversions: z74.number(),
|
|
84718
|
+
rate: z74.number().nullable()
|
|
84547
84719
|
});
|
|
84548
|
-
var experimentTimelinePointSchema =
|
|
84720
|
+
var experimentTimelinePointSchema = z74.object({
|
|
84549
84721
|
/** Bucket start, ISO 8601. */
|
|
84550
|
-
bucket:
|
|
84722
|
+
bucket: z74.string(),
|
|
84551
84723
|
control: experimentTimelineArmSchema,
|
|
84552
84724
|
variant: experimentTimelineArmSchema
|
|
84553
84725
|
});
|
|
84554
|
-
var experimentStatusRowSchema =
|
|
84555
|
-
experimentId:
|
|
84556
|
-
landingSlug:
|
|
84557
|
-
variantSlug:
|
|
84726
|
+
var experimentStatusRowSchema = z74.object({
|
|
84727
|
+
experimentId: z74.string(),
|
|
84728
|
+
landingSlug: z74.string(),
|
|
84729
|
+
variantSlug: z74.string(),
|
|
84558
84730
|
hypothesis: experimentHypothesisSchema,
|
|
84559
84731
|
/**
|
|
84560
84732
|
* The hypothesis as one sentence, built from its parts plus the goal and the
|
|
84561
84733
|
* lift on this same row. Rendered backend-side so what a client reads in chat
|
|
84562
84734
|
* and what they read on the dashboard can never be two different sentences.
|
|
84563
84735
|
*/
|
|
84564
|
-
hypothesisStatement:
|
|
84736
|
+
hypothesisStatement: z74.string(),
|
|
84565
84737
|
/**
|
|
84566
84738
|
* What the test found out about the belief — derived from `verdict` and
|
|
84567
84739
|
* `winner`, never entered.
|
|
@@ -84578,28 +84750,28 @@ var experimentStatusRowSchema = z73.object({
|
|
|
84578
84750
|
* the plan because the claim is not readable without it — "leads should go up"
|
|
84579
84751
|
* is not a hypothesis, "leads should go up by at least 20%" is.
|
|
84580
84752
|
*/
|
|
84581
|
-
minDetectableRelativeLift:
|
|
84753
|
+
minDetectableRelativeLift: z74.number(),
|
|
84582
84754
|
/** The event the verdict is read from on the page, by key. */
|
|
84583
84755
|
goal: experimentGoalSchema,
|
|
84584
84756
|
/** The event the new version is read on, when it differs from `goal`. */
|
|
84585
84757
|
variantGoal: experimentGoalSchema.nullable(),
|
|
84586
84758
|
/** Both, in words a marketer reads — the Form's name, the moment. */
|
|
84587
|
-
goalLabel:
|
|
84759
|
+
goalLabel: z74.string(),
|
|
84588
84760
|
status: experimentStatusValueSchema,
|
|
84589
|
-
startedAt:
|
|
84761
|
+
startedAt: z74.number(),
|
|
84590
84762
|
/** Set while paused. The split is off and everyone sees the original. */
|
|
84591
|
-
pausedAt:
|
|
84592
|
-
finishedAt:
|
|
84763
|
+
pausedAt: z74.number().nullable(),
|
|
84764
|
+
finishedAt: z74.number().nullable(),
|
|
84593
84765
|
verdict: experimentVerdictSchema,
|
|
84594
84766
|
/** Set only on `winner`. Which version the numbers favour. */
|
|
84595
|
-
winner:
|
|
84767
|
+
winner: z74.enum(["control", "variant"]).nullable(),
|
|
84596
84768
|
/**
|
|
84597
84769
|
* Which version is live, once the test has ended. Chosen by whoever ended it,
|
|
84598
84770
|
* and allowed to disagree with `winner` — the card says so when it does.
|
|
84599
84771
|
*/
|
|
84600
|
-
survivor:
|
|
84772
|
+
survivor: z74.enum(["control", "variant"]).nullable(),
|
|
84601
84773
|
/** Set only on `invalid`. Why the numbers cannot be read. */
|
|
84602
|
-
invalidReason:
|
|
84774
|
+
invalidReason: z74.enum(["sample_ratio_mismatch", "crossover"]).nullable(),
|
|
84603
84775
|
/**
|
|
84604
84776
|
* Where the page's own content stands after the test, once it has ended.
|
|
84605
84777
|
*
|
|
@@ -84613,17 +84785,17 @@ var experimentStatusRowSchema = z73.object({
|
|
|
84613
84785
|
* version" links still show the right file, and whether there is anything
|
|
84614
84786
|
* left for a person to do.
|
|
84615
84787
|
*/
|
|
84616
|
-
fold:
|
|
84788
|
+
fold: z74.enum(["none", "owed", "landing", "done"]).nullable(),
|
|
84617
84789
|
/** What a person should be told, in one sentence, in product language. */
|
|
84618
|
-
summary:
|
|
84790
|
+
summary: z74.string(),
|
|
84619
84791
|
/** What the test taught, once somebody wrote it down. Null until then, and always null while it runs. */
|
|
84620
|
-
learning:
|
|
84792
|
+
learning: z74.string().nullable(),
|
|
84621
84793
|
/** Why it was ended, when whoever ended it said. Null while it runs. */
|
|
84622
84794
|
endReason: experimentEndReasonSchema.nullable(),
|
|
84623
84795
|
control: experimentArmSchema,
|
|
84624
84796
|
variant: experimentArmSchema,
|
|
84625
84797
|
/** Every other conversion the company counts, on the same two arms. */
|
|
84626
|
-
secondary:
|
|
84798
|
+
secondary: z74.array(experimentMetricSchema),
|
|
84627
84799
|
/**
|
|
84628
84800
|
* How close this test is to being able to say anything at all, 0 to 1.
|
|
84629
84801
|
*
|
|
@@ -84632,9 +84804,9 @@ var experimentStatusRowSchema = z73.object({
|
|
|
84632
84804
|
* measured variance says it needs, so it moves when the page gets noisier or
|
|
84633
84805
|
* steadier rather than only when visitors arrive.
|
|
84634
84806
|
*/
|
|
84635
|
-
precision:
|
|
84807
|
+
precision: z74.number(),
|
|
84636
84808
|
/** When the test could first say something. `null` with no traffic to project from. */
|
|
84637
|
-
earliestDecisionAt:
|
|
84809
|
+
earliestDecisionAt: z74.number().nullable(),
|
|
84638
84810
|
/**
|
|
84639
84811
|
* What the test is still missing before its verdict changes, if the gap it
|
|
84640
84812
|
* is seeing holds: visitors across both arms, and the conversions they bring.
|
|
@@ -84642,13 +84814,13 @@ var experimentStatusRowSchema = z73.object({
|
|
|
84642
84814
|
* divided by the page's pace. Null once it has spoken, before anyone has
|
|
84643
84815
|
* arrived, or when no amount of traffic would settle it.
|
|
84644
84816
|
*/
|
|
84645
|
-
stillNeeded:
|
|
84817
|
+
stillNeeded: z74.object({ visitors: z74.number(), conversions: z74.number() }).nullable().default(null),
|
|
84646
84818
|
/**
|
|
84647
84819
|
* True when no amount of this page's traffic would settle the comparison.
|
|
84648
84820
|
* A different thing from a page that is merely slow, and the reason to end
|
|
84649
84821
|
* a test rather than leave it running.
|
|
84650
84822
|
*/
|
|
84651
|
-
willNotConclude:
|
|
84823
|
+
willNotConclude: z74.boolean(),
|
|
84652
84824
|
/**
|
|
84653
84825
|
* How the two arms got where they are, one running total per bucket.
|
|
84654
84826
|
*
|
|
@@ -84657,74 +84829,74 @@ var experimentStatusRowSchema = z73.object({
|
|
|
84657
84829
|
* already told it in one word — the same reason everything else here is
|
|
84658
84830
|
* compact by default.
|
|
84659
84831
|
*/
|
|
84660
|
-
timeline:
|
|
84832
|
+
timeline: z74.array(experimentTimelinePointSchema).optional(),
|
|
84661
84833
|
/** Printing only. Never a reason to act — `verdict` is. */
|
|
84662
|
-
evidence:
|
|
84663
|
-
relativeLift:
|
|
84664
|
-
probabilityVariantBetter:
|
|
84834
|
+
evidence: z74.object({
|
|
84835
|
+
relativeLift: z74.number(),
|
|
84836
|
+
probabilityVariantBetter: z74.number(),
|
|
84665
84837
|
/**
|
|
84666
84838
|
* How sure the numbers are that the two versions differ at all, 0 to 1.
|
|
84667
84839
|
* Anytime-valid — the same construction the verdict is read from, so it
|
|
84668
84840
|
* holds however often it is read — and a winner is called exactly when
|
|
84669
84841
|
* it crosses 95%. The figure a card leads with.
|
|
84670
84842
|
*/
|
|
84671
|
-
confidence:
|
|
84672
|
-
controlInterval:
|
|
84673
|
-
variantInterval:
|
|
84843
|
+
confidence: z74.number(),
|
|
84844
|
+
controlInterval: z74.tuple([z74.number(), z74.number()]),
|
|
84845
|
+
variantInterval: z74.tuple([z74.number(), z74.number()]),
|
|
84674
84846
|
/** The anytime-valid interval on the relative lift — what the verdict is read from. */
|
|
84675
|
-
liftInterval:
|
|
84847
|
+
liftInterval: z74.tuple([z74.number(), z74.number()])
|
|
84676
84848
|
}).optional()
|
|
84677
84849
|
});
|
|
84678
|
-
var experimentStatusResponseSchema =
|
|
84679
|
-
experiments:
|
|
84850
|
+
var experimentStatusResponseSchema = z74.object({
|
|
84851
|
+
experiments: z74.array(experimentStatusRowSchema)
|
|
84680
84852
|
});
|
|
84681
|
-
var experimentHistoryEntrySchema =
|
|
84682
|
-
experimentId:
|
|
84683
|
-
landingSlug:
|
|
84684
|
-
variantSlug:
|
|
84685
|
-
startedAt:
|
|
84686
|
-
finishedAt:
|
|
84853
|
+
var experimentHistoryEntrySchema = z74.object({
|
|
84854
|
+
experimentId: z74.string(),
|
|
84855
|
+
landingSlug: z74.string(),
|
|
84856
|
+
variantSlug: z74.string(),
|
|
84857
|
+
startedAt: z74.number(),
|
|
84858
|
+
finishedAt: z74.number(),
|
|
84687
84859
|
/** Days the split ran, whole. */
|
|
84688
|
-
ranForDays:
|
|
84860
|
+
ranForDays: z74.number(),
|
|
84689
84861
|
hypothesis: experimentHypothesisSchema,
|
|
84690
|
-
hypothesisStatement:
|
|
84862
|
+
hypothesisStatement: z74.string(),
|
|
84691
84863
|
/** The sections the new version owned, as labels — what was actually different. */
|
|
84692
|
-
changed:
|
|
84864
|
+
changed: z74.array(z74.string()),
|
|
84693
84865
|
goal: experimentGoalSchema,
|
|
84694
84866
|
variantGoal: experimentGoalSchema.nullable(),
|
|
84695
|
-
goalLabel:
|
|
84867
|
+
goalLabel: z74.string(),
|
|
84696
84868
|
verdict: experimentVerdictSchema,
|
|
84697
84869
|
/** Which version the numbers favoured, on `winner`. */
|
|
84698
|
-
winner:
|
|
84870
|
+
winner: z74.enum(["control", "variant"]).nullable(),
|
|
84699
84871
|
/** Which version kept the traffic. */
|
|
84700
|
-
survivor:
|
|
84872
|
+
survivor: z74.enum(["control", "variant"]),
|
|
84701
84873
|
/** True when what was kept is not what the numbers favoured. */
|
|
84702
|
-
againstVerdict:
|
|
84874
|
+
againstVerdict: z74.boolean(),
|
|
84703
84875
|
hypothesisOutcome: experimentHypothesisOutcomeSchema,
|
|
84704
84876
|
/** The numbers, as they were when the test ended. */
|
|
84705
|
-
result:
|
|
84706
|
-
control:
|
|
84707
|
-
variant:
|
|
84708
|
-
relativeLift:
|
|
84709
|
-
liftInterval:
|
|
84710
|
-
confidence:
|
|
84711
|
-
secondary:
|
|
84877
|
+
result: z74.object({
|
|
84878
|
+
control: z74.object({ visitors: z74.number(), conversions: z74.number(), rate: z74.number() }),
|
|
84879
|
+
variant: z74.object({ visitors: z74.number(), conversions: z74.number(), rate: z74.number() }),
|
|
84880
|
+
relativeLift: z74.number(),
|
|
84881
|
+
liftInterval: z74.tuple([z74.number(), z74.number()]),
|
|
84882
|
+
confidence: z74.number(),
|
|
84883
|
+
secondary: z74.array(experimentMetricSchema)
|
|
84712
84884
|
}),
|
|
84713
|
-
summary:
|
|
84885
|
+
summary: z74.string(),
|
|
84714
84886
|
/** Why it was ended, when whoever ended it said. */
|
|
84715
84887
|
endReason: experimentEndReasonSchema.nullable(),
|
|
84716
84888
|
/** What the test taught, or null when nobody has written it down yet. */
|
|
84717
|
-
learning:
|
|
84889
|
+
learning: z74.string().nullable()
|
|
84718
84890
|
});
|
|
84719
|
-
var experimentHistoryResponseSchema =
|
|
84720
|
-
tests:
|
|
84891
|
+
var experimentHistoryResponseSchema = z74.object({
|
|
84892
|
+
tests: z74.array(experimentHistoryEntrySchema)
|
|
84721
84893
|
});
|
|
84722
|
-
var experimentFinishResponseSchema =
|
|
84894
|
+
var experimentFinishResponseSchema = z74.object({
|
|
84723
84895
|
verdict: experimentVerdictSchema,
|
|
84724
|
-
survivor:
|
|
84896
|
+
survivor: z74.enum(["control", "variant"]),
|
|
84725
84897
|
/** True when `keep` went against a verdict the numbers had reached. */
|
|
84726
|
-
againstVerdict:
|
|
84727
|
-
summary:
|
|
84898
|
+
againstVerdict: z74.boolean(),
|
|
84899
|
+
summary: z74.string()
|
|
84728
84900
|
});
|
|
84729
84901
|
|
|
84730
84902
|
// src/commands/experiment/hints.ts
|
|
@@ -84944,11 +85116,11 @@ function parseEvidence(raw, rawArgs) {
|
|
|
84944
85116
|
}
|
|
84945
85117
|
|
|
84946
85118
|
// src/commands/experiment/identity.ts
|
|
84947
|
-
import { readFile as
|
|
84948
|
-
import
|
|
85119
|
+
import { readFile as readFile44 } from "fs/promises";
|
|
85120
|
+
import path51 from "path";
|
|
84949
85121
|
async function readPageInternalId(root, slug) {
|
|
84950
85122
|
try {
|
|
84951
|
-
const text3 = await
|
|
85123
|
+
const text3 = await readFile44(path51.resolve(root, "src", "pages", slug, "_definition.md"), "utf8");
|
|
84952
85124
|
return /^internalId:\s*"?([^"\s]+)"?\s*$/m.exec(text3)?.[1];
|
|
84953
85125
|
} catch {
|
|
84954
85126
|
return void 0;
|
|
@@ -84956,8 +85128,8 @@ async function readPageInternalId(root, slug) {
|
|
|
84956
85128
|
}
|
|
84957
85129
|
|
|
84958
85130
|
// src/commands/experiment/measurement.ts
|
|
84959
|
-
import { readFile as
|
|
84960
|
-
import
|
|
85131
|
+
import { readFile as readFile45 } from "fs/promises";
|
|
85132
|
+
import path52 from "path";
|
|
84961
85133
|
function eventDeclarationsIn(source) {
|
|
84962
85134
|
const found = [];
|
|
84963
85135
|
const pattern = /\b(data-baker-(?:view|event))\s*=\s*["']([^"']+)["']/g;
|
|
@@ -85017,7 +85189,7 @@ function sectionsOf(files, slug) {
|
|
|
85017
85189
|
return files.filter((file) => file.startsWith(prefix) && !file.slice(prefix.length).startsWith("index."));
|
|
85018
85190
|
}
|
|
85019
85191
|
async function declarationsOf(root, files) {
|
|
85020
|
-
const sources = await Promise.all(files.map((file) =>
|
|
85192
|
+
const sources = await Promise.all(files.map((file) => readFile45(path52.join(root, file), "utf8").catch(() => "")));
|
|
85021
85193
|
return sources.flatMap(eventDeclarationsIn);
|
|
85022
85194
|
}
|
|
85023
85195
|
|
|
@@ -86439,10 +86611,10 @@ function parseValueExpression(raw) {
|
|
|
86439
86611
|
return parts.map(parsePart);
|
|
86440
86612
|
}
|
|
86441
86613
|
function trackingFieldIds() {
|
|
86442
|
-
const
|
|
86443
|
-
if (!existsSync9(
|
|
86614
|
+
const path68 = join4(flowsDir(), "..", "tracking.ts");
|
|
86615
|
+
if (!existsSync9(path68)) return null;
|
|
86444
86616
|
try {
|
|
86445
|
-
const source = readFileSync19(
|
|
86617
|
+
const source = readFileSync19(path68, "utf-8");
|
|
86446
86618
|
const block2 = source.match(/TRACKING_FIELD_IDS\s*=\s*\[([\s\S]*?)\]\s*as const/)?.[1];
|
|
86447
86619
|
if (!block2) return null;
|
|
86448
86620
|
const ids2 = [...block2.matchAll(/"(tracking\.[a-z0-9_]+)"/g)].map((match) => match[1]);
|
|
@@ -87024,13 +87196,13 @@ function specsFromFile(parsed) {
|
|
|
87024
87196
|
return `${destField}${type}=${entry?.value ?? ""}`;
|
|
87025
87197
|
});
|
|
87026
87198
|
}
|
|
87027
|
-
function readSpecFile(
|
|
87199
|
+
function readSpecFile(path68) {
|
|
87028
87200
|
let raw;
|
|
87029
87201
|
try {
|
|
87030
|
-
raw =
|
|
87202
|
+
raw = path68 === "-" ? readFileSync20(0, "utf-8") : readFileSync20(path68, "utf-8");
|
|
87031
87203
|
} catch (error) {
|
|
87032
87204
|
refuse3(
|
|
87033
|
-
`Could not read ${
|
|
87205
|
+
`Could not read ${path68 === "-" ? "the mapping from stdin" : `"${path68}"`}: ${error instanceof Error ? error.message : String(error)}`
|
|
87034
87206
|
);
|
|
87035
87207
|
}
|
|
87036
87208
|
let parsed;
|
|
@@ -87038,7 +87210,7 @@ function readSpecFile(path67) {
|
|
|
87038
87210
|
parsed = JSON.parse(raw);
|
|
87039
87211
|
} catch (error) {
|
|
87040
87212
|
refuse3(
|
|
87041
|
-
`${
|
|
87213
|
+
`${path68 === "-" ? "stdin" : `"${path68}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
87042
87214
|
'Expected { "map": { "<destField>": "<value>", \u2026 } }'
|
|
87043
87215
|
);
|
|
87044
87216
|
}
|
|
@@ -87337,21 +87509,21 @@ var ARRAY_FIELDS = [
|
|
|
87337
87509
|
"tagIds"
|
|
87338
87510
|
];
|
|
87339
87511
|
var ARRAY_OWNERS = ["", "body"];
|
|
87340
|
-
function dropUnsetOptionals(sideEffect,
|
|
87512
|
+
function dropUnsetOptionals(sideEffect, path68) {
|
|
87341
87513
|
return OPTIONAL_STRINGS.flatMap((key) => {
|
|
87342
87514
|
if (!(key in sideEffect) || sideEffect[key] !== null && sideEffect[key] !== "") return [];
|
|
87343
87515
|
delete sideEffect[key];
|
|
87344
|
-
return [{ path:
|
|
87516
|
+
return [{ path: path68, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
|
|
87345
87517
|
});
|
|
87346
87518
|
}
|
|
87347
|
-
function fillNulledArrays(target2, prefix,
|
|
87519
|
+
function fillNulledArrays(target2, prefix, path68) {
|
|
87348
87520
|
return ARRAY_FIELDS.flatMap((key) => {
|
|
87349
87521
|
if (!(key in target2) || target2[key] !== null) return [];
|
|
87350
87522
|
target2[key] = [];
|
|
87351
|
-
return [{ path:
|
|
87523
|
+
return [{ path: path68, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
|
|
87352
87524
|
});
|
|
87353
87525
|
}
|
|
87354
|
-
function tidyEmailRecipients(sideEffect,
|
|
87526
|
+
function tidyEmailRecipients(sideEffect, path68) {
|
|
87355
87527
|
if (sideEffect.type !== "email") return [];
|
|
87356
87528
|
const config = sideEffect.encryptedConfig;
|
|
87357
87529
|
if (!config || typeof config !== "object" || Array.isArray(config)) return [];
|
|
@@ -87362,7 +87534,7 @@ function tidyEmailRecipients(sideEffect, path67) {
|
|
|
87362
87534
|
const tidied = to.join(", ");
|
|
87363
87535
|
if (tidied === raw) return [];
|
|
87364
87536
|
config.email = tidied;
|
|
87365
|
-
return [{ path:
|
|
87537
|
+
return [{ path: path68, change: "tidied the recipient list's spacing and separators" }];
|
|
87366
87538
|
}
|
|
87367
87539
|
function sideEffectsOf(node) {
|
|
87368
87540
|
if (!Array.isArray(node.sideEffects)) return [];
|
|
@@ -87371,13 +87543,13 @@ function sideEffectsOf(node) {
|
|
|
87371
87543
|
);
|
|
87372
87544
|
}
|
|
87373
87545
|
function normalizeSideEffect(sideEffect, where) {
|
|
87374
|
-
const
|
|
87546
|
+
const path68 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
|
|
87375
87547
|
const arrays = ARRAY_OWNERS.flatMap((owner) => {
|
|
87376
87548
|
const target2 = owner ? sideEffect[owner] : sideEffect;
|
|
87377
87549
|
if (!target2 || typeof target2 !== "object") return [];
|
|
87378
|
-
return fillNulledArrays(target2, owner ? `${owner}.` : "",
|
|
87550
|
+
return fillNulledArrays(target2, owner ? `${owner}.` : "", path68);
|
|
87379
87551
|
});
|
|
87380
|
-
return [...dropUnsetOptionals(sideEffect,
|
|
87552
|
+
return [...dropUnsetOptionals(sideEffect, path68), ...arrays, ...tidyEmailRecipients(sideEffect, path68)];
|
|
87381
87553
|
}
|
|
87382
87554
|
function normalizeFlowTree(tree) {
|
|
87383
87555
|
const changes = [];
|
|
@@ -88152,45 +88324,45 @@ var FUNNEL_VERTICALS = [
|
|
|
88152
88324
|
];
|
|
88153
88325
|
|
|
88154
88326
|
// ../api/src/funnel/wire.ts
|
|
88155
|
-
import { z as
|
|
88327
|
+
import { z as z75 } from "zod";
|
|
88156
88328
|
var MAX_STEPS = 40;
|
|
88157
88329
|
var MAX_PER_STEP = 20;
|
|
88158
|
-
var funnelTriggerSchema =
|
|
88159
|
-
|
|
88160
|
-
id:
|
|
88161
|
-
kind:
|
|
88330
|
+
var funnelTriggerSchema = z75.discriminatedUnion("kind", [
|
|
88331
|
+
z75.object({
|
|
88332
|
+
id: z75.string().min(1).max(64),
|
|
88333
|
+
kind: z75.literal("event"),
|
|
88162
88334
|
/** The event's key, from `--moments`: `form:…`, `submit:…`, `page:…`, `exit:…`. */
|
|
88163
|
-
eventKey:
|
|
88335
|
+
eventKey: z75.string().min(1).max(320)
|
|
88164
88336
|
}),
|
|
88165
|
-
|
|
88166
|
-
id:
|
|
88167
|
-
kind:
|
|
88337
|
+
z75.object({
|
|
88338
|
+
id: z75.string().min(1).max(64),
|
|
88339
|
+
kind: z75.literal("landing"),
|
|
88168
88340
|
/** The landing's internal id — what the page stamps on its rows, never its slug. */
|
|
88169
|
-
landingId:
|
|
88341
|
+
landingId: z75.string().min(1).max(120)
|
|
88170
88342
|
}),
|
|
88171
88343
|
/** Any landing being viewed — what a funnel's first step usually listens for. */
|
|
88172
|
-
|
|
88173
|
-
id:
|
|
88174
|
-
kind:
|
|
88344
|
+
z75.object({
|
|
88345
|
+
id: z75.string().min(1).max(64),
|
|
88346
|
+
kind: z75.literal("any_landing")
|
|
88175
88347
|
})
|
|
88176
88348
|
]);
|
|
88177
|
-
var funnelStepSchema =
|
|
88349
|
+
var funnelStepSchema = z75.object({
|
|
88178
88350
|
/** Lowercase letters, digits and underscores. Stable — renaming never changes it. */
|
|
88179
|
-
key:
|
|
88351
|
+
key: z75.string().min(1).max(64),
|
|
88180
88352
|
/** The client's own words for this step. Shown everywhere; unconstrained. */
|
|
88181
|
-
displayName:
|
|
88182
|
-
stepClass:
|
|
88353
|
+
displayName: z75.string().min(1).max(120),
|
|
88354
|
+
stepClass: z75.enum(FUNNEL_STEP_CLASSES),
|
|
88183
88355
|
/** Predecessor keys. Empty means people come in here. */
|
|
88184
|
-
after:
|
|
88356
|
+
after: z75.array(z75.string().max(64)).max(MAX_STEPS).default([]),
|
|
88185
88357
|
/** What moves somebody onto this step. Any one of them is enough. */
|
|
88186
|
-
triggers:
|
|
88358
|
+
triggers: z75.array(funnelTriggerSchema).max(MAX_PER_STEP).default([])
|
|
88187
88359
|
});
|
|
88188
|
-
var funnelRequestSchema =
|
|
88189
|
-
steps:
|
|
88360
|
+
var funnelRequestSchema = z75.object({
|
|
88361
|
+
steps: z75.array(funnelStepSchema).max(MAX_STEPS).optional(),
|
|
88190
88362
|
/** One step, added or replaced by its key. Everything else is untouched. */
|
|
88191
88363
|
step: funnelStepSchema.optional(),
|
|
88192
88364
|
/** One step taken out by key; whatever came after it moves up to whatever came before it. */
|
|
88193
|
-
removeStep:
|
|
88365
|
+
removeStep: z75.string().min(1).max(64).optional(),
|
|
88194
88366
|
/**
|
|
88195
88367
|
* ONE way in added to a step, leaving the rest of that step alone.
|
|
88196
88368
|
*
|
|
@@ -88200,129 +88372,129 @@ var funnelRequestSchema = z74.object({
|
|
|
88200
88372
|
* for the thing that is actually being asked ("this Form's booking should
|
|
88201
88373
|
* count here"), and it cannot drop anything.
|
|
88202
88374
|
*/
|
|
88203
|
-
addTrigger:
|
|
88375
|
+
addTrigger: z75.object({ stepKey: z75.string().min(1).max(64), eventKey: z75.string().min(1).max(320) }).optional(),
|
|
88204
88376
|
/** ONE way in taken off a step, by the event key it listens for. */
|
|
88205
|
-
removeTrigger:
|
|
88377
|
+
removeTrigger: z75.object({ stepKey: z75.string().min(1).max(64), eventKey: z75.string().min(1).max(320) }).optional(),
|
|
88206
88378
|
/** One of the ready-made graphs, instead of stating steps. */
|
|
88207
|
-
preset:
|
|
88379
|
+
preset: z75.enum(FUNNEL_VERTICALS).optional(),
|
|
88208
88380
|
/** Also return the suggestion library and the presets. */
|
|
88209
|
-
templates:
|
|
88381
|
+
templates: z75.boolean().optional(),
|
|
88210
88382
|
/**
|
|
88211
88383
|
* Also return everything a trigger can name: every event this company's
|
|
88212
88384
|
* Forms, pages and systems have actually produced, its sources, and its
|
|
88213
88385
|
* landings. The list to pick a trigger FROM, so nobody has to guess.
|
|
88214
88386
|
*/
|
|
88215
|
-
moments:
|
|
88387
|
+
moments: z75.boolean().optional(),
|
|
88216
88388
|
/** How many days the counts beside each step cover. Default 30. */
|
|
88217
|
-
days:
|
|
88389
|
+
days: z75.number().int().min(1).max(400).optional(),
|
|
88218
88390
|
/**
|
|
88219
88391
|
* The Forms, by slug, the caller names itself when no step counts them —
|
|
88220
88392
|
* `baker funnel` reads its own workspace and names each with the key that
|
|
88221
88393
|
* places it. The backend leaves them out of its own hint, so no Form is
|
|
88222
88394
|
* named twice.
|
|
88223
88395
|
*/
|
|
88224
|
-
namedByCaller:
|
|
88396
|
+
namedByCaller: z75.array(z75.string().min(1).max(200)).max(1e3).optional()
|
|
88225
88397
|
});
|
|
88226
|
-
var funnelResponseSchema =
|
|
88227
|
-
ok:
|
|
88228
|
-
data:
|
|
88398
|
+
var funnelResponseSchema = z75.object({
|
|
88399
|
+
ok: z75.literal(true),
|
|
88400
|
+
data: z75.object({
|
|
88229
88401
|
/** The published version, or 0 when this company has never drawn a funnel. */
|
|
88230
|
-
version:
|
|
88402
|
+
version: z75.number(),
|
|
88231
88403
|
/** Whether `steps` is a proposal waiting on this chat's publish rather than what is live. */
|
|
88232
|
-
staged:
|
|
88233
|
-
steps:
|
|
88234
|
-
|
|
88235
|
-
key:
|
|
88236
|
-
displayName:
|
|
88237
|
-
stepClass:
|
|
88238
|
-
after:
|
|
88239
|
-
triggers:
|
|
88404
|
+
staged: z75.boolean(),
|
|
88405
|
+
steps: z75.array(
|
|
88406
|
+
z75.object({
|
|
88407
|
+
key: z75.string(),
|
|
88408
|
+
displayName: z75.string(),
|
|
88409
|
+
stepClass: z75.string(),
|
|
88410
|
+
after: z75.array(z75.string()),
|
|
88411
|
+
triggers: z75.array(z75.record(z75.string(), z75.unknown())),
|
|
88240
88412
|
/**
|
|
88241
88413
|
* Where the people who reach this step are sent: the sends of every
|
|
88242
88414
|
* system event among its triggers. Read-only here — a send belongs to
|
|
88243
88415
|
* the event and is set with `baker analytics sends`.
|
|
88244
88416
|
*/
|
|
88245
|
-
sends:
|
|
88246
|
-
|
|
88247
|
-
id:
|
|
88248
|
-
eventKey:
|
|
88249
|
-
platform:
|
|
88250
|
-
event:
|
|
88417
|
+
sends: z75.array(
|
|
88418
|
+
z75.object({
|
|
88419
|
+
id: z75.string(),
|
|
88420
|
+
eventKey: z75.string(),
|
|
88421
|
+
platform: z75.string(),
|
|
88422
|
+
event: z75.string(),
|
|
88251
88423
|
/** What happened to it in the window, in one line. */
|
|
88252
|
-
outcome:
|
|
88424
|
+
outcome: z75.string().optional()
|
|
88253
88425
|
})
|
|
88254
88426
|
),
|
|
88255
88427
|
/** How many people reached this step in the window. Counted in people, across visits and devices. */
|
|
88256
|
-
people:
|
|
88428
|
+
people: z75.number().optional(),
|
|
88257
88429
|
/** How many reached this step or any step after it. */
|
|
88258
|
-
atLeast:
|
|
88430
|
+
atLeast: z75.number().optional(),
|
|
88259
88431
|
/** How many got as far as the step before it. Absent for a way in. */
|
|
88260
|
-
before:
|
|
88432
|
+
before: z75.number().optional(),
|
|
88261
88433
|
/** `atLeast / before`, 0–1. Absent where there is no before. */
|
|
88262
|
-
share:
|
|
88263
|
-
events:
|
|
88264
|
-
money:
|
|
88434
|
+
share: z75.number().optional(),
|
|
88435
|
+
events: z75.number().optional(),
|
|
88436
|
+
money: z75.record(z75.string(), z75.number()).optional()
|
|
88265
88437
|
})
|
|
88266
88438
|
),
|
|
88267
88439
|
/** The window `people` and friends were counted over. */
|
|
88268
|
-
window:
|
|
88440
|
+
window: z75.object({ startDate: z75.string(), endDate: z75.string(), days: z75.number() }).optional(),
|
|
88269
88441
|
/** The step that loses the most people, by key. Absent while nothing is lost. */
|
|
88270
|
-
worstStep:
|
|
88442
|
+
worstStep: z75.string().optional(),
|
|
88271
88443
|
/**
|
|
88272
88444
|
* The steps this write took off the funnel, by key. Present on every write,
|
|
88273
88445
|
* empty when nothing was dropped — the answer to "did my restatement forget
|
|
88274
88446
|
* one", given in the same turn.
|
|
88275
88447
|
*/
|
|
88276
|
-
removed:
|
|
88448
|
+
removed: z75.array(z75.string()).optional(),
|
|
88277
88449
|
/**
|
|
88278
88450
|
* What this company counts as page conversions, this chat's staged counts
|
|
88279
88451
|
* included, by event key and name. Here so a Form's ending can say it is
|
|
88280
88452
|
* already counted — the agent choosing a step's way in should use that
|
|
88281
88453
|
* same key — and outside `moments` because the plain read's hints say it too.
|
|
88282
88454
|
*/
|
|
88283
|
-
counted:
|
|
88455
|
+
counted: z75.array(z75.object({ eventKey: z75.string(), name: z75.string() })).optional(),
|
|
88284
88456
|
/** Everything a trigger can name, when asked for with `moments`. */
|
|
88285
|
-
moments:
|
|
88286
|
-
events:
|
|
88287
|
-
|
|
88288
|
-
eventKey:
|
|
88457
|
+
moments: z75.object({
|
|
88458
|
+
events: z75.array(
|
|
88459
|
+
z75.object({
|
|
88460
|
+
eventKey: z75.string(),
|
|
88289
88461
|
/** `site` for a page or Form, `systems` for the client's own server, `both` when both sent it. */
|
|
88290
|
-
origin:
|
|
88462
|
+
origin: z75.string().optional(),
|
|
88291
88463
|
/** The Form it belongs to, when it belongs to one. */
|
|
88292
|
-
flowSlug:
|
|
88293
|
-
nodeType:
|
|
88294
|
-
events:
|
|
88295
|
-
sessions:
|
|
88464
|
+
flowSlug: z75.string().optional(),
|
|
88465
|
+
nodeType: z75.string().optional(),
|
|
88466
|
+
events: z75.number(),
|
|
88467
|
+
sessions: z75.number(),
|
|
88296
88468
|
/** The name it is counted under as a page conversion, when the company counts it. Informational. */
|
|
88297
|
-
countedAs:
|
|
88469
|
+
countedAs: z75.string().optional(),
|
|
88298
88470
|
/**
|
|
88299
88471
|
* The step of the Form this happens on, in the Form's own words —
|
|
88300
88472
|
* never a node id. Present on a Form's ending read from the Form
|
|
88301
88473
|
* itself, which is the only place the name is known.
|
|
88302
88474
|
*/
|
|
88303
|
-
step:
|
|
88475
|
+
step: z75.string().optional(),
|
|
88304
88476
|
/**
|
|
88305
88477
|
* `false` on an ending a Form raises natively but nobody has fired
|
|
88306
88478
|
* yet — derived from the Form, offered at zero, exactly as a
|
|
88307
88479
|
* Source is offered before its first send. Absent means it came
|
|
88308
88480
|
* from the event store and therefore happened.
|
|
88309
88481
|
*/
|
|
88310
|
-
seen:
|
|
88482
|
+
seen: z75.boolean().optional()
|
|
88311
88483
|
})
|
|
88312
88484
|
),
|
|
88313
88485
|
/** The company's sources: a system of theirs that posts events to Baker, by the event name it raises. */
|
|
88314
|
-
sources:
|
|
88315
|
-
landings:
|
|
88486
|
+
sources: z75.array(z75.object({ eventKey: z75.string(), name: z75.string(), status: z75.string() })),
|
|
88487
|
+
landings: z75.array(z75.object({ landingId: z75.string(), title: z75.string(), slug: z75.string() }))
|
|
88316
88488
|
}).optional(),
|
|
88317
|
-
templates:
|
|
88318
|
-
|
|
88319
|
-
key:
|
|
88320
|
-
displayName:
|
|
88321
|
-
meaning:
|
|
88322
|
-
stepClass:
|
|
88489
|
+
templates: z75.array(
|
|
88490
|
+
z75.object({
|
|
88491
|
+
key: z75.string(),
|
|
88492
|
+
displayName: z75.string(),
|
|
88493
|
+
meaning: z75.string(),
|
|
88494
|
+
stepClass: z75.string()
|
|
88323
88495
|
})
|
|
88324
88496
|
).optional(),
|
|
88325
|
-
presets:
|
|
88497
|
+
presets: z75.array(z75.object({ vertical: z75.string(), label: z75.string(), description: z75.string(), steps: z75.array(z75.string()) })).optional(),
|
|
88326
88498
|
/**
|
|
88327
88499
|
* What each stage means, the test to apply, and the same stage in three
|
|
88328
88500
|
* shapes of business.
|
|
@@ -88333,17 +88505,17 @@ var funnelResponseSchema = z74.object({
|
|
|
88333
88505
|
* engaged or customer. Same source as the dashboard's stage picker, so the
|
|
88334
88506
|
* two cannot teach it differently.
|
|
88335
88507
|
*/
|
|
88336
|
-
stages:
|
|
88337
|
-
|
|
88338
|
-
stepClass:
|
|
88339
|
-
title:
|
|
88340
|
-
meaning:
|
|
88341
|
-
test:
|
|
88342
|
-
examples:
|
|
88508
|
+
stages: z75.array(
|
|
88509
|
+
z75.object({
|
|
88510
|
+
stepClass: z75.string(),
|
|
88511
|
+
title: z75.string(),
|
|
88512
|
+
meaning: z75.string(),
|
|
88513
|
+
test: z75.string(),
|
|
88514
|
+
examples: z75.object({ shop: z75.string(), selfServe: z75.string(), salesLed: z75.string() })
|
|
88343
88515
|
})
|
|
88344
88516
|
).optional()
|
|
88345
88517
|
}),
|
|
88346
|
-
hints:
|
|
88518
|
+
hints: z75.array(z75.string()).optional()
|
|
88347
88519
|
});
|
|
88348
88520
|
|
|
88349
88521
|
// src/commands/funnel/funnelArgs.ts
|
|
@@ -88883,10 +89055,10 @@ async function stageOps(ops) {
|
|
|
88883
89055
|
handleError5(err);
|
|
88884
89056
|
}
|
|
88885
89057
|
}
|
|
88886
|
-
async function draftAction2(
|
|
89058
|
+
async function draftAction2(path68, body, chat) {
|
|
88887
89059
|
const chatId = resolveChatId(chat);
|
|
88888
89060
|
try {
|
|
88889
|
-
const { data, hints: hints2 } = splitHints(await apiPost(
|
|
89061
|
+
const { data, hints: hints2 } = splitHints(await apiPost(path68, { chatId, ...body }));
|
|
88890
89062
|
writeJsonEnvelope({ ok: true, data, ...hints2.length > 0 ? { hints: hints2 } : {} });
|
|
88891
89063
|
return data;
|
|
88892
89064
|
} catch (err) {
|
|
@@ -91397,7 +91569,7 @@ function cropSprite(input, region) {
|
|
|
91397
91569
|
|
|
91398
91570
|
// src/lib/image/io.ts
|
|
91399
91571
|
import { randomBytes } from "crypto";
|
|
91400
|
-
import { glob as fsGlob, readFile as
|
|
91572
|
+
import { glob as fsGlob, readFile as readFile46, rename as rename2, stat as stat9, writeFile as writeFile26 } from "fs/promises";
|
|
91401
91573
|
import { dirname as dirname2, extname as extname3, join as join5, resolve as resolve4 } from "path";
|
|
91402
91574
|
var REMOTE_RE = /^https?:\/\//i;
|
|
91403
91575
|
var GLOB_RE = /[*?[\]{}]/;
|
|
@@ -91430,11 +91602,11 @@ async function readImageBuffer(pathOrUrl) {
|
|
|
91430
91602
|
const { buffer } = await fetchExternalBytes(pathOrUrl, { maxBytes: MAX_REMOTE_IMAGE_BYTES });
|
|
91431
91603
|
return buffer;
|
|
91432
91604
|
}
|
|
91433
|
-
return
|
|
91605
|
+
return readFile46(pathOrUrl);
|
|
91434
91606
|
}
|
|
91435
|
-
async function isDirectory(
|
|
91607
|
+
async function isDirectory(path68) {
|
|
91436
91608
|
try {
|
|
91437
|
-
const s = await stat9(
|
|
91609
|
+
const s = await stat9(path68);
|
|
91438
91610
|
return s.isDirectory();
|
|
91439
91611
|
} catch {
|
|
91440
91612
|
return false;
|
|
@@ -91453,7 +91625,7 @@ async function atomicWrite(targetPath, data) {
|
|
|
91453
91625
|
const absolute = resolve4(targetPath);
|
|
91454
91626
|
const dir = dirname2(absolute);
|
|
91455
91627
|
const tmp = join5(dir, `.baker-image-${randomBytes(8).toString("hex")}.tmp`);
|
|
91456
|
-
await
|
|
91628
|
+
await writeFile26(tmp, data);
|
|
91457
91629
|
await rename2(tmp, absolute);
|
|
91458
91630
|
}
|
|
91459
91631
|
|
|
@@ -91862,13 +92034,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
|
|
|
91862
92034
|
}
|
|
91863
92035
|
function disambiguate(paths) {
|
|
91864
92036
|
const taken = /* @__PURE__ */ new Set();
|
|
91865
|
-
return paths.map((
|
|
91866
|
-
if (!taken.has(
|
|
91867
|
-
taken.add(
|
|
91868
|
-
return
|
|
92037
|
+
return paths.map((path68) => {
|
|
92038
|
+
if (!taken.has(path68)) {
|
|
92039
|
+
taken.add(path68);
|
|
92040
|
+
return path68;
|
|
91869
92041
|
}
|
|
91870
|
-
const ext = extname4(
|
|
91871
|
-
const stem =
|
|
92042
|
+
const ext = extname4(path68);
|
|
92043
|
+
const stem = path68.slice(0, path68.length - ext.length);
|
|
91872
92044
|
let n = 2;
|
|
91873
92045
|
while (taken.has(`${stem}-${n}${ext}`)) n += 1;
|
|
91874
92046
|
const unique2 = `${stem}-${n}${ext}`;
|
|
@@ -91994,10 +92166,10 @@ async function runDownloads(plan) {
|
|
|
91994
92166
|
const paths = disambiguate(fetched.map((item) => item.path));
|
|
91995
92167
|
const downloaded = [];
|
|
91996
92168
|
for (const [index, item] of fetched.entries()) {
|
|
91997
|
-
const
|
|
92169
|
+
const path68 = paths[index] ?? item.path;
|
|
91998
92170
|
try {
|
|
91999
|
-
await atomicWrite(
|
|
92000
|
-
downloaded.push({ input: item.input, output:
|
|
92171
|
+
await atomicWrite(path68, item.buffer);
|
|
92172
|
+
downloaded.push({ input: item.input, output: path68, bytes: item.buffer.length, contentType: item.contentType });
|
|
92001
92173
|
} catch (err) {
|
|
92002
92174
|
failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
|
|
92003
92175
|
}
|
|
@@ -94892,8 +95064,8 @@ Full guide: __tooling__/docs/tools/baker/images.md`
|
|
|
94892
95064
|
import { defineCommand as defineCommand226 } from "citty";
|
|
94893
95065
|
|
|
94894
95066
|
// src/commands/landing/critique.ts
|
|
94895
|
-
import { readdir as readdir22, readFile as
|
|
94896
|
-
import
|
|
95067
|
+
import { readdir as readdir22, readFile as readFile48, stat as stat11 } from "fs/promises";
|
|
95068
|
+
import path55 from "path";
|
|
94897
95069
|
import { defineCommand as defineCommand213 } from "citty";
|
|
94898
95070
|
|
|
94899
95071
|
// src/engine/landing/lib/constants.ts
|
|
@@ -96084,27 +96256,27 @@ function describeCounts(findings) {
|
|
|
96084
96256
|
}
|
|
96085
96257
|
|
|
96086
96258
|
// src/commands/landing/snapshot.ts
|
|
96087
|
-
import { mkdir as
|
|
96088
|
-
import
|
|
96259
|
+
import { mkdir as mkdir16, rename as rename3, writeFile as writeFile27 } from "fs/promises";
|
|
96260
|
+
import path53 from "path";
|
|
96089
96261
|
var CRITIC_VERSION = "4";
|
|
96090
96262
|
function critiqueCacheDir(projectRoot) {
|
|
96091
|
-
return
|
|
96263
|
+
return path53.join(projectRoot, ".cache", "landing-critique");
|
|
96092
96264
|
}
|
|
96093
96265
|
function snapshotPath(projectRoot, slug) {
|
|
96094
|
-
return
|
|
96266
|
+
return path53.join(critiqueCacheDir(projectRoot), `${slug}.json`);
|
|
96095
96267
|
}
|
|
96096
96268
|
async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
96097
|
-
await
|
|
96269
|
+
await mkdir16(critiqueCacheDir(projectRoot), { recursive: true });
|
|
96098
96270
|
const dest = snapshotPath(projectRoot, snapshot.slug);
|
|
96099
96271
|
const tmp = `${dest}.tmp`;
|
|
96100
|
-
await
|
|
96272
|
+
await writeFile27(tmp, `${JSON.stringify(snapshot, null, 2)}
|
|
96101
96273
|
`, "utf8");
|
|
96102
96274
|
await rename3(tmp, dest);
|
|
96103
96275
|
}
|
|
96104
96276
|
|
|
96105
96277
|
// src/commands/landing/source-version.ts
|
|
96106
|
-
import { readdir as readdir21, readFile as
|
|
96107
|
-
import
|
|
96278
|
+
import { readdir as readdir21, readFile as readFile47, stat as stat10 } from "fs/promises";
|
|
96279
|
+
import path54 from "path";
|
|
96108
96280
|
var CRITIQUED_ROOTS = ["src/pages/", "src/components/"];
|
|
96109
96281
|
async function landingSourceRelPaths(root, slug) {
|
|
96110
96282
|
const files = await landingGraphFiles(root, slug);
|
|
@@ -96113,7 +96285,7 @@ async function landingSourceRelPaths(root, slug) {
|
|
|
96113
96285
|
async function readLandingSources(root, slug) {
|
|
96114
96286
|
const rel = await landingSourceRelPaths(root, slug);
|
|
96115
96287
|
const out = [];
|
|
96116
|
-
for (const r of rel) out.push({ path: r, text: await
|
|
96288
|
+
for (const r of rel) out.push({ path: r, text: await readFile47(path54.join(root, r), "utf8") });
|
|
96117
96289
|
return out;
|
|
96118
96290
|
}
|
|
96119
96291
|
async function computeLandingSourceSha(root, slug) {
|
|
@@ -96122,7 +96294,7 @@ async function computeLandingSourceSha(root, slug) {
|
|
|
96122
96294
|
for (const r of rel) {
|
|
96123
96295
|
let bytes;
|
|
96124
96296
|
try {
|
|
96125
|
-
bytes = await
|
|
96297
|
+
bytes = await readFile47(path54.join(root, r));
|
|
96126
96298
|
} catch {
|
|
96127
96299
|
bytes = Buffer.alloc(0);
|
|
96128
96300
|
}
|
|
@@ -96132,16 +96304,16 @@ async function computeLandingSourceSha(root, slug) {
|
|
|
96132
96304
|
}
|
|
96133
96305
|
async function computeLegacyLandingSourceSha(landingDir) {
|
|
96134
96306
|
const rel = [];
|
|
96135
|
-
if (await isFile2(
|
|
96136
|
-
for (const abs of await walkAstro(
|
|
96137
|
-
rel.push(
|
|
96307
|
+
if (await isFile2(path54.join(landingDir, "index.astro"))) rel.push("index.astro");
|
|
96308
|
+
for (const abs of await walkAstro(path54.join(landingDir, "_components"))) {
|
|
96309
|
+
rel.push(path54.relative(landingDir, abs).split(path54.sep).join("/"));
|
|
96138
96310
|
}
|
|
96139
96311
|
rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
96140
96312
|
const parts = [];
|
|
96141
96313
|
for (const r of rel) {
|
|
96142
96314
|
let bytes;
|
|
96143
96315
|
try {
|
|
96144
|
-
bytes = await
|
|
96316
|
+
bytes = await readFile47(path54.join(landingDir, r));
|
|
96145
96317
|
} catch {
|
|
96146
96318
|
bytes = Buffer.alloc(0);
|
|
96147
96319
|
}
|
|
@@ -96165,7 +96337,7 @@ async function walkAstro(dir) {
|
|
|
96165
96337
|
}
|
|
96166
96338
|
const out = [];
|
|
96167
96339
|
for (const entry of entries) {
|
|
96168
|
-
const abs =
|
|
96340
|
+
const abs = path54.join(dir, entry.name);
|
|
96169
96341
|
if (entry.isDirectory()) out.push(...await walkAstro(abs));
|
|
96170
96342
|
else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
|
|
96171
96343
|
}
|
|
@@ -96226,7 +96398,7 @@ var critiqueCommand2 = defineCommand213({
|
|
|
96226
96398
|
{ availableSlugs: await listLandingSlugs(projectRoot) }
|
|
96227
96399
|
);
|
|
96228
96400
|
}
|
|
96229
|
-
if (!await isDir2(
|
|
96401
|
+
if (!await isDir2(path55.resolve(projectRoot, "src", "pages", slug))) {
|
|
96230
96402
|
fail10("NOT_FOUND", `No landing at src/pages/${slug}/`, {
|
|
96231
96403
|
availableSlugs: await listLandingSlugs(projectRoot)
|
|
96232
96404
|
});
|
|
@@ -96269,7 +96441,7 @@ async function critiqueOne(projectRoot, slug, brand, competitors) {
|
|
|
96269
96441
|
const [sources, compositionSha, sourceSha] = await Promise.all([
|
|
96270
96442
|
readLandingSources(projectRoot, slug),
|
|
96271
96443
|
computeLandingSourceSha(projectRoot, slug),
|
|
96272
|
-
computeLegacyLandingSourceSha(
|
|
96444
|
+
computeLegacyLandingSourceSha(path55.resolve(projectRoot, "src", "pages", slug))
|
|
96273
96445
|
]);
|
|
96274
96446
|
const report = critiqueLanding({ slug, sources, brand, competitors });
|
|
96275
96447
|
let snapshotFailed = false;
|
|
@@ -96289,7 +96461,7 @@ async function critiqueOne(projectRoot, slug, brand, competitors) {
|
|
|
96289
96461
|
return { slug, report, snapshotFailed };
|
|
96290
96462
|
}
|
|
96291
96463
|
async function readCompetitorNames(projectRoot) {
|
|
96292
|
-
const dir =
|
|
96464
|
+
const dir = path55.join(projectRoot, "src", "content", "competitors");
|
|
96293
96465
|
let entries;
|
|
96294
96466
|
try {
|
|
96295
96467
|
entries = (await readdir22(dir)).filter((f) => f.endsWith(".md"));
|
|
@@ -96298,9 +96470,9 @@ async function readCompetitorNames(projectRoot) {
|
|
|
96298
96470
|
}
|
|
96299
96471
|
const names = [];
|
|
96300
96472
|
for (const entry of entries) {
|
|
96301
|
-
names.push(
|
|
96473
|
+
names.push(path55.basename(entry, ".md").replace(/[-_]+/g, " "));
|
|
96302
96474
|
try {
|
|
96303
|
-
const head = (await
|
|
96475
|
+
const head = (await readFile48(path55.join(dir, entry), "utf8")).slice(0, 2e3);
|
|
96304
96476
|
const titled = /^\s*(?:title|name)\s*:\s*["']?([^"'\n]+)["']?\s*$/im.exec(head);
|
|
96305
96477
|
if (titled?.[1]) names.push(titled[1].trim());
|
|
96306
96478
|
} catch {
|
|
@@ -96310,7 +96482,7 @@ async function readCompetitorNames(projectRoot) {
|
|
|
96310
96482
|
}
|
|
96311
96483
|
async function listLandingSlugs(projectRoot) {
|
|
96312
96484
|
try {
|
|
96313
|
-
const entries = await readdir22(
|
|
96485
|
+
const entries = await readdir22(path55.join(projectRoot, "src", "pages"), { withFileTypes: true });
|
|
96314
96486
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
96315
96487
|
} catch {
|
|
96316
96488
|
return [];
|
|
@@ -96434,10 +96606,10 @@ function exportStatusHints(view) {
|
|
|
96434
96606
|
}
|
|
96435
96607
|
return ["Still being prepared. Carry on with other work and check again in a minute \u2014 never sleep-loop on it."];
|
|
96436
96608
|
}
|
|
96437
|
-
function parseProfileFlag(raw,
|
|
96609
|
+
function parseProfileFlag(raw, readFile52 = (path68) => readFileSync25(path68, "utf8")) {
|
|
96438
96610
|
let parsed;
|
|
96439
96611
|
try {
|
|
96440
|
-
parsed = JSON.parse(raw.startsWith("@") ?
|
|
96612
|
+
parsed = JSON.parse(raw.startsWith("@") ? readFile52(raw.slice(1)) : raw);
|
|
96441
96613
|
} catch (error) {
|
|
96442
96614
|
return {
|
|
96443
96615
|
ok: false,
|
|
@@ -96608,8 +96780,8 @@ var addCommand = defineCommand215({
|
|
|
96608
96780
|
});
|
|
96609
96781
|
|
|
96610
96782
|
// src/commands/landing/inspiration/code.ts
|
|
96611
|
-
import { mkdir as
|
|
96612
|
-
import
|
|
96783
|
+
import { mkdir as mkdir17, writeFile as writeFile28 } from "fs/promises";
|
|
96784
|
+
import path56 from "path";
|
|
96613
96785
|
import { defineCommand as defineCommand216 } from "citty";
|
|
96614
96786
|
registerSchema({
|
|
96615
96787
|
command: "landing.inspiration.code",
|
|
@@ -96632,10 +96804,10 @@ var codeCommand = defineCommand216({
|
|
|
96632
96804
|
try {
|
|
96633
96805
|
const id = args8.id;
|
|
96634
96806
|
const data = await apiGet("/api/landing-inspiration/section-code", { id });
|
|
96635
|
-
const dir =
|
|
96636
|
-
await
|
|
96637
|
-
const file =
|
|
96638
|
-
await
|
|
96807
|
+
const dir = path56.join(process.cwd(), ".baker", "inspiration", id);
|
|
96808
|
+
await mkdir17(dir, { recursive: true });
|
|
96809
|
+
const file = path56.join(dir, "section.html");
|
|
96810
|
+
await writeFile28(file, data.html);
|
|
96639
96811
|
const hints2 = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
96640
96812
|
const fidelity = fidelityHint(data.fidelity);
|
|
96641
96813
|
if (fidelity) hints2.push(fidelity);
|
|
@@ -96644,7 +96816,7 @@ var codeCommand = defineCommand216({
|
|
|
96644
96816
|
ok: true,
|
|
96645
96817
|
data: {
|
|
96646
96818
|
id,
|
|
96647
|
-
file:
|
|
96819
|
+
file: path56.relative(process.cwd(), file),
|
|
96648
96820
|
bytes: data.html.length,
|
|
96649
96821
|
fidelity: data.fidelity,
|
|
96650
96822
|
reproduction_notes: data.reproductionNotes,
|
|
@@ -97103,8 +97275,8 @@ function classifyCaptureFailure(error) {
|
|
|
97103
97275
|
}
|
|
97104
97276
|
|
|
97105
97277
|
// src/engine/landing-library/run.ts
|
|
97106
|
-
import { mkdir as
|
|
97107
|
-
import
|
|
97278
|
+
import { mkdir as mkdir18, writeFile as writeFile30 } from "fs/promises";
|
|
97279
|
+
import path58 from "path";
|
|
97108
97280
|
|
|
97109
97281
|
// ../proxy/src/preflight.ts
|
|
97110
97282
|
import http from "http";
|
|
@@ -98521,11 +98693,11 @@ async function renderBundleToPng(browser, html, viewportWidth, options = {}) {
|
|
|
98521
98693
|
}
|
|
98522
98694
|
|
|
98523
98695
|
// src/engine/landing-library/report.ts
|
|
98524
|
-
import { writeFile as
|
|
98525
|
-
import
|
|
98696
|
+
import { writeFile as writeFile29 } from "fs/promises";
|
|
98697
|
+
import path57 from "path";
|
|
98526
98698
|
async function writeCaptureReport(manifest, outDir) {
|
|
98527
|
-
const file =
|
|
98528
|
-
await
|
|
98699
|
+
const file = path57.join(outDir, "report.html");
|
|
98700
|
+
await writeFile29(file, renderReport(manifest));
|
|
98529
98701
|
return file;
|
|
98530
98702
|
}
|
|
98531
98703
|
function escapeHtml3(value2) {
|
|
@@ -98668,32 +98840,32 @@ async function reproducePage(args8) {
|
|
|
98668
98840
|
const { browser, page, outDir, pageUrl, livePageShot } = args8;
|
|
98669
98841
|
const built = await buildSectionBundle(page, "body", pageUrl).catch(() => null);
|
|
98670
98842
|
if (!built) return { bundle: null, fidelity: null };
|
|
98671
|
-
await
|
|
98843
|
+
await writeFile30(path58.join(outDir, "page.html"), built.html);
|
|
98672
98844
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width, {
|
|
98673
98845
|
wholePage: true,
|
|
98674
98846
|
timeoutMs: 6e4
|
|
98675
98847
|
});
|
|
98676
98848
|
if (!rendered || !livePageShot) return { bundle: "page.html", fidelity: null };
|
|
98677
|
-
await
|
|
98849
|
+
await writeFile30(path58.join(outDir, "page-rendered.png"), rendered);
|
|
98678
98850
|
const { score, note } = await scoreFidelity(livePageShot, rendered);
|
|
98679
98851
|
return { bundle: "page.html", fidelity: score, ...note ? { fidelityNote: note } : {} };
|
|
98680
98852
|
}
|
|
98681
98853
|
async function captureOneSection(args8) {
|
|
98682
98854
|
const { browser, page, candidate, sectionsDir, outDir, pageUrl, withCode } = args8;
|
|
98683
|
-
const dir =
|
|
98684
|
-
await
|
|
98855
|
+
const dir = path58.join(sectionsDir, String(candidate.index).padStart(2, "0"));
|
|
98856
|
+
await mkdir18(dir, { recursive: true });
|
|
98685
98857
|
const desktop = await captureSection(page, candidate);
|
|
98686
|
-
if (desktop) await
|
|
98858
|
+
if (desktop) await writeFile30(path58.join(dir, "desktop.png"), desktop);
|
|
98687
98859
|
const visualHash = desktop ? await perceptualHash(desktop) : null;
|
|
98688
98860
|
const motion = await collectMotion(page, candidate.selector);
|
|
98689
98861
|
const built = withCode ? await buildSectionBundle(page, candidate.selector, pageUrl) : null;
|
|
98690
98862
|
let fidelity = null;
|
|
98691
98863
|
let fidelityNote;
|
|
98692
98864
|
if (built) {
|
|
98693
|
-
await
|
|
98865
|
+
await writeFile30(path58.join(dir, "section.html"), built.html);
|
|
98694
98866
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width);
|
|
98695
98867
|
if (rendered && desktop) {
|
|
98696
|
-
await
|
|
98868
|
+
await writeFile30(path58.join(dir, "section-rendered.png"), rendered);
|
|
98697
98869
|
const result = await scoreFidelity(desktop, rendered);
|
|
98698
98870
|
fidelity = result.score;
|
|
98699
98871
|
fidelityNote = result.note;
|
|
@@ -98701,9 +98873,9 @@ async function captureOneSection(args8) {
|
|
|
98701
98873
|
}
|
|
98702
98874
|
return {
|
|
98703
98875
|
...candidate,
|
|
98704
|
-
desktopShot: desktop ?
|
|
98876
|
+
desktopShot: desktop ? path58.relative(outDir, path58.join(dir, "desktop.png")) : null,
|
|
98705
98877
|
mobileShot: null,
|
|
98706
|
-
bundle: built ?
|
|
98878
|
+
bundle: built ? path58.relative(outDir, path58.join(dir, "section.html")) : null,
|
|
98707
98879
|
fidelity,
|
|
98708
98880
|
...fidelityNote ? { fidelityNote } : {},
|
|
98709
98881
|
...built ? { cssStats: built.stats } : {},
|
|
@@ -98722,9 +98894,9 @@ async function captureMobileShots(args8) {
|
|
|
98722
98894
|
for (const section2 of sections) {
|
|
98723
98895
|
const shot = await captureSectionOnMobile(mobile.page, section2);
|
|
98724
98896
|
if (!shot) continue;
|
|
98725
|
-
const file =
|
|
98726
|
-
await
|
|
98727
|
-
section2.mobileShot =
|
|
98897
|
+
const file = path58.join(sectionsDir, String(section2.index).padStart(2, "0"), "mobile.png");
|
|
98898
|
+
await writeFile30(file, shot);
|
|
98899
|
+
section2.mobileShot = path58.relative(outDir, file);
|
|
98728
98900
|
}
|
|
98729
98901
|
} finally {
|
|
98730
98902
|
await mobile.context.close();
|
|
@@ -98739,10 +98911,10 @@ async function captureMotionTakes(args8) {
|
|
|
98739
98911
|
const filmOne = async (section2) => {
|
|
98740
98912
|
const take = await captureMotionTake(browser, pageUrl, section2.selector).catch(() => null);
|
|
98741
98913
|
if (!take) return;
|
|
98742
|
-
const dir =
|
|
98743
|
-
const file =
|
|
98744
|
-
await
|
|
98745
|
-
section2.motionFilmstrip =
|
|
98914
|
+
const dir = path58.join(sectionsDir, String(section2.index).padStart(2, "0"));
|
|
98915
|
+
const file = path58.join(dir, "motion-filmstrip.png");
|
|
98916
|
+
await writeFile30(file, take.filmstrip);
|
|
98917
|
+
section2.motionFilmstrip = path58.relative(outDir, file);
|
|
98746
98918
|
log(` [${section2.index}] ${section2.motion.summary}`);
|
|
98747
98919
|
};
|
|
98748
98920
|
const queue = [...moving];
|
|
@@ -98799,7 +98971,7 @@ async function captureAlternateViews(args8) {
|
|
|
98799
98971
|
async function reproduceWholePage(args8) {
|
|
98800
98972
|
const { browser, page, outDir, pageUrl, withCode, log } = args8;
|
|
98801
98973
|
const fullPage = await page.screenshot({ type: "png", fullPage: true }).catch(() => null);
|
|
98802
|
-
if (fullPage) await
|
|
98974
|
+
if (fullPage) await writeFile30(path58.join(outDir, "full-page.png"), fullPage);
|
|
98803
98975
|
if (!withCode) return { bundle: null, fidelity: null };
|
|
98804
98976
|
const reproduction = await reproducePage({ browser, page, outDir, pageUrl, livePageShot: fullPage });
|
|
98805
98977
|
log(`page reproduction: ${reproduction.fidelity === null ? "unavailable" : reproduction.fidelity.toFixed(2)}`);
|
|
@@ -98870,7 +99042,7 @@ async function openViaLadder(args8) {
|
|
|
98870
99042
|
async function scrapeLanding(options) {
|
|
98871
99043
|
const timeoutMs = options.timeoutMs ?? 45e3;
|
|
98872
99044
|
const log = options.onProgress ?? (() => void 0);
|
|
98873
|
-
const sectionsDir =
|
|
99045
|
+
const sectionsDir = path58.join(options.outDir, "sections");
|
|
98874
99046
|
const nonPublic = refuseNonPublicUrl(options.url);
|
|
98875
99047
|
if (nonPublic) {
|
|
98876
99048
|
throw new BlockedPageError({
|
|
@@ -98890,7 +99062,7 @@ async function scrapeLanding(options) {
|
|
|
98890
99062
|
const withMotion = options.motion !== false && !escalated;
|
|
98891
99063
|
const renderBrowser = options.code === false ? null : await launchBrowser();
|
|
98892
99064
|
try {
|
|
98893
|
-
await
|
|
99065
|
+
await mkdir18(sectionsDir, { recursive: true });
|
|
98894
99066
|
const sections = await captureSections({
|
|
98895
99067
|
browser: renderBrowser ?? browser,
|
|
98896
99068
|
page,
|
|
@@ -98932,7 +99104,7 @@ async function scrapeLanding(options) {
|
|
|
98932
99104
|
security: prepared.security,
|
|
98933
99105
|
captureTier: tier
|
|
98934
99106
|
};
|
|
98935
|
-
await
|
|
99107
|
+
await writeFile30(path58.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
98936
99108
|
`);
|
|
98937
99109
|
if (options.report !== false) {
|
|
98938
99110
|
const reportPath = await writeCaptureReport(manifest, options.outDir);
|
|
@@ -98947,28 +99119,28 @@ async function scrapeLanding(options) {
|
|
|
98947
99119
|
|
|
98948
99120
|
// src/commands/landing/inspiration/captureOut.ts
|
|
98949
99121
|
import { existsSync as existsSync12 } from "fs";
|
|
98950
|
-
import
|
|
99122
|
+
import path59 from "path";
|
|
98951
99123
|
var SCRATCH_DIR = ".baker";
|
|
98952
99124
|
function isWithin(parent, target2) {
|
|
98953
|
-
const relative =
|
|
98954
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
99125
|
+
const relative = path59.relative(parent, target2);
|
|
99126
|
+
return relative === "" || !relative.startsWith("..") && !path59.isAbsolute(relative);
|
|
98955
99127
|
}
|
|
98956
99128
|
function findRepoRoot(from) {
|
|
98957
|
-
let dir =
|
|
99129
|
+
let dir = path59.resolve(from);
|
|
98958
99130
|
for (; ; ) {
|
|
98959
|
-
if (existsSync12(
|
|
98960
|
-
const parent =
|
|
99131
|
+
if (existsSync12(path59.join(dir, ".git"))) return dir;
|
|
99132
|
+
const parent = path59.dirname(dir);
|
|
98961
99133
|
if (parent === dir) return null;
|
|
98962
99134
|
dir = parent;
|
|
98963
99135
|
}
|
|
98964
99136
|
}
|
|
98965
99137
|
function checkCaptureOut(out, options) {
|
|
98966
99138
|
const { cwd, repoRoot } = options;
|
|
98967
|
-
const resolved =
|
|
99139
|
+
const resolved = path59.resolve(cwd, out);
|
|
98968
99140
|
if (repoRoot === null || !isWithin(repoRoot, resolved)) return { ok: true };
|
|
98969
|
-
const scratch =
|
|
99141
|
+
const scratch = path59.join(repoRoot, SCRATCH_DIR);
|
|
98970
99142
|
if (isWithin(scratch, resolved)) return { ok: true };
|
|
98971
|
-
const suggestion =
|
|
99143
|
+
const suggestion = path59.posix.join(SCRATCH_DIR, "teardowns", path59.basename(resolved) || "capture");
|
|
98972
99144
|
return {
|
|
98973
99145
|
ok: false,
|
|
98974
99146
|
error: {
|
|
@@ -99142,12 +99314,12 @@ var scrapeCommand = defineCommand220({
|
|
|
99142
99314
|
});
|
|
99143
99315
|
|
|
99144
99316
|
// src/commands/landing/inspiration/search.ts
|
|
99145
|
-
import
|
|
99317
|
+
import path61 from "path";
|
|
99146
99318
|
import { defineCommand as defineCommand221 } from "citty";
|
|
99147
99319
|
|
|
99148
99320
|
// src/commands/landing/inspiration/shot.ts
|
|
99149
|
-
import { mkdir as
|
|
99150
|
-
import
|
|
99321
|
+
import { mkdir as mkdir19, writeFile as writeFile31 } from "fs/promises";
|
|
99322
|
+
import path60 from "path";
|
|
99151
99323
|
import sharp9 from "sharp";
|
|
99152
99324
|
var READABLE_SHOT = {
|
|
99153
99325
|
maxWidth: 1440,
|
|
@@ -99173,9 +99345,9 @@ async function downloadReadableShot(url, file) {
|
|
|
99173
99345
|
const response = await fetch(url);
|
|
99174
99346
|
if (!response.ok) return null;
|
|
99175
99347
|
const shot = await toReadableShot(Buffer.from(await response.arrayBuffer()));
|
|
99176
|
-
await
|
|
99177
|
-
await
|
|
99178
|
-
return
|
|
99348
|
+
await mkdir19(path60.dirname(file), { recursive: true });
|
|
99349
|
+
await writeFile31(file, shot);
|
|
99350
|
+
return path60.relative(process.cwd(), file);
|
|
99179
99351
|
} catch {
|
|
99180
99352
|
return null;
|
|
99181
99353
|
}
|
|
@@ -99267,13 +99439,13 @@ function buildSearchBody2(args8) {
|
|
|
99267
99439
|
return body;
|
|
99268
99440
|
}
|
|
99269
99441
|
async function downloadShots(results) {
|
|
99270
|
-
const dir =
|
|
99442
|
+
const dir = path61.join(process.cwd(), ".baker", "inspiration");
|
|
99271
99443
|
const saved = /* @__PURE__ */ new Map();
|
|
99272
99444
|
await Promise.all(
|
|
99273
99445
|
results.map(async (result) => {
|
|
99274
99446
|
const file = await downloadReadableShot(
|
|
99275
99447
|
result.desktopShotUrl,
|
|
99276
|
-
|
|
99448
|
+
path61.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
|
|
99277
99449
|
);
|
|
99278
99450
|
if (file) saved.set(result.id, file);
|
|
99279
99451
|
})
|
|
@@ -99501,7 +99673,7 @@ var sequencesCommand = defineCommand222({
|
|
|
99501
99673
|
});
|
|
99502
99674
|
|
|
99503
99675
|
// src/commands/landing/inspiration/view.ts
|
|
99504
|
-
import
|
|
99676
|
+
import path62 from "path";
|
|
99505
99677
|
import { defineCommand as defineCommand223 } from "citty";
|
|
99506
99678
|
registerSchema({
|
|
99507
99679
|
command: "landing.inspiration.view",
|
|
@@ -99534,12 +99706,12 @@ var viewCommand2 = defineCommand223({
|
|
|
99534
99706
|
const id = args8.id;
|
|
99535
99707
|
const data = await apiGet("/api/landing-inspiration/section", { id });
|
|
99536
99708
|
const section2 = data.section;
|
|
99537
|
-
const dir =
|
|
99709
|
+
const dir = path62.join(process.cwd(), ".baker", "inspiration", id);
|
|
99538
99710
|
const ext = READABLE_SHOT.extension;
|
|
99539
99711
|
const [desktop, mobile, filmstrip] = await Promise.all([
|
|
99540
|
-
downloadReadableShot(section2.desktopShotUrl,
|
|
99541
|
-
downloadReadableShot(section2.mobileShotUrl,
|
|
99542
|
-
downloadReadableShot(section2.motionFilmstripUrl,
|
|
99712
|
+
downloadReadableShot(section2.desktopShotUrl, path62.join(dir, `desktop.${ext}`)),
|
|
99713
|
+
downloadReadableShot(section2.mobileShotUrl, path62.join(dir, `mobile.${ext}`)),
|
|
99714
|
+
downloadReadableShot(section2.motionFilmstripUrl, path62.join(dir, `motion-filmstrip.${ext}`))
|
|
99543
99715
|
]);
|
|
99544
99716
|
const full = args8.full;
|
|
99545
99717
|
const hints2 = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
@@ -99647,8 +99819,8 @@ Full guide: __tooling__/docs/tools/baker/landing.md`
|
|
|
99647
99819
|
|
|
99648
99820
|
// src/commands/landing/variant.ts
|
|
99649
99821
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
99650
|
-
import { mkdir as
|
|
99651
|
-
import
|
|
99822
|
+
import { mkdir as mkdir20, readdir as readdir23, readFile as readFile49, stat as stat12, writeFile as writeFile32 } from "fs/promises";
|
|
99823
|
+
import path63 from "path";
|
|
99652
99824
|
import { defineCommand as defineCommand225 } from "citty";
|
|
99653
99825
|
registerSchema({
|
|
99654
99826
|
command: "landing.variant",
|
|
@@ -99675,7 +99847,7 @@ registerSchema({
|
|
|
99675
99847
|
var SLUG_RE2 = /^[a-z0-9][a-z0-9-]*$/;
|
|
99676
99848
|
async function readInternalId(definitionPath) {
|
|
99677
99849
|
try {
|
|
99678
|
-
const text3 = await
|
|
99850
|
+
const text3 = await readFile49(definitionPath, "utf8");
|
|
99679
99851
|
return /^internalId:\s*"?([^"\s]+)"?\s*$/m.exec(text3)?.[1] ?? null;
|
|
99680
99852
|
} catch {
|
|
99681
99853
|
return null;
|
|
@@ -99684,7 +99856,7 @@ async function readInternalId(definitionPath) {
|
|
|
99684
99856
|
async function archivedVariantNumbers(root, internalId) {
|
|
99685
99857
|
if (!internalId) return [];
|
|
99686
99858
|
try {
|
|
99687
|
-
const entries = await readdir23(
|
|
99859
|
+
const entries = await readdir23(path63.resolve(root, "src/_variants", internalId), { withFileTypes: true });
|
|
99688
99860
|
return entries.filter((entry) => entry.isDirectory()).map((entry) => Number(entry.name)).filter((n) => Number.isInteger(n) && n > 0);
|
|
99689
99861
|
} catch {
|
|
99690
99862
|
return [];
|
|
@@ -99714,7 +99886,7 @@ async function exists2(p) {
|
|
|
99714
99886
|
}
|
|
99715
99887
|
async function listLandingSlugs2(root) {
|
|
99716
99888
|
try {
|
|
99717
|
-
const entries = await readdir23(
|
|
99889
|
+
const entries = await readdir23(path63.resolve(root, "src", "pages"), { withFileTypes: true });
|
|
99718
99890
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
99719
99891
|
} catch {
|
|
99720
99892
|
return [];
|
|
@@ -99730,7 +99902,7 @@ async function listComponents(componentsDir, prefix = "") {
|
|
|
99730
99902
|
const out = [];
|
|
99731
99903
|
for (const entry of entries) {
|
|
99732
99904
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
99733
|
-
if (entry.isDirectory()) out.push(...await listComponents(
|
|
99905
|
+
if (entry.isDirectory()) out.push(...await listComponents(path63.join(componentsDir, entry.name), rel));
|
|
99734
99906
|
else if (entry.name.endsWith(".astro")) out.push(rel);
|
|
99735
99907
|
}
|
|
99736
99908
|
return out.sort();
|
|
@@ -99797,16 +99969,16 @@ async function resolveTargets(projectRoot, opts) {
|
|
|
99797
99969
|
`"${controlSlug}" is already a variant of "${variantOfSlug(controlSlug)}". Make the next variant of the page itself: \`baker landing variant ${variantOfSlug(controlSlug)}\`.`
|
|
99798
99970
|
);
|
|
99799
99971
|
}
|
|
99800
|
-
const controlDir =
|
|
99972
|
+
const controlDir = path63.resolve(projectRoot, "src", "pages", controlSlug);
|
|
99801
99973
|
if (!await isDir3(controlDir)) {
|
|
99802
99974
|
fail11("NOT_FOUND", `No page at src/pages/${controlSlug}/`, {
|
|
99803
99975
|
availableSlugs: await listLandingSlugs2(projectRoot)
|
|
99804
99976
|
});
|
|
99805
99977
|
}
|
|
99806
|
-
if (!await exists2(
|
|
99978
|
+
if (!await exists2(path63.join(controlDir, "index.astro"))) {
|
|
99807
99979
|
fail11("NOT_FOUND", `src/pages/${controlSlug}/index.astro is missing, so there is no page to make a variant of.`);
|
|
99808
99980
|
}
|
|
99809
|
-
const internalId = await readInternalId(
|
|
99981
|
+
const internalId = await readInternalId(path63.join(controlDir, "_definition.md"));
|
|
99810
99982
|
const variantSlugValue = variantSlug(
|
|
99811
99983
|
controlSlug,
|
|
99812
99984
|
nextVariantNumber(
|
|
@@ -99815,8 +99987,8 @@ async function resolveTargets(projectRoot, opts) {
|
|
|
99815
99987
|
await archivedVariantNumbers(projectRoot, internalId)
|
|
99816
99988
|
)
|
|
99817
99989
|
);
|
|
99818
|
-
const variantDir =
|
|
99819
|
-
const available = await listComponents(
|
|
99990
|
+
const variantDir = path63.resolve(projectRoot, "src", "pages", variantSlugValue);
|
|
99991
|
+
const available = await listComponents(path63.join(controlDir, "_components"));
|
|
99820
99992
|
const forks = [];
|
|
99821
99993
|
for (const name of parseForks(opts.fork, opts.rawArgs)) {
|
|
99822
99994
|
const resolved = resolveForkName(name, available);
|
|
@@ -99832,26 +100004,26 @@ async function resolveTargets(projectRoot, opts) {
|
|
|
99832
100004
|
}
|
|
99833
100005
|
async function writeVariant(opts) {
|
|
99834
100006
|
const { controlDir, variantDir, controlSlug, variantSlug: variantSlug2, forks } = opts;
|
|
99835
|
-
const forkedTargets = new Set(forks.map((f) =>
|
|
100007
|
+
const forkedTargets = new Set(forks.map((f) => path63.join(controlDir, "_components", f)));
|
|
99836
100008
|
const written = [];
|
|
99837
|
-
const indexText = await
|
|
99838
|
-
await
|
|
99839
|
-
await
|
|
99840
|
-
|
|
100009
|
+
const indexText = await readFile49(path63.join(controlDir, "index.astro"), "utf8");
|
|
100010
|
+
await mkdir20(variantDir, { recursive: true });
|
|
100011
|
+
await writeFile32(
|
|
100012
|
+
path63.join(variantDir, "index.astro"),
|
|
99841
100013
|
repointFile(indexText, { fromDir: controlDir, toDir: variantDir, controlDir, variantDir, forkedTargets }),
|
|
99842
100014
|
"utf8"
|
|
99843
100015
|
);
|
|
99844
100016
|
written.push(`src/pages/${variantSlug2}/index.astro`);
|
|
99845
100017
|
for (const fork of forks) {
|
|
99846
|
-
const fromFile =
|
|
99847
|
-
const toFile =
|
|
99848
|
-
const text3 = await
|
|
99849
|
-
await
|
|
99850
|
-
await
|
|
100018
|
+
const fromFile = path63.join(controlDir, "_components", fork);
|
|
100019
|
+
const toFile = path63.join(variantDir, "_components", fork);
|
|
100020
|
+
const text3 = await readFile49(fromFile, "utf8");
|
|
100021
|
+
await mkdir20(path63.dirname(toFile), { recursive: true });
|
|
100022
|
+
await writeFile32(
|
|
99851
100023
|
toFile,
|
|
99852
100024
|
repointFile(text3, {
|
|
99853
|
-
fromDir:
|
|
99854
|
-
toDir:
|
|
100025
|
+
fromDir: path63.dirname(fromFile),
|
|
100026
|
+
toDir: path63.dirname(toFile),
|
|
99855
100027
|
controlDir,
|
|
99856
100028
|
variantDir,
|
|
99857
100029
|
forkedTargets
|
|
@@ -99860,9 +100032,9 @@ async function writeVariant(opts) {
|
|
|
99860
100032
|
);
|
|
99861
100033
|
written.push(`src/pages/${variantSlug2}/_components/${fork}`);
|
|
99862
100034
|
}
|
|
99863
|
-
const controlDefinitionPath =
|
|
100035
|
+
const controlDefinitionPath = path63.join(controlDir, "_definition.md");
|
|
99864
100036
|
if (await exists2(controlDefinitionPath)) {
|
|
99865
|
-
const definition = buildVariantDefinition(await
|
|
100037
|
+
const definition = buildVariantDefinition(await readFile49(controlDefinitionPath, "utf8"), {
|
|
99866
100038
|
internalId: randomUUID4().replace(/-/g, "").slice(0, 8),
|
|
99867
100039
|
variantSlug: variantSlug2,
|
|
99868
100040
|
controlSlug,
|
|
@@ -99870,11 +100042,11 @@ async function writeVariant(opts) {
|
|
|
99870
100042
|
...opts.because ? { because: opts.because } : {},
|
|
99871
100043
|
...opts.change ? { change: opts.change } : {}
|
|
99872
100044
|
});
|
|
99873
|
-
await
|
|
100045
|
+
await writeFile32(path63.join(variantDir, "_definition.md"), definition, "utf8");
|
|
99874
100046
|
written.push(`src/pages/${variantSlug2}/_definition.md`);
|
|
99875
100047
|
}
|
|
99876
|
-
await
|
|
99877
|
-
await
|
|
100048
|
+
await mkdir20(path63.join(variantDir, "_images"), { recursive: true });
|
|
100049
|
+
await writeFile32(path63.join(variantDir, "_images", ".gitkeep"), "", "utf8");
|
|
99878
100050
|
return written;
|
|
99879
100051
|
}
|
|
99880
100052
|
var variantCommand = defineCommand225({
|
|
@@ -99979,28 +100151,28 @@ Subcommands:
|
|
|
99979
100151
|
});
|
|
99980
100152
|
|
|
99981
100153
|
// ../api/src/mcp-catalog.ts
|
|
99982
|
-
import { z as
|
|
100154
|
+
import { z as z76 } from "zod";
|
|
99983
100155
|
var MCP_CATALOG_DEFAULT_LIMIT = 8;
|
|
99984
100156
|
var MCP_CATALOG_DESCRIPTION_MAX = 160;
|
|
99985
|
-
var mcpCatalogRequestSchema =
|
|
99986
|
-
search:
|
|
99987
|
-
limit:
|
|
100157
|
+
var mcpCatalogRequestSchema = z76.object({
|
|
100158
|
+
search: z76.string().trim().min(1, "Provide --search").max(100),
|
|
100159
|
+
limit: z76.coerce.number().int().min(1).max(20).default(MCP_CATALOG_DEFAULT_LIMIT)
|
|
99988
100160
|
});
|
|
99989
|
-
var mcpCatalogConnectSchema =
|
|
99990
|
-
var mcpCatalogEntrySchema =
|
|
100161
|
+
var mcpCatalogConnectSchema = z76.enum(["sign-in", "key"]);
|
|
100162
|
+
var mcpCatalogEntrySchema = z76.object({
|
|
99991
100163
|
/** The toolkit slug — what request_connection `{ app }` takes. */
|
|
99992
|
-
app:
|
|
99993
|
-
name:
|
|
99994
|
-
description:
|
|
100164
|
+
app: z76.string(),
|
|
100165
|
+
name: z76.string(),
|
|
100166
|
+
description: z76.string().max(MCP_CATALOG_DESCRIPTION_MAX),
|
|
99995
100167
|
connect: mcpCatalogConnectSchema,
|
|
99996
|
-
connected:
|
|
100168
|
+
connected: z76.boolean()
|
|
99997
100169
|
});
|
|
99998
|
-
var mcpCatalogResponseSchema =
|
|
99999
|
-
apps:
|
|
100170
|
+
var mcpCatalogResponseSchema = z76.object({
|
|
100171
|
+
apps: z76.array(mcpCatalogEntrySchema),
|
|
100000
100172
|
/** The company has managed apps switched off: nothing here can be connected, so `apps` is empty. */
|
|
100001
|
-
managedDisabled:
|
|
100173
|
+
managedDisabled: z76.boolean().optional(),
|
|
100002
100174
|
/** Baker's own connections this search matched — ask for these with `{ platform }`, never a managed copy. */
|
|
100003
|
-
platforms:
|
|
100175
|
+
platforms: z76.array(z76.string()).optional()
|
|
100004
100176
|
});
|
|
100005
100177
|
|
|
100006
100178
|
// src/commands/mcp/index.ts
|
|
@@ -101221,11 +101393,11 @@ var countriesCommand = defineCommand237({
|
|
|
101221
101393
|
});
|
|
101222
101394
|
|
|
101223
101395
|
// src/commands/research/customers.ts
|
|
101224
|
-
import { mkdir as
|
|
101225
|
-
import
|
|
101396
|
+
import { mkdir as mkdir21, readdir as readdir24, readFile as readFile50, writeFile as writeFile33 } from "fs/promises";
|
|
101397
|
+
import path64 from "path";
|
|
101226
101398
|
import { defineCommand as defineCommand239 } from "citty";
|
|
101227
101399
|
import { parse as parseYaml5 } from "yaml";
|
|
101228
|
-
import { z as
|
|
101400
|
+
import { z as z77 } from "zod";
|
|
101229
101401
|
|
|
101230
101402
|
// src/commands/research/reddit.ts
|
|
101231
101403
|
import { defineCommand as defineCommand238 } from "citty";
|
|
@@ -101637,13 +101809,13 @@ var PLATFORM_NAMES = {
|
|
|
101637
101809
|
app_store: "App Store",
|
|
101638
101810
|
google_play: "Google Play"
|
|
101639
101811
|
};
|
|
101640
|
-
var LibraryReviewSchema =
|
|
101641
|
-
_id:
|
|
101642
|
-
reviewText:
|
|
101643
|
-
sourceType:
|
|
101644
|
-
reviewerName:
|
|
101645
|
-
rating:
|
|
101646
|
-
reviewDate:
|
|
101812
|
+
var LibraryReviewSchema = z77.object({
|
|
101813
|
+
_id: z77.string().min(1),
|
|
101814
|
+
reviewText: z77.string(),
|
|
101815
|
+
sourceType: z77.string().optional(),
|
|
101816
|
+
reviewerName: z77.string().optional(),
|
|
101817
|
+
rating: z77.number().optional(),
|
|
101818
|
+
reviewDate: z77.number().optional()
|
|
101647
101819
|
});
|
|
101648
101820
|
function dayOf(ms) {
|
|
101649
101821
|
const date = new Date(ms);
|
|
@@ -101726,13 +101898,13 @@ function buildCustomersPrompt(sources, focus) {
|
|
|
101726
101898
|
...sources.map((source, i) => `[${i + 1}] ${source.text}`)
|
|
101727
101899
|
].join("\n");
|
|
101728
101900
|
}
|
|
101729
|
-
var AnswerSchema =
|
|
101730
|
-
var CandidateSchema =
|
|
101731
|
-
source:
|
|
101732
|
-
quote:
|
|
101733
|
-
type:
|
|
101734
|
-
force:
|
|
101735
|
-
persona:
|
|
101901
|
+
var AnswerSchema = z77.object({ insights: z77.array(z77.unknown()) });
|
|
101902
|
+
var CandidateSchema = z77.object({
|
|
101903
|
+
source: z77.coerce.number().int().positive(),
|
|
101904
|
+
quote: z77.string().trim().min(1),
|
|
101905
|
+
type: z77.enum(INSIGHT_TYPES).catch("other"),
|
|
101906
|
+
force: z77.enum(FORCES).optional().catch(void 0),
|
|
101907
|
+
persona: z77.string().trim().optional().catch(void 0)
|
|
101736
101908
|
});
|
|
101737
101909
|
function candidatesFrom(answer) {
|
|
101738
101910
|
const parsed = AnswerSchema.safeParse(answer);
|
|
@@ -101774,7 +101946,7 @@ function customersHints(input) {
|
|
|
101774
101946
|
const cite = `Cite these by id in an idea's \`evidence\` (src/creatives/_ideas/<idea>.md); write the ad from the quote as said.`;
|
|
101775
101947
|
return written < FEW_OBSERVED ? [`Only ${written} observed. ${widen}`, cite] : [cite];
|
|
101776
101948
|
}
|
|
101777
|
-
var INSIGHTS_DIR2 =
|
|
101949
|
+
var INSIGHTS_DIR2 = path64.join("src", "content", "insights");
|
|
101778
101950
|
var BATCH_CHARS = 24e3;
|
|
101779
101951
|
var MAX_BATCHES = 5;
|
|
101780
101952
|
var WEB_PAGES = 3;
|
|
@@ -101823,7 +101995,7 @@ var ARGS15 = {
|
|
|
101823
101995
|
}
|
|
101824
101996
|
};
|
|
101825
101997
|
registerSchema({ command: "research.customers", description: DESCRIPTION13, args: ARGS15 });
|
|
101826
|
-
var QuoteFrontmatter =
|
|
101998
|
+
var QuoteFrontmatter = z77.object({ quote: z77.string() });
|
|
101827
101999
|
function quoteOf(raw) {
|
|
101828
102000
|
const front = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1];
|
|
101829
102001
|
if (!front) return "";
|
|
@@ -101839,12 +102011,12 @@ async function readExistingInsights(dir) {
|
|
|
101839
102011
|
return await Promise.all(
|
|
101840
102012
|
files.map(async (file) => ({
|
|
101841
102013
|
id: file.slice(0, -3),
|
|
101842
|
-
quote: quoteOf(await
|
|
102014
|
+
quote: quoteOf(await readFile50(path64.join(dir, file), "utf8").catch(() => ""))
|
|
101843
102015
|
}))
|
|
101844
102016
|
);
|
|
101845
102017
|
}
|
|
101846
102018
|
async function productSlugs(root) {
|
|
101847
|
-
const files = await readdir24(
|
|
102019
|
+
const files = await readdir24(path64.join(root, "src", "content", "products")).catch(() => []);
|
|
101848
102020
|
return files.filter((f) => f.endsWith(".md")).map((f) => f.slice(0, -3));
|
|
101849
102021
|
}
|
|
101850
102022
|
async function gatherTestimonials(focus) {
|
|
@@ -101867,7 +102039,7 @@ async function gatherReddit(query) {
|
|
|
101867
102039
|
const items = answers.flatMap((a) => a.status === "fulfilled" ? a.value.results : []).filter((item) => !seen.has(item.id) && Boolean(seen.add(item.id)));
|
|
101868
102040
|
return sourcesFromReddit(items);
|
|
101869
102041
|
}
|
|
101870
|
-
var ExploreAnswer =
|
|
102042
|
+
var ExploreAnswer = z77.object({ sources: z77.array(z77.object({ url: z77.string() })) });
|
|
101871
102043
|
async function resolveGroundingRedirect(url) {
|
|
101872
102044
|
if (!URL.canParse(url) || new URL(url).host !== "vertexaisearch.cloud.google.com") return url;
|
|
101873
102045
|
try {
|
|
@@ -101944,7 +102116,7 @@ async function classifyAll(sources, focus, root) {
|
|
|
101944
102116
|
const batches = batchSources(sources, BATCH_CHARS).slice(0, MAX_BATCHES);
|
|
101945
102117
|
const spent = [];
|
|
101946
102118
|
const answers = await Promise.all(batches.map((batch) => classifyBatch(batch, focus, (u) => spent.push(u))));
|
|
101947
|
-
await holdUnbilledSpend(
|
|
102119
|
+
await holdUnbilledSpend(path64.join(root, "canvas", ".cache"), spent);
|
|
101948
102120
|
if (batches.length > 0 && answers.every((a) => a === null)) return null;
|
|
101949
102121
|
const result = { kept: [], dropped: 0, tooLong: 0 };
|
|
101950
102122
|
batches.forEach((batch, i) => {
|
|
@@ -101957,11 +102129,11 @@ async function classifyAll(sources, focus, root) {
|
|
|
101957
102129
|
}
|
|
101958
102130
|
async function writeInsights(dir, writes, opts) {
|
|
101959
102131
|
if (writes.length === 0) return [];
|
|
101960
|
-
await
|
|
102132
|
+
await mkdir21(dir, { recursive: true });
|
|
101961
102133
|
const written = [];
|
|
101962
102134
|
for (const entry of writes) {
|
|
101963
102135
|
const file = renderInsightFile(entry.insight, opts);
|
|
101964
|
-
const ok = await
|
|
102136
|
+
const ok = await writeFile33(path64.join(dir, `${entry.id}.md`), file, { encoding: "utf8", flag: "wx" }).then(
|
|
101965
102137
|
() => true,
|
|
101966
102138
|
() => false
|
|
101967
102139
|
);
|
|
@@ -102012,7 +102184,7 @@ var customersCommand = defineCommand239({
|
|
|
102012
102184
|
"Run it again. Do not write insights by hand: an insight is only a quote this command found in its source."
|
|
102013
102185
|
);
|
|
102014
102186
|
}
|
|
102015
|
-
const dir =
|
|
102187
|
+
const dir = path64.join(root, INSIGHTS_DIR2);
|
|
102016
102188
|
const plan = planInsightWrites(gated.kept, await readExistingInsights(dir), limit);
|
|
102017
102189
|
const today2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
102018
102190
|
const shown = dryRun ? plan.writes : await writeInsights(dir, plan.writes, { today: today2, ...product ? { product } : {} });
|
|
@@ -103614,8 +103786,8 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
|
|
|
103614
103786
|
});
|
|
103615
103787
|
|
|
103616
103788
|
// src/commands/scheduled-agents/templates.ts
|
|
103617
|
-
import { readFile as
|
|
103618
|
-
import
|
|
103789
|
+
import { readFile as readFile51 } from "fs/promises";
|
|
103790
|
+
import path65 from "path";
|
|
103619
103791
|
import { defineCommand as defineCommand256 } from "citty";
|
|
103620
103792
|
registerSchema({
|
|
103621
103793
|
command: "scheduled-agents.templates",
|
|
@@ -103722,7 +103894,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
|
|
|
103722
103894
|
}
|
|
103723
103895
|
if (save.length > 0) {
|
|
103724
103896
|
const briefFile = flag2("brief-file");
|
|
103725
|
-
const brief2 = briefFile.length > 0 ? await
|
|
103897
|
+
const brief2 = briefFile.length > 0 ? await readFile51(path65.resolve(briefFile), "utf8") : flag2("brief");
|
|
103726
103898
|
if (brief2.trim().length === 0) {
|
|
103727
103899
|
failValidation6("--brief-file (preferred) or --brief is required: the brief is the recipe.");
|
|
103728
103900
|
}
|
|
@@ -104267,8 +104439,8 @@ function onOff(flag2, value2) {
|
|
|
104267
104439
|
if (value2 === "off" || value2 === "false") return false;
|
|
104268
104440
|
throw new SiteArgsError(`--${flag2} must be on or off`);
|
|
104269
104441
|
}
|
|
104270
|
-
function parseProfile(raw,
|
|
104271
|
-
const text3 = raw.startsWith("@") ?
|
|
104442
|
+
function parseProfile(raw, readFile52) {
|
|
104443
|
+
const text3 = raw.startsWith("@") ? readFile52(raw.slice(1)) : raw;
|
|
104272
104444
|
let parsed;
|
|
104273
104445
|
try {
|
|
104274
104446
|
parsed = JSON.parse(text3);
|
|
@@ -104280,7 +104452,7 @@ function parseProfile(raw, readFile51) {
|
|
|
104280
104452
|
}
|
|
104281
104453
|
return parsed;
|
|
104282
104454
|
}
|
|
104283
|
-
function parseSetArgs(args8,
|
|
104455
|
+
function parseSetArgs(args8, readFile52) {
|
|
104284
104456
|
const inputs = [];
|
|
104285
104457
|
if (args8.consent !== void 0) {
|
|
104286
104458
|
inputs.push({ setting: "consent", mode: oneOf3("consent", args8.consent, SITE_CONSENT_MODES) });
|
|
@@ -104313,7 +104485,7 @@ function parseSetArgs(args8, readFile51) {
|
|
|
104313
104485
|
inputs.push({ setting: "timezone", timezone: args8.timezone });
|
|
104314
104486
|
}
|
|
104315
104487
|
if (args8["export-profile"] !== void 0) {
|
|
104316
|
-
inputs.push({ setting: "exportProfile", profile: parseProfile(args8["export-profile"],
|
|
104488
|
+
inputs.push({ setting: "exportProfile", profile: parseProfile(args8["export-profile"], readFile52) });
|
|
104317
104489
|
}
|
|
104318
104490
|
if (inputs.length === 0) {
|
|
104319
104491
|
throw new SiteArgsError(
|
|
@@ -104504,7 +104676,7 @@ var setCommand = defineCommand264({
|
|
|
104504
104676
|
timezone: args8.timezone,
|
|
104505
104677
|
"export-profile": args8["export-profile"]
|
|
104506
104678
|
},
|
|
104507
|
-
(
|
|
104679
|
+
(path68) => readFileSync26(path68, "utf8")
|
|
104508
104680
|
);
|
|
104509
104681
|
const body = { chatId: requireChatId(), inputs };
|
|
104510
104682
|
writeJson(await apiPost("/api/site/stage", body));
|
|
@@ -104786,7 +104958,7 @@ function parseImageRefs(spec) {
|
|
|
104786
104958
|
}
|
|
104787
104959
|
var defaultDeps = {
|
|
104788
104960
|
ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
|
|
104789
|
-
upload: (
|
|
104961
|
+
upload: (path68) => uploadLocalImage({ file: path68, contentType: detectImageContentType(path68), source: "uploaded" })
|
|
104790
104962
|
};
|
|
104791
104963
|
async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
104792
104964
|
const refs = parseImageRefs(spec);
|
|
@@ -104806,10 +104978,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
|
104806
104978
|
}
|
|
104807
104979
|
return { imageIds, added };
|
|
104808
104980
|
}
|
|
104809
|
-
function uploadFailure(
|
|
104981
|
+
function uploadFailure(path68) {
|
|
104810
104982
|
return (error) => {
|
|
104811
104983
|
if (error instanceof ApiError) throw error;
|
|
104812
|
-
throw new ApiError("VALIDATION_ERROR", `Could not read "${
|
|
104984
|
+
throw new ApiError("VALIDATION_ERROR", `Could not read "${path68}" as an image.`);
|
|
104813
104985
|
};
|
|
104814
104986
|
}
|
|
104815
104987
|
|
|
@@ -105909,10 +106081,10 @@ async function stageOp4(op) {
|
|
|
105909
106081
|
handleError8(err);
|
|
105910
106082
|
}
|
|
105911
106083
|
}
|
|
105912
|
-
async function draftAction3(
|
|
106084
|
+
async function draftAction3(path68, body, chat) {
|
|
105913
106085
|
const chatId = resolveChatId(chat);
|
|
105914
106086
|
try {
|
|
105915
|
-
const { data, hints: hints2 } = splitHints(await apiPost(
|
|
106087
|
+
const { data, hints: hints2 } = splitHints(await apiPost(path68, { chatId, ...body }));
|
|
105916
106088
|
writeJsonEnvelope({ ok: true, data, ...hints2.length > 0 ? { hints: hints2 } : {} });
|
|
105917
106089
|
return data;
|
|
105918
106090
|
} catch (err) {
|
|
@@ -107474,9 +107646,9 @@ Example: baker videos describe j571abc123 --description "Customer explaining how
|
|
|
107474
107646
|
|
|
107475
107647
|
// src/commands/videos/fetch.ts
|
|
107476
107648
|
import { constants } from "fs";
|
|
107477
|
-
import { copyFile as copyFile2, mkdir as
|
|
107649
|
+
import { copyFile as copyFile2, mkdir as mkdir22, mkdtemp as mkdtemp3, rm as rm9 } from "fs/promises";
|
|
107478
107650
|
import { tmpdir as tmpdir4 } from "os";
|
|
107479
|
-
import
|
|
107651
|
+
import path66 from "path";
|
|
107480
107652
|
import { defineCommand as defineCommand293 } from "citty";
|
|
107481
107653
|
|
|
107482
107654
|
// src/commands/videos/videoPlatforms.ts
|
|
@@ -107644,10 +107816,10 @@ var fetchCommand2 = defineCommand293({
|
|
|
107644
107816
|
}
|
|
107645
107817
|
});
|
|
107646
107818
|
async function fetchVideo(args8) {
|
|
107647
|
-
const outDir =
|
|
107648
|
-
const workDir = await mkdtemp3(
|
|
107819
|
+
const outDir = path66.resolve(args8.out);
|
|
107820
|
+
const workDir = await mkdtemp3(path66.join(tmpdir4(), "videos-fetch-"));
|
|
107649
107821
|
try {
|
|
107650
|
-
await
|
|
107822
|
+
await mkdir22(outDir, { recursive: true });
|
|
107651
107823
|
const vimeoCookie = captureVimeoCookie();
|
|
107652
107824
|
const probe = await probeYtDlp({ url: args8.url, vimeoCookie, cookieDir: workDir });
|
|
107653
107825
|
if (isAudioOnly(probe.info)) {
|
|
@@ -107690,7 +107862,7 @@ function fileStem(info) {
|
|
|
107690
107862
|
}
|
|
107691
107863
|
async function placeInto(outDir, source, stem) {
|
|
107692
107864
|
for (let n = 1; ; n++) {
|
|
107693
|
-
const target2 =
|
|
107865
|
+
const target2 = path66.join(outDir, `${stem}${n === 1 ? "" : `-${n}`}${path66.extname(source)}`);
|
|
107694
107866
|
try {
|
|
107695
107867
|
await copyFile2(source, target2, constants.COPYFILE_EXCL);
|
|
107696
107868
|
return target2;
|
|
@@ -107777,7 +107949,7 @@ var groupCommand2 = defineCommand295({
|
|
|
107777
107949
|
// src/commands/videos/ingest.ts
|
|
107778
107950
|
import { mkdtemp as mkdtemp4, rm as rm10, stat as stat13 } from "fs/promises";
|
|
107779
107951
|
import { tmpdir as tmpdir5 } from "os";
|
|
107780
|
-
import
|
|
107952
|
+
import path67 from "path";
|
|
107781
107953
|
import { defineCommand as defineCommand296 } from "citty";
|
|
107782
107954
|
|
|
107783
107955
|
// src/lib/streamUpload.ts
|
|
@@ -108017,7 +108189,7 @@ function ingestUrl(args8) {
|
|
|
108017
108189
|
}
|
|
108018
108190
|
async function downloadThenIngest(args8, country) {
|
|
108019
108191
|
const vimeoCookie = captureVimeoCookie();
|
|
108020
|
-
const workDir = await mkdtemp4(
|
|
108192
|
+
const workDir = await mkdtemp4(path67.join(tmpdir5(), "videos-ingest-"));
|
|
108021
108193
|
try {
|
|
108022
108194
|
const probe = await probeYtDlp({ url: args8.url, country, vimeoCookie, cookieDir: workDir });
|
|
108023
108195
|
if (isAudioOnly(probe.info)) {
|