@koda-sl/baker-cli 0.149.0 → 0.150.0-dev.a970fa118

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
@@ -36,7 +36,7 @@ import {
36
36
  toModelSafeImage,
37
37
  ulid,
38
38
  validateCanvasDeep
39
- } from "./chunk-OO5BDH3J.js";
39
+ } from "./chunk-JBDSJZBZ.js";
40
40
  import {
41
41
  csvOrJson,
42
42
  daysAgoIso,
@@ -75,7 +75,7 @@ import {
75
75
  } from "./chunk-RK67WL4O.js";
76
76
 
77
77
  // src/cli.ts
78
- import { defineCommand as defineCommand178, runMain } from "citty";
78
+ import { defineCommand as defineCommand180, runMain } from "citty";
79
79
 
80
80
  // src/commands/actions/index.ts
81
81
  import { defineCommand as defineCommand18 } from "citty";
@@ -5686,11 +5686,11 @@ function rawTextEntries(value) {
5686
5686
  const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
5687
5687
  return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
5688
5688
  }
5689
- function rawFileEntries(path27) {
5690
- if (typeof path27 !== "string" || path27.length === 0) {
5689
+ function rawFileEntries(path28) {
5690
+ if (typeof path28 !== "string" || path28.length === 0) {
5691
5691
  return [];
5692
5692
  }
5693
- return readFileSync2(path27, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
5693
+ return readFileSync2(path28, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
5694
5694
  }
5695
5695
  function keywordEntries(args) {
5696
5696
  const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
@@ -5713,19 +5713,19 @@ function keywordEntries(args) {
5713
5713
  }
5714
5714
  return entries;
5715
5715
  }
5716
- function loadJsonFileArg(path27) {
5717
- if (typeof path27 !== "string" || path27.length === 0) {
5716
+ function loadJsonFileArg(path28) {
5717
+ if (typeof path28 !== "string" || path28.length === 0) {
5718
5718
  return {};
5719
5719
  }
5720
5720
  try {
5721
- const parsed = JSON.parse(readFileSync2(path27, "utf8"));
5721
+ const parsed = JSON.parse(readFileSync2(path28, "utf8"));
5722
5722
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
5723
- failWriteValidation(`${path27} must contain a JSON object`);
5723
+ failWriteValidation(`${path28} must contain a JSON object`);
5724
5724
  }
5725
5725
  return parsed;
5726
5726
  } catch (err) {
5727
5727
  if (err instanceof SyntaxError) {
5728
- failWriteValidation(`${path27} is not valid JSON: ${err.message}`);
5728
+ failWriteValidation(`${path28} is not valid JSON: ${err.message}`);
5729
5729
  }
5730
5730
  throw err;
5731
5731
  }
@@ -5847,10 +5847,10 @@ async function stageUpdate(kind, customerId, target, payload) {
5847
5847
  async function stageTarget(kind, customerId, target) {
5848
5848
  await stageGoogleOp({ kind, customerId, target });
5849
5849
  }
5850
- async function draftAction(path27, body) {
5850
+ async function draftAction(path28, body) {
5851
5851
  try {
5852
5852
  const chatId = requireChatId();
5853
- const response = await apiPost(path27, { chatId, ...body });
5853
+ const response = await apiPost(path28, { chatId, ...body });
5854
5854
  writeJsonEnvelope(response);
5855
5855
  } catch (err) {
5856
5856
  handleGoogleError(err);
@@ -9433,19 +9433,19 @@ function failWriteValidation2(message) {
9433
9433
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
9434
9434
  process.exit(1);
9435
9435
  }
9436
- function loadJsonFileArg2(path27) {
9437
- if (typeof path27 !== "string" || path27.length === 0) {
9436
+ function loadJsonFileArg2(path28) {
9437
+ if (typeof path28 !== "string" || path28.length === 0) {
9438
9438
  return {};
9439
9439
  }
9440
9440
  try {
9441
- const parsed = JSON.parse(readFileSync6(path27, "utf8"));
9441
+ const parsed = JSON.parse(readFileSync6(path28, "utf8"));
9442
9442
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
9443
- failWriteValidation2(`${path27} must contain a JSON object`);
9443
+ failWriteValidation2(`${path28} must contain a JSON object`);
9444
9444
  }
9445
9445
  return parsed;
9446
9446
  } catch (err) {
9447
9447
  if (err instanceof SyntaxError) {
9448
- failWriteValidation2(`${path27} is not valid JSON: ${err.message}`);
9448
+ failWriteValidation2(`${path28} is not valid JSON: ${err.message}`);
9449
9449
  }
9450
9450
  throw err;
9451
9451
  }
@@ -9530,15 +9530,15 @@ function parseLocaleFlag(value) {
9530
9530
  }
9531
9531
  return { language: match[1], country: match[2].toUpperCase() };
9532
9532
  }
9533
- function loadTargetingFileArg(path27) {
9534
- if (typeof path27 !== "string" || path27.length === 0) {
9533
+ function loadTargetingFileArg(path28) {
9534
+ if (typeof path28 !== "string" || path28.length === 0) {
9535
9535
  return void 0;
9536
9536
  }
9537
- const parsed = loadJsonFileArg2(path27);
9537
+ const parsed = loadJsonFileArg2(path28);
9538
9538
  const criteria = parsed.targetingCriteria ?? parsed;
9539
9539
  if (!criteria.include) {
9540
9540
  failWriteValidation2(
9541
- `${path27} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
9541
+ `${path28} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
9542
9542
  );
9543
9543
  }
9544
9544
  return criteria;
@@ -9573,14 +9573,14 @@ function parseCsvLine(line) {
9573
9573
  cells.push(current);
9574
9574
  return cells.map((cell) => cell.trim());
9575
9575
  }
9576
- function parseListFileArg(path27, maxRows) {
9577
- if (typeof path27 !== "string" || path27.length === 0) {
9576
+ function parseListFileArg(path28, maxRows) {
9577
+ if (typeof path28 !== "string" || path28.length === 0) {
9578
9578
  return void 0;
9579
9579
  }
9580
- const raw = readFileSync6(path27, "utf8");
9580
+ const raw = readFileSync6(path28, "utf8");
9581
9581
  const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
9582
9582
  if (lines.length < 2) {
9583
- failWriteValidation2(`${path27} needs a header row and at least one data row`);
9583
+ failWriteValidation2(`${path28} needs a header row and at least one data row`);
9584
9584
  }
9585
9585
  const columns = parseCsvLine(lines[0]).map((column) => column.trim());
9586
9586
  const rows = [];
@@ -9599,7 +9599,7 @@ function parseListFileArg(path27, maxRows) {
9599
9599
  }
9600
9600
  }
9601
9601
  if (rows.length > maxRows) {
9602
- failWriteValidation2(`${path27} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
9602
+ failWriteValidation2(`${path28} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
9603
9603
  }
9604
9604
  return { columns, rows };
9605
9605
  }
@@ -11699,11 +11699,11 @@ var updateStatusSchema = z14.enum(UPDATE_STATUSES);
11699
11699
  function currencyMinimums2(currencyCode) {
11700
11700
  return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
11701
11701
  }
11702
- function validateDailyBudgetFloor(money, ctx, path27) {
11702
+ function validateDailyBudgetFloor(money, ctx, path28) {
11703
11703
  if (money?.currencyCode) {
11704
11704
  const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
11705
11705
  if (Number(money.amount) < min) {
11706
- ctx.addIssue({ code: "custom", path: path27, message: `below the ${min} ${money.currencyCode} daily minimum` });
11706
+ ctx.addIssue({ code: "custom", path: path28, message: `below the ${min} ${money.currencyCode} daily minimum` });
11707
11707
  }
11708
11708
  }
11709
11709
  }
@@ -12335,19 +12335,19 @@ function failWriteValidation3(message) {
12335
12335
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
12336
12336
  process.exit(1);
12337
12337
  }
12338
- function loadJsonFileArg3(path27) {
12339
- if (typeof path27 !== "string" || path27.length === 0) {
12338
+ function loadJsonFileArg3(path28) {
12339
+ if (typeof path28 !== "string" || path28.length === 0) {
12340
12340
  return {};
12341
12341
  }
12342
12342
  try {
12343
- const parsed = JSON.parse(readFileSync8(path27, "utf8"));
12343
+ const parsed = JSON.parse(readFileSync8(path28, "utf8"));
12344
12344
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
12345
- failWriteValidation3(`${path27} must contain a JSON object`);
12345
+ failWriteValidation3(`${path28} must contain a JSON object`);
12346
12346
  }
12347
12347
  return parsed;
12348
12348
  } catch (err) {
12349
12349
  if (err instanceof SyntaxError) {
12350
- failWriteValidation3(`${path27} is not valid JSON: ${err.message}`);
12350
+ failWriteValidation3(`${path28} is not valid JSON: ${err.message}`);
12351
12351
  }
12352
12352
  throw err;
12353
12353
  }
@@ -15286,12 +15286,327 @@ Full guides: __tooling__/docs/tools/baker/ads-<platform>.md (google|meta|linkedi
15286
15286
  }
15287
15287
  });
15288
15288
 
15289
+ // src/commands/brand/index.ts
15290
+ import { defineCommand as defineCommand86 } from "citty";
15291
+
15292
+ // src/commands/brand/fonts.ts
15293
+ import { mkdir, readFile, writeFile } from "fs/promises";
15294
+ import path from "path";
15295
+ import { defineCommand as defineCommand85 } from "citty";
15296
+
15297
+ // src/engine/brand/fonts.ts
15298
+ var GOOGLE_CSS2 = "https://fonts.googleapis.com/css2";
15299
+ function parseGoogleFontFaces(css) {
15300
+ const faces = [];
15301
+ const re = /\/\*\s*([\w-]+)\s*\*\/\s*@font-face\s*\{([^}]*)\}/g;
15302
+ for (const match of String(css).matchAll(re)) {
15303
+ const subset = match[1] ?? "";
15304
+ const body = match[2] ?? "";
15305
+ const family = body.match(/font-family\s*:\s*['"]?([^'";]+)['"]?\s*;/i)?.[1]?.trim();
15306
+ const url = body.match(/src\s*:\s*url\(\s*['"]?([^'")]+)['"]?\s*\)/i)?.[1]?.trim();
15307
+ const weight = body.match(/font-weight\s*:\s*(\d{3})\s*;/i)?.[1];
15308
+ if (!family || !url || !weight) continue;
15309
+ faces.push({
15310
+ subset,
15311
+ family,
15312
+ style: body.match(/font-style\s*:\s*([\w-]+)\s*;/i)?.[1]?.trim() ?? "normal",
15313
+ weight: Number(weight),
15314
+ url,
15315
+ unicodeRange: body.match(/unicode-range\s*:\s*([^;]+);/i)?.[1]?.trim() ?? ""
15316
+ });
15317
+ }
15318
+ return faces;
15319
+ }
15320
+ function fontFileName(face) {
15321
+ const family = face.family.replace(/[^A-Za-z0-9]/g, "");
15322
+ const style = face.style === "italic" ? "italic" : "";
15323
+ return `${family}-${face.weight}${style}-${face.subset}.woff2`;
15324
+ }
15325
+ function renderFontFaceCss(faces, hrefFor) {
15326
+ return faces.map(
15327
+ (face) => [
15328
+ "@font-face {",
15329
+ ` font-family: '${face.family}';`,
15330
+ ` font-style: ${face.style};`,
15331
+ ` font-weight: ${face.weight};`,
15332
+ " font-display: swap;",
15333
+ ` src: url('${hrefFor(face)}') format('woff2');`,
15334
+ ...face.unicodeRange ? [` unicode-range: ${face.unicodeRange};`] : [],
15335
+ "}"
15336
+ ].join("\n")
15337
+ ).join("\n");
15338
+ }
15339
+ function parseWeightAxis(spec) {
15340
+ const axis = spec.match(/wght@([^&]+)/i)?.[1] ?? "";
15341
+ const weights = /* @__PURE__ */ new Set();
15342
+ for (const tuple of axis.split(";")) {
15343
+ const last = tuple.split(",").pop()?.trim() ?? "";
15344
+ const range = last.match(/^(\d{3})\.\.(\d{3})$/);
15345
+ if (range) {
15346
+ for (let w = Number(range[1]); w <= Number(range[2]); w += 100) weights.add(w);
15347
+ } else if (/^\d{3}$/.test(last)) {
15348
+ weights.add(Number(last));
15349
+ }
15350
+ }
15351
+ return [...weights].sort((a, b) => a - b);
15352
+ }
15353
+ function googleFontRequests(css) {
15354
+ const out = [];
15355
+ for (const link of String(css).matchAll(/fonts\.googleapis\.com\/[^\s"')]+/gi)) {
15356
+ for (const param of link[0].matchAll(/[?&]family=([^&]+)/gi)) {
15357
+ const [namePart, ...specParts] = (param[1] ?? "").split(":");
15358
+ const family = decodeURIComponent((namePart ?? "").replaceAll("+", " ")).trim();
15359
+ if (!family) continue;
15360
+ out.push({ family, weights: parseWeightAxis(specParts.join(":")) });
15361
+ }
15362
+ }
15363
+ return out;
15364
+ }
15365
+ function resolveFetchWeights(flag, declared) {
15366
+ if (flag) {
15367
+ return String(flag).split(",").map((w) => Number(w.trim())).filter((w) => Number.isInteger(w) && w >= 100 && w <= 900);
15368
+ }
15369
+ return declared?.length ? declared : [400];
15370
+ }
15371
+ function googleFontCssUrl(family, weights) {
15372
+ const name = family.trim().replace(/\s+/g, "+");
15373
+ const axis = [...new Set(weights)].sort((a, b) => a - b).join(";");
15374
+ const spec = axis ? `${name}:wght@${axis}` : name;
15375
+ return `${GOOGLE_CSS2}?family=${spec}&display=swap`;
15376
+ }
15377
+
15378
+ // src/commands/brand/fonts.ts
15379
+ var GLOBAL_CSS = path.join("src", "styles", "global.css");
15380
+ var FONTS_DIR = path.join("src", "brand", "fonts");
15381
+ var DEFAULT_SUBSETS = "latin";
15382
+ var BROWSER_UA = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/120.0 Safari/537.36";
15383
+ registerSchema({
15384
+ command: "brand.fonts.check",
15385
+ description: "Start here when a brand's fonts are 'confirmed' but nobody verified it. Asks Google Fonts what it ACTUALLY serves for every family/weight src/styles/global.css requests, and reports any the brand promises but Google will not serve.",
15386
+ args: {}
15387
+ });
15388
+ registerSchema({
15389
+ command: "brand.fonts.fetch",
15390
+ description: "Download a Google font into src/brand/fonts/ so the brand stops depending on a third party at page load, and print the @font-face block to paste above @theme in src/styles/global.css.",
15391
+ args: {
15392
+ family: { type: "string", description: 'Font family, e.g. "DM Sans"', required: true },
15393
+ weights: {
15394
+ type: "string",
15395
+ description: "Comma-separated weights (default: what global.css requests, else 400)",
15396
+ required: false
15397
+ },
15398
+ subsets: {
15399
+ type: "string",
15400
+ description: `Comma-separated unicode subsets (default: ${DEFAULT_SUBSETS})`,
15401
+ required: false
15402
+ }
15403
+ }
15404
+ });
15405
+ function fail(code, message, fix) {
15406
+ writeJson({ ok: false, error: { code, message, ...fix ? { fix } : {} } });
15407
+ process.exit(2);
15408
+ }
15409
+ async function fetchGoogleCss(url) {
15410
+ try {
15411
+ const res = await fetch(url, { headers: { "User-Agent": BROWSER_UA } });
15412
+ if (!res.ok) return null;
15413
+ return await res.text();
15414
+ } catch {
15415
+ return null;
15416
+ }
15417
+ }
15418
+ async function diagnoseUnserved(family) {
15419
+ const bare = await fetchGoogleCss(googleFontCssUrl(family, []));
15420
+ return bare === null ? "unknown-family" : "unavailable-weight";
15421
+ }
15422
+ async function readGlobalCss() {
15423
+ try {
15424
+ return await readFile(path.resolve(process.cwd(), GLOBAL_CSS), "utf8");
15425
+ } catch {
15426
+ return "";
15427
+ }
15428
+ }
15429
+ var fontsCheckCommand = defineCommand85({
15430
+ meta: {
15431
+ name: "check",
15432
+ description: "Verify the brand's fonts really exist. For every family/weight src/styles/global.css requests from Google Fonts, ask Google what it actually serves and report anything missing \u2014 a weight nobody serves renders as a synthesized fallback and looks off-brand everywhere."
15433
+ },
15434
+ async run() {
15435
+ const css = await readGlobalCss();
15436
+ const requests = googleFontRequests(css);
15437
+ if (requests.length === 0) {
15438
+ writeJson({
15439
+ ok: true,
15440
+ data: { families: [], selfHosted: true },
15441
+ hints: [
15442
+ `No Google Fonts requested in ${GLOBAL_CSS}. If the brand self-hosts, the offline validator already checks those files; nothing to verify against Google.`
15443
+ ]
15444
+ });
15445
+ return;
15446
+ }
15447
+ const families = [];
15448
+ const unavailable = [];
15449
+ for (const request of requests) {
15450
+ const url = googleFontCssUrl(request.family, request.weights);
15451
+ const served = await fetchGoogleCss(url);
15452
+ if (served === null) {
15453
+ const cause = await diagnoseUnserved(request.family);
15454
+ unavailable.push(
15455
+ cause === "unknown-family" ? `${request.family} (no such family on Google Fonts \u2014 check the exact spelling)` : `${request.family} ${request.weights.join("/")} (the family exists, these weights do not)`
15456
+ );
15457
+ families.push({
15458
+ family: request.family,
15459
+ requested: request.weights,
15460
+ served: [],
15461
+ missing: request.weights,
15462
+ cause
15463
+ });
15464
+ continue;
15465
+ }
15466
+ const faces = parseGoogleFontFaces(served);
15467
+ const servedWeights = [...new Set(faces.map((f) => f.weight))].sort((a, b) => a - b);
15468
+ const missing = request.weights.filter((w) => !servedWeights.includes(w));
15469
+ if (missing.length > 0) unavailable.push(`${request.family} ${missing.join("/")}`);
15470
+ families.push({
15471
+ family: request.family,
15472
+ requested: request.weights,
15473
+ served: servedWeights,
15474
+ missing,
15475
+ subsets: [...new Set(faces.map((f) => f.subset))]
15476
+ });
15477
+ }
15478
+ if (unavailable.length > 0) {
15479
+ fail("FONT_NOT_SERVED", `Google Fonts does not serve: ${unavailable.join("; ")}`, {
15480
+ action: `Correct the family name or weight in ${GLOBAL_CSS} and src/brand/BRAND.md, or run \`baker brand fonts fetch\` for a weight that does exist.`,
15481
+ explanation: "A requested weight Google does not serve is silently synthesized by the browser, so the page looks subtly wrong with nothing failing."
15482
+ });
15483
+ }
15484
+ writeJson({
15485
+ ok: true,
15486
+ data: { families },
15487
+ hints: [
15488
+ "Every requested family and weight is really served by Google.",
15489
+ `To stop depending on Google at page load, run \`baker brand fonts fetch "<family>"\` to self-host into ${FONTS_DIR}.`
15490
+ ]
15491
+ });
15492
+ }
15493
+ });
15494
+ var fontsFetchCommand = defineCommand85({
15495
+ meta: {
15496
+ name: "fetch",
15497
+ description: "Download a Google font into src/brand/fonts/ and print the @font-face block to paste above @theme in src/styles/global.css. Use when a brand font should be self-hosted rather than requested from Google on every page load."
15498
+ },
15499
+ args: {
15500
+ family: { type: "positional", required: true, description: 'Font family, e.g. "DM Sans"' },
15501
+ weights: { type: "string", description: "Comma-separated weights (default: what global.css requests, else 400)" },
15502
+ subsets: { type: "string", description: `Comma-separated unicode subsets (default: ${DEFAULT_SUBSETS})` }
15503
+ },
15504
+ async run({ args }) {
15505
+ const family = String(args.family).trim();
15506
+ if (!family) fail("INVALID_FAMILY", 'Pass a font family, e.g. `baker brand fonts fetch "DM Sans"`.');
15507
+ const css = await readGlobalCss();
15508
+ const declared = googleFontRequests(css).find((r) => r.family.toLowerCase() === family.toLowerCase());
15509
+ const weights = resolveFetchWeights(args.weights, declared?.weights);
15510
+ if (weights.length === 0) {
15511
+ fail("INVALID_WEIGHTS", `"${args.weights}" has no usable weight \u2014 pass whole hundreds, e.g. --weights 400,700.`);
15512
+ }
15513
+ const wanted = new Set(
15514
+ String(args.subsets ?? DEFAULT_SUBSETS).split(",").map((s) => s.trim()).filter(Boolean)
15515
+ );
15516
+ const servedCss = await fetchGoogleCss(googleFontCssUrl(family, weights));
15517
+ if (servedCss === null) {
15518
+ const cause = await diagnoseUnserved(family);
15519
+ fail(
15520
+ "FONT_NOT_SERVED",
15521
+ cause === "unknown-family" ? `Google Fonts has no family called "${family}".` : `"${family}" exists on Google Fonts but not at weight ${weights.join("/")}.`,
15522
+ cause === "unknown-family" ? {
15523
+ action: "Check the exact family name at fonts.google.com, then retry.",
15524
+ explanation: "Family names are case- and space-sensitive in the Google Fonts API."
15525
+ } : {
15526
+ action: `Run \`baker brand fonts check\` to see which weights this family really ships, then retry with --weights set to one of those.`,
15527
+ explanation: "Google rejects the whole request when one requested weight does not exist."
15528
+ }
15529
+ );
15530
+ }
15531
+ const faces = parseGoogleFontFaces(servedCss).filter((f) => wanted.has(f.subset));
15532
+ if (faces.length === 0) {
15533
+ fail("SUBSET_NOT_SERVED", `"${family}" has no ${[...wanted].join("/")} subset.`, {
15534
+ action: `Retry with --subsets set to one Google actually serves for this family.`,
15535
+ explanation: "Google splits each family into unicode subsets; not every family ships every subset."
15536
+ });
15537
+ }
15538
+ const fontsDir = path.resolve(process.cwd(), FONTS_DIR);
15539
+ await mkdir(fontsDir, { recursive: true });
15540
+ const downloaded = [];
15541
+ for (const face of faces) {
15542
+ const res = await fetch(face.url, { headers: { "User-Agent": BROWSER_UA } });
15543
+ if (!res.ok) continue;
15544
+ await writeFile(path.join(fontsDir, fontFileName(face)), Buffer.from(await res.arrayBuffer()));
15545
+ downloaded.push(face);
15546
+ }
15547
+ const written = downloaded.map(fontFileName);
15548
+ if (written.length === 0) {
15549
+ fail("DOWNLOAD_FAILED", `Could not download any file for "${family}".`, {
15550
+ action: "Retry; if it keeps failing, keep the Google @import and note it in BRAND.md.",
15551
+ explanation: "The font stylesheet resolved but the font files themselves did not download."
15552
+ });
15553
+ }
15554
+ const fontFace = renderFontFaceCss(downloaded, (f) => `/${FONTS_DIR}/${fontFileName(f)}`);
15555
+ const downloadedWeights = [...new Set(downloaded.map((f) => f.weight))].sort((a, b) => a - b);
15556
+ const incomplete = weights.filter((w) => !downloadedWeights.includes(w));
15557
+ writeJson({
15558
+ ok: true,
15559
+ data: { family, weights: downloadedWeights, files: written, fontFace },
15560
+ hints: [
15561
+ `Paste the \`fontFace\` block ABOVE the @theme block in ${GLOBAL_CSS}, then remove "${family}" from the Google @import so the page stops fetching it at load.`,
15562
+ `Record the exact weights (${downloadedWeights.join(", ")}) in src/brand/BRAND.md \u2014 a weight listed there but not downloaded renders as a synthesized fallback.`,
15563
+ ...incomplete.length > 0 ? [
15564
+ `PARTIAL: ${incomplete.join(", ")} did not download and is NOT in the \`fontFace\` block. Keep "${family}" in the Google @import for those weights, or re-run \`baker brand fonts fetch "${family}" --weights ${incomplete.join(",")}\`.`
15565
+ ] : [],
15566
+ "Licensing: Google Fonts are open-licensed and safe to self-host. A client's own commercial face is not \u2014 confirm before re-hosting one."
15567
+ ]
15568
+ });
15569
+ }
15570
+ });
15571
+ var fontsCommand = defineCommand85({
15572
+ meta: {
15573
+ name: "fonts",
15574
+ description: `Verify and self-host the brand's typefaces.
15575
+
15576
+ Start here: \`baker brand fonts check\` \u2014 confirms the fonts the brand claims are really served.
15577
+
15578
+ Subcommands:
15579
+ baker brand fonts check \u2014 ask Google what it actually serves for every family/weight global.css requests
15580
+ baker brand fonts fetch <family> \u2014 download a family into src/brand/fonts/ and print its @font-face block`
15581
+ },
15582
+ subCommands: {
15583
+ check: fontsCheckCommand,
15584
+ fetch: fontsFetchCommand
15585
+ }
15586
+ });
15587
+
15588
+ // src/commands/brand/index.ts
15589
+ var brandCommand = defineCommand86({
15590
+ meta: {
15591
+ name: "brand",
15592
+ description: `Brand asset verification for src/brand/.
15593
+
15594
+ Start here: \`baker brand fonts check\` after defining or editing the brand's typography.
15595
+
15596
+ Subcommands:
15597
+ baker brand fonts \u2014 verify the brand's typefaces really load, and self-host them into src/brand/fonts/`
15598
+ },
15599
+ subCommands: {
15600
+ fonts: fontsCommand
15601
+ }
15602
+ });
15603
+
15289
15604
  // src/commands/canvas/index.ts
15290
- import { defineCommand as defineCommand95 } from "citty";
15605
+ import { defineCommand as defineCommand97 } from "citty";
15291
15606
 
15292
15607
  // src/commands/canvas/catalog.ts
15293
- import { defineCommand as defineCommand85 } from "citty";
15294
- var catalogCommand = defineCommand85({
15608
+ import { defineCommand as defineCommand87 } from "citty";
15609
+ var catalogCommand = defineCommand87({
15295
15610
  meta: {
15296
15611
  name: "catalog",
15297
15612
  description: "Print the agent-facing node catalog (JSON Schema). Includes every registered node grouped by category."
@@ -15304,9 +15619,9 @@ var catalogCommand = defineCommand85({
15304
15619
  });
15305
15620
 
15306
15621
  // src/commands/canvas/critique.ts
15307
- import { readFile } from "fs/promises";
15308
- import path from "path";
15309
- import { defineCommand as defineCommand86 } from "citty";
15622
+ import { readFile as readFile2 } from "fs/promises";
15623
+ import path2 from "path";
15624
+ import { defineCommand as defineCommand88 } from "citty";
15310
15625
 
15311
15626
  // src/engine/scaffold/lib/critique.ts
15312
15627
  var VIBE_CUE = /\b(light|lighting|golden|moody|warm|cool|tone|grade|contrast|shadow|glow|rim|backlit|neon|soft)\b/i;
@@ -15426,15 +15741,15 @@ function critiqueCanvas(canvas) {
15426
15741
  }
15427
15742
 
15428
15743
  // src/commands/canvas/critique.ts
15429
- var critiqueCommand = defineCommand86({
15744
+ var critiqueCommand = defineCommand88({
15430
15745
  meta: {
15431
15746
  name: "critique",
15432
15747
  description: "Pre-spend creative critic (ADVISORY \u2014 never blocks). Scores a scaffolded creative BEFORE the billed render, so weak spots get fixed for free. A VIDEO creative is scored on hook strength, sound-off first-frame legibility, retention risk, and mechanism clarity; a STATIC ad on baked-text legibility risk, identity grounding, brand-type fidelity, and mechanism clarity. Ground a video hook against `baker winning-ads hooks` for a proven pattern."
15433
15748
  },
15434
15749
  args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
15435
15750
  async run({ args }) {
15436
- const filePath = path.resolve(String(args.file));
15437
- const raw = await readFile(filePath, "utf8");
15751
+ const filePath = path2.resolve(String(args.file));
15752
+ const raw = await readFile2(filePath, "utf8");
15438
15753
  let parsed;
15439
15754
  try {
15440
15755
  parsed = JSON.parse(raw);
@@ -15465,12 +15780,12 @@ var critiqueCommand = defineCommand86({
15465
15780
 
15466
15781
  // src/commands/canvas/inspect.ts
15467
15782
  import { execFile } from "child_process";
15468
- import { readdir, readFile as readFile2, stat } from "fs/promises";
15469
- import path2 from "path";
15783
+ import { readdir, readFile as readFile3, stat } from "fs/promises";
15784
+ import path3 from "path";
15470
15785
  import { promisify } from "util";
15471
- import { defineCommand as defineCommand87 } from "citty";
15786
+ import { defineCommand as defineCommand89 } from "citty";
15472
15787
  var execFileAsync = promisify(execFile);
15473
- var inspectCommand = defineCommand87({
15788
+ var inspectCommand = defineCommand89({
15474
15789
  meta: {
15475
15790
  name: "inspect",
15476
15791
  description: "Dump a one-page summary of a canvas run: per-node duration + cache status, list of output files in the run dir, and optionally three thumbnail frames per video output. Pass either a run_id (resolved against --outputs-dir) or an absolute run directory."
@@ -15484,7 +15799,7 @@ var inspectCommand = defineCommand87({
15484
15799
  }
15485
15800
  },
15486
15801
  async run({ args }) {
15487
- const outputsDir = path2.resolve(String(args["outputs-dir"] ?? "canvas"));
15802
+ const outputsDir = path3.resolve(String(args["outputs-dir"] ?? "canvas"));
15488
15803
  const runArg = String(args.run);
15489
15804
  const runDir = await resolveRunDir(runArg, outputsDir);
15490
15805
  const manifest = await loadManifest(runDir);
@@ -15496,7 +15811,7 @@ var inspectCommand = defineCommand87({
15496
15811
  }
15497
15812
  const summary = {
15498
15813
  ok: true,
15499
- run_id: manifest.run_id ?? path2.basename(runDir),
15814
+ run_id: manifest.run_id ?? path3.basename(runDir),
15500
15815
  run_dir: runDir,
15501
15816
  stats: manifest.stats ?? null,
15502
15817
  output: manifest.output ?? null,
@@ -15509,20 +15824,20 @@ var inspectCommand = defineCommand87({
15509
15824
  }
15510
15825
  });
15511
15826
  async function resolveRunDir(run, outputsDir) {
15512
- if (path2.isAbsolute(run)) {
15827
+ if (path3.isAbsolute(run)) {
15513
15828
  const s2 = await stat(run).catch(() => null);
15514
15829
  if (s2?.isDirectory()) return run;
15515
15830
  throw new Error(`inspect: ${run} is not a directory`);
15516
15831
  }
15517
- const candidate = path2.join(outputsDir, run);
15832
+ const candidate = path3.join(outputsDir, run);
15518
15833
  const s = await stat(candidate).catch(() => null);
15519
15834
  if (s?.isDirectory()) return candidate;
15520
15835
  throw new Error(`inspect: no run directory at ${candidate}`);
15521
15836
  }
15522
15837
  async function loadManifest(runDir) {
15523
- const manifestPath = path2.join(runDir, "manifest.json");
15838
+ const manifestPath = path3.join(runDir, "manifest.json");
15524
15839
  try {
15525
- const raw = await readFile2(manifestPath, "utf-8");
15840
+ const raw = await readFile3(manifestPath, "utf-8");
15526
15841
  return JSON.parse(raw);
15527
15842
  } catch {
15528
15843
  return {};
@@ -15532,7 +15847,7 @@ async function listRunFiles(runDir) {
15532
15847
  const out = [];
15533
15848
  const names = await readdir(runDir);
15534
15849
  for (const name of names) {
15535
- const abs = path2.join(runDir, name);
15850
+ const abs = path3.join(runDir, name);
15536
15851
  const s = await stat(abs).catch(() => null);
15537
15852
  if (!s?.isFile()) continue;
15538
15853
  out.push({ name, path: abs, size: s.size });
@@ -15577,13 +15892,13 @@ async function probeDuration(filePath) {
15577
15892
  }
15578
15893
 
15579
15894
  // src/commands/canvas/rerun.ts
15580
- import path13 from "path";
15581
- import { defineCommand as defineCommand89 } from "citty";
15895
+ import path14 from "path";
15896
+ import { defineCommand as defineCommand91 } from "citty";
15582
15897
 
15583
15898
  // src/commands/canvas/run.ts
15584
- import { readFile as readFile9 } from "fs/promises";
15585
- import path12 from "path";
15586
- import { defineCommand as defineCommand88 } from "citty";
15899
+ import { readFile as readFile10 } from "fs/promises";
15900
+ import path13 from "path";
15901
+ import { defineCommand as defineCommand90 } from "citty";
15587
15902
 
15588
15903
  // src/commands/canvas/placeholders.ts
15589
15904
  function unsuppliedPlaceholderAssets(canvas) {
@@ -15602,7 +15917,7 @@ function unsuppliedPlaceholderAssets(canvas) {
15602
15917
  }
15603
15918
 
15604
15919
  // src/commands/canvas/resolve-paths.ts
15605
- import path3 from "path";
15920
+ import path4 from "path";
15606
15921
  function resolveRelativeCanvasPaths(canvas, baseDir) {
15607
15922
  if (!canvas || typeof canvas !== "object") return canvas;
15608
15923
  const c = canvas;
@@ -15615,24 +15930,24 @@ function resolveNode(node, baseDir) {
15615
15930
  const params = n.params;
15616
15931
  if (!params || typeof params !== "object") return node;
15617
15932
  if (n.type === "ingest" && params.source === "path" && isResolvableRelative(params.path)) {
15618
- return { ...node, params: { ...params, path: path3.resolve(baseDir, params.path) } };
15933
+ return { ...node, params: { ...params, path: path4.resolve(baseDir, params.path) } };
15619
15934
  }
15620
15935
  if (n.type === "hyperframe_render" && isResolvableRelative(params.composition)) {
15621
- return { ...node, params: { ...params, composition: path3.resolve(baseDir, params.composition) } };
15936
+ return { ...node, params: { ...params, composition: path4.resolve(baseDir, params.composition) } };
15622
15937
  }
15623
15938
  return node;
15624
15939
  }
15625
15940
  function isResolvableRelative(value) {
15626
- return typeof value === "string" && value.length > 0 && !value.includes("[TODO") && !looksLikeHttpUrl(value) && !path3.isAbsolute(value);
15941
+ return typeof value === "string" && value.length > 0 && !value.includes("[TODO") && !looksLikeHttpUrl(value) && !path4.isAbsolute(value);
15627
15942
  }
15628
15943
 
15629
15944
  // src/commands/canvas/source-version.ts
15630
- import { readFile as readFile4 } from "fs/promises";
15631
- import path5 from "path";
15945
+ import { readFile as readFile5 } from "fs/promises";
15946
+ import path6 from "path";
15632
15947
 
15633
15948
  // src/commands/canvas/scene-files.ts
15634
- import { mkdir, readFile as readFile3, readdir as readdir2, rm, writeFile } from "fs/promises";
15635
- import path4 from "path";
15949
+ import { mkdir as mkdir2, readFile as readFile4, readdir as readdir2, rm, writeFile as writeFile2 } from "fs/promises";
15950
+ import path5 from "path";
15636
15951
  var SCENES_DIR = "scenes";
15637
15952
  var GLOBAL_PROMPT_FILE = "prompt.json";
15638
15953
  var REBUILD_FILE = "prompt.rebuild.json";
@@ -15649,19 +15964,19 @@ function splitBlueprint(blueprint) {
15649
15964
  }
15650
15965
  async function writeSceneFiles(outDir, blueprint) {
15651
15966
  const { global, scenes } = splitBlueprint(blueprint);
15652
- await writeFile(path4.join(outDir, GLOBAL_PROMPT_FILE), `${JSON.stringify(global, null, 2)}
15967
+ await writeFile2(path5.join(outDir, GLOBAL_PROMPT_FILE), `${JSON.stringify(global, null, 2)}
15653
15968
  `, "utf8");
15654
- const scenesDir = path4.join(outDir, SCENES_DIR);
15655
- await mkdir(scenesDir, { recursive: true });
15969
+ const scenesDir = path5.join(outDir, SCENES_DIR);
15970
+ await mkdir2(scenesDir, { recursive: true });
15656
15971
  const written = /* @__PURE__ */ new Set();
15657
15972
  for (let i = 0; i < scenes.length; i++) {
15658
15973
  const name = sceneFileName(i, scenes.length);
15659
15974
  written.add(name);
15660
- await writeFile(path4.join(scenesDir, name), `${JSON.stringify(scenes[i], null, 2)}
15975
+ await writeFile2(path5.join(scenesDir, name), `${JSON.stringify(scenes[i], null, 2)}
15661
15976
  `, "utf8");
15662
15977
  }
15663
15978
  for (const name of await listSceneFileNames(scenesDir)) {
15664
- if (!written.has(name)) await rm(path4.join(scenesDir, name), { force: true });
15979
+ if (!written.has(name)) await rm(path5.join(scenesDir, name), { force: true });
15665
15980
  }
15666
15981
  }
15667
15982
  async function listSceneFileNames(scenesDir) {
@@ -15674,17 +15989,17 @@ async function listSceneFileNames(scenesDir) {
15674
15989
  return entries.filter((n) => /^s\d+\.json$/.test(n)).sort(bySceneIndex);
15675
15990
  }
15676
15991
  async function listSceneFiles(creativeDir) {
15677
- const scenesDir = path4.join(creativeDir, SCENES_DIR);
15678
- return (await listSceneFileNames(scenesDir)).map((n) => path4.join(scenesDir, n));
15992
+ const scenesDir = path5.join(creativeDir, SCENES_DIR);
15993
+ return (await listSceneFileNames(scenesDir)).map((n) => path5.join(scenesDir, n));
15679
15994
  }
15680
15995
  async function reassembleBlueprint(creativeDir) {
15681
15996
  const files = await listSceneFiles(creativeDir);
15682
15997
  if (files.length === 0) return null;
15683
- const globalRaw = await readFile3(path4.join(creativeDir, GLOBAL_PROMPT_FILE), "utf8");
15998
+ const globalRaw = await readFile4(path5.join(creativeDir, GLOBAL_PROMPT_FILE), "utf8");
15684
15999
  const global = JSON.parse(globalRaw);
15685
16000
  const scenes = [];
15686
16001
  for (const file of files) {
15687
- scenes.push(JSON.parse(await readFile3(file, "utf8")));
16002
+ scenes.push(JSON.parse(await readFile4(file, "utf8")));
15688
16003
  }
15689
16004
  return { ...global, scenes };
15690
16005
  }
@@ -15698,15 +16013,15 @@ function bySceneIndex(a, b) {
15698
16013
  async function computeSourceSha(canvasPath) {
15699
16014
  let canvasBytes;
15700
16015
  try {
15701
- canvasBytes = await readFile4(canvasPath);
16016
+ canvasBytes = await readFile5(canvasPath);
15702
16017
  } catch {
15703
16018
  return void 0;
15704
16019
  }
15705
- const canvasDir = path5.dirname(canvasPath);
15706
- const promptPath = path5.join(canvasDir, "prompt.json");
16020
+ const canvasDir = path6.dirname(canvasPath);
16021
+ const promptPath = path6.join(canvasDir, "prompt.json");
15707
16022
  let promptBytes;
15708
16023
  try {
15709
- promptBytes = await readFile4(promptPath);
16024
+ promptBytes = await readFile5(promptPath);
15710
16025
  } catch {
15711
16026
  promptBytes = Buffer.alloc(0);
15712
16027
  }
@@ -15719,7 +16034,7 @@ async function computeSourceSha(canvasPath) {
15719
16034
  for (const sceneFile of await listSceneFiles(canvasDir)) {
15720
16035
  let sceneBytes;
15721
16036
  try {
15722
- sceneBytes = await readFile4(sceneFile);
16037
+ sceneBytes = await readFile5(sceneFile);
15723
16038
  } catch {
15724
16039
  sceneBytes = Buffer.alloc(0);
15725
16040
  }
@@ -15729,8 +16044,8 @@ async function computeSourceSha(canvasPath) {
15729
16044
  }
15730
16045
 
15731
16046
  // src/commands/canvas/scene-projection.ts
15732
- import { readFile as readFile5 } from "fs/promises";
15733
- import path6 from "path";
16047
+ import { readFile as readFile6 } from "fs/promises";
16048
+ import path7 from "path";
15734
16049
 
15735
16050
  // src/engine/scaffold/video.ts
15736
16051
  import { toCardinal as nwAr } from "n2words/ar-SA";
@@ -18989,12 +19304,12 @@ function nonPromptParamsDiverge(live = {}, rebuilt = {}) {
18989
19304
  return false;
18990
19305
  }
18991
19306
  async function syncSceneNodeParams(canvas, canvasPath, log) {
18992
- const creativeDir = path6.dirname(canvasPath);
19307
+ const creativeDir = path7.dirname(canvasPath);
18993
19308
  const blueprint = await reassembleBlueprint(creativeDir);
18994
19309
  if (!blueprint) return "not_applicable";
18995
19310
  let rebuildRaw;
18996
19311
  try {
18997
- rebuildRaw = await readFile5(path6.join(creativeDir, REBUILD_FILE), "utf8");
19312
+ rebuildRaw = await readFile6(path7.join(creativeDir, REBUILD_FILE), "utf8");
18998
19313
  } catch {
18999
19314
  return "not_applicable";
19000
19315
  }
@@ -19035,7 +19350,7 @@ async function syncSceneNodeParams(canvas, canvasPath, log) {
19035
19350
  }
19036
19351
 
19037
19352
  // src/commands/canvas/style-projection.ts
19038
- import { readFile as readFile6, writeFile as writeFile2 } from "fs/promises";
19353
+ import { readFile as readFile7, writeFile as writeFile3 } from "fs/promises";
19039
19354
  function findBlueprintProjection(canvas) {
19040
19355
  if (!canvas || typeof canvas !== "object") return null;
19041
19356
  const nodes = canvas.nodes;
@@ -19063,10 +19378,10 @@ function renderStyleProjectionFromValue(blueprint) {
19063
19378
  async function syncStyleProjection(canvas, log) {
19064
19379
  const pair = findBlueprintProjection(canvas);
19065
19380
  if (!pair) return "not_applicable";
19066
- const rendered = renderStyleProjection(await readFile6(pair.promptPath, "utf8"));
19067
- const current = await readFile6(pair.stylePath, "utf8").catch(() => null);
19381
+ const rendered = renderStyleProjection(await readFile7(pair.promptPath, "utf8"));
19382
+ const current = await readFile7(pair.stylePath, "utf8").catch(() => null);
19068
19383
  if (current === rendered) return "up_to_date";
19069
- await writeFile2(pair.stylePath, rendered, "utf8");
19384
+ await writeFile3(pair.stylePath, rendered, "utf8");
19070
19385
  log(
19071
19386
  "[style] prompt.style.json regenerated from prompt.json \u2014 every frame's shared ad spec changed; affected image frames will re-bill on the next run"
19072
19387
  );
@@ -19126,13 +19441,13 @@ ${body}` : header || body || compactJson(record);
19126
19441
  }
19127
19442
 
19128
19443
  // src/commands/canvas/run-record.ts
19129
- import path7 from "path";
19444
+ import path8 from "path";
19130
19445
  var MAX_RUN_NODES = 200;
19131
19446
  var MAX_OUTPUTS_PER_NODE = 10;
19132
19447
  var MAX_FINAL_OUTPUTS = 10;
19133
19448
  var MAX_CREATIVE_SLUG_LENGTH = 100;
19134
19449
  function creativeSlugFromCanvasPath(filePath) {
19135
- const normalized = filePath.split(path7.sep).join("/");
19450
+ const normalized = filePath.split(path8.sep).join("/");
19136
19451
  const match = normalized.match(/(?:^|\/)src\/creatives\/([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\//);
19137
19452
  const slug = match?.[1] ?? null;
19138
19453
  return slug && slug.length <= MAX_CREATIVE_SLUG_LENGTH ? slug : null;
@@ -19420,7 +19735,7 @@ var RunRecordPoster = class {
19420
19735
 
19421
19736
  // src/commands/canvas/run-retention.ts
19422
19737
  import { rm as rm2 } from "fs/promises";
19423
- import path8 from "path";
19738
+ import path9 from "path";
19424
19739
  function runDirsToPrune(entries, keep, currentRunId) {
19425
19740
  const runs = entries.filter((e) => /^r_[0-9A-Za-z]+$/.test(e) && e !== currentRunId).sort();
19426
19741
  if (keep <= 0) return runs;
@@ -19437,7 +19752,7 @@ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
19437
19752
  const toPrune = runDirsToPrune(entries, keep, currentRunId);
19438
19753
  if (toPrune.length === 0) return;
19439
19754
  for (const dir of toPrune) {
19440
- await rm2(path8.join(outputsDir, dir), { recursive: true, force: true }).catch(
19755
+ await rm2(path9.join(outputsDir, dir), { recursive: true, force: true }).catch(
19441
19756
  (e) => log(`[prune ] could not remove ${dir}: ${e.message}`)
19442
19757
  );
19443
19758
  }
@@ -19445,13 +19760,13 @@ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
19445
19760
  }
19446
19761
 
19447
19762
  // src/commands/canvas/dirty-marker.ts
19448
- import { mkdir as mkdir2, readdir as readdir3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
19449
- import path9 from "path";
19763
+ import { mkdir as mkdir3, readdir as readdir3, rm as rm3, writeFile as writeFile4 } from "fs/promises";
19764
+ import path10 from "path";
19450
19765
  function creativeDirtyDir(base) {
19451
- return base ?? path9.resolve("canvas", ".dirty");
19766
+ return base ?? path10.resolve("canvas", ".dirty");
19452
19767
  }
19453
19768
  function dirtyMarkerFile(slug, base) {
19454
- return path9.join(creativeDirtyDir(base), `${slug}.json`);
19769
+ return path10.join(creativeDirtyDir(base), `${slug}.json`);
19455
19770
  }
19456
19771
  async function clearCreativeDirty(slug, base) {
19457
19772
  try {
@@ -19461,18 +19776,18 @@ async function clearCreativeDirty(slug, base) {
19461
19776
  }
19462
19777
 
19463
19778
  // src/commands/canvas/run-resume.ts
19464
- import { mkdir as mkdir3, readFile as readFile7, rm as rm4, writeFile as writeFile4 } from "fs/promises";
19465
- import path10 from "path";
19779
+ import { mkdir as mkdir4, readFile as readFile8, rm as rm4, writeFile as writeFile5 } from "fs/promises";
19780
+ import path11 from "path";
19466
19781
  function markerKey(canvasPath) {
19467
19782
  const slug = creativeSlugFromCanvasPath(canvasPath);
19468
- const identity = slug ?? path10.relative(process.cwd(), path10.resolve(canvasPath));
19783
+ const identity = slug ?? path11.relative(process.cwd(), path11.resolve(canvasPath));
19469
19784
  return sha256Hex(Buffer.from(identity)).slice(0, 32);
19470
19785
  }
19471
19786
  function legacyMarkerKey(canvasPath) {
19472
- return sha256Hex(Buffer.from(path10.resolve(canvasPath))).slice(0, 32);
19787
+ return sha256Hex(Buffer.from(path11.resolve(canvasPath))).slice(0, 32);
19473
19788
  }
19474
19789
  function markerFile(outputsDir, key) {
19475
- return path10.join(outputsDir, ".inflight", `${key}.json`);
19790
+ return path11.join(outputsDir, ".inflight", `${key}.json`);
19476
19791
  }
19477
19792
  var REMOTE_ADOPT_STALE_MS = 12e4;
19478
19793
  function classifyRemoteRun(run, now) {
@@ -19508,7 +19823,7 @@ async function resolveRunId(opts) {
19508
19823
  async function readMarkerRunId(outputsDir, canvasPath) {
19509
19824
  for (const key of [markerKey(canvasPath), legacyMarkerKey(canvasPath)]) {
19510
19825
  try {
19511
- const raw = await readFile7(markerFile(outputsDir, key), "utf8");
19826
+ const raw = await readFile8(markerFile(outputsDir, key), "utf8");
19512
19827
  const parsed = JSON.parse(raw);
19513
19828
  if (typeof parsed.runId === "string" && parsed.runId.length > 0) return parsed.runId;
19514
19829
  } catch {
@@ -19519,8 +19834,8 @@ async function readMarkerRunId(outputsDir, canvasPath) {
19519
19834
  async function markRunInFlight(outputsDir, canvasPath, runId) {
19520
19835
  try {
19521
19836
  const file = markerFile(outputsDir, markerKey(canvasPath));
19522
- await mkdir3(path10.dirname(file), { recursive: true });
19523
- await writeFile4(file, JSON.stringify({ runId, canvasPath: path10.resolve(canvasPath), startedAt: Date.now() }));
19837
+ await mkdir4(path11.dirname(file), { recursive: true });
19838
+ await writeFile5(file, JSON.stringify({ runId, canvasPath: path11.resolve(canvasPath), startedAt: Date.now() }));
19524
19839
  } catch {
19525
19840
  }
19526
19841
  }
@@ -19534,8 +19849,8 @@ async function clearRunMarker(outputsDir, canvasPath) {
19534
19849
  }
19535
19850
 
19536
19851
  // src/commands/canvas/run-snapshot.ts
19537
- import { mkdir as mkdir4, readdir as readdir4, readFile as readFile8, stat as stat2, writeFile as writeFile5 } from "fs/promises";
19538
- import path11 from "path";
19852
+ import { mkdir as mkdir5, readdir as readdir4, readFile as readFile9, stat as stat2, writeFile as writeFile6 } from "fs/promises";
19853
+ import path12 from "path";
19539
19854
  var SNAPSHOT_SCHEMA = "baker-canvas-snapshot/1";
19540
19855
  var MAX_SNAPSHOT_FILE_BYTES = 32 * 1024 * 1024;
19541
19856
  var EXT_TO_MIME = {
@@ -19562,11 +19877,11 @@ var EXT_TO_MIME = {
19562
19877
  woff2: "font/woff2"
19563
19878
  };
19564
19879
  function mimeForFile(filePath) {
19565
- const ext = path11.extname(filePath).slice(1).toLowerCase();
19880
+ const ext = path12.extname(filePath).slice(1).toLowerCase();
19566
19881
  return EXT_TO_MIME[ext] ?? "application/octet-stream";
19567
19882
  }
19568
19883
  function toPosix(p) {
19569
- return p.split(path11.sep).join("/");
19884
+ return p.split(path12.sep).join("/");
19570
19885
  }
19571
19886
  function localPathRefsFromCanvas(parsed) {
19572
19887
  const nodes = parsed?.nodes;
@@ -19591,37 +19906,37 @@ function isSnapshotablePath(value) {
19591
19906
  async function sourceRefsToSnapshot(canvasDir, parsed) {
19592
19907
  const refs = new Set(localPathRefsFromCanvas(parsed));
19593
19908
  for (const sceneFile of await listSceneFiles(canvasDir)) {
19594
- refs.add(toPosix(path11.relative(canvasDir, sceneFile)));
19909
+ refs.add(toPosix(path12.relative(canvasDir, sceneFile)));
19595
19910
  }
19596
19911
  refs.add(REBUILD_FILE);
19597
19912
  return [...refs];
19598
19913
  }
19599
19914
  async function uploadRunSnapshot(client, opts) {
19600
19915
  try {
19601
- const canvasDir = path11.dirname(opts.canvasPath);
19916
+ const canvasDir = path12.dirname(opts.canvasPath);
19602
19917
  const put = (bytes, mime) => putContentAddressed(client, bytes, mime, opts.signal);
19603
19918
  const canvasBytes = Buffer.from(opts.raw);
19604
19919
  const canvasUpload = await put(canvasBytes, "application/json");
19605
19920
  const files = [];
19606
19921
  const skipped = [];
19607
19922
  for (const refPath of await sourceRefsToSnapshot(canvasDir, opts.parsed)) {
19608
- const abs = path11.isAbsolute(refPath) ? refPath : path11.resolve(canvasDir, refPath);
19923
+ const abs = path12.isAbsolute(refPath) ? refPath : path12.resolve(canvasDir, refPath);
19609
19924
  let st;
19610
19925
  try {
19611
19926
  st = await stat2(abs);
19612
19927
  } catch {
19613
- skipped.push({ path: toPosix(path11.relative(canvasDir, abs)), reason: "missing" });
19928
+ skipped.push({ path: toPosix(path12.relative(canvasDir, abs)), reason: "missing" });
19614
19929
  continue;
19615
19930
  }
19616
19931
  const fileList = st.isDirectory() ? await listFilesRecursive(abs) : [abs];
19617
19932
  for (const file of fileList) {
19618
- const rel = toPosix(path11.relative(canvasDir, file));
19933
+ const rel = toPosix(path12.relative(canvasDir, file));
19619
19934
  const size = (await stat2(file)).size;
19620
19935
  if (size > MAX_SNAPSHOT_FILE_BYTES) {
19621
19936
  skipped.push({ path: rel, reason: `too large (${size} bytes)` });
19622
19937
  continue;
19623
19938
  }
19624
- const bytes = await readFile8(file);
19939
+ const bytes = await readFile9(file);
19625
19940
  const upload = await put(bytes, mimeForFile(file));
19626
19941
  files.push({ path: rel, sha256: upload.sha256, url: upload.url });
19627
19942
  }
@@ -19630,7 +19945,7 @@ async function uploadRunSnapshot(client, opts) {
19630
19945
  schema: SNAPSHOT_SCHEMA,
19631
19946
  creativeSlug: opts.creativeSlug,
19632
19947
  canvasSha: canvasUpload.sha256,
19633
- canvas: { path: path11.basename(opts.canvasPath), sha256: canvasUpload.sha256, url: canvasUpload.url },
19948
+ canvas: { path: path12.basename(opts.canvasPath), sha256: canvasUpload.sha256, url: canvasUpload.url },
19634
19949
  files,
19635
19950
  skipped: skipped.length > 0 ? skipped : void 0
19636
19951
  };
@@ -19657,7 +19972,7 @@ async function putContentAddressed(client, bytes, mime, signal) {
19657
19972
  }
19658
19973
  async function listFilesRecursive(dir) {
19659
19974
  const entries = await readdir4(dir, { recursive: true, withFileTypes: true });
19660
- return entries.filter((d) => d.isFile()).map((d) => path11.join(d.parentPath, d.name));
19975
+ return entries.filter((d) => d.isFile()).map((d) => path12.join(d.parentPath, d.name));
19661
19976
  }
19662
19977
  var SnapshotConflictError = class extends Error {
19663
19978
  conflicts;
@@ -19669,19 +19984,19 @@ var SnapshotConflictError = class extends Error {
19669
19984
  };
19670
19985
  async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
19671
19986
  const entries = [manifest.canvas, ...manifest.files];
19672
- const resolvedTarget = path11.resolve(targetDir);
19987
+ const resolvedTarget = path12.resolve(targetDir);
19673
19988
  const planned = [];
19674
19989
  const conflicts = [];
19675
19990
  const upToDate = [];
19676
19991
  for (const entry of entries) {
19677
- if (path11.isAbsolute(entry.path) || entry.path.split("/").includes("..")) {
19992
+ if (path12.isAbsolute(entry.path) || entry.path.split("/").includes("..")) {
19678
19993
  throw new Error(`snapshot entry escapes the creative directory: ${entry.path}`);
19679
19994
  }
19680
- const target = path11.resolve(resolvedTarget, entry.path);
19681
- if (target !== resolvedTarget && !target.startsWith(resolvedTarget + path11.sep)) {
19995
+ const target = path12.resolve(resolvedTarget, entry.path);
19996
+ if (target !== resolvedTarget && !target.startsWith(resolvedTarget + path12.sep)) {
19682
19997
  throw new Error(`snapshot entry escapes the creative directory: ${entry.path}`);
19683
19998
  }
19684
- const existing = await readFile8(target).catch(() => null);
19999
+ const existing = await readFile9(target).catch(() => null);
19685
20000
  if (existing) {
19686
20001
  if (sha256Hex(existing) === entry.sha256) {
19687
20002
  upToDate.push(entry.path);
@@ -19703,15 +20018,15 @@ async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
19703
20018
  if (sha256Hex(bytes) !== entry.sha256) {
19704
20019
  throw new Error(`snapshot download for ${entry.path} does not match its recorded sha256`);
19705
20020
  }
19706
- await mkdir4(path11.dirname(target), { recursive: true });
19707
- await writeFile5(target, bytes);
20021
+ await mkdir5(path12.dirname(target), { recursive: true });
20022
+ await writeFile6(target, bytes);
19708
20023
  restored.push(entry.path);
19709
20024
  }
19710
- return { canvasPath: path11.resolve(resolvedTarget, manifest.canvas.path), restored, upToDate };
20025
+ return { canvasPath: path12.resolve(resolvedTarget, manifest.canvas.path), restored, upToDate };
19711
20026
  }
19712
20027
 
19713
20028
  // src/commands/canvas/run.ts
19714
- var runCommand = defineCommand88({
20029
+ var runCommand = defineCommand90({
19715
20030
  meta: { name: "run", description: "Validate and execute a canvas JSON file." },
19716
20031
  args: {
19717
20032
  file: { type: "positional", required: true, description: "Path to canvas JSON" },
@@ -19787,8 +20102,8 @@ function resolveMaxCredits(...candidates) {
19787
20102
  return void 0;
19788
20103
  }
19789
20104
  async function executeCanvasRun(opts) {
19790
- const filePath = path12.resolve(opts.file);
19791
- const raw = await readFile9(filePath, "utf8");
20105
+ const filePath = path13.resolve(opts.file);
20106
+ const raw = await readFile10(filePath, "utf8");
19792
20107
  let parsed;
19793
20108
  try {
19794
20109
  parsed = JSON.parse(raw);
@@ -19800,7 +20115,7 @@ async function executeCanvasRun(opts) {
19800
20115
  }
19801
20116
  const attemptedSlug = creativeSlugFromCanvasPath(filePath);
19802
20117
  if (attemptedSlug) await clearCreativeDirty(attemptedSlug);
19803
- parsed = resolveRelativeCanvasPaths(parsed, path12.dirname(filePath));
20118
+ parsed = resolveRelativeCanvasPaths(parsed, path13.dirname(filePath));
19804
20119
  try {
19805
20120
  await syncStyleProjection(parsed, (line) => process.stderr.write(`${line}
19806
20121
  `));
@@ -19906,7 +20221,7 @@ async function executeCanvasRun(opts) {
19906
20221
  const canvasSha = sha256Hex(Buffer.from(raw));
19907
20222
  const creativeSlug = creativeSlugFromCanvasPath(filePath) ?? void 0;
19908
20223
  const client = opts.record === false ? null : buildBackendClient();
19909
- const outputsDir = opts.outputsDir ? path12.resolve(opts.outputsDir) : path12.resolve("canvas");
20224
+ const outputsDir = opts.outputsDir ? path13.resolve(opts.outputsDir) : path13.resolve("canvas");
19910
20225
  const { runId, resumed, source, concurrentRunId } = await resolveRunId({
19911
20226
  explicitRunId: opts.runId,
19912
20227
  fresh: opts.fresh === true,
@@ -19940,7 +20255,7 @@ async function executeCanvasRun(opts) {
19940
20255
  const canvasSnapshotUrl = client && creativeSlug ? await uploadRunSnapshot(client, { canvasPath: filePath, raw, creativeSlug, parsed }) ?? void 0 : void 0;
19941
20256
  const recordMeta = {
19942
20257
  creativeSlug,
19943
- canvasPath: path12.relative(process.cwd(), filePath) || void 0,
20258
+ canvasPath: path13.relative(process.cwd(), filePath) || void 0,
19944
20259
  canvasSha,
19945
20260
  // The fingerprint the dashboard compares against the current source to flag
19946
20261
  // "edited since last render". Computed from the on-disk canvas.json +
@@ -20081,7 +20396,7 @@ async function postInitialRunRecord(client, payload) {
20081
20396
 
20082
20397
  // src/commands/canvas/rerun.ts
20083
20398
  var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
20084
- var rerunCommand = defineCommand89({
20399
+ var rerunCommand = defineCommand91({
20085
20400
  meta: {
20086
20401
  name: "rerun",
20087
20402
  description: "Re-run a creative's latest recorded canvas. Restores its definition files from run history first, so it works in a fresh workspace that never had the source chat's files \u2014 an interrupted run resumes (in-flight jobs re-attach), a completed one re-renders from the cache."
@@ -20110,24 +20425,24 @@ var rerunCommand = defineCommand89({
20110
20425
  async run({ args }) {
20111
20426
  const slug = String(args.slug);
20112
20427
  if (!SLUG_PATTERN.test(slug)) {
20113
- fail("invalid_slug", `"${slug}" is not a valid creative slug`);
20428
+ fail2("invalid_slug", `"${slug}" is not a valid creative slug`);
20114
20429
  }
20115
20430
  const client = buildBackendClient();
20116
20431
  if (!client) {
20117
- fail("missing_credentials", "rerun needs backend credentials (BAKER_API_URL / BAKER_API_KEY)");
20432
+ fail2("missing_credentials", "rerun needs backend credentials (BAKER_API_URL / BAKER_API_KEY)");
20118
20433
  }
20119
20434
  const latest = await client.getLatestSnapshotRun(slug);
20120
20435
  if (!latest) {
20121
- fail(
20436
+ fail2(
20122
20437
  "no_snapshot_run",
20123
20438
  `no rerunnable history for "${slug}" \u2014 the creative was never run by a CLI that records canvas snapshots`
20124
20439
  );
20125
20440
  }
20126
20441
  const manifest = await fetchManifest(latest.canvasSnapshotUrl);
20127
20442
  if (latest.canvasSha && manifest.canvasSha !== latest.canvasSha) {
20128
- fail("snapshot_mismatch", `snapshot manifest for run ${latest.runId} does not match its recorded canvas sha`);
20443
+ fail2("snapshot_mismatch", `snapshot manifest for run ${latest.runId} does not match its recorded canvas sha`);
20129
20444
  }
20130
- const targetDir = path13.resolve("src", "creatives", slug);
20445
+ const targetDir = path14.resolve("src", "creatives", slug);
20131
20446
  let restoredCanvasPath;
20132
20447
  try {
20133
20448
  const restore = await restoreRunSnapshot(manifest, targetDir, { force: args["force-remote"] === true });
@@ -20145,7 +20460,7 @@ var rerunCommand = defineCommand89({
20145
20460
  }
20146
20461
  } catch (e) {
20147
20462
  if (e instanceof SnapshotConflictError) {
20148
- fail(
20463
+ fail2(
20149
20464
  "local_files_differ",
20150
20465
  `local files differ from the recorded snapshot: ${e.conflicts.join(", ")}. Re-run with --force-remote to overwrite them, or run the local canvas directly with \`baker canvas run\`.`
20151
20466
  );
@@ -20160,25 +20475,25 @@ var rerunCommand = defineCommand89({
20160
20475
  });
20161
20476
  }
20162
20477
  });
20163
- function fail(code, message) {
20478
+ function fail2(code, message) {
20164
20479
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code, message } }, null, 2)}
20165
20480
  `);
20166
20481
  process.exit(2);
20167
20482
  }
20168
20483
  async function fetchManifest(url) {
20169
20484
  const res = await fetch(url);
20170
- if (!res.ok) fail("snapshot_unavailable", `snapshot manifest download failed: ${res.status} ${res.statusText}`);
20485
+ if (!res.ok) fail2("snapshot_unavailable", `snapshot manifest download failed: ${res.status} ${res.statusText}`);
20171
20486
  const manifest = await res.json();
20172
20487
  if (manifest?.schema !== SNAPSHOT_SCHEMA || typeof manifest.canvas?.path !== "string") {
20173
- fail("snapshot_invalid", "snapshot manifest has an unexpected shape");
20488
+ fail2("snapshot_invalid", "snapshot manifest has an unexpected shape");
20174
20489
  }
20175
20490
  return manifest;
20176
20491
  }
20177
20492
 
20178
20493
  // src/commands/canvas/scaffold-static-ad.ts
20179
- import { access, mkdir as mkdir5, readFile as readFile11, writeFile as writeFile6 } from "fs/promises";
20180
- import path17 from "path";
20181
- import { defineCommand as defineCommand91 } from "citty";
20494
+ import { access, mkdir as mkdir6, readFile as readFile12, writeFile as writeFile7 } from "fs/promises";
20495
+ import path18 from "path";
20496
+ import { defineCommand as defineCommand93 } from "citty";
20182
20497
 
20183
20498
  // src/engine/scaffold/staticAd.ts
20184
20499
  import { z as z17 } from "zod";
@@ -20455,7 +20770,7 @@ function staticAdReport(input, elementsInput, opts) {
20455
20770
  }
20456
20771
 
20457
20772
  // src/commands/canvas/creative-definition.ts
20458
- import path14 from "path";
20773
+ import path15 from "path";
20459
20774
  var PLATFORM_VALUES = ["meta", "google", "linkedin", "tiktok", "youtube", "x", "other"];
20460
20775
  var FORMAT_VALUES = ["1:1", "4:5", "9:16", "16:9", "1.91:1"];
20461
20776
  function titleFromSlug(slug) {
@@ -20503,16 +20818,16 @@ function buildCreativeDefinition(input) {
20503
20818
  }
20504
20819
 
20505
20820
  // src/commands/canvas/scaffold-static-ad-paths.ts
20506
- import path15 from "path";
20821
+ import path16 from "path";
20507
20822
  function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd(), slug) {
20508
20823
  const file = rawFile.trim();
20509
20824
  const imageIsUrl = /^https?:\/\//i.test(file);
20510
- const imageSource = imageIsUrl ? file : path15.resolve(cwd, file);
20511
- const outPath = out ? path15.resolve(cwd, out) : slug ? path15.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path15.join(cwd, "static-ad.canvas.json") : path15.join(path15.dirname(imageSource), "static-ad.canvas.json");
20512
- const blueprintPath = path15.join(path15.dirname(outPath), "prompt.json");
20513
- const creativeDir = slug ? path15.dirname(outPath) : null;
20514
- const definitionPath = creativeDir ? path15.join(creativeDir, "_definition.md") : null;
20515
- const referencesDir = creativeDir ? path15.join(creativeDir, "references") : null;
20825
+ const imageSource = imageIsUrl ? file : path16.resolve(cwd, file);
20826
+ const outPath = out ? path16.resolve(cwd, out) : slug ? path16.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path16.join(cwd, "static-ad.canvas.json") : path16.join(path16.dirname(imageSource), "static-ad.canvas.json");
20827
+ const blueprintPath = path16.join(path16.dirname(outPath), "prompt.json");
20828
+ const creativeDir = slug ? path16.dirname(outPath) : null;
20829
+ const definitionPath = creativeDir ? path16.join(creativeDir, "_definition.md") : null;
20830
+ const referencesDir = creativeDir ? path16.join(creativeDir, "references") : null;
20516
20831
  return { imageIsUrl, imageSource, outPath, blueprintPath, creativeDir, definitionPath, referencesDir };
20517
20832
  }
20518
20833
  var SCAFFOLD_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
@@ -20522,9 +20837,9 @@ function isValidScaffoldSlug(slug) {
20522
20837
  }
20523
20838
 
20524
20839
  // src/commands/canvas/sync-definition.ts
20525
- import { readdir as readdir5, readFile as readFile10, stat as stat3 } from "fs/promises";
20526
- import path16 from "path";
20527
- import { defineCommand as defineCommand90 } from "citty";
20840
+ import { readdir as readdir5, readFile as readFile11, stat as stat3 } from "fs/promises";
20841
+ import path17 from "path";
20842
+ import { defineCommand as defineCommand92 } from "citty";
20528
20843
 
20529
20844
  // src/commands/canvas/definition-graph.ts
20530
20845
  var MAX_NODES = 300;
@@ -20616,15 +20931,15 @@ async function syncCreativeDefinitionBestEffort(input) {
20616
20931
  }
20617
20932
  }
20618
20933
  async function resolveCanvasPath(inputPath) {
20619
- const resolved = path16.resolve(inputPath);
20934
+ const resolved = path17.resolve(inputPath);
20620
20935
  let dir = resolved;
20621
20936
  try {
20622
20937
  if ((await stat3(resolved)).isFile()) {
20623
20938
  if (resolved.endsWith(".canvas.json")) return resolved;
20624
- dir = path16.dirname(resolved);
20939
+ dir = path17.dirname(resolved);
20625
20940
  }
20626
20941
  } catch {
20627
- dir = resolved.endsWith(".canvas.json") ? path16.dirname(resolved) : resolved;
20942
+ dir = resolved.endsWith(".canvas.json") ? path17.dirname(resolved) : resolved;
20628
20943
  }
20629
20944
  let entries;
20630
20945
  try {
@@ -20635,9 +20950,9 @@ async function resolveCanvasPath(inputPath) {
20635
20950
  const canvases = entries.filter((name) => name.endsWith(".canvas.json"));
20636
20951
  const slug = creativeSlugFromCanvasPath(`${dir}/x/`);
20637
20952
  const chosen = (slug ? canvases.find((name) => name === `${slug}.canvas.json`) : void 0) ?? canvases[0];
20638
- return chosen ? path16.join(dir, chosen) : null;
20953
+ return chosen ? path17.join(dir, chosen) : null;
20639
20954
  }
20640
- var syncDefinitionCommand = defineCommand90({
20955
+ var syncDefinitionCommand = defineCommand92({
20641
20956
  meta: {
20642
20957
  name: "sync-definition",
20643
20958
  description: "Push a creative's current node-graph + input thumbnails to the dashboard without rendering. Runs automatically when you edit a creative's canvas.json or prompt.json."
@@ -20658,7 +20973,7 @@ var syncDefinitionCommand = defineCommand90({
20658
20973
  if (!slug) return;
20659
20974
  let canvas;
20660
20975
  try {
20661
- canvas = JSON.parse(await readFile10(canvasPath, "utf8"));
20976
+ canvas = JSON.parse(await readFile11(canvasPath, "utf8"));
20662
20977
  } catch {
20663
20978
  return;
20664
20979
  }
@@ -20683,7 +20998,7 @@ async function uploadSourceAsReference(source, isUrl, client) {
20683
20998
  if (!res.ok) throw new Error(`failed to download source image (${res.status})`);
20684
20999
  bytes = Buffer.from(await res.arrayBuffer());
20685
21000
  } else {
20686
- bytes = await readFile11(source);
21001
+ bytes = await readFile12(source);
20687
21002
  }
20688
21003
  const safe = await toModelSafeImage(bytes);
20689
21004
  const sha256 = sha256Hex(safe.bytes);
@@ -20738,7 +21053,7 @@ DROP background extras, decorative props, generic scenery, and anything small or
20738
21053
  For each kept element return: { "type": one of logo|product|person|animal|badge, "label": a short UPPER_SNAKE_CASE name (e.g. LOGO, PRODUCT, HERO_DOG, TRUSTPILOT), "description": a concrete reusable description to source/shoot the real asset (include the exact expression for a living subject, and its castable attributes \u2014 breed/species for an animal, apparent age band, apparent origin/ethnicity, and wardrobe/setting for a person \u2014 so it can be recast to fit OUR audience/market), "expression": the facial expression for a living subject or null, "reason": why it is identity-critical, "locator": the blueprint entry this element came from as { "collection": one of "subjects" | "people" | "brands_logos", "index": its 0-based position in that array } (people -> people; logos/badges -> brands_logos; products/animals/objects -> subjects). Output ONLY the JSON object.`;
20739
21054
  async function loadAssetText(ref, label) {
20740
21055
  const r = ref;
20741
- if (typeof r?.path === "string") return readFile11(r.path, "utf8");
21056
+ if (typeof r?.path === "string") return readFile12(r.path, "utf8");
20742
21057
  if (typeof r?.url === "string") {
20743
21058
  const res = await fetch(r.url);
20744
21059
  if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
@@ -20762,7 +21077,7 @@ function parseLayout(raw) {
20762
21077
  }
20763
21078
  return null;
20764
21079
  }
20765
- function fail2(code, message) {
21080
+ function fail3(code, message) {
20766
21081
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code, message } }, null, 2)}
20767
21082
  `);
20768
21083
  process.exit(2);
@@ -20853,8 +21168,8 @@ async function runVisionPasses(canvas) {
20853
21168
  outputsByNode = result.outputs_by_node;
20854
21169
  creditsSpent = result.stats?.total_credits;
20855
21170
  } catch (e) {
20856
- if (e instanceof ValidationError) return fail2("validation", JSON.stringify(e.issues));
20857
- return fail2("describe", e instanceof Error ? e.message : String(e));
21171
+ if (e instanceof ValidationError) return fail3("validation", JSON.stringify(e.issues));
21172
+ return fail3("describe", e instanceof Error ? e.message : String(e));
20858
21173
  }
20859
21174
  try {
20860
21175
  const blueprint = JSON.parse(await loadAssetText(outputsByNode.describe?.description, "describe output"));
@@ -20862,10 +21177,10 @@ async function runVisionPasses(canvas) {
20862
21177
  const layout = parseLayout(await loadAssetText(outputsByNode.layout?.text, "layout output"));
20863
21178
  return { blueprint, elements, layout, creditsSpent };
20864
21179
  } catch (e) {
20865
- return fail2("read_outputs", e instanceof Error ? e.message : String(e));
21180
+ return fail3("read_outputs", e instanceof Error ? e.message : String(e));
20866
21181
  }
20867
21182
  }
20868
- var scaffoldStaticAdCommand = defineCommand91({
21183
+ var scaffoldStaticAdCommand = defineCommand93({
20869
21184
  meta: {
20870
21185
  name: "scaffold-static-ad",
20871
21186
  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'), an AI selection of the image's MAIN identity elements, and a structured global-layout pass (the column/row grid with per-region bounds and text sizes) \u2014 then scaffolds a canvas that wires one [TODO] ingest slot per element (logo/product/subject/badge + brand font) into image_generate. By default it also fans the hero out to the platform's placement ratios (via image_aspect_adapt \u2014 the hero ratio is free, the rest are billed AI recompositions); 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."
@@ -20914,7 +21229,7 @@ var scaffoldStaticAdCommand = defineCommand91({
20914
21229
  process.cwd(),
20915
21230
  slug
20916
21231
  );
20917
- await mkdir5(path17.dirname(outPath), { recursive: true });
21232
+ await mkdir6(path18.dirname(outPath), { recursive: true });
20918
21233
  const { describeModel, selectModel, layoutModel, genModel } = resolveModels(args);
20919
21234
  let durableSourceUrl;
20920
21235
  if (referencesDir) {
@@ -20923,7 +21238,7 @@ var scaffoldStaticAdCommand = defineCommand91({
20923
21238
  try {
20924
21239
  durableSourceUrl = await uploadSourceAsReference(imageSource, imageIsUrl, client);
20925
21240
  } catch (e) {
20926
- return fail2("source_unavailable", e instanceof Error ? e.message : String(e));
21241
+ return fail3("source_unavailable", e instanceof Error ? e.message : String(e));
20927
21242
  }
20928
21243
  }
20929
21244
  }
@@ -20940,7 +21255,7 @@ var scaffoldStaticAdCommand = defineCommand91({
20940
21255
  if (layout && annotated && typeof annotated === "object") {
20941
21256
  annotated.layout = layout;
20942
21257
  }
20943
- await writeFile6(blueprintPath, `${JSON.stringify(annotated, null, 2)}
21258
+ await writeFile7(blueprintPath, `${JSON.stringify(annotated, null, 2)}
20944
21259
  `, "utf8");
20945
21260
  let canvasImagePath = imageSource;
20946
21261
  let canvasImageIsUrl = imageIsUrl;
@@ -20957,7 +21272,7 @@ var scaffoldStaticAdCommand = defineCommand91({
20957
21272
  args.placements ? String(args.placements) : void 0,
20958
21273
  definitionPlatform
20959
21274
  );
20960
- if (!placementsResult.ok) return fail2("invalid_placements", placementsResult.message);
21275
+ if (!placementsResult.ok) return fail3("invalid_placements", placementsResult.message);
20961
21276
  const placements = placementsResult.placements;
20962
21277
  const opts = {
20963
21278
  genModel,
@@ -20975,7 +21290,7 @@ var scaffoldStaticAdCommand = defineCommand91({
20975
21290
  canvas = scaffoldStaticAd(blueprint, elements, opts);
20976
21291
  report = staticAdReport(blueprint, elements, opts);
20977
21292
  } catch (e) {
20978
- return fail2("scaffold", e instanceof Error ? e.message : String(e));
21293
+ return fail3("scaffold", e instanceof Error ? e.message : String(e));
20979
21294
  }
20980
21295
  const validation = await validateCanvasDeep(canvas, defaultRegistry());
20981
21296
  if (!validation.ok) {
@@ -20985,10 +21300,10 @@ var scaffoldStaticAdCommand = defineCommand91({
20985
21300
  );
20986
21301
  process.exit(2);
20987
21302
  }
20988
- await writeFile6(outPath, `${JSON.stringify(canvas, null, 2)}
21303
+ await writeFile7(outPath, `${JSON.stringify(canvas, null, 2)}
20989
21304
  `, "utf8");
20990
21305
  if (definitionPath && !await fileExists(definitionPath)) {
20991
- await writeFile6(
21306
+ await writeFile7(
20992
21307
  definitionPath,
20993
21308
  buildCreativeDefinition({
20994
21309
  title: args.title ? String(args.title) : titleFromSlug(slug ?? ""),
@@ -21034,7 +21349,7 @@ var scaffoldStaticAdCommand = defineCommand91({
21034
21349
  run_estimated_credits: validation.estimatedCredits
21035
21350
  },
21036
21351
  checklist: {
21037
- edit_prompt: `Edit ${path17.basename(blueprintPath)} \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.`,
21352
+ edit_prompt: `Edit ${path18.basename(blueprintPath)} \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.`,
21038
21353
  assets_to_supply: report.elements,
21039
21354
  font_slot: report.includes_font ? "Drop a brand font at the [TODO] brandfont path (the describe pass recorded the ad's typefaces under `fonts` in prompt.json \u2014 match those). The font is wired into the render as a TYPE SPECIMEN reference so generated text takes the brand letterforms. Delete the brandfont + type_ref nodes to skip it." : "skipped (--skip-font)",
21040
21355
  actor_sheets: report.actor_sheets.length > 0 ? `Each living hero (${report.actor_sheets.join(", ")}) is fused into a generated multi-view reference sheet (image_reference_sheet) that the render grounds on \u2014 so drop ONE clean photo at that hero's ingest and the sheet builds the consistent turnaround. Pass --skip-actor-sheets to ground on the lone photo instead.` : "none (no person/animal heroes detected, or --skip-actor-sheets)",
@@ -21051,10 +21366,10 @@ var scaffoldStaticAdCommand = defineCommand91({
21051
21366
  });
21052
21367
 
21053
21368
  // src/commands/canvas/scaffold-video.ts
21054
- import { access as access2, cp, mkdir as mkdir6, readFile as readFile14, rm as rm6, writeFile as writeFile7 } from "fs/promises";
21369
+ import { access as access2, cp, mkdir as mkdir7, readFile as readFile15, rm as rm6, writeFile as writeFile8 } from "fs/promises";
21055
21370
  import { tmpdir as tmpdir2 } from "os";
21056
- import path20 from "path";
21057
- import { defineCommand as defineCommand92 } from "citty";
21371
+ import path21 from "path";
21372
+ import { defineCommand as defineCommand94 } from "citty";
21058
21373
 
21059
21374
  // src/engine/scaffold/lib/model-router.ts
21060
21375
  var SEEDANCE = "bytedance/seedance-2.0";
@@ -21092,7 +21407,7 @@ function routeVideoModel(input) {
21092
21407
 
21093
21408
  // src/engine/nodes/local/lib/sceneDetect.ts
21094
21409
  import { execFile as execFile2 } from "child_process";
21095
- import { mkdtemp, readdir as readdir6, readFile as readFile12, rm as rm5 } from "fs/promises";
21410
+ import { mkdtemp, readdir as readdir6, readFile as readFile13, rm as rm5 } from "fs/promises";
21096
21411
  import { tmpdir } from "os";
21097
21412
  import { join as join2 } from "path";
21098
21413
  import { promisify as promisify2 } from "util";
@@ -21168,7 +21483,7 @@ async function runSceneDetectOnce(filePath, threshold, minSceneLenS, timeoutMs)
21168
21483
  );
21169
21484
  const csvName = (await readdir6(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
21170
21485
  if (!csvName) return [];
21171
- return parsePySceneDetectCsvCuts(await readFile12(join2(outDir, csvName), "utf-8"));
21486
+ return parsePySceneDetectCsvCuts(await readFile13(join2(outDir, csvName), "utf-8"));
21172
21487
  } finally {
21173
21488
  await rm5(outDir, { recursive: true, force: true });
21174
21489
  }
@@ -21193,23 +21508,23 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
21193
21508
 
21194
21509
  // src/commands/canvas/composition-path.ts
21195
21510
  import { existsSync as existsSync3 } from "fs";
21196
- import path18 from "path";
21511
+ import path19 from "path";
21197
21512
  function resolveShippedCanvasDir(name, startDir, exists = existsSync3, maxDepth = 8) {
21198
- const rel = path18.join("canvas", name);
21513
+ const rel = path19.join("canvas", name);
21199
21514
  let dir = startDir;
21200
21515
  for (let i = 0; i < maxDepth; i++) {
21201
- const candidate = path18.join(dir, rel);
21202
- if (exists(path18.join(candidate, "meta.json"))) return candidate;
21203
- const parent = path18.dirname(dir);
21516
+ const candidate = path19.join(dir, rel);
21517
+ if (exists(path19.join(candidate, "meta.json"))) return candidate;
21518
+ const parent = path19.dirname(dir);
21204
21519
  if (parent === dir) break;
21205
21520
  dir = parent;
21206
21521
  }
21207
- return path18.resolve(startDir, "../../../", rel);
21522
+ return path19.resolve(startDir, "../../../", rel);
21208
21523
  }
21209
21524
 
21210
21525
  // src/commands/canvas/gitignore.ts
21211
- import { appendFile, readFile as readFile13 } from "fs/promises";
21212
- import path19 from "path";
21526
+ import { appendFile, readFile as readFile14 } from "fs/promises";
21527
+ import path20 from "path";
21213
21528
  function missingGitignoreEntries(existing, entries) {
21214
21529
  const present2 = new Set(
21215
21530
  existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
@@ -21217,10 +21532,10 @@ function missingGitignoreEntries(existing, entries) {
21217
21532
  return entries.filter((e) => !present2.has(e.trim().replace(/\/+$/, "")));
21218
21533
  }
21219
21534
  async function ensureGitignore(dir, entries) {
21220
- const file = path19.join(dir, ".gitignore");
21535
+ const file = path20.join(dir, ".gitignore");
21221
21536
  let existing;
21222
21537
  try {
21223
- existing = await readFile13(file, "utf8");
21538
+ existing = await readFile14(file, "utf8");
21224
21539
  } catch {
21225
21540
  return;
21226
21541
  }
@@ -21259,7 +21574,7 @@ ONE PERSON, MULTIPLE LOOKS: if a single individual plays MULTIPLE personas or wa
21259
21574
  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.`;
21260
21575
  async function loadAssetText2(ref, label) {
21261
21576
  const r = ref;
21262
- if (typeof r?.path === "string") return readFile14(r.path, "utf8");
21577
+ if (typeof r?.path === "string") return readFile15(r.path, "utf8");
21263
21578
  if (typeof r?.url === "string") {
21264
21579
  const res = await fetch(r.url);
21265
21580
  if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
@@ -21278,7 +21593,7 @@ async function loadTranscriptBestEffort(ref) {
21278
21593
  async function stageCaptions(outDir, transcript) {
21279
21594
  const text = transcript?.trim();
21280
21595
  if (!text || text === "[]") return {};
21281
- const compositionPath = path20.join(outDir, "tiktok-captions-composition");
21596
+ const compositionPath = path21.join(outDir, "tiktok-captions-composition");
21282
21597
  await cp(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
21283
21598
  return { compositionPath };
21284
21599
  }
@@ -21296,12 +21611,12 @@ function patchCompositionHtml(html, dims) {
21296
21611
  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`);
21297
21612
  }
21298
21613
  async function stampCompositionDims(compositionDir, dims) {
21299
- const metaPath = path20.join(compositionDir, "meta.json");
21300
- const rawMeta = await readFile14(metaPath, "utf8");
21301
- await writeFile7(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
21302
- const htmlPath = path20.join(compositionDir, "index.html");
21303
- const rawHtml = await readFile14(htmlPath, "utf8");
21304
- await writeFile7(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
21614
+ const metaPath = path21.join(compositionDir, "meta.json");
21615
+ const rawMeta = await readFile15(metaPath, "utf8");
21616
+ await writeFile8(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
21617
+ const htmlPath = path21.join(compositionDir, "index.html");
21618
+ const rawHtml = await readFile15(htmlPath, "utf8");
21619
+ await writeFile8(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
21305
21620
  }
21306
21621
  function parseElements2(raw) {
21307
21622
  const parsed = JSON.parse(raw);
@@ -21336,7 +21651,7 @@ async function detectShotCutsBestEffort(videoPath, threshold) {
21336
21651
  return void 0;
21337
21652
  }
21338
21653
  }
21339
- function fail3(code, message) {
21654
+ function fail4(code, message) {
21340
21655
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code, message } }, null, 2)}
21341
21656
  `);
21342
21657
  process.exit(2);
@@ -21348,7 +21663,7 @@ var VIDEO_EXT_BY_MIME = {
21348
21663
  "video/x-matroska": ".mkv"
21349
21664
  };
21350
21665
  function referenceVideoExt(url, contentType) {
21351
- const fromPath = path20.extname(new URL(url).pathname).toLowerCase();
21666
+ const fromPath = path21.extname(new URL(url).pathname).toLowerCase();
21352
21667
  if (fromPath && fromPath.length <= 5) return fromPath;
21353
21668
  const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase();
21354
21669
  return mime && VIDEO_EXT_BY_MIME[mime] || ".mp4";
@@ -21374,7 +21689,7 @@ function videoDefinitionDescription(blueprint) {
21374
21689
  return typeof product === "string" && product.trim() ? product.trim() : void 0;
21375
21690
  }
21376
21691
  async function materializeReferenceVideo(fileArg2) {
21377
- if (!/^https?:\/\//i.test(fileArg2)) return path20.resolve(fileArg2);
21692
+ if (!/^https?:\/\//i.test(fileArg2)) return path21.resolve(fileArg2);
21378
21693
  let res;
21379
21694
  try {
21380
21695
  res = await fetch(fileArg2);
@@ -21384,11 +21699,11 @@ async function materializeReferenceVideo(fileArg2) {
21384
21699
  if (!res.ok) throw new Error(`failed to download reference video (${res.status} ${res.statusText})`);
21385
21700
  const bytes = Buffer.from(await res.arrayBuffer());
21386
21701
  if (bytes.length === 0) throw new Error("reference video download was empty");
21387
- const dest = path20.join(
21702
+ const dest = path21.join(
21388
21703
  tmpdir2(),
21389
21704
  `baker-ref-${sha256Hex(bytes).slice(0, 16)}${referenceVideoExt(fileArg2, res.headers.get("content-type"))}`
21390
21705
  );
21391
- await writeFile7(dest, bytes);
21706
+ await writeFile8(dest, bytes);
21392
21707
  return dest;
21393
21708
  }
21394
21709
  function resolveSeamDedup(raw) {
@@ -21496,9 +21811,9 @@ async function runAnalysisPasses(deconstructCanvas, selectModel) {
21496
21811
  blueprint = JSON.parse(await loadAssetText2(r1.outputs_by_node.deconstruct?.analysis, "deconstruct output"));
21497
21812
  transcript = await loadTranscriptBestEffort(r1.outputs_by_node.deconstruct?.transcript);
21498
21813
  } catch (e) {
21499
- if (e instanceof ValidationError) return fail3("validation", JSON.stringify(e.issues));
21500
- if (e instanceof SyntaxError) return fail3("read_outputs", e.message);
21501
- return fail3("deconstruct", e instanceof Error ? e.message : String(e));
21814
+ if (e instanceof ValidationError) return fail4("validation", JSON.stringify(e.issues));
21815
+ if (e instanceof SyntaxError) return fail4("read_outputs", e.message);
21816
+ return fail4("deconstruct", e instanceof Error ? e.message : String(e));
21502
21817
  }
21503
21818
  const slimJson = JSON.stringify(slimBlueprintForSelection(blueprint));
21504
21819
  try {
@@ -21507,12 +21822,12 @@ async function runAnalysisPasses(deconstructCanvas, selectModel) {
21507
21822
  const elements = parseElements2(await loadAssetText2(r2.outputs_by_node.select?.text, "selection output"));
21508
21823
  return { blueprint, elements, transcript, creditsSpent: sawCredits ? credits : void 0 };
21509
21824
  } catch (e) {
21510
- if (e instanceof ValidationError) return fail3("validation", JSON.stringify(e.issues));
21511
- if (e instanceof SyntaxError) return fail3("read_outputs", e.message);
21512
- return fail3("deconstruct", e instanceof Error ? e.message : String(e));
21825
+ if (e instanceof ValidationError) return fail4("validation", JSON.stringify(e.issues));
21826
+ if (e instanceof SyntaxError) return fail4("read_outputs", e.message);
21827
+ return fail4("deconstruct", e instanceof Error ? e.message : String(e));
21513
21828
  }
21514
21829
  }
21515
- var scaffoldVideoCommand = defineCommand92({
21830
+ var scaffoldVideoCommand = defineCommand94({
21516
21831
  meta: {
21517
21832
  name: "scaffold-video",
21518
21833
  description: "Turn a reference video into a runnable reproduction canvas in one command. Runs billed passes \u2014 video_deconstruct (the full scene-by-scene blueprint + transcript, baked to a split, editable blueprint: a global prompt.json plus one small scenes/sNN.json per scene) and an AI selection of the video's RECURRING identity elements (person/animal/product/logo) \u2014 then scaffolds a pipeline where every scene boundary is a static-ad-grade frame (the blueprint as target_blueprint, a reference legend, the real frame as anchor) and each recurring element gets ONE shared [TODO] ingest slot wired into every frame it appears in. The clips feed Seedance an ultra-detailed motion brief (action, camera, dialogue, transcript). Edit a scene's scenes/sNN.json (or prompt.json for global cast/palette/brand), drop the real source images, then `baker canvas run`."
@@ -21593,7 +21908,7 @@ var scaffoldVideoCommand = defineCommand92({
21593
21908
  }
21594
21909
  const isUrl = /^https?:\/\//i.test(fileArg2);
21595
21910
  if (isUrl && !slug && !args.out) {
21596
- return fail3(
21911
+ return fail4(
21597
21912
  "missing_output_target",
21598
21913
  "When the reference is a URL, pass --slug (writes src/creatives/<slug>/) or --out <path> so the scaffolded canvas has a home in the repo."
21599
21914
  );
@@ -21602,13 +21917,13 @@ var scaffoldVideoCommand = defineCommand92({
21602
21917
  try {
21603
21918
  videoPath = await materializeReferenceVideo(fileArg2);
21604
21919
  } catch (e) {
21605
- return fail3("download", e instanceof Error ? e.message : String(e));
21920
+ return fail4("download", e instanceof Error ? e.message : String(e));
21606
21921
  }
21607
- const base = path20.basename(videoPath, path20.extname(videoPath));
21608
- const outPath = args.out ? path20.resolve(String(args.out)) : slug ? path20.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path20.join(path20.dirname(videoPath), `${base}.video.canvas.json`);
21609
- const outDir = path20.dirname(outPath);
21610
- const blueprintPath = path20.join(outDir, "prompt.json");
21611
- const blueprintStylePath = path20.join(outDir, "prompt.style.json");
21922
+ const base = path21.basename(videoPath, path21.extname(videoPath));
21923
+ const outPath = args.out ? path21.resolve(String(args.out)) : slug ? path21.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path21.join(path21.dirname(videoPath), `${base}.video.canvas.json`);
21924
+ const outDir = path21.dirname(outPath);
21925
+ const blueprintPath = path21.join(outDir, "prompt.json");
21926
+ const blueprintStylePath = path21.join(outDir, "prompt.style.json");
21612
21927
  const frames = args.frames === "reuse" ? "reuse" : "generate";
21613
21928
  const maxScenes = args["max-scenes"] ? Number(args["max-scenes"]) : void 0;
21614
21929
  if (Number.isFinite(maxScenes)) {
@@ -21632,10 +21947,10 @@ var scaffoldVideoCommand = defineCommand92({
21632
21947
  process.stderr.write(`\u{1F3AC} Photoreal on-camera cast detected \u2192 generating clips on ${videoModel} (dodges Seedance's real-person filter).
21633
21948
  `);
21634
21949
  }
21635
- await mkdir6(outDir, { recursive: true });
21950
+ await mkdir7(outDir, { recursive: true });
21636
21951
  const annotated = annotateBlueprintWithElements(blueprint, elements);
21637
21952
  await writeSceneFiles(outDir, annotated);
21638
- await writeFile7(blueprintStylePath, renderStyleProjectionFromValue(annotated), "utf8");
21953
+ await writeFile8(blueprintStylePath, renderStyleProjectionFromValue(annotated), "utf8");
21639
21954
  let aspect;
21640
21955
  try {
21641
21956
  aspect = resolveAspect(
@@ -21644,7 +21959,7 @@ var scaffoldVideoCommand = defineCommand92({
21644
21959
  genAspectsFor(videoModel)
21645
21960
  );
21646
21961
  } catch (e) {
21647
- return fail3("aspect", e instanceof Error ? e.message : String(e));
21962
+ return fail4("aspect", e instanceof Error ? e.message : String(e));
21648
21963
  }
21649
21964
  const outDims = canvasDims(aspect.outAr);
21650
21965
  if (aspect.remapped) {
@@ -21653,29 +21968,29 @@ var scaffoldVideoCommand = defineCommand92({
21653
21968
  `
21654
21969
  );
21655
21970
  }
21656
- const compositionDest = path20.join(outDir, "video-overlay-composition");
21971
+ const compositionDest = path21.join(outDir, "video-overlay-composition");
21657
21972
  await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
21658
21973
  await stampCompositionDims(compositionDest, outDims);
21659
- const indexPath = path20.join(compositionDest, "index.html");
21974
+ const indexPath = path21.join(compositionDest, "index.html");
21660
21975
  const overlayHtml = buildOverlayHtml(blueprint, { captionsActive: Boolean(transcript) });
21661
- const indexHtml = await readFile14(indexPath, "utf8");
21976
+ const indexHtml = await readFile15(indexPath, "utf8");
21662
21977
  const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
21663
21978
  if (injected === indexHtml && overlayHtml.trim()) {
21664
- fail3(
21979
+ fail4(
21665
21980
  "composition_marker_missing",
21666
21981
  `video-overlay-composition/index.html is missing the <!--OVERLAYS--> marker \u2014 cannot inject the overlay layer`
21667
21982
  );
21668
21983
  }
21669
- await writeFile7(indexPath, injected, "utf8");
21984
+ await writeFile8(indexPath, injected, "utf8");
21670
21985
  const captions = await stageCaptions(outDir, transcript);
21671
21986
  if (captions.compositionPath) await stampCompositionDims(captions.compositionPath, outDims);
21672
21987
  const opts = {
21673
21988
  imageModel,
21674
21989
  videoModel,
21675
- overlayCompositionPath: path20.relative(outDir, compositionDest),
21676
- captionsCompositionPath: captions.compositionPath ? path20.relative(outDir, captions.compositionPath) : void 0,
21677
- blueprintPath: path20.relative(outDir, blueprintPath),
21678
- blueprintStylePath: path20.relative(outDir, blueprintStylePath),
21990
+ overlayCompositionPath: path21.relative(outDir, compositionDest),
21991
+ captionsCompositionPath: captions.compositionPath ? path21.relative(outDir, captions.compositionPath) : void 0,
21992
+ blueprintPath: path21.relative(outDir, blueprintPath),
21993
+ blueprintStylePath: path21.relative(outDir, blueprintStylePath),
21679
21994
  frames,
21680
21995
  ambient: Boolean(args.ambient),
21681
21996
  seamDedup: resolveSeamDedup(args["seam-dedup"]),
@@ -21688,7 +22003,7 @@ var scaffoldVideoCommand = defineCommand92({
21688
22003
  canvas = scaffoldVideoCanvas(blueprint, elements, opts);
21689
22004
  report = videoReport(blueprint, elements);
21690
22005
  } catch (e) {
21691
- return fail3("scaffold", e instanceof Error ? e.message : String(e));
22006
+ return fail4("scaffold", e instanceof Error ? e.message : String(e));
21692
22007
  }
21693
22008
  if (captions.compositionPath && !canvas.nodes.some((n) => n.id === "captions")) {
21694
22009
  await rm6(captions.compositionPath, { recursive: true, force: true });
@@ -21700,10 +22015,10 @@ var scaffoldVideoCommand = defineCommand92({
21700
22015
  todo.blocking_validation_issues = validation.issues;
21701
22016
  meta.todo = todo;
21702
22017
  }
21703
- await writeFile7(outPath, `${JSON.stringify(canvas, null, 2)}
22018
+ await writeFile8(outPath, `${JSON.stringify(canvas, null, 2)}
21704
22019
  `, "utf8");
21705
- await writeFile7(
21706
- path20.join(outDir, REBUILD_FILE),
22020
+ await writeFile8(
22021
+ path21.join(outDir, REBUILD_FILE),
21707
22022
  `${JSON.stringify({ elements, opts }, null, 2)}
21708
22023
  `,
21709
22024
  "utf8"
@@ -21728,9 +22043,9 @@ var scaffoldVideoCommand = defineCommand92({
21728
22043
  await ensureGitignore(process.cwd(), ["canvas/", ".context/"]);
21729
22044
  const sourceRef = videoSourceReference(blueprint, fileArg2);
21730
22045
  if (slug) {
21731
- const definitionPath = path20.join(outDir, "_definition.md");
22046
+ const definitionPath = path21.join(outDir, "_definition.md");
21732
22047
  if (!await fileExists2(definitionPath)) {
21733
- await writeFile7(
22048
+ await writeFile8(
21734
22049
  definitionPath,
21735
22050
  buildCreativeDefinition({
21736
22051
  title: titleFromSlug(slug),
@@ -21783,7 +22098,7 @@ var scaffoldVideoCommand = defineCommand92({
21783
22098
  graph: canvas.metadata?.video?.graph_stats
21784
22099
  },
21785
22100
  checklist: {
21786
- 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 \`${path20.basename(blueprintPath)}\`. \`baker canvas validate\`/\`run\` re-assemble the blueprint and re-flow every edited scene back into the render (and regenerate ${path20.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 ${path20.basename(blueprintStylePath)}; both are regenerated.`,
22101
+ 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 \`${path21.basename(blueprintPath)}\`. \`baker canvas validate\`/\`run\` re-assemble the blueprint and re-flow every edited scene back into the render (and regenerate ${path21.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 ${path21.basename(blueprintStylePath)}; both are regenerated.`,
21787
22102
  recurring_elements_to_supply: report.elements,
21788
22103
  voices_to_confirm: report.dialogue.map((d) => ({
21789
22104
  scene: d.scene,
@@ -21820,9 +22135,9 @@ var scaffoldVideoCommand = defineCommand92({
21820
22135
  });
21821
22136
 
21822
22137
  // src/commands/canvas/set-prompt.ts
21823
- import { readFile as readFile15, writeFile as writeFile8 } from "fs/promises";
21824
- import path21 from "path";
21825
- import { defineCommand as defineCommand93 } from "citty";
22138
+ import { readFile as readFile16, writeFile as writeFile9 } from "fs/promises";
22139
+ import path22 from "path";
22140
+ import { defineCommand as defineCommand95 } from "citty";
21826
22141
  function setNodePrompt(canvas, nodeId, text) {
21827
22142
  const nodes = canvas?.nodes;
21828
22143
  if (!Array.isArray(nodes)) throw new Error("canvas has no nodes array");
@@ -21837,7 +22152,7 @@ function setNodePrompt(canvas, nodeId, text) {
21837
22152
  newNodes[idx] = newNode;
21838
22153
  return { ...canvas, nodes: newNodes };
21839
22154
  }
21840
- var setPromptCommand = defineCommand93({
22155
+ var setPromptCommand = defineCommand95({
21841
22156
  meta: {
21842
22157
  name: "set-prompt",
21843
22158
  description: "Safely set a node's params.prompt (a frame description, motion prompt, etc.) without hand-editing the JSON. Prefer --text-file for multi-line/accented copy \u2014 it preserves UTF-8 exactly, unlike shell-quoted jq."
@@ -21849,17 +22164,17 @@ var setPromptCommand = defineCommand93({
21849
22164
  "text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
21850
22165
  },
21851
22166
  async run({ args }) {
21852
- const filePath = path21.resolve(String(args.file));
22167
+ const filePath = path22.resolve(String(args.file));
21853
22168
  let canvas;
21854
22169
  try {
21855
- canvas = JSON.parse(await readFile15(filePath, "utf8"));
22170
+ canvas = JSON.parse(await readFile16(filePath, "utf8"));
21856
22171
  } catch (e) {
21857
22172
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "parse", message: String(e) } }, null, 2)}
21858
22173
  `);
21859
22174
  process.exit(2);
21860
22175
  }
21861
22176
  let text;
21862
- if (args["text-file"]) text = await readFile15(path21.resolve(String(args["text-file"])), "utf8");
22177
+ if (args["text-file"]) text = await readFile16(path22.resolve(String(args["text-file"])), "utf8");
21863
22178
  else if (args.text !== void 0) text = String(args.text);
21864
22179
  else {
21865
22180
  process.stderr.write(
@@ -21880,14 +22195,14 @@ var setPromptCommand = defineCommand93({
21880
22195
  process.exit(2);
21881
22196
  return;
21882
22197
  }
21883
- const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path21.dirname(filePath)), defaultRegistry());
22198
+ const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path22.dirname(filePath)), defaultRegistry());
21884
22199
  if (!validation.ok) {
21885
22200
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
21886
22201
  `);
21887
22202
  process.exit(2);
21888
22203
  return;
21889
22204
  }
21890
- await writeFile8(filePath, `${JSON.stringify(updated, null, 2)}
22205
+ await writeFile9(filePath, `${JSON.stringify(updated, null, 2)}
21891
22206
  `, "utf8");
21892
22207
  process.stdout.write(`${JSON.stringify({ ok: true, node: String(args.node), bytes: text.length }, null, 2)}
21893
22208
  `);
@@ -21895,18 +22210,18 @@ var setPromptCommand = defineCommand93({
21895
22210
  });
21896
22211
 
21897
22212
  // src/commands/canvas/validate.ts
21898
- import { readFile as readFile16 } from "fs/promises";
21899
- import path22 from "path";
21900
- import { defineCommand as defineCommand94 } from "citty";
21901
- var validateCommand = defineCommand94({
22213
+ import { readFile as readFile17 } from "fs/promises";
22214
+ import path23 from "path";
22215
+ import { defineCommand as defineCommand96 } from "citty";
22216
+ var validateCommand = defineCommand96({
21902
22217
  meta: {
21903
22218
  name: "validate",
21904
22219
  description: "Validate a canvas JSON file (no execution). Includes a per-node cost preview and runs each node's deep validators (composition meta checks for hyperframe_render/_snapshot)."
21905
22220
  },
21906
22221
  args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
21907
22222
  async run({ args }) {
21908
- const filePath = path22.resolve(String(args.file));
21909
- const raw = await readFile16(filePath, "utf8");
22223
+ const filePath = path23.resolve(String(args.file));
22224
+ const raw = await readFile17(filePath, "utf8");
21910
22225
  let parsed;
21911
22226
  try {
21912
22227
  parsed = JSON.parse(raw);
@@ -21916,7 +22231,7 @@ var validateCommand = defineCommand94({
21916
22231
  `);
21917
22232
  process.exit(2);
21918
22233
  }
21919
- parsed = resolveRelativeCanvasPaths(parsed, path22.dirname(filePath));
22234
+ parsed = resolveRelativeCanvasPaths(parsed, path23.dirname(filePath));
21920
22235
  let styleProjection = "not_applicable";
21921
22236
  try {
21922
22237
  styleProjection = await syncStyleProjection(parsed, (line) => process.stderr.write(`${line}
@@ -21968,7 +22283,7 @@ var validateCommand = defineCommand94({
21968
22283
  });
21969
22284
 
21970
22285
  // src/commands/canvas/index.ts
21971
- var canvasCommand = defineCommand95({
22286
+ var canvasCommand = defineCommand97({
21972
22287
  meta: {
21973
22288
  name: "canvas",
21974
22289
  description: `Run Baker creative canvas JSON files locally. Local nodes execute in-process; remote nodes POST to the Convex backend gateway.
@@ -22002,7 +22317,7 @@ Full guide: __tooling__/docs/tools/baker/canvas.md`
22002
22317
  });
22003
22318
 
22004
22319
  // src/commands/chats/index.ts
22005
- import { defineCommand as defineCommand96 } from "citty";
22320
+ import { defineCommand as defineCommand98 } from "citty";
22006
22321
  var STATUS_GROUPS = ["active", "archived", "all"];
22007
22322
  var REPO_SURFACES = ["knowledge", "company", "brand", "landings", "flows", "creatives"];
22008
22323
  function parseBoundedInt(raw, name, min, max) {
@@ -22024,7 +22339,7 @@ registerSchema({
22024
22339
  full: { type: "boolean", description: "Include each Session's full change list", required: false, default: false }
22025
22340
  }
22026
22341
  });
22027
- var listCommand5 = defineCommand96({
22342
+ var listCommand5 = defineCommand98({
22028
22343
  meta: { name: "list", description: "List other Sessions on this account, newest first." },
22029
22344
  args: {
22030
22345
  status: { type: "string", description: "Filter: active|archived|all (default: all)", required: false },
@@ -22079,7 +22394,7 @@ registerSchema({
22079
22394
  }
22080
22395
  }
22081
22396
  });
22082
- var viewCommand = defineCommand96({
22397
+ var viewCommand = defineCommand98({
22083
22398
  meta: {
22084
22399
  name: "view",
22085
22400
  description: "Inspect one Session's full effects \u2014 git content + actions/tags/ads, kickoff, commits."
@@ -22123,7 +22438,7 @@ registerSchema({
22123
22438
  }
22124
22439
  }
22125
22440
  });
22126
- var transcriptCommand = defineCommand96({
22441
+ var transcriptCommand = defineCommand98({
22127
22442
  meta: { name: "transcript", description: "Read another Session's conversation." },
22128
22443
  args: {
22129
22444
  id: { type: "positional", description: "The Session id", required: true },
@@ -22175,7 +22490,7 @@ registerSchema({
22175
22490
  }
22176
22491
  }
22177
22492
  });
22178
- var diffCommand = defineCommand96({
22493
+ var diffCommand = defineCommand98({
22179
22494
  meta: { name: "diff", description: "See a published Session's real file-level changes." },
22180
22495
  args: {
22181
22496
  id: { type: "positional", description: "The Session id", required: true },
@@ -22219,7 +22534,7 @@ var diffCommand = defineCommand96({
22219
22534
  }
22220
22535
  }
22221
22536
  });
22222
- var chatsCommand = defineCommand96({
22537
+ var chatsCommand = defineCommand98({
22223
22538
  meta: {
22224
22539
  name: "chats",
22225
22540
  description: `Inspect other Sessions on this account (read-only): list them, view what they produced, read their conversation, and see their real file changes \u2014 so you can reuse past work for a new goal.
@@ -22234,13 +22549,13 @@ Full guide: __tooling__/docs/tools/baker/chats.md`
22234
22549
  });
22235
22550
 
22236
22551
  // src/commands/creatives/index.ts
22237
- import { defineCommand as defineCommand98 } from "citty";
22552
+ import { defineCommand as defineCommand100 } from "citty";
22238
22553
 
22239
22554
  // src/commands/creatives/publish.ts
22240
- import { defineCommand as defineCommand97 } from "citty";
22555
+ import { defineCommand as defineCommand99 } from "citty";
22241
22556
 
22242
22557
  // src/commands/images/api.ts
22243
- import { readFile as readFile17 } from "fs/promises";
22558
+ import { readFile as readFile18 } from "fs/promises";
22244
22559
  import { extname } from "path";
22245
22560
  var imageProcessingTimeoutMs = 18e4;
22246
22561
  var imageReadyPollIntervalMs = 2e3;
@@ -22254,7 +22569,7 @@ var mimeMap = {
22254
22569
  ".avif": "image/avif"
22255
22570
  };
22256
22571
  var defaultImageApiDeps = {
22257
- readFile: readFile17,
22572
+ readFile: readFile18,
22258
22573
  post: apiPost,
22259
22574
  get: apiGet,
22260
22575
  sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
@@ -22381,7 +22696,7 @@ async function publishCreative(args, deps = defaultImageApiDeps) {
22381
22696
  chatId: chatIdFromEnv()
22382
22697
  });
22383
22698
  }
22384
- var publishCommand = defineCommand97({
22699
+ var publishCommand = defineCommand99({
22385
22700
  meta: {
22386
22701
  name: "publish",
22387
22702
  description: "Publish a final static creative image to Baker Creatives and print the creative reference JSON."
@@ -22439,7 +22754,7 @@ var publishCommand = defineCommand97({
22439
22754
  });
22440
22755
 
22441
22756
  // src/commands/creatives/index.ts
22442
- var creativesCommand3 = defineCommand98({
22757
+ var creativesCommand3 = defineCommand100({
22443
22758
  meta: {
22444
22759
  name: "creatives",
22445
22760
  description: `Publish static ad creatives as first-class Baker outputs.
@@ -22456,7 +22771,7 @@ Full guide: __tooling__/docs/tools/baker/creatives.md`
22456
22771
  });
22457
22772
 
22458
22773
  // src/commands/flows/index.ts
22459
- import { defineCommand as defineCommand99 } from "citty";
22774
+ import { defineCommand as defineCommand101 } from "citty";
22460
22775
 
22461
22776
  // src/commands/flows/shared.ts
22462
22777
  import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync9 } from "fs";
@@ -22489,12 +22804,12 @@ function listFlowSlugs() {
22489
22804
  return readdirSync2(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith("_") && entry.name !== ".gitkeep").map((entry) => entry.name).sort();
22490
22805
  }
22491
22806
  function readFlowTree(slug) {
22492
- const path27 = join3(flowsDir(), slug, "_data.json");
22493
- if (!existsSync4(path27)) {
22807
+ const path28 = join3(flowsDir(), slug, "_data.json");
22808
+ if (!existsSync4(path28)) {
22494
22809
  failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
22495
22810
  }
22496
22811
  try {
22497
- return JSON.parse(readFileSync9(path27, "utf-8"));
22812
+ return JSON.parse(readFileSync9(path28, "utf-8"));
22498
22813
  } catch (error) {
22499
22814
  failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
22500
22815
  }
@@ -22611,7 +22926,7 @@ function displayName(slug) {
22611
22926
  const name = tree.displayName;
22612
22927
  return typeof name === "string" && name.trim() ? name.trim() : slug;
22613
22928
  }
22614
- var listCommand6 = defineCommand99({
22929
+ var listCommand6 = defineCommand101({
22615
22930
  meta: {
22616
22931
  name: "list",
22617
22932
  description: "List Forms in this workspace with the count of confidential fields still needing setup. Example: baker flows list"
@@ -22625,7 +22940,7 @@ var listCommand6 = defineCommand99({
22625
22940
  writeJson({ ok: true, data: { flows } });
22626
22941
  }
22627
22942
  });
22628
- var showCommand2 = defineCommand99({
22943
+ var showCommand2 = defineCommand101({
22629
22944
  meta: {
22630
22945
  name: "show",
22631
22946
  description: "Show a Form's confidential fields and their configuration status (never secret values). Example: baker flows show contact"
@@ -22646,7 +22961,7 @@ var showCommand2 = defineCommand99({
22646
22961
  writeJson({ ok: true, data: response });
22647
22962
  }
22648
22963
  });
22649
- var flowsCommand = defineCommand99({
22964
+ var flowsCommand = defineCommand101({
22650
22965
  meta: {
22651
22966
  name: "flows",
22652
22967
  description: `Read this workspace's Forms (flows) and the configuration status of their confidential fields \u2014 connection secrets, OAuth connections, and third-party field definitions (HubSpot, Calendly, HighLevel, SavvyCal).
@@ -22674,10 +22989,10 @@ Full guide: __tooling__/docs/tools/baker/flows.md`
22674
22989
  });
22675
22990
 
22676
22991
  // src/commands/ga4/index.ts
22677
- import { defineCommand as defineCommand103 } from "citty";
22992
+ import { defineCommand as defineCommand105 } from "citty";
22678
22993
 
22679
22994
  // src/commands/ga4/audit.ts
22680
- import { defineCommand as defineCommand100 } from "citty";
22995
+ import { defineCommand as defineCommand102 } from "citty";
22681
22996
 
22682
22997
  // src/commands/ga4/resolve.ts
22683
22998
  async function fetchProperties(useCache = true) {
@@ -22740,7 +23055,7 @@ registerSchema({
22740
23055
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
22741
23056
  }
22742
23057
  });
22743
- var auditCommand2 = defineCommand100({
23058
+ var auditCommand2 = defineCommand102({
22744
23059
  meta: {
22745
23060
  name: "audit",
22746
23061
  description: `Run all GA4 admin health checks. Returns property config with playbook warnings.
@@ -22792,7 +23107,7 @@ Examples:
22792
23107
  });
22793
23108
 
22794
23109
  // src/commands/ga4/properties.ts
22795
- import { defineCommand as defineCommand101 } from "citty";
23110
+ import { defineCommand as defineCommand103 } from "citty";
22796
23111
  registerSchema({
22797
23112
  command: "ga4.properties",
22798
23113
  description: "List all accessible GA4 properties. Returns property IDs needed for query and audit commands. Run this first to find property IDs.",
@@ -22800,7 +23115,7 @@ registerSchema({
22800
23115
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
22801
23116
  }
22802
23117
  });
22803
- var propertiesCommand = defineCommand101({
23118
+ var propertiesCommand = defineCommand103({
22804
23119
  meta: {
22805
23120
  name: "properties",
22806
23121
  description: `List accessible GA4 properties.
@@ -22850,7 +23165,7 @@ Examples:
22850
23165
  // src/commands/ga4/query.ts
22851
23166
  import { appendFileSync as appendFileSync2, existsSync as existsSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync3 } from "fs";
22852
23167
  import { resolve as resolve2 } from "path";
22853
- import { defineCommand as defineCommand102 } from "citty";
23168
+ import { defineCommand as defineCommand104 } from "citty";
22854
23169
 
22855
23170
  // src/commands/ga4/presets.ts
22856
23171
  var GA4_PRESETS = [
@@ -22982,7 +23297,7 @@ function handleError(err) {
22982
23297
  });
22983
23298
  process.exit(1);
22984
23299
  }
22985
- var queryCommand2 = defineCommand102({
23300
+ var queryCommand2 = defineCommand104({
22986
23301
  meta: {
22987
23302
  name: "query",
22988
23303
  description: `Run GA4 Data API reports. Preset-first with free-form escape hatch.
@@ -23053,7 +23368,7 @@ Free-form (escape hatch):
23053
23368
  });
23054
23369
 
23055
23370
  // src/commands/ga4/index.ts
23056
- var ga4Command = defineCommand103({
23371
+ var ga4Command = defineCommand105({
23057
23372
  meta: {
23058
23373
  name: "ga4",
23059
23374
  description: `Google Analytics 4 commands. Audit property config, run playbook-aligned reports.
@@ -23077,12 +23392,12 @@ Full guide: __tooling__/docs/tools/baker/ga4.md`
23077
23392
  });
23078
23393
 
23079
23394
  // src/commands/gsc/index.ts
23080
- import { defineCommand as defineCommand107 } from "citty";
23395
+ import { defineCommand as defineCommand109 } from "citty";
23081
23396
 
23082
23397
  // src/commands/gsc/query.ts
23083
23398
  import { appendFileSync as appendFileSync3, existsSync as existsSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "fs";
23084
23399
  import { resolve as resolve3 } from "path";
23085
- import { defineCommand as defineCommand104 } from "citty";
23400
+ import { defineCommand as defineCommand106 } from "citty";
23086
23401
 
23087
23402
  // src/commands/gsc/presets.ts
23088
23403
  var GSC_PRESETS = [
@@ -23270,7 +23585,7 @@ function handleError2(err) {
23270
23585
  });
23271
23586
  process.exit(1);
23272
23587
  }
23273
- var queryCommand3 = defineCommand104({
23588
+ var queryCommand3 = defineCommand106({
23274
23589
  meta: {
23275
23590
  name: "query",
23276
23591
  description: `Run GSC Search Analytics queries. Preset-first with free-form escape hatch.
@@ -23348,7 +23663,7 @@ Free-form (escape hatch):
23348
23663
  });
23349
23664
 
23350
23665
  // src/commands/gsc/sitemaps.ts
23351
- import { defineCommand as defineCommand105 } from "citty";
23666
+ import { defineCommand as defineCommand107 } from "citty";
23352
23667
  registerSchema({
23353
23668
  command: "gsc.sitemaps",
23354
23669
  description: "List sitemaps for a Search Console site. Check sitemap health and errors.",
@@ -23357,7 +23672,7 @@ registerSchema({
23357
23672
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
23358
23673
  }
23359
23674
  });
23360
- var sitemapsCommand = defineCommand105({
23675
+ var sitemapsCommand = defineCommand107({
23361
23676
  meta: {
23362
23677
  name: "sitemaps",
23363
23678
  description: `List sitemaps for a site. Check health and errors.
@@ -23407,7 +23722,7 @@ Examples:
23407
23722
  });
23408
23723
 
23409
23724
  // src/commands/gsc/sites.ts
23410
- import { defineCommand as defineCommand106 } from "citty";
23725
+ import { defineCommand as defineCommand108 } from "citty";
23411
23726
  registerSchema({
23412
23727
  command: "gsc.sites",
23413
23728
  description: "List all verified Google Search Console sites. Returns site URLs needed for query and sitemaps commands.",
@@ -23415,7 +23730,7 @@ registerSchema({
23415
23730
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
23416
23731
  }
23417
23732
  });
23418
- var sitesCommand = defineCommand106({
23733
+ var sitesCommand = defineCommand108({
23419
23734
  meta: {
23420
23735
  name: "sites",
23421
23736
  description: `List verified Search Console sites.
@@ -23463,7 +23778,7 @@ Examples:
23463
23778
  });
23464
23779
 
23465
23780
  // src/commands/gsc/index.ts
23466
- var gscCommand = defineCommand107({
23781
+ var gscCommand = defineCommand109({
23467
23782
  meta: {
23468
23783
  name: "gsc",
23469
23784
  description: `Google Search Console commands. PPC-SEO arbitrage, brand halo analysis, negative keyword discovery.
@@ -23487,7 +23802,7 @@ Full guide: __tooling__/docs/tools/baker/gsc.md`
23487
23802
  });
23488
23803
 
23489
23804
  // src/commands/history/index.ts
23490
- import { defineCommand as defineCommand108 } from "citty";
23805
+ import { defineCommand as defineCommand110 } from "citty";
23491
23806
  registerSchema({
23492
23807
  command: "history.list",
23493
23808
  description: "Start here: unified account history (audit log) \u2014 everything that changed on this account, newest first: publishes, chat lifecycle, backlog actions, team changes, setup links, tags, schedules, ad-platform writes, followed advertisers, media, creatives, reports, domains, and integrations. Use it to see what happened recently before planning work. Compact by default; add --full for raw metadata per entry.",
@@ -23542,7 +23857,7 @@ function parseBoundedInt2(raw, name, min, max) {
23542
23857
  }
23543
23858
  return value;
23544
23859
  }
23545
- var listCommand7 = defineCommand108({
23860
+ var listCommand7 = defineCommand110({
23546
23861
  meta: {
23547
23862
  name: "list",
23548
23863
  description: "List recent account changes (unified audit log), newest first."
@@ -23588,7 +23903,7 @@ var listCommand7 = defineCommand108({
23588
23903
  }
23589
23904
  }
23590
23905
  });
23591
- var historyCommand = defineCommand108({
23906
+ var historyCommand = defineCommand110({
23592
23907
  meta: {
23593
23908
  name: "history",
23594
23909
  description: `Unified account history (audit log): what changed, who did it, and when.
@@ -23598,10 +23913,10 @@ Full guide: __tooling__/docs/tools/baker/history.md`
23598
23913
  });
23599
23914
 
23600
23915
  // src/commands/images/index.ts
23601
- import { defineCommand as defineCommand132 } from "citty";
23916
+ import { defineCommand as defineCommand134 } from "citty";
23602
23917
 
23603
23918
  // src/commands/images/crop.ts
23604
- import { defineCommand as defineCommand109 } from "citty";
23919
+ import { defineCommand as defineCommand111 } from "citty";
23605
23920
 
23606
23921
  // src/lib/image/crop-sprite.ts
23607
23922
  import sharp from "sharp";
@@ -23616,7 +23931,7 @@ function cropSprite(input, region) {
23616
23931
 
23617
23932
  // src/lib/image/io.ts
23618
23933
  import { randomBytes } from "crypto";
23619
- import { glob as fsGlob, readFile as readFile18, rename, stat as stat4, writeFile as writeFile9 } from "fs/promises";
23934
+ import { glob as fsGlob, readFile as readFile19, rename, stat as stat4, writeFile as writeFile10 } from "fs/promises";
23620
23935
  import { dirname as dirname2, extname as extname2, join as join4, resolve as resolve4 } from "path";
23621
23936
  var REMOTE_RE = /^https?:\/\//i;
23622
23937
  var GLOB_RE = /[*?[\]{}]/;
@@ -23652,11 +23967,11 @@ async function readImageBuffer(pathOrUrl) {
23652
23967
  }
23653
23968
  return Buffer.from(await response.arrayBuffer());
23654
23969
  }
23655
- return readFile18(pathOrUrl);
23970
+ return readFile19(pathOrUrl);
23656
23971
  }
23657
- async function isDirectory(path27) {
23972
+ async function isDirectory(path28) {
23658
23973
  try {
23659
- const s = await stat4(path27);
23974
+ const s = await stat4(path28);
23660
23975
  return s.isDirectory();
23661
23976
  } catch {
23662
23977
  return false;
@@ -23675,7 +23990,7 @@ async function atomicWrite(targetPath, data) {
23675
23990
  const absolute = resolve4(targetPath);
23676
23991
  const dir = dirname2(absolute);
23677
23992
  const tmp = join4(dir, `.baker-image-${randomBytes(8).toString("hex")}.tmp`);
23678
- await writeFile9(tmp, data);
23993
+ await writeFile10(tmp, data);
23679
23994
  await rename(tmp, absolute);
23680
23995
  }
23681
23996
 
@@ -23726,7 +24041,7 @@ function emitError2(err) {
23726
24041
  }
23727
24042
  process.exit(1);
23728
24043
  }
23729
- var cropCommand = defineCommand109({
24044
+ var cropCommand = defineCommand111({
23730
24045
  meta: {
23731
24046
  name: "crop",
23732
24047
  description: "Crop a rectangular region from an image.\n\nExample: baker images crop sprite.png --x 0 --y 0 --width 64 --height 64 --output icon.png"
@@ -23762,7 +24077,7 @@ var cropCommand = defineCommand109({
23762
24077
  });
23763
24078
 
23764
24079
  // src/commands/images/delete.ts
23765
- import { defineCommand as defineCommand110 } from "citty";
24080
+ import { defineCommand as defineCommand112 } from "citty";
23766
24081
  registerSchema({
23767
24082
  command: "images.delete",
23768
24083
  description: "Delete an image by ID",
@@ -23776,7 +24091,7 @@ registerSchema({
23776
24091
  }
23777
24092
  }
23778
24093
  });
23779
- var deleteCommand = defineCommand110({
24094
+ var deleteCommand = defineCommand112({
23780
24095
  meta: {
23781
24096
  name: "delete",
23782
24097
  description: "Delete an image by ID. Use --dry-run to preview. Example: baker images delete j571abc123 --dry-run"
@@ -23817,7 +24132,7 @@ var deleteCommand = defineCommand110({
23817
24132
  });
23818
24133
 
23819
24134
  // src/commands/images/dimensions.ts
23820
- import { defineCommand as defineCommand111 } from "citty";
24135
+ import { defineCommand as defineCommand113 } from "citty";
23821
24136
 
23822
24137
  // src/lib/image/dimensions.ts
23823
24138
  import { imageSize } from "image-size";
@@ -23840,7 +24155,7 @@ registerSchema({
23840
24155
  target: { type: "string", description: "Local file path or remote http(s) URL", required: true }
23841
24156
  }
23842
24157
  });
23843
- var dimensionsCommand = defineCommand111({
24158
+ var dimensionsCommand = defineCommand113({
23844
24159
  meta: {
23845
24160
  name: "dimensions",
23846
24161
  description: "Read image dimensions without decoding the full file.\n\nExample: baker images dimensions ./logo.png\nExample: baker images dimensions https://acme.com/hero.png"
@@ -23884,7 +24199,7 @@ var dimensionsCommand = defineCommand111({
23884
24199
  });
23885
24200
 
23886
24201
  // src/commands/images/extract.ts
23887
- import { defineCommand as defineCommand112 } from "citty";
24202
+ import { defineCommand as defineCommand114 } from "citty";
23888
24203
  registerSchema({
23889
24204
  command: "images.extract",
23890
24205
  description: "Extract images from a URL via Firecrawl (formats: images).",
@@ -23900,7 +24215,7 @@ registerSchema({
23900
24215
  }
23901
24216
  }
23902
24217
  });
23903
- var extractCommand = defineCommand112({
24218
+ var extractCommand = defineCommand114({
23904
24219
  meta: {
23905
24220
  name: "extract",
23906
24221
  description: "Pull every image from a single URL via Firecrawl. ~$0.001/scrape. Cap auto-ingest at 20.\n\nExample: baker images extract https://stripe.com --auto-ingest 5"
@@ -23938,7 +24253,7 @@ var extractCommand = defineCommand112({
23938
24253
  });
23939
24254
 
23940
24255
  // src/commands/images/find.ts
23941
- import { defineCommand as defineCommand113 } from "citty";
24256
+ import { defineCommand as defineCommand115 } from "citty";
23942
24257
  registerSchema({
23943
24258
  command: "images.find",
23944
24259
  description: "Fanout image search: library first, then opted-in external providers.",
@@ -23970,7 +24285,7 @@ registerSchema({
23970
24285
  }
23971
24286
  }
23972
24287
  });
23973
- var findCommand = defineCommand113({
24288
+ var findCommand = defineCommand115({
23974
24289
  meta: {
23975
24290
  name: "find",
23976
24291
  description: "Library-first fanout image search. Opt in to providers with --sources. `--fallback` short-circuits to externals only when library is thin. With --auto-ingest, ingested external hits return Baker-owned URLs.\n\nExample: baker images find 'office' --sources library,magnific --limit 20"
@@ -24018,8 +24333,8 @@ var findCommand = defineCommand113({
24018
24333
  });
24019
24334
 
24020
24335
  // src/commands/images/generate.ts
24021
- import { readFile as readFile19 } from "fs/promises";
24022
- import { defineCommand as defineCommand114 } from "citty";
24336
+ import { readFile as readFile20 } from "fs/promises";
24337
+ import { defineCommand as defineCommand116 } from "citty";
24023
24338
  import sharp2 from "sharp";
24024
24339
  var GENERATE_TIMEOUT_MS = 18e4;
24025
24340
  var REFERENCE_MAX_EDGE = 1536;
@@ -24108,7 +24423,7 @@ async function resolveReferences(spec) {
24108
24423
  }
24109
24424
  let raw;
24110
24425
  try {
24111
- raw = await readFile19(entry);
24426
+ raw = await readFile20(entry);
24112
24427
  } catch {
24113
24428
  throw new ApiError("VALIDATION_ERROR", `Reference file not found: ${entry}`);
24114
24429
  }
@@ -24122,7 +24437,7 @@ async function resolveReferences(spec) {
24122
24437
  }
24123
24438
  return out;
24124
24439
  }
24125
- var generateCommand = defineCommand114({
24440
+ var generateCommand = defineCommand116({
24126
24441
  meta: {
24127
24442
  name: "generate",
24128
24443
  description: "Generate an image with AI and store it in the library (cost-tracked per request via OpenRouter usage). Models mirror the canvas: google/gemini-3.1-flash-image-preview (Nano Banana flash \u2014 default, fast, extreme aspect ratios), google/gemini-3-pro-image-preview (Nano Banana Pro \u2014 highest fidelity), openai/gpt-image-2 (photoreal, cleanest in-image text, best for ad/landing reproduction \u2014 no --image-size, and no 4:5 / 5:4), recraft/recraft-v4.1-pro-vector (vector/SVG-style with palette control). The result is auto-ingested (describe + embed), so the next `baker images library` query finds it. Pass --reference with image URLs and/or local file paths (Pinterest, stock, brand assets, sandbox files) to ground generation in reality.\n\nExamples:\n baker images generate 'a friendly golden retriever sitting in a bright modern living room' --aspect-ratio 16:9\n baker images generate 'hero shot of a matte black water bottle on marble' --model openai/gpt-image-2 --aspect-ratio 3:2\n baker images generate 'lifestyle photo matching this mood' --reference 'https://\u2026/ref1.jpg,https://\u2026/ref2.jpg'\n baker images generate 'put this product on a marble countertop, soft daylight' --reference './src/brand/logos/product.png,./refs/kitchen-mood.jpg'\n baker images generate 'flat geometric mascot, brand palette' --model recraft/recraft-v4.1-pro-vector --rgb-colors '[[10,10,10],[255,80,0]]'"
@@ -24174,7 +24489,7 @@ var generateCommand = defineCommand114({
24174
24489
  });
24175
24490
 
24176
24491
  // src/commands/images/get.ts
24177
- import { defineCommand as defineCommand115 } from "citty";
24492
+ import { defineCommand as defineCommand117 } from "citty";
24178
24493
  registerSchema({
24179
24494
  command: "images.get",
24180
24495
  description: "Get a single image by ID",
@@ -24182,7 +24497,7 @@ registerSchema({
24182
24497
  id: { type: "string", description: "Image ID", required: true }
24183
24498
  }
24184
24499
  });
24185
- var getCommand2 = defineCommand115({
24500
+ var getCommand2 = defineCommand117({
24186
24501
  meta: { name: "get", description: "Get a single image by ID. Example: baker images get j571abc123" },
24187
24502
  args: {
24188
24503
  id: { type: "positional", description: "Image ID", required: false },
@@ -24218,7 +24533,7 @@ var getCommand2 = defineCommand115({
24218
24533
  });
24219
24534
 
24220
24535
  // src/commands/images/gif.ts
24221
- import { defineCommand as defineCommand116 } from "citty";
24536
+ import { defineCommand as defineCommand118 } from "citty";
24222
24537
  registerSchema({
24223
24538
  command: "images.gif",
24224
24539
  description: "Search Giphy for GIFs / reaction memes (paid social creative).",
@@ -24250,7 +24565,7 @@ registerSchema({
24250
24565
  }
24251
24566
  }
24252
24567
  });
24253
- var gifCommand = defineCommand116({
24568
+ var gifCommand = defineCommand118({
24254
24569
  meta: {
24255
24570
  name: "gif",
24256
24571
  description: "Search Giphy for GIFs / reaction memes \u2014 built for paid-social creative (Meta, TikTok, LinkedIn, X). Free API. Each hit carries WebP + GIF + MP4 URLs in providerMeta so you can pick the right format per platform.\n\nExample: baker images gif 'this is fine' --limit 10\nExample: baker images gif 'office reaction' --rating pg --auto-ingest 2\nExample: baker images gif --trending --limit 25"
@@ -24297,7 +24612,7 @@ var gifCommand = defineCommand116({
24297
24612
  });
24298
24613
 
24299
24614
  // src/commands/images/google.ts
24300
- import { defineCommand as defineCommand117 } from "citty";
24615
+ import { defineCommand as defineCommand119 } from "citty";
24301
24616
 
24302
24617
  // src/commands/images/searchHints.ts
24303
24618
  var FALLBACK = {
@@ -24361,7 +24676,7 @@ registerSchema({
24361
24676
  }
24362
24677
  }
24363
24678
  });
24364
- var googleCommand2 = defineCommand117({
24679
+ var googleCommand2 = defineCommand119({
24365
24680
  meta: {
24366
24681
  name: "google",
24367
24682
  description: "Google Images via the official Custom Search JSON API ($0.005/query, free 100/day). \u26A0 Source unverified \u2014 watermarks, low-res, mislabeled results are common. Use as last resort. With --auto-ingest, ingested hits return Baker-owned URLs.\n\nExample: baker images google 'industrial workshop' --type photo --size large --limit 20"
@@ -24417,7 +24732,7 @@ var googleCommand2 = defineCommand117({
24417
24732
  });
24418
24733
 
24419
24734
  // src/commands/images/icon.ts
24420
- import { defineCommand as defineCommand118 } from "citty";
24735
+ import { defineCommand as defineCommand120 } from "citty";
24421
24736
  registerSchema({
24422
24737
  command: "images.icon",
24423
24738
  description: "Icon lookup via Iconify (200+ icon sets, free CDN).",
@@ -24443,7 +24758,7 @@ registerSchema({
24443
24758
  }
24444
24759
  }
24445
24760
  });
24446
- var iconCommand = defineCommand118({
24761
+ var iconCommand = defineCommand120({
24447
24762
  meta: {
24448
24763
  name: "icon",
24449
24764
  description: "Icon via Iconify (simple-icons, logos, lucide, devicon, heroicons, tabler, phosphor, material-symbols, \u2026). Free CDN, no API key.\n\nExample: baker images icon react --set devicon\nExample: baker images icon lucide:check --color '#0a0a0a'"
@@ -24483,7 +24798,7 @@ var iconCommand = defineCommand118({
24483
24798
  });
24484
24799
 
24485
24800
  // src/commands/images/ingest.ts
24486
- import { defineCommand as defineCommand119 } from "citty";
24801
+ import { defineCommand as defineCommand121 } from "citty";
24487
24802
  registerSchema({
24488
24803
  command: "images.ingest",
24489
24804
  description: "Ingest a remote image URL into the library (full describe + embed).",
@@ -24495,7 +24810,7 @@ registerSchema({
24495
24810
  context: { type: "string", description: "Description context hint", required: false }
24496
24811
  }
24497
24812
  });
24498
- var ingestCommand = defineCommand119({
24813
+ var ingestCommand = defineCommand121({
24499
24814
  meta: {
24500
24815
  name: "ingest",
24501
24816
  description: "Download a remote URL and store it in the library. Hash-deduped on bytes + externalId.\n\nExample: baker images ingest https://img.freepik.com/free-photo/xyz.jpg --source magnific --external-id 12345"
@@ -24537,7 +24852,7 @@ var ingestCommand = defineCommand119({
24537
24852
  });
24538
24853
 
24539
24854
  // src/commands/images/library.ts
24540
- import { defineCommand as defineCommand120 } from "citty";
24855
+ import { defineCommand as defineCommand122 } from "citty";
24541
24856
  registerSchema({
24542
24857
  command: "images.library",
24543
24858
  description: "Search the company image library. Returns only ready images.",
@@ -24563,7 +24878,7 @@ registerSchema({
24563
24878
  }
24564
24879
  }
24565
24880
  });
24566
- var libraryCommand = defineCommand120({
24881
+ var libraryCommand = defineCommand122({
24567
24882
  meta: {
24568
24883
  name: "library",
24569
24884
  description: "Search the company image library (hybrid BM25 + vector + Cohere rerank). Use this BEFORE any external provider.\n\nExample: baker images library 'hero banner' --aspect-ratio 16:9 --source magnific"
@@ -24620,7 +24935,7 @@ var libraryCommand = defineCommand120({
24620
24935
  });
24621
24936
 
24622
24937
  // src/commands/images/logo.ts
24623
- import { defineCommand as defineCommand121 } from "citty";
24938
+ import { defineCommand as defineCommand123 } from "citty";
24624
24939
  registerSchema({
24625
24940
  command: "images.logo",
24626
24941
  description: "Brand logo lookup via Brandfetch CDN (fallback/404). Auto-ingests by default.",
@@ -24645,7 +24960,7 @@ registerSchema({
24645
24960
  }
24646
24961
  }
24647
24962
  });
24648
- var logoCommand = defineCommand121({
24963
+ var logoCommand = defineCommand123({
24649
24964
  meta: {
24650
24965
  name: "logo",
24651
24966
  description: "Brand logo via Brandfetch CDN. Returns up to 5 variants (icon, light/dark logo, light/dark symbol). Auto-ingests the first variant.\n\nExample: baker images logo stripe.com --variant logo"
@@ -24683,7 +24998,7 @@ var logoCommand = defineCommand121({
24683
24998
  });
24684
24999
 
24685
25000
  // src/commands/images/normalize.ts
24686
- import { defineCommand as defineCommand122 } from "citty";
25001
+ import { defineCommand as defineCommand124 } from "citty";
24687
25002
 
24688
25003
  // src/lib/image/color-changer.ts
24689
25004
  import quantize from "quantize";
@@ -25415,7 +25730,7 @@ function coerceRawArgs(args) {
25415
25730
  "dry-run": bool(args["dry-run"])
25416
25731
  };
25417
25732
  }
25418
- var normalizeCommand = defineCommand122({
25733
+ var normalizeCommand = defineCommand124({
25419
25734
  meta: {
25420
25735
  name: "normalize",
25421
25736
  description: `Normalize logos / images: declarative recolor + bg removal + trim + resize. Operates on local files; writes in-place by default.
@@ -25470,7 +25785,7 @@ Examples:
25470
25785
  });
25471
25786
 
25472
25787
  // src/commands/images/pinterest.ts
25473
- import { defineCommand as defineCommand123 } from "citty";
25788
+ import { defineCommand as defineCommand125 } from "citty";
25474
25789
  registerSchema({
25475
25790
  command: "images.pinterest",
25476
25791
  description: "Pinterest image search via ScrapeCreators. Reference-grade real-world photography, product styling, interiors, fashion, food, and aesthetic mood boards. Inspect before placing \u2014 Pinterest is unverified, trademark-bearing web content.",
@@ -25490,7 +25805,7 @@ registerSchema({
25490
25805
  }
25491
25806
  }
25492
25807
  });
25493
- var pinterestCommand = defineCommand123({
25808
+ var pinterestCommand = defineCommand125({
25494
25809
  meta: {
25495
25810
  name: "pinterest",
25496
25811
  description: "Pinterest image search via ScrapeCreators ($0.00188/request). Best for photo-realistic reference imagery \u2014 lifestyle, interiors, fashion, food, product styling, and mood boards to brief AI generation against. \u26A0 Unverified, trademark-bearing web content \u2014 inspect and respect rights before placing on a customer page. Browse first; auto-ingest only the pins you commit to.\n\nExamples:\n baker images pinterest 'scandinavian living room'\n baker images pinterest 'minimalist skincare product photography' --limit 20\n baker images pinterest 'cozy coffee shop interior' --auto-ingest 2 --context 'Mood reference for hero photography'"
@@ -25530,7 +25845,7 @@ var pinterestCommand = defineCommand123({
25530
25845
  });
25531
25846
 
25532
25847
  // src/commands/images/screenshot.ts
25533
- import { defineCommand as defineCommand124 } from "citty";
25848
+ import { defineCommand as defineCommand126 } from "citty";
25534
25849
  registerSchema({
25535
25850
  command: "images.screenshot",
25536
25851
  description: "Capture a website screenshot via ScreenshotOne. Auto-ingests on success.",
@@ -25546,7 +25861,7 @@ registerSchema({
25546
25861
  }
25547
25862
  }
25548
25863
  });
25549
- var screenshotCommand = defineCommand124({
25864
+ var screenshotCommand = defineCommand126({
25550
25865
  meta: {
25551
25866
  name: "screenshot",
25552
25867
  description: "Screenshot a URL via ScreenshotOne. $0.009/capture. Auto-ingests to library.\n\nExample: baker images screenshot https://stripe.com --full-page"
@@ -25609,7 +25924,7 @@ var screenshotCommand = defineCommand124({
25609
25924
  });
25610
25925
 
25611
25926
  // src/commands/images/search.ts
25612
- import { defineCommand as defineCommand125 } from "citty";
25927
+ import { defineCommand as defineCommand127 } from "citty";
25613
25928
  registerSchema({
25614
25929
  command: "images.search",
25615
25930
  description: "Search images by text query. Only returns ready images.",
@@ -25625,7 +25940,7 @@ registerSchema({
25625
25940
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
25626
25941
  }
25627
25942
  });
25628
- var searchCommand = defineCommand125({
25943
+ var searchCommand = defineCommand127({
25629
25944
  meta: {
25630
25945
  name: "search",
25631
25946
  description: "Semantic search images by text query. Uses hybrid BM25 + vector + reranking. Example: baker images search 'hero banner' --aspect-ratio 16:9 --tags logo"
@@ -25685,7 +26000,7 @@ var searchCommand = defineCommand125({
25685
26000
  });
25686
26001
 
25687
26002
  // src/commands/images/sticker.ts
25688
- import { defineCommand as defineCommand126 } from "citty";
26003
+ import { defineCommand as defineCommand128 } from "citty";
25689
26004
  registerSchema({
25690
26005
  command: "images.sticker",
25691
26006
  description: "Search Giphy stickers \u2014 transparent-background overlays for ad creative.",
@@ -25717,7 +26032,7 @@ registerSchema({
25717
26032
  }
25718
26033
  }
25719
26034
  });
25720
- var stickerCommand = defineCommand126({
26035
+ var stickerCommand = defineCommand128({
25721
26036
  meta: {
25722
26037
  name: "sticker",
25723
26038
  description: "Search Giphy's sticker corpus \u2014 transparent-background WebPs / GIFs ideal for overlaying on ad creative (Meta, TikTok, Stories). Same Giphy free API as `baker images gif`; results carry WebP + GIF + MP4 URLs in providerMeta.\n\nExample: baker images sticker 'thumbs up' --limit 10\nExample: baker images sticker celebration --rating g --auto-ingest 3\nExample: baker images sticker --trending --limit 25"
@@ -25764,7 +26079,7 @@ var stickerCommand = defineCommand126({
25764
26079
  });
25765
26080
 
25766
26081
  // src/commands/images/stock.ts
25767
- import { defineCommand as defineCommand127 } from "citty";
26082
+ import { defineCommand as defineCommand129 } from "citty";
25768
26083
  var STOCK_ERROR_FIX = {
25769
26084
  action: "use_different_resource",
25770
26085
  explanation: "Switch provider instead of retrying stock search. Stock search is one of several image sources. Run `baker images find <query> --sources library,pinterest,google` (`--sources` is required \u2014 `find` alone searches the library only) or `baker images generate` to make the asset. Never sleep-and-retry this command \u2014 the CLI already backs off on rate limits. If no image can be sourced, continue the rest of the task with a placeholder rather than aborting it."
@@ -25841,7 +26156,7 @@ function buildStockRequest(query, args) {
25841
26156
  if (args.context) body.descriptionContext = args.context;
25842
26157
  return body;
25843
26158
  }
25844
- var stockCommand = defineCommand127({
26159
+ var stockCommand = defineCommand129({
25845
26160
  meta: {
25846
26161
  name: "stock",
25847
26162
  description: "Stock search via Magnific \u2014 Freepik's developer API (~250M assets: photos, vectors, illustrations, icons, PSDs). $0.002/req. With --auto-ingest, ingested hits return Baker-owned URLs.\n\nExamples:\n baker images stock 'minimalist office'\n baker images stock 'flat office workers' --type vector\n baker images stock 'hero photo of a kitchen' --type photo --orientation landscape --ai exclude\n baker images stock 'brand pattern' --color '#0a0a0a' --license freemium --auto-ingest 2"
@@ -25897,7 +26212,7 @@ var stockCommand = defineCommand127({
25897
26212
  });
25898
26213
 
25899
26214
  // src/lib/tags-command.ts
25900
- import { defineCommand as defineCommand128 } from "citty";
26215
+ import { defineCommand as defineCommand130 } from "citty";
25901
26216
  function makeTagsCommand(command, label, endpoint) {
25902
26217
  registerSchema({
25903
26218
  command: `${command}.tags`,
@@ -25906,7 +26221,7 @@ function makeTagsCommand(command, label, endpoint) {
25906
26221
  output: { type: "string", description: "Output format: md|json", required: false, default: "md" }
25907
26222
  }
25908
26223
  });
25909
- return defineCommand128({
26224
+ return defineCommand130({
25910
26225
  meta: {
25911
26226
  name: "tags",
25912
26227
  description: `List the available ${label} tag names (defaults + company custom tags). Use before filtering with --tags. Example: baker ${command} tags`
@@ -25942,7 +26257,7 @@ function makeTagsCommand(command, label, endpoint) {
25942
26257
  var tagsCommand2 = makeTagsCommand("images", "image", "/api/images/tags");
25943
26258
 
25944
26259
  // src/commands/images/upload.ts
25945
- import { defineCommand as defineCommand129 } from "citty";
26260
+ import { defineCommand as defineCommand131 } from "citty";
25946
26261
  registerSchema({
25947
26262
  command: "images.upload",
25948
26263
  description: "Upload an image to the library \u2014 local file path or remote http(s) URL.",
@@ -25980,7 +26295,7 @@ registerSchema({
25980
26295
  function isRemoteUrl2(value) {
25981
26296
  return /^https?:\/\//i.test(value);
25982
26297
  }
25983
- var uploadCommand = defineCommand129({
26298
+ var uploadCommand = defineCommand131({
25984
26299
  meta: {
25985
26300
  name: "upload",
25986
26301
  description: "Upload an image to the library \u2014 accepts a local file path OR a remote http(s) URL.\n\nLocal: reads bytes, sends to /api/images/upload, content-type auto-detected from extension.\nRemote: dispatches to /api/images/ingest with hash-dedup on bytes + externalId.\n\nExamples:\n baker images upload ./logo.png --source uploaded\n baker images upload ./cert.png --context 'ISO 27001 badge \u2014 enterprise tier'\n baker images upload https://acme.com/hero.png --source firecrawl --context 'Acme competitor pricing hero'"
@@ -26073,7 +26388,7 @@ async function uploadLocal(target, args) {
26073
26388
  }
26074
26389
 
26075
26390
  // src/commands/images/upscale.ts
26076
- import { defineCommand as defineCommand130 } from "citty";
26391
+ import { defineCommand as defineCommand132 } from "citty";
26077
26392
  registerSchema({
26078
26393
  command: "images.upscale",
26079
26394
  description: "Upscale a library image via the backend (Replicate, cost-tracked). Waits for completion by default. The image must be status 'ready' and raster (not SVG/AVIF).",
@@ -26088,7 +26403,7 @@ registerSchema({
26088
26403
  }
26089
26404
  });
26090
26405
  var POLL_INTERVAL_MS3 = 1500;
26091
- var upscaleCommand = defineCommand130({
26406
+ var upscaleCommand = defineCommand132({
26092
26407
  meta: {
26093
26408
  name: "upscale",
26094
26409
  description: "Upscale a library image via the Convex backend (Replicate, cost-tracked at $0.05/image). Waits for completion by default.\n\nExample: baker images upscale j571abc123def\nExample: baker images upscale j571abc123def --max-wait 0 # fire-and-forget"
@@ -26143,7 +26458,7 @@ var upscaleCommand = defineCommand130({
26143
26458
  });
26144
26459
 
26145
26460
  // src/commands/images/use.ts
26146
- import { defineCommand as defineCommand131 } from "citty";
26461
+ import { defineCommand as defineCommand133 } from "citty";
26147
26462
  registerSchema({
26148
26463
  command: "images.use",
26149
26464
  description: "Ingest a URL and wait for the library record to be ready.",
@@ -26159,7 +26474,7 @@ registerSchema({
26159
26474
  }
26160
26475
  });
26161
26476
  var POLL_INTERVAL_MS4 = 1500;
26162
- var useCommand = defineCommand131({
26477
+ var useCommand = defineCommand133({
26163
26478
  meta: {
26164
26479
  name: "use",
26165
26480
  description: "Sugar over `ingest`: download \u2192 store \u2192 wait until describe + embed complete \u2192 return ready library record.\n\nExample: baker images use https://cdn.example.com/hero.png --source uploaded"
@@ -26205,7 +26520,7 @@ var useCommand = defineCommand131({
26205
26520
  });
26206
26521
 
26207
26522
  // src/commands/images/index.ts
26208
- var imagesCommand = defineCommand132({
26523
+ var imagesCommand = defineCommand134({
26209
26524
  meta: {
26210
26525
  name: "images",
26211
26526
  description: `Find, source, and normalize images. Subcommands route by provider so cost + license are explicit.
@@ -26276,16 +26591,16 @@ Full guide: __tooling__/docs/tools/baker/images.md`
26276
26591
  });
26277
26592
 
26278
26593
  // src/commands/landing/index.ts
26279
- import { defineCommand as defineCommand134 } from "citty";
26594
+ import { defineCommand as defineCommand136 } from "citty";
26280
26595
 
26281
26596
  // src/commands/landing/critique.ts
26282
26597
  import { readdir as readdir8, stat as stat6 } from "fs/promises";
26283
- import path26 from "path";
26284
- import { defineCommand as defineCommand133 } from "citty";
26598
+ import path27 from "path";
26599
+ import { defineCommand as defineCommand135 } from "citty";
26285
26600
 
26286
26601
  // src/engine/landing/lib/brand-tokens.ts
26287
- import { readFile as readFile20 } from "fs/promises";
26288
- import path23 from "path";
26602
+ import { readFile as readFile21 } from "fs/promises";
26603
+ import path24 from "path";
26289
26604
 
26290
26605
  // src/engine/landing/lib/color.ts
26291
26606
  var NEUTRAL_COLOR_KEYWORDS = /* @__PURE__ */ new Set([
@@ -26447,14 +26762,14 @@ function parseBrandMd(brandMd, fonts, colors) {
26447
26762
  }
26448
26763
  }
26449
26764
  async function loadBrandTokens(projectRoot) {
26450
- const globalCss = await safeRead(path23.join(projectRoot, "src", "styles", "global.css"));
26451
- const brandMd = await safeRead(path23.join(projectRoot, "src", "brand", "BRAND.md"));
26765
+ const globalCss = await safeRead(path24.join(projectRoot, "src", "styles", "global.css"));
26766
+ const brandMd = await safeRead(path24.join(projectRoot, "src", "brand", "BRAND.md"));
26452
26767
  if (!globalCss && !brandMd) return EMPTY;
26453
26768
  return parseBrandTokens(globalCss, brandMd);
26454
26769
  }
26455
26770
  async function safeRead(file) {
26456
26771
  try {
26457
- return await readFile20(file, "utf8");
26772
+ return await readFile21(file, "utf8");
26458
26773
  } catch {
26459
26774
  return "";
26460
26775
  }
@@ -27240,40 +27555,40 @@ function describeCounts(findings) {
27240
27555
  }
27241
27556
 
27242
27557
  // src/commands/landing/snapshot.ts
27243
- import { mkdir as mkdir7, rename as rename2, writeFile as writeFile10 } from "fs/promises";
27244
- import path24 from "path";
27558
+ import { mkdir as mkdir8, rename as rename2, writeFile as writeFile11 } from "fs/promises";
27559
+ import path25 from "path";
27245
27560
  var CRITIC_VERSION = "1";
27246
27561
  function critiqueCacheDir(projectRoot) {
27247
- return path24.join(projectRoot, ".cache", "landing-critique");
27562
+ return path25.join(projectRoot, ".cache", "landing-critique");
27248
27563
  }
27249
27564
  function snapshotPath(projectRoot, slug) {
27250
- return path24.join(critiqueCacheDir(projectRoot), `${slug}.json`);
27565
+ return path25.join(critiqueCacheDir(projectRoot), `${slug}.json`);
27251
27566
  }
27252
27567
  async function writeCritiqueSnapshot(projectRoot, snapshot) {
27253
- await mkdir7(critiqueCacheDir(projectRoot), { recursive: true });
27568
+ await mkdir8(critiqueCacheDir(projectRoot), { recursive: true });
27254
27569
  const dest = snapshotPath(projectRoot, snapshot.slug);
27255
27570
  const tmp = `${dest}.tmp`;
27256
- await writeFile10(tmp, `${JSON.stringify(snapshot, null, 2)}
27571
+ await writeFile11(tmp, `${JSON.stringify(snapshot, null, 2)}
27257
27572
  `, "utf8");
27258
27573
  await rename2(tmp, dest);
27259
27574
  }
27260
27575
 
27261
27576
  // src/commands/landing/source-version.ts
27262
- import { readdir as readdir7, readFile as readFile21, stat as stat5 } from "fs/promises";
27263
- import path25 from "path";
27577
+ import { readdir as readdir7, readFile as readFile22, stat as stat5 } from "fs/promises";
27578
+ import path26 from "path";
27264
27579
  async function landingSourceRelPaths(landingDir) {
27265
27580
  const rel = [];
27266
- if (await isFile(path25.join(landingDir, "index.astro"))) rel.push("index.astro");
27267
- const componentsDir = path25.join(landingDir, "_components");
27581
+ if (await isFile(path26.join(landingDir, "index.astro"))) rel.push("index.astro");
27582
+ const componentsDir = path26.join(landingDir, "_components");
27268
27583
  for (const abs of await walkAstro(componentsDir)) {
27269
- rel.push(path25.relative(landingDir, abs).split(path25.sep).join("/"));
27584
+ rel.push(path26.relative(landingDir, abs).split(path26.sep).join("/"));
27270
27585
  }
27271
27586
  return rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
27272
27587
  }
27273
27588
  async function readLandingSources(landingDir) {
27274
27589
  const rel = await landingSourceRelPaths(landingDir);
27275
27590
  const out = [];
27276
- for (const r of rel) out.push({ path: r, text: await readFile21(path25.join(landingDir, r), "utf8") });
27591
+ for (const r of rel) out.push({ path: r, text: await readFile22(path26.join(landingDir, r), "utf8") });
27277
27592
  return out;
27278
27593
  }
27279
27594
  async function computeLandingSourceSha(landingDir) {
@@ -27282,7 +27597,7 @@ async function computeLandingSourceSha(landingDir) {
27282
27597
  for (const r of rel) {
27283
27598
  let bytes;
27284
27599
  try {
27285
- bytes = await readFile21(path25.join(landingDir, r));
27600
+ bytes = await readFile22(path26.join(landingDir, r));
27286
27601
  } catch {
27287
27602
  bytes = Buffer.alloc(0);
27288
27603
  }
@@ -27306,7 +27621,7 @@ async function walkAstro(dir) {
27306
27621
  }
27307
27622
  const out = [];
27308
27623
  for (const entry of entries) {
27309
- const abs = path25.join(dir, entry.name);
27624
+ const abs = path26.join(dir, entry.name);
27310
27625
  if (entry.isDirectory()) out.push(...await walkAstro(abs));
27311
27626
  else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
27312
27627
  }
@@ -27327,14 +27642,14 @@ registerSchema({
27327
27642
  }
27328
27643
  });
27329
27644
  var SLUG_RE = /^[a-z0-9][a-z0-9._-]*$/i;
27330
- function fail4(code, message, fix) {
27645
+ function fail5(code, message, fix) {
27331
27646
  process.stderr.write(
27332
27647
  `${JSON.stringify({ ok: false, error: { code, message, ...fix ? { fix } : {} } }, null, 2)}
27333
27648
  `
27334
27649
  );
27335
27650
  process.exit(2);
27336
27651
  }
27337
- var critiqueCommand2 = defineCommand133({
27652
+ var critiqueCommand2 = defineCommand135({
27338
27653
  meta: {
27339
27654
  name: "critique",
27340
27655
  description: "Start here: `baker landing critique <slug>` after building or editing a landing. Deterministic design-quality critic (ADVISORY \u2014 findings never fail it). Flags the known AI design tells (gradient text, overused fonts, side-tab borders, cream palettes, buzzword copy, broken images) tiered block/warn/advisory, respecting the client's BRAND.md as the allowlist. Also records the critique that publishing requires \u2014 run it before finishing a landing."
@@ -27347,15 +27662,15 @@ var critiqueCommand2 = defineCommand133({
27347
27662
  const slug = String(args.slug);
27348
27663
  const projectRoot = process.cwd();
27349
27664
  if (!SLUG_RE.test(slug) || slug.includes("..")) {
27350
- fail4(
27665
+ fail5(
27351
27666
  "INVALID_SLUG",
27352
27667
  `"${slug}" is not a landing slug \u2014 use the folder name directly under src/pages/ (letters, digits, dashes; not a path, not a _-private folder).`,
27353
27668
  { availableSlugs: await listLandingSlugs(projectRoot) }
27354
27669
  );
27355
27670
  }
27356
- const landingDir = path26.resolve(projectRoot, "src", "pages", slug);
27671
+ const landingDir = path27.resolve(projectRoot, "src", "pages", slug);
27357
27672
  if (!await isDir(landingDir)) {
27358
- fail4("NOT_FOUND", `No landing at src/pages/${slug}/`, {
27673
+ fail5("NOT_FOUND", `No landing at src/pages/${slug}/`, {
27359
27674
  availableSlugs: await listLandingSlugs(projectRoot)
27360
27675
  });
27361
27676
  }
@@ -27408,7 +27723,7 @@ var critiqueCommand2 = defineCommand133({
27408
27723
  });
27409
27724
  async function listLandingSlugs(projectRoot) {
27410
27725
  try {
27411
- const entries = await readdir8(path26.join(projectRoot, "src", "pages"), { withFileTypes: true });
27726
+ const entries = await readdir8(path27.join(projectRoot, "src", "pages"), { withFileTypes: true });
27412
27727
  return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
27413
27728
  } catch {
27414
27729
  return [];
@@ -27434,7 +27749,7 @@ async function isDir(p) {
27434
27749
  }
27435
27750
 
27436
27751
  // src/commands/landing/index.ts
27437
- var landingCommand = defineCommand134({
27752
+ var landingCommand = defineCommand136({
27438
27753
  meta: {
27439
27754
  name: "landing",
27440
27755
  description: `Design-quality tools for landing pages (src/pages/<slug>/).
@@ -27450,7 +27765,7 @@ Subcommands:
27450
27765
  });
27451
27766
 
27452
27767
  // src/commands/mcp/index.ts
27453
- import { defineCommand as defineCommand135 } from "citty";
27768
+ import { defineCommand as defineCommand137 } from "citty";
27454
27769
  var SCOPES = ["user", "user_org", "company", "org"];
27455
27770
  function parseScope(raw) {
27456
27771
  const scope = raw === void 0 ? "company" : String(raw);
@@ -27476,7 +27791,7 @@ function parseHeaders(raw) {
27476
27791
  }
27477
27792
  return Object.keys(headers).length > 0 ? headers : void 0;
27478
27793
  }
27479
- function fail5(err) {
27794
+ function fail6(err) {
27480
27795
  if (err instanceof ApiError) {
27481
27796
  writeJson({ ok: false, error: { code: err.code, message: err.message } });
27482
27797
  process.exit(1);
@@ -27489,7 +27804,7 @@ registerSchema({
27489
27804
  description: "List every third-party tool this chat can reach: managed integrations (Attio, Slack, Gmail, Google Sheets, \u2026) plus custom MCP servers. Start here when the user mentions an external tool.",
27490
27805
  args: {}
27491
27806
  });
27492
- var connectedCommand = defineCommand135({
27807
+ var connectedCommand = defineCommand137({
27493
27808
  meta: {
27494
27809
  name: "connected",
27495
27810
  description: `List all connected third-party tools \u2014 managed integrations plus custom MCP servers.
@@ -27527,7 +27842,7 @@ A tool the user names that is NOT listed here is simply not connected yet.`
27527
27842
  hints
27528
27843
  });
27529
27844
  } catch (err) {
27530
- fail5(err);
27845
+ fail6(err);
27531
27846
  }
27532
27847
  }
27533
27848
  });
@@ -27536,7 +27851,7 @@ registerSchema({
27536
27851
  description: "List the custom MCP servers this company's chats see (org + company + your own user scope).",
27537
27852
  args: {}
27538
27853
  });
27539
- var listCommand8 = defineCommand135({
27854
+ var listCommand8 = defineCommand137({
27540
27855
  meta: { name: "list", description: "List custom MCP servers visible to this company's chats." },
27541
27856
  run: async () => {
27542
27857
  try {
@@ -27547,7 +27862,7 @@ var listCommand8 = defineCommand135({
27547
27862
  );
27548
27863
  writeJson({ ok: true, data: data.servers });
27549
27864
  } catch (err) {
27550
- fail5(err);
27865
+ fail6(err);
27551
27866
  }
27552
27867
  }
27553
27868
  });
@@ -27561,7 +27876,7 @@ registerSchema({
27561
27876
  header: { type: "string", description: 'Auth header "Key: Value" (repeatable)', required: false }
27562
27877
  }
27563
27878
  });
27564
- var addCommand = defineCommand135({
27879
+ var addCommand = defineCommand137({
27565
27880
  meta: {
27566
27881
  name: "add",
27567
27882
  description: `Register a custom MCP server. Tools appear as mcp__<name>__* on the NEXT message.
@@ -27593,7 +27908,7 @@ Examples:
27593
27908
  });
27594
27909
  writeJson({ ok: true, data });
27595
27910
  } catch (err) {
27596
- fail5(err);
27911
+ fail6(err);
27597
27912
  }
27598
27913
  }
27599
27914
  });
@@ -27602,7 +27917,7 @@ registerSchema({
27602
27917
  description: "Remove a company custom MCP server by name.",
27603
27918
  args: { name: { type: "string", description: "Server name to remove", required: true } }
27604
27919
  });
27605
- var removeCommand4 = defineCommand135({
27920
+ var removeCommand4 = defineCommand137({
27606
27921
  meta: {
27607
27922
  name: "remove",
27608
27923
  description: `Remove a company custom MCP server by name.
@@ -27620,11 +27935,11 @@ Example:
27620
27935
  });
27621
27936
  writeJson({ ok: true, data });
27622
27937
  } catch (err) {
27623
- fail5(err);
27938
+ fail6(err);
27624
27939
  }
27625
27940
  }
27626
27941
  });
27627
- var mcpCommand = defineCommand135({
27942
+ var mcpCommand = defineCommand137({
27628
27943
  meta: {
27629
27944
  name: "mcp",
27630
27945
  description: `Third-party tools for this company \u2014 see what's connected, register custom HTTPS MCP endpoints.
@@ -27650,10 +27965,10 @@ Full guide: __tooling__/docs/tools/baker/mcp.md`
27650
27965
  });
27651
27966
 
27652
27967
  // src/commands/research/index.ts
27653
- import { defineCommand as defineCommand146 } from "citty";
27968
+ import { defineCommand as defineCommand148 } from "citty";
27654
27969
 
27655
27970
  // src/commands/research/advertisers.ts
27656
- import { defineCommand as defineCommand136 } from "citty";
27971
+ import { defineCommand as defineCommand138 } from "citty";
27657
27972
 
27658
27973
  // src/commands/research/output.ts
27659
27974
  var RESEARCH_DATA_NOTE = "Estimates based on third-party SERP data \u2014 not exact figures. Use for directional insights, not precise measurement.";
@@ -27766,7 +28081,7 @@ var FIELDS3 = {
27766
28081
  etv: "Estimated traffic value (USD)",
27767
28082
  visibility: "SERP visibility score (0-1)"
27768
28083
  };
27769
- var advertisersCommand = defineCommand136({
28084
+ var advertisersCommand = defineCommand138({
27770
28085
  meta: {
27771
28086
  name: "advertisers",
27772
28087
  description: `Find domains competing for a keyword in Google SERPs.
@@ -27813,7 +28128,7 @@ Examples:
27813
28128
  });
27814
28129
 
27815
28130
  // src/commands/research/autocomplete.ts
27816
- import { defineCommand as defineCommand137 } from "citty";
28131
+ import { defineCommand as defineCommand139 } from "citty";
27817
28132
  registerSchema({
27818
28133
  command: "research.autocomplete",
27819
28134
  description: "Get Google Autocomplete suggestions for a seed keyword. Useful for keyword expansion and discovering what people actually search for. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en).",
@@ -27836,7 +28151,7 @@ registerSchema({
27836
28151
  var FIELDS4 = {
27837
28152
  suggestion: "Autocomplete suggestion from Google"
27838
28153
  };
27839
- var autocompleteCommand = defineCommand137({
28154
+ var autocompleteCommand = defineCommand139({
27840
28155
  meta: {
27841
28156
  name: "autocomplete",
27842
28157
  description: `Get Google Autocomplete suggestions for keyword expansion.
@@ -27882,7 +28197,7 @@ Examples:
27882
28197
  });
27883
28198
 
27884
28199
  // src/commands/research/countries.ts
27885
- import { defineCommand as defineCommand138 } from "citty";
28200
+ import { defineCommand as defineCommand140 } from "citty";
27886
28201
  registerSchema({
27887
28202
  command: "research.countries",
27888
28203
  description: "List all supported country codes for --location flag in research commands.",
@@ -27939,7 +28254,7 @@ var FIELDS5 = {
27939
28254
  code: "Country code to pass as --location",
27940
28255
  name: "Country name"
27941
28256
  };
27942
- var countriesCommand = defineCommand138({
28257
+ var countriesCommand = defineCommand140({
27943
28258
  meta: {
27944
28259
  name: "countries",
27945
28260
  description: "List all supported country codes for --location flag."
@@ -27950,7 +28265,7 @@ var countriesCommand = defineCommand138({
27950
28265
  });
27951
28266
 
27952
28267
  // src/commands/research/intent.ts
27953
- import { defineCommand as defineCommand139 } from "citty";
28268
+ import { defineCommand as defineCommand141 } from "citty";
27954
28269
  registerSchema({
27955
28270
  command: "research.intent",
27956
28271
  description: "Classify Google Search intent for keywords. Determines if someone searching is looking to buy, research, or navigate. IMPORTANT: If --language is omitted, defaults to English (en). The response includes a query_context object showing which language was used.",
@@ -27973,7 +28288,7 @@ var FIELDS6 = {
27973
28288
  intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
27974
28289
  probability: "Confidence score 0.0-1.0"
27975
28290
  };
27976
- var intentCommand = defineCommand139({
28291
+ var intentCommand = defineCommand141({
27977
28292
  meta: {
27978
28293
  name: "intent",
27979
28294
  description: `Classify Google Search intent for keywords. Returns intent type and confidence.
@@ -28021,7 +28336,7 @@ Examples:
28021
28336
  });
28022
28337
 
28023
28338
  // src/commands/research/keyword-gap.ts
28024
- import { defineCommand as defineCommand140 } from "citty";
28339
+ import { defineCommand as defineCommand142 } from "citty";
28025
28340
  registerSchema({
28026
28341
  command: "research.keyword-gap",
28027
28342
  description: "Find keywords a competitor ranks for (organic or paid) that you don't. Discovers expansion opportunities. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en). The response includes a query_context object showing which location/language were used.",
@@ -28050,7 +28365,7 @@ var FIELDS7 = {
28050
28365
  cpc: "Cost per click USD",
28051
28366
  their_position: "Competitor's ranking position"
28052
28367
  };
28053
- var keywordGapCommand = defineCommand140({
28368
+ var keywordGapCommand = defineCommand142({
28054
28369
  meta: {
28055
28370
  name: "keyword-gap",
28056
28371
  description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
@@ -28124,7 +28439,7 @@ Examples:
28124
28439
  });
28125
28440
 
28126
28441
  // src/commands/research/keywords-for-site.ts
28127
- import { defineCommand as defineCommand141 } from "citty";
28442
+ import { defineCommand as defineCommand143 } from "citty";
28128
28443
  registerSchema({
28129
28444
  command: "research.keywords-for-site",
28130
28445
  description: "Get keywords a competitor targets in Google. Use --type paid to see only paid keywords, --type organic for organic only. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en). The response includes a query_context object showing which location/language were used.",
@@ -28157,7 +28472,7 @@ var FIELDS8 = {
28157
28472
  competition: "LOW, MEDIUM, or HIGH",
28158
28473
  competition_index: "Competition score 0-100"
28159
28474
  };
28160
- var keywordsForSiteCommand = defineCommand141({
28475
+ var keywordsForSiteCommand = defineCommand143({
28161
28476
  meta: {
28162
28477
  name: "keywords-for-site",
28163
28478
  description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
@@ -28210,7 +28525,7 @@ Examples:
28210
28525
  });
28211
28526
 
28212
28527
  // src/commands/research/languages.ts
28213
- import { defineCommand as defineCommand142 } from "citty";
28528
+ import { defineCommand as defineCommand144 } from "citty";
28214
28529
  registerSchema({
28215
28530
  command: "research.languages",
28216
28531
  description: "List all supported language codes for --language flag in research commands.",
@@ -28240,7 +28555,7 @@ var FIELDS9 = {
28240
28555
  code: "Language code to pass as --language",
28241
28556
  name: "Language name (also accepted by --language)"
28242
28557
  };
28243
- var languagesCommand2 = defineCommand142({
28558
+ var languagesCommand2 = defineCommand144({
28244
28559
  meta: {
28245
28560
  name: "languages",
28246
28561
  description: "List all supported language codes for --language flag."
@@ -28251,7 +28566,7 @@ var languagesCommand2 = defineCommand142({
28251
28566
  });
28252
28567
 
28253
28568
  // src/commands/research/lighthouse.ts
28254
- import { defineCommand as defineCommand143 } from "citty";
28569
+ import { defineCommand as defineCommand145 } from "citty";
28255
28570
  registerSchema({
28256
28571
  command: "research.lighthouse",
28257
28572
  description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
@@ -28270,7 +28585,7 @@ var FIELDS10 = {
28270
28585
  speed_index_ms: "Speed Index in ms (good: < 3400)",
28271
28586
  interactive_ms: "Time to Interactive in ms (good: < 3800)"
28272
28587
  };
28273
- var lighthouseCommand = defineCommand143({
28588
+ var lighthouseCommand = defineCommand145({
28274
28589
  meta: {
28275
28590
  name: "lighthouse",
28276
28591
  description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
@@ -28308,7 +28623,7 @@ Examples:
28308
28623
  });
28309
28624
 
28310
28625
  // src/commands/research/relevant-pages.ts
28311
- import { defineCommand as defineCommand144 } from "citty";
28626
+ import { defineCommand as defineCommand146 } from "citty";
28312
28627
  registerSchema({
28313
28628
  command: "research.relevant-pages",
28314
28629
  description: "Get the top pages of a competitor domain with organic traffic and ranking data. Shows which pages drive the most traffic. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en).",
@@ -28334,7 +28649,7 @@ var FIELDS11 = {
28334
28649
  keywords: "Total organic keywords the page ranks for",
28335
28650
  top_10: "Keywords in positions 1-10"
28336
28651
  };
28337
- var relevantPagesCommand = defineCommand144({
28652
+ var relevantPagesCommand = defineCommand146({
28338
28653
  meta: {
28339
28654
  name: "relevant-pages",
28340
28655
  description: `Get the top pages of a competitor domain with traffic data.
@@ -28380,7 +28695,7 @@ Examples:
28380
28695
  });
28381
28696
 
28382
28697
  // src/commands/research/web.ts
28383
- import { defineCommand as defineCommand145 } from "citty";
28698
+ import { defineCommand as defineCommand147 } from "citty";
28384
28699
  registerSchema({
28385
28700
  command: "research.web",
28386
28701
  description: "Search the web with AI to answer marketing questions \u2014 competitors, ICP, pricing, pain points, market trends. Three depth levels: medium (quick, default), high (thorough), xhigh (exhaustive deep research).",
@@ -28431,7 +28746,7 @@ async function runDeepResearch(question) {
28431
28746
  }
28432
28747
  throw new Error("Deep research timed out");
28433
28748
  }
28434
- var webCommand = defineCommand145({
28749
+ var webCommand = defineCommand147({
28435
28750
  meta: {
28436
28751
  name: "web",
28437
28752
  description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
@@ -28491,7 +28806,7 @@ Examples:
28491
28806
  });
28492
28807
 
28493
28808
  // src/commands/research/index.ts
28494
- var researchCommand = defineCommand146({
28809
+ var researchCommand = defineCommand148({
28495
28810
  meta: {
28496
28811
  name: "research",
28497
28812
  description: `Competitive intelligence and AI-powered research commands.
@@ -28532,10 +28847,10 @@ Full guide: __tooling__/docs/tools/baker/research.md`
28532
28847
  });
28533
28848
 
28534
28849
  // src/commands/scheduled-actions/index.ts
28535
- import { defineCommand as defineCommand153 } from "citty";
28850
+ import { defineCommand as defineCommand155 } from "citty";
28536
28851
 
28537
28852
  // src/commands/scheduled-actions/create.ts
28538
- import { defineCommand as defineCommand147 } from "citty";
28853
+ import { defineCommand as defineCommand149 } from "citty";
28539
28854
 
28540
28855
  // src/commands/scheduled-actions/shared.ts
28541
28856
  var TEMP_SCHEDULED_ACTION_PREFIX = "temp_sched_";
@@ -28650,7 +28965,7 @@ registerSchema({
28650
28965
  prompt: { type: "string", description: "Additional prompt instructions for the spawned agent", required: false }
28651
28966
  }
28652
28967
  });
28653
- var createCommand2 = defineCommand147({
28968
+ var createCommand2 = defineCommand149({
28654
28969
  meta: {
28655
28970
  name: "create",
28656
28971
  description: 'Stage a scheduled action. Example: baker scheduled-actions create --name "Weekly report" --description "..." --cron "0 9 * * MON"'
@@ -28699,7 +29014,7 @@ var createCommand2 = defineCommand147({
28699
29014
  });
28700
29015
 
28701
29016
  // src/commands/scheduled-actions/delete.ts
28702
- import { defineCommand as defineCommand148 } from "citty";
29017
+ import { defineCommand as defineCommand150 } from "citty";
28703
29018
  registerSchema({
28704
29019
  command: "scheduled-actions.delete",
28705
29020
  description: "Stage deletion of a published scheduled action or cancellation of a temp_sched_* draft creation.",
@@ -28707,7 +29022,7 @@ registerSchema({
28707
29022
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
28708
29023
  }
28709
29024
  });
28710
- var deleteCommand2 = defineCommand148({
29025
+ var deleteCommand2 = defineCommand150({
28711
29026
  meta: {
28712
29027
  name: "delete",
28713
29028
  description: "Stage scheduled action deletion. Example: baker scheduled-actions delete <id-or-temp_sched_id>"
@@ -28736,7 +29051,7 @@ var deleteCommand2 = defineCommand148({
28736
29051
  });
28737
29052
 
28738
29053
  // src/commands/scheduled-actions/get.ts
28739
- import { defineCommand as defineCommand149 } from "citty";
29054
+ import { defineCommand as defineCommand151 } from "citty";
28740
29055
  registerSchema({
28741
29056
  command: "scheduled-actions.get",
28742
29057
  description: "Get a published scheduled action or a temp_sched_* draft-created scheduled action.",
@@ -28744,7 +29059,7 @@ registerSchema({
28744
29059
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
28745
29060
  }
28746
29061
  });
28747
- var getCommand3 = defineCommand149({
29062
+ var getCommand3 = defineCommand151({
28748
29063
  meta: {
28749
29064
  name: "get",
28750
29065
  description: "Get a scheduled action. Example: baker scheduled-actions get <id-or-temp_sched_id>"
@@ -28781,13 +29096,13 @@ var getCommand3 = defineCommand149({
28781
29096
  });
28782
29097
 
28783
29098
  // src/commands/scheduled-actions/list.ts
28784
- import { defineCommand as defineCommand150 } from "citty";
29099
+ import { defineCommand as defineCommand152 } from "citty";
28785
29100
  registerSchema({
28786
29101
  command: "scheduled-actions.list",
28787
29102
  description: "List published scheduled actions. Includes draft state when BAKER_CHAT_ID is set.",
28788
29103
  args: {}
28789
29104
  });
28790
- var listCommand9 = defineCommand150({
29105
+ var listCommand9 = defineCommand152({
28791
29106
  meta: {
28792
29107
  name: "list",
28793
29108
  description: "List scheduled actions. Includes staged draft ops when BAKER_CHAT_ID is set."
@@ -28808,7 +29123,7 @@ var listCommand9 = defineCommand150({
28808
29123
  });
28809
29124
 
28810
29125
  // src/commands/scheduled-actions/trigger.ts
28811
- import { defineCommand as defineCommand151 } from "citty";
29126
+ import { defineCommand as defineCommand153 } from "citty";
28812
29127
  registerSchema({
28813
29128
  command: "scheduled-actions.trigger",
28814
29129
  description: "Immediately trigger a published scheduled action. Does not require BAKER_CHAT_ID and rejects temp_sched_* IDs.",
@@ -28816,7 +29131,7 @@ registerSchema({
28816
29131
  id: { type: "string", description: "Published scheduled action ID", required: true }
28817
29132
  }
28818
29133
  });
28819
- var triggerCommand = defineCommand151({
29134
+ var triggerCommand = defineCommand153({
28820
29135
  meta: {
28821
29136
  name: "trigger",
28822
29137
  description: "Immediately trigger a published scheduled action. Example: baker scheduled-actions trigger <id>"
@@ -28853,7 +29168,7 @@ var triggerCommand = defineCommand151({
28853
29168
  });
28854
29169
 
28855
29170
  // src/commands/scheduled-actions/update.ts
28856
- import { defineCommand as defineCommand152 } from "citty";
29171
+ import { defineCommand as defineCommand154 } from "citty";
28857
29172
  registerSchema({
28858
29173
  command: "scheduled-actions.update",
28859
29174
  description: "Stage an update to a published scheduled action or temp_sched_* draft-created scheduled action.",
@@ -28878,7 +29193,7 @@ registerSchema({
28878
29193
  prompt: { type: "string", description: "Replacement additional spawned-agent instructions", required: false }
28879
29194
  }
28880
29195
  });
28881
- var updateCommand2 = defineCommand152({
29196
+ var updateCommand2 = defineCommand154({
28882
29197
  meta: {
28883
29198
  name: "update",
28884
29199
  description: "Stage a scheduled action update. Example: baker scheduled-actions update <id> --enabled false"
@@ -28949,7 +29264,7 @@ var updateCommand2 = defineCommand152({
28949
29264
  });
28950
29265
 
28951
29266
  // src/commands/scheduled-actions/index.ts
28952
- var scheduledActionsCommand = defineCommand153({
29267
+ var scheduledActionsCommand = defineCommand155({
28953
29268
  meta: {
28954
29269
  name: "scheduled-actions",
28955
29270
  description: `Manage Scheduled Actions. Subcommands: list, get, create, update, delete, trigger.
@@ -28976,8 +29291,8 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
28976
29291
  });
28977
29292
 
28978
29293
  // src/commands/schema.ts
28979
- import { defineCommand as defineCommand154 } from "citty";
28980
- var schemaCommand = defineCommand154({
29294
+ import { defineCommand as defineCommand156 } from "citty";
29295
+ var schemaCommand = defineCommand156({
28981
29296
  meta: {
28982
29297
  name: "schema",
28983
29298
  description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images.search"
@@ -29019,7 +29334,7 @@ var schemaCommand = defineCommand154({
29019
29334
  });
29020
29335
 
29021
29336
  // src/commands/tags/index.ts
29022
- import { defineCommand as defineCommand155 } from "citty";
29337
+ import { defineCommand as defineCommand157 } from "citty";
29023
29338
 
29024
29339
  // src/commands/tags/shared.ts
29025
29340
  function failApi3(err) {
@@ -29085,7 +29400,7 @@ async function listTags(json) {
29085
29400
  failApi3(err);
29086
29401
  }
29087
29402
  }
29088
- var listCommand10 = defineCommand155({
29403
+ var listCommand10 = defineCommand157({
29089
29404
  meta: {
29090
29405
  name: "list",
29091
29406
  description: "Effective tags for this chat (production + staged), with each tag's full readable config (secrets excluded) \u2014 reuse a stored value to pre-fill a change rather than asking the user. Refs printed here are what flow side-effect tagIds should use. Example: baker tags list"
@@ -29104,7 +29419,7 @@ async function listDraft3() {
29104
29419
  failApi3(err);
29105
29420
  }
29106
29421
  }
29107
- var draftCommand3 = defineCommand155({
29422
+ var draftCommand3 = defineCommand157({
29108
29423
  meta: {
29109
29424
  name: "draft",
29110
29425
  description: "Review the tag changes staged in this chat (read-only). Staged changes were approved via request_tag_input and apply when the chat is published; to amend or drop one, propose a follow-up change through the same tool (a delete on a tag_temp_* ref drops the staged create)."
@@ -29113,7 +29428,7 @@ var draftCommand3 = defineCommand155({
29113
29428
  await listDraft3();
29114
29429
  }
29115
29430
  });
29116
- var tagsCommand3 = defineCommand155({
29431
+ var tagsCommand3 = defineCommand157({
29117
29432
  meta: {
29118
29433
  name: "tags",
29119
29434
  description: `Read the client's marketing/analytics tags (Meta pixel, GA4, Google Ads, GTM, Clarity, Hotjar, \u2026) \u2014 production tags plus the changes staged in this chat.
@@ -29139,10 +29454,10 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
29139
29454
  });
29140
29455
 
29141
29456
  // src/commands/testimonials/index.ts
29142
- import { defineCommand as defineCommand159 } from "citty";
29457
+ import { defineCommand as defineCommand161 } from "citty";
29143
29458
 
29144
29459
  // src/commands/testimonials/get.ts
29145
- import { defineCommand as defineCommand156 } from "citty";
29460
+ import { defineCommand as defineCommand158 } from "citty";
29146
29461
  registerSchema({
29147
29462
  command: "testimonials.get",
29148
29463
  description: "Get a single testimonial by ID",
@@ -29150,7 +29465,7 @@ registerSchema({
29150
29465
  id: { type: "string", description: "Testimonial ID", required: true }
29151
29466
  }
29152
29467
  });
29153
- var getCommand4 = defineCommand156({
29468
+ var getCommand4 = defineCommand158({
29154
29469
  meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
29155
29470
  args: {
29156
29471
  id: { type: "positional", description: "Testimonial ID", required: false },
@@ -29187,7 +29502,7 @@ var getCommand4 = defineCommand156({
29187
29502
  });
29188
29503
 
29189
29504
  // src/commands/testimonials/list.ts
29190
- import { defineCommand as defineCommand157 } from "citty";
29505
+ import { defineCommand as defineCommand159 } from "citty";
29191
29506
  registerSchema({
29192
29507
  command: "testimonials.list",
29193
29508
  description: "List testimonials with optional filters.",
@@ -29217,7 +29532,7 @@ registerSchema({
29217
29532
  limit: { type: "number", description: "Max results (default 50)", required: false, default: 50 }
29218
29533
  }
29219
29534
  });
29220
- var listCommand11 = defineCommand157({
29535
+ var listCommand11 = defineCommand159({
29221
29536
  meta: {
29222
29537
  name: "list",
29223
29538
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
@@ -29266,7 +29581,7 @@ var listCommand11 = defineCommand157({
29266
29581
  });
29267
29582
 
29268
29583
  // src/commands/testimonials/search.ts
29269
- import { defineCommand as defineCommand158 } from "citty";
29584
+ import { defineCommand as defineCommand160 } from "citty";
29270
29585
  function languageBiasHint(results, requestedLanguage) {
29271
29586
  if (requestedLanguage) {
29272
29587
  return null;
@@ -29344,7 +29659,7 @@ function buildSearchRequest(query, args) {
29344
29659
  }
29345
29660
  return body;
29346
29661
  }
29347
- var searchCommand2 = defineCommand158({
29662
+ var searchCommand2 = defineCommand160({
29348
29663
  meta: {
29349
29664
  name: "search",
29350
29665
  description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
@@ -29400,7 +29715,7 @@ var searchCommand2 = defineCommand158({
29400
29715
  var tagsCommand4 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
29401
29716
 
29402
29717
  // src/commands/testimonials/index.ts
29403
- var testimonialsCommand = defineCommand159({
29718
+ var testimonialsCommand = defineCommand161({
29404
29719
  meta: {
29405
29720
  name: "testimonials",
29406
29721
  description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
@@ -29422,10 +29737,10 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
29422
29737
  });
29423
29738
 
29424
29739
  // src/commands/videos/index.ts
29425
- import { defineCommand as defineCommand164 } from "citty";
29740
+ import { defineCommand as defineCommand166 } from "citty";
29426
29741
 
29427
29742
  // src/commands/videos/delete.ts
29428
- import { defineCommand as defineCommand160 } from "citty";
29743
+ import { defineCommand as defineCommand162 } from "citty";
29429
29744
  registerSchema({
29430
29745
  command: "videos.delete",
29431
29746
  description: "Delete a video by ID",
@@ -29439,7 +29754,7 @@ registerSchema({
29439
29754
  }
29440
29755
  }
29441
29756
  });
29442
- var deleteCommand3 = defineCommand160({
29757
+ var deleteCommand3 = defineCommand162({
29443
29758
  meta: {
29444
29759
  name: "delete",
29445
29760
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -29480,7 +29795,7 @@ var deleteCommand3 = defineCommand160({
29480
29795
  });
29481
29796
 
29482
29797
  // src/commands/videos/get.ts
29483
- import { defineCommand as defineCommand161 } from "citty";
29798
+ import { defineCommand as defineCommand163 } from "citty";
29484
29799
  registerSchema({
29485
29800
  command: "videos.get",
29486
29801
  description: "Get a single video by ID",
@@ -29488,7 +29803,7 @@ registerSchema({
29488
29803
  id: { type: "string", description: "Video ID", required: true }
29489
29804
  }
29490
29805
  });
29491
- var getCommand5 = defineCommand161({
29806
+ var getCommand5 = defineCommand163({
29492
29807
  meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
29493
29808
  args: {
29494
29809
  id: { type: "positional", description: "Video ID", required: false },
@@ -29525,7 +29840,7 @@ var getCommand5 = defineCommand161({
29525
29840
  });
29526
29841
 
29527
29842
  // src/commands/videos/search.ts
29528
- import { defineCommand as defineCommand162 } from "citty";
29843
+ import { defineCommand as defineCommand164 } from "citty";
29529
29844
  registerSchema({
29530
29845
  command: "videos.search",
29531
29846
  description: "Search videos by text query. Only returns ready videos.",
@@ -29535,7 +29850,7 @@ registerSchema({
29535
29850
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
29536
29851
  }
29537
29852
  });
29538
- var searchCommand3 = defineCommand162({
29853
+ var searchCommand3 = defineCommand164({
29539
29854
  meta: {
29540
29855
  name: "search",
29541
29856
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -29585,9 +29900,9 @@ var searchCommand3 = defineCommand162({
29585
29900
  var tagsCommand5 = makeTagsCommand("videos", "video", "/api/videos/tags");
29586
29901
 
29587
29902
  // src/commands/videos/upload.ts
29588
- import { readFile as readFile22, stat as stat7 } from "fs/promises";
29903
+ import { readFile as readFile23, stat as stat7 } from "fs/promises";
29589
29904
  import { extname as extname3 } from "path";
29590
- import { defineCommand as defineCommand163 } from "citty";
29905
+ import { defineCommand as defineCommand165 } from "citty";
29591
29906
  var MIME_MAP = {
29592
29907
  ".mp4": "video/mp4",
29593
29908
  ".mov": "video/quicktime",
@@ -29621,7 +29936,7 @@ function detectContentType(filePath) {
29621
29936
  }
29622
29937
  return mime;
29623
29938
  }
29624
- var uploadCommand2 = defineCommand163({
29939
+ var uploadCommand2 = defineCommand165({
29625
29940
  meta: {
29626
29941
  name: "upload",
29627
29942
  description: "Upload a video file to Baker via Mux direct upload. Auto-detects content type. Example: baker videos upload ./demo.mp4"
@@ -29650,7 +29965,7 @@ var uploadCommand2 = defineCommand163({
29650
29965
  return;
29651
29966
  }
29652
29967
  const { uploadUrl, videoId } = await apiPost("/api/videos/upload", {});
29653
- const fileBuffer = await readFile22(filePath);
29968
+ const fileBuffer = await readFile23(filePath);
29654
29969
  const uploadResponse = await fetch(uploadUrl, {
29655
29970
  method: "PUT",
29656
29971
  headers: { "Content-Type": contentType },
@@ -29675,7 +29990,7 @@ var uploadCommand2 = defineCommand163({
29675
29990
  });
29676
29991
 
29677
29992
  // src/commands/videos/index.ts
29678
- var videosCommand = defineCommand164({
29993
+ var videosCommand = defineCommand166({
29679
29994
  meta: {
29680
29995
  name: "videos",
29681
29996
  description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete, tags.
@@ -29699,10 +30014,10 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
29699
30014
  });
29700
30015
 
29701
30016
  // src/commands/winning-ads/index.ts
29702
- import { defineCommand as defineCommand177 } from "citty";
30017
+ import { defineCommand as defineCommand179 } from "citty";
29703
30018
 
29704
30019
  // src/commands/winning-ads/advertisers.ts
29705
- import { defineCommand as defineCommand165 } from "citty";
30020
+ import { defineCommand as defineCommand167 } from "citty";
29706
30021
 
29707
30022
  // src/commands/winning-ads/shared.ts
29708
30023
  function splitList(value) {
@@ -29755,7 +30070,7 @@ function advertiserNormalizer(record, full) {
29755
30070
  last_synced_at: record.last_synced_at ?? null
29756
30071
  };
29757
30072
  }
29758
- var advertisersCommand2 = defineCommand165({
30073
+ var advertisersCommand2 = defineCommand167({
29759
30074
  meta: {
29760
30075
  name: "advertisers",
29761
30076
  description: 'List corpus advertisers by name or domain. Find your own advertiser for --exclude-advertiser, or a competitor for --advertiser-id / winners. Example: baker winning-ads advertisers "Deel" --output md'
@@ -29813,7 +30128,7 @@ var advertisersCommand2 = defineCommand165({
29813
30128
  });
29814
30129
 
29815
30130
  // src/commands/winning-ads/brief.ts
29816
- import { defineCommand as defineCommand166 } from "citty";
30131
+ import { defineCommand as defineCommand168 } from "citty";
29817
30132
  registerSchema({
29818
30133
  command: "winning-ads.brief",
29819
30134
  description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
@@ -29859,7 +30174,7 @@ function parseDna(raw) {
29859
30174
  }
29860
30175
  return parsed;
29861
30176
  }
29862
- var briefCommand = defineCommand166({
30177
+ var briefCommand = defineCommand168({
29863
30178
  meta: {
29864
30179
  name: "brief",
29865
30180
  description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
@@ -29895,7 +30210,7 @@ var briefCommand = defineCommand166({
29895
30210
  });
29896
30211
 
29897
30212
  // src/commands/winning-ads/content.ts
29898
- import { defineCommand as defineCommand167 } from "citty";
30213
+ import { defineCommand as defineCommand169 } from "citty";
29899
30214
  registerSchema({
29900
30215
  command: "winning-ads.content",
29901
30216
  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. Add --full for speech, pacing, and soundtrack detail. Video ads carry the transcript/on-screen text; static ads carry only the copy.",
@@ -29908,7 +30223,7 @@ registerSchema({
29908
30223
  }
29909
30224
  }
29910
30225
  });
29911
- var contentCommand = defineCommand167({
30226
+ var contentCommand = defineCommand169({
29912
30227
  meta: {
29913
30228
  name: "content",
29914
30229
  description: "Read the transcript + on-screen text + copy of one winning ad. Example: baker winning-ads content adg_123 --platform meta --full --output md"
@@ -29957,7 +30272,7 @@ var contentCommand = defineCommand167({
29957
30272
  });
29958
30273
 
29959
30274
  // src/commands/winning-ads/feed.ts
29960
- import { defineCommand as defineCommand168 } from "citty";
30275
+ import { defineCommand as defineCommand170 } from "citty";
29961
30276
  function buildFeedParams(input) {
29962
30277
  const params = {};
29963
30278
  const advertiser = splitList(input.advertiser);
@@ -30009,7 +30324,7 @@ registerSchema({
30009
30324
  format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
30010
30325
  }
30011
30326
  });
30012
- var feedCommand = defineCommand168({
30327
+ var feedCommand = defineCommand170({
30013
30328
  meta: {
30014
30329
  name: "feed",
30015
30330
  description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
@@ -30094,7 +30409,7 @@ var feedCommand = defineCommand168({
30094
30409
  });
30095
30410
 
30096
30411
  // src/commands/winning-ads/follow.ts
30097
- import { defineCommand as defineCommand169 } from "citty";
30412
+ import { defineCommand as defineCommand171 } from "citty";
30098
30413
  var PLATFORMS = ["meta", "linkedin"];
30099
30414
  registerSchema({
30100
30415
  command: "winning-ads.follow",
@@ -30109,7 +30424,7 @@ registerSchema({
30109
30424
  label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
30110
30425
  }
30111
30426
  });
30112
- var followCommand = defineCommand169({
30427
+ var followCommand = defineCommand171({
30113
30428
  meta: {
30114
30429
  name: "follow",
30115
30430
  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 both Meta + LinkedIn. Example: baker winning-ads follow "deel.com" --platform meta'
@@ -30156,7 +30471,7 @@ var followCommand = defineCommand169({
30156
30471
  });
30157
30472
 
30158
30473
  // src/commands/winning-ads/follow-competitors.ts
30159
- import { defineCommand as defineCommand170 } from "citty";
30474
+ import { defineCommand as defineCommand172 } from "citty";
30160
30475
  var PLATFORMS2 = ["meta", "linkedin"];
30161
30476
  var BATCH_TIMEOUT_MS = 3e5;
30162
30477
  function buildFollowBatchBody(input) {
@@ -30189,7 +30504,7 @@ registerSchema({
30189
30504
  }
30190
30505
  }
30191
30506
  });
30192
- var followCompetitorsCommand = defineCommand170({
30507
+ var followCompetitorsCommand = defineCommand172({
30193
30508
  meta: {
30194
30509
  name: "follow-competitors",
30195
30510
  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"'
@@ -30264,7 +30579,7 @@ var followCompetitorsCommand = defineCommand170({
30264
30579
  });
30265
30580
 
30266
30581
  // src/commands/winning-ads/following.ts
30267
- import { defineCommand as defineCommand171 } from "citty";
30582
+ import { defineCommand as defineCommand173 } from "citty";
30268
30583
  registerSchema({
30269
30584
  command: "winning-ads.following",
30270
30585
  description: "List the brands you follow in your ad-dna library, with each one's status (ready vs still adding) and cached ad counts.",
@@ -30297,7 +30612,7 @@ function followingNormalizer(record, full) {
30297
30612
  platforms: Array.isArray(record.platforms) ? record.platforms : []
30298
30613
  };
30299
30614
  }
30300
- var followingCommand = defineCommand171({
30615
+ var followingCommand = defineCommand173({
30301
30616
  meta: {
30302
30617
  name: "following",
30303
30618
  description: "List brands you follow, with status (ready / adding\u2026) and cached counts. Example: baker winning-ads following --output md"
@@ -30332,7 +30647,7 @@ var followingCommand = defineCommand171({
30332
30647
  });
30333
30648
 
30334
30649
  // src/commands/winning-ads/patterns.ts
30335
- import { defineCommand as defineCommand172 } from "citty";
30650
+ import { defineCommand as defineCommand174 } from "citty";
30336
30651
  registerSchema({
30337
30652
  command: "winning-ads.patterns",
30338
30653
  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.",
@@ -30371,7 +30686,7 @@ function discriminatorRow(record) {
30371
30686
  top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
30372
30687
  };
30373
30688
  }
30374
- var patternsCommand = defineCommand172({
30689
+ var patternsCommand = defineCommand174({
30375
30690
  meta: {
30376
30691
  name: "patterns",
30377
30692
  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"
@@ -30427,7 +30742,7 @@ var patternsCommand = defineCommand172({
30427
30742
  });
30428
30743
 
30429
30744
  // src/commands/winning-ads/search.ts
30430
- import { defineCommand as defineCommand173 } from "citty";
30745
+ import { defineCommand as defineCommand175 } from "citty";
30431
30746
  registerSchema({
30432
30747
  command: "winning-ads.search",
30433
30748
  description: "Search the ad-dna corpus of scored winning ads. Returns a lean shortlist (advertiser, summary, scores, media_url) to pick a reference to reproduce.",
@@ -30535,7 +30850,7 @@ function buildSearchBody(args) {
30535
30850
  }
30536
30851
  return body;
30537
30852
  }
30538
- var searchCommand4 = defineCommand173({
30853
+ var searchCommand4 = defineCommand175({
30539
30854
  meta: {
30540
30855
  name: "search",
30541
30856
  description: "Search winning reference ads. Example: baker winning-ads search 'B2B SaaS before/after AI automation' --platform meta --format static --winner-category winner --exclude-advertiser adv_123 --output md"
@@ -30650,7 +30965,7 @@ var searchCommand4 = defineCommand173({
30650
30965
  });
30651
30966
 
30652
30967
  // src/commands/winning-ads/seeds.ts
30653
- import { defineCommand as defineCommand174 } from "citty";
30968
+ import { defineCommand as defineCommand176 } from "citty";
30654
30969
  function leanRow(r) {
30655
30970
  return {
30656
30971
  key: r.key,
@@ -30678,7 +30993,7 @@ function makeSeedCommand(opts) {
30678
30993
  limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
30679
30994
  }
30680
30995
  });
30681
- return defineCommand174({
30996
+ return defineCommand176({
30682
30997
  meta: { name: opts.name, description: opts.description },
30683
30998
  args: {
30684
30999
  platform: { type: "string", description: "Single platform to segment on", required: false },
@@ -30727,7 +31042,7 @@ var formatsCommand = makeSeedCommand({
30727
31042
  });
30728
31043
 
30729
31044
  // src/commands/winning-ads/unfollow.ts
30730
- import { defineCommand as defineCommand175 } from "citty";
31045
+ import { defineCommand as defineCommand177 } from "citty";
30731
31046
  registerSchema({
30732
31047
  command: "winning-ads.unfollow",
30733
31048
  description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
@@ -30735,7 +31050,7 @@ registerSchema({
30735
31050
  advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
30736
31051
  }
30737
31052
  });
30738
- var unfollowCommand = defineCommand175({
31053
+ var unfollowCommand = defineCommand177({
30739
31054
  meta: {
30740
31055
  name: "unfollow",
30741
31056
  description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
@@ -30756,7 +31071,7 @@ var unfollowCommand = defineCommand175({
30756
31071
  });
30757
31072
 
30758
31073
  // src/commands/winning-ads/winners.ts
30759
- import { defineCommand as defineCommand176 } from "citty";
31074
+ import { defineCommand as defineCommand178 } from "citty";
30760
31075
  registerSchema({
30761
31076
  command: "winning-ads.winners",
30762
31077
  description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
@@ -30766,7 +31081,7 @@ registerSchema({
30766
31081
  platform: { type: "string", description: "Filter to a single platform: meta|linkedin", required: false }
30767
31082
  }
30768
31083
  });
30769
- var winnersCommand = defineCommand176({
31084
+ var winnersCommand = defineCommand178({
30770
31085
  meta: {
30771
31086
  name: "winners",
30772
31087
  description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
@@ -30816,7 +31131,7 @@ var winnersCommand = defineCommand176({
30816
31131
  });
30817
31132
 
30818
31133
  // src/commands/winning-ads/index.ts
30819
- var winningAdsCommand = defineCommand177({
31134
+ var winningAdsCommand = defineCommand179({
30820
31135
  meta: {
30821
31136
  name: "winning-ads",
30822
31137
  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.
@@ -30888,7 +31203,7 @@ function getCliVersion() {
30888
31203
  }
30889
31204
 
30890
31205
  // src/cli.ts
30891
- var main = defineCommand178({
31206
+ var main = defineCommand180({
30892
31207
  meta: {
30893
31208
  name: "baker",
30894
31209
  version: getCliVersion(),
@@ -30904,6 +31219,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
30904
31219
  actions: actionsCommand,
30905
31220
  "scheduled-actions": scheduledActionsCommand,
30906
31221
  ads: adsCommand2,
31222
+ brand: brandCommand,
30907
31223
  ga4: ga4Command,
30908
31224
  gsc: gscCommand,
30909
31225
  research: researchCommand,