@koda-sl/baker-cli 0.268.0-dev.1f1c09c80 → 0.270.0-dev.1f1c09c80

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -9786,11 +9786,11 @@ function rawTextEntries(value) {
9786
9786
  const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
9787
9787
  return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
9788
9788
  }
9789
- function rawFileEntries(path40) {
9790
- if (typeof path40 !== "string" || path40.length === 0) {
9789
+ function rawFileEntries(path41) {
9790
+ if (typeof path41 !== "string" || path41.length === 0) {
9791
9791
  return [];
9792
9792
  }
9793
- return readFileSync2(path40, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
9793
+ return readFileSync2(path41, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
9794
9794
  }
9795
9795
  function keywordEntries(args) {
9796
9796
  const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
@@ -9813,19 +9813,19 @@ function keywordEntries(args) {
9813
9813
  }
9814
9814
  return entries;
9815
9815
  }
9816
- function loadJsonFileArg(path40) {
9817
- if (typeof path40 !== "string" || path40.length === 0) {
9816
+ function loadJsonFileArg(path41) {
9817
+ if (typeof path41 !== "string" || path41.length === 0) {
9818
9818
  return {};
9819
9819
  }
9820
9820
  try {
9821
- const parsed = JSON.parse(readFileSync2(path40, "utf8"));
9821
+ const parsed = JSON.parse(readFileSync2(path41, "utf8"));
9822
9822
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
9823
- failWriteValidation(`${path40} must contain a JSON object`);
9823
+ failWriteValidation(`${path41} must contain a JSON object`);
9824
9824
  }
9825
9825
  return parsed;
9826
9826
  } catch (err) {
9827
9827
  if (err instanceof SyntaxError) {
9828
- failWriteValidation(`${path40} is not valid JSON: ${err.message}`);
9828
+ failWriteValidation(`${path41} is not valid JSON: ${err.message}`);
9829
9829
  }
9830
9830
  throw err;
9831
9831
  }
@@ -9955,10 +9955,10 @@ async function stageUpdate(kind, customerId, target, payload, hints) {
9955
9955
  async function stageTarget(kind, customerId, target, hints) {
9956
9956
  await stageGoogleOp({ kind, customerId, target }, hints);
9957
9957
  }
9958
- async function draftAction(path40, body, chat) {
9958
+ async function draftAction(path41, body, chat) {
9959
9959
  try {
9960
9960
  const chatId = resolveChatId(chat);
9961
- const response = await apiPost(path40, { chatId, ...body });
9961
+ const response = await apiPost(path41, { chatId, ...body });
9962
9962
  writeJsonEnvelope(response);
9963
9963
  } catch (err) {
9964
9964
  handleGoogleError(err);
@@ -15172,19 +15172,19 @@ function failWriteValidation2(message) {
15172
15172
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
15173
15173
  process.exit(1);
15174
15174
  }
15175
- function loadJsonFileArg2(path40) {
15176
- if (typeof path40 !== "string" || path40.length === 0) {
15175
+ function loadJsonFileArg2(path41) {
15176
+ if (typeof path41 !== "string" || path41.length === 0) {
15177
15177
  return {};
15178
15178
  }
15179
15179
  try {
15180
- const parsed = JSON.parse(readFileSync4(path40, "utf8"));
15180
+ const parsed = JSON.parse(readFileSync4(path41, "utf8"));
15181
15181
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
15182
- failWriteValidation2(`${path40} must contain a JSON object`);
15182
+ failWriteValidation2(`${path41} must contain a JSON object`);
15183
15183
  }
15184
15184
  return parsed;
15185
15185
  } catch (err) {
15186
15186
  if (err instanceof SyntaxError) {
15187
- failWriteValidation2(`${path40} is not valid JSON: ${err.message}`);
15187
+ failWriteValidation2(`${path41} is not valid JSON: ${err.message}`);
15188
15188
  }
15189
15189
  throw err;
15190
15190
  }
@@ -15269,15 +15269,15 @@ function parseLocaleFlag(value) {
15269
15269
  }
15270
15270
  return { language: match[1], country: match[2].toUpperCase() };
15271
15271
  }
15272
- function loadTargetingFileArg(path40) {
15273
- if (typeof path40 !== "string" || path40.length === 0) {
15272
+ function loadTargetingFileArg(path41) {
15273
+ if (typeof path41 !== "string" || path41.length === 0) {
15274
15274
  return void 0;
15275
15275
  }
15276
- const parsed = loadJsonFileArg2(path40);
15276
+ const parsed = loadJsonFileArg2(path41);
15277
15277
  const criteria = parsed.targetingCriteria ?? parsed;
15278
15278
  if (!criteria.include) {
15279
15279
  failWriteValidation2(
15280
- `${path40} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
15280
+ `${path41} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
15281
15281
  );
15282
15282
  }
15283
15283
  return criteria;
@@ -15312,14 +15312,14 @@ function parseCsvLine(line) {
15312
15312
  cells.push(current);
15313
15313
  return cells.map((cell2) => cell2.trim());
15314
15314
  }
15315
- function parseListFileArg(path40, maxRows) {
15316
- if (typeof path40 !== "string" || path40.length === 0) {
15315
+ function parseListFileArg(path41, maxRows) {
15316
+ if (typeof path41 !== "string" || path41.length === 0) {
15317
15317
  return void 0;
15318
15318
  }
15319
- const raw = readFileSync4(path40, "utf8");
15319
+ const raw = readFileSync4(path41, "utf8");
15320
15320
  const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
15321
15321
  if (lines.length < 2) {
15322
- failWriteValidation2(`${path40} needs a header row and at least one data row`);
15322
+ failWriteValidation2(`${path41} needs a header row and at least one data row`);
15323
15323
  }
15324
15324
  const columns = parseCsvLine(lines[0]).map((column) => column.trim());
15325
15325
  const rows = [];
@@ -15338,7 +15338,7 @@ function parseListFileArg(path40, maxRows) {
15338
15338
  }
15339
15339
  }
15340
15340
  if (rows.length > maxRows) {
15341
- failWriteValidation2(`${path40} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
15341
+ failWriteValidation2(`${path41} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
15342
15342
  }
15343
15343
  return { columns, rows };
15344
15344
  }
@@ -15434,11 +15434,11 @@ function readPositionals(args) {
15434
15434
  function splitIdList(raw) {
15435
15435
  return raw.split(",").map((id) => id.trim()).filter(Boolean);
15436
15436
  }
15437
- function idsFileEntries(path40) {
15438
- if (typeof path40 !== "string" || path40.length === 0) {
15437
+ function idsFileEntries(path41) {
15438
+ if (typeof path41 !== "string" || path41.length === 0) {
15439
15439
  return [];
15440
15440
  }
15441
- return readFileSync4(path40, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
15441
+ return readFileSync4(path41, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
15442
15442
  }
15443
15443
  function requireTargets(args, entity) {
15444
15444
  const positionals = readPositionals(args);
@@ -18059,9 +18059,9 @@ function compactRow(row) {
18059
18059
  ...destination.postUrn ? { postUrn: destination.postUrn } : {}
18060
18060
  };
18061
18061
  }
18062
- function readPath(row, path40) {
18062
+ function readPath(row, path41) {
18063
18063
  let current = row;
18064
- for (const segment of path40.split(".")) {
18064
+ for (const segment of path41.split(".")) {
18065
18065
  const record = asRecord2(current);
18066
18066
  if (!record) return void 0;
18067
18067
  current = record[segment];
@@ -18071,10 +18071,10 @@ function readPath(row, path40) {
18071
18071
  function projectFields(rows, paths) {
18072
18072
  return rows.map((row) => {
18073
18073
  const projected = {};
18074
- for (const path40 of paths) {
18075
- const value = readPath(row, path40);
18074
+ for (const path41 of paths) {
18075
+ const value = readPath(row, path41);
18076
18076
  if (value !== void 0) {
18077
- projected[path40] = value;
18077
+ projected[path41] = value;
18078
18078
  }
18079
18079
  }
18080
18080
  return projected;
@@ -19374,11 +19374,11 @@ var updateStatusSchema = z25.enum(UPDATE_STATUSES);
19374
19374
  function currencyMinimums2(currencyCode) {
19375
19375
  return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
19376
19376
  }
19377
- function validateDailyBudgetFloor(money, ctx, path40) {
19377
+ function validateDailyBudgetFloor(money, ctx, path41) {
19378
19378
  if (money?.currencyCode) {
19379
19379
  const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
19380
19380
  if (Number(money.amount) < min) {
19381
- ctx.addIssue({ code: "custom", path: path40, message: `below the ${min} ${money.currencyCode} daily minimum` });
19381
+ ctx.addIssue({ code: "custom", path: path41, message: `below the ${min} ${money.currencyCode} daily minimum` });
19382
19382
  }
19383
19383
  }
19384
19384
  }
@@ -20047,19 +20047,19 @@ function failWriteValidation3(message) {
20047
20047
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
20048
20048
  process.exit(1);
20049
20049
  }
20050
- function loadJsonFileArg3(path40) {
20051
- if (typeof path40 !== "string" || path40.length === 0) {
20050
+ function loadJsonFileArg3(path41) {
20051
+ if (typeof path41 !== "string" || path41.length === 0) {
20052
20052
  return {};
20053
20053
  }
20054
20054
  try {
20055
- const parsed = JSON.parse(readFileSync8(path40, "utf8"));
20055
+ const parsed = JSON.parse(readFileSync8(path41, "utf8"));
20056
20056
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
20057
- failWriteValidation3(`${path40} must contain a JSON object`);
20057
+ failWriteValidation3(`${path41} must contain a JSON object`);
20058
20058
  }
20059
20059
  return parsed;
20060
20060
  } catch (err) {
20061
20061
  if (err instanceof SyntaxError) {
20062
- failWriteValidation3(`${path40} is not valid JSON: ${err.message}`);
20062
+ failWriteValidation3(`${path41} is not valid JSON: ${err.message}`);
20063
20063
  }
20064
20064
  throw err;
20065
20065
  }
@@ -24452,8 +24452,8 @@ import { defineCommand as defineCommand94 } from "citty";
24452
24452
  import { defineCommand as defineCommand89 } from "citty";
24453
24453
 
24454
24454
  // src/commands/avatars/casting.ts
24455
- var CAST_FLAG_RULE = "Cast with `--avatar <handle>` on `baker studio generate` and `baker studio animate`. Do NOT hand-roll it by pasting `subjectDescription` into the prompt or by passing the sheet through `--reference` \u2014 both render something that merely resembles them. `--avatar` is also the ONLY thing that carries their pinned VOICE and how they speak into a clip; without it the video model invents a different voice, and no later step can put theirs back.";
24456
- var VERBATIM_RULE = "When you do write the description yourself \u2014 a canvas node, a landing image, anywhere `--avatar` does not exist \u2014 copy `subjectDescription` VERBATIM, word for word. Re-phrasing it per generation is the other reason a face drifts across a set.";
24455
+ var CAST_FLAG_RULE = "Cast with `--avatar <handle>` on `baker studio generate`, `baker studio animate` and `baker canvas scaffold-ad`. Do NOT hand-roll it by pasting `subjectDescription` into the prompt or by passing the sheet through `--reference` \u2014 both render something that merely resembles them. `--avatar` is also the ONLY thing that carries their pinned VOICE and how they speak into a clip; without it the video model invents a different voice, and no later step can put theirs back.";
24456
+ var VERBATIM_RULE = "When you do write the description yourself \u2014 a hand-built canvas node, a landing image, anywhere `--avatar` does not exist \u2014 copy `subjectDescription` VERBATIM, word for word. Re-phrasing it per generation is the other reason a face drifts across a set.";
24457
24457
  var VIDEO_ROUTING = "In video, a photoreal presenter renders on `google/veo-3.1` (or `google/veo-3.1-fast`), never `bytedance/seedance-2.0` \u2014 it refuses photoreal human faces, AI-generated ones included. Scaffolding a video creative: `baker canvas scaffold-video \u2026 --real-face`.";
24458
24458
  function castingHints(avatar) {
24459
24459
  if (avatar.status === "generating") {
@@ -24668,11 +24668,11 @@ function unwrap(response) {
24668
24668
  }
24669
24669
  return response.data;
24670
24670
  }
24671
- async function readAvatars(path40, params) {
24672
- return unwrap(await apiGet(path40, params));
24671
+ async function readAvatars(path41, params) {
24672
+ return unwrap(await apiGet(path41, params));
24673
24673
  }
24674
- async function writeAvatars(path40, body) {
24675
- return unwrap(await apiPost(path40, body));
24674
+ async function writeAvatars(path41, body) {
24675
+ return unwrap(await apiPost(path41, body));
24676
24676
  }
24677
24677
 
24678
24678
  // src/commands/avatars/create.ts
@@ -25516,12 +25516,12 @@ function missingFontFiles(urls, available) {
25516
25516
  function planFontAdoption(sources, families) {
25517
25517
  const wanted = new Map(families.map((family) => [normalizeFamily(family), family]));
25518
25518
  const byFamily = /* @__PURE__ */ new Map();
25519
- for (const { path: path40, source } of sources) {
25520
- const dir = posix.dirname(path40);
25519
+ for (const { path: path41, source } of sources) {
25520
+ const dir = posix.dirname(path41);
25521
25521
  for (const face of declaredFontFaces(source)) {
25522
25522
  if (!wanted.has(face.family)) continue;
25523
25523
  const perFile = byFamily.get(face.family) ?? /* @__PURE__ */ new Map();
25524
- perFile.set(path40, [...perFile.get(path40) ?? [], rebaseFontFaceSrc(face.block, dir)]);
25524
+ perFile.set(path41, [...perFile.get(path41) ?? [], rebaseFontFaceSrc(face.block, dir)]);
25525
25525
  byFamily.set(face.family, perFile);
25526
25526
  }
25527
25527
  }
@@ -30515,10 +30515,10 @@ function runDirsToPrune(entries, keep, currentRunId) {
30515
30515
  return runs.slice(0, Math.max(0, runs.length - keep));
30516
30516
  }
30517
30517
  async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
30518
- const { readdir: readdir10 } = await import("fs/promises");
30518
+ const { readdir: readdir11 } = await import("fs/promises");
30519
30519
  let entries;
30520
30520
  try {
30521
- entries = await readdir10(outputsDir);
30521
+ entries = await readdir11(outputsDir);
30522
30522
  } catch {
30523
30523
  return;
30524
30524
  }
@@ -32436,8 +32436,8 @@ var scaffoldStaticAdCommand = defineCommand103({
32436
32436
  });
32437
32437
 
32438
32438
  // src/commands/canvas/scaffold-ad.ts
32439
- import { copyFile, cp, mkdir as mkdir7, readFile as readFile15, writeFile as writeFile9 } from "fs/promises";
32440
- import path23 from "path";
32439
+ import { copyFile, cp, mkdir as mkdir7, readFile as readFile16, writeFile as writeFile9 } from "fs/promises";
32440
+ import path24 from "path";
32441
32441
  import { defineCommand as defineCommand104 } from "citty";
32442
32442
 
32443
32443
  // src/engine/scaffold/ad-spec.ts
@@ -32841,20 +32841,69 @@ function injectAdBrandOverlay(indexHtml, overlayHtml) {
32841
32841
  ${withCss.slice(rootClose)}`;
32842
32842
  }
32843
32843
 
32844
+ // src/engine/scaffold/ad-brand-source.ts
32845
+ import { readdir as readdir7, readFile as readFile15 } from "fs/promises";
32846
+ import path22 from "path";
32847
+ var BRAND_DIR = "src/brand";
32848
+ function paletteFromBrandDoc(markdown) {
32849
+ const seen = /* @__PURE__ */ new Set();
32850
+ const out = [];
32851
+ for (const match of markdown.matchAll(/#([0-9a-fA-F]{6})\b/g)) {
32852
+ const hex = `#${match[1].toLowerCase()}`;
32853
+ if (seen.has(hex)) continue;
32854
+ seen.add(hex);
32855
+ const r = Number.parseInt(hex.slice(1, 3), 16);
32856
+ const g = Number.parseInt(hex.slice(3, 5), 16);
32857
+ const b = Number.parseInt(hex.slice(5, 7), 16);
32858
+ const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;
32859
+ if (luma > 236 || luma < 14) continue;
32860
+ out.push(hex);
32861
+ }
32862
+ return out;
32863
+ }
32864
+ function pickBrandMark(files) {
32865
+ const usable = files.filter((f) => /\.(svg|png|webp)$/i.test(f));
32866
+ if (usable.length === 0) return null;
32867
+ const score = (f) => {
32868
+ const name = f.toLowerCase();
32869
+ let s = 0;
32870
+ if (name.endsWith(".svg")) s += 4;
32871
+ if (/\b(logo|wordmark|lockup)\b/.test(name)) s += 2;
32872
+ if (/\b(mono|white|black|inverse|icon|favicon|mark-only|isotipo)\b/.test(name)) s -= 3;
32873
+ return s;
32874
+ };
32875
+ return [...usable].sort((a, b) => score(b) - score(a) || a.localeCompare(b))[0] ?? null;
32876
+ }
32877
+ async function readBrandFromWorkspace(root = ".") {
32878
+ const found = {};
32879
+ const doc = await readFile15(path22.join(root, BRAND_DIR, "BRAND.md"), "utf-8").catch(() => null);
32880
+ if (doc) {
32881
+ const palette = paletteFromBrandDoc(doc);
32882
+ if (palette.length > 0) found.palette = palette;
32883
+ }
32884
+ const dir = path22.join(root, BRAND_DIR, "logos");
32885
+ const files = await readdir7(dir).catch(() => null);
32886
+ if (files) {
32887
+ const mark = pickBrandMark(files);
32888
+ if (mark) found.logo = path22.join(BRAND_DIR, "logos", mark);
32889
+ }
32890
+ return found;
32891
+ }
32892
+
32844
32893
  // src/commands/canvas/composition-path.ts
32845
32894
  import { existsSync as existsSync4 } from "fs";
32846
- import path22 from "path";
32895
+ import path23 from "path";
32847
32896
  function resolveShippedCanvasDir(name, startDir, exists = existsSync4, maxDepth = 8) {
32848
- const rel = path22.join("canvas", name);
32897
+ const rel = path23.join("canvas", name);
32849
32898
  let dir = startDir;
32850
32899
  for (let i = 0; i < maxDepth; i++) {
32851
- const candidate = path22.join(dir, rel);
32852
- if (exists(path22.join(candidate, "meta.json"))) return candidate;
32853
- const parent = path22.dirname(dir);
32900
+ const candidate = path23.join(dir, rel);
32901
+ if (exists(path23.join(candidate, "meta.json"))) return candidate;
32902
+ const parent = path23.dirname(dir);
32854
32903
  if (parent === dir) break;
32855
32904
  dir = parent;
32856
32905
  }
32857
- return path22.resolve(startDir, "../../../", rel);
32906
+ return path23.resolve(startDir, "../../../", rel);
32858
32907
  }
32859
32908
 
32860
32909
  // src/commands/canvas/scaffold-ad.ts
@@ -32870,6 +32919,11 @@ registerSchema({
32870
32919
  required: true,
32871
32920
  description: 'Path to the ad spec JSON. Shape: { format?, market?, brand?, cast?, end_card?, voice?, music?, beats: [{ say, show, on_camera?, cast? }] }. Fill `brand` from src/brand/BRAND.md \u2014 `palette` (its hex tokens, most important first) and `logo` (the repo path to the mark) are what make the ad look like the client rather than like stock. `market` is where the ad is SET; omitted, it is inferred from the voice language, and getting it wrong is what fills a Spanish ad with British houses. `say` is ONE clause ending in its own punctuation \u2014 it becomes a caption card verbatim, so two sentences in one beat produce a card holding both. `show` is the shot brief for that line. Set `on_camera` ONLY when that beat\'s subject talks to camera. `cast` is WHO the ad is about, and an ad with people in it needs one: `{ "avatar": "marta" }` names a cast avatar (`baker avatars list`) and every beat is grounded on that avatar\'s identity sheet with its subject description copied verbatim \u2014 the same face here as in the rest of the company\'s work. Without it each beat invents its own stranger, which is how one 28-second ad came back with five different men playing one customer. `{ "description": "..." }` is the fallback when there is no avatar, and it scaffolds a canvas that asks you to drop a photo before it can run \u2014 so prefer the avatar. A beat sets `cast: false` for a shot they are not in. `end_card` is on by default whenever `brand` is set: the last beat becomes a flat brand plate with the mark and a call to action drawn over it. `{ "cta": "..." }` sets the button copy, `false` keeps the footage.'
32872
32921
  },
32922
+ avatar: {
32923
+ type: "string",
32924
+ required: false,
32925
+ description: "Cast this avatar as the ad's recurring person \u2014 the same flag, the same word and the same meaning as on `baker studio generate` and `baker studio animate`. Every beat they appear in is grounded on their identity sheet and their subject description is copied verbatim, so it is one face throughout and the same face as the rest of the company's work. Overrides `cast.avatar` in the spec. `baker avatars list` shows who there is."
32926
+ },
32873
32927
  slug: {
32874
32928
  type: "string",
32875
32929
  required: false,
@@ -32889,6 +32943,7 @@ var scaffoldAdCommand = defineCommand104({
32889
32943
  },
32890
32944
  args: {
32891
32945
  spec: { type: "string", required: true, description: "Path to the ad spec JSON" },
32946
+ avatar: { type: "string", required: false, description: "Cast this avatar as the ad's recurring person \u2014 the same flag, the same word and the same meaning as on `baker studio generate` and `baker studio animate`. Every beat they appear in is grounded on their identity sheet and their subject description is copied verbatim, so it is one face throughout and the same face as the rest of the company's work. Overrides `cast.avatar` in the spec. `baker avatars list` shows who there is." },
32892
32947
  slug: {
32893
32948
  type: "string",
32894
32949
  required: false,
@@ -32900,7 +32955,7 @@ var scaffoldAdCommand = defineCommand104({
32900
32955
  const specPath = args.spec;
32901
32956
  let parsed;
32902
32957
  try {
32903
- parsed = JSON.parse(await readFile15(specPath, "utf-8"));
32958
+ parsed = JSON.parse(await readFile16(specPath, "utf-8"));
32904
32959
  } catch (e) {
32905
32960
  writeJson({
32906
32961
  ok: false,
@@ -32926,7 +32981,11 @@ var scaffoldAdCommand = defineCommand104({
32926
32981
  process.exit(1);
32927
32982
  return;
32928
32983
  }
32929
- const handle = spec.data.cast?.avatar?.trim();
32984
+ const flagAvatar = args.avatar?.trim();
32985
+ if (flagAvatar) {
32986
+ spec.data.cast = { ...spec.data.cast, avatar: flagAvatar };
32987
+ }
32988
+ const handle = flagAvatar || spec.data.cast?.avatar?.trim();
32930
32989
  let avatar = null;
32931
32990
  let avatarError = null;
32932
32991
  if (handle) {
@@ -32946,27 +33005,37 @@ var scaffoldAdCommand = defineCommand104({
32946
33005
  avatarError = `Could not read avatar \`${handle}\`: ${e instanceof Error ? e.message : String(e)}. \`baker avatars list\` shows who there is.`;
32947
33006
  }
32948
33007
  }
33008
+ const fromWorkspace = await readBrandFromWorkspace();
33009
+ const filledLogo = !spec.data.brand?.logo && fromWorkspace.logo ? fromWorkspace.logo : void 0;
33010
+ const filledPalette = !spec.data.brand?.palette?.length && fromWorkspace.palette?.length ? fromWorkspace.palette : void 0;
33011
+ if (filledLogo || filledPalette) {
33012
+ spec.data.brand = {
33013
+ ...spec.data.brand,
33014
+ ...filledLogo ? { logo: filledLogo } : {},
33015
+ ...filledPalette ? { palette: filledPalette } : {}
33016
+ };
33017
+ }
32949
33018
  const blueprint = adSpecToBlueprint(spec.data);
32950
- const slug = args.slug ?? path23.basename(specPath).replace(/\.[^.]+$/, "");
32951
- const outPath = args.out ?? (args.slug ? path23.join("src/creatives", slug, `${slug}.canvas.json`) : path23.join(path23.dirname(specPath), `${slug}.canvas.json`));
32952
- const outDir = path23.dirname(outPath);
33019
+ const slug = args.slug ?? path24.basename(specPath).replace(/\.[^.]+$/, "");
33020
+ const outPath = args.out ?? (args.slug ? path24.join("src/creatives", slug, `${slug}.canvas.json`) : path24.join(path24.dirname(specPath), `${slug}.canvas.json`));
33021
+ const outDir = path24.dirname(outPath);
32953
33022
  await mkdir7(outDir, { recursive: true });
32954
- const compositionDest = path23.join(outDir, "video-overlay-composition");
32955
- const captionsDest = path23.join(outDir, "tiktok-captions-composition");
33023
+ const compositionDest = path24.join(outDir, "video-overlay-composition");
33024
+ const captionsDest = path24.join(outDir, "tiktok-captions-composition");
32956
33025
  await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
32957
33026
  await cp(SHIPPED_CAPTIONS_DIR, captionsDest, { recursive: true });
32958
- const blueprintPath = path23.join(outDir, "prompt.json");
32959
- const blueprintStylePath = path23.join(outDir, "prompt.style.json");
33027
+ const blueprintPath = path24.join(outDir, "prompt.json");
33028
+ const blueprintStylePath = path24.join(outDir, "prompt.style.json");
32960
33029
  await writeSceneFiles(outDir, blueprint);
32961
33030
  await writeFile9(blueprintStylePath, renderStyleProjectionFromValue(blueprint), "utf8");
32962
33031
  const logoPath = spec.data.brand?.logo?.trim();
32963
33032
  const opts = {
32964
33033
  imageModel: AD_IMAGE_MODEL,
32965
33034
  videoModel: DEFAULT_VIDEO_GENERATE_MODEL,
32966
- overlayCompositionPath: path23.relative(outDir, compositionDest),
32967
- captionsCompositionPath: path23.relative(outDir, captionsDest),
32968
- blueprintPath: path23.relative(outDir, blueprintPath),
32969
- blueprintStylePath: path23.relative(outDir, blueprintStylePath),
33035
+ overlayCompositionPath: path24.relative(outDir, compositionDest),
33036
+ captionsCompositionPath: path24.relative(outDir, captionsDest),
33037
+ blueprintPath: path24.relative(outDir, blueprintPath),
33038
+ blueprintStylePath: path24.relative(outDir, blueprintStylePath),
32970
33039
  aspect: spec.data.format.aspect_ratio,
32971
33040
  resolution: spec.data.format.resolution,
32972
33041
  // A beat is one clause and one card, so the cap has to clear a whole clause.
@@ -32980,12 +33049,12 @@ var scaffoldAdCommand = defineCommand104({
32980
33049
  };
32981
33050
  const canvas = scaffoldVideoCanvas(blueprint, adSpecCastElements(spec.data, avatar), opts);
32982
33051
  const renderNode3 = canvas.nodes.find((n) => n.type === "hyperframe_render");
32983
- const renderedDir = renderNode3 ? path23.join(outDir, String(renderNode3.params?.composition ?? "")) : null;
33052
+ const renderedDir = renderNode3 ? path24.join(outDir, String(renderNode3.params?.composition ?? "")) : null;
32984
33053
  let staged = null;
32985
33054
  if (logoPath && renderedDir) {
32986
- const ext = path23.extname(logoPath) || ".png";
33055
+ const ext = path24.extname(logoPath) || ".png";
32987
33056
  try {
32988
- await copyFile(logoPath, path23.join(renderedDir, `brand-mark${ext}`));
33057
+ await copyFile(logoPath, path24.join(renderedDir, `brand-mark${ext}`));
32989
33058
  staged = { file: `brand-mark${ext}`, alt: spec.data.brand?.name ?? "brand" };
32990
33059
  } catch {
32991
33060
  }
@@ -33000,8 +33069,8 @@ var scaffoldAdCommand = defineCommand104({
33000
33069
  accent: firstHex(spec.data.brand?.palette)
33001
33070
  }) : "";
33002
33071
  if (overlayHtml && renderedDir) {
33003
- const indexPath = path23.join(renderedDir, "index.html");
33004
- const html = await readFile15(indexPath, "utf-8");
33072
+ const indexPath = path24.join(renderedDir, "index.html");
33073
+ const html = await readFile16(indexPath, "utf-8");
33005
33074
  await writeFile9(indexPath, injectAdBrandOverlay(html, overlayHtml), "utf-8");
33006
33075
  }
33007
33076
  await writeFile9(outPath, `${JSON.stringify(canvas, null, 2)}
@@ -33015,6 +33084,9 @@ var scaffoldAdCommand = defineCommand104({
33015
33084
  ...logoPath && !staged ? [`Could not read the logo at \`${logoPath}\` \u2014 the ad has no mark on screen. Give the path as it appears in src/brand/BRAND.md, relative to the workspace root.`] : [],
33016
33085
  ...staged ? ["The brand mark is drawn by the composition \u2014 top-left throughout, and large on the closing card. It is never generated as a picture, so it cannot come back garbled."] : [],
33017
33086
  ...endCardWanted(spec.data) ? ["The last beat closes on a flat brand plate with the call to action over it, instead of one more photograph."] : [],
33087
+ ...filledLogo || filledPalette ? [
33088
+ `The spec left the brand empty, so it was read from the workspace: ${[filledLogo ? `logo \`${filledLogo}\`` : null, filledPalette ? `palette ${filledPalette.slice(0, 3).join(", ")}` : null].filter(Boolean).join(" and ")}. Pass them yourself when the ad needs a different mark or colour.`
33089
+ ] : [],
33018
33090
  ...avatarError ? [avatarError] : [],
33019
33091
  ...avatar ? [
33020
33092
  `Every beat the customer appears in is grounded on \`${handle}\`'s identity sheet, and their subject description was copied into the frames verbatim \u2014 so it is the same face throughout and the same face as the rest of this company's work.`
@@ -33044,9 +33116,9 @@ var scaffoldAdCommand = defineCommand104({
33044
33116
  });
33045
33117
 
33046
33118
  // src/commands/canvas/scaffold-video.ts
33047
- import { access as access2, cp as cp2, mkdir as mkdir8, readFile as readFile18, rm as rm7, writeFile as writeFile10 } from "fs/promises";
33119
+ import { access as access2, cp as cp2, mkdir as mkdir8, readFile as readFile19, rm as rm7, writeFile as writeFile10 } from "fs/promises";
33048
33120
  import { tmpdir as tmpdir3 } from "os";
33049
- import path25 from "path";
33121
+ import path26 from "path";
33050
33122
  import { defineCommand as defineCommand105 } from "citty";
33051
33123
 
33052
33124
  // src/engine/scaffold/lib/model-router.ts
@@ -33086,7 +33158,7 @@ function routeVideoModel(input) {
33086
33158
 
33087
33159
  // src/engine/nodes/local/lib/sceneDetect.ts
33088
33160
  import { execFile as execFile3 } from "child_process";
33089
- import { mkdtemp as mkdtemp2, readdir as readdir7, readFile as readFile16, rm as rm6 } from "fs/promises";
33161
+ import { mkdtemp as mkdtemp2, readdir as readdir8, readFile as readFile17, rm as rm6 } from "fs/promises";
33090
33162
  import { tmpdir as tmpdir2 } from "os";
33091
33163
  import { join as join2 } from "path";
33092
33164
  import { promisify as promisify3 } from "util";
@@ -33160,9 +33232,9 @@ async function runSceneDetectOnce(filePath, threshold, minSceneLenS, timeoutMs)
33160
33232
  ],
33161
33233
  { encoding: "utf-8", maxBuffer: 32 * 1024 * 1024, timeout: timeoutMs }
33162
33234
  );
33163
- const csvName = (await readdir7(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
33235
+ const csvName = (await readdir8(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
33164
33236
  if (!csvName) return [];
33165
- return parsePySceneDetectCsvCuts(await readFile16(join2(outDir, csvName), "utf-8"));
33237
+ return parsePySceneDetectCsvCuts(await readFile17(join2(outDir, csvName), "utf-8"));
33166
33238
  } finally {
33167
33239
  await rm6(outDir, { recursive: true, force: true });
33168
33240
  }
@@ -33186,8 +33258,8 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
33186
33258
  }
33187
33259
 
33188
33260
  // src/commands/canvas/gitignore.ts
33189
- import { appendFile, readFile as readFile17 } from "fs/promises";
33190
- import path24 from "path";
33261
+ import { appendFile, readFile as readFile18 } from "fs/promises";
33262
+ import path25 from "path";
33191
33263
  function missingGitignoreEntries(existing, entries) {
33192
33264
  const present2 = new Set(
33193
33265
  existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
@@ -33195,10 +33267,10 @@ function missingGitignoreEntries(existing, entries) {
33195
33267
  return entries.filter((e) => !present2.has(e.trim().replace(/\/+$/, "")));
33196
33268
  }
33197
33269
  async function ensureGitignore(dir, entries) {
33198
- const file = path24.join(dir, ".gitignore");
33270
+ const file = path25.join(dir, ".gitignore");
33199
33271
  let existing;
33200
33272
  try {
33201
- existing = await readFile17(file, "utf8");
33273
+ existing = await readFile18(file, "utf8");
33202
33274
  } catch {
33203
33275
  return;
33204
33276
  }
@@ -33237,7 +33309,7 @@ ONE PERSON, MULTIPLE LOOKS: if a single individual plays MULTIPLE personas or wa
33237
33309
  For each kept element return: { "type": one of person|animal|product|logo|badge|location, "label": a short UPPER_SNAKE_CASE name (e.g. HERO, CREATOR_SKEPTIC, INSURANCE_CARD, LOGO), "description": a concrete reusable description to source/shoot the real asset \u2014 for a person/animal give a NEUTRAL castable role (e.g. "hero pet-owner, woman in her 30s" or "a small beagle"), NOT the original individual's literal face/identity: we RECAST with a FRESH person/animal, so never tell the agent to reuse the original. "expression": a living subject's typical expression or null, "cast_id": the global.cast id if it maps to one else null, "same_as": the label of another element this is the SAME individual as (different wardrobe/persona) else null, "scenes": the 0-based indices of ONLY the scenes where the element is ACTUALLY VISIBLE ON SCREEN \u2014 judged from that scene's start_frame_prompt / end_frame_prompt subjects and its action_detail, NOT from who is merely speaking. A narrator heard over b-roll is NOT present in that b-roll scene; a dog-running cutaway does NOT contain the couch creator just because she talks across it. Do NOT pad the list \u2014 an element wrongly listed in a scene makes the reproduction render the wrong subject there (e.g. the creator appearing in a pure-dog b-roll). When in doubt, leave a scene OUT. Output ONLY the JSON object.`;
33238
33310
  async function loadAssetText2(ref, label) {
33239
33311
  const r = ref;
33240
- if (typeof r?.path === "string") return readFile18(r.path, "utf8");
33312
+ if (typeof r?.path === "string") return readFile19(r.path, "utf8");
33241
33313
  if (typeof r?.url === "string") {
33242
33314
  const res = await fetch(r.url);
33243
33315
  if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
@@ -33256,7 +33328,7 @@ async function loadTranscriptBestEffort(ref) {
33256
33328
  async function stageCaptions(outDir, transcript) {
33257
33329
  const text2 = transcript?.trim();
33258
33330
  if (!text2 || text2 === "[]") return {};
33259
- const compositionPath = path25.join(outDir, "tiktok-captions-composition");
33331
+ const compositionPath = path26.join(outDir, "tiktok-captions-composition");
33260
33332
  await cp2(SHIPPED_CAPTIONS_DIR2, compositionPath, { recursive: true });
33261
33333
  return { compositionPath };
33262
33334
  }
@@ -33274,11 +33346,11 @@ function patchCompositionHtml(html, dims) {
33274
33346
  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`);
33275
33347
  }
33276
33348
  async function stampCompositionDims(compositionDir, dims) {
33277
- const metaPath = path25.join(compositionDir, "meta.json");
33278
- const rawMeta = await readFile18(metaPath, "utf8");
33349
+ const metaPath = path26.join(compositionDir, "meta.json");
33350
+ const rawMeta = await readFile19(metaPath, "utf8");
33279
33351
  await writeFile10(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
33280
- const htmlPath = path25.join(compositionDir, "index.html");
33281
- const rawHtml = await readFile18(htmlPath, "utf8");
33352
+ const htmlPath = path26.join(compositionDir, "index.html");
33353
+ const rawHtml = await readFile19(htmlPath, "utf8");
33282
33354
  await writeFile10(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
33283
33355
  }
33284
33356
  function parseElements2(raw) {
@@ -33326,7 +33398,7 @@ var VIDEO_EXT_BY_MIME = {
33326
33398
  "video/x-matroska": ".mkv"
33327
33399
  };
33328
33400
  function referenceVideoExt(url, contentType) {
33329
- const fromPath = path25.extname(new URL(url).pathname).toLowerCase();
33401
+ const fromPath = path26.extname(new URL(url).pathname).toLowerCase();
33330
33402
  if (fromPath && fromPath.length <= 5) return fromPath;
33331
33403
  const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase();
33332
33404
  return mime && VIDEO_EXT_BY_MIME[mime] || ".mp4";
@@ -33352,7 +33424,7 @@ function videoDefinitionDescription(blueprint) {
33352
33424
  return typeof product === "string" && product.trim() ? product.trim() : void 0;
33353
33425
  }
33354
33426
  async function materializeReferenceVideo(fileArg2) {
33355
- if (!/^https?:\/\//i.test(fileArg2)) return path25.resolve(fileArg2);
33427
+ if (!/^https?:\/\//i.test(fileArg2)) return path26.resolve(fileArg2);
33356
33428
  let bytes;
33357
33429
  let contentType;
33358
33430
  try {
@@ -33364,7 +33436,7 @@ async function materializeReferenceVideo(fileArg2) {
33364
33436
  throw new Error(`failed to download reference video: ${e instanceof Error ? e.message : String(e)}`);
33365
33437
  }
33366
33438
  if (bytes.length === 0) throw new Error("reference video download was empty");
33367
- const dest = path25.join(
33439
+ const dest = path26.join(
33368
33440
  tmpdir3(),
33369
33441
  `baker-ref-${sha256Hex(bytes).slice(0, 16)}${referenceVideoExt(fileArg2, contentType)}`
33370
33442
  );
@@ -33584,11 +33656,11 @@ var scaffoldVideoCommand = defineCommand105({
33584
33656
  } catch (e) {
33585
33657
  return fail4("download", e instanceof Error ? e.message : String(e));
33586
33658
  }
33587
- const base = path25.basename(videoPath, path25.extname(videoPath));
33588
- const outPath = args.out ? path25.resolve(String(args.out)) : slug ? path25.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path25.join(path25.dirname(videoPath), `${base}.video.canvas.json`);
33589
- const outDir = path25.dirname(outPath);
33590
- const blueprintPath = path25.join(outDir, "prompt.json");
33591
- const blueprintStylePath = path25.join(outDir, "prompt.style.json");
33659
+ const base = path26.basename(videoPath, path26.extname(videoPath));
33660
+ const outPath = args.out ? path26.resolve(String(args.out)) : slug ? path26.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path26.join(path26.dirname(videoPath), `${base}.video.canvas.json`);
33661
+ const outDir = path26.dirname(outPath);
33662
+ const blueprintPath = path26.join(outDir, "prompt.json");
33663
+ const blueprintStylePath = path26.join(outDir, "prompt.style.json");
33592
33664
  const frames = args.frames === "reuse" ? "reuse" : "generate";
33593
33665
  const maxScenes = args["max-scenes"] ? Number(args["max-scenes"]) : void 0;
33594
33666
  if (Number.isFinite(maxScenes)) {
@@ -33633,12 +33705,12 @@ var scaffoldVideoCommand = defineCommand105({
33633
33705
  `
33634
33706
  );
33635
33707
  }
33636
- const compositionDest = path25.join(outDir, "video-overlay-composition");
33708
+ const compositionDest = path26.join(outDir, "video-overlay-composition");
33637
33709
  await cp2(SHIPPED_COMPOSITION_DIR2, compositionDest, { recursive: true });
33638
33710
  await stampCompositionDims(compositionDest, outDims);
33639
- const indexPath = path25.join(compositionDest, "index.html");
33711
+ const indexPath = path26.join(compositionDest, "index.html");
33640
33712
  const overlayHtml = buildOverlayHtml(blueprint, { captionsActive: Boolean(transcript) });
33641
- const indexHtml = await readFile18(indexPath, "utf8");
33713
+ const indexHtml = await readFile19(indexPath, "utf8");
33642
33714
  const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
33643
33715
  if (injected === indexHtml && overlayHtml.trim()) {
33644
33716
  fail4(
@@ -33652,10 +33724,10 @@ var scaffoldVideoCommand = defineCommand105({
33652
33724
  const opts = {
33653
33725
  imageModel,
33654
33726
  videoModel,
33655
- overlayCompositionPath: path25.relative(outDir, compositionDest),
33656
- captionsCompositionPath: captions.compositionPath ? path25.relative(outDir, captions.compositionPath) : void 0,
33657
- blueprintPath: path25.relative(outDir, blueprintPath),
33658
- blueprintStylePath: path25.relative(outDir, blueprintStylePath),
33727
+ overlayCompositionPath: path26.relative(outDir, compositionDest),
33728
+ captionsCompositionPath: captions.compositionPath ? path26.relative(outDir, captions.compositionPath) : void 0,
33729
+ blueprintPath: path26.relative(outDir, blueprintPath),
33730
+ blueprintStylePath: path26.relative(outDir, blueprintStylePath),
33659
33731
  frames,
33660
33732
  ambient: Boolean(args.ambient),
33661
33733
  seamDedup: resolveSeamDedup(args["seam-dedup"]),
@@ -33683,7 +33755,7 @@ var scaffoldVideoCommand = defineCommand105({
33683
33755
  await writeFile10(outPath, `${JSON.stringify(canvas, null, 2)}
33684
33756
  `, "utf8");
33685
33757
  await writeFile10(
33686
- path25.join(outDir, REBUILD_FILE),
33758
+ path26.join(outDir, REBUILD_FILE),
33687
33759
  `${JSON.stringify({ elements, opts }, null, 2)}
33688
33760
  `,
33689
33761
  "utf8"
@@ -33708,7 +33780,7 @@ var scaffoldVideoCommand = defineCommand105({
33708
33780
  await ensureGitignore(process.cwd(), ["canvas/", ".context/"]);
33709
33781
  const sourceRef = videoSourceReference(blueprint, fileArg2);
33710
33782
  if (slug) {
33711
- const definitionPath = path25.join(outDir, "_definition.md");
33783
+ const definitionPath = path26.join(outDir, "_definition.md");
33712
33784
  if (!await fileExists2(definitionPath)) {
33713
33785
  await writeFile10(
33714
33786
  definitionPath,
@@ -33763,7 +33835,7 @@ var scaffoldVideoCommand = defineCommand105({
33763
33835
  graph: canvas.metadata?.video?.graph_stats
33764
33836
  },
33765
33837
  checklist: {
33766
- 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 \`${path25.basename(blueprintPath)}\`. \`baker canvas validate\`/\`run\` re-assemble the blueprint and re-flow every edited scene back into the render (and regenerate ${path25.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 ${path25.basename(blueprintStylePath)}; both are regenerated.`,
33838
+ 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 \`${path26.basename(blueprintPath)}\`. \`baker canvas validate\`/\`run\` re-assemble the blueprint and re-flow every edited scene back into the render (and regenerate ${path26.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 ${path26.basename(blueprintStylePath)}; both are regenerated.`,
33767
33839
  recurring_elements_to_supply: report.elements,
33768
33840
  voices_to_confirm: report.dialogue.map((d) => ({
33769
33841
  scene: d.scene,
@@ -33800,8 +33872,8 @@ var scaffoldVideoCommand = defineCommand105({
33800
33872
  });
33801
33873
 
33802
33874
  // src/commands/canvas/set-prompt.ts
33803
- import { readFile as readFile19, writeFile as writeFile11 } from "fs/promises";
33804
- import path26 from "path";
33875
+ import { readFile as readFile20, writeFile as writeFile11 } from "fs/promises";
33876
+ import path27 from "path";
33805
33877
  import { defineCommand as defineCommand106 } from "citty";
33806
33878
  function setNodePrompt(canvas, nodeId, text2) {
33807
33879
  const nodes = canvas?.nodes;
@@ -33829,17 +33901,17 @@ var setPromptCommand = defineCommand106({
33829
33901
  "text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
33830
33902
  },
33831
33903
  async run({ args }) {
33832
- const filePath = path26.resolve(String(args.file));
33904
+ const filePath = path27.resolve(String(args.file));
33833
33905
  let canvas;
33834
33906
  try {
33835
- canvas = JSON.parse(await readFile19(filePath, "utf8"));
33907
+ canvas = JSON.parse(await readFile20(filePath, "utf8"));
33836
33908
  } catch (e) {
33837
33909
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "parse", message: String(e) } }, null, 2)}
33838
33910
  `);
33839
33911
  process.exit(2);
33840
33912
  }
33841
33913
  let text2;
33842
- if (args["text-file"]) text2 = await readFile19(path26.resolve(String(args["text-file"])), "utf8");
33914
+ if (args["text-file"]) text2 = await readFile20(path27.resolve(String(args["text-file"])), "utf8");
33843
33915
  else if (args.text !== void 0) text2 = String(args.text);
33844
33916
  else {
33845
33917
  process.stderr.write(
@@ -33860,7 +33932,7 @@ var setPromptCommand = defineCommand106({
33860
33932
  process.exit(2);
33861
33933
  return;
33862
33934
  }
33863
- const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path26.dirname(filePath)), defaultRegistry());
33935
+ const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path27.dirname(filePath)), defaultRegistry());
33864
33936
  if (!validation.ok) {
33865
33937
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
33866
33938
  `);
@@ -33875,8 +33947,8 @@ var setPromptCommand = defineCommand106({
33875
33947
  });
33876
33948
 
33877
33949
  // src/commands/canvas/validate.ts
33878
- import { readFile as readFile20 } from "fs/promises";
33879
- import path27 from "path";
33950
+ import { readFile as readFile21 } from "fs/promises";
33951
+ import path28 from "path";
33880
33952
  import { defineCommand as defineCommand107 } from "citty";
33881
33953
  var validateCommand = defineCommand107({
33882
33954
  meta: {
@@ -33885,8 +33957,8 @@ var validateCommand = defineCommand107({
33885
33957
  },
33886
33958
  args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
33887
33959
  async run({ args }) {
33888
- const filePath = path27.resolve(String(args.file));
33889
- const raw = await readFile20(filePath, "utf8");
33960
+ const filePath = path28.resolve(String(args.file));
33961
+ const raw = await readFile21(filePath, "utf8");
33890
33962
  let parsed;
33891
33963
  try {
33892
33964
  parsed = JSON.parse(raw);
@@ -33898,7 +33970,7 @@ var validateCommand = defineCommand107({
33898
33970
  }
33899
33971
  const healed = await healAbsoluteCanvasPaths(filePath, parsed);
33900
33972
  parsed = healed.canvas;
33901
- parsed = resolveRelativeCanvasPaths(parsed, path27.dirname(filePath));
33973
+ parsed = resolveRelativeCanvasPaths(parsed, path28.dirname(filePath));
33902
33974
  let styleProjection = "not_applicable";
33903
33975
  try {
33904
33976
  styleProjection = await syncStyleProjection(parsed, (line) => process.stderr.write(`${line}
@@ -34324,7 +34396,7 @@ import { defineCommand as defineCommand112 } from "citty";
34324
34396
  import { defineCommand as defineCommand111 } from "citty";
34325
34397
 
34326
34398
  // src/commands/images/api.ts
34327
- import { readFile as readFile21 } from "fs/promises";
34399
+ import { readFile as readFile22 } from "fs/promises";
34328
34400
  import { basename, extname } from "path";
34329
34401
  var imageProcessingTimeoutMs = 18e4;
34330
34402
  var imageReadyPollIntervalMs = 2e3;
@@ -34338,7 +34410,7 @@ var mimeMap = {
34338
34410
  ".avif": "image/avif"
34339
34411
  };
34340
34412
  var defaultImageApiDeps = {
34341
- readFile: readFile21,
34413
+ readFile: readFile22,
34342
34414
  post: apiPost,
34343
34415
  get: apiGet,
34344
34416
  sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
@@ -34600,12 +34672,12 @@ function collectSideEffects(tree) {
34600
34672
  );
34601
34673
  }
34602
34674
  function readFlowTree(slug) {
34603
- const path40 = join3(flowsDir(), slug, "_data.json");
34604
- if (!existsSync5(path40)) {
34675
+ const path41 = join3(flowsDir(), slug, "_data.json");
34676
+ if (!existsSync5(path41)) {
34605
34677
  failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
34606
34678
  }
34607
34679
  try {
34608
- return JSON.parse(readFileSync9(path40, "utf-8"));
34680
+ return JSON.parse(readFileSync9(path41, "utf-8"));
34609
34681
  } catch (error) {
34610
34682
  failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
34611
34683
  }
@@ -34997,10 +35069,10 @@ function parseValueExpression(raw) {
34997
35069
  return parts.map(parsePart);
34998
35070
  }
34999
35071
  function trackingFieldIds() {
35000
- const path40 = join4(flowsDir(), "..", "tracking.ts");
35001
- if (!existsSync6(path40)) return null;
35072
+ const path41 = join4(flowsDir(), "..", "tracking.ts");
35073
+ if (!existsSync6(path41)) return null;
35002
35074
  try {
35003
- const source = readFileSync10(path40, "utf-8");
35075
+ const source = readFileSync10(path41, "utf-8");
35004
35076
  const block2 = source.match(/TRACKING_FIELD_IDS\s*=\s*\[([\s\S]*?)\]\s*as const/)?.[1];
35005
35077
  if (!block2) return null;
35006
35078
  const ids = [...block2.matchAll(/"(tracking\.[a-z0-9_]+)"/g)].map((match) => match[1]);
@@ -35552,13 +35624,13 @@ function specsFromFile(parsed) {
35552
35624
  return `${destField}${type}=${entry?.value ?? ""}`;
35553
35625
  });
35554
35626
  }
35555
- function readSpecFile(path40) {
35627
+ function readSpecFile(path41) {
35556
35628
  let raw;
35557
35629
  try {
35558
- raw = path40 === "-" ? readFileSync11(0, "utf-8") : readFileSync11(path40, "utf-8");
35630
+ raw = path41 === "-" ? readFileSync11(0, "utf-8") : readFileSync11(path41, "utf-8");
35559
35631
  } catch (error) {
35560
35632
  refuse(
35561
- `Could not read ${path40 === "-" ? "the mapping from stdin" : `"${path40}"`}: ${error instanceof Error ? error.message : String(error)}`
35633
+ `Could not read ${path41 === "-" ? "the mapping from stdin" : `"${path41}"`}: ${error instanceof Error ? error.message : String(error)}`
35562
35634
  );
35563
35635
  }
35564
35636
  let parsed;
@@ -35566,7 +35638,7 @@ function readSpecFile(path40) {
35566
35638
  parsed = JSON.parse(raw);
35567
35639
  } catch (error) {
35568
35640
  refuse(
35569
- `${path40 === "-" ? "stdin" : `"${path40}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
35641
+ `${path41 === "-" ? "stdin" : `"${path41}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
35570
35642
  'Expected { "map": { "<destField>": "<value>", \u2026 } }'
35571
35643
  );
35572
35644
  }
@@ -35820,18 +35892,18 @@ var ARRAY_FIELDS = [
35820
35892
  "tagIds"
35821
35893
  ];
35822
35894
  var ARRAY_OWNERS = ["", "body"];
35823
- function dropUnsetOptionals(sideEffect, path40) {
35895
+ function dropUnsetOptionals(sideEffect, path41) {
35824
35896
  return OPTIONAL_STRINGS.flatMap((key) => {
35825
35897
  if (!(key in sideEffect) || sideEffect[key] !== null && sideEffect[key] !== "") return [];
35826
35898
  delete sideEffect[key];
35827
- return [{ path: path40, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
35899
+ return [{ path: path41, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
35828
35900
  });
35829
35901
  }
35830
- function fillNulledArrays(target, prefix, path40) {
35902
+ function fillNulledArrays(target, prefix, path41) {
35831
35903
  return ARRAY_FIELDS.flatMap((key) => {
35832
35904
  if (!(key in target) || target[key] !== null) return [];
35833
35905
  target[key] = [];
35834
- return [{ path: path40, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
35906
+ return [{ path: path41, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
35835
35907
  });
35836
35908
  }
35837
35909
  function sideEffectsOf(node) {
@@ -35841,13 +35913,13 @@ function sideEffectsOf(node) {
35841
35913
  );
35842
35914
  }
35843
35915
  function normalizeSideEffect(sideEffect, where) {
35844
- const path40 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
35916
+ const path41 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
35845
35917
  const arrays = ARRAY_OWNERS.flatMap((owner) => {
35846
35918
  const target = owner ? sideEffect[owner] : sideEffect;
35847
35919
  if (!target || typeof target !== "object") return [];
35848
- return fillNulledArrays(target, owner ? `${owner}.` : "", path40);
35920
+ return fillNulledArrays(target, owner ? `${owner}.` : "", path41);
35849
35921
  });
35850
- return [...dropUnsetOptionals(sideEffect, path40), ...arrays];
35922
+ return [...dropUnsetOptionals(sideEffect, path41), ...arrays];
35851
35923
  }
35852
35924
  function normalizeFlowTree(tree) {
35853
35925
  const changes = [];
@@ -36385,10 +36457,10 @@ async function stageOps(ops) {
36385
36457
  handleError2(err);
36386
36458
  }
36387
36459
  }
36388
- async function draftAction2(path40, body, chat) {
36460
+ async function draftAction2(path41, body, chat) {
36389
36461
  const chatId = resolveChatId(chat);
36390
36462
  try {
36391
- const data = await apiPost(path40, { chatId, ...body });
36463
+ const data = await apiPost(path41, { chatId, ...body });
36392
36464
  writeJsonEnvelope({ ok: true, data });
36393
36465
  return data;
36394
36466
  } catch (err) {
@@ -38785,7 +38857,7 @@ function cropSprite(input, region) {
38785
38857
 
38786
38858
  // src/lib/image/io.ts
38787
38859
  import { randomBytes } from "crypto";
38788
- import { glob as fsGlob, readFile as readFile22, rename, stat as stat4, writeFile as writeFile12 } from "fs/promises";
38860
+ import { glob as fsGlob, readFile as readFile23, rename, stat as stat4, writeFile as writeFile12 } from "fs/promises";
38789
38861
  import { dirname as dirname2, extname as extname2, join as join5, resolve as resolve4 } from "path";
38790
38862
  var REMOTE_RE = /^https?:\/\//i;
38791
38863
  var GLOB_RE = /[*?[\]{}]/;
@@ -38818,11 +38890,11 @@ async function readImageBuffer(pathOrUrl) {
38818
38890
  const { buffer } = await fetchExternalBytes(pathOrUrl, { maxBytes: MAX_REMOTE_IMAGE_BYTES });
38819
38891
  return buffer;
38820
38892
  }
38821
- return readFile22(pathOrUrl);
38893
+ return readFile23(pathOrUrl);
38822
38894
  }
38823
- async function isDirectory(path40) {
38895
+ async function isDirectory(path41) {
38824
38896
  try {
38825
- const s = await stat4(path40);
38897
+ const s = await stat4(path41);
38826
38898
  return s.isDirectory();
38827
38899
  } catch {
38828
38900
  return false;
@@ -39124,13 +39196,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
39124
39196
  }
39125
39197
  function disambiguate(paths) {
39126
39198
  const taken = /* @__PURE__ */ new Set();
39127
- return paths.map((path40) => {
39128
- if (!taken.has(path40)) {
39129
- taken.add(path40);
39130
- return path40;
39199
+ return paths.map((path41) => {
39200
+ if (!taken.has(path41)) {
39201
+ taken.add(path41);
39202
+ return path41;
39131
39203
  }
39132
- const ext = extname3(path40);
39133
- const stem = path40.slice(0, path40.length - ext.length);
39204
+ const ext = extname3(path41);
39205
+ const stem = path41.slice(0, path41.length - ext.length);
39134
39206
  let n = 2;
39135
39207
  while (taken.has(`${stem}-${n}${ext}`)) n += 1;
39136
39208
  const unique = `${stem}-${n}${ext}`;
@@ -39257,10 +39329,10 @@ async function runDownloads(plan) {
39257
39329
  const paths = disambiguate(fetched.map((item) => item.path));
39258
39330
  const downloaded = [];
39259
39331
  for (const [index, item] of fetched.entries()) {
39260
- const path40 = paths[index] ?? item.path;
39332
+ const path41 = paths[index] ?? item.path;
39261
39333
  try {
39262
- await atomicWrite(path40, item.buffer);
39263
- downloaded.push({ input: item.input, output: path40, bytes: item.buffer.length, contentType: item.contentType });
39334
+ await atomicWrite(path41, item.buffer);
39335
+ downloaded.push({ input: item.input, output: path41, bytes: item.buffer.length, contentType: item.contentType });
39264
39336
  } catch (err) {
39265
39337
  failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
39266
39338
  }
@@ -42219,8 +42291,8 @@ Full guide: __tooling__/docs/tools/baker/images.md`
42219
42291
  import { defineCommand as defineCommand167 } from "citty";
42220
42292
 
42221
42293
  // src/commands/landing/critique.ts
42222
- import { readdir as readdir9, stat as stat6 } from "fs/promises";
42223
- import path30 from "path";
42294
+ import { readdir as readdir10, stat as stat6 } from "fs/promises";
42295
+ import path31 from "path";
42224
42296
  import { defineCommand as defineCommand157 } from "citty";
42225
42297
 
42226
42298
  // src/engine/landing/lib/constants.ts
@@ -43188,13 +43260,13 @@ function describeCounts(findings) {
43188
43260
 
43189
43261
  // src/commands/landing/snapshot.ts
43190
43262
  import { mkdir as mkdir9, rename as rename2, writeFile as writeFile13 } from "fs/promises";
43191
- import path28 from "path";
43263
+ import path29 from "path";
43192
43264
  var CRITIC_VERSION = "2";
43193
43265
  function critiqueCacheDir(projectRoot) {
43194
- return path28.join(projectRoot, ".cache", "landing-critique");
43266
+ return path29.join(projectRoot, ".cache", "landing-critique");
43195
43267
  }
43196
43268
  function snapshotPath(projectRoot, slug) {
43197
- return path28.join(critiqueCacheDir(projectRoot), `${slug}.json`);
43269
+ return path29.join(critiqueCacheDir(projectRoot), `${slug}.json`);
43198
43270
  }
43199
43271
  async function writeCritiqueSnapshot(projectRoot, snapshot) {
43200
43272
  await mkdir9(critiqueCacheDir(projectRoot), { recursive: true });
@@ -43206,21 +43278,21 @@ async function writeCritiqueSnapshot(projectRoot, snapshot) {
43206
43278
  }
43207
43279
 
43208
43280
  // src/commands/landing/source-version.ts
43209
- import { readdir as readdir8, readFile as readFile23, stat as stat5 } from "fs/promises";
43210
- import path29 from "path";
43281
+ import { readdir as readdir9, readFile as readFile24, stat as stat5 } from "fs/promises";
43282
+ import path30 from "path";
43211
43283
  async function landingSourceRelPaths(landingDir) {
43212
43284
  const rel = [];
43213
- if (await isFile(path29.join(landingDir, "index.astro"))) rel.push("index.astro");
43214
- const componentsDir = path29.join(landingDir, "_components");
43285
+ if (await isFile(path30.join(landingDir, "index.astro"))) rel.push("index.astro");
43286
+ const componentsDir = path30.join(landingDir, "_components");
43215
43287
  for (const abs of await walkAstro(componentsDir)) {
43216
- rel.push(path29.relative(landingDir, abs).split(path29.sep).join("/"));
43288
+ rel.push(path30.relative(landingDir, abs).split(path30.sep).join("/"));
43217
43289
  }
43218
43290
  return rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
43219
43291
  }
43220
43292
  async function readLandingSources(landingDir) {
43221
43293
  const rel = await landingSourceRelPaths(landingDir);
43222
43294
  const out = [];
43223
- for (const r of rel) out.push({ path: r, text: await readFile23(path29.join(landingDir, r), "utf8") });
43295
+ for (const r of rel) out.push({ path: r, text: await readFile24(path30.join(landingDir, r), "utf8") });
43224
43296
  return out;
43225
43297
  }
43226
43298
  async function computeLandingSourceSha(landingDir) {
@@ -43229,7 +43301,7 @@ async function computeLandingSourceSha(landingDir) {
43229
43301
  for (const r of rel) {
43230
43302
  let bytes;
43231
43303
  try {
43232
- bytes = await readFile23(path29.join(landingDir, r));
43304
+ bytes = await readFile24(path30.join(landingDir, r));
43233
43305
  } catch {
43234
43306
  bytes = Buffer.alloc(0);
43235
43307
  }
@@ -43247,13 +43319,13 @@ async function isFile(p) {
43247
43319
  async function walkAstro(dir) {
43248
43320
  let entries;
43249
43321
  try {
43250
- entries = await readdir8(dir, { withFileTypes: true });
43322
+ entries = await readdir9(dir, { withFileTypes: true });
43251
43323
  } catch {
43252
43324
  return [];
43253
43325
  }
43254
43326
  const out = [];
43255
43327
  for (const entry of entries) {
43256
- const abs = path29.join(dir, entry.name);
43328
+ const abs = path30.join(dir, entry.name);
43257
43329
  if (entry.isDirectory()) out.push(...await walkAstro(abs));
43258
43330
  else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
43259
43331
  }
@@ -43314,7 +43386,7 @@ var critiqueCommand2 = defineCommand157({
43314
43386
  { availableSlugs: await listLandingSlugs(projectRoot) }
43315
43387
  );
43316
43388
  }
43317
- if (!await isDir(path30.resolve(projectRoot, "src", "pages", slug))) {
43389
+ if (!await isDir(path31.resolve(projectRoot, "src", "pages", slug))) {
43318
43390
  fail5("NOT_FOUND", `No landing at src/pages/${slug}/`, {
43319
43391
  availableSlugs: await listLandingSlugs(projectRoot)
43320
43392
  });
@@ -43353,7 +43425,7 @@ var critiqueCommand2 = defineCommand157({
43353
43425
  }
43354
43426
  });
43355
43427
  async function critiqueOne(projectRoot, slug, brand) {
43356
- const landingDir = path30.resolve(projectRoot, "src", "pages", slug);
43428
+ const landingDir = path31.resolve(projectRoot, "src", "pages", slug);
43357
43429
  const [sources, sourceSha] = await Promise.all([readLandingSources(landingDir), computeLandingSourceSha(landingDir)]);
43358
43430
  const report = critiqueLanding({ slug, sources, brand });
43359
43431
  let snapshotFailed = false;
@@ -43373,7 +43445,7 @@ async function critiqueOne(projectRoot, slug, brand) {
43373
43445
  }
43374
43446
  async function listLandingSlugs(projectRoot) {
43375
43447
  try {
43376
- const entries = await readdir9(path30.join(projectRoot, "src", "pages"), { withFileTypes: true });
43448
+ const entries = await readdir10(path31.join(projectRoot, "src", "pages"), { withFileTypes: true });
43377
43449
  return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
43378
43450
  } catch {
43379
43451
  return [];
@@ -43525,7 +43597,7 @@ var addCommand = defineCommand158({
43525
43597
 
43526
43598
  // src/commands/landing/inspiration/code.ts
43527
43599
  import { mkdir as mkdir10, writeFile as writeFile14 } from "fs/promises";
43528
- import path31 from "path";
43600
+ import path32 from "path";
43529
43601
  import { defineCommand as defineCommand159 } from "citty";
43530
43602
  registerSchema({
43531
43603
  command: "landing.inspiration.code",
@@ -43548,9 +43620,9 @@ var codeCommand = defineCommand159({
43548
43620
  try {
43549
43621
  const id = args.id;
43550
43622
  const data = await apiGet("/api/landing-inspiration/section-code", { id });
43551
- const dir = path31.join(process.cwd(), ".baker", "inspiration", id);
43623
+ const dir = path32.join(process.cwd(), ".baker", "inspiration", id);
43552
43624
  await mkdir10(dir, { recursive: true });
43553
- const file = path31.join(dir, "section.html");
43625
+ const file = path32.join(dir, "section.html");
43554
43626
  await writeFile14(file, data.html);
43555
43627
  const hints = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
43556
43628
  const fidelity = fidelityHint(data.fidelity);
@@ -43560,7 +43632,7 @@ var codeCommand = defineCommand159({
43560
43632
  ok: true,
43561
43633
  data: {
43562
43634
  id,
43563
- file: path31.relative(process.cwd(), file),
43635
+ file: path32.relative(process.cwd(), file),
43564
43636
  bytes: data.html.length,
43565
43637
  fidelity: data.fidelity,
43566
43638
  reproduction_notes: data.reproductionNotes,
@@ -43992,7 +44064,7 @@ function classifyCaptureFailure(error) {
43992
44064
 
43993
44065
  // src/engine/landing-library/run.ts
43994
44066
  import { mkdir as mkdir11, writeFile as writeFile16 } from "fs/promises";
43995
- import path33 from "path";
44067
+ import path34 from "path";
43996
44068
 
43997
44069
  // ../proxy/src/preflight.ts
43998
44070
  import http from "http";
@@ -45408,9 +45480,9 @@ async function renderBundleToPng(browser, html, viewportWidth, options = {}) {
45408
45480
 
45409
45481
  // src/engine/landing-library/report.ts
45410
45482
  import { writeFile as writeFile15 } from "fs/promises";
45411
- import path32 from "path";
45483
+ import path33 from "path";
45412
45484
  async function writeCaptureReport(manifest, outDir) {
45413
- const file = path32.join(outDir, "report.html");
45485
+ const file = path33.join(outDir, "report.html");
45414
45486
  await writeFile15(file, renderReport(manifest));
45415
45487
  return file;
45416
45488
  }
@@ -45589,32 +45661,32 @@ async function reproducePage(args) {
45589
45661
  const { browser, page, outDir, pageUrl, livePageShot } = args;
45590
45662
  const built = await buildSectionBundle(page, "body", pageUrl).catch(() => null);
45591
45663
  if (!built) return { bundle: null, fidelity: null };
45592
- await writeFile16(path33.join(outDir, "page.html"), built.html);
45664
+ await writeFile16(path34.join(outDir, "page.html"), built.html);
45593
45665
  const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width, {
45594
45666
  wholePage: true,
45595
45667
  timeoutMs: 6e4
45596
45668
  });
45597
45669
  if (!rendered || !livePageShot) return { bundle: "page.html", fidelity: null };
45598
- await writeFile16(path33.join(outDir, "page-rendered.png"), rendered);
45670
+ await writeFile16(path34.join(outDir, "page-rendered.png"), rendered);
45599
45671
  const { score, note } = await scoreFidelity(livePageShot, rendered);
45600
45672
  return { bundle: "page.html", fidelity: score, ...note ? { fidelityNote: note } : {} };
45601
45673
  }
45602
45674
  async function captureOneSection(args) {
45603
45675
  const { browser, page, candidate, sectionsDir, outDir, pageUrl, withCode } = args;
45604
- const dir = path33.join(sectionsDir, String(candidate.index).padStart(2, "0"));
45676
+ const dir = path34.join(sectionsDir, String(candidate.index).padStart(2, "0"));
45605
45677
  await mkdir11(dir, { recursive: true });
45606
45678
  const desktop = await captureSection(page, candidate);
45607
- if (desktop) await writeFile16(path33.join(dir, "desktop.png"), desktop);
45679
+ if (desktop) await writeFile16(path34.join(dir, "desktop.png"), desktop);
45608
45680
  const visualHash = desktop ? await perceptualHash(desktop) : null;
45609
45681
  const motion = await collectMotion(page, candidate.selector);
45610
45682
  const built = withCode ? await buildSectionBundle(page, candidate.selector, pageUrl) : null;
45611
45683
  let fidelity = null;
45612
45684
  let fidelityNote;
45613
45685
  if (built) {
45614
- await writeFile16(path33.join(dir, "section.html"), built.html);
45686
+ await writeFile16(path34.join(dir, "section.html"), built.html);
45615
45687
  const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width);
45616
45688
  if (rendered && desktop) {
45617
- await writeFile16(path33.join(dir, "section-rendered.png"), rendered);
45689
+ await writeFile16(path34.join(dir, "section-rendered.png"), rendered);
45618
45690
  const result = await scoreFidelity(desktop, rendered);
45619
45691
  fidelity = result.score;
45620
45692
  fidelityNote = result.note;
@@ -45622,9 +45694,9 @@ async function captureOneSection(args) {
45622
45694
  }
45623
45695
  return {
45624
45696
  ...candidate,
45625
- desktopShot: desktop ? path33.relative(outDir, path33.join(dir, "desktop.png")) : null,
45697
+ desktopShot: desktop ? path34.relative(outDir, path34.join(dir, "desktop.png")) : null,
45626
45698
  mobileShot: null,
45627
- bundle: built ? path33.relative(outDir, path33.join(dir, "section.html")) : null,
45699
+ bundle: built ? path34.relative(outDir, path34.join(dir, "section.html")) : null,
45628
45700
  fidelity,
45629
45701
  ...fidelityNote ? { fidelityNote } : {},
45630
45702
  ...built ? { cssStats: built.stats } : {},
@@ -45643,9 +45715,9 @@ async function captureMobileShots(args) {
45643
45715
  for (const section of sections) {
45644
45716
  const shot = await captureSectionOnMobile(mobile.page, section);
45645
45717
  if (!shot) continue;
45646
- const file = path33.join(sectionsDir, String(section.index).padStart(2, "0"), "mobile.png");
45718
+ const file = path34.join(sectionsDir, String(section.index).padStart(2, "0"), "mobile.png");
45647
45719
  await writeFile16(file, shot);
45648
- section.mobileShot = path33.relative(outDir, file);
45720
+ section.mobileShot = path34.relative(outDir, file);
45649
45721
  }
45650
45722
  } finally {
45651
45723
  await mobile.context.close();
@@ -45660,10 +45732,10 @@ async function captureMotionTakes(args) {
45660
45732
  const filmOne = async (section) => {
45661
45733
  const take = await captureMotionTake(browser, pageUrl, section.selector).catch(() => null);
45662
45734
  if (!take) return;
45663
- const dir = path33.join(sectionsDir, String(section.index).padStart(2, "0"));
45664
- const file = path33.join(dir, "motion-filmstrip.png");
45735
+ const dir = path34.join(sectionsDir, String(section.index).padStart(2, "0"));
45736
+ const file = path34.join(dir, "motion-filmstrip.png");
45665
45737
  await writeFile16(file, take.filmstrip);
45666
- section.motionFilmstrip = path33.relative(outDir, file);
45738
+ section.motionFilmstrip = path34.relative(outDir, file);
45667
45739
  log(` [${section.index}] ${section.motion.summary}`);
45668
45740
  };
45669
45741
  const queue = [...moving];
@@ -45720,7 +45792,7 @@ async function captureAlternateViews(args) {
45720
45792
  async function reproduceWholePage(args) {
45721
45793
  const { browser, page, outDir, pageUrl, withCode, log } = args;
45722
45794
  const fullPage = await page.screenshot({ type: "png", fullPage: true }).catch(() => null);
45723
- if (fullPage) await writeFile16(path33.join(outDir, "full-page.png"), fullPage);
45795
+ if (fullPage) await writeFile16(path34.join(outDir, "full-page.png"), fullPage);
45724
45796
  if (!withCode) return { bundle: null, fidelity: null };
45725
45797
  const reproduction = await reproducePage({ browser, page, outDir, pageUrl, livePageShot: fullPage });
45726
45798
  log(`page reproduction: ${reproduction.fidelity === null ? "unavailable" : reproduction.fidelity.toFixed(2)}`);
@@ -45791,7 +45863,7 @@ async function openViaLadder(args) {
45791
45863
  async function scrapeLanding(options) {
45792
45864
  const timeoutMs = options.timeoutMs ?? 45e3;
45793
45865
  const log = options.onProgress ?? (() => void 0);
45794
- const sectionsDir = path33.join(options.outDir, "sections");
45866
+ const sectionsDir = path34.join(options.outDir, "sections");
45795
45867
  const nonPublic = refuseNonPublicUrl(options.url);
45796
45868
  if (nonPublic) {
45797
45869
  throw new BlockedPageError({
@@ -45853,7 +45925,7 @@ async function scrapeLanding(options) {
45853
45925
  security: prepared.security,
45854
45926
  captureTier: tier
45855
45927
  };
45856
- await writeFile16(path33.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
45928
+ await writeFile16(path34.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
45857
45929
  `);
45858
45930
  if (options.report !== false) {
45859
45931
  const reportPath = await writeCaptureReport(manifest, options.outDir);
@@ -45868,28 +45940,28 @@ async function scrapeLanding(options) {
45868
45940
 
45869
45941
  // src/commands/landing/inspiration/captureOut.ts
45870
45942
  import { existsSync as existsSync9 } from "fs";
45871
- import path34 from "path";
45943
+ import path35 from "path";
45872
45944
  var SCRATCH_DIR = ".baker";
45873
45945
  function isWithin(parent, target) {
45874
- const relative = path34.relative(parent, target);
45875
- return relative === "" || !relative.startsWith("..") && !path34.isAbsolute(relative);
45946
+ const relative = path35.relative(parent, target);
45947
+ return relative === "" || !relative.startsWith("..") && !path35.isAbsolute(relative);
45876
45948
  }
45877
45949
  function findRepoRoot(from) {
45878
- let dir = path34.resolve(from);
45950
+ let dir = path35.resolve(from);
45879
45951
  for (; ; ) {
45880
- if (existsSync9(path34.join(dir, ".git"))) return dir;
45881
- const parent = path34.dirname(dir);
45952
+ if (existsSync9(path35.join(dir, ".git"))) return dir;
45953
+ const parent = path35.dirname(dir);
45882
45954
  if (parent === dir) return null;
45883
45955
  dir = parent;
45884
45956
  }
45885
45957
  }
45886
45958
  function checkCaptureOut(out, options) {
45887
45959
  const { cwd, repoRoot } = options;
45888
- const resolved = path34.resolve(cwd, out);
45960
+ const resolved = path35.resolve(cwd, out);
45889
45961
  if (repoRoot === null || !isWithin(repoRoot, resolved)) return { ok: true };
45890
- const scratch = path34.join(repoRoot, SCRATCH_DIR);
45962
+ const scratch = path35.join(repoRoot, SCRATCH_DIR);
45891
45963
  if (isWithin(scratch, resolved)) return { ok: true };
45892
- const suggestion = path34.posix.join(SCRATCH_DIR, "teardowns", path34.basename(resolved) || "capture");
45964
+ const suggestion = path35.posix.join(SCRATCH_DIR, "teardowns", path35.basename(resolved) || "capture");
45893
45965
  return {
45894
45966
  ok: false,
45895
45967
  error: {
@@ -46063,12 +46135,12 @@ var scrapeCommand = defineCommand162({
46063
46135
  });
46064
46136
 
46065
46137
  // src/commands/landing/inspiration/search.ts
46066
- import path36 from "path";
46138
+ import path37 from "path";
46067
46139
  import { defineCommand as defineCommand163 } from "citty";
46068
46140
 
46069
46141
  // src/commands/landing/inspiration/shot.ts
46070
46142
  import { mkdir as mkdir12, writeFile as writeFile17 } from "fs/promises";
46071
- import path35 from "path";
46143
+ import path36 from "path";
46072
46144
  import sharp6 from "sharp";
46073
46145
  var READABLE_SHOT = {
46074
46146
  maxWidth: 1440,
@@ -46094,9 +46166,9 @@ async function downloadReadableShot(url, file) {
46094
46166
  const response = await fetch(url);
46095
46167
  if (!response.ok) return null;
46096
46168
  const shot = await toReadableShot(Buffer.from(await response.arrayBuffer()));
46097
- await mkdir12(path35.dirname(file), { recursive: true });
46169
+ await mkdir12(path36.dirname(file), { recursive: true });
46098
46170
  await writeFile17(file, shot);
46099
- return path35.relative(process.cwd(), file);
46171
+ return path36.relative(process.cwd(), file);
46100
46172
  } catch {
46101
46173
  return null;
46102
46174
  }
@@ -46188,13 +46260,13 @@ function buildSearchBody(args) {
46188
46260
  return body;
46189
46261
  }
46190
46262
  async function downloadShots(results) {
46191
- const dir = path36.join(process.cwd(), ".baker", "inspiration");
46263
+ const dir = path37.join(process.cwd(), ".baker", "inspiration");
46192
46264
  const saved = /* @__PURE__ */ new Map();
46193
46265
  await Promise.all(
46194
46266
  results.map(async (result) => {
46195
46267
  const file = await downloadReadableShot(
46196
46268
  result.desktopShotUrl,
46197
- path36.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
46269
+ path37.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
46198
46270
  );
46199
46271
  if (file) saved.set(result.id, file);
46200
46272
  })
@@ -46422,7 +46494,7 @@ var sequencesCommand = defineCommand164({
46422
46494
  });
46423
46495
 
46424
46496
  // src/commands/landing/inspiration/view.ts
46425
- import path37 from "path";
46497
+ import path38 from "path";
46426
46498
  import { defineCommand as defineCommand165 } from "citty";
46427
46499
  registerSchema({
46428
46500
  command: "landing.inspiration.view",
@@ -46455,12 +46527,12 @@ var viewCommand2 = defineCommand165({
46455
46527
  const id = args.id;
46456
46528
  const data = await apiGet("/api/landing-inspiration/section", { id });
46457
46529
  const section = data.section;
46458
- const dir = path37.join(process.cwd(), ".baker", "inspiration", id);
46530
+ const dir = path38.join(process.cwd(), ".baker", "inspiration", id);
46459
46531
  const ext = READABLE_SHOT.extension;
46460
46532
  const [desktop, mobile, filmstrip] = await Promise.all([
46461
- downloadReadableShot(section.desktopShotUrl, path37.join(dir, `desktop.${ext}`)),
46462
- downloadReadableShot(section.mobileShotUrl, path37.join(dir, `mobile.${ext}`)),
46463
- downloadReadableShot(section.motionFilmstripUrl, path37.join(dir, `motion-filmstrip.${ext}`))
46533
+ downloadReadableShot(section.desktopShotUrl, path38.join(dir, `desktop.${ext}`)),
46534
+ downloadReadableShot(section.mobileShotUrl, path38.join(dir, `mobile.${ext}`)),
46535
+ downloadReadableShot(section.motionFilmstripUrl, path38.join(dir, `motion-filmstrip.${ext}`))
46464
46536
  ]);
46465
46537
  const full = args.full;
46466
46538
  const hints = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
@@ -48309,8 +48381,8 @@ var listCommand15 = defineCommand184({
48309
48381
  });
48310
48382
 
48311
48383
  // src/commands/scheduled-actions/templates.ts
48312
- import { readFile as readFile24 } from "fs/promises";
48313
- import path38 from "path";
48384
+ import { readFile as readFile25 } from "fs/promises";
48385
+ import path39 from "path";
48314
48386
  import { defineCommand as defineCommand185 } from "citty";
48315
48387
  registerSchema({
48316
48388
  command: "scheduled-actions.templates",
@@ -48413,7 +48485,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
48413
48485
  }
48414
48486
  if (save.length > 0) {
48415
48487
  const briefFile = flag("brief-file");
48416
- const brief = briefFile.length > 0 ? await readFile24(path38.resolve(briefFile), "utf8") : flag("brief");
48488
+ const brief = briefFile.length > 0 ? await readFile25(path39.resolve(briefFile), "utf8") : flag("brief");
48417
48489
  if (brief.trim().length === 0) {
48418
48490
  failValidation4("--brief-file (preferred) or --brief is required: the brief is the recipe.");
48419
48491
  }
@@ -48884,7 +48956,7 @@ function parseImageRefs(spec) {
48884
48956
  }
48885
48957
  var defaultDeps = {
48886
48958
  ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
48887
- upload: (path40) => uploadLocalImage({ file: path40, contentType: detectImageContentType(path40), source: "uploaded" })
48959
+ upload: (path41) => uploadLocalImage({ file: path41, contentType: detectImageContentType(path41), source: "uploaded" })
48888
48960
  };
48889
48961
  async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
48890
48962
  const refs = parseImageRefs(spec);
@@ -48904,10 +48976,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
48904
48976
  }
48905
48977
  return { imageIds, added };
48906
48978
  }
48907
- function uploadFailure(path40) {
48979
+ function uploadFailure(path41) {
48908
48980
  return (error) => {
48909
48981
  if (error instanceof ApiError) throw error;
48910
- throw new ApiError("VALIDATION_ERROR", `Could not read "${path40}" as an image.`);
48982
+ throw new ApiError("VALIDATION_ERROR", `Could not read "${path41}" as an image.`);
48911
48983
  };
48912
48984
  }
48913
48985
 
@@ -49979,10 +50051,10 @@ async function stageOp4(op) {
49979
50051
  handleError5(err);
49980
50052
  }
49981
50053
  }
49982
- async function draftAction3(path40, body, chat) {
50054
+ async function draftAction3(path41, body, chat) {
49983
50055
  const chatId = resolveChatId(chat);
49984
50056
  try {
49985
- const data = await apiPost(path40, { chatId, ...body });
50057
+ const data = await apiPost(path41, { chatId, ...body });
49986
50058
  writeJsonEnvelope({ ok: true, data });
49987
50059
  return data;
49988
50060
  } catch (err) {
@@ -51089,7 +51161,7 @@ var groupCommand2 = defineCommand210({
51089
51161
  // src/commands/videos/ingest.ts
51090
51162
  import { mkdtemp as mkdtemp3, rm as rm8, stat as stat7 } from "fs/promises";
51091
51163
  import { tmpdir as tmpdir4 } from "os";
51092
- import path39 from "path";
51164
+ import path40 from "path";
51093
51165
  import { defineCommand as defineCommand211 } from "citty";
51094
51166
 
51095
51167
  // src/lib/streamUpload.ts
@@ -51440,7 +51512,7 @@ function ingestUrl(args) {
51440
51512
  }
51441
51513
  async function downloadThenIngest(args, country) {
51442
51514
  const vimeoCookie = captureVimeoCookie();
51443
- const workDir = await mkdtemp3(path39.join(tmpdir4(), "videos-ingest-"));
51515
+ const workDir = await mkdtemp3(path40.join(tmpdir4(), "videos-ingest-"));
51444
51516
  try {
51445
51517
  const probe = await probeYtDlp({ url: args.url, country, vimeoCookie, cookieDir: workDir });
51446
51518
  if (isAudioOnly(probe.info)) {
@@ -51576,7 +51648,7 @@ var searchCommand4 = defineCommand212({
51576
51648
  var tagsCommand6 = makeTagsCommand("videos", "video", "/api/videos/tags");
51577
51649
 
51578
51650
  // src/commands/videos/upload.ts
51579
- import { readFile as readFile25, stat as stat8 } from "fs/promises";
51651
+ import { readFile as readFile26, stat as stat8 } from "fs/promises";
51580
51652
  import { basename as basename3, extname as extname4 } from "path";
51581
51653
  import { defineCommand as defineCommand213 } from "citty";
51582
51654
  var MIME_MAP = {
@@ -51673,7 +51745,7 @@ var uploadCommand2 = defineCommand213({
51673
51745
  originalFilename,
51674
51746
  descriptionContext
51675
51747
  });
51676
- const fileBuffer = await readFile25(filePath);
51748
+ const fileBuffer = await readFile26(filePath);
51677
51749
  const uploadResponse = await fetch(uploadUrl, {
51678
51750
  method: "PUT",
51679
51751
  headers: { "Content-Type": contentType },
@@ -53060,7 +53132,7 @@ function unknownFlagEnvelope(unknown, commandPath, suggestion) {
53060
53132
  };
53061
53133
  }
53062
53134
  function commandPathOf(root, argv) {
53063
- const path40 = [];
53135
+ const path41 = [];
53064
53136
  let command = root;
53065
53137
  for (const token of argv) {
53066
53138
  if (token === "--" || token.startsWith("-")) {
@@ -53071,10 +53143,10 @@ function commandPathOf(root, argv) {
53071
53143
  if (next === void 0 || typeof next !== "object") {
53072
53144
  break;
53073
53145
  }
53074
- path40.push(token);
53146
+ path41.push(token);
53075
53147
  command = next;
53076
53148
  }
53077
- return path40.join(" ");
53149
+ return path41.join(" ");
53078
53150
  }
53079
53151
  function refuseUnknownFlags(root, argv) {
53080
53152
  const unknown = findUnknownFlags(root, argv);