@koda-sl/baker-cli 0.439.0-dev.33cf4ca99 → 0.443.0-dev.edb8d8767

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