@koda-sl/baker-cli 0.408.0 → 0.410.0

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 (26) hide show
  1. package/README.md +14 -23
  2. package/dist/{chunk-N72QYDJY.js → chunk-ISCXDKWB.js} +25 -8
  3. package/dist/{chunk-N72QYDJY.js.map → chunk-ISCXDKWB.js.map} +1 -1
  4. package/dist/{chunk-5X5LUMVV.js → chunk-ISJOVAJP.js} +2 -2
  5. package/dist/{chunk-KMCVSEGU.js → chunk-KTMFHBN6.js} +3 -3
  6. package/dist/chunk-KTMFHBN6.js.map +1 -0
  7. package/dist/{chunk-WQE4SRLY.js → chunk-S5BY45H7.js} +3 -3
  8. package/dist/{chunk-X37GPJ54.js → chunk-VKWHVMMD.js} +3 -3
  9. package/dist/{chunk-3ZN2DUBU.js → chunk-ZIOW64BL.js} +4 -4
  10. package/dist/cli.js +750 -685
  11. package/dist/cli.js.map +1 -1
  12. package/dist/{client-BPKOHQWA.js → client-AY6TPFQ4.js} +4 -4
  13. package/dist/engine/index.js +3 -3
  14. package/dist/{env-B7V77K6K.js → env-CWEGTJF3.js} +2 -2
  15. package/dist/{output-5M2QPMKZ.js → output-EPF3HHJ4.js} +5 -5
  16. package/dist/{shared-OEQKSDWO.js → shared-53G34I3U.js} +6 -6
  17. package/package.json +1 -1
  18. package/dist/chunk-KMCVSEGU.js.map +0 -1
  19. /package/dist/{chunk-5X5LUMVV.js.map → chunk-ISJOVAJP.js.map} +0 -0
  20. /package/dist/{chunk-WQE4SRLY.js.map → chunk-S5BY45H7.js.map} +0 -0
  21. /package/dist/{chunk-X37GPJ54.js.map → chunk-VKWHVMMD.js.map} +0 -0
  22. /package/dist/{chunk-3ZN2DUBU.js.map → chunk-ZIOW64BL.js.map} +0 -0
  23. /package/dist/{client-BPKOHQWA.js.map → client-AY6TPFQ4.js.map} +0 -0
  24. /package/dist/{env-B7V77K6K.js.map → env-CWEGTJF3.js.map} +0 -0
  25. /package/dist/{output-5M2QPMKZ.js.map → output-EPF3HHJ4.js.map} +0 -0
  26. /package/dist/{shared-OEQKSDWO.js.map → shared-53G34I3U.js.map} +0 -0
package/dist/cli.js CHANGED
@@ -97,7 +97,7 @@ import {
97
97
  withoutCastName,
98
98
  wordCount,
99
99
  ytDlpBlockSignal
100
- } from "./chunk-KMCVSEGU.js";
100
+ } from "./chunk-KTMFHBN6.js";
101
101
  import {
102
102
  NAME_CONTAINS_ARG,
103
103
  csvOrJson,
@@ -108,7 +108,7 @@ import {
108
108
  resolveEffectiveStatus,
109
109
  statusFilterHints,
110
110
  todayIso
111
- } from "./chunk-3ZN2DUBU.js";
111
+ } from "./chunk-ZIOW64BL.js";
112
112
  import {
113
113
  buildQueryCacheKey,
114
114
  cacheGet,
@@ -124,19 +124,19 @@ import {
124
124
  writeAdsJson,
125
125
  writeAdsOutput,
126
126
  writeJsonEnvelope
127
- } from "./chunk-X37GPJ54.js";
127
+ } from "./chunk-VKWHVMMD.js";
128
128
  import {
129
129
  ApiError,
130
130
  apiGet,
131
131
  apiGetWithHeaders,
132
132
  apiPost,
133
133
  validateConvexId
134
- } from "./chunk-WQE4SRLY.js";
134
+ } from "./chunk-S5BY45H7.js";
135
135
  import {
136
136
  installExitRecord,
137
137
  installStreamTaps,
138
138
  logInvocation
139
- } from "./chunk-5X5LUMVV.js";
139
+ } from "./chunk-ISJOVAJP.js";
140
140
  import {
141
141
  __commonJS,
142
142
  __esm,
@@ -171,7 +171,7 @@ import {
171
171
  winningAdNormalizer,
172
172
  writeJson,
173
173
  writeOutput
174
- } from "./chunk-N72QYDJY.js";
174
+ } from "./chunk-ISCXDKWB.js";
175
175
 
176
176
  // ../../node_modules/.pnpm/i18n-iso-countries@7.14.0/node_modules/i18n-iso-countries/langs/af.json
177
177
  var require_af = __commonJS({
@@ -21715,7 +21715,7 @@ var require_entry_node = __commonJS({
21715
21715
  });
21716
21716
 
21717
21717
  // src/cli.ts
21718
- import { defineCommand as defineCommand280, runMain } from "citty";
21718
+ import { defineCommand as defineCommand278, runMain } from "citty";
21719
21719
 
21720
21720
  // src/cache-flag.ts
21721
21721
  var NO_CACHE_ARG = {
@@ -22118,6 +22118,7 @@ var validatedHardFiltersSchema = winnersHardFiltersSchema.extend({
22118
22118
  first_seen_after: firstSeenBoundSchema.optional(),
22119
22119
  first_seen_before: firstSeenBoundSchema.optional()
22120
22120
  });
22121
+ var winnersSortSchema = z2.enum(["winner", "longest_running", "newest"]);
22121
22122
  var winnersSearchFields = z2.object({
22122
22123
  free_text_query: z2.string().max(500).optional(),
22123
22124
  ref_ad_id: z2.string().optional(),
@@ -22133,6 +22134,10 @@ var winnersSearchFields = z2.object({
22133
22134
  // One page picked for variety (different brands, containers and hooks)
22134
22135
  // instead of rank order: a reference set for N concepts. Ignores offset.
22135
22136
  diverse: z2.boolean().optional(),
22137
+ // The order of a browse (no query): by winner score (default), by how long
22138
+ // the ad has run, or by when it was first seen. A query ranks by relevance
22139
+ // blended with winner score, so `sort` only means something without one.
22140
+ sort: winnersSortSchema.optional(),
22136
22141
  hard_filters: winnersHardFiltersSchema.optional(),
22137
22142
  // `score_breakdown` surfaces the "why it ranked" panel; facets drive the
22138
22143
  // filter rail. thumbnails/full_dna_url are opt-in extras.
@@ -22140,9 +22145,11 @@ var winnersSearchFields = z2.object({
22140
22145
  });
22141
22146
  var hasSearchInput = (b) => b.free_text_query !== void 0 || b.ref_ad_id !== void 0 || b.hard_filters !== void 0;
22142
22147
  var SEARCH_INPUT_MESSAGE = "Provide free_text_query, ref_ad_id, or hard_filters";
22143
- var winnersSearchRequestSchema = winnersSearchFields.refine(hasSearchInput, { message: SEARCH_INPUT_MESSAGE });
22144
22148
  var winnerSearchScopeSchema = z2.enum(["all", "followed", "followed-first"]);
22145
- var scopedWinnersSearchRequestSchema = winnersSearchFields.extend({ scope: winnerSearchScopeSchema.optional(), hard_filters: validatedHardFiltersSchema.optional() }).refine(hasSearchInput, { message: SEARCH_INPUT_MESSAGE }).refine((b) => b.scope !== "followed-first" || !b.offset, {
22149
+ var scopedWinnersSearchRequestSchema = winnersSearchFields.extend({
22150
+ scope: winnerSearchScopeSchema.default("followed-first"),
22151
+ hard_filters: validatedHardFiltersSchema.optional()
22152
+ }).refine(hasSearchInput, { message: SEARCH_INPUT_MESSAGE }).refine((b) => b.scope !== "followed-first" || !b.offset, {
22146
22153
  message: "offset is not supported with scope followed-first; raise limit instead"
22147
22154
  });
22148
22155
  var winningAdDnaSchema = z2.object({
@@ -22231,7 +22238,12 @@ var winnersSearchResponseSchema = z2.object({
22231
22238
  country_coverage: z2.array(z2.object({ code: z2.string(), ads: z2.number() })).nullable()
22232
22239
  });
22233
22240
  var winnerSourceSchema = z2.enum(["followed", "library"]);
22234
- var referenceUsesSchema = z2.object({ companies: z2.number(), ours: z2.boolean() });
22241
+ var referenceUsesSchema = z2.object({
22242
+ companies: z2.number(),
22243
+ ours: z2.boolean(),
22244
+ approved: z2.number(),
22245
+ rejected: z2.number()
22246
+ });
22235
22247
  var scopedWinningAdSchema = winningAdSchema.extend({
22236
22248
  source: winnerSourceSchema,
22237
22249
  baker_uses: referenceUsesSchema.optional()
@@ -22277,10 +22289,6 @@ var advertiserConvergenceSchema = z2.object({
22277
22289
  iteration_velocity_30d: z2.number().nullable(),
22278
22290
  last_refresh_completed_at: z2.string().nullable()
22279
22291
  });
22280
- var resolvePreviewRequestSchema = z2.object({
22281
- input: z2.string().min(1),
22282
- platform: adLibraryPlatformSchema
22283
- });
22284
22292
  var resolvePreviewSchema = z2.object({
22285
22293
  platform: adLibraryPlatformSchema,
22286
22294
  external_id: z2.string(),
@@ -22365,11 +22373,6 @@ var unfollowRequestSchema = z2.object({
22365
22373
  advertiser_id: z2.string().optional()
22366
22374
  });
22367
22375
  var unfollowResponseSchema = z2.object({ removed: z2.boolean() });
22368
- var advertiserWinnersRequestSchema = z2.object({
22369
- advertiser_id: z2.string().min(1),
22370
- top: z2.number().int().min(1).max(100).optional(),
22371
- platform: adLibraryPlatformSchema.optional()
22372
- });
22373
22376
  var advertiserWinnersResponseSchema = z2.object({
22374
22377
  advertiser_id: z2.string(),
22375
22378
  winners: z2.array(winningAdSchema)
@@ -22389,7 +22392,9 @@ var feedRequestSchema = z2.object({
22389
22392
  limit: z2.number().int().min(1).max(200).optional()
22390
22393
  });
22391
22394
  var feedResponseSchema = z2.object({
22392
- winners: z2.array(winningAdSchema),
22395
+ // The feed only ever reads followed brands, so every winner is `followed`:
22396
+ // the same `source` a scoped search carries, so the surfaces agree.
22397
+ winners: z2.array(winningAdSchema.extend({ source: winnerSourceSchema })),
22393
22398
  // How many brands the company follows (regardless of the trim).
22394
22399
  following_count: z2.number(),
22395
22400
  // The advertiser ids actually queried (follows ∩ requested subset).
@@ -22398,39 +22403,6 @@ var feedResponseSchema = z2.object({
22398
22403
  // followed them yet.
22399
22404
  unknown_requested: z2.array(z2.string())
22400
22405
  });
22401
- var briefRequestSchema = z2.object({
22402
- // Partial DNA describing the target creative (all optional); the service
22403
- // pulls its own strategically-similar references.
22404
- target_dna: z2.record(z2.string(), z2.unknown()).optional(),
22405
- k_reference_ads: z2.number().int().min(1).max(50).optional(),
22406
- notes: z2.string().optional()
22407
- });
22408
- var briefResponseSchema = z2.object({
22409
- brief_markdown: z2.string(),
22410
- reference_ad_ids: z2.array(z2.string())
22411
- });
22412
- var cohortSelectorSchema = z2.object({
22413
- hard_filters: winnersHardFiltersSchema.optional(),
22414
- ad_ids: z2.array(z2.string()).optional()
22415
- });
22416
- var patternsRequestSchema = z2.object({
22417
- cohort_a: cohortSelectorSchema,
22418
- cohort_b: cohortSelectorSchema,
22419
- top_n: z2.number().int().min(1).max(50).optional()
22420
- });
22421
- var patternDiscriminatorSchema = z2.object({
22422
- field: z2.string(),
22423
- method: z2.string(),
22424
- score: z2.number(),
22425
- direction: z2.enum(["favors_a", "favors_b"]),
22426
- top_values_a: z2.array(z2.string()).nullable(),
22427
- top_values_b: z2.array(z2.string()).nullable()
22428
- });
22429
- var patternsResponseSchema = z2.object({
22430
- cohort_a_size: z2.number(),
22431
- cohort_b_size: z2.number(),
22432
- discriminators: z2.array(patternDiscriminatorSchema)
22433
- });
22434
22406
  var seedCatalogFieldSchema = z2.enum(["hook_archetype", "mechanism_type", "static_format_pattern"]);
22435
22407
  var seedCatalogRequestSchema = z2.object({
22436
22408
  field: seedCatalogFieldSchema,
@@ -34520,16 +34492,16 @@ async function runChatgptRead(reference, read, hintsFor4) {
34520
34492
  }
34521
34493
  }
34522
34494
  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.";
34523
- async function getChatgptList(path59, params) {
34524
- const { data, headers } = await apiGetWithHeaders(path59, params);
34495
+ async function getChatgptList(path58, params) {
34496
+ const { data, headers } = await apiGetWithHeaders(path58, params);
34525
34497
  return { data, truncated: headers.get(CHATGPT_ADS_TRUNCATED_HEADER) === "true" };
34526
34498
  }
34527
- async function runChatgptListRead(reference, path59, params, hintsFor4) {
34499
+ async function runChatgptListRead(reference, path58, params, hintsFor4) {
34528
34500
  let truncated = false;
34529
34501
  await runChatgptRead(
34530
34502
  reference,
34531
34503
  async () => {
34532
- const read = await getChatgptList(path59, params);
34504
+ const read = await getChatgptList(path58, params);
34533
34505
  truncated = read.truncated;
34534
34506
  return read.data;
34535
34507
  },
@@ -35334,8 +35306,8 @@ var MEMBER_ARGS = {
35334
35306
  },
35335
35307
  country: { type: "string", description: "Country of phones written without a +code, e.g. ES" }
35336
35308
  };
35337
- function membersFromArgs(args8, readFile42 = (path59) => readFileSync2(path59, "utf8")) {
35338
- const fromFile = typeof args8.file === "string" && args8.file ? membersFromFile(readFile42(args8.file)) : { emails: [], phones: [] };
35309
+ function membersFromArgs(args8, readFile43 = (path58) => readFileSync2(path58, "utf8")) {
35310
+ const fromFile = typeof args8.file === "string" && args8.file ? membersFromFile(readFile43(args8.file)) : { emails: [], phones: [] };
35339
35311
  const given = [...csvArg(args8.emails) ?? [], ...fromFile.emails];
35340
35312
  const givenPhones = [...csvArg(args8.phones) ?? [], ...fromFile.phones];
35341
35313
  const members = hashedMembers({
@@ -38709,9 +38681,9 @@ var COPY_LIST_FLAG = {
38709
38681
  descriptions: "--descriptions",
38710
38682
  longHeadlines: "--long-headlines"
38711
38683
  };
38712
- function valueAt(root, path59) {
38684
+ function valueAt(root, path58) {
38713
38685
  let node = root;
38714
- for (const key of path59) {
38686
+ for (const key of path58) {
38715
38687
  if (node === null || typeof node !== "object") return void 0;
38716
38688
  node = node[key];
38717
38689
  }
@@ -38944,11 +38916,11 @@ function parseKeywordEntry(raw, defaultMatch) {
38944
38916
  function rawTextEntries(value2, rawArgs) {
38945
38917
  return repeatedValues(rawArgs, "text", value2).flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
38946
38918
  }
38947
- function rawFileEntries(path59) {
38948
- if (typeof path59 !== "string" || path59.length === 0) {
38919
+ function rawFileEntries(path58) {
38920
+ if (typeof path58 !== "string" || path58.length === 0) {
38949
38921
  return [];
38950
38922
  }
38951
- return readFileSync4(path59, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
38923
+ return readFileSync4(path58, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
38952
38924
  }
38953
38925
  function keywordEntries(args8, rawArgs) {
38954
38926
  const defaultMatch = typeof args8["match-type"] === "string" ? args8["match-type"].toUpperCase() : void 0;
@@ -38971,14 +38943,14 @@ function keywordEntries(args8, rawArgs) {
38971
38943
  }
38972
38944
  return entries;
38973
38945
  }
38974
- function loadJsonFileArg(path59) {
38975
- if (typeof path59 !== "string" || path59.length === 0) {
38946
+ function loadJsonFileArg(path58) {
38947
+ if (typeof path58 !== "string" || path58.length === 0) {
38976
38948
  return {};
38977
38949
  }
38978
- const inline = path59.trimStart().startsWith("{");
38979
- const source = inline ? "inline JSON" : path59;
38950
+ const inline = path58.trimStart().startsWith("{");
38951
+ const source = inline ? "inline JSON" : path58;
38980
38952
  try {
38981
- const parsed = JSON.parse(inline ? path59 : readFileSync4(path59, "utf8"));
38953
+ const parsed = JSON.parse(inline ? path58 : readFileSync4(path58, "utf8"));
38982
38954
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
38983
38955
  failWriteValidation2(`${source} must contain a JSON object`);
38984
38956
  }
@@ -39115,10 +39087,10 @@ async function stageUpdate(kind, customerId, target2, payload, hints2) {
39115
39087
  async function stageTarget(kind, customerId, target2, hints2) {
39116
39088
  await stageGoogleOp({ kind, customerId, target: target2 }, hints2);
39117
39089
  }
39118
- async function draftAction(path59, body, chat) {
39090
+ async function draftAction(path58, body, chat) {
39119
39091
  try {
39120
39092
  const chatId = resolveChatId(chat);
39121
- const response = await apiPost(path59, { chatId, ...body });
39093
+ const response = await apiPost(path58, { chatId, ...body });
39122
39094
  writeJsonEnvelope(response);
39123
39095
  } catch (err) {
39124
39096
  handleGoogleError(err);
@@ -41538,14 +41510,14 @@ function mappingRowProblem(row) {
41538
41510
 
41539
41511
  // ../api/src/event-sources/template.ts
41540
41512
  var MENTION = /\{\{\s*([^{}]+?)\s*\}\}/g;
41541
- function mentionOf(path59) {
41542
- return `{{${path59}}}`;
41513
+ function mentionOf(path58) {
41514
+ return `{{${path58}}}`;
41543
41515
  }
41544
41516
  function templateMentions(template) {
41545
41517
  const seen = [];
41546
41518
  for (const match of template.matchAll(MENTION)) {
41547
- const path59 = (match[1] ?? "").trim();
41548
- if (path59 !== "" && !seen.includes(path59)) seen.push(path59);
41519
+ const path58 = (match[1] ?? "").trim();
41520
+ if (path58 !== "" && !seen.includes(path58)) seen.push(path58);
41549
41521
  }
41550
41522
  return seen;
41551
41523
  }
@@ -44920,17 +44892,17 @@ function ignoredColumnHints(columns) {
44920
44892
  `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.`
44921
44893
  ];
44922
44894
  }
44923
- function readRowsFile(path59) {
44924
- const inline = path59.trimStart().startsWith("[") || path59.trimStart().startsWith("{");
44895
+ function readRowsFile(path58) {
44896
+ const inline = path58.trimStart().startsWith("[") || path58.trimStart().startsWith("{");
44925
44897
  let text2;
44926
44898
  try {
44927
- text2 = inline ? path59 : readFileSync6(path59, "utf8");
44899
+ text2 = inline ? path58 : readFileSync6(path58, "utf8");
44928
44900
  } catch (err) {
44929
- failWriteValidation2(`could not read ${path59}: ${err instanceof Error ? err.message : String(err)}`);
44901
+ failWriteValidation2(`could not read ${path58}: ${err instanceof Error ? err.message : String(err)}`);
44930
44902
  }
44931
44903
  const parsed = parseConversionRowsText(text2);
44932
44904
  if (!parsed.ok) {
44933
- failWriteValidation2(`${inline ? "the conversions passed inline" : path59}: ${parsed.error}`);
44905
+ failWriteValidation2(`${inline ? "the conversions passed inline" : path58}: ${parsed.error}`);
44934
44906
  }
44935
44907
  return { rows: parsed.rows, ignoredColumns: parsed.ignoredColumns };
44936
44908
  }
@@ -46831,19 +46803,19 @@ function failWriteValidation3(message) {
46831
46803
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
46832
46804
  process.exit(1);
46833
46805
  }
46834
- function loadJsonFileArg2(path59) {
46835
- if (typeof path59 !== "string" || path59.length === 0) {
46806
+ function loadJsonFileArg2(path58) {
46807
+ if (typeof path58 !== "string" || path58.length === 0) {
46836
46808
  return {};
46837
46809
  }
46838
46810
  try {
46839
- const parsed = JSON.parse(readFileSync7(path59, "utf8"));
46811
+ const parsed = JSON.parse(readFileSync7(path58, "utf8"));
46840
46812
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
46841
- failWriteValidation3(`${path59} must contain a JSON object`);
46813
+ failWriteValidation3(`${path58} must contain a JSON object`);
46842
46814
  }
46843
46815
  return parsed;
46844
46816
  } catch (err) {
46845
46817
  if (err instanceof SyntaxError) {
46846
- failWriteValidation3(`${path59} is not valid JSON: ${err.message}`);
46818
+ failWriteValidation3(`${path58} is not valid JSON: ${err.message}`);
46847
46819
  }
46848
46820
  throw err;
46849
46821
  }
@@ -46928,15 +46900,15 @@ function parseLocaleFlag(value2) {
46928
46900
  }
46929
46901
  return { language: match[1], country: match[2].toUpperCase() };
46930
46902
  }
46931
- function loadTargetingFileArg(path59) {
46932
- if (typeof path59 !== "string" || path59.length === 0) {
46903
+ function loadTargetingFileArg(path58) {
46904
+ if (typeof path58 !== "string" || path58.length === 0) {
46933
46905
  return void 0;
46934
46906
  }
46935
- const parsed = loadJsonFileArg2(path59);
46907
+ const parsed = loadJsonFileArg2(path58);
46936
46908
  const criteria = parsed.targetingCriteria ?? parsed;
46937
46909
  if (!criteria.include) {
46938
46910
  failWriteValidation3(
46939
- `${path59} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
46911
+ `${path58} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
46940
46912
  );
46941
46913
  }
46942
46914
  return criteria;
@@ -46971,14 +46943,14 @@ function parseCsvLine(line) {
46971
46943
  cells.push(current);
46972
46944
  return cells.map((cell3) => cell3.trim());
46973
46945
  }
46974
- function parseListFileArg(path59, maxRows) {
46975
- if (typeof path59 !== "string" || path59.length === 0) {
46946
+ function parseListFileArg(path58, maxRows) {
46947
+ if (typeof path58 !== "string" || path58.length === 0) {
46976
46948
  return void 0;
46977
46949
  }
46978
- const raw = readFileSync7(path59, "utf8");
46950
+ const raw = readFileSync7(path58, "utf8");
46979
46951
  const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
46980
46952
  if (lines.length < 2) {
46981
- failWriteValidation3(`${path59} needs a header row and at least one data row`);
46953
+ failWriteValidation3(`${path58} needs a header row and at least one data row`);
46982
46954
  }
46983
46955
  const columns = parseCsvLine(lines[0]).map((column) => column.trim());
46984
46956
  const rows = [];
@@ -46997,7 +46969,7 @@ function parseListFileArg(path59, maxRows) {
46997
46969
  }
46998
46970
  }
46999
46971
  if (rows.length > maxRows) {
47000
- failWriteValidation3(`${path59} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
46972
+ failWriteValidation3(`${path58} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
47001
46973
  }
47002
46974
  return { columns, rows };
47003
46975
  }
@@ -47093,11 +47065,11 @@ function readPositionals(args8) {
47093
47065
  function splitIdList(raw) {
47094
47066
  return raw.split(",").map((id) => id.trim()).filter(Boolean);
47095
47067
  }
47096
- function idsFileEntries(path59) {
47097
- if (typeof path59 !== "string" || path59.length === 0) {
47068
+ function idsFileEntries(path58) {
47069
+ if (typeof path58 !== "string" || path58.length === 0) {
47098
47070
  return [];
47099
47071
  }
47100
- return readFileSync7(path59, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
47072
+ return readFileSync7(path58, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
47101
47073
  }
47102
47074
  function requireTargets2(args8, entity) {
47103
47075
  const positionals = readPositionals(args8);
@@ -49858,9 +49830,9 @@ import { defineCommand as defineCommand64 } from "citty";
49858
49830
  function asRecord(value2) {
49859
49831
  return typeof value2 === "object" && value2 !== null && !Array.isArray(value2) ? value2 : void 0;
49860
49832
  }
49861
- function readPath2(row, path59) {
49833
+ function readPath2(row, path58) {
49862
49834
  let current = row;
49863
- for (const segment of path59.split(".")) {
49835
+ for (const segment of path58.split(".")) {
49864
49836
  const record = asRecord(current);
49865
49837
  if (!record) return void 0;
49866
49838
  current = record[segment];
@@ -49870,10 +49842,10 @@ function readPath2(row, path59) {
49870
49842
  function projectFields(rows, paths) {
49871
49843
  return rows.map((row) => {
49872
49844
  const projected = {};
49873
- for (const path59 of paths) {
49874
- const value2 = readPath2(row, path59);
49845
+ for (const path58 of paths) {
49846
+ const value2 = readPath2(row, path58);
49875
49847
  if (value2 !== void 0) {
49876
- projected[path59] = value2;
49848
+ projected[path58] = value2;
49877
49849
  }
49878
49850
  }
49879
49851
  return projected;
@@ -49886,16 +49858,16 @@ function shapeListRows(rows, args8, compact5) {
49886
49858
  const paths = fields.split(",").map((field) => field.trim()).filter((field) => field.length > 0);
49887
49859
  if (paths.length > 0) {
49888
49860
  const projected = projectFields(raw, paths).map((row, i) => {
49889
- const missing = paths.filter((path59) => row[path59] === void 0);
49861
+ const missing = paths.filter((path58) => row[path58] === void 0);
49890
49862
  const source = rows[i];
49891
49863
  if (missing.length === 0 || source === void 0) return row;
49892
49864
  const compactRow3 = compact5.row(source);
49893
- for (const path59 of missing) {
49894
- if (compactRow3[path59] !== void 0) row[path59] = compactRow3[path59];
49865
+ for (const path58 of missing) {
49866
+ if (compactRow3[path58] !== void 0) row[path58] = compactRow3[path58];
49895
49867
  }
49896
49868
  return row;
49897
49869
  });
49898
- const unmatched = paths.filter((path59) => projected.every((row) => row[path59] === void 0));
49870
+ const unmatched = paths.filter((path58) => projected.every((row) => row[path58] === void 0));
49899
49871
  if (projected.length === 0 || unmatched.length === 0) return { rows: projected, fields: paths };
49900
49872
  const carried = /* @__PURE__ */ new Set([...compact5.columns, ...raw.flatMap((row) => Object.keys(row))]);
49901
49873
  return {
@@ -51255,54 +51227,54 @@ function ruleFields(node) {
51255
51227
  ([key, value2]) => (key === "and" || key === "or") && Array.isArray(value2) ? value2.flatMap(ruleFields) : [key]
51256
51228
  );
51257
51229
  }
51258
- function branchRefusal(key, value2, path59) {
51230
+ function branchRefusal(key, value2, path58) {
51259
51231
  if (!Array.isArray(value2) || value2.length === 0) {
51260
- return `${path59}.${key} must be a non-empty array of conditions \u2014 an empty one counts every event the pixel sends`;
51232
+ return `${path58}.${key} must be a non-empty array of conditions \u2014 an empty one counts every event the pixel sends`;
51261
51233
  }
51262
51234
  for (const [index, child] of value2.entries()) {
51263
- const refusal = ruleNodeRefusal(child, `${path59}.${key}[${index}]`);
51235
+ const refusal = ruleNodeRefusal(child, `${path58}.${key}[${index}]`);
51264
51236
  if (refusal) {
51265
51237
  return refusal;
51266
51238
  }
51267
51239
  }
51268
51240
  return void 0;
51269
51241
  }
51270
- function conditionRefusal(key, value2, path59) {
51271
- return key === "and" || key === "or" ? branchRefusal(key, value2, path59) : leafRefusal(key, value2, path59);
51242
+ function conditionRefusal(key, value2, path58) {
51243
+ return key === "and" || key === "or" ? branchRefusal(key, value2, path58) : leafRefusal(key, value2, path58);
51272
51244
  }
51273
- function ruleNodeRefusal(node, path59) {
51245
+ function ruleNodeRefusal(node, path58) {
51274
51246
  if (!isRecord2(node)) {
51275
- return `${path59} must be an object like {"URL":{"i_contains":"/thank-you"}}`;
51247
+ return `${path58} must be an object like {"URL":{"i_contains":"/thank-you"}}`;
51276
51248
  }
51277
51249
  const entries = Object.entries(node);
51278
51250
  if (entries.length === 0) {
51279
- return `${path59} is empty \u2014 a rule with no condition counts every event the pixel sends`;
51251
+ return `${path58} is empty \u2014 a rule with no condition counts every event the pixel sends`;
51280
51252
  }
51281
51253
  if (entries.length > 1) {
51282
- return `${path59} 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]}`;
51254
+ return `${path58} 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]}`;
51283
51255
  }
51284
51256
  for (const [key, value2] of entries) {
51285
- const refusal = conditionRefusal(key, value2, path59);
51257
+ const refusal = conditionRefusal(key, value2, path58);
51286
51258
  if (refusal) {
51287
51259
  return refusal;
51288
51260
  }
51289
51261
  }
51290
51262
  return void 0;
51291
51263
  }
51292
- function leafRefusal(field, condition, path59) {
51264
+ function leafRefusal(field, condition, path58) {
51293
51265
  if (!isRecord2(condition)) {
51294
- return `${path59}.${field} must be a test like {"i_contains":"/thank-you"}, not a bare value`;
51266
+ return `${path58}.${field} must be a test like {"i_contains":"/thank-you"}, not a bare value`;
51295
51267
  }
51296
51268
  const tests = Object.entries(condition);
51297
51269
  if (tests.length === 0) {
51298
- return `${path59}.${field} names no test \u2014 use one of ${Object.keys(RULE_OPERATORS).join(", ")}`;
51270
+ return `${path58}.${field} names no test \u2014 use one of ${Object.keys(RULE_OPERATORS).join(", ")}`;
51299
51271
  }
51300
51272
  if (tests.length > 1) {
51301
- return `${path59}.${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]}`;
51273
+ return `${path58}.${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]}`;
51302
51274
  }
51303
51275
  for (const [operator] of tests) {
51304
51276
  if (!RULE_OPERATORS[operator]) {
51305
- return `${path59}.${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)`;
51277
+ return `${path58}.${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)`;
51306
51278
  }
51307
51279
  }
51308
51280
  return void 0;
@@ -52018,11 +51990,11 @@ var updateStatusSchema2 = z45.enum(UPDATE_STATUSES);
52018
51990
  function currencyMinimums2(currencyCode) {
52019
51991
  return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
52020
51992
  }
52021
- function validateDailyBudgetFloor(money, ctx, path59) {
51993
+ function validateDailyBudgetFloor(money, ctx, path58) {
52022
51994
  if (money?.currencyCode) {
52023
51995
  const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
52024
51996
  if (Number(money.amount) < min) {
52025
- ctx.addIssue({ code: "custom", path: path59, message: `below the ${min} ${money.currencyCode} daily minimum` });
51997
+ ctx.addIssue({ code: "custom", path: path58, message: `below the ${min} ${money.currencyCode} daily minimum` });
52026
51998
  }
52027
51999
  }
52028
52000
  }
@@ -52940,19 +52912,19 @@ function refuseDeleteByUpdate(payload, group) {
52940
52912
  );
52941
52913
  }
52942
52914
  }
52943
- function loadJsonFileArg3(path59) {
52944
- if (typeof path59 !== "string" || path59.length === 0) {
52915
+ function loadJsonFileArg3(path58) {
52916
+ if (typeof path58 !== "string" || path58.length === 0) {
52945
52917
  return {};
52946
52918
  }
52947
52919
  try {
52948
- const parsed = JSON.parse(readFileSync11(path59, "utf8"));
52920
+ const parsed = JSON.parse(readFileSync11(path58, "utf8"));
52949
52921
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
52950
- failWriteValidation4(`${path59} must contain a JSON object`);
52922
+ failWriteValidation4(`${path58} must contain a JSON object`);
52951
52923
  }
52952
52924
  return parsed;
52953
52925
  } catch (err) {
52954
52926
  if (err instanceof SyntaxError) {
52955
- failWriteValidation4(`${path59} is not valid JSON: ${err.message}`);
52927
+ failWriteValidation4(`${path58} is not valid JSON: ${err.message}`);
52956
52928
  }
52957
52929
  throw err;
52958
52930
  }
@@ -53530,10 +53502,10 @@ Example: baker ads meta ads retag 120252158038400247 --url-tags "utm_source={{si
53530
53502
  }
53531
53503
  },
53532
53504
  run: async ({ args: args8 }) => {
53533
- const { apiPost: apiPost2 } = await import("./client-BPKOHQWA.js");
53534
- const { requireChatId: requireChatId2 } = await import("./env-B7V77K6K.js");
53535
- const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-5M2QPMKZ.js");
53536
- const { handleMetaError: handleMetaError2 } = await import("./shared-OEQKSDWO.js");
53505
+ const { apiPost: apiPost2 } = await import("./client-AY6TPFQ4.js");
53506
+ const { requireChatId: requireChatId2 } = await import("./env-CWEGTJF3.js");
53507
+ const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-EPF3HHJ4.js");
53508
+ const { handleMetaError: handleMetaError2 } = await import("./shared-53G34I3U.js");
53537
53509
  try {
53538
53510
  const accountId = bareAccountId2(args8);
53539
53511
  const urlTags = typeof args8["url-tags"] === "string" ? args8["url-tags"] : void 0;
@@ -53684,10 +53656,10 @@ function duplicateCommand2(entity, label) {
53684
53656
  replace: { type: "boolean", description: "Pause the original once the copy publishes" }
53685
53657
  },
53686
53658
  run: async ({ args: args8 }) => {
53687
- const { apiPost: apiPost2 } = await import("./client-BPKOHQWA.js");
53688
- const { requireChatId: requireChatId2 } = await import("./env-B7V77K6K.js");
53689
- const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-5M2QPMKZ.js");
53690
- const { handleMetaError: handleMetaError2 } = await import("./shared-OEQKSDWO.js");
53659
+ const { apiPost: apiPost2 } = await import("./client-AY6TPFQ4.js");
53660
+ const { requireChatId: requireChatId2 } = await import("./env-CWEGTJF3.js");
53661
+ const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-EPF3HHJ4.js");
53662
+ const { handleMetaError: handleMetaError2 } = await import("./shared-53G34I3U.js");
53691
53663
  try {
53692
53664
  const accountId = bareAccountId2(args8);
53693
53665
  const chatId = requireChatId2();
@@ -58723,12 +58695,12 @@ function flowExists(slug) {
58723
58695
  return existsSync3(join2(flowsDir(), slug, "_data.json"));
58724
58696
  }
58725
58697
  function readFlowTree(slug) {
58726
- const path59 = join2(flowsDir(), slug, "_data.json");
58727
- if (!existsSync3(path59)) {
58698
+ const path58 = join2(flowsDir(), slug, "_data.json");
58699
+ if (!existsSync3(path58)) {
58728
58700
  failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
58729
58701
  }
58730
58702
  try {
58731
- return JSON.parse(readFileSync13(path59, "utf-8"));
58703
+ return JSON.parse(readFileSync13(path58, "utf-8"));
58732
58704
  } catch (error) {
58733
58705
  failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
58734
58706
  }
@@ -59688,12 +59660,12 @@ function readPeopleFile(args8) {
59688
59660
  }
59689
59661
  function unreadablePeopleFile(args8) {
59690
59662
  if (!args8["people-file"]) return void 0;
59691
- const path59 = String(args8["people-file"]);
59663
+ const path58 = String(args8["people-file"]);
59692
59664
  try {
59693
- readFileSync14(path59, "utf8");
59665
+ readFileSync14(path58, "utf8");
59694
59666
  return void 0;
59695
59667
  } catch {
59696
- return `Couldn't read --people-file ${path59}. Check the path, or pass the people inline with --person.`;
59668
+ return `Couldn't read --people-file ${path58}. Check the path, or pass the people inline with --person.`;
59697
59669
  }
59698
59670
  }
59699
59671
  function peopleLimitError(args8) {
@@ -60665,11 +60637,11 @@ function parseFieldArgs(argv) {
60665
60637
  for (const pair of parseRepeated(argv, "--field")) {
60666
60638
  const eq = pair.indexOf("=");
60667
60639
  const field = eq === -1 ? pair : pair.slice(0, eq);
60668
- const path59 = eq === -1 ? "" : pair.slice(eq + 1).trim();
60640
+ const path58 = eq === -1 ? "" : pair.slice(eq + 1).trim();
60669
60641
  const problem = mappingRowProblem(field);
60670
60642
  if (problem !== null) throw new ConversionArgsError(problem);
60671
- if (path59 === "") throw new ConversionArgsError(`--field ${field} needs a path: --field ${field}=<dotted.path>.`);
60672
- mapping[field] = path59;
60643
+ if (path58 === "") throw new ConversionArgsError(`--field ${field} needs a path: --field ${field}=<dotted.path>.`);
60644
+ mapping[field] = path58;
60673
60645
  }
60674
60646
  return mapping;
60675
60647
  }
@@ -62271,11 +62243,11 @@ function unwrap(response) {
62271
62243
  }
62272
62244
  return response.data;
62273
62245
  }
62274
- async function readAvatars(path59, params) {
62275
- return unwrap(await apiGet(path59, params));
62246
+ async function readAvatars(path58, params) {
62247
+ return unwrap(await apiGet(path58, params));
62276
62248
  }
62277
- async function writeAvatars(path59, body) {
62278
- return unwrap(await apiPost(path59, body));
62249
+ async function writeAvatars(path58, body) {
62250
+ return unwrap(await apiPost(path58, body));
62279
62251
  }
62280
62252
 
62281
62253
  // src/commands/avatars/create.ts
@@ -63184,12 +63156,12 @@ function missingFontFiles(urls, available) {
63184
63156
  function planFontAdoption(sources, families) {
63185
63157
  const wanted = new Map(families.map((family) => [normalizeFamily(family), family]));
63186
63158
  const byFamily = /* @__PURE__ */ new Map();
63187
- for (const { path: path59, source } of sources) {
63188
- const dir = posix.dirname(path59);
63159
+ for (const { path: path58, source } of sources) {
63160
+ const dir = posix.dirname(path58);
63189
63161
  for (const face of declaredFontFaces(source)) {
63190
63162
  if (!wanted.has(face.family)) continue;
63191
63163
  const perFile = byFamily.get(face.family) ?? /* @__PURE__ */ new Map();
63192
- perFile.set(path59, [...perFile.get(path59) ?? [], rebaseFontFaceSrc(face.block, dir)]);
63164
+ perFile.set(path58, [...perFile.get(path58) ?? [], rebaseFontFaceSrc(face.block, dir)]);
63193
63165
  byFamily.set(face.family, perFile);
63194
63166
  }
63195
63167
  }
@@ -68196,8 +68168,8 @@ function relayPrompt(args8) {
68196
68168
  ...args8.exclusionsLast ? ["", EXCLUSIONS] : []
68197
68169
  ].join("\n");
68198
68170
  }
68199
- function ingestNode(id, path59, expect) {
68200
- return { id, type: "ingest", params: { source: "path", path: path59, expect } };
68171
+ function ingestNode(id, path58, expect) {
68172
+ return { id, type: "ingest", params: { source: "path", path: path58, expect } };
68201
68173
  }
68202
68174
  function annotateBlueprintWithLabels(blueprintInput, elementsInput) {
68203
68175
  if (!blueprintInput || typeof blueprintInput !== "object") return blueprintInput;
@@ -68452,8 +68424,7 @@ function fontLabel(font) {
68452
68424
  const origin = {
68453
68425
  brand_file: "the brand's font file",
68454
68426
  tokens: "named in tokens.json, loaded as a web font",
68455
- brand_md: "named in BRAND.md, loaded as a web font",
68456
- default: "the default, since the brand names no typeface"
68427
+ brand_md: "named in BRAND.md, loaded as a web font"
68457
68428
  };
68458
68429
  const name = font.family ?? ("path" in font.slot ? font.slot.path.split("/").pop() ?? font.slot.path : "a web font");
68459
68430
  return `${name} (${origin[font.origin]})`;
@@ -71574,8 +71545,8 @@ var rerunCommand = defineCommand132({
71574
71545
  });
71575
71546
 
71576
71547
  // src/commands/canvas/scaffold-static-ad.ts
71577
- import { mkdir as mkdir8, readFile as readFile23, writeFile as writeFile12 } from "fs/promises";
71578
- import path31 from "path";
71548
+ import { mkdir as mkdir8, readFile as readFile24, writeFile as writeFile12 } from "fs/promises";
71549
+ import path30 from "path";
71579
71550
  import { defineCommand as defineCommand134 } from "citty";
71580
71551
 
71581
71552
  // src/commands/canvas/creative-limit.ts
@@ -71637,7 +71608,7 @@ import path26 from "path";
71637
71608
 
71638
71609
  // src/engine/scaffold/static-fonts.ts
71639
71610
  import { marked } from "marked";
71640
- var DEFAULT_FONTS = { headline: { family: "Inter", weight: 800 }, body: { family: "Inter", weight: 400 } };
71611
+ var HEADLINE_WEIGHT = 800;
71641
71612
  var HEADLINE_WORDS = /\b(headings?|headlines?|display|titles?|h[1-3])\b/i;
71642
71613
  var BODY_WORDS = /\b(body|paragraphs?|text|copy)\b/i;
71643
71614
  var PROHIBITION = /\b(never|don'?t|do not|avoid|instead of|ban(?:ned)?|no)\b/i;
@@ -71741,7 +71712,7 @@ async function brandHeadlineFont(args8) {
71741
71712
  if (args8.file) return fileFace(args8.file);
71742
71713
  const named = args8.named.headline ?? args8.named.body;
71743
71714
  if (!named) return null;
71744
- const url = await args8.resolveUrl(named.family, DEFAULT_FONTS.headline.weight);
71715
+ const url = await args8.resolveUrl(named.family, HEADLINE_WEIGHT);
71745
71716
  return url ? { slot: { url }, family: named.family, origin: named.origin } : null;
71746
71717
  }
71747
71718
 
@@ -71914,7 +71885,7 @@ function fileExists(target2) {
71914
71885
  }
71915
71886
 
71916
71887
  // src/commands/canvas/creative-definition.ts
71917
- import path28 from "path";
71888
+ import { parseDocument as parseDocument2 } from "yaml";
71918
71889
  var PLATFORM_VALUES = ["meta", "google", "linkedin", "tiktok", "youtube", "x", "other"];
71919
71890
  var FORMAT_VALUES = ["1:1", "4:5", "9:16", "16:9", "1.91:1"];
71920
71891
  function titleFromSlug(slug) {
@@ -71953,6 +71924,9 @@ function buildCreativeDefinition(input) {
71953
71924
  lines.push(`status: ${input.status ?? "draft"}`);
71954
71925
  if (input.sourceReferenceUrl) lines.push(`sourceReferenceUrl: ${yamlScalar(input.sourceReferenceUrl)}`);
71955
71926
  if (input.sourceAdvertiser) lines.push(`sourceAdvertiser: ${yamlScalar(input.sourceAdvertiser)}`);
71927
+ if (input.sourceAdvertiserId) lines.push(`sourceAdvertiserId: ${yamlScalar(input.sourceAdvertiserId)}`);
71928
+ if (input.sourceAdId) lines.push(`sourceAdId: ${yamlScalar(input.sourceAdId)}`);
71929
+ if (input.sourcePlatform) lines.push(`sourcePlatform: ${input.sourcePlatform}`);
71956
71930
  if (input.sourceKind) lines.push(`sourceKind: ${input.sourceKind}`);
71957
71931
  if (input.sourcePath) lines.push(`sourcePath: ${yamlScalar(input.sourcePath)}`);
71958
71932
  lines.push("---", "");
@@ -71960,18 +71934,31 @@ function buildCreativeDefinition(input) {
71960
71934
  lines.push("");
71961
71935
  return lines.join("\n");
71962
71936
  }
71937
+ var SOURCE_FIELD_KEYS = ["sourceAdId", "sourcePlatform", "sourceAdvertiserId"];
71938
+ function parseDefinitionSourceFields(raw) {
71939
+ const match = raw.match(/^---\r?\n([\s\S]*?\r?\n)?---/);
71940
+ if (!match) return {};
71941
+ const doc = parseDocument2(match[1] ?? "");
71942
+ if (doc.errors.length > 0) return {};
71943
+ const fields = {};
71944
+ for (const key of SOURCE_FIELD_KEYS) {
71945
+ const value2 = doc.get(key);
71946
+ if (typeof value2 === "string" && value2.trim()) fields[key] = value2.trim();
71947
+ }
71948
+ return fields;
71949
+ }
71963
71950
 
71964
71951
  // src/commands/canvas/scaffold-static-ad-paths.ts
71965
- import path29 from "path";
71952
+ import path28 from "path";
71966
71953
  function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd(), slug) {
71967
71954
  const file = rawFile.trim();
71968
71955
  const imageIsUrl = /^https?:\/\//i.test(file);
71969
- const imageSource = imageIsUrl ? file : path29.resolve(cwd, file);
71970
- const outPath = out ? path29.resolve(cwd, out) : slug ? path29.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path29.join(cwd, "static-ad.canvas.json") : path29.join(path29.dirname(imageSource), "static-ad.canvas.json");
71971
- const blueprintPath2 = path29.join(path29.dirname(outPath), "prompt.json");
71972
- const creativeDir = slug ? path29.dirname(outPath) : null;
71973
- const definitionPath = creativeDir ? path29.join(creativeDir, "_definition.md") : null;
71974
- const referencesDir = creativeDir ? path29.join(creativeDir, "references") : null;
71956
+ const imageSource = imageIsUrl ? file : path28.resolve(cwd, file);
71957
+ const outPath = out ? path28.resolve(cwd, out) : slug ? path28.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path28.join(cwd, "static-ad.canvas.json") : path28.join(path28.dirname(imageSource), "static-ad.canvas.json");
71958
+ const blueprintPath2 = path28.join(path28.dirname(outPath), "prompt.json");
71959
+ const creativeDir = slug ? path28.dirname(outPath) : null;
71960
+ const definitionPath = creativeDir ? path28.join(creativeDir, "_definition.md") : null;
71961
+ const referencesDir = creativeDir ? path28.join(creativeDir, "references") : null;
71975
71962
  return { imageIsUrl, imageSource, outPath, blueprintPath: blueprintPath2, creativeDir, definitionPath, referencesDir };
71976
71963
  }
71977
71964
  var SCAFFOLD_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
@@ -71981,8 +71968,8 @@ function isValidScaffoldSlug(slug) {
71981
71968
  }
71982
71969
 
71983
71970
  // src/commands/canvas/sync-definition.ts
71984
- import { readdir as readdir11, stat as stat4 } from "fs/promises";
71985
- import path30 from "path";
71971
+ import { readdir as readdir11, readFile as readFile23, stat as stat4 } from "fs/promises";
71972
+ import path29 from "path";
71986
71973
  import { defineCommand as defineCommand133 } from "citty";
71987
71974
 
71988
71975
  // src/commands/canvas/definition-graph.ts
@@ -72029,6 +72016,21 @@ function canvasToDefinitionGraph(canvas) {
72029
72016
  }
72030
72017
 
72031
72018
  // src/commands/canvas/sync-definition.ts
72019
+ function buildDefinitionSyncPayload(input, graph, chatId) {
72020
+ const { canvas: _canvas, ...fields } = input;
72021
+ return Object.fromEntries([
72022
+ ...Object.entries(fields).filter(([, v]) => v !== void 0),
72023
+ ["graph", graph],
72024
+ ["chatId", chatId]
72025
+ ]);
72026
+ }
72027
+ async function readDefinitionSourceFields(canvasPath) {
72028
+ try {
72029
+ return parseDefinitionSourceFields(await readFile23(path29.join(path29.dirname(canvasPath), "_definition.md"), "utf8"));
72030
+ } catch {
72031
+ return {};
72032
+ }
72033
+ }
72032
72034
  async function syncCreativeDefinitionBestEffort(input) {
72033
72035
  const chatId = process.env.BAKER_CHAT_ID;
72034
72036
  if (!chatId) return;
@@ -72037,16 +72039,7 @@ async function syncCreativeDefinitionBestEffort(input) {
72037
72039
  try {
72038
72040
  const creds = requireCredentialsFromEnv();
72039
72041
  const client = new BackendClient2({ baseUrl: creds.url, apiKey: creds.apiKey });
72040
- await client.syncCreativeDefinition({
72041
- slug: input.slug,
72042
- title: input.title,
72043
- platform: input.platform,
72044
- formats: input.formats,
72045
- sourceReferenceUrl: input.sourceReferenceUrl,
72046
- graph,
72047
- sourceSha: input.sourceSha,
72048
- chatId
72049
- });
72042
+ await client.syncCreativeDefinition(buildDefinitionSyncPayload(input, graph, chatId));
72050
72043
  process.stdout.write(`[definition] synced workflow graph (${graph.nodes.length} nodes) \u2014 view it in the dashboard
72051
72044
  `);
72052
72045
  } catch (e) {
@@ -72056,15 +72049,15 @@ async function syncCreativeDefinitionBestEffort(input) {
72056
72049
  }
72057
72050
  }
72058
72051
  async function resolveCanvasPath(inputPath) {
72059
- const resolved = path30.resolve(inputPath);
72052
+ const resolved = path29.resolve(inputPath);
72060
72053
  let dir = resolved;
72061
72054
  try {
72062
72055
  if ((await stat4(resolved)).isFile()) {
72063
72056
  if (resolved.endsWith(".canvas.json")) return resolved;
72064
- dir = path30.dirname(resolved);
72057
+ dir = path29.dirname(resolved);
72065
72058
  }
72066
72059
  } catch {
72067
- dir = resolved.endsWith(".canvas.json") ? path30.dirname(resolved) : resolved;
72060
+ dir = resolved.endsWith(".canvas.json") ? path29.dirname(resolved) : resolved;
72068
72061
  }
72069
72062
  let entries;
72070
72063
  try {
@@ -72075,7 +72068,7 @@ async function resolveCanvasPath(inputPath) {
72075
72068
  const canvases = entries.filter((name) => name.endsWith(".canvas.json"));
72076
72069
  const slug = creativeSlugFromCanvasPath(`${dir}/x/`);
72077
72070
  const chosen = (slug ? canvases.find((name) => name === `${slug}.canvas.json`) : void 0) ?? canvases[0];
72078
- return chosen ? path30.join(dir, chosen) : null;
72071
+ return chosen ? path29.join(dir, chosen) : null;
72079
72072
  }
72080
72073
  var syncDefinitionCommand = defineCommand133({
72081
72074
  meta: {
@@ -72105,7 +72098,7 @@ var syncDefinitionCommand = defineCommand133({
72105
72098
  } catch {
72106
72099
  return;
72107
72100
  }
72108
- await syncCreativeDefinitionBestEffort({ slug, canvas, sourceSha });
72101
+ await syncCreativeDefinitionBestEffort({ slug, canvas, sourceSha, ...await readDefinitionSourceFields(canvasPath) });
72109
72102
  }
72110
72103
  });
72111
72104
 
@@ -72138,9 +72131,120 @@ async function resolveCastAvatars(flag) {
72138
72131
  return { handles, avatars };
72139
72132
  }
72140
72133
 
72134
+ // src/commands/canvas/library-reference.ts
72135
+ var LIBRARY_PLATFORMS = ["meta", "linkedin", "tiktok"];
72136
+ function resolveSourcePlatform(flag) {
72137
+ const value2 = flag?.trim();
72138
+ if (!value2) return { ok: true, platform: "meta" };
72139
+ if (LIBRARY_PLATFORMS.includes(value2)) return { ok: true, platform: value2 };
72140
+ return {
72141
+ ok: false,
72142
+ message: `--source-platform "${value2}" is not a library platform. Use one of: ${LIBRARY_PLATFORMS.join(", ")}.`
72143
+ };
72144
+ }
72145
+ async function fetchLibraryReference(adId, platform) {
72146
+ const data = await apiGet("/api/ad-library/ad-content", { id: adId, platform });
72147
+ if (!data?.ad) throw new Error(`the library returned no ad for ${adId} on ${platform}`);
72148
+ return data.ad;
72149
+ }
72150
+ function filled(entries) {
72151
+ return Object.fromEntries(
72152
+ Object.entries(entries).filter(([, v]) => v !== null && v !== void 0 && !(Array.isArray(v) && v.length === 0))
72153
+ );
72154
+ }
72155
+ function libraryReferenceBlock(ad, ids2) {
72156
+ const textBlocks = ad.on_screen_text.flatMap(
72157
+ (o) => o.text ? [{ text: o.text, ...o.role ? { role: o.role } : {}, ...o.position ? { position: o.position } : {} }] : []
72158
+ );
72159
+ const advertiser = ad.advertiser ?? void 0;
72160
+ const advertiserId = ids2.advertiserId ?? ad.advertiser_id ?? void 0;
72161
+ return {
72162
+ ad_id: ids2.adId,
72163
+ platform: ids2.platform,
72164
+ ...advertiser ? { advertiser } : {},
72165
+ ...advertiserId ? { advertiser_id: advertiserId } : {},
72166
+ ...ad.layout.format_pattern ? { format_pattern: ad.layout.format_pattern } : {},
72167
+ layout: filled({
72168
+ composition: ad.layout.composition,
72169
+ focal_subject: ad.layout.focal_subject,
72170
+ aspect_ratio: ad.layout.aspect_ratio,
72171
+ background: ad.layout.background,
72172
+ palette_mood: ad.layout.palette_mood,
72173
+ colors: ad.layout.colors,
72174
+ production: ad.layout.production,
72175
+ aesthetic: ad.layout.aesthetic,
72176
+ cta_in_image: ad.layout.cta_in_image,
72177
+ markers: ad.layout.markers
72178
+ }),
72179
+ message: filled({
72180
+ mechanism: ad.message.mechanism,
72181
+ awareness: ad.message.awareness_stage,
72182
+ sophistication: ad.message.sophistication,
72183
+ angle: ad.angle,
72184
+ hook_archetype: ad.hook_archetype,
72185
+ pain_points: ad.message.pain_points,
72186
+ desires: ad.message.desires,
72187
+ value_props: ad.message.value_props,
72188
+ objections_handled: ad.message.objections_handled,
72189
+ proof: ad.message.proof,
72190
+ risk_reversal: ad.message.risk_reversal
72191
+ }),
72192
+ offer: filled({
72193
+ offer_type: ad.offer.offer_type,
72194
+ pricing_model: ad.offer.pricing_model,
72195
+ entry_price: ad.offer.entry_price,
72196
+ no_credit_card: ad.offer.no_credit_card,
72197
+ guarantee: ad.offer.guarantee,
72198
+ guarantee_days: ad.offer.guarantee_days,
72199
+ urgency: ad.offer.urgency,
72200
+ bonus: ad.offer.bonus
72201
+ }),
72202
+ text_blocks: textBlocks,
72203
+ copy: filled({ primary_text: ad.primary_text, headline: ad.headline, cta: ad.cta })
72204
+ };
72205
+ }
72206
+ function referenceDescribeContext(reference, agentContext) {
72207
+ const own = agentContext?.trim() || void 0;
72208
+ if (!reference) return own;
72209
+ const facts = [];
72210
+ if (reference.advertiser) facts.push(`advertiser: ${reference.advertiser}`);
72211
+ if (reference.format_pattern) facts.push(`container (format_pattern): ${reference.format_pattern}`);
72212
+ if (typeof reference.layout.composition === "string") facts.push(`composition: ${reference.layout.composition}`);
72213
+ if (typeof reference.message.mechanism === "string") facts.push(`persuasion mechanism: ${reference.message.mechanism}`);
72214
+ if (typeof reference.message.awareness === "string") facts.push(`awareness stage: ${reference.message.awareness}`);
72215
+ if (typeof reference.offer.offer_type === "string") facts.push(`offer: ${reference.offer.offer_type}`);
72216
+ if (reference.text_blocks.length > 0) {
72217
+ facts.push(
72218
+ `text blocks: ${reference.text_blocks.map((b) => `"${b.text}"${b.role ? ` (${b.role})` : ""}`).join("; ")}`
72219
+ );
72220
+ }
72221
+ const library = `The ads library has already read this ad: ${facts.join(". ")}. Your blueprint must agree with it on the container and the mechanism; refine and detail them, never contradict them, and transcribe the text blocks exactly.`;
72222
+ return own ? `${own}. ${library}` : library;
72223
+ }
72224
+ function mergeReferenceIntoBlueprint(blueprint, reference) {
72225
+ if (!reference || !blueprint || typeof blueprint !== "object" || Array.isArray(blueprint)) return blueprint;
72226
+ const merged = { ...blueprint, reference };
72227
+ if (winningMechanisms(blueprint).length === 0) {
72228
+ const container = reference.format_pattern;
72229
+ const mechanism = typeof reference.message.mechanism === "string" ? reference.message.mechanism : void 0;
72230
+ const device = container && mechanism ? `${container} container carrying ${mechanism}` : container && `${container} container` || mechanism && `${mechanism} persuasion mechanism`;
72231
+ if (device) {
72232
+ merged.winning_mechanisms = [
72233
+ {
72234
+ kind: "structural",
72235
+ device,
72236
+ why_it_works: "The winners library recorded this ad as a winner on this container and persuasion mechanism.",
72237
+ source: "library"
72238
+ }
72239
+ ];
72240
+ }
72241
+ }
72242
+ return merged;
72243
+ }
72244
+
72141
72245
  // src/commands/canvas/scaffold-static-ad.ts
72142
72246
  function toCanvasRelative2(canvasDir, file) {
72143
- const rel = path31.relative(canvasDir, file).split(path31.sep).join("/");
72247
+ const rel = path30.relative(canvasDir, file).split(path30.sep).join("/");
72144
72248
  return rel.startsWith(".") ? rel : `./${rel}`;
72145
72249
  }
72146
72250
  async function uploadSourceAsReference(source, isUrl, client) {
@@ -72152,7 +72256,7 @@ async function uploadSourceAsReference(source, isUrl, client) {
72152
72256
  throw new Error(`failed to download source image (${e instanceof Error ? e.message : String(e)})`);
72153
72257
  }
72154
72258
  } else {
72155
- bytes = await readFile23(source);
72259
+ bytes = await readFile24(source);
72156
72260
  }
72157
72261
  const safe = await toModelSafeImage(bytes);
72158
72262
  const sha2562 = sha256Hex(safe.bytes);
@@ -72292,13 +72396,102 @@ async function runVisionPasses(canvas) {
72292
72396
  return fail4("read_outputs", e instanceof Error ? e.message : String(e));
72293
72397
  }
72294
72398
  }
72399
+ var SOURCE_AD_ID_DESCRIPTION = "The winners-library ad this creative adapts (the `ad_id` from `baker winning-ads search`). The library's read of that ad (container, layout, colours, text blocks with their roles, persuasion mechanism, offer) is fetched and written into prompt.json as `reference`, the describe pass is told it so the two reads agree, and the source is recorded on the creative from this scaffold on. With it, the image is optional: the library's picture is used when none is given. Pass it whenever the reference came from the library.";
72400
+ var SOURCE_PLATFORM_DESCRIPTION = `Which platform the library ad is on: ${LIBRARY_PLATFORMS.join("|")} (default meta).`;
72401
+ var SOURCE_ADVERTISER_ID_DESCRIPTION = "The library advertiser id of the reference (the `advertiser_id` beside the ad in search results). Defaults to the one the library returns for the ad.";
72402
+ registerSchema({
72403
+ command: "canvas.scaffold-static-ad",
72404
+ description: "Turn a reference image into a runnable static-ad canvas: billed image_describe + element-selection passes, then a canvas on which the image model renders the finished ad from prompt.json. Start here for a static ad adapted from a winning ad: pass `--source-ad-id <ad_id> --source-platform <platform>` from the search result, so the library's structured read grounds the blueprint and the source is recorded on the creative; the image may then be omitted. `baker avatars list`, then `--avatar <handle>` when the image shows a person.",
72405
+ args: {
72406
+ file: {
72407
+ type: "positional",
72408
+ required: false,
72409
+ description: "Path or http(s) URL to the reference image. Optional with --source-ad-id: the library's picture is used."
72410
+ },
72411
+ "source-ad-id": { type: "string", required: false, description: SOURCE_AD_ID_DESCRIPTION },
72412
+ "source-platform": {
72413
+ type: "string",
72414
+ required: false,
72415
+ description: SOURCE_PLATFORM_DESCRIPTION,
72416
+ enum: [...LIBRARY_PLATFORMS]
72417
+ },
72418
+ "source-advertiser-id": { type: "string", required: false, description: SOURCE_ADVERTISER_ID_DESCRIPTION },
72419
+ context: {
72420
+ type: "string",
72421
+ required: false,
72422
+ description: "Known provenance (advertiser, category, market, their brand palette) to ground the describe pass"
72423
+ },
72424
+ slug: {
72425
+ type: "string",
72426
+ required: false,
72427
+ description: "Creative slug: writes src/creatives/<slug>/<slug>.canvas.json, prompt.json and _definition.md"
72428
+ },
72429
+ title: { type: "string", required: false, description: "Creative title for _definition.md (default: title-cased slug)" },
72430
+ platform: {
72431
+ type: "string",
72432
+ required: false,
72433
+ description: "Ad platform for _definition.md and the default placement set (meta|google|linkedin|tiktok|youtube|x|other; default meta)"
72434
+ },
72435
+ advertiser: {
72436
+ type: "string",
72437
+ required: false,
72438
+ description: "Source advertiser name for _definition.md (default: the library ad's advertiser when --source-ad-id is given)"
72439
+ },
72440
+ placements: {
72441
+ type: "string",
72442
+ required: false,
72443
+ description: "Formats delivered: meta_feed|meta_feed_stories|meta_all|linkedin|google_demandgen|tiktok|youtube, or none for a single base ad (default from --platform)"
72444
+ },
72445
+ aspect: { type: "string", required: false, description: "Force the lead format (else inferred from the image)" },
72446
+ takes: { type: "string", required: false, description: "Finished ads rendered and judged, 1-3 (default 3)" },
72447
+ avatar: {
72448
+ type: "string",
72449
+ required: false,
72450
+ description: "Cast company avatars as the ad's people, comma-separated in detection order (`marta` or `marta,joan`)"
72451
+ },
72452
+ "skip-actor-sheets": {
72453
+ type: "boolean",
72454
+ required: false,
72455
+ description: "Ground each animal hero on its lone dropped photo instead of a generated multi-view sheet"
72456
+ },
72457
+ out: { type: "string", required: false, description: "Output canvas path (default <image-dir>/static-ad.canvas.json)" },
72458
+ "describe-model": { type: "string", required: false, description: "Override the image_describe model id" },
72459
+ "select-model": { type: "string", required: false, description: "Override the element-selection text_generate model id" },
72460
+ "gen-model": { type: "string", required: false, description: "Override the image_generate model id" }
72461
+ }
72462
+ });
72463
+ async function lookupReference(args8) {
72464
+ const adId = args8["source-ad-id"] ? String(args8["source-ad-id"]).trim() : "";
72465
+ if (!adId) return { status: "none" };
72466
+ const platformResult = resolveSourcePlatform(args8["source-platform"] ? String(args8["source-platform"]) : void 0);
72467
+ if (!platformResult.ok) return fail4("invalid_source_platform", platformResult.message);
72468
+ const platform = platformResult.platform;
72469
+ const advertiserId = args8["source-advertiser-id"] ? String(args8["source-advertiser-id"]).trim() : void 0;
72470
+ try {
72471
+ const ad = await fetchLibraryReference(adId, platform);
72472
+ return {
72473
+ status: "grounded",
72474
+ reference: libraryReferenceBlock(ad, { adId, platform, advertiserId }),
72475
+ mediaUrl: ad.media_url ?? void 0
72476
+ };
72477
+ } catch (e) {
72478
+ return { status: "unavailable", adId, platform, message: e instanceof Error ? e.message : String(e) };
72479
+ }
72480
+ }
72295
72481
  var scaffoldStaticAdCommand = defineCommand134({
72296
72482
  meta: {
72297
72483
  name: "scaffold-static-ad",
72298
- description: "Turn a source/inspiration image into a runnable static-ad canvas. Runs billed passes \u2014 image_describe (the blueprint, baked to prompt.json as the editable 'prompt') and an AI selection of the image's MAIN identity elements \u2014 then scaffolds a canvas on which the image model renders the FINISHED ad: up to three candidates from prompt.json, the copy quoted verbatim, the brand's typeface shown as a specimen and the logo from src/brand/logos/ as a reference (only when the concept shows one), judged on one idea, exact words, layout, mechanism, brand and artifacts; every other placement format is the winner re-laid by the model, and a format the model has no shape for is cropped exact. The run reads every word back and refuses to call the ad ready when one is off. No copy means the picture is the ad. Pass --placements none for a single base ad, or a preset to pick the set. Edit prompt.json and drop the real assets, then `baker canvas run` it. Start here: `baker avatars list`, then pass `--avatar <handle>` when the image shows a person \u2014 the ad's people are played by the company's avatars, never by a photo of anyone real."
72484
+ description: "Turn a source/inspiration image into a runnable static-ad canvas. Runs billed passes \u2014 image_describe (the blueprint, baked to prompt.json as the editable 'prompt') and an AI selection of the image's MAIN identity elements \u2014 then scaffolds a canvas on which the image model renders the FINISHED ad: up to three candidates from prompt.json, the copy quoted verbatim, the brand's typeface shown as a specimen and the logo from src/brand/logos/ as a reference (only when the concept shows one), judged on one idea, exact words, layout, mechanism, brand and artifacts; every other placement format is the winner re-laid by the model, and a format the model has no shape for is cropped exact. The run reads every word back and refuses to call the ad ready when one is off. No copy means the picture is the ad. Pass --placements none for a single base ad, or a preset to pick the set. Edit prompt.json and drop the real assets, then `baker canvas run` it. Start here: `baker avatars list`, then pass `--avatar <handle>` when the image shows a person \u2014 the ad's people are played by the company's avatars, never by a photo of anyone real. A reference from the library: pass `--source-ad-id <ad_id> --source-platform <platform>` so its structured read grounds the blueprint and the source is recorded on the creative."
72299
72485
  },
72300
72486
  args: {
72301
- file: { type: "positional", required: true, description: "Path or http(s) URL to the source/inspiration image" },
72487
+ file: {
72488
+ type: "positional",
72489
+ required: false,
72490
+ description: "Path or http(s) URL to the source/inspiration image (optional with --source-ad-id)"
72491
+ },
72492
+ "source-ad-id": { type: "string", description: SOURCE_AD_ID_DESCRIPTION },
72493
+ "source-platform": { type: "string", description: SOURCE_PLATFORM_DESCRIPTION },
72494
+ "source-advertiser-id": { type: "string", description: SOURCE_ADVERTISER_ID_DESCRIPTION },
72302
72495
  context: { type: "string", description: "Known provenance (advertiser, category, market) to ground the describe" },
72303
72496
  out: { type: "string", description: "Output canvas path (default <image-dir>/static-ad.canvas.json)" },
72304
72497
  slug: {
@@ -72310,7 +72503,10 @@ var scaffoldStaticAdCommand = defineCommand134({
72310
72503
  type: "string",
72311
72504
  description: "Ad platform (meta|google|linkedin|tiktok|youtube|x|other; default meta). Sets _definition.md AND, unless --placements is given, the formats the ad is delivered in (meta \u2192 4:5+9:16, google \u2192 1:1+4:5+16:9, linkedin \u2192 1:1, tiktok \u2192 9:16, youtube \u2192 16:9; x/other \u2192 one format)."
72312
72505
  },
72313
- advertiser: { type: "string", description: "Source advertiser recorded in _definition.md" },
72506
+ advertiser: {
72507
+ type: "string",
72508
+ description: "Source advertiser recorded in _definition.md (default: the library ad's advertiser)"
72509
+ },
72314
72510
  "describe-model": { type: "string", description: "Override the image_describe model id" },
72315
72511
  "select-model": { type: "string", description: "Override the text_generate model id for element selection" },
72316
72512
  "gen-model": { type: "string", description: "Override the image_generate model id" },
@@ -72344,14 +72540,24 @@ var scaffoldStaticAdCommand = defineCommand134({
72344
72540
  );
72345
72541
  process.exit(2);
72346
72542
  }
72347
- const { imageIsUrl, imageSource, outPath, blueprintPath: blueprintPath2, definitionPath, referencesDir } = resolveScaffoldStaticAdPaths(
72348
- String(args8.file),
72349
- args8.out ? String(args8.out) : void 0,
72350
- process.cwd(),
72351
- slug
72352
- );
72543
+ const lookup = await lookupReference(args8);
72544
+ if (lookup.status === "unavailable") {
72545
+ process.stderr.write(
72546
+ `[warn] winners-library ad ${lookup.adId} (${lookup.platform}) could not be read (${lookup.message}); scaffolding without its structured read
72547
+ `
72548
+ );
72549
+ }
72550
+ const reference = lookup.status === "grounded" ? lookup.reference : void 0;
72551
+ const fileArg2 = args8.file ? String(args8.file) : void 0;
72552
+ const sourceFile = fileArg2 ?? (lookup.status === "grounded" ? lookup.mediaUrl : void 0);
72553
+ if (!sourceFile) {
72554
+ if (lookup.status === "none") return fail4("missing_source", "Pass the reference image, or --source-ad-id <ad_id> to use a library ad.");
72555
+ if (lookup.status === "unavailable") return fail4("source_unavailable", `No image given and the library ad could not be read (${lookup.message}).`);
72556
+ return fail4("source_unavailable", `The library holds no picture for ad ${reference?.ad_id}; pass the image.`);
72557
+ }
72558
+ const { imageIsUrl, imageSource, outPath, blueprintPath: blueprintPath2, definitionPath, referencesDir } = resolveScaffoldStaticAdPaths(sourceFile, args8.out ? String(args8.out) : void 0, process.cwd(), slug);
72353
72559
  if (slug) await enforceNewCreativeLimit(slug, outPath);
72354
- await mkdir8(path31.dirname(outPath), { recursive: true });
72560
+ await mkdir8(path30.dirname(outPath), { recursive: true });
72355
72561
  let models;
72356
72562
  try {
72357
72563
  models = resolveModels(args8);
@@ -72377,13 +72583,14 @@ var scaffoldStaticAdCommand = defineCommand134({
72377
72583
  durableSourceUrl !== void 0 || imageIsUrl,
72378
72584
  describeModel,
72379
72585
  selectModel,
72380
- args8.context ? String(args8.context) : void 0
72586
+ referenceDescribeContext(reference, args8.context ? String(args8.context) : void 0)
72381
72587
  );
72382
72588
  const vision = await runVisionPasses(describeCanvas);
72383
- const { blueprint, creditsSpent } = vision;
72589
+ const { creditsSpent } = vision;
72590
+ const blueprint = mergeReferenceIntoBlueprint(vision.blueprint, reference);
72384
72591
  const elements = castAvatarsOnStaticElements(vision.elements, cast.avatars);
72385
72592
  const uncast = staticAvatarsNotCast(vision.elements, cast.handles);
72386
- const canvasDir = path31.dirname(outPath);
72593
+ const canvasDir = path30.dirname(outPath);
72387
72594
  const workspaceRoot = await findBrandRoot(canvasDir) ?? process.cwd();
72388
72595
  const kit = await readStaticBrandKit(workspaceRoot);
72389
72596
  const annotated = annotateBlueprintWithLabels(blueprint, elements);
@@ -72431,7 +72638,7 @@ var scaffoldStaticAdCommand = defineCommand134({
72431
72638
  } catch (e) {
72432
72639
  return fail4("scaffold", e instanceof Error ? e.message : String(e));
72433
72640
  }
72434
- const validation = await validateScaffoldedStaticAd(canvas, path31.dirname(outPath));
72641
+ const validation = await validateScaffoldedStaticAd(canvas, path30.dirname(outPath));
72435
72642
  if (validation.blocking.length > 0) {
72436
72643
  process.stderr.write(
72437
72644
  `${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.blocking } }, null, 2)}
@@ -72441,6 +72648,15 @@ var scaffoldStaticAdCommand = defineCommand134({
72441
72648
  }
72442
72649
  await writeFile12(outPath, `${JSON.stringify(canvas, null, 2)}
72443
72650
  `, "utf8");
72651
+ const sourceFields = reference ? {
72652
+ sourceAdId: reference.ad_id,
72653
+ sourcePlatform: reference.platform,
72654
+ ...reference.advertiser_id ? { sourceAdvertiserId: reference.advertiser_id } : {}
72655
+ } : lookup.status === "unavailable" ? {
72656
+ sourceAdId: lookup.adId,
72657
+ sourcePlatform: lookup.platform,
72658
+ ...args8["source-advertiser-id"] ? { sourceAdvertiserId: String(args8["source-advertiser-id"]).trim() } : {}
72659
+ } : {};
72444
72660
  if (definitionPath && !await fileExists(definitionPath)) {
72445
72661
  await writeFile12(
72446
72662
  definitionPath,
@@ -72450,7 +72666,8 @@ var scaffoldStaticAdCommand = defineCommand134({
72450
72666
  platform: definitionPlatform,
72451
72667
  formats: resolveFormatList(report.output_formats),
72452
72668
  sourceReferenceUrl: durableSourceUrl ?? (imageIsUrl ? imageSource : void 0),
72453
- sourceAdvertiser: args8.advertiser ? String(args8.advertiser) : args8.context ? String(args8.context) : void 0,
72669
+ sourceAdvertiser: args8.advertiser ? String(args8.advertiser) : reference?.advertiser ?? (args8.context ? String(args8.context) : void 0),
72670
+ ...sourceFields,
72454
72671
  sourceKind: "image",
72455
72672
  description: describeBlueprintIntent(blueprint)
72456
72673
  }),
@@ -72464,11 +72681,15 @@ var scaffoldStaticAdCommand = defineCommand134({
72464
72681
  platform: definitionPlatform,
72465
72682
  formats: resolveFormatList(report.output_formats),
72466
72683
  sourceReferenceUrl: durableSourceUrl ?? (imageIsUrl ? imageSource : void 0),
72684
+ ...sourceFields,
72467
72685
  canvas,
72468
72686
  sourceSha: await computeSourceSha(outPath)
72469
72687
  });
72470
72688
  }
72471
72689
  const hints2 = [
72690
+ ...lookup.status === "unavailable" ? [
72691
+ `REFERENCE NOT READ: the winners library could not return ad ${lookup.adId} (${lookup.message}). The blueprint is the describe pass alone and prompt.json has no \`reference\` block; the source ids are still recorded. Check the ad id and platform, or continue from the picture.`
72692
+ ] : [],
72472
72693
  ...uncast.length ? [
72473
72694
  `NOT CAST: ${uncast.map((h) => `\`${h}\``).join(", ")} \u2014 the element selection found fewer people than avatars passed, so ${uncast.length === 1 ? "this avatar plays" : "these avatars play"} nobody in this ad. Tell the user who is cast and who is not.`
72474
72695
  ] : [],
@@ -72485,6 +72706,15 @@ var scaffoldStaticAdCommand = defineCommand134({
72485
72706
  prompt_path: blueprintPath2,
72486
72707
  definition_path: definitionPath ?? void 0,
72487
72708
  source_reference: durableSourceUrl ?? void 0,
72709
+ ...reference ? {
72710
+ reference: {
72711
+ ad_id: reference.ad_id,
72712
+ platform: reference.platform,
72713
+ advertiser_id: reference.advertiser_id,
72714
+ format_pattern: reference.format_pattern,
72715
+ mechanism: reference.message.mechanism
72716
+ }
72717
+ } : {},
72488
72718
  output: canvas.output,
72489
72719
  ...hints2.length ? { hints: hints2 } : {},
72490
72720
  models: { describe: describeModel, select: selectModel, gen: opts.genModel },
@@ -72497,7 +72727,7 @@ var scaffoldStaticAdCommand = defineCommand134({
72497
72727
  run_estimated_credits: validation.estimatedCredits
72498
72728
  },
72499
72729
  checklist: {
72500
- edit_prompt: `Edit ${path31.basename(blueprintPath2)} \u2014 it is the blueprint generated from your image; rewrite it into the ad you want (palette, copy, claims, subjects). It feeds the generator directly.`,
72730
+ edit_prompt: `Edit ${path30.basename(blueprintPath2)} \u2014 it is the blueprint generated from your image; rewrite it into the ad you want (palette, copy, claims, subjects). It feeds the generator directly.${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." : ""}`,
72501
72731
  winning_mechanisms: report.winning_mechanisms,
72502
72732
  rebuild_the_mechanism: "`winning_mechanisms` is WHY the source ad works. Rewrite each one for our brand in prompt.json (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.",
72503
72733
  assets_to_supply: report.elements,
@@ -72558,8 +72788,8 @@ function clientLogoMissNote(decision, domain) {
72558
72788
  }
72559
72789
 
72560
72790
  // src/commands/canvas/scaffold-ad.ts
72561
- import { copyFile, cp, mkdir as mkdir9, readFile as readFile25, stat as stat5, writeFile as writeFile14 } from "fs/promises";
72562
- import path33 from "path";
72791
+ import { copyFile, cp, mkdir as mkdir9, readFile as readFile26, stat as stat5, writeFile as writeFile14 } from "fs/promises";
72792
+ import path32 from "path";
72563
72793
  import { defineCommand as defineCommand135 } from "citty";
72564
72794
 
72565
72795
  // src/engine/scaffold/ad-spec.ts
@@ -73273,8 +73503,8 @@ function injectAdBrandOverlay(indexHtml, overlayHtml, style = {}) {
73273
73503
  }
73274
73504
 
73275
73505
  // src/commands/canvas/composition-dims.ts
73276
- import { readFile as readFile24, writeFile as writeFile13 } from "fs/promises";
73277
- import path32 from "path";
73506
+ import { readFile as readFile25, writeFile as writeFile13 } from "fs/promises";
73507
+ import path31 from "path";
73278
73508
  function patchCompositionMeta(metaJson, dims) {
73279
73509
  try {
73280
73510
  const meta = JSON.parse(metaJson);
@@ -73289,11 +73519,11 @@ function patchCompositionHtml(html, dims) {
73289
73519
  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`);
73290
73520
  }
73291
73521
  async function stampCompositionDims(compositionDir, dims) {
73292
- const metaPath = path32.join(compositionDir, "meta.json");
73293
- const rawMeta = await readFile24(metaPath, "utf8");
73522
+ const metaPath = path31.join(compositionDir, "meta.json");
73523
+ const rawMeta = await readFile25(metaPath, "utf8");
73294
73524
  await writeFile13(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
73295
- const htmlPath = path32.join(compositionDir, "index.html");
73296
- const rawHtml = await readFile24(htmlPath, "utf8");
73525
+ const htmlPath = path31.join(compositionDir, "index.html");
73526
+ const rawHtml = await readFile25(htmlPath, "utf8");
73297
73527
  await writeFile13(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
73298
73528
  }
73299
73529
 
@@ -73376,7 +73606,7 @@ var scaffoldAdCommand = defineCommand135({
73376
73606
  const specPath = args8.spec;
73377
73607
  let parsed;
73378
73608
  try {
73379
- parsed = JSON.parse(await readFile25(specPath, "utf-8"));
73609
+ parsed = JSON.parse(await readFile26(specPath, "utf-8"));
73380
73610
  } catch (e) {
73381
73611
  writeJson({
73382
73612
  ok: false,
@@ -73537,7 +73767,7 @@ var scaffoldAdCommand = defineCommand135({
73537
73767
  ] : [];
73538
73768
  const markPath = spec.data.brand?.logo?.trim();
73539
73769
  if (!noBrand && markPath?.toLowerCase().endsWith(".svg")) {
73540
- const svg = await readFile25(markPath, "utf-8").catch(() => null);
73770
+ const svg = await readFile26(markPath, "utf-8").catch(() => null);
73541
73771
  if (svg === null) {
73542
73772
  writeJson({
73543
73773
  ok: false,
@@ -73589,8 +73819,8 @@ var scaffoldAdCommand = defineCommand135({
73589
73819
  videoModel: chosenVideoModel,
73590
73820
  ...avatarVoice ? { avatarVoice } : {}
73591
73821
  });
73592
- const slug = args8.slug ?? path33.basename(specPath).replace(/\.[^.]+$/, "");
73593
- const outPath = args8.out ?? (args8.slug ? path33.join("src/creatives", slug, `${slug}.canvas.json`) : path33.join(path33.dirname(specPath), `${slug}.canvas.json`));
73822
+ const slug = args8.slug ?? path32.basename(specPath).replace(/\.[^.]+$/, "");
73823
+ const outPath = args8.out ?? (args8.slug ? path32.join("src/creatives", slug, `${slug}.canvas.json`) : path32.join(path32.dirname(specPath), `${slug}.canvas.json`));
73594
73824
  if (args8.slug) await enforceNewCreativeLimit(slug, outPath);
73595
73825
  let outAr;
73596
73826
  try {
@@ -73608,28 +73838,28 @@ var scaffoldAdCommand = defineCommand135({
73608
73838
  return;
73609
73839
  }
73610
73840
  const outDims = canvasDims(outAr);
73611
- const outDir = path33.dirname(outPath);
73841
+ const outDir = path32.dirname(outPath);
73612
73842
  await mkdir9(outDir, { recursive: true });
73613
- const compositionDest = path33.join(outDir, "video-overlay-composition");
73614
- const captionsDest = path33.join(outDir, "tiktok-captions-composition");
73843
+ const compositionDest = path32.join(outDir, "video-overlay-composition");
73844
+ const captionsDest = path32.join(outDir, "tiktok-captions-composition");
73615
73845
  await cp(SHIPPED_OVERLAY_COMPOSITION_DIR, compositionDest, { recursive: true });
73616
73846
  await cp(SHIPPED_CAPTIONS_DIR, captionsDest, { recursive: true });
73617
73847
  await stampCompositionDims(compositionDest, outDims);
73618
73848
  await stampCompositionDims(captionsDest, outDims);
73619
- const captionsIndex = path33.join(captionsDest, "index.html");
73620
- await writeFile14(captionsIndex, injectAdCaptionPlacement(await readFile25(captionsIndex, "utf-8"), outAr), "utf-8");
73849
+ const captionsIndex = path32.join(captionsDest, "index.html");
73850
+ await writeFile14(captionsIndex, injectAdCaptionPlacement(await readFile26(captionsIndex, "utf-8"), outAr), "utf-8");
73621
73851
  const face = await stageBrandDisplayFace(outDir, captionsDest);
73622
73852
  if (face) {
73623
- await writeFile14(captionsIndex, injectAdDisplayFace(await readFile25(captionsIndex, "utf-8"), face.file), "utf-8");
73853
+ await writeFile14(captionsIndex, injectAdDisplayFace(await readFile26(captionsIndex, "utf-8"), face.file), "utf-8");
73624
73854
  }
73625
- const blueprintPath2 = path33.join(outDir, "prompt.json");
73626
- const blueprintStylePath = path33.join(outDir, "prompt.style.json");
73855
+ const blueprintPath2 = path32.join(outDir, "prompt.json");
73856
+ const blueprintStylePath = path32.join(outDir, "prompt.style.json");
73627
73857
  await writeSceneFiles(outDir, blueprint);
73628
73858
  await writeFile14(blueprintStylePath, renderStyleProjectionFromValue(blueprint), "utf8");
73629
- await writeFile14(path33.join(outDir, AD_SPEC_FILE), `${JSON.stringify(specArtifact, null, 2)}
73859
+ await writeFile14(path32.join(outDir, AD_SPEC_FILE), `${JSON.stringify(specArtifact, null, 2)}
73630
73860
  `, "utf8");
73631
73861
  const logoPath = noBrand ? void 0 : spec.data.brand?.logo?.trim();
73632
- const transcriptPath = path33.join(outDir, "transcript.json");
73862
+ const transcriptPath = path32.join(outDir, "transcript.json");
73633
73863
  if (spec.data.voiceover === false) {
73634
73864
  await writeFile14(transcriptPath, `${JSON.stringify(staticTranscript(spec.data, chosenVideoModel), null, 2)}
73635
73865
  `, "utf-8");
@@ -73641,10 +73871,10 @@ var scaffoldAdCommand = defineCommand135({
73641
73871
  // that stops the whole canvas. The same sheet model every other scaffold uses.
73642
73872
  sheetModel: ACTOR_SHEET_MODEL,
73643
73873
  videoModel: chosenVideoModel,
73644
- overlayCompositionPath: path33.relative(outDir, compositionDest),
73645
- captionsCompositionPath: path33.relative(outDir, captionsDest),
73646
- blueprintPath: path33.relative(outDir, blueprintPath2),
73647
- blueprintStylePath: path33.relative(outDir, blueprintStylePath),
73874
+ overlayCompositionPath: path32.relative(outDir, compositionDest),
73875
+ captionsCompositionPath: path32.relative(outDir, captionsDest),
73876
+ blueprintPath: path32.relative(outDir, blueprintPath2),
73877
+ blueprintStylePath: path32.relative(outDir, blueprintStylePath),
73648
73878
  aspect: spec.data.format.aspect_ratio,
73649
73879
  resolution: spec.data.format.resolution,
73650
73880
  // A beat is one clause and one card, so the cap has to clear a whole clause.
@@ -73683,17 +73913,17 @@ var scaffoldAdCommand = defineCommand135({
73683
73913
  // the stock voice this exists to avoid. It is to convert the LATER extracts onto the
73684
73914
  // first beat of the same speaker, which keeps a voice the clip really produced.
73685
73915
  ...avatar ? { keepRenderedVoice: true } : {},
73686
- ...spec.data.voiceover === false ? { staticTranscriptPath: path33.relative(outDir, transcriptPath) } : {}
73916
+ ...spec.data.voiceover === false ? { staticTranscriptPath: path32.relative(outDir, transcriptPath) } : {}
73687
73917
  };
73688
73918
  const canvas = scaffoldVideoCanvas(blueprint, adSpecCastElements(spec.data, avatar), opts);
73689
73919
  const renderNode5 = canvas.nodes.find((n) => n.type === "hyperframe_render");
73690
- const renderedDir = renderNode5 ? path33.join(outDir, String(renderNode5.params?.composition ?? "")) : null;
73920
+ const renderedDir = renderNode5 ? path32.join(outDir, String(renderNode5.params?.composition ?? "")) : null;
73691
73921
  let staged = null;
73692
73922
  let markIsLight = false;
73693
73923
  if (logoPath && renderedDir) {
73694
- const ext = path33.extname(logoPath) || ".png";
73924
+ const ext = path32.extname(logoPath) || ".png";
73695
73925
  try {
73696
- await copyFile(logoPath, path33.join(renderedDir, `brand-mark${ext}`));
73926
+ await copyFile(logoPath, path32.join(renderedDir, `brand-mark${ext}`));
73697
73927
  staged = { file: `brand-mark${ext}`, alt: spec.data.brand?.name ?? "brand" };
73698
73928
  markIsLight = await markInkIsLight(logoPath);
73699
73929
  } catch {
@@ -73712,12 +73942,12 @@ var scaffoldAdCommand = defineCommand135({
73712
73942
  plateIsDark: plateIsDark(spec.data.brand?.palette)
73713
73943
  }) : "";
73714
73944
  if (overlayHtml && renderedDir) {
73715
- const indexPath = path33.join(renderedDir, "index.html");
73716
- const html = await readFile25(indexPath, "utf-8");
73945
+ const indexPath = path32.join(renderedDir, "index.html");
73946
+ const html = await readFile26(indexPath, "utf-8");
73717
73947
  await writeFile14(indexPath, injectAdBrandOverlay(html, overlayHtml, { aspect: outAr, ...face ? { face: AD_DISPLAY_FAMILY } : {} }), "utf-8");
73718
73948
  }
73719
73949
  await writeFile14(
73720
- path33.join(outDir, REBUILD_FILE),
73950
+ path32.join(outDir, REBUILD_FILE),
73721
73951
  `${JSON.stringify({ elements: adSpecCastElements(spec.data, avatar), opts }, null, 2)}
73722
73952
  `,
73723
73953
  "utf8"
@@ -73747,7 +73977,7 @@ var scaffoldAdCommand = defineCommand135({
73747
73977
  return;
73748
73978
  }
73749
73979
  if (args8.slug) {
73750
- const definitionPath = path33.join(outDir, "_definition.md");
73980
+ const definitionPath = path32.join(outDir, "_definition.md");
73751
73981
  const definitionExists = await stat5(definitionPath).then(() => true, () => false);
73752
73982
  if (!definitionExists) {
73753
73983
  await writeFile14(
@@ -73865,9 +74095,9 @@ async function fetchClientLogo() {
73865
74095
  if (!response.ok) return { ok: false, reason: clientLogoMissNote({ use: false, reason: "no_hits" }, domain) };
73866
74096
  const bytes = Buffer.from(await response.arrayBuffer());
73867
74097
  const extension = /\.(svg|png|jpg|jpeg|webp)(?:\?|$)/i.exec(decision.url)?.[1]?.toLowerCase() ?? "svg";
73868
- const dir = path33.join(BRAND_DIR, "logos");
74098
+ const dir = path32.join(BRAND_DIR, "logos");
73869
74099
  await mkdir9(dir, { recursive: true });
73870
- const file = path33.join(dir, `brand-mark.${extension}`);
74100
+ const file = path32.join(dir, `brand-mark.${extension}`);
73871
74101
  await writeFile14(file, bytes);
73872
74102
  return { ok: true, path: file };
73873
74103
  } catch {
@@ -73886,14 +74116,14 @@ async function stageBrandDisplayFace(outDir, compositionDir) {
73886
74116
  if (!face) return null;
73887
74117
  try {
73888
74118
  if ("path" in face.slot) {
73889
- const file2 = `brand-face${path33.extname(face.slot.path).toLowerCase()}`;
73890
- await copyFile(face.slot.path, path33.join(compositionDir, file2));
73891
- return { file: file2, label: `the font file \`${path33.relative(root, face.slot.path)}\`` };
74119
+ const file2 = `brand-face${path32.extname(face.slot.path).toLowerCase()}`;
74120
+ await copyFile(face.slot.path, path32.join(compositionDir, file2));
74121
+ return { file: file2, label: `the font file \`${path32.relative(root, face.slot.path)}\`` };
73892
74122
  }
73893
74123
  const { buffer } = await fetchExternalBytes(face.slot.url, { maxBytes: MAX_FONT_BYTES });
73894
74124
  const extension = /\.(woff2|woff|ttf|otf)(?:\?|$)/i.exec(face.slot.url)?.[1]?.toLowerCase() ?? "woff2";
73895
74125
  const file = `brand-face.${extension}`;
73896
- await writeFile14(path33.join(compositionDir, file), buffer);
74126
+ await writeFile14(path32.join(compositionDir, file), buffer);
73897
74127
  return { file, label: `${face.family ?? "the brand's typeface"}, loaded as a web font` };
73898
74128
  } catch {
73899
74129
  return null;
@@ -73901,9 +74131,9 @@ async function stageBrandDisplayFace(outDir, compositionDir) {
73901
74131
  }
73902
74132
 
73903
74133
  // src/commands/canvas/scaffold-video.ts
73904
- import { cp as cp2, mkdir as mkdir10, readFile as readFile28, rm as rm8, writeFile as writeFile15 } from "fs/promises";
74134
+ import { cp as cp2, mkdir as mkdir10, readFile as readFile29, rm as rm8, writeFile as writeFile15 } from "fs/promises";
73905
74135
  import { tmpdir as tmpdir3 } from "os";
73906
- import path35 from "path";
74136
+ import path34 from "path";
73907
74137
  import { defineCommand as defineCommand136 } from "citty";
73908
74138
 
73909
74139
  // src/engine/scaffold/lib/model-router.ts
@@ -73943,7 +74173,7 @@ function routeVideoModel(input) {
73943
74173
 
73944
74174
  // src/engine/nodes/local/lib/sceneDetect.ts
73945
74175
  import { execFile as execFile3 } from "child_process";
73946
- import { mkdtemp as mkdtemp2, readdir as readdir12, readFile as readFile26, rm as rm7 } from "fs/promises";
74176
+ import { mkdtemp as mkdtemp2, readdir as readdir12, readFile as readFile27, rm as rm7 } from "fs/promises";
73947
74177
  import { tmpdir as tmpdir2 } from "os";
73948
74178
  import { join as join3 } from "path";
73949
74179
  import { promisify as promisify3 } from "util";
@@ -74019,7 +74249,7 @@ async function runSceneDetectOnce(filePath, threshold, minSceneLenS, timeoutMs)
74019
74249
  );
74020
74250
  const csvName = (await readdir12(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
74021
74251
  if (!csvName) return [];
74022
- return parsePySceneDetectCsvCuts(await readFile26(join3(outDir, csvName), "utf-8"));
74252
+ return parsePySceneDetectCsvCuts(await readFile27(join3(outDir, csvName), "utf-8"));
74023
74253
  } finally {
74024
74254
  await rm7(outDir, { recursive: true, force: true });
74025
74255
  }
@@ -74043,8 +74273,8 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
74043
74273
  }
74044
74274
 
74045
74275
  // src/commands/canvas/gitignore.ts
74046
- import { appendFile, readFile as readFile27 } from "fs/promises";
74047
- import path34 from "path";
74276
+ import { appendFile, readFile as readFile28 } from "fs/promises";
74277
+ import path33 from "path";
74048
74278
  function missingGitignoreEntries(existing, entries) {
74049
74279
  const present2 = new Set(
74050
74280
  existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
@@ -74052,10 +74282,10 @@ function missingGitignoreEntries(existing, entries) {
74052
74282
  return entries.filter((e) => !present2.has(e.trim().replace(/\/+$/, "")));
74053
74283
  }
74054
74284
  async function ensureGitignore(dir, entries) {
74055
- const file = path34.join(dir, ".gitignore");
74285
+ const file = path33.join(dir, ".gitignore");
74056
74286
  let existing;
74057
74287
  try {
74058
- existing = await readFile27(file, "utf8");
74288
+ existing = await readFile28(file, "utf8");
74059
74289
  } catch {
74060
74290
  return;
74061
74291
  }
@@ -74097,7 +74327,7 @@ async function loadTranscriptBestEffort(ref2) {
74097
74327
  async function stageCaptions(outDir, transcript) {
74098
74328
  const text2 = transcript?.trim();
74099
74329
  if (!text2 || text2 === "[]") return {};
74100
- const compositionPath = path35.join(outDir, "tiktok-captions-composition");
74330
+ const compositionPath = path34.join(outDir, "tiktok-captions-composition");
74101
74331
  await cp2(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
74102
74332
  return { compositionPath };
74103
74333
  }
@@ -74133,7 +74363,7 @@ var VIDEO_EXT_BY_MIME = {
74133
74363
  "video/x-matroska": ".mkv"
74134
74364
  };
74135
74365
  function referenceVideoExt(url, contentType) {
74136
- const fromPath = path35.extname(new URL(url).pathname).toLowerCase();
74366
+ const fromPath = path34.extname(new URL(url).pathname).toLowerCase();
74137
74367
  if (fromPath && fromPath.length <= 5) return fromPath;
74138
74368
  const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase();
74139
74369
  return mime && VIDEO_EXT_BY_MIME[mime] || ".mp4";
@@ -74159,7 +74389,7 @@ function framesModeReport(canvas, requested) {
74159
74389
  return { frames_mode: mode, ...refused ? { frames_reuse_refused: refused } : {} };
74160
74390
  }
74161
74391
  async function materializeReferenceVideo(fileArg2) {
74162
- if (!/^https?:\/\//i.test(fileArg2)) return path35.resolve(fileArg2);
74392
+ if (!/^https?:\/\//i.test(fileArg2)) return path34.resolve(fileArg2);
74163
74393
  let bytes;
74164
74394
  let contentType;
74165
74395
  try {
@@ -74171,7 +74401,7 @@ async function materializeReferenceVideo(fileArg2) {
74171
74401
  throw new Error(`failed to download reference video: ${e instanceof Error ? e.message : String(e)}`);
74172
74402
  }
74173
74403
  if (bytes.length === 0) throw new Error("reference video download was empty");
74174
- const dest = path35.join(
74404
+ const dest = path34.join(
74175
74405
  tmpdir3(),
74176
74406
  `baker-ref-${sha256Hex(bytes).slice(0, 16)}${referenceVideoExt(fileArg2, contentType)}`
74177
74407
  );
@@ -74398,12 +74628,12 @@ var scaffoldVideoCommand = defineCommand136({
74398
74628
  } catch (e) {
74399
74629
  return fail4("download", e instanceof Error ? e.message : String(e));
74400
74630
  }
74401
- const base = path35.basename(videoPath, path35.extname(videoPath));
74402
- const outPath = args8.out ? path35.resolve(String(args8.out)) : slug ? path35.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path35.join(path35.dirname(videoPath), `${base}.video.canvas.json`);
74631
+ const base = path34.basename(videoPath, path34.extname(videoPath));
74632
+ const outPath = args8.out ? path34.resolve(String(args8.out)) : slug ? path34.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path34.join(path34.dirname(videoPath), `${base}.video.canvas.json`);
74403
74633
  if (slug) await enforceNewCreativeLimit(slug, outPath);
74404
- const outDir = path35.dirname(outPath);
74405
- const blueprintPath2 = path35.join(outDir, "prompt.json");
74406
- const blueprintStylePath = path35.join(outDir, "prompt.style.json");
74634
+ const outDir = path34.dirname(outPath);
74635
+ const blueprintPath2 = path34.join(outDir, "prompt.json");
74636
+ const blueprintStylePath = path34.join(outDir, "prompt.style.json");
74407
74637
  const frames = args8.frames === "reuse" ? "reuse" : "generate";
74408
74638
  const maxScenes = args8["max-scenes"] ? Number(args8["max-scenes"]) : void 0;
74409
74639
  if (Number.isFinite(maxScenes)) {
@@ -74472,12 +74702,12 @@ var scaffoldVideoCommand = defineCommand136({
74472
74702
  `
74473
74703
  );
74474
74704
  }
74475
- const compositionDest = path35.join(outDir, "video-overlay-composition");
74705
+ const compositionDest = path34.join(outDir, "video-overlay-composition");
74476
74706
  await cp2(SHIPPED_OVERLAY_COMPOSITION_DIR, compositionDest, { recursive: true });
74477
74707
  await stampCompositionDims(compositionDest, outDims);
74478
- const indexPath = path35.join(compositionDest, "index.html");
74708
+ const indexPath = path34.join(compositionDest, "index.html");
74479
74709
  const overlayHtml = buildOverlayHtml(blueprint, { captionsActive: Boolean(transcript), videoModel });
74480
- const indexHtml = await readFile28(indexPath, "utf8");
74710
+ const indexHtml = await readFile29(indexPath, "utf8");
74481
74711
  const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
74482
74712
  if (injected === indexHtml && overlayHtml.trim()) {
74483
74713
  fail4(
@@ -74491,10 +74721,10 @@ var scaffoldVideoCommand = defineCommand136({
74491
74721
  const opts = {
74492
74722
  imageModel,
74493
74723
  videoModel,
74494
- overlayCompositionPath: path35.relative(outDir, compositionDest),
74495
- captionsCompositionPath: captions.compositionPath ? path35.relative(outDir, captions.compositionPath) : void 0,
74496
- blueprintPath: path35.relative(outDir, blueprintPath2),
74497
- blueprintStylePath: path35.relative(outDir, blueprintStylePath),
74724
+ overlayCompositionPath: path34.relative(outDir, compositionDest),
74725
+ captionsCompositionPath: captions.compositionPath ? path34.relative(outDir, captions.compositionPath) : void 0,
74726
+ blueprintPath: path34.relative(outDir, blueprintPath2),
74727
+ blueprintStylePath: path34.relative(outDir, blueprintStylePath),
74498
74728
  frames,
74499
74729
  ambient: Boolean(args8.ambient),
74500
74730
  seamDedup: resolveSeamDedup(args8["seam-dedup"]),
@@ -74522,7 +74752,7 @@ var scaffoldVideoCommand = defineCommand136({
74522
74752
  await writeFile15(outPath, `${JSON.stringify(canvas, null, 2)}
74523
74753
  `, "utf8");
74524
74754
  await writeFile15(
74525
- path35.join(outDir, REBUILD_FILE),
74755
+ path34.join(outDir, REBUILD_FILE),
74526
74756
  `${JSON.stringify({ elements, opts }, null, 2)}
74527
74757
  `,
74528
74758
  "utf8"
@@ -74549,7 +74779,7 @@ var scaffoldVideoCommand = defineCommand136({
74549
74779
  await ensureGitignore(process.cwd(), ["canvas/", ".context/"]);
74550
74780
  const sourceRef = videoSourceReference(blueprint, fileArg2);
74551
74781
  if (slug) {
74552
- const definitionPath = path35.join(outDir, "_definition.md");
74782
+ const definitionPath = path34.join(outDir, "_definition.md");
74553
74783
  if (!await fileExists(definitionPath)) {
74554
74784
  await writeFile15(
74555
74785
  definitionPath,
@@ -74605,7 +74835,7 @@ var scaffoldVideoCommand = defineCommand136({
74605
74835
  graph: canvas.metadata?.video?.graph_stats
74606
74836
  },
74607
74837
  checklist: {
74608
- 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 \`${path35.basename(blueprintPath2)}\`. \`baker canvas validate\`/\`run\` re-assemble the blueprint and re-flow every edited scene back into the render (and regenerate ${path35.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 ${path35.basename(blueprintStylePath)}; both are regenerated.`,
74838
+ 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 \`${path34.basename(blueprintPath2)}\`. \`baker canvas validate\`/\`run\` re-assemble the blueprint and re-flow every edited scene back into the render (and regenerate ${path34.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 ${path34.basename(blueprintStylePath)}; both are regenerated.`,
74609
74839
  recurring_elements_to_supply: report.elements,
74610
74840
  voices_to_confirm: report.dialogue.map((d) => ({
74611
74841
  scene: d.scene,
@@ -74642,8 +74872,8 @@ var scaffoldVideoCommand = defineCommand136({
74642
74872
  });
74643
74873
 
74644
74874
  // src/commands/canvas/set-prompt.ts
74645
- import { readFile as readFile29, writeFile as writeFile16 } from "fs/promises";
74646
- import path36 from "path";
74875
+ import { readFile as readFile30, writeFile as writeFile16 } from "fs/promises";
74876
+ import path35 from "path";
74647
74877
  import { defineCommand as defineCommand137 } from "citty";
74648
74878
  function setNodePrompt(canvas, nodeId, text2) {
74649
74879
  const nodes = canvas?.nodes;
@@ -74672,8 +74902,8 @@ var SetPromptError = class extends Error {
74672
74902
  }
74673
74903
  };
74674
74904
  async function setPromptInCanvasFile(canvasPath, nodeId, text2) {
74675
- const filePath = path36.resolve(canvasPath);
74676
- const original = await readFile29(filePath, "utf8");
74905
+ const filePath = path35.resolve(canvasPath);
74906
+ const original = await readFile30(filePath, "utf8");
74677
74907
  let canvas;
74678
74908
  try {
74679
74909
  canvas = JSON.parse(original);
@@ -74740,7 +74970,7 @@ var setPromptCommand = defineCommand137({
74740
74970
  },
74741
74971
  async run({ args: args8 }) {
74742
74972
  let text2;
74743
- if (args8["text-file"]) text2 = await readFile29(path36.resolve(String(args8["text-file"])), "utf8");
74973
+ if (args8["text-file"]) text2 = await readFile30(path35.resolve(String(args8["text-file"])), "utf8");
74744
74974
  else if (args8.text !== void 0) text2 = String(args8.text);
74745
74975
  else {
74746
74976
  process.stderr.write(
@@ -74797,7 +75027,7 @@ var staleCommand = defineCommand138({
74797
75027
  });
74798
75028
 
74799
75029
  // src/commands/canvas/validate.ts
74800
- import path37 from "path";
75030
+ import path36 from "path";
74801
75031
  import { defineCommand as defineCommand139 } from "citty";
74802
75032
  var validateCommand = defineCommand139({
74803
75033
  meta: {
@@ -74806,7 +75036,7 @@ var validateCommand = defineCommand139({
74806
75036
  },
74807
75037
  args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
74808
75038
  async run({ args: args8 }) {
74809
- const filePath = path37.resolve(String(args8.file));
75039
+ const filePath = path36.resolve(String(args8.file));
74810
75040
  let prepared;
74811
75041
  try {
74812
75042
  prepared = await prepareCanvas(filePath, { log: (line) => process.stderr.write(`${line}
@@ -75368,7 +75598,7 @@ var feedbackCommand = defineCommand144({
75368
75598
 
75369
75599
  // src/commands/creatives/performance.ts
75370
75600
  import { readFileSync as readFileSync16 } from "fs";
75371
- import path38 from "path";
75601
+ import path37 from "path";
75372
75602
  import { defineCommand as defineCommand145 } from "citty";
75373
75603
  import { parse as parseYaml2 } from "yaml";
75374
75604
  var DESCRIPTION4 = `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.
@@ -75421,7 +75651,7 @@ function definitionInsights(markdown) {
75421
75651
  }
75422
75652
  function readLocalDefinition(slug) {
75423
75653
  try {
75424
- return readFileSync16(path38.join(process.cwd(), "src", "creatives", slug, "_definition.md"), "utf8");
75654
+ return readFileSync16(path37.join(process.cwd(), "src", "creatives", slug, "_definition.md"), "utf8");
75425
75655
  } catch {
75426
75656
  return null;
75427
75657
  }
@@ -75497,7 +75727,7 @@ import { Buffer as Buffer2 } from "buffer";
75497
75727
  import { defineCommand as defineCommand146 } from "citty";
75498
75728
 
75499
75729
  // src/commands/images/api.ts
75500
- import { readFile as readFile30 } from "fs/promises";
75730
+ import { readFile as readFile31 } from "fs/promises";
75501
75731
  import { basename, extname } from "path";
75502
75732
  var imageProcessingTimeoutMs = 18e4;
75503
75733
  var imageReadyPollIntervalMs = 2e3;
@@ -75511,7 +75741,7 @@ var mimeMap = {
75511
75741
  ".avif": "image/avif"
75512
75742
  };
75513
75743
  var defaultImageApiDeps = {
75514
- readFile: readFile30,
75744
+ readFile: readFile31,
75515
75745
  post: apiPost,
75516
75746
  get: apiGet,
75517
75747
  sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
@@ -75577,7 +75807,7 @@ async function waitForReadyImage(deps, imageId, opts = {}) {
75577
75807
  }
75578
75808
 
75579
75809
  // src/commands/videos/api.ts
75580
- import { readFile as readFile31 } from "fs/promises";
75810
+ import { readFile as readFile32 } from "fs/promises";
75581
75811
  import { basename as basename2, extname as extname2 } from "path";
75582
75812
  var videoProcessingTimeoutMs = 18e4;
75583
75813
  var videoReadyPollIntervalMs = 3e3;
@@ -75589,7 +75819,7 @@ var mimeMap2 = {
75589
75819
  ".mkv": "video/x-matroska"
75590
75820
  };
75591
75821
  var defaultVideoApiDeps = {
75592
- readFile: readFile31,
75822
+ readFile: readFile32,
75593
75823
  post: apiPost,
75594
75824
  get: apiGet,
75595
75825
  sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
@@ -75980,12 +76210,12 @@ Full guide: __tooling__/docs/tools/baker/creatives.md`
75980
76210
  import { defineCommand as defineCommand149 } from "citty";
75981
76211
 
75982
76212
  // src/commands/experiment/composition.ts
75983
- import { readdir as readdir14, readFile as readFile33 } from "fs/promises";
75984
- import path40 from "path";
76213
+ import { readdir as readdir14, readFile as readFile34 } from "fs/promises";
76214
+ import path39 from "path";
75985
76215
 
75986
76216
  // src/commands/landing/import-graph.ts
75987
- import { readdir as readdir13, readFile as readFile32, stat as stat6 } from "fs/promises";
75988
- import path39 from "path";
76217
+ import { readdir as readdir13, readFile as readFile33, stat as stat6 } from "fs/promises";
76218
+ import path38 from "path";
75989
76219
  var SOURCE_EXT = /\.(astro|ts|tsx|js|jsx|mjs|cjs|vue|svelte)$/;
75990
76220
  var RESOLVE_CANDIDATES = [
75991
76221
  "",
@@ -76018,7 +76248,7 @@ async function walkFiles(dir) {
76018
76248
  }
76019
76249
  const out = [];
76020
76250
  for (const entry of entries) {
76021
- const full = path39.join(dir, entry.name);
76251
+ const full = path38.join(dir, entry.name);
76022
76252
  if (entry.isDirectory()) {
76023
76253
  if (entry.name === "node_modules" || entry.name === "dist") continue;
76024
76254
  out.push(...await walkFiles(full));
@@ -76032,8 +76262,8 @@ async function resolveSpecifier(specifier, fromFile, root) {
76032
76262
  const bare = specifier.split("?")[0] ?? "";
76033
76263
  if (bare.includes("*")) return null;
76034
76264
  let base;
76035
- if (bare.startsWith("/")) base = path39.join(root, bare.slice(1));
76036
- else if (bare.startsWith(".")) base = path39.resolve(path39.dirname(fromFile), bare);
76265
+ if (bare.startsWith("/")) base = path38.join(root, bare.slice(1));
76266
+ else if (bare.startsWith(".")) base = path38.resolve(path38.dirname(fromFile), bare);
76037
76267
  else return null;
76038
76268
  for (const suffix of RESOLVE_CANDIDATES) {
76039
76269
  const candidate = base + suffix;
@@ -76055,13 +76285,13 @@ async function importedFiles(text2, file, root) {
76055
76285
  }
76056
76286
  async function landingGraphFiles(root, slug) {
76057
76287
  const seen = /* @__PURE__ */ new Set();
76058
- const queue = await walkFiles(path39.join(root, "src/pages", slug));
76288
+ const queue = await walkFiles(path38.join(root, "src/pages", slug));
76059
76289
  while (queue.length > 0) {
76060
76290
  const file = queue.pop();
76061
76291
  if (file === void 0 || seen.has(file)) continue;
76062
76292
  let text2;
76063
76293
  try {
76064
- text2 = await readFile32(file, "utf8");
76294
+ text2 = await readFile33(file, "utf8");
76065
76295
  } catch {
76066
76296
  continue;
76067
76297
  }
@@ -76070,7 +76300,7 @@ async function landingGraphFiles(root, slug) {
76070
76300
  if (!seen.has(target2)) queue.push(target2);
76071
76301
  }
76072
76302
  }
76073
- return new Set([...seen].map((abs) => path39.relative(root, abs).split(path39.sep).join("/")));
76303
+ return new Set([...seen].map((abs) => path38.relative(root, abs).split(path38.sep).join("/")));
76074
76304
  }
76075
76305
 
76076
76306
  // src/commands/experiment/composition.ts
@@ -76115,7 +76345,7 @@ function formsOnlyIn(these, those) {
76115
76345
  }
76116
76346
  async function knownFlows(root) {
76117
76347
  try {
76118
- const entries = await readdir14(path40.join(root, "src/lib/flow-engine/flows"), { withFileTypes: true });
76348
+ const entries = await readdir14(path39.join(root, "src/lib/flow-engine/flows"), { withFileTypes: true });
76119
76349
  return new Set(entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name));
76120
76350
  } catch {
76121
76351
  return /* @__PURE__ */ new Set();
@@ -76126,7 +76356,7 @@ async function readForms(root, slug) {
76126
76356
  const [files, flows] = await Promise.all([landingGraphFiles(root, slug), knownFlows(root)]);
76127
76357
  if (flows.size === 0) return [];
76128
76358
  const texts = await Promise.all(
76129
- [...files].filter((file) => /\.(astro|ts|tsx|js|mjs)$/.test(file)).map((file) => readFile33(path40.join(root, file), "utf8"))
76359
+ [...files].filter((file) => /\.(astro|ts|tsx|js|mjs)$/.test(file)).map((file) => readFile34(path39.join(root, file), "utf8"))
76130
76360
  );
76131
76361
  return formsReferenced(texts, flows);
76132
76362
  } catch {
@@ -76136,8 +76366,8 @@ async function readForms(root, slug) {
76136
76366
 
76137
76367
  // src/commands/experiment/fold.ts
76138
76368
  import { statSync } from "fs";
76139
- import { cp as cp3, mkdir as mkdir11, readdir as readdir15, readFile as readFile34, rm as rm9, stat as stat7, writeFile as writeFile17 } from "fs/promises";
76140
- import path42 from "path";
76369
+ import { cp as cp3, mkdir as mkdir11, readdir as readdir15, readFile as readFile35, rm as rm9, stat as stat7, writeFile as writeFile17 } from "fs/promises";
76370
+ import path41 from "path";
76141
76371
 
76142
76372
  // ../api/src/experiments/variants.ts
76143
76373
  var VARIANT_SEPARATOR = "--";
@@ -76162,21 +76392,21 @@ function nextVariantNumber(page, existingSlugs, archived) {
76162
76392
  }
76163
76393
 
76164
76394
  // src/commands/landing/repoint.ts
76165
- import path41 from "path";
76395
+ import path40 from "path";
76166
76396
  var QUOTED_RELATIVE = /(['"])(\.\.?\/[^'"]*)\1/g;
76167
76397
  function isInside3(abs, dir) {
76168
- return abs === dir || abs.startsWith(dir + path41.sep);
76398
+ return abs === dir || abs.startsWith(dir + path40.sep);
76169
76399
  }
76170
76400
  function toSpecifier(from, to) {
76171
- const rel = path41.relative(from, to).split(path41.sep).join("/");
76401
+ const rel = path40.relative(from, to).split(path40.sep).join("/");
76172
76402
  return rel.startsWith(".") ? rel : `./${rel}`;
76173
76403
  }
76174
76404
  function repointSpecifier(spec, opts) {
76175
76405
  if (!spec.startsWith(".")) return spec;
76176
- const abs = path41.resolve(opts.fromDir, spec);
76406
+ const abs = path40.resolve(opts.fromDir, spec);
76177
76407
  if (opts.forkedTargets?.has(abs)) {
76178
- const withinControl = path41.relative(opts.controlDir, abs);
76179
- return toSpecifier(opts.toDir, path41.resolve(opts.variantDir, withinControl));
76408
+ const withinControl = path40.relative(opts.controlDir, abs);
76409
+ return toSpecifier(opts.toDir, path40.resolve(opts.variantDir, withinControl));
76180
76410
  }
76181
76411
  if (!isInside3(abs, opts.controlDir)) return spec;
76182
76412
  return toSpecifier(opts.toDir, abs);
@@ -76188,9 +76418,9 @@ function repointFile(text2, opts) {
76188
76418
  }
76189
76419
  function repointMoved(text2, opts) {
76190
76420
  return text2.replace(QUOTED_RELATIVE, (_match, quote, spec) => {
76191
- const abs = path41.resolve(opts.fromDir, spec);
76192
- const within = path41.relative(opts.movedFrom, abs);
76193
- const target2 = within.startsWith("..") || path41.isAbsolute(within) ? abs : path41.resolve(opts.movedTo, within);
76421
+ const abs = path40.resolve(opts.fromDir, spec);
76422
+ const within = path40.relative(opts.movedFrom, abs);
76423
+ const target2 = within.startsWith("..") || path40.isAbsolute(within) ? abs : path40.resolve(opts.movedTo, within);
76194
76424
  return `${quote}${toSpecifier(opts.toDir, target2)}${quote}`;
76195
76425
  });
76196
76426
  }
@@ -76199,7 +76429,7 @@ function repointMoved(text2, opts) {
76199
76429
  var ARCHIVE_ROOT = "src/_variants";
76200
76430
  var QUOTED_RELATIVE2 = /(['"])(\.\.?\/[^'"]*)\1/g;
76201
76431
  function posix2(p) {
76202
- return p.split(path42.sep).join("/");
76432
+ return p.split(path41.sep).join("/");
76203
76433
  }
76204
76434
  async function isDir(p) {
76205
76435
  try {
@@ -76218,39 +76448,39 @@ async function filesUnder(dir, prefix = "") {
76218
76448
  const out = [];
76219
76449
  for (const entry of entries) {
76220
76450
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
76221
- if (entry.isDirectory()) out.push(...await filesUnder(path42.join(dir, entry.name), rel));
76451
+ if (entry.isDirectory()) out.push(...await filesUnder(path41.join(dir, entry.name), rel));
76222
76452
  else out.push(rel);
76223
76453
  }
76224
76454
  return out.sort();
76225
76455
  }
76226
76456
  var TEXT_EXT = /\.(astro|ts|tsx|js|jsx|mjs|cjs|vue|svelte|md|json|css)$/;
76227
76457
  function archiveRelPath(abs, base) {
76228
- if (!abs.startsWith(base + path42.sep)) return null;
76229
- return posix2(path42.relative(base, abs));
76458
+ if (!abs.startsWith(base + path41.sep)) return null;
76459
+ return posix2(path41.relative(base, abs));
76230
76460
  }
76231
76461
  async function archiveArm(opts) {
76232
76462
  const { root, armDir, pageDir, archiveDir, armSlug } = opts;
76233
76463
  const targets = await archiveContents({ root, armDir, pageDir, armSlug });
76234
- const archivedBySource = new Map([...targets].map(([rel, abs]) => [abs, path42.join(archiveDir, rel)]));
76464
+ const archivedBySource = new Map([...targets].map(([rel, abs]) => [abs, path41.join(archiveDir, rel)]));
76235
76465
  const written = [];
76236
76466
  for (const [rel, abs] of [...targets].sort()) {
76237
- const dest = path42.join(archiveDir, rel);
76238
- await mkdir11(path42.dirname(dest), { recursive: true });
76467
+ const dest = path41.join(archiveDir, rel);
76468
+ await mkdir11(path41.dirname(dest), { recursive: true });
76239
76469
  if (!TEXT_EXT.test(rel)) {
76240
76470
  await cp3(abs, dest);
76241
76471
  } else {
76242
- const text2 = await readFile34(abs, "utf8");
76472
+ const text2 = await readFile35(abs, "utf8");
76243
76473
  await writeFile17(dest, repointIntoArchive(text2, abs, dest, archivedBySource), "utf8");
76244
76474
  }
76245
- written.push(posix2(path42.relative(root, dest)));
76475
+ written.push(posix2(path41.relative(root, dest)));
76246
76476
  }
76247
76477
  return written;
76248
76478
  }
76249
76479
  async function archiveContents(opts) {
76250
76480
  const { root, armDir, pageDir, armSlug } = opts;
76251
76481
  const graph = await landingGraphFiles(root, armSlug);
76252
- const reachable = new Set([...graph].map((rel) => path42.resolve(root, rel)));
76253
- for (const rel of await filesUnder(armDir)) reachable.add(path42.join(armDir, rel));
76482
+ const reachable = new Set([...graph].map((rel) => path41.resolve(root, rel)));
76483
+ for (const rel of await filesUnder(armDir)) reachable.add(path41.join(armDir, rel));
76254
76484
  const sorted = [...reachable].sort();
76255
76485
  const targets = /* @__PURE__ */ new Map();
76256
76486
  for (const base of [pageDir, armDir]) {
@@ -76263,14 +76493,14 @@ async function archiveContents(opts) {
76263
76493
  return targets;
76264
76494
  }
76265
76495
  function repointIntoArchive(text2, sourceAbs, destAbs, archivedBySource) {
76266
- const fromDir = path42.dirname(sourceAbs);
76267
- const toDir = path42.dirname(destAbs);
76496
+ const fromDir = path41.dirname(sourceAbs);
76497
+ const toDir = path41.dirname(destAbs);
76268
76498
  return text2.replace(QUOTED_RELATIVE2, (match, quote, spec, offset) => {
76269
76499
  const [specPath, suffix] = splitSuffix(spec);
76270
- const target2 = path42.resolve(fromDir, specPath);
76500
+ const target2 = path41.resolve(fromDir, specPath);
76271
76501
  const resolved = resolveInArchive(target2, archivedBySource) ?? resolveOnDisk(target2, isImportPosition(text2, offset));
76272
76502
  if (resolved === null) return match;
76273
- const rel = posix2(path42.relative(toDir, resolved));
76503
+ const rel = posix2(path41.relative(toDir, resolved));
76274
76504
  return `${quote}${rel.startsWith(".") ? rel : `./${rel}`}${suffix}${quote}`;
76275
76505
  });
76276
76506
  }
@@ -76304,25 +76534,25 @@ async function promoteVariantFiles(opts) {
76304
76534
  const written = [];
76305
76535
  for (const rel of await filesUnder(variantDir)) {
76306
76536
  if (rel === "_definition.md") continue;
76307
- const from = path42.join(variantDir, rel);
76308
- const to = path42.join(pageDir, rel);
76309
- await mkdir11(path42.dirname(to), { recursive: true });
76537
+ const from = path41.join(variantDir, rel);
76538
+ const to = path41.join(pageDir, rel);
76539
+ await mkdir11(path41.dirname(to), { recursive: true });
76310
76540
  if (!TEXT_EXT.test(rel)) {
76311
76541
  await cp3(from, to);
76312
76542
  } else {
76313
- const text2 = await readFile34(from, "utf8");
76543
+ const text2 = await readFile35(from, "utf8");
76314
76544
  await writeFile17(
76315
76545
  to,
76316
76546
  repointMoved(text2, {
76317
- fromDir: path42.dirname(from),
76318
- toDir: path42.dirname(to),
76547
+ fromDir: path41.dirname(from),
76548
+ toDir: path41.dirname(to),
76319
76549
  movedFrom: variantDir,
76320
76550
  movedTo: pageDir
76321
76551
  }),
76322
76552
  "utf8"
76323
76553
  );
76324
76554
  }
76325
- written.push(posix2(path42.relative(root, to)));
76555
+ written.push(posix2(path41.relative(root, to)));
76326
76556
  }
76327
76557
  return written;
76328
76558
  }
@@ -76346,8 +76576,8 @@ function archiveRecord(target2, variantNumber) {
76346
76576
  `;
76347
76577
  }
76348
76578
  async function foldExperiment(root, target2) {
76349
- const pageDir = path42.resolve(root, "src/pages", target2.landingSlug);
76350
- const variantDir = path42.resolve(root, "src/pages", target2.variantSlug);
76579
+ const pageDir = path41.resolve(root, "src/pages", target2.landingSlug);
76580
+ const variantDir = path41.resolve(root, "src/pages", target2.variantSlug);
76351
76581
  const variantNumber = variantNumberOf(target2.variantSlug);
76352
76582
  if (variantNumber === null) {
76353
76583
  return {
@@ -76361,7 +76591,7 @@ async function foldExperiment(root, target2) {
76361
76591
  return { error: `src/pages/${target2.variantSlug}/ is already gone \u2014 this test looks folded in already` };
76362
76592
  }
76363
76593
  const archiveKey = target2.landingInternalId ?? target2.landingSlug;
76364
- const archiveDir = path42.resolve(root, ARCHIVE_ROOT, archiveKey, String(variantNumber));
76594
+ const archiveDir = path41.resolve(root, ARCHIVE_ROOT, archiveKey, String(variantNumber));
76365
76595
  await rm9(archiveDir, { recursive: true, force: true });
76366
76596
  await mkdir11(archiveDir, { recursive: true });
76367
76597
  const written = await archiveArm({
@@ -76371,8 +76601,8 @@ async function foldExperiment(root, target2) {
76371
76601
  archiveDir,
76372
76602
  armSlug: target2.survivor === "variant" ? target2.landingSlug : target2.variantSlug
76373
76603
  });
76374
- await writeFile17(path42.join(archiveDir, "_variant.json"), archiveRecord(target2, variantNumber), "utf8");
76375
- written.push(posix2(path42.join(ARCHIVE_ROOT, archiveKey, String(variantNumber), "_variant.json")));
76604
+ await writeFile17(path41.join(archiveDir, "_variant.json"), archiveRecord(target2, variantNumber), "utf8");
76605
+ written.push(posix2(path41.join(ARCHIVE_ROOT, archiveKey, String(variantNumber), "_variant.json")));
76376
76606
  if (target2.survivor === "variant") {
76377
76607
  written.push(...await promoteVariantFiles({ root, pageDir, variantDir }));
76378
76608
  }
@@ -76380,7 +76610,7 @@ async function foldExperiment(root, target2) {
76380
76610
  return {
76381
76611
  experimentId: target2.experimentId,
76382
76612
  page: target2.landingSlug,
76383
- archivedTo: posix2(path42.join(ARCHIVE_ROOT, archiveKey, String(variantNumber))),
76613
+ archivedTo: posix2(path41.join(ARCHIVE_ROOT, archiveKey, String(variantNumber))),
76384
76614
  written,
76385
76615
  removed: [`src/pages/${target2.variantSlug}`]
76386
76616
  };
@@ -77152,11 +77382,11 @@ function parseEvidence(raw, rawArgs) {
77152
77382
  }
77153
77383
 
77154
77384
  // src/commands/experiment/identity.ts
77155
- import { readFile as readFile35 } from "fs/promises";
77156
- import path43 from "path";
77385
+ import { readFile as readFile36 } from "fs/promises";
77386
+ import path42 from "path";
77157
77387
  async function readPageInternalId(root, slug) {
77158
77388
  try {
77159
- const text2 = await readFile35(path43.resolve(root, "src", "pages", slug, "_definition.md"), "utf8");
77389
+ const text2 = await readFile36(path42.resolve(root, "src", "pages", slug, "_definition.md"), "utf8");
77160
77390
  return /^internalId:\s*"?([^"\s]+)"?\s*$/m.exec(text2)?.[1];
77161
77391
  } catch {
77162
77392
  return void 0;
@@ -77164,8 +77394,8 @@ async function readPageInternalId(root, slug) {
77164
77394
  }
77165
77395
 
77166
77396
  // src/commands/experiment/measurement.ts
77167
- import { readFile as readFile36 } from "fs/promises";
77168
- import path44 from "path";
77397
+ import { readFile as readFile37 } from "fs/promises";
77398
+ import path43 from "path";
77169
77399
  function eventDeclarationsIn(source) {
77170
77400
  const found = [];
77171
77401
  const pattern = /\b(data-baker-(?:view|event))\s*=\s*["']([^"']+)["']/g;
@@ -77225,7 +77455,7 @@ function sectionsOf(files, slug) {
77225
77455
  return files.filter((file) => file.startsWith(prefix) && !file.slice(prefix.length).startsWith("index."));
77226
77456
  }
77227
77457
  async function declarationsOf(root, files) {
77228
- const sources = await Promise.all(files.map((file) => readFile36(path44.join(root, file), "utf8").catch(() => "")));
77458
+ const sources = await Promise.all(files.map((file) => readFile37(path43.join(root, file), "utf8").catch(() => "")));
77229
77459
  return sources.flatMap(eventDeclarationsIn);
77230
77460
  }
77231
77461
 
@@ -78277,10 +78507,10 @@ function parseValueExpression(raw) {
78277
78507
  return parts.map(parsePart);
78278
78508
  }
78279
78509
  function trackingFieldIds() {
78280
- const path59 = join4(flowsDir(), "..", "tracking.ts");
78281
- if (!existsSync9(path59)) return null;
78510
+ const path58 = join4(flowsDir(), "..", "tracking.ts");
78511
+ if (!existsSync9(path58)) return null;
78282
78512
  try {
78283
- const source = readFileSync17(path59, "utf-8");
78513
+ const source = readFileSync17(path58, "utf-8");
78284
78514
  const block2 = source.match(/TRACKING_FIELD_IDS\s*=\s*\[([\s\S]*?)\]\s*as const/)?.[1];
78285
78515
  if (!block2) return null;
78286
78516
  const ids2 = [...block2.matchAll(/"(tracking\.[a-z0-9_]+)"/g)].map((match) => match[1]);
@@ -78862,13 +79092,13 @@ function specsFromFile(parsed) {
78862
79092
  return `${destField}${type}=${entry?.value ?? ""}`;
78863
79093
  });
78864
79094
  }
78865
- function readSpecFile(path59) {
79095
+ function readSpecFile(path58) {
78866
79096
  let raw;
78867
79097
  try {
78868
- raw = path59 === "-" ? readFileSync18(0, "utf-8") : readFileSync18(path59, "utf-8");
79098
+ raw = path58 === "-" ? readFileSync18(0, "utf-8") : readFileSync18(path58, "utf-8");
78869
79099
  } catch (error) {
78870
79100
  refuse(
78871
- `Could not read ${path59 === "-" ? "the mapping from stdin" : `"${path59}"`}: ${error instanceof Error ? error.message : String(error)}`
79101
+ `Could not read ${path58 === "-" ? "the mapping from stdin" : `"${path58}"`}: ${error instanceof Error ? error.message : String(error)}`
78872
79102
  );
78873
79103
  }
78874
79104
  let parsed;
@@ -78876,7 +79106,7 @@ function readSpecFile(path59) {
78876
79106
  parsed = JSON.parse(raw);
78877
79107
  } catch (error) {
78878
79108
  refuse(
78879
- `${path59 === "-" ? "stdin" : `"${path59}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
79109
+ `${path58 === "-" ? "stdin" : `"${path58}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
78880
79110
  'Expected { "map": { "<destField>": "<value>", \u2026 } }'
78881
79111
  );
78882
79112
  }
@@ -79175,21 +79405,21 @@ var ARRAY_FIELDS = [
79175
79405
  "tagIds"
79176
79406
  ];
79177
79407
  var ARRAY_OWNERS = ["", "body"];
79178
- function dropUnsetOptionals(sideEffect, path59) {
79408
+ function dropUnsetOptionals(sideEffect, path58) {
79179
79409
  return OPTIONAL_STRINGS.flatMap((key) => {
79180
79410
  if (!(key in sideEffect) || sideEffect[key] !== null && sideEffect[key] !== "") return [];
79181
79411
  delete sideEffect[key];
79182
- return [{ path: path59, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
79412
+ return [{ path: path58, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
79183
79413
  });
79184
79414
  }
79185
- function fillNulledArrays(target2, prefix, path59) {
79415
+ function fillNulledArrays(target2, prefix, path58) {
79186
79416
  return ARRAY_FIELDS.flatMap((key) => {
79187
79417
  if (!(key in target2) || target2[key] !== null) return [];
79188
79418
  target2[key] = [];
79189
- return [{ path: path59, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
79419
+ return [{ path: path58, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
79190
79420
  });
79191
79421
  }
79192
- function tidyEmailRecipients(sideEffect, path59) {
79422
+ function tidyEmailRecipients(sideEffect, path58) {
79193
79423
  if (sideEffect.type !== "email") return [];
79194
79424
  const config = sideEffect.encryptedConfig;
79195
79425
  if (!config || typeof config !== "object" || Array.isArray(config)) return [];
@@ -79200,7 +79430,7 @@ function tidyEmailRecipients(sideEffect, path59) {
79200
79430
  const tidied = to.join(", ");
79201
79431
  if (tidied === raw) return [];
79202
79432
  config.email = tidied;
79203
- return [{ path: path59, change: "tidied the recipient list's spacing and separators" }];
79433
+ return [{ path: path58, change: "tidied the recipient list's spacing and separators" }];
79204
79434
  }
79205
79435
  function sideEffectsOf(node) {
79206
79436
  if (!Array.isArray(node.sideEffects)) return [];
@@ -79209,13 +79439,13 @@ function sideEffectsOf(node) {
79209
79439
  );
79210
79440
  }
79211
79441
  function normalizeSideEffect(sideEffect, where) {
79212
- const path59 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
79442
+ const path58 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
79213
79443
  const arrays = ARRAY_OWNERS.flatMap((owner) => {
79214
79444
  const target2 = owner ? sideEffect[owner] : sideEffect;
79215
79445
  if (!target2 || typeof target2 !== "object") return [];
79216
- return fillNulledArrays(target2, owner ? `${owner}.` : "", path59);
79446
+ return fillNulledArrays(target2, owner ? `${owner}.` : "", path58);
79217
79447
  });
79218
- return [...dropUnsetOptionals(sideEffect, path59), ...arrays, ...tidyEmailRecipients(sideEffect, path59)];
79448
+ return [...dropUnsetOptionals(sideEffect, path58), ...arrays, ...tidyEmailRecipients(sideEffect, path58)];
79219
79449
  }
79220
79450
  function normalizeFlowTree(tree) {
79221
79451
  const changes = [];
@@ -80607,10 +80837,10 @@ async function stageOps(ops) {
80607
80837
  handleError5(err);
80608
80838
  }
80609
80839
  }
80610
- async function draftAction2(path59, body, chat) {
80840
+ async function draftAction2(path58, body, chat) {
80611
80841
  const chatId = resolveChatId(chat);
80612
80842
  try {
80613
- const { data, hints: hints2 } = splitHints(await apiPost(path59, { chatId, ...body }));
80843
+ const { data, hints: hints2 } = splitHints(await apiPost(path58, { chatId, ...body }));
80614
80844
  writeJsonEnvelope({ ok: true, data, ...hints2.length > 0 ? { hints: hints2 } : {} });
80615
80845
  return data;
80616
80846
  } catch (err) {
@@ -83105,7 +83335,7 @@ function cropSprite(input, region) {
83105
83335
 
83106
83336
  // src/lib/image/io.ts
83107
83337
  import { randomBytes } from "crypto";
83108
- import { glob as fsGlob, readFile as readFile37, rename, stat as stat8, writeFile as writeFile18 } from "fs/promises";
83338
+ import { glob as fsGlob, readFile as readFile38, rename, stat as stat8, writeFile as writeFile18 } from "fs/promises";
83109
83339
  import { dirname as dirname2, extname as extname3, join as join5, resolve as resolve4 } from "path";
83110
83340
  var REMOTE_RE = /^https?:\/\//i;
83111
83341
  var GLOB_RE = /[*?[\]{}]/;
@@ -83138,11 +83368,11 @@ async function readImageBuffer(pathOrUrl) {
83138
83368
  const { buffer } = await fetchExternalBytes(pathOrUrl, { maxBytes: MAX_REMOTE_IMAGE_BYTES });
83139
83369
  return buffer;
83140
83370
  }
83141
- return readFile37(pathOrUrl);
83371
+ return readFile38(pathOrUrl);
83142
83372
  }
83143
- async function isDirectory(path59) {
83373
+ async function isDirectory(path58) {
83144
83374
  try {
83145
- const s = await stat8(path59);
83375
+ const s = await stat8(path58);
83146
83376
  return s.isDirectory();
83147
83377
  } catch {
83148
83378
  return false;
@@ -83570,13 +83800,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
83570
83800
  }
83571
83801
  function disambiguate(paths) {
83572
83802
  const taken = /* @__PURE__ */ new Set();
83573
- return paths.map((path59) => {
83574
- if (!taken.has(path59)) {
83575
- taken.add(path59);
83576
- return path59;
83803
+ return paths.map((path58) => {
83804
+ if (!taken.has(path58)) {
83805
+ taken.add(path58);
83806
+ return path58;
83577
83807
  }
83578
- const ext = extname4(path59);
83579
- const stem = path59.slice(0, path59.length - ext.length);
83808
+ const ext = extname4(path58);
83809
+ const stem = path58.slice(0, path58.length - ext.length);
83580
83810
  let n = 2;
83581
83811
  while (taken.has(`${stem}-${n}${ext}`)) n += 1;
83582
83812
  const unique = `${stem}-${n}${ext}`;
@@ -83702,10 +83932,10 @@ async function runDownloads(plan) {
83702
83932
  const paths = disambiguate(fetched.map((item) => item.path));
83703
83933
  const downloaded = [];
83704
83934
  for (const [index, item] of fetched.entries()) {
83705
- const path59 = paths[index] ?? item.path;
83935
+ const path58 = paths[index] ?? item.path;
83706
83936
  try {
83707
- await atomicWrite(path59, item.buffer);
83708
- downloaded.push({ input: item.input, output: path59, bytes: item.buffer.length, contentType: item.contentType });
83937
+ await atomicWrite(path58, item.buffer);
83938
+ downloaded.push({ input: item.input, output: path58, bytes: item.buffer.length, contentType: item.contentType });
83709
83939
  } catch (err) {
83710
83940
  failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
83711
83941
  }
@@ -86687,8 +86917,8 @@ Full guide: __tooling__/docs/tools/baker/images.md`
86687
86917
  import { defineCommand as defineCommand207 } from "citty";
86688
86918
 
86689
86919
  // src/commands/landing/critique.ts
86690
- import { readdir as readdir17, readFile as readFile39, stat as stat10 } from "fs/promises";
86691
- import path47 from "path";
86920
+ import { readdir as readdir17, readFile as readFile40, stat as stat10 } from "fs/promises";
86921
+ import path46 from "path";
86692
86922
  import { defineCommand as defineCommand196 } from "citty";
86693
86923
 
86694
86924
  // src/engine/landing/lib/constants.ts
@@ -87880,13 +88110,13 @@ function describeCounts(findings) {
87880
88110
 
87881
88111
  // src/commands/landing/snapshot.ts
87882
88112
  import { mkdir as mkdir12, rename as rename2, writeFile as writeFile19 } from "fs/promises";
87883
- import path45 from "path";
88113
+ import path44 from "path";
87884
88114
  var CRITIC_VERSION = "4";
87885
88115
  function critiqueCacheDir(projectRoot) {
87886
- return path45.join(projectRoot, ".cache", "landing-critique");
88116
+ return path44.join(projectRoot, ".cache", "landing-critique");
87887
88117
  }
87888
88118
  function snapshotPath(projectRoot, slug) {
87889
- return path45.join(critiqueCacheDir(projectRoot), `${slug}.json`);
88119
+ return path44.join(critiqueCacheDir(projectRoot), `${slug}.json`);
87890
88120
  }
87891
88121
  async function writeCritiqueSnapshot(projectRoot, snapshot) {
87892
88122
  await mkdir12(critiqueCacheDir(projectRoot), { recursive: true });
@@ -87898,8 +88128,8 @@ async function writeCritiqueSnapshot(projectRoot, snapshot) {
87898
88128
  }
87899
88129
 
87900
88130
  // src/commands/landing/source-version.ts
87901
- import { readdir as readdir16, readFile as readFile38, stat as stat9 } from "fs/promises";
87902
- import path46 from "path";
88131
+ import { readdir as readdir16, readFile as readFile39, stat as stat9 } from "fs/promises";
88132
+ import path45 from "path";
87903
88133
  var CRITIQUED_ROOTS = ["src/pages/", "src/components/"];
87904
88134
  async function landingSourceRelPaths(root, slug) {
87905
88135
  const files = await landingGraphFiles(root, slug);
@@ -87908,7 +88138,7 @@ async function landingSourceRelPaths(root, slug) {
87908
88138
  async function readLandingSources(root, slug) {
87909
88139
  const rel = await landingSourceRelPaths(root, slug);
87910
88140
  const out = [];
87911
- for (const r of rel) out.push({ path: r, text: await readFile38(path46.join(root, r), "utf8") });
88141
+ for (const r of rel) out.push({ path: r, text: await readFile39(path45.join(root, r), "utf8") });
87912
88142
  return out;
87913
88143
  }
87914
88144
  async function computeLandingSourceSha(root, slug) {
@@ -87917,7 +88147,7 @@ async function computeLandingSourceSha(root, slug) {
87917
88147
  for (const r of rel) {
87918
88148
  let bytes;
87919
88149
  try {
87920
- bytes = await readFile38(path46.join(root, r));
88150
+ bytes = await readFile39(path45.join(root, r));
87921
88151
  } catch {
87922
88152
  bytes = Buffer.alloc(0);
87923
88153
  }
@@ -87927,16 +88157,16 @@ async function computeLandingSourceSha(root, slug) {
87927
88157
  }
87928
88158
  async function computeLegacyLandingSourceSha(landingDir) {
87929
88159
  const rel = [];
87930
- if (await isFile2(path46.join(landingDir, "index.astro"))) rel.push("index.astro");
87931
- for (const abs of await walkAstro(path46.join(landingDir, "_components"))) {
87932
- rel.push(path46.relative(landingDir, abs).split(path46.sep).join("/"));
88160
+ if (await isFile2(path45.join(landingDir, "index.astro"))) rel.push("index.astro");
88161
+ for (const abs of await walkAstro(path45.join(landingDir, "_components"))) {
88162
+ rel.push(path45.relative(landingDir, abs).split(path45.sep).join("/"));
87933
88163
  }
87934
88164
  rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
87935
88165
  const parts = [];
87936
88166
  for (const r of rel) {
87937
88167
  let bytes;
87938
88168
  try {
87939
- bytes = await readFile38(path46.join(landingDir, r));
88169
+ bytes = await readFile39(path45.join(landingDir, r));
87940
88170
  } catch {
87941
88171
  bytes = Buffer.alloc(0);
87942
88172
  }
@@ -87960,7 +88190,7 @@ async function walkAstro(dir) {
87960
88190
  }
87961
88191
  const out = [];
87962
88192
  for (const entry of entries) {
87963
- const abs = path46.join(dir, entry.name);
88193
+ const abs = path45.join(dir, entry.name);
87964
88194
  if (entry.isDirectory()) out.push(...await walkAstro(abs));
87965
88195
  else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
87966
88196
  }
@@ -88021,7 +88251,7 @@ var critiqueCommand2 = defineCommand196({
88021
88251
  { availableSlugs: await listLandingSlugs(projectRoot) }
88022
88252
  );
88023
88253
  }
88024
- if (!await isDir2(path47.resolve(projectRoot, "src", "pages", slug))) {
88254
+ if (!await isDir2(path46.resolve(projectRoot, "src", "pages", slug))) {
88025
88255
  fail8("NOT_FOUND", `No landing at src/pages/${slug}/`, {
88026
88256
  availableSlugs: await listLandingSlugs(projectRoot)
88027
88257
  });
@@ -88064,7 +88294,7 @@ async function critiqueOne(projectRoot, slug, brand, competitors) {
88064
88294
  const [sources, compositionSha, sourceSha] = await Promise.all([
88065
88295
  readLandingSources(projectRoot, slug),
88066
88296
  computeLandingSourceSha(projectRoot, slug),
88067
- computeLegacyLandingSourceSha(path47.resolve(projectRoot, "src", "pages", slug))
88297
+ computeLegacyLandingSourceSha(path46.resolve(projectRoot, "src", "pages", slug))
88068
88298
  ]);
88069
88299
  const report = critiqueLanding({ slug, sources, brand, competitors });
88070
88300
  let snapshotFailed = false;
@@ -88084,7 +88314,7 @@ async function critiqueOne(projectRoot, slug, brand, competitors) {
88084
88314
  return { slug, report, snapshotFailed };
88085
88315
  }
88086
88316
  async function readCompetitorNames(projectRoot) {
88087
- const dir = path47.join(projectRoot, "src", "content", "competitors");
88317
+ const dir = path46.join(projectRoot, "src", "content", "competitors");
88088
88318
  let entries;
88089
88319
  try {
88090
88320
  entries = (await readdir17(dir)).filter((f) => f.endsWith(".md"));
@@ -88093,9 +88323,9 @@ async function readCompetitorNames(projectRoot) {
88093
88323
  }
88094
88324
  const names = [];
88095
88325
  for (const entry of entries) {
88096
- names.push(path47.basename(entry, ".md").replace(/[-_]+/g, " "));
88326
+ names.push(path46.basename(entry, ".md").replace(/[-_]+/g, " "));
88097
88327
  try {
88098
- const head = (await readFile39(path47.join(dir, entry), "utf8")).slice(0, 2e3);
88328
+ const head = (await readFile40(path46.join(dir, entry), "utf8")).slice(0, 2e3);
88099
88329
  const titled = /^\s*(?:title|name)\s*:\s*["']?([^"'\n]+)["']?\s*$/im.exec(head);
88100
88330
  if (titled?.[1]) names.push(titled[1].trim());
88101
88331
  } catch {
@@ -88105,7 +88335,7 @@ async function readCompetitorNames(projectRoot) {
88105
88335
  }
88106
88336
  async function listLandingSlugs(projectRoot) {
88107
88337
  try {
88108
- const entries = await readdir17(path47.join(projectRoot, "src", "pages"), { withFileTypes: true });
88338
+ const entries = await readdir17(path46.join(projectRoot, "src", "pages"), { withFileTypes: true });
88109
88339
  return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
88110
88340
  } catch {
88111
88341
  return [];
@@ -88257,7 +88487,7 @@ var addCommand = defineCommand197({
88257
88487
 
88258
88488
  // src/commands/landing/inspiration/code.ts
88259
88489
  import { mkdir as mkdir13, writeFile as writeFile20 } from "fs/promises";
88260
- import path48 from "path";
88490
+ import path47 from "path";
88261
88491
  import { defineCommand as defineCommand198 } from "citty";
88262
88492
  registerSchema({
88263
88493
  command: "landing.inspiration.code",
@@ -88280,9 +88510,9 @@ var codeCommand = defineCommand198({
88280
88510
  try {
88281
88511
  const id = args8.id;
88282
88512
  const data = await apiGet("/api/landing-inspiration/section-code", { id });
88283
- const dir = path48.join(process.cwd(), ".baker", "inspiration", id);
88513
+ const dir = path47.join(process.cwd(), ".baker", "inspiration", id);
88284
88514
  await mkdir13(dir, { recursive: true });
88285
- const file = path48.join(dir, "section.html");
88515
+ const file = path47.join(dir, "section.html");
88286
88516
  await writeFile20(file, data.html);
88287
88517
  const hints2 = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
88288
88518
  const fidelity = fidelityHint(data.fidelity);
@@ -88292,7 +88522,7 @@ var codeCommand = defineCommand198({
88292
88522
  ok: true,
88293
88523
  data: {
88294
88524
  id,
88295
- file: path48.relative(process.cwd(), file),
88525
+ file: path47.relative(process.cwd(), file),
88296
88526
  bytes: data.html.length,
88297
88527
  fidelity: data.fidelity,
88298
88528
  reproduction_notes: data.reproductionNotes,
@@ -88724,7 +88954,7 @@ function classifyCaptureFailure(error) {
88724
88954
 
88725
88955
  // src/engine/landing-library/run.ts
88726
88956
  import { mkdir as mkdir14, writeFile as writeFile22 } from "fs/promises";
88727
- import path50 from "path";
88957
+ import path49 from "path";
88728
88958
 
88729
88959
  // ../proxy/src/preflight.ts
88730
88960
  import http from "http";
@@ -90140,9 +90370,9 @@ async function renderBundleToPng(browser, html, viewportWidth, options = {}) {
90140
90370
 
90141
90371
  // src/engine/landing-library/report.ts
90142
90372
  import { writeFile as writeFile21 } from "fs/promises";
90143
- import path49 from "path";
90373
+ import path48 from "path";
90144
90374
  async function writeCaptureReport(manifest, outDir) {
90145
- const file = path49.join(outDir, "report.html");
90375
+ const file = path48.join(outDir, "report.html");
90146
90376
  await writeFile21(file, renderReport(manifest));
90147
90377
  return file;
90148
90378
  }
@@ -90321,32 +90551,32 @@ async function reproducePage(args8) {
90321
90551
  const { browser, page, outDir, pageUrl, livePageShot } = args8;
90322
90552
  const built = await buildSectionBundle(page, "body", pageUrl).catch(() => null);
90323
90553
  if (!built) return { bundle: null, fidelity: null };
90324
- await writeFile22(path50.join(outDir, "page.html"), built.html);
90554
+ await writeFile22(path49.join(outDir, "page.html"), built.html);
90325
90555
  const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width, {
90326
90556
  wholePage: true,
90327
90557
  timeoutMs: 6e4
90328
90558
  });
90329
90559
  if (!rendered || !livePageShot) return { bundle: "page.html", fidelity: null };
90330
- await writeFile22(path50.join(outDir, "page-rendered.png"), rendered);
90560
+ await writeFile22(path49.join(outDir, "page-rendered.png"), rendered);
90331
90561
  const { score, note } = await scoreFidelity(livePageShot, rendered);
90332
90562
  return { bundle: "page.html", fidelity: score, ...note ? { fidelityNote: note } : {} };
90333
90563
  }
90334
90564
  async function captureOneSection(args8) {
90335
90565
  const { browser, page, candidate, sectionsDir, outDir, pageUrl, withCode } = args8;
90336
- const dir = path50.join(sectionsDir, String(candidate.index).padStart(2, "0"));
90566
+ const dir = path49.join(sectionsDir, String(candidate.index).padStart(2, "0"));
90337
90567
  await mkdir14(dir, { recursive: true });
90338
90568
  const desktop = await captureSection(page, candidate);
90339
- if (desktop) await writeFile22(path50.join(dir, "desktop.png"), desktop);
90569
+ if (desktop) await writeFile22(path49.join(dir, "desktop.png"), desktop);
90340
90570
  const visualHash = desktop ? await perceptualHash(desktop) : null;
90341
90571
  const motion = await collectMotion(page, candidate.selector);
90342
90572
  const built = withCode ? await buildSectionBundle(page, candidate.selector, pageUrl) : null;
90343
90573
  let fidelity = null;
90344
90574
  let fidelityNote;
90345
90575
  if (built) {
90346
- await writeFile22(path50.join(dir, "section.html"), built.html);
90576
+ await writeFile22(path49.join(dir, "section.html"), built.html);
90347
90577
  const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width);
90348
90578
  if (rendered && desktop) {
90349
- await writeFile22(path50.join(dir, "section-rendered.png"), rendered);
90579
+ await writeFile22(path49.join(dir, "section-rendered.png"), rendered);
90350
90580
  const result = await scoreFidelity(desktop, rendered);
90351
90581
  fidelity = result.score;
90352
90582
  fidelityNote = result.note;
@@ -90354,9 +90584,9 @@ async function captureOneSection(args8) {
90354
90584
  }
90355
90585
  return {
90356
90586
  ...candidate,
90357
- desktopShot: desktop ? path50.relative(outDir, path50.join(dir, "desktop.png")) : null,
90587
+ desktopShot: desktop ? path49.relative(outDir, path49.join(dir, "desktop.png")) : null,
90358
90588
  mobileShot: null,
90359
- bundle: built ? path50.relative(outDir, path50.join(dir, "section.html")) : null,
90589
+ bundle: built ? path49.relative(outDir, path49.join(dir, "section.html")) : null,
90360
90590
  fidelity,
90361
90591
  ...fidelityNote ? { fidelityNote } : {},
90362
90592
  ...built ? { cssStats: built.stats } : {},
@@ -90375,9 +90605,9 @@ async function captureMobileShots(args8) {
90375
90605
  for (const section of sections) {
90376
90606
  const shot = await captureSectionOnMobile(mobile.page, section);
90377
90607
  if (!shot) continue;
90378
- const file = path50.join(sectionsDir, String(section.index).padStart(2, "0"), "mobile.png");
90608
+ const file = path49.join(sectionsDir, String(section.index).padStart(2, "0"), "mobile.png");
90379
90609
  await writeFile22(file, shot);
90380
- section.mobileShot = path50.relative(outDir, file);
90610
+ section.mobileShot = path49.relative(outDir, file);
90381
90611
  }
90382
90612
  } finally {
90383
90613
  await mobile.context.close();
@@ -90392,10 +90622,10 @@ async function captureMotionTakes(args8) {
90392
90622
  const filmOne = async (section) => {
90393
90623
  const take = await captureMotionTake(browser, pageUrl, section.selector).catch(() => null);
90394
90624
  if (!take) return;
90395
- const dir = path50.join(sectionsDir, String(section.index).padStart(2, "0"));
90396
- const file = path50.join(dir, "motion-filmstrip.png");
90625
+ const dir = path49.join(sectionsDir, String(section.index).padStart(2, "0"));
90626
+ const file = path49.join(dir, "motion-filmstrip.png");
90397
90627
  await writeFile22(file, take.filmstrip);
90398
- section.motionFilmstrip = path50.relative(outDir, file);
90628
+ section.motionFilmstrip = path49.relative(outDir, file);
90399
90629
  log(` [${section.index}] ${section.motion.summary}`);
90400
90630
  };
90401
90631
  const queue = [...moving];
@@ -90452,7 +90682,7 @@ async function captureAlternateViews(args8) {
90452
90682
  async function reproduceWholePage(args8) {
90453
90683
  const { browser, page, outDir, pageUrl, withCode, log } = args8;
90454
90684
  const fullPage = await page.screenshot({ type: "png", fullPage: true }).catch(() => null);
90455
- if (fullPage) await writeFile22(path50.join(outDir, "full-page.png"), fullPage);
90685
+ if (fullPage) await writeFile22(path49.join(outDir, "full-page.png"), fullPage);
90456
90686
  if (!withCode) return { bundle: null, fidelity: null };
90457
90687
  const reproduction = await reproducePage({ browser, page, outDir, pageUrl, livePageShot: fullPage });
90458
90688
  log(`page reproduction: ${reproduction.fidelity === null ? "unavailable" : reproduction.fidelity.toFixed(2)}`);
@@ -90523,7 +90753,7 @@ async function openViaLadder(args8) {
90523
90753
  async function scrapeLanding(options) {
90524
90754
  const timeoutMs = options.timeoutMs ?? 45e3;
90525
90755
  const log = options.onProgress ?? (() => void 0);
90526
- const sectionsDir = path50.join(options.outDir, "sections");
90756
+ const sectionsDir = path49.join(options.outDir, "sections");
90527
90757
  const nonPublic = refuseNonPublicUrl(options.url);
90528
90758
  if (nonPublic) {
90529
90759
  throw new BlockedPageError({
@@ -90585,7 +90815,7 @@ async function scrapeLanding(options) {
90585
90815
  security: prepared.security,
90586
90816
  captureTier: tier
90587
90817
  };
90588
- await writeFile22(path50.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
90818
+ await writeFile22(path49.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
90589
90819
  `);
90590
90820
  if (options.report !== false) {
90591
90821
  const reportPath = await writeCaptureReport(manifest, options.outDir);
@@ -90600,28 +90830,28 @@ async function scrapeLanding(options) {
90600
90830
 
90601
90831
  // src/commands/landing/inspiration/captureOut.ts
90602
90832
  import { existsSync as existsSync12 } from "fs";
90603
- import path51 from "path";
90833
+ import path50 from "path";
90604
90834
  var SCRATCH_DIR = ".baker";
90605
90835
  function isWithin(parent, target2) {
90606
- const relative = path51.relative(parent, target2);
90607
- return relative === "" || !relative.startsWith("..") && !path51.isAbsolute(relative);
90836
+ const relative = path50.relative(parent, target2);
90837
+ return relative === "" || !relative.startsWith("..") && !path50.isAbsolute(relative);
90608
90838
  }
90609
90839
  function findRepoRoot(from) {
90610
- let dir = path51.resolve(from);
90840
+ let dir = path50.resolve(from);
90611
90841
  for (; ; ) {
90612
- if (existsSync12(path51.join(dir, ".git"))) return dir;
90613
- const parent = path51.dirname(dir);
90842
+ if (existsSync12(path50.join(dir, ".git"))) return dir;
90843
+ const parent = path50.dirname(dir);
90614
90844
  if (parent === dir) return null;
90615
90845
  dir = parent;
90616
90846
  }
90617
90847
  }
90618
90848
  function checkCaptureOut(out, options) {
90619
90849
  const { cwd, repoRoot } = options;
90620
- const resolved = path51.resolve(cwd, out);
90850
+ const resolved = path50.resolve(cwd, out);
90621
90851
  if (repoRoot === null || !isWithin(repoRoot, resolved)) return { ok: true };
90622
- const scratch = path51.join(repoRoot, SCRATCH_DIR);
90852
+ const scratch = path50.join(repoRoot, SCRATCH_DIR);
90623
90853
  if (isWithin(scratch, resolved)) return { ok: true };
90624
- const suggestion = path51.posix.join(SCRATCH_DIR, "teardowns", path51.basename(resolved) || "capture");
90854
+ const suggestion = path50.posix.join(SCRATCH_DIR, "teardowns", path50.basename(resolved) || "capture");
90625
90855
  return {
90626
90856
  ok: false,
90627
90857
  error: {
@@ -90795,12 +91025,12 @@ var scrapeCommand = defineCommand201({
90795
91025
  });
90796
91026
 
90797
91027
  // src/commands/landing/inspiration/search.ts
90798
- import path53 from "path";
91028
+ import path52 from "path";
90799
91029
  import { defineCommand as defineCommand202 } from "citty";
90800
91030
 
90801
91031
  // src/commands/landing/inspiration/shot.ts
90802
91032
  import { mkdir as mkdir15, writeFile as writeFile23 } from "fs/promises";
90803
- import path52 from "path";
91033
+ import path51 from "path";
90804
91034
  import sharp8 from "sharp";
90805
91035
  var READABLE_SHOT = {
90806
91036
  maxWidth: 1440,
@@ -90826,9 +91056,9 @@ async function downloadReadableShot(url, file) {
90826
91056
  const response = await fetch(url);
90827
91057
  if (!response.ok) return null;
90828
91058
  const shot = await toReadableShot(Buffer.from(await response.arrayBuffer()));
90829
- await mkdir15(path52.dirname(file), { recursive: true });
91059
+ await mkdir15(path51.dirname(file), { recursive: true });
90830
91060
  await writeFile23(file, shot);
90831
- return path52.relative(process.cwd(), file);
91061
+ return path51.relative(process.cwd(), file);
90832
91062
  } catch {
90833
91063
  return null;
90834
91064
  }
@@ -90920,13 +91150,13 @@ function buildSearchBody(args8) {
90920
91150
  return body;
90921
91151
  }
90922
91152
  async function downloadShots(results) {
90923
- const dir = path53.join(process.cwd(), ".baker", "inspiration");
91153
+ const dir = path52.join(process.cwd(), ".baker", "inspiration");
90924
91154
  const saved = /* @__PURE__ */ new Map();
90925
91155
  await Promise.all(
90926
91156
  results.map(async (result) => {
90927
91157
  const file = await downloadReadableShot(
90928
91158
  result.desktopShotUrl,
90929
- path53.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
91159
+ path52.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
90930
91160
  );
90931
91161
  if (file) saved.set(result.id, file);
90932
91162
  })
@@ -91154,7 +91384,7 @@ var sequencesCommand = defineCommand203({
91154
91384
  });
91155
91385
 
91156
91386
  // src/commands/landing/inspiration/view.ts
91157
- import path54 from "path";
91387
+ import path53 from "path";
91158
91388
  import { defineCommand as defineCommand204 } from "citty";
91159
91389
  registerSchema({
91160
91390
  command: "landing.inspiration.view",
@@ -91187,12 +91417,12 @@ var viewCommand2 = defineCommand204({
91187
91417
  const id = args8.id;
91188
91418
  const data = await apiGet("/api/landing-inspiration/section", { id });
91189
91419
  const section = data.section;
91190
- const dir = path54.join(process.cwd(), ".baker", "inspiration", id);
91420
+ const dir = path53.join(process.cwd(), ".baker", "inspiration", id);
91191
91421
  const ext = READABLE_SHOT.extension;
91192
91422
  const [desktop, mobile, filmstrip] = await Promise.all([
91193
- downloadReadableShot(section.desktopShotUrl, path54.join(dir, `desktop.${ext}`)),
91194
- downloadReadableShot(section.mobileShotUrl, path54.join(dir, `mobile.${ext}`)),
91195
- downloadReadableShot(section.motionFilmstripUrl, path54.join(dir, `motion-filmstrip.${ext}`))
91423
+ downloadReadableShot(section.desktopShotUrl, path53.join(dir, `desktop.${ext}`)),
91424
+ downloadReadableShot(section.mobileShotUrl, path53.join(dir, `mobile.${ext}`)),
91425
+ downloadReadableShot(section.motionFilmstripUrl, path53.join(dir, `motion-filmstrip.${ext}`))
91196
91426
  ]);
91197
91427
  const full = args8.full;
91198
91428
  const hints2 = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
@@ -91298,8 +91528,8 @@ Full guide: __tooling__/docs/tools/baker/landing.md`
91298
91528
 
91299
91529
  // src/commands/landing/variant.ts
91300
91530
  import { randomUUID as randomUUID3 } from "crypto";
91301
- import { mkdir as mkdir16, readdir as readdir18, readFile as readFile40, stat as stat11, writeFile as writeFile24 } from "fs/promises";
91302
- import path55 from "path";
91531
+ import { mkdir as mkdir16, readdir as readdir18, readFile as readFile41, stat as stat11, writeFile as writeFile24 } from "fs/promises";
91532
+ import path54 from "path";
91303
91533
  import { defineCommand as defineCommand206 } from "citty";
91304
91534
  registerSchema({
91305
91535
  command: "landing.variant",
@@ -91326,7 +91556,7 @@ registerSchema({
91326
91556
  var SLUG_RE2 = /^[a-z0-9][a-z0-9-]*$/;
91327
91557
  async function readInternalId(definitionPath) {
91328
91558
  try {
91329
- const text2 = await readFile40(definitionPath, "utf8");
91559
+ const text2 = await readFile41(definitionPath, "utf8");
91330
91560
  return /^internalId:\s*"?([^"\s]+)"?\s*$/m.exec(text2)?.[1] ?? null;
91331
91561
  } catch {
91332
91562
  return null;
@@ -91335,7 +91565,7 @@ async function readInternalId(definitionPath) {
91335
91565
  async function archivedVariantNumbers(root, internalId) {
91336
91566
  if (!internalId) return [];
91337
91567
  try {
91338
- const entries = await readdir18(path55.resolve(root, "src/_variants", internalId), { withFileTypes: true });
91568
+ const entries = await readdir18(path54.resolve(root, "src/_variants", internalId), { withFileTypes: true });
91339
91569
  return entries.filter((entry) => entry.isDirectory()).map((entry) => Number(entry.name)).filter((n) => Number.isInteger(n) && n > 0);
91340
91570
  } catch {
91341
91571
  return [];
@@ -91365,7 +91595,7 @@ async function exists2(p) {
91365
91595
  }
91366
91596
  async function listLandingSlugs2(root) {
91367
91597
  try {
91368
- const entries = await readdir18(path55.resolve(root, "src", "pages"), { withFileTypes: true });
91598
+ const entries = await readdir18(path54.resolve(root, "src", "pages"), { withFileTypes: true });
91369
91599
  return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
91370
91600
  } catch {
91371
91601
  return [];
@@ -91381,7 +91611,7 @@ async function listComponents(componentsDir, prefix = "") {
91381
91611
  const out = [];
91382
91612
  for (const entry of entries) {
91383
91613
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
91384
- if (entry.isDirectory()) out.push(...await listComponents(path55.join(componentsDir, entry.name), rel));
91614
+ if (entry.isDirectory()) out.push(...await listComponents(path54.join(componentsDir, entry.name), rel));
91385
91615
  else if (entry.name.endsWith(".astro")) out.push(rel);
91386
91616
  }
91387
91617
  return out.sort();
@@ -91448,16 +91678,16 @@ async function resolveTargets(projectRoot, opts) {
91448
91678
  `"${controlSlug}" is already a variant of "${variantOfSlug(controlSlug)}". Make the next variant of the page itself: \`baker landing variant ${variantOfSlug(controlSlug)}\`.`
91449
91679
  );
91450
91680
  }
91451
- const controlDir = path55.resolve(projectRoot, "src", "pages", controlSlug);
91681
+ const controlDir = path54.resolve(projectRoot, "src", "pages", controlSlug);
91452
91682
  if (!await isDir3(controlDir)) {
91453
91683
  fail9("NOT_FOUND", `No page at src/pages/${controlSlug}/`, {
91454
91684
  availableSlugs: await listLandingSlugs2(projectRoot)
91455
91685
  });
91456
91686
  }
91457
- if (!await exists2(path55.join(controlDir, "index.astro"))) {
91687
+ if (!await exists2(path54.join(controlDir, "index.astro"))) {
91458
91688
  fail9("NOT_FOUND", `src/pages/${controlSlug}/index.astro is missing, so there is no page to make a variant of.`);
91459
91689
  }
91460
- const internalId = await readInternalId(path55.join(controlDir, "_definition.md"));
91690
+ const internalId = await readInternalId(path54.join(controlDir, "_definition.md"));
91461
91691
  const variantSlugValue = variantSlug(
91462
91692
  controlSlug,
91463
91693
  nextVariantNumber(
@@ -91466,8 +91696,8 @@ async function resolveTargets(projectRoot, opts) {
91466
91696
  await archivedVariantNumbers(projectRoot, internalId)
91467
91697
  )
91468
91698
  );
91469
- const variantDir = path55.resolve(projectRoot, "src", "pages", variantSlugValue);
91470
- const available = await listComponents(path55.join(controlDir, "_components"));
91699
+ const variantDir = path54.resolve(projectRoot, "src", "pages", variantSlugValue);
91700
+ const available = await listComponents(path54.join(controlDir, "_components"));
91471
91701
  const forks = [];
91472
91702
  for (const name of parseForks(opts.fork, opts.rawArgs)) {
91473
91703
  const resolved = resolveForkName(name, available);
@@ -91483,26 +91713,26 @@ async function resolveTargets(projectRoot, opts) {
91483
91713
  }
91484
91714
  async function writeVariant(opts) {
91485
91715
  const { controlDir, variantDir, controlSlug, variantSlug: variantSlug2, forks } = opts;
91486
- const forkedTargets = new Set(forks.map((f) => path55.join(controlDir, "_components", f)));
91716
+ const forkedTargets = new Set(forks.map((f) => path54.join(controlDir, "_components", f)));
91487
91717
  const written = [];
91488
- const indexText = await readFile40(path55.join(controlDir, "index.astro"), "utf8");
91718
+ const indexText = await readFile41(path54.join(controlDir, "index.astro"), "utf8");
91489
91719
  await mkdir16(variantDir, { recursive: true });
91490
91720
  await writeFile24(
91491
- path55.join(variantDir, "index.astro"),
91721
+ path54.join(variantDir, "index.astro"),
91492
91722
  repointFile(indexText, { fromDir: controlDir, toDir: variantDir, controlDir, variantDir, forkedTargets }),
91493
91723
  "utf8"
91494
91724
  );
91495
91725
  written.push(`src/pages/${variantSlug2}/index.astro`);
91496
91726
  for (const fork of forks) {
91497
- const fromFile = path55.join(controlDir, "_components", fork);
91498
- const toFile = path55.join(variantDir, "_components", fork);
91499
- const text2 = await readFile40(fromFile, "utf8");
91500
- await mkdir16(path55.dirname(toFile), { recursive: true });
91727
+ const fromFile = path54.join(controlDir, "_components", fork);
91728
+ const toFile = path54.join(variantDir, "_components", fork);
91729
+ const text2 = await readFile41(fromFile, "utf8");
91730
+ await mkdir16(path54.dirname(toFile), { recursive: true });
91501
91731
  await writeFile24(
91502
91732
  toFile,
91503
91733
  repointFile(text2, {
91504
- fromDir: path55.dirname(fromFile),
91505
- toDir: path55.dirname(toFile),
91734
+ fromDir: path54.dirname(fromFile),
91735
+ toDir: path54.dirname(toFile),
91506
91736
  controlDir,
91507
91737
  variantDir,
91508
91738
  forkedTargets
@@ -91511,9 +91741,9 @@ async function writeVariant(opts) {
91511
91741
  );
91512
91742
  written.push(`src/pages/${variantSlug2}/_components/${fork}`);
91513
91743
  }
91514
- const controlDefinitionPath = path55.join(controlDir, "_definition.md");
91744
+ const controlDefinitionPath = path54.join(controlDir, "_definition.md");
91515
91745
  if (await exists2(controlDefinitionPath)) {
91516
- const definition = buildVariantDefinition(await readFile40(controlDefinitionPath, "utf8"), {
91746
+ const definition = buildVariantDefinition(await readFile41(controlDefinitionPath, "utf8"), {
91517
91747
  internalId: randomUUID3().replace(/-/g, "").slice(0, 8),
91518
91748
  variantSlug: variantSlug2,
91519
91749
  controlSlug,
@@ -91521,11 +91751,11 @@ async function writeVariant(opts) {
91521
91751
  ...opts.because ? { because: opts.because } : {},
91522
91752
  ...opts.change ? { change: opts.change } : {}
91523
91753
  });
91524
- await writeFile24(path55.join(variantDir, "_definition.md"), definition, "utf8");
91754
+ await writeFile24(path54.join(variantDir, "_definition.md"), definition, "utf8");
91525
91755
  written.push(`src/pages/${variantSlug2}/_definition.md`);
91526
91756
  }
91527
- await mkdir16(path55.join(variantDir, "_images"), { recursive: true });
91528
- await writeFile24(path55.join(variantDir, "_images", ".gitkeep"), "", "utf8");
91757
+ await mkdir16(path54.join(variantDir, "_images"), { recursive: true });
91758
+ await writeFile24(path54.join(variantDir, "_images", ".gitkeep"), "", "utf8");
91529
91759
  return written;
91530
91760
  }
91531
91761
  var variantCommand = defineCommand206({
@@ -94230,8 +94460,8 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
94230
94460
  });
94231
94461
 
94232
94462
  // src/commands/scheduled-agents/templates.ts
94233
- import { readFile as readFile41 } from "fs/promises";
94234
- import path56 from "path";
94463
+ import { readFile as readFile42 } from "fs/promises";
94464
+ import path55 from "path";
94235
94465
  import { defineCommand as defineCommand234 } from "citty";
94236
94466
  registerSchema({
94237
94467
  command: "scheduled-agents.templates",
@@ -94338,7 +94568,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
94338
94568
  }
94339
94569
  if (save.length > 0) {
94340
94570
  const briefFile = flag("brief-file");
94341
- const brief2 = briefFile.length > 0 ? await readFile41(path56.resolve(briefFile), "utf8") : flag("brief");
94571
+ const brief2 = briefFile.length > 0 ? await readFile42(path55.resolve(briefFile), "utf8") : flag("brief");
94342
94572
  if (brief2.trim().length === 0) {
94343
94573
  failValidation4("--brief-file (preferred) or --brief is required: the brief is the recipe.");
94344
94574
  }
@@ -94978,7 +95208,7 @@ function parseImageRefs(spec) {
94978
95208
  }
94979
95209
  var defaultDeps = {
94980
95210
  ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
94981
- upload: (path59) => uploadLocalImage({ file: path59, contentType: detectImageContentType(path59), source: "uploaded" })
95211
+ upload: (path58) => uploadLocalImage({ file: path58, contentType: detectImageContentType(path58), source: "uploaded" })
94982
95212
  };
94983
95213
  async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
94984
95214
  const refs = parseImageRefs(spec);
@@ -94998,10 +95228,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
94998
95228
  }
94999
95229
  return { imageIds, added };
95000
95230
  }
95001
- function uploadFailure(path59) {
95231
+ function uploadFailure(path58) {
95002
95232
  return (error) => {
95003
95233
  if (error instanceof ApiError) throw error;
95004
- throw new ApiError("VALIDATION_ERROR", `Could not read "${path59}" as an image.`);
95234
+ throw new ApiError("VALIDATION_ERROR", `Could not read "${path58}" as an image.`);
95005
95235
  };
95006
95236
  }
95007
95237
 
@@ -96101,10 +96331,10 @@ async function stageOp4(op) {
96101
96331
  handleError8(err);
96102
96332
  }
96103
96333
  }
96104
- async function draftAction3(path59, body, chat) {
96334
+ async function draftAction3(path58, body, chat) {
96105
96335
  const chatId = resolveChatId(chat);
96106
96336
  try {
96107
- const { data, hints: hints2 } = splitHints(await apiPost(path59, { chatId, ...body }));
96337
+ const { data, hints: hints2 } = splitHints(await apiPost(path58, { chatId, ...body }));
96108
96338
  writeJsonEnvelope({ ok: true, data, ...hints2.length > 0 ? { hints: hints2 } : {} });
96109
96339
  return data;
96110
96340
  } catch (err) {
@@ -97262,7 +97492,7 @@ Example: baker videos describe j571abc123 --description "Customer explaining how
97262
97492
  import { constants } from "fs";
97263
97493
  import { copyFile as copyFile2, mkdir as mkdir17, mkdtemp as mkdtemp3, rm as rm10 } from "fs/promises";
97264
97494
  import { tmpdir as tmpdir4 } from "os";
97265
- import path57 from "path";
97495
+ import path56 from "path";
97266
97496
  import { defineCommand as defineCommand260 } from "citty";
97267
97497
 
97268
97498
  // src/commands/videos/videoPlatforms.ts
@@ -97430,8 +97660,8 @@ var fetchCommand2 = defineCommand260({
97430
97660
  }
97431
97661
  });
97432
97662
  async function fetchVideo(args8) {
97433
- const outDir = path57.resolve(args8.out);
97434
- const workDir = await mkdtemp3(path57.join(tmpdir4(), "videos-fetch-"));
97663
+ const outDir = path56.resolve(args8.out);
97664
+ const workDir = await mkdtemp3(path56.join(tmpdir4(), "videos-fetch-"));
97435
97665
  try {
97436
97666
  await mkdir17(outDir, { recursive: true });
97437
97667
  const vimeoCookie = captureVimeoCookie();
@@ -97476,7 +97706,7 @@ function fileStem(info) {
97476
97706
  }
97477
97707
  async function placeInto(outDir, source, stem) {
97478
97708
  for (let n = 1; ; n++) {
97479
- const target2 = path57.join(outDir, `${stem}${n === 1 ? "" : `-${n}`}${path57.extname(source)}`);
97709
+ const target2 = path56.join(outDir, `${stem}${n === 1 ? "" : `-${n}`}${path56.extname(source)}`);
97480
97710
  try {
97481
97711
  await copyFile2(source, target2, constants.COPYFILE_EXCL);
97482
97712
  return target2;
@@ -97563,7 +97793,7 @@ var groupCommand2 = defineCommand262({
97563
97793
  // src/commands/videos/ingest.ts
97564
97794
  import { mkdtemp as mkdtemp4, rm as rm11, stat as stat12 } from "fs/promises";
97565
97795
  import { tmpdir as tmpdir5 } from "os";
97566
- import path58 from "path";
97796
+ import path57 from "path";
97567
97797
  import { defineCommand as defineCommand263 } from "citty";
97568
97798
 
97569
97799
  // src/lib/streamUpload.ts
@@ -97803,7 +98033,7 @@ function ingestUrl(args8) {
97803
98033
  }
97804
98034
  async function downloadThenIngest(args8, country) {
97805
98035
  const vimeoCookie = captureVimeoCookie();
97806
- const workDir = await mkdtemp4(path58.join(tmpdir5(), "videos-ingest-"));
98036
+ const workDir = await mkdtemp4(path57.join(tmpdir5(), "videos-ingest-"));
97807
98037
  try {
97808
98038
  const probe = await probeYtDlp({ url: args8.url, country, vimeoCookie, cookieDir: workDir });
97809
98039
  if (isAudioOnly(probe.info)) {
@@ -98068,7 +98298,7 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
98068
98298
  });
98069
98299
 
98070
98300
  // src/commands/winning-ads/index.ts
98071
- import { defineCommand as defineCommand279 } from "citty";
98301
+ import { defineCommand as defineCommand277 } from "citty";
98072
98302
 
98073
98303
  // src/commands/winning-ads/advertisers.ts
98074
98304
  import { defineCommand as defineCommand267 } from "citty";
@@ -98181,90 +98411,8 @@ var advertisersCommand2 = defineCommand267({
98181
98411
  }
98182
98412
  });
98183
98413
 
98184
- // src/commands/winning-ads/brief.ts
98185
- import { defineCommand as defineCommand268 } from "citty";
98186
- registerSchema({
98187
- command: "winning-ads.brief",
98188
- description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
98189
- args: {
98190
- dna: {
98191
- type: "string",
98192
- description: `Partial target DNA as JSON, e.g. '{"angle":"cost savings","awareness_stage":"solution_aware"}'`,
98193
- required: false
98194
- },
98195
- notes: { type: "string", description: "Free-text guidance for the brief", required: false },
98196
- k: {
98197
- type: "number",
98198
- description: "Number of reference ads to pull 1-50 (default: service default)",
98199
- required: false
98200
- }
98201
- }
98202
- });
98203
- function buildBriefBody(args8) {
98204
- const body = {};
98205
- if (args8.targetDna && Object.keys(args8.targetDna).length) {
98206
- body.target_dna = args8.targetDna;
98207
- }
98208
- if (args8.notes) {
98209
- body.notes = args8.notes;
98210
- }
98211
- if (args8.k !== void 0 && args8.k !== "") {
98212
- body.k_reference_ads = Number(args8.k);
98213
- }
98214
- return body;
98215
- }
98216
- function parseDna(raw) {
98217
- if (!raw) {
98218
- return void 0;
98219
- }
98220
- let parsed;
98221
- try {
98222
- parsed = JSON.parse(raw);
98223
- } catch {
98224
- throw new SyntaxError("--dna must be valid JSON");
98225
- }
98226
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
98227
- throw new SyntaxError("--dna must be a JSON object");
98228
- }
98229
- return parsed;
98230
- }
98231
- var briefCommand = defineCommand268({
98232
- meta: {
98233
- name: "brief",
98234
- description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
98235
- },
98236
- args: {
98237
- dna: { type: "string", description: "Partial target DNA as JSON", required: false },
98238
- notes: { type: "string", description: "Free-text guidance", required: false },
98239
- k: { type: "string", description: "Number of reference ads (1-50)", required: false }
98240
- },
98241
- run: async ({ args: args8 }) => {
98242
- try {
98243
- let targetDna;
98244
- try {
98245
- targetDna = parseDna(args8.dna);
98246
- } catch (err) {
98247
- writeJson({
98248
- ok: false,
98249
- error: { code: "VALIDATION_ERROR", message: err instanceof Error ? err.message : "Invalid --dna" }
98250
- });
98251
- process.exit(1);
98252
- }
98253
- const body = buildBriefBody({
98254
- targetDna,
98255
- notes: args8.notes,
98256
- k: args8.k
98257
- });
98258
- const data = await apiPost("/api/ad-library/brief", body);
98259
- writeJson({ ok: true, data });
98260
- } catch (err) {
98261
- reportError3(err);
98262
- }
98263
- }
98264
- });
98265
-
98266
98414
  // src/commands/winning-ads/content.ts
98267
- import { defineCommand as defineCommand269 } from "citty";
98415
+ import { defineCommand as defineCommand268 } from "citty";
98268
98416
  registerSchema({
98269
98417
  command: "winning-ads.content",
98270
98418
  description: "Read what's INSIDE one winning ad: the spoken transcript, the on-screen text, and the ad copy. Use this after `search`/`winners`/`feed` return a shortlist \u2014 pass an ad_id to understand a reference before reproducing it. Static and carousel ads return the text drawn into the image with the job each piece does (headline, benefit, cta\u2026), the card it sits on, and their format_pattern. Add --full for how it is built (composition, colours, CTA, markers), how it persuades (awareness, mechanism, pains, desires, proof, objections), what it offers (offer type, price, guarantee, urgency), plus speech, pacing and soundtrack for video.",
@@ -98277,7 +98425,7 @@ registerSchema({
98277
98425
  }
98278
98426
  }
98279
98427
  });
98280
- var contentCommand = defineCommand269({
98428
+ var contentCommand = defineCommand268({
98281
98429
  meta: {
98282
98430
  name: "content",
98283
98431
  description: "Read the transcript + on-screen text + copy of one winning ad; --full adds its build, persuasion and offer. Example: baker winning-ads content adg_123 --platform meta --full --output md"
@@ -98326,7 +98474,7 @@ var contentCommand = defineCommand269({
98326
98474
  });
98327
98475
 
98328
98476
  // src/commands/winning-ads/feed.ts
98329
- import { defineCommand as defineCommand270 } from "citty";
98477
+ import { defineCommand as defineCommand269 } from "citty";
98330
98478
  function buildFeedParams(input) {
98331
98479
  const params = {};
98332
98480
  const advertiser = splitList4(input.advertiser);
@@ -98378,7 +98526,7 @@ registerSchema({
98378
98526
  format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
98379
98527
  }
98380
98528
  });
98381
- var feedCommand2 = defineCommand270({
98529
+ var feedCommand2 = defineCommand269({
98382
98530
  meta: {
98383
98531
  name: "feed",
98384
98532
  description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
@@ -98463,7 +98611,7 @@ var feedCommand2 = defineCommand270({
98463
98611
  });
98464
98612
 
98465
98613
  // src/commands/winning-ads/follow.ts
98466
- import { defineCommand as defineCommand271 } from "citty";
98614
+ import { defineCommand as defineCommand270 } from "citty";
98467
98615
  var PLATFORMS = adLibraryPlatformSchema.options;
98468
98616
  registerSchema({
98469
98617
  command: "winning-ads.follow",
@@ -98478,7 +98626,7 @@ registerSchema({
98478
98626
  label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
98479
98627
  }
98480
98628
  });
98481
- var followCommand = defineCommand271({
98629
+ var followCommand = defineCommand270({
98482
98630
  meta: {
98483
98631
  name: "follow",
98484
98632
  description: 'Follow a brand to track ALL its ads \u2014 every platform and country. --platform is how we read your input, not a limit. A domain tracks every platform we can resolve. Example: baker winning-ads follow "deel.com" --platform meta'
@@ -98525,7 +98673,7 @@ var followCommand = defineCommand271({
98525
98673
  });
98526
98674
 
98527
98675
  // src/commands/winning-ads/follow-competitors.ts
98528
- import { defineCommand as defineCommand272 } from "citty";
98676
+ import { defineCommand as defineCommand271 } from "citty";
98529
98677
  var PLATFORMS2 = adLibraryPlatformSchema.options;
98530
98678
  var BATCH_TIMEOUT_MS = 3e5;
98531
98679
  function buildFollowBatchBody(input) {
@@ -98558,7 +98706,7 @@ registerSchema({
98558
98706
  }
98559
98707
  }
98560
98708
  });
98561
- var followCompetitorsCommand = defineCommand272({
98709
+ var followCompetitorsCommand = defineCommand271({
98562
98710
  meta: {
98563
98711
  name: "follow-competitors",
98564
98712
  description: 'Follow many brands at once by domain \u2014 add every competitor in one call. Example: baker winning-ads follow-competitors "deel.com,notion.so,hubspot.com"'
@@ -98633,7 +98781,7 @@ var followCompetitorsCommand = defineCommand272({
98633
98781
  });
98634
98782
 
98635
98783
  // src/commands/winning-ads/following.ts
98636
- import { defineCommand as defineCommand273 } from "citty";
98784
+ import { defineCommand as defineCommand272 } from "citty";
98637
98785
  registerSchema({
98638
98786
  command: "winning-ads.following",
98639
98787
  description: "List the brands you follow in your ad-dna library, with each one's status (ready vs still adding) and cached ad counts. A brand still adding has counts that are a lie in progress; one with `discovery_failed` has counts that are short because we couldn't finish looking, which is not the same as it running no ads.",
@@ -98687,7 +98835,7 @@ function followingNormalizer(record, full) {
98687
98835
  platforms: Array.isArray(record.platforms) ? record.platforms : []
98688
98836
  };
98689
98837
  }
98690
- var followingCommand = defineCommand273({
98838
+ var followingCommand = defineCommand272({
98691
98839
  meta: {
98692
98840
  name: "following",
98693
98841
  description: "List brands you follow, with status (ready / adding\u2026) and cached counts. A brand's counts are only final once it is ready. Example: baker winning-ads following --output md"
@@ -98722,113 +98870,18 @@ var followingCommand = defineCommand273({
98722
98870
  }
98723
98871
  });
98724
98872
 
98725
- // src/commands/winning-ads/patterns.ts
98726
- import { defineCommand as defineCommand274 } from "citty";
98727
- registerSchema({
98728
- command: "winning-ads.patterns",
98729
- description: "Mine what separates two cohorts of ads: pass a comma-list of winning ad ids (--winners) and a comma-list of weaker ad ids (--duds). Returns the discriminating DNA fields.",
98730
- args: {
98731
- winners: { type: "string", description: "Comma list of winning ad ids (cohort A)", required: true },
98732
- duds: { type: "string", description: "Comma list of weaker/dud ad ids (cohort B)", required: true },
98733
- "top-n": {
98734
- type: "number",
98735
- description: "Max discriminators to return 1-50 (default: service default)",
98736
- required: false
98737
- }
98738
- }
98739
- });
98740
- function buildPatternsBody(args8) {
98741
- const winners = splitList4(args8.winners);
98742
- const duds = splitList4(args8.duds);
98743
- if (!winners.length || !duds.length) {
98744
- throw new Error("Provide at least one ad id for both --winners and --duds");
98745
- }
98746
- const body = {
98747
- cohort_a: { ad_ids: winners },
98748
- cohort_b: { ad_ids: duds }
98749
- };
98750
- if (args8.topN !== void 0 && args8.topN !== "") {
98751
- body.top_n = Number(args8.topN);
98752
- }
98753
- return body;
98754
- }
98755
- function discriminatorRow(record) {
98756
- const dir = record.direction === "favors_a" ? "winners" : "duds";
98757
- return {
98758
- field: String(record.field ?? ""),
98759
- favors: dir,
98760
- score: record.score ?? null,
98761
- top_values_winners: Array.isArray(record.top_values_a) ? record.top_values_a.join(", ") : "",
98762
- top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
98763
- };
98764
- }
98765
- var patternsCommand = defineCommand274({
98766
- meta: {
98767
- name: "patterns",
98768
- description: "Discover what separates winning ads from weak ones. Example: baker winning-ads patterns --winners a_1,a_2,a_3 --duds a_9,a_8 --output md"
98769
- },
98770
- args: {
98771
- winners: { type: "string", description: "Comma list of winning ad ids (cohort A)", required: true },
98772
- duds: { type: "string", description: "Comma list of weaker/dud ad ids (cohort B)", required: true },
98773
- "top-n": { type: "string", description: "Max discriminators (1-50)", required: false },
98774
- output: { type: "string", description: "Output format: json|files|md", required: false, default: "json" },
98775
- fields: { type: "string", description: "Comma-separated field names to include", required: false }
98776
- },
98777
- run: async ({ args: args8 }) => {
98778
- try {
98779
- let body;
98780
- try {
98781
- body = buildPatternsBody({
98782
- winners: args8.winners,
98783
- duds: args8.duds,
98784
- topN: args8["top-n"]
98785
- });
98786
- } catch (err) {
98787
- writeJson({
98788
- ok: false,
98789
- error: { code: "VALIDATION_ERROR", message: err instanceof Error ? err.message : "Invalid cohorts" }
98790
- });
98791
- process.exit(1);
98792
- }
98793
- const data = await apiPost("/api/ad-library/patterns", body);
98794
- const output = args8.output || "json";
98795
- const rawDiscriminators = Array.isArray(data?.discriminators) ? data.discriminators : [];
98796
- if (output === "json") {
98797
- writeJson({
98798
- ok: true,
98799
- data: {
98800
- cohort_a_size: data?.cohort_a_size ?? 0,
98801
- cohort_b_size: data?.cohort_b_size ?? 0,
98802
- discriminators: rawDiscriminators.map((d) => discriminatorRow(d))
98803
- }
98804
- });
98805
- return;
98806
- }
98807
- writeOutput(
98808
- { ok: true, data: rawDiscriminators },
98809
- output,
98810
- args8.fields ? args8.fields.split(",") : void 0,
98811
- false,
98812
- (record) => discriminatorRow(record)
98813
- );
98814
- } catch (err) {
98815
- reportError3(err);
98816
- }
98817
- }
98818
- });
98819
-
98820
98873
  // src/commands/winning-ads/search.ts
98821
- import { defineCommand as defineCommand275 } from "citty";
98874
+ import { defineCommand as defineCommand273 } from "citty";
98822
98875
  registerSchema({
98823
98876
  command: "winning-ads.search",
98824
- description: "Search the ad-dna corpus of scored winning ads. Returns a lean shortlist (advertiser, summary, scores, media_url, and visual/container/mechanism when known) to pick a reference to reproduce. A value outside a closed list (--platform, --format, --winner-category, --awareness, --format-pattern) is refused with error.fix.validValues.",
98877
+ description: "Search the ad-dna corpus of scored winning ads, the company's followed brands first. Returns a lean shortlist (advertiser, summary, scores, days_active/active/winner_category as proof, media_url, and visual/container/mechanism when known) to pick a reference to reproduce. Each search is a paid reranker + judge call: one strong search, at most one adjusted retry. A value outside a closed list (--platform, --format, --winner-category, --awareness, --format-pattern, --sort) is refused with error.fix.validValues.",
98825
98878
  args: {
98826
98879
  query: { type: "string", description: "Free-text natural-language query", required: false },
98827
98880
  scope: {
98828
98881
  type: "string",
98829
- description: "Which brands to draw from: all (default, the whole shared library), followed (only brands the company follows), followed-first (followed brands first, topped up from the library when too few clear the relevance floor). Every result carries source: followed|library.",
98882
+ description: "Which brands to draw from: followed-first (default: followed brands first, topped up from the library when too few clear the relevance floor; the whole library when the company follows nobody), followed (only brands the company follows), all (the whole shared library). Every result carries source: followed|library.",
98830
98883
  required: false,
98831
- default: "all"
98884
+ default: "followed-first"
98832
98885
  },
98833
98886
  "ref-ad-id": {
98834
98887
  type: "string",
@@ -98843,6 +98896,12 @@ registerSchema({
98843
98896
  default: 3
98844
98897
  },
98845
98898
  "min-relevance": { type: "number", description: "Relevance floor 0-1; trims weak matches", required: false },
98899
+ sort: {
98900
+ type: "string",
98901
+ description: "Order of a browse with no query: winner (default, by winner score), longest_running (by days active), newest (by first seen). Ignored when a query is given: a query ranks by relevance blended with winner score.",
98902
+ required: false,
98903
+ default: "winner"
98904
+ },
98846
98905
  platform: {
98847
98906
  type: "string",
98848
98907
  description: "Comma list: meta,linkedin,tiktok",
@@ -98931,6 +98990,7 @@ function buildSearchBody2(args8) {
98931
98990
  setNumber(body, "limit", args8.limit);
98932
98991
  setNumber(body, "max_per_advertiser", args8.maxPerAdvertiser);
98933
98992
  setNumber(body, "min_relevance", args8.minRelevance);
98993
+ setString(body, "sort", args8.sort);
98934
98994
  if (args8.diverse) {
98935
98995
  body.diverse = true;
98936
98996
  }
@@ -99011,8 +99071,8 @@ var FLAG_FOR_FIELD = {
99011
99071
  first_seen_after: "--first-seen-after",
99012
99072
  first_seen_before: "--first-seen-before"
99013
99073
  };
99014
- function flagFor(path59) {
99015
- const field = String(path59[0] === "hard_filters" ? path59[1] : path59[0]);
99074
+ function flagFor(path58) {
99075
+ const field = String(path58[0] === "hard_filters" ? path58[1] : path58[0]);
99016
99076
  return FLAG_FOR_FIELD[field] ?? field;
99017
99077
  }
99018
99078
  function schemaRejection(body) {
@@ -99051,6 +99111,9 @@ function searchRejection(args8, body) {
99051
99111
  if (args8.scope !== void 0 && !winnerSearchScopeSchema.safeParse(args8.scope).success) {
99052
99112
  return { message: `Unknown --scope "${args8.scope}". Use one of: ${winnerSearchScopeSchema.options.join(", ")}.` };
99053
99113
  }
99114
+ if (args8.sort !== void 0 && !winnersSortSchema.safeParse(args8.sort).success) {
99115
+ return { message: `Unknown --sort "${args8.sort}". Use one of: ${winnersSortSchema.options.join(", ")}.` };
99116
+ }
99054
99117
  for (const [flag, key] of NUMERIC_FLAGS) {
99055
99118
  const value2 = args8[key];
99056
99119
  if (value2 !== void 0 && value2 !== "" && Number.isNaN(Number(value2))) {
@@ -99086,16 +99149,16 @@ function writeSearchResult(data, opts) {
99086
99149
  writeOutput({ ok: true, data: rawResults, ...withHints2 }, opts.output, opts.fields, opts.full, winningAdNormalizer);
99087
99150
  }
99088
99151
  var SEARCH_TIMEOUT_MS = 12e4;
99089
- var searchCommand5 = defineCommand275({
99152
+ var searchCommand5 = defineCommand273({
99090
99153
  meta: {
99091
99154
  name: "search",
99092
- description: "Search winning reference ads. Start here: baker winning-ads search 'B2B SaaS before/after AI automation' --scope followed-first --platform meta --format static --exclude-advertiser adv_123 --output md. A set of references for N concepts in one call: add --diverse --limit N. The best proven ad in one container from any category: --format-pattern comparison_table (no query needed)."
99155
+ description: "Search winning reference ads, the company's followed brands first. Start here: baker winning-ads search 'B2B SaaS before/after AI automation' --platform meta --format static --exclude-advertiser adv_123 --output md. A set of references for N concepts in one call: add --diverse --limit N. The best proven ad in one container from any category: --format-pattern comparison_table (no query needed; add --sort longest_running to order by staying power). Each search is a paid reranker + judge call: one strong search, at most one adjusted retry."
99093
99156
  },
99094
99157
  args: {
99095
99158
  query: { type: "positional", description: "Free-text search query", required: false },
99096
99159
  scope: {
99097
99160
  type: "string",
99098
- description: "all (default) | followed | followed-first. followed-first = the company's followed brands first, topped up from the whole library",
99161
+ description: "followed-first (default) | followed | all. followed-first = the company's followed brands first, topped up from the whole library (the whole library when it follows nobody)",
99099
99162
  required: false
99100
99163
  },
99101
99164
  "ref-ad-id": { type: "string", description: "Find ads similar to this ad id", required: false },
@@ -99111,6 +99174,11 @@ var searchCommand5 = defineCommand275({
99111
99174
  required: false
99112
99175
  },
99113
99176
  "min-relevance": { type: "string", description: "Relevance floor 0-1", required: false },
99177
+ sort: {
99178
+ type: "string",
99179
+ description: "Order of a no-query browse: winner (default) | longest_running | newest. Ignored when a query is given",
99180
+ required: false
99181
+ },
99114
99182
  platform: {
99115
99183
  type: "string",
99116
99184
  description: "Comma list of platforms (meta,linkedin,tiktok) \u2014 search one or several",
@@ -99157,7 +99225,7 @@ var searchCommand5 = defineCommand275({
99157
99225
  fields: { type: "string", description: "Comma-separated field names to include", required: false },
99158
99226
  full: {
99159
99227
  type: "boolean",
99160
- description: "Include DNA detail (angle, persona, hook) + longevity; mechanism is in the default output",
99228
+ description: "Include DNA detail (angle, persona, hook, awareness, industry) + reach and media kind; mechanism, days_active, active and winner_category are in the default output",
99161
99229
  required: false,
99162
99230
  default: false
99163
99231
  }
@@ -99171,6 +99239,7 @@ var searchCommand5 = defineCommand275({
99171
99239
  limit: args8.limit,
99172
99240
  maxPerAdvertiser: args8["max-per-advertiser"],
99173
99241
  minRelevance: args8["min-relevance"],
99242
+ sort: args8.sort,
99174
99243
  platform: args8.platform,
99175
99244
  format: args8.format,
99176
99245
  winnerCategory: args8["winner-category"],
@@ -99206,7 +99275,7 @@ var searchCommand5 = defineCommand275({
99206
99275
  });
99207
99276
 
99208
99277
  // src/commands/winning-ads/seeds.ts
99209
- import { defineCommand as defineCommand276 } from "citty";
99278
+ import { defineCommand as defineCommand274 } from "citty";
99210
99279
  function leanRow(r) {
99211
99280
  return {
99212
99281
  key: r.key,
@@ -99234,7 +99303,7 @@ function makeSeedCommand(opts) {
99234
99303
  limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
99235
99304
  }
99236
99305
  });
99237
- return defineCommand276({
99306
+ return defineCommand274({
99238
99307
  meta: { name: opts.name, description: opts.description },
99239
99308
  args: {
99240
99309
  platform: { type: "string", description: "Single platform to segment on", required: false },
@@ -99283,7 +99352,7 @@ var formatsCommand = makeSeedCommand({
99283
99352
  });
99284
99353
 
99285
99354
  // src/commands/winning-ads/unfollow.ts
99286
- import { defineCommand as defineCommand277 } from "citty";
99355
+ import { defineCommand as defineCommand275 } from "citty";
99287
99356
  registerSchema({
99288
99357
  command: "winning-ads.unfollow",
99289
99358
  description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
@@ -99291,7 +99360,7 @@ registerSchema({
99291
99360
  advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
99292
99361
  }
99293
99362
  });
99294
- var unfollowCommand = defineCommand277({
99363
+ var unfollowCommand = defineCommand275({
99295
99364
  meta: {
99296
99365
  name: "unfollow",
99297
99366
  description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
@@ -99312,7 +99381,7 @@ var unfollowCommand = defineCommand277({
99312
99381
  });
99313
99382
 
99314
99383
  // src/commands/winning-ads/winners.ts
99315
- import { defineCommand as defineCommand278 } from "citty";
99384
+ import { defineCommand as defineCommand276 } from "citty";
99316
99385
  registerSchema({
99317
99386
  command: "winning-ads.winners",
99318
99387
  description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
@@ -99322,7 +99391,7 @@ registerSchema({
99322
99391
  platform: { type: "string", description: "Filter to a single platform: meta|linkedin|tiktok", required: false }
99323
99392
  }
99324
99393
  });
99325
- var winnersCommand = defineCommand278({
99394
+ var winnersCommand = defineCommand276({
99326
99395
  meta: {
99327
99396
  name: "winners",
99328
99397
  description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
@@ -99372,7 +99441,7 @@ var winnersCommand = defineCommand278({
99372
99441
  });
99373
99442
 
99374
99443
  // src/commands/winning-ads/index.ts
99375
- var winningAdsCommand = defineCommand279({
99444
+ var winningAdsCommand = defineCommand277({
99376
99445
  meta: {
99377
99446
  name: "winning-ads",
99378
99447
  description: `Search the ad-dna corpus of scored "winning" ads for reference creatives to reproduce, and manage the brands your library tracks. Proxied through the Baker backend (BAKER_API_KEY) \u2014 no separate token needed.
@@ -99380,7 +99449,7 @@ var winningAdsCommand = defineCommand279({
99380
99449
  Auth: BAKER_API_KEY (bk_ or bkr_) + BAKER_API_URL (your Convex .convex.site URL).
99381
99450
 
99382
99451
  Subcommands:
99383
- baker winning-ads search "<free text>" \u2014 semantic search across the corpus; lean shortlist (advertiser, summary, scores, media_url)
99452
+ baker winning-ads search "<free text>" \u2014 semantic search, followed brands first; lean shortlist (advertiser, summary, scores, proof, media_url)
99384
99453
  baker winning-ads advertisers "<brand>" \u2014 list corpus brands \u2192 advertiser ids (for --exclude-advertiser / --advertiser-id)
99385
99454
  baker winning-ads follow "<domain|url|brand>" --platform meta|linkedin|tiktok \u2014 add a brand's ads to your library
99386
99455
  baker winning-ads follow-competitors "<domain,domain,...>" \u2014 add MANY brands at once (every competitor by domain)
@@ -99389,8 +99458,6 @@ Subcommands:
99389
99458
  baker winning-ads winners <advertiser> \u2014 top winners for one advertiser id
99390
99459
  baker winning-ads content <ad-id> \u2014 read ONE ad's transcript + on-screen text + copy (understand a reference before reproducing it)
99391
99460
  baker winning-ads unfollow <advertiser> \u2014 stop following a brand
99392
- baker winning-ads brief \u2014 creative brief grounded in similar winners
99393
- baker winning-ads patterns --winners \u2026 --duds \u2026 \u2014 what separates a winning cohort from a weak one
99394
99461
  baker winning-ads hooks \u2014 top winner-weighted HOOK seeds for a segment (feed the seed to search --hook-archetype)
99395
99462
  baker winning-ads mechanisms \u2014 top winner-weighted PRODUCT mechanism seeds (how the product claims to work)
99396
99463
  baker winning-ads formats \u2014 top winner-weighted static FORMAT patterns for a segment
@@ -99406,7 +99473,7 @@ Examples:
99406
99473
  baker winning-ads content adg_123 --full --output md
99407
99474
  baker winning-ads hooks --platform meta --awareness problem_aware --industry saas --output md
99408
99475
  baker winning-ads search "fintech onboarding" --hook-archetype callout --winner-category winner --output md
99409
- baker winning-ads patterns --winners a_1,a_2 --duds a_9,a_8 --output md
99476
+ baker winning-ads search --format-pattern comparison_table --sort longest_running --output md
99410
99477
  Full guide: __tooling__/docs/tools/baker/winning-ads.md`
99411
99478
  },
99412
99479
  subCommands: {
@@ -99419,8 +99486,6 @@ Full guide: __tooling__/docs/tools/baker/winning-ads.md`
99419
99486
  winners: winnersCommand,
99420
99487
  content: contentCommand,
99421
99488
  unfollow: unfollowCommand,
99422
- brief: briefCommand,
99423
- patterns: patternsCommand,
99424
99489
  hooks: hooksCommand,
99425
99490
  mechanisms: mechanismsCommand,
99426
99491
  formats: formatsCommand
@@ -99444,7 +99509,7 @@ function getCliVersion() {
99444
99509
  }
99445
99510
 
99446
99511
  // src/cli.ts
99447
- var main = defineCommand280({
99512
+ var main = defineCommand278({
99448
99513
  meta: {
99449
99514
  name: "baker",
99450
99515
  version: getCliVersion(),