@koda-sl/baker-cli 0.434.0-dev.42b55fa66 → 0.440.0-dev.dab0aaba9

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