@koda-sl/baker-cli 0.439.0-dev.33cf4ca99 → 0.440.0-dev.dab0aaba9

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