@koda-sl/baker-cli 0.149.0 → 0.150.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -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,346 @@ 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().split(/\s+/).map(encodeURIComponent).join("+");
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
+ var STANDARD_WEIGHTS = [100, 200, 300, 400, 500, 600, 700, 800, 900];
15378
+ async function probeServedWeights(family, fetchCss) {
15379
+ const probes = await Promise.all(
15380
+ STANDARD_WEIGHTS.map(async (weight) => await fetchCss(googleFontCssUrl(family, [weight])) ? weight : null)
15381
+ );
15382
+ return probes.filter((w) => w !== null);
15383
+ }
15384
+ async function checkFontRequests(requests, fetchCss) {
15385
+ const results = [];
15386
+ for (const request of requests) {
15387
+ const css = await fetchCss(googleFontCssUrl(request.family, request.weights));
15388
+ if (css === null) {
15389
+ const served2 = await probeServedWeights(request.family, fetchCss);
15390
+ results.push({
15391
+ family: request.family,
15392
+ requested: request.weights,
15393
+ served: served2,
15394
+ missing: request.weights.filter((w) => !served2.includes(w)),
15395
+ subsets: [],
15396
+ cause: served2.length === 0 ? "unknown-family" : "unavailable-weight"
15397
+ });
15398
+ continue;
15399
+ }
15400
+ const faces = parseGoogleFontFaces(css);
15401
+ const served = [...new Set(faces.map((f) => f.weight))].sort((a, b) => a - b);
15402
+ results.push({
15403
+ family: request.family,
15404
+ requested: request.weights,
15405
+ served,
15406
+ missing: request.weights.filter((w) => !served.includes(w)),
15407
+ subsets: [...new Set(faces.map((f) => f.subset))]
15408
+ });
15409
+ }
15410
+ return results;
15411
+ }
15412
+
15413
+ // src/commands/brand/fonts.ts
15414
+ var GLOBAL_CSS = path.join("src", "styles", "global.css");
15415
+ var FONTS_DIR = path.join("src", "brand", "fonts");
15416
+ var DEFAULT_SUBSETS = "latin";
15417
+ var BROWSER_UA = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/120.0 Safari/537.36";
15418
+ registerSchema({
15419
+ command: "brand.fonts.check",
15420
+ 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.",
15421
+ args: {}
15422
+ });
15423
+ registerSchema({
15424
+ command: "brand.fonts.fetch",
15425
+ 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.",
15426
+ args: {
15427
+ family: { type: "string", description: 'Font family, e.g. "DM Sans"', required: true },
15428
+ weights: {
15429
+ type: "string",
15430
+ description: "Comma-separated weights (default: what global.css requests, else 400)",
15431
+ required: false
15432
+ },
15433
+ subsets: {
15434
+ type: "string",
15435
+ description: `Comma-separated unicode subsets (default: ${DEFAULT_SUBSETS})`,
15436
+ required: false
15437
+ }
15438
+ }
15439
+ });
15440
+ function fail(code, message, fix) {
15441
+ writeJson({ ok: false, error: { code, message, ...fix ? { fix } : {} } });
15442
+ process.exit(2);
15443
+ }
15444
+ var REQUEST_TIMEOUT_MS = 15e3;
15445
+ async function fetchGoogleCss(url) {
15446
+ try {
15447
+ const res = await fetch(url, {
15448
+ headers: { "User-Agent": BROWSER_UA },
15449
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
15450
+ });
15451
+ if (!res.ok) return null;
15452
+ return await res.text();
15453
+ } catch {
15454
+ return null;
15455
+ }
15456
+ }
15457
+ async function readGlobalCss() {
15458
+ try {
15459
+ return await readFile(path.resolve(process.cwd(), GLOBAL_CSS), "utf8");
15460
+ } catch {
15461
+ return "";
15462
+ }
15463
+ }
15464
+ var fontsCheckCommand = defineCommand85({
15465
+ meta: {
15466
+ name: "check",
15467
+ 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."
15468
+ },
15469
+ async run() {
15470
+ const css = await readGlobalCss();
15471
+ const requests = googleFontRequests(css);
15472
+ if (requests.length === 0) {
15473
+ writeJson({
15474
+ ok: true,
15475
+ data: { families: [], selfHosted: true },
15476
+ hints: [
15477
+ `No Google Fonts requested in ${GLOBAL_CSS}. If the brand self-hosts, the offline validator already checks those files; nothing to verify against Google.`
15478
+ ]
15479
+ });
15480
+ return;
15481
+ }
15482
+ const families = await checkFontRequests(requests, fetchGoogleCss);
15483
+ const broken = families.filter((f) => f.missing.length > 0);
15484
+ if (broken.length > 0) {
15485
+ const summary = broken.map(
15486
+ (f) => f.cause === "unknown-family" ? `${f.family} (no such family on Google Fonts \u2014 check the exact spelling)` : `${f.family} ${f.missing.join("/")} (served: ${f.served.join("/") || "none"})`
15487
+ ).join("; ");
15488
+ fail("FONT_NOT_SERVED", `Google Fonts does not serve: ${summary}`, {
15489
+ action: `Correct the family name or weight in ${GLOBAL_CSS} and src/brand/BRAND.md, picking from the \`served\` weights below.`,
15490
+ explanation: "A requested weight Google does not serve is silently synthesized by the browser, so the page looks subtly wrong with nothing failing.",
15491
+ // The per-family breakdown is the point of the command, so it ships on
15492
+ // the failure path too — otherwise the only remedy is re-running the
15493
+ // command that just failed. Healthy families are included so a
15494
+ // multi-family brand shows what NOT to touch.
15495
+ families
15496
+ });
15497
+ }
15498
+ writeJson({
15499
+ ok: true,
15500
+ data: { families },
15501
+ hints: [
15502
+ "Every requested family and weight is really served by Google.",
15503
+ `To stop depending on Google at page load, run \`baker brand fonts fetch "<family>"\` to self-host into ${FONTS_DIR}.`
15504
+ ]
15505
+ });
15506
+ }
15507
+ });
15508
+ var fontsFetchCommand = defineCommand85({
15509
+ meta: {
15510
+ name: "fetch",
15511
+ 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."
15512
+ },
15513
+ args: {
15514
+ family: { type: "positional", required: true, description: 'Font family, e.g. "DM Sans"' },
15515
+ weights: { type: "string", description: "Comma-separated weights (default: what global.css requests, else 400)" },
15516
+ subsets: { type: "string", description: `Comma-separated unicode subsets (default: ${DEFAULT_SUBSETS})` }
15517
+ },
15518
+ async run({ args }) {
15519
+ const family = String(args.family).trim();
15520
+ if (!family) fail("INVALID_FAMILY", 'Pass a font family, e.g. `baker brand fonts fetch "DM Sans"`.');
15521
+ const css = await readGlobalCss();
15522
+ const declared = googleFontRequests(css).find((r) => r.family.toLowerCase() === family.toLowerCase());
15523
+ const weights = resolveFetchWeights(args.weights, declared?.weights);
15524
+ if (weights.length === 0) {
15525
+ fail("INVALID_WEIGHTS", `"${args.weights}" has no usable weight \u2014 pass whole hundreds, e.g. --weights 400,700.`);
15526
+ }
15527
+ const wanted = new Set(
15528
+ String(args.subsets ?? DEFAULT_SUBSETS).split(",").map((s) => s.trim()).filter(Boolean)
15529
+ );
15530
+ const servedCss = await fetchGoogleCss(googleFontCssUrl(family, weights));
15531
+ if (servedCss === null) {
15532
+ const [diagnosis] = await checkFontRequests([{ family, weights }], fetchGoogleCss);
15533
+ const served = diagnosis?.served ?? [];
15534
+ fail(
15535
+ "FONT_NOT_SERVED",
15536
+ served.length === 0 ? `Google Fonts has no family called "${family}".` : `"${family}" exists on Google Fonts but not at weight ${weights.join("/")}.`,
15537
+ served.length === 0 ? {
15538
+ action: "Check the exact family name at fonts.google.com, then retry.",
15539
+ explanation: "Family names are case- and space-sensitive in the Google Fonts API."
15540
+ } : {
15541
+ action: `Retry with --weights set to one of these: ${served.join(",")}.`,
15542
+ explanation: "Google rejects the whole request when one requested weight does not exist.",
15543
+ served
15544
+ }
15545
+ );
15546
+ }
15547
+ const faces = parseGoogleFontFaces(servedCss).filter((f) => wanted.has(f.subset));
15548
+ if (faces.length === 0) {
15549
+ fail("SUBSET_NOT_SERVED", `"${family}" has no ${[...wanted].join("/")} subset.`, {
15550
+ action: `Retry with --subsets set to one Google actually serves for this family.`,
15551
+ explanation: "Google splits each family into unicode subsets; not every family ships every subset."
15552
+ });
15553
+ }
15554
+ const fontsDir = path.resolve(process.cwd(), FONTS_DIR);
15555
+ await mkdir(fontsDir, { recursive: true });
15556
+ const downloaded = [];
15557
+ for (const face of faces) {
15558
+ const res = await fetch(face.url, {
15559
+ headers: { "User-Agent": BROWSER_UA },
15560
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
15561
+ });
15562
+ if (!res.ok) continue;
15563
+ await writeFile(path.join(fontsDir, fontFileName(face)), Buffer.from(await res.arrayBuffer()));
15564
+ downloaded.push(face);
15565
+ }
15566
+ const written = downloaded.map(fontFileName);
15567
+ if (written.length === 0) {
15568
+ fail("DOWNLOAD_FAILED", `Could not download any file for "${family}".`, {
15569
+ action: "Retry; if it keeps failing, keep the Google @import and note it in BRAND.md.",
15570
+ explanation: "The font stylesheet resolved but the font files themselves did not download."
15571
+ });
15572
+ }
15573
+ const fontFace = renderFontFaceCss(downloaded, (f) => `/${FONTS_DIR}/${fontFileName(f)}`);
15574
+ const downloadedWeights = [...new Set(downloaded.map((f) => f.weight))].sort((a, b) => a - b);
15575
+ const incomplete = weights.filter((w) => !downloadedWeights.includes(w));
15576
+ writeJson({
15577
+ ok: true,
15578
+ data: { family, weights: downloadedWeights, files: written, fontFace },
15579
+ hints: [
15580
+ `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.`,
15581
+ `Record the exact weights (${downloadedWeights.join(", ")}) in src/brand/BRAND.md \u2014 a weight listed there but not downloaded renders as a synthesized fallback.`,
15582
+ ...incomplete.length > 0 ? [
15583
+ `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(",")}\`.`
15584
+ ] : [],
15585
+ "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."
15586
+ ]
15587
+ });
15588
+ }
15589
+ });
15590
+ var fontsCommand = defineCommand85({
15591
+ meta: {
15592
+ name: "fonts",
15593
+ description: `Verify and self-host the brand's typefaces.
15594
+
15595
+ Start here: \`baker brand fonts check\` \u2014 confirms the fonts the brand claims are really served.
15596
+
15597
+ Subcommands:
15598
+ baker brand fonts check \u2014 ask Google what it actually serves for every family/weight global.css requests
15599
+ baker brand fonts fetch <family> \u2014 download a family into src/brand/fonts/ and print its @font-face block`
15600
+ },
15601
+ subCommands: {
15602
+ check: fontsCheckCommand,
15603
+ fetch: fontsFetchCommand
15604
+ }
15605
+ });
15606
+
15607
+ // src/commands/brand/index.ts
15608
+ var brandCommand = defineCommand86({
15609
+ meta: {
15610
+ name: "brand",
15611
+ description: `Brand asset verification for src/brand/.
15612
+
15613
+ Start here: \`baker brand fonts check\` after defining or editing the brand's typography.
15614
+
15615
+ Subcommands:
15616
+ baker brand fonts \u2014 verify the brand's typefaces really load, and self-host them into src/brand/fonts/`
15617
+ },
15618
+ subCommands: {
15619
+ fonts: fontsCommand
15620
+ }
15621
+ });
15622
+
15289
15623
  // src/commands/canvas/index.ts
15290
- import { defineCommand as defineCommand95 } from "citty";
15624
+ import { defineCommand as defineCommand97 } from "citty";
15291
15625
 
15292
15626
  // src/commands/canvas/catalog.ts
15293
- import { defineCommand as defineCommand85 } from "citty";
15294
- var catalogCommand = defineCommand85({
15627
+ import { defineCommand as defineCommand87 } from "citty";
15628
+ var catalogCommand = defineCommand87({
15295
15629
  meta: {
15296
15630
  name: "catalog",
15297
15631
  description: "Print the agent-facing node catalog (JSON Schema). Includes every registered node grouped by category."
@@ -15304,9 +15638,9 @@ var catalogCommand = defineCommand85({
15304
15638
  });
15305
15639
 
15306
15640
  // src/commands/canvas/critique.ts
15307
- import { readFile } from "fs/promises";
15308
- import path from "path";
15309
- import { defineCommand as defineCommand86 } from "citty";
15641
+ import { readFile as readFile2 } from "fs/promises";
15642
+ import path2 from "path";
15643
+ import { defineCommand as defineCommand88 } from "citty";
15310
15644
 
15311
15645
  // src/engine/scaffold/lib/critique.ts
15312
15646
  var VIBE_CUE = /\b(light|lighting|golden|moody|warm|cool|tone|grade|contrast|shadow|glow|rim|backlit|neon|soft)\b/i;
@@ -15426,15 +15760,15 @@ function critiqueCanvas(canvas) {
15426
15760
  }
15427
15761
 
15428
15762
  // src/commands/canvas/critique.ts
15429
- var critiqueCommand = defineCommand86({
15763
+ var critiqueCommand = defineCommand88({
15430
15764
  meta: {
15431
15765
  name: "critique",
15432
15766
  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
15767
  },
15434
15768
  args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
15435
15769
  async run({ args }) {
15436
- const filePath = path.resolve(String(args.file));
15437
- const raw = await readFile(filePath, "utf8");
15770
+ const filePath = path2.resolve(String(args.file));
15771
+ const raw = await readFile2(filePath, "utf8");
15438
15772
  let parsed;
15439
15773
  try {
15440
15774
  parsed = JSON.parse(raw);
@@ -15465,12 +15799,12 @@ var critiqueCommand = defineCommand86({
15465
15799
 
15466
15800
  // src/commands/canvas/inspect.ts
15467
15801
  import { execFile } from "child_process";
15468
- import { readdir, readFile as readFile2, stat } from "fs/promises";
15469
- import path2 from "path";
15802
+ import { readdir, readFile as readFile3, stat } from "fs/promises";
15803
+ import path3 from "path";
15470
15804
  import { promisify } from "util";
15471
- import { defineCommand as defineCommand87 } from "citty";
15805
+ import { defineCommand as defineCommand89 } from "citty";
15472
15806
  var execFileAsync = promisify(execFile);
15473
- var inspectCommand = defineCommand87({
15807
+ var inspectCommand = defineCommand89({
15474
15808
  meta: {
15475
15809
  name: "inspect",
15476
15810
  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 +15818,7 @@ var inspectCommand = defineCommand87({
15484
15818
  }
15485
15819
  },
15486
15820
  async run({ args }) {
15487
- const outputsDir = path2.resolve(String(args["outputs-dir"] ?? "canvas"));
15821
+ const outputsDir = path3.resolve(String(args["outputs-dir"] ?? "canvas"));
15488
15822
  const runArg = String(args.run);
15489
15823
  const runDir = await resolveRunDir(runArg, outputsDir);
15490
15824
  const manifest = await loadManifest(runDir);
@@ -15496,7 +15830,7 @@ var inspectCommand = defineCommand87({
15496
15830
  }
15497
15831
  const summary = {
15498
15832
  ok: true,
15499
- run_id: manifest.run_id ?? path2.basename(runDir),
15833
+ run_id: manifest.run_id ?? path3.basename(runDir),
15500
15834
  run_dir: runDir,
15501
15835
  stats: manifest.stats ?? null,
15502
15836
  output: manifest.output ?? null,
@@ -15509,20 +15843,20 @@ var inspectCommand = defineCommand87({
15509
15843
  }
15510
15844
  });
15511
15845
  async function resolveRunDir(run, outputsDir) {
15512
- if (path2.isAbsolute(run)) {
15846
+ if (path3.isAbsolute(run)) {
15513
15847
  const s2 = await stat(run).catch(() => null);
15514
15848
  if (s2?.isDirectory()) return run;
15515
15849
  throw new Error(`inspect: ${run} is not a directory`);
15516
15850
  }
15517
- const candidate = path2.join(outputsDir, run);
15851
+ const candidate = path3.join(outputsDir, run);
15518
15852
  const s = await stat(candidate).catch(() => null);
15519
15853
  if (s?.isDirectory()) return candidate;
15520
15854
  throw new Error(`inspect: no run directory at ${candidate}`);
15521
15855
  }
15522
15856
  async function loadManifest(runDir) {
15523
- const manifestPath = path2.join(runDir, "manifest.json");
15857
+ const manifestPath = path3.join(runDir, "manifest.json");
15524
15858
  try {
15525
- const raw = await readFile2(manifestPath, "utf-8");
15859
+ const raw = await readFile3(manifestPath, "utf-8");
15526
15860
  return JSON.parse(raw);
15527
15861
  } catch {
15528
15862
  return {};
@@ -15532,7 +15866,7 @@ async function listRunFiles(runDir) {
15532
15866
  const out = [];
15533
15867
  const names = await readdir(runDir);
15534
15868
  for (const name of names) {
15535
- const abs = path2.join(runDir, name);
15869
+ const abs = path3.join(runDir, name);
15536
15870
  const s = await stat(abs).catch(() => null);
15537
15871
  if (!s?.isFile()) continue;
15538
15872
  out.push({ name, path: abs, size: s.size });
@@ -15577,13 +15911,13 @@ async function probeDuration(filePath) {
15577
15911
  }
15578
15912
 
15579
15913
  // src/commands/canvas/rerun.ts
15580
- import path13 from "path";
15581
- import { defineCommand as defineCommand89 } from "citty";
15914
+ import path14 from "path";
15915
+ import { defineCommand as defineCommand91 } from "citty";
15582
15916
 
15583
15917
  // 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";
15918
+ import { readFile as readFile10 } from "fs/promises";
15919
+ import path13 from "path";
15920
+ import { defineCommand as defineCommand90 } from "citty";
15587
15921
 
15588
15922
  // src/commands/canvas/placeholders.ts
15589
15923
  function unsuppliedPlaceholderAssets(canvas) {
@@ -15602,7 +15936,7 @@ function unsuppliedPlaceholderAssets(canvas) {
15602
15936
  }
15603
15937
 
15604
15938
  // src/commands/canvas/resolve-paths.ts
15605
- import path3 from "path";
15939
+ import path4 from "path";
15606
15940
  function resolveRelativeCanvasPaths(canvas, baseDir) {
15607
15941
  if (!canvas || typeof canvas !== "object") return canvas;
15608
15942
  const c = canvas;
@@ -15615,24 +15949,24 @@ function resolveNode(node, baseDir) {
15615
15949
  const params = n.params;
15616
15950
  if (!params || typeof params !== "object") return node;
15617
15951
  if (n.type === "ingest" && params.source === "path" && isResolvableRelative(params.path)) {
15618
- return { ...node, params: { ...params, path: path3.resolve(baseDir, params.path) } };
15952
+ return { ...node, params: { ...params, path: path4.resolve(baseDir, params.path) } };
15619
15953
  }
15620
15954
  if (n.type === "hyperframe_render" && isResolvableRelative(params.composition)) {
15621
- return { ...node, params: { ...params, composition: path3.resolve(baseDir, params.composition) } };
15955
+ return { ...node, params: { ...params, composition: path4.resolve(baseDir, params.composition) } };
15622
15956
  }
15623
15957
  return node;
15624
15958
  }
15625
15959
  function isResolvableRelative(value) {
15626
- return typeof value === "string" && value.length > 0 && !value.includes("[TODO") && !looksLikeHttpUrl(value) && !path3.isAbsolute(value);
15960
+ return typeof value === "string" && value.length > 0 && !value.includes("[TODO") && !looksLikeHttpUrl(value) && !path4.isAbsolute(value);
15627
15961
  }
15628
15962
 
15629
15963
  // src/commands/canvas/source-version.ts
15630
- import { readFile as readFile4 } from "fs/promises";
15631
- import path5 from "path";
15964
+ import { readFile as readFile5 } from "fs/promises";
15965
+ import path6 from "path";
15632
15966
 
15633
15967
  // 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";
15968
+ import { mkdir as mkdir2, readFile as readFile4, readdir as readdir2, rm, writeFile as writeFile2 } from "fs/promises";
15969
+ import path5 from "path";
15636
15970
  var SCENES_DIR = "scenes";
15637
15971
  var GLOBAL_PROMPT_FILE = "prompt.json";
15638
15972
  var REBUILD_FILE = "prompt.rebuild.json";
@@ -15649,19 +15983,19 @@ function splitBlueprint(blueprint) {
15649
15983
  }
15650
15984
  async function writeSceneFiles(outDir, blueprint) {
15651
15985
  const { global, scenes } = splitBlueprint(blueprint);
15652
- await writeFile(path4.join(outDir, GLOBAL_PROMPT_FILE), `${JSON.stringify(global, null, 2)}
15986
+ await writeFile2(path5.join(outDir, GLOBAL_PROMPT_FILE), `${JSON.stringify(global, null, 2)}
15653
15987
  `, "utf8");
15654
- const scenesDir = path4.join(outDir, SCENES_DIR);
15655
- await mkdir(scenesDir, { recursive: true });
15988
+ const scenesDir = path5.join(outDir, SCENES_DIR);
15989
+ await mkdir2(scenesDir, { recursive: true });
15656
15990
  const written = /* @__PURE__ */ new Set();
15657
15991
  for (let i = 0; i < scenes.length; i++) {
15658
15992
  const name = sceneFileName(i, scenes.length);
15659
15993
  written.add(name);
15660
- await writeFile(path4.join(scenesDir, name), `${JSON.stringify(scenes[i], null, 2)}
15994
+ await writeFile2(path5.join(scenesDir, name), `${JSON.stringify(scenes[i], null, 2)}
15661
15995
  `, "utf8");
15662
15996
  }
15663
15997
  for (const name of await listSceneFileNames(scenesDir)) {
15664
- if (!written.has(name)) await rm(path4.join(scenesDir, name), { force: true });
15998
+ if (!written.has(name)) await rm(path5.join(scenesDir, name), { force: true });
15665
15999
  }
15666
16000
  }
15667
16001
  async function listSceneFileNames(scenesDir) {
@@ -15674,17 +16008,17 @@ async function listSceneFileNames(scenesDir) {
15674
16008
  return entries.filter((n) => /^s\d+\.json$/.test(n)).sort(bySceneIndex);
15675
16009
  }
15676
16010
  async function listSceneFiles(creativeDir) {
15677
- const scenesDir = path4.join(creativeDir, SCENES_DIR);
15678
- return (await listSceneFileNames(scenesDir)).map((n) => path4.join(scenesDir, n));
16011
+ const scenesDir = path5.join(creativeDir, SCENES_DIR);
16012
+ return (await listSceneFileNames(scenesDir)).map((n) => path5.join(scenesDir, n));
15679
16013
  }
15680
16014
  async function reassembleBlueprint(creativeDir) {
15681
16015
  const files = await listSceneFiles(creativeDir);
15682
16016
  if (files.length === 0) return null;
15683
- const globalRaw = await readFile3(path4.join(creativeDir, GLOBAL_PROMPT_FILE), "utf8");
16017
+ const globalRaw = await readFile4(path5.join(creativeDir, GLOBAL_PROMPT_FILE), "utf8");
15684
16018
  const global = JSON.parse(globalRaw);
15685
16019
  const scenes = [];
15686
16020
  for (const file of files) {
15687
- scenes.push(JSON.parse(await readFile3(file, "utf8")));
16021
+ scenes.push(JSON.parse(await readFile4(file, "utf8")));
15688
16022
  }
15689
16023
  return { ...global, scenes };
15690
16024
  }
@@ -15698,15 +16032,15 @@ function bySceneIndex(a, b) {
15698
16032
  async function computeSourceSha(canvasPath) {
15699
16033
  let canvasBytes;
15700
16034
  try {
15701
- canvasBytes = await readFile4(canvasPath);
16035
+ canvasBytes = await readFile5(canvasPath);
15702
16036
  } catch {
15703
16037
  return void 0;
15704
16038
  }
15705
- const canvasDir = path5.dirname(canvasPath);
15706
- const promptPath = path5.join(canvasDir, "prompt.json");
16039
+ const canvasDir = path6.dirname(canvasPath);
16040
+ const promptPath = path6.join(canvasDir, "prompt.json");
15707
16041
  let promptBytes;
15708
16042
  try {
15709
- promptBytes = await readFile4(promptPath);
16043
+ promptBytes = await readFile5(promptPath);
15710
16044
  } catch {
15711
16045
  promptBytes = Buffer.alloc(0);
15712
16046
  }
@@ -15719,7 +16053,7 @@ async function computeSourceSha(canvasPath) {
15719
16053
  for (const sceneFile of await listSceneFiles(canvasDir)) {
15720
16054
  let sceneBytes;
15721
16055
  try {
15722
- sceneBytes = await readFile4(sceneFile);
16056
+ sceneBytes = await readFile5(sceneFile);
15723
16057
  } catch {
15724
16058
  sceneBytes = Buffer.alloc(0);
15725
16059
  }
@@ -15729,8 +16063,8 @@ async function computeSourceSha(canvasPath) {
15729
16063
  }
15730
16064
 
15731
16065
  // src/commands/canvas/scene-projection.ts
15732
- import { readFile as readFile5 } from "fs/promises";
15733
- import path6 from "path";
16066
+ import { readFile as readFile6 } from "fs/promises";
16067
+ import path7 from "path";
15734
16068
 
15735
16069
  // src/engine/scaffold/video.ts
15736
16070
  import { toCardinal as nwAr } from "n2words/ar-SA";
@@ -18989,12 +19323,12 @@ function nonPromptParamsDiverge(live = {}, rebuilt = {}) {
18989
19323
  return false;
18990
19324
  }
18991
19325
  async function syncSceneNodeParams(canvas, canvasPath, log) {
18992
- const creativeDir = path6.dirname(canvasPath);
19326
+ const creativeDir = path7.dirname(canvasPath);
18993
19327
  const blueprint = await reassembleBlueprint(creativeDir);
18994
19328
  if (!blueprint) return "not_applicable";
18995
19329
  let rebuildRaw;
18996
19330
  try {
18997
- rebuildRaw = await readFile5(path6.join(creativeDir, REBUILD_FILE), "utf8");
19331
+ rebuildRaw = await readFile6(path7.join(creativeDir, REBUILD_FILE), "utf8");
18998
19332
  } catch {
18999
19333
  return "not_applicable";
19000
19334
  }
@@ -19035,7 +19369,7 @@ async function syncSceneNodeParams(canvas, canvasPath, log) {
19035
19369
  }
19036
19370
 
19037
19371
  // src/commands/canvas/style-projection.ts
19038
- import { readFile as readFile6, writeFile as writeFile2 } from "fs/promises";
19372
+ import { readFile as readFile7, writeFile as writeFile3 } from "fs/promises";
19039
19373
  function findBlueprintProjection(canvas) {
19040
19374
  if (!canvas || typeof canvas !== "object") return null;
19041
19375
  const nodes = canvas.nodes;
@@ -19063,10 +19397,10 @@ function renderStyleProjectionFromValue(blueprint) {
19063
19397
  async function syncStyleProjection(canvas, log) {
19064
19398
  const pair = findBlueprintProjection(canvas);
19065
19399
  if (!pair) return "not_applicable";
19066
- const rendered = renderStyleProjection(await readFile6(pair.promptPath, "utf8"));
19067
- const current = await readFile6(pair.stylePath, "utf8").catch(() => null);
19400
+ const rendered = renderStyleProjection(await readFile7(pair.promptPath, "utf8"));
19401
+ const current = await readFile7(pair.stylePath, "utf8").catch(() => null);
19068
19402
  if (current === rendered) return "up_to_date";
19069
- await writeFile2(pair.stylePath, rendered, "utf8");
19403
+ await writeFile3(pair.stylePath, rendered, "utf8");
19070
19404
  log(
19071
19405
  "[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
19406
  );
@@ -19126,13 +19460,13 @@ ${body}` : header || body || compactJson(record);
19126
19460
  }
19127
19461
 
19128
19462
  // src/commands/canvas/run-record.ts
19129
- import path7 from "path";
19463
+ import path8 from "path";
19130
19464
  var MAX_RUN_NODES = 200;
19131
19465
  var MAX_OUTPUTS_PER_NODE = 10;
19132
19466
  var MAX_FINAL_OUTPUTS = 10;
19133
19467
  var MAX_CREATIVE_SLUG_LENGTH = 100;
19134
19468
  function creativeSlugFromCanvasPath(filePath) {
19135
- const normalized = filePath.split(path7.sep).join("/");
19469
+ const normalized = filePath.split(path8.sep).join("/");
19136
19470
  const match = normalized.match(/(?:^|\/)src\/creatives\/([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\//);
19137
19471
  const slug = match?.[1] ?? null;
19138
19472
  return slug && slug.length <= MAX_CREATIVE_SLUG_LENGTH ? slug : null;
@@ -19420,7 +19754,7 @@ var RunRecordPoster = class {
19420
19754
 
19421
19755
  // src/commands/canvas/run-retention.ts
19422
19756
  import { rm as rm2 } from "fs/promises";
19423
- import path8 from "path";
19757
+ import path9 from "path";
19424
19758
  function runDirsToPrune(entries, keep, currentRunId) {
19425
19759
  const runs = entries.filter((e) => /^r_[0-9A-Za-z]+$/.test(e) && e !== currentRunId).sort();
19426
19760
  if (keep <= 0) return runs;
@@ -19437,7 +19771,7 @@ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
19437
19771
  const toPrune = runDirsToPrune(entries, keep, currentRunId);
19438
19772
  if (toPrune.length === 0) return;
19439
19773
  for (const dir of toPrune) {
19440
- await rm2(path8.join(outputsDir, dir), { recursive: true, force: true }).catch(
19774
+ await rm2(path9.join(outputsDir, dir), { recursive: true, force: true }).catch(
19441
19775
  (e) => log(`[prune ] could not remove ${dir}: ${e.message}`)
19442
19776
  );
19443
19777
  }
@@ -19445,13 +19779,13 @@ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
19445
19779
  }
19446
19780
 
19447
19781
  // 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";
19782
+ import { mkdir as mkdir3, readdir as readdir3, rm as rm3, writeFile as writeFile4 } from "fs/promises";
19783
+ import path10 from "path";
19450
19784
  function creativeDirtyDir(base) {
19451
- return base ?? path9.resolve("canvas", ".dirty");
19785
+ return base ?? path10.resolve("canvas", ".dirty");
19452
19786
  }
19453
19787
  function dirtyMarkerFile(slug, base) {
19454
- return path9.join(creativeDirtyDir(base), `${slug}.json`);
19788
+ return path10.join(creativeDirtyDir(base), `${slug}.json`);
19455
19789
  }
19456
19790
  async function clearCreativeDirty(slug, base) {
19457
19791
  try {
@@ -19461,18 +19795,18 @@ async function clearCreativeDirty(slug, base) {
19461
19795
  }
19462
19796
 
19463
19797
  // 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";
19798
+ import { mkdir as mkdir4, readFile as readFile8, rm as rm4, writeFile as writeFile5 } from "fs/promises";
19799
+ import path11 from "path";
19466
19800
  function markerKey(canvasPath) {
19467
19801
  const slug = creativeSlugFromCanvasPath(canvasPath);
19468
- const identity = slug ?? path10.relative(process.cwd(), path10.resolve(canvasPath));
19802
+ const identity = slug ?? path11.relative(process.cwd(), path11.resolve(canvasPath));
19469
19803
  return sha256Hex(Buffer.from(identity)).slice(0, 32);
19470
19804
  }
19471
19805
  function legacyMarkerKey(canvasPath) {
19472
- return sha256Hex(Buffer.from(path10.resolve(canvasPath))).slice(0, 32);
19806
+ return sha256Hex(Buffer.from(path11.resolve(canvasPath))).slice(0, 32);
19473
19807
  }
19474
19808
  function markerFile(outputsDir, key) {
19475
- return path10.join(outputsDir, ".inflight", `${key}.json`);
19809
+ return path11.join(outputsDir, ".inflight", `${key}.json`);
19476
19810
  }
19477
19811
  var REMOTE_ADOPT_STALE_MS = 12e4;
19478
19812
  function classifyRemoteRun(run, now) {
@@ -19508,7 +19842,7 @@ async function resolveRunId(opts) {
19508
19842
  async function readMarkerRunId(outputsDir, canvasPath) {
19509
19843
  for (const key of [markerKey(canvasPath), legacyMarkerKey(canvasPath)]) {
19510
19844
  try {
19511
- const raw = await readFile7(markerFile(outputsDir, key), "utf8");
19845
+ const raw = await readFile8(markerFile(outputsDir, key), "utf8");
19512
19846
  const parsed = JSON.parse(raw);
19513
19847
  if (typeof parsed.runId === "string" && parsed.runId.length > 0) return parsed.runId;
19514
19848
  } catch {
@@ -19519,8 +19853,8 @@ async function readMarkerRunId(outputsDir, canvasPath) {
19519
19853
  async function markRunInFlight(outputsDir, canvasPath, runId) {
19520
19854
  try {
19521
19855
  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() }));
19856
+ await mkdir4(path11.dirname(file), { recursive: true });
19857
+ await writeFile5(file, JSON.stringify({ runId, canvasPath: path11.resolve(canvasPath), startedAt: Date.now() }));
19524
19858
  } catch {
19525
19859
  }
19526
19860
  }
@@ -19534,8 +19868,8 @@ async function clearRunMarker(outputsDir, canvasPath) {
19534
19868
  }
19535
19869
 
19536
19870
  // 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";
19871
+ import { mkdir as mkdir5, readdir as readdir4, readFile as readFile9, stat as stat2, writeFile as writeFile6 } from "fs/promises";
19872
+ import path12 from "path";
19539
19873
  var SNAPSHOT_SCHEMA = "baker-canvas-snapshot/1";
19540
19874
  var MAX_SNAPSHOT_FILE_BYTES = 32 * 1024 * 1024;
19541
19875
  var EXT_TO_MIME = {
@@ -19562,11 +19896,11 @@ var EXT_TO_MIME = {
19562
19896
  woff2: "font/woff2"
19563
19897
  };
19564
19898
  function mimeForFile(filePath) {
19565
- const ext = path11.extname(filePath).slice(1).toLowerCase();
19899
+ const ext = path12.extname(filePath).slice(1).toLowerCase();
19566
19900
  return EXT_TO_MIME[ext] ?? "application/octet-stream";
19567
19901
  }
19568
19902
  function toPosix(p) {
19569
- return p.split(path11.sep).join("/");
19903
+ return p.split(path12.sep).join("/");
19570
19904
  }
19571
19905
  function localPathRefsFromCanvas(parsed) {
19572
19906
  const nodes = parsed?.nodes;
@@ -19591,37 +19925,37 @@ function isSnapshotablePath(value) {
19591
19925
  async function sourceRefsToSnapshot(canvasDir, parsed) {
19592
19926
  const refs = new Set(localPathRefsFromCanvas(parsed));
19593
19927
  for (const sceneFile of await listSceneFiles(canvasDir)) {
19594
- refs.add(toPosix(path11.relative(canvasDir, sceneFile)));
19928
+ refs.add(toPosix(path12.relative(canvasDir, sceneFile)));
19595
19929
  }
19596
19930
  refs.add(REBUILD_FILE);
19597
19931
  return [...refs];
19598
19932
  }
19599
19933
  async function uploadRunSnapshot(client, opts) {
19600
19934
  try {
19601
- const canvasDir = path11.dirname(opts.canvasPath);
19935
+ const canvasDir = path12.dirname(opts.canvasPath);
19602
19936
  const put = (bytes, mime) => putContentAddressed(client, bytes, mime, opts.signal);
19603
19937
  const canvasBytes = Buffer.from(opts.raw);
19604
19938
  const canvasUpload = await put(canvasBytes, "application/json");
19605
19939
  const files = [];
19606
19940
  const skipped = [];
19607
19941
  for (const refPath of await sourceRefsToSnapshot(canvasDir, opts.parsed)) {
19608
- const abs = path11.isAbsolute(refPath) ? refPath : path11.resolve(canvasDir, refPath);
19942
+ const abs = path12.isAbsolute(refPath) ? refPath : path12.resolve(canvasDir, refPath);
19609
19943
  let st;
19610
19944
  try {
19611
19945
  st = await stat2(abs);
19612
19946
  } catch {
19613
- skipped.push({ path: toPosix(path11.relative(canvasDir, abs)), reason: "missing" });
19947
+ skipped.push({ path: toPosix(path12.relative(canvasDir, abs)), reason: "missing" });
19614
19948
  continue;
19615
19949
  }
19616
19950
  const fileList = st.isDirectory() ? await listFilesRecursive(abs) : [abs];
19617
19951
  for (const file of fileList) {
19618
- const rel = toPosix(path11.relative(canvasDir, file));
19952
+ const rel = toPosix(path12.relative(canvasDir, file));
19619
19953
  const size = (await stat2(file)).size;
19620
19954
  if (size > MAX_SNAPSHOT_FILE_BYTES) {
19621
19955
  skipped.push({ path: rel, reason: `too large (${size} bytes)` });
19622
19956
  continue;
19623
19957
  }
19624
- const bytes = await readFile8(file);
19958
+ const bytes = await readFile9(file);
19625
19959
  const upload = await put(bytes, mimeForFile(file));
19626
19960
  files.push({ path: rel, sha256: upload.sha256, url: upload.url });
19627
19961
  }
@@ -19630,7 +19964,7 @@ async function uploadRunSnapshot(client, opts) {
19630
19964
  schema: SNAPSHOT_SCHEMA,
19631
19965
  creativeSlug: opts.creativeSlug,
19632
19966
  canvasSha: canvasUpload.sha256,
19633
- canvas: { path: path11.basename(opts.canvasPath), sha256: canvasUpload.sha256, url: canvasUpload.url },
19967
+ canvas: { path: path12.basename(opts.canvasPath), sha256: canvasUpload.sha256, url: canvasUpload.url },
19634
19968
  files,
19635
19969
  skipped: skipped.length > 0 ? skipped : void 0
19636
19970
  };
@@ -19657,7 +19991,7 @@ async function putContentAddressed(client, bytes, mime, signal) {
19657
19991
  }
19658
19992
  async function listFilesRecursive(dir) {
19659
19993
  const entries = await readdir4(dir, { recursive: true, withFileTypes: true });
19660
- return entries.filter((d) => d.isFile()).map((d) => path11.join(d.parentPath, d.name));
19994
+ return entries.filter((d) => d.isFile()).map((d) => path12.join(d.parentPath, d.name));
19661
19995
  }
19662
19996
  var SnapshotConflictError = class extends Error {
19663
19997
  conflicts;
@@ -19669,19 +20003,19 @@ var SnapshotConflictError = class extends Error {
19669
20003
  };
19670
20004
  async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
19671
20005
  const entries = [manifest.canvas, ...manifest.files];
19672
- const resolvedTarget = path11.resolve(targetDir);
20006
+ const resolvedTarget = path12.resolve(targetDir);
19673
20007
  const planned = [];
19674
20008
  const conflicts = [];
19675
20009
  const upToDate = [];
19676
20010
  for (const entry of entries) {
19677
- if (path11.isAbsolute(entry.path) || entry.path.split("/").includes("..")) {
20011
+ if (path12.isAbsolute(entry.path) || entry.path.split("/").includes("..")) {
19678
20012
  throw new Error(`snapshot entry escapes the creative directory: ${entry.path}`);
19679
20013
  }
19680
- const target = path11.resolve(resolvedTarget, entry.path);
19681
- if (target !== resolvedTarget && !target.startsWith(resolvedTarget + path11.sep)) {
20014
+ const target = path12.resolve(resolvedTarget, entry.path);
20015
+ if (target !== resolvedTarget && !target.startsWith(resolvedTarget + path12.sep)) {
19682
20016
  throw new Error(`snapshot entry escapes the creative directory: ${entry.path}`);
19683
20017
  }
19684
- const existing = await readFile8(target).catch(() => null);
20018
+ const existing = await readFile9(target).catch(() => null);
19685
20019
  if (existing) {
19686
20020
  if (sha256Hex(existing) === entry.sha256) {
19687
20021
  upToDate.push(entry.path);
@@ -19703,15 +20037,15 @@ async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
19703
20037
  if (sha256Hex(bytes) !== entry.sha256) {
19704
20038
  throw new Error(`snapshot download for ${entry.path} does not match its recorded sha256`);
19705
20039
  }
19706
- await mkdir4(path11.dirname(target), { recursive: true });
19707
- await writeFile5(target, bytes);
20040
+ await mkdir5(path12.dirname(target), { recursive: true });
20041
+ await writeFile6(target, bytes);
19708
20042
  restored.push(entry.path);
19709
20043
  }
19710
- return { canvasPath: path11.resolve(resolvedTarget, manifest.canvas.path), restored, upToDate };
20044
+ return { canvasPath: path12.resolve(resolvedTarget, manifest.canvas.path), restored, upToDate };
19711
20045
  }
19712
20046
 
19713
20047
  // src/commands/canvas/run.ts
19714
- var runCommand = defineCommand88({
20048
+ var runCommand = defineCommand90({
19715
20049
  meta: { name: "run", description: "Validate and execute a canvas JSON file." },
19716
20050
  args: {
19717
20051
  file: { type: "positional", required: true, description: "Path to canvas JSON" },
@@ -19787,8 +20121,8 @@ function resolveMaxCredits(...candidates) {
19787
20121
  return void 0;
19788
20122
  }
19789
20123
  async function executeCanvasRun(opts) {
19790
- const filePath = path12.resolve(opts.file);
19791
- const raw = await readFile9(filePath, "utf8");
20124
+ const filePath = path13.resolve(opts.file);
20125
+ const raw = await readFile10(filePath, "utf8");
19792
20126
  let parsed;
19793
20127
  try {
19794
20128
  parsed = JSON.parse(raw);
@@ -19800,7 +20134,7 @@ async function executeCanvasRun(opts) {
19800
20134
  }
19801
20135
  const attemptedSlug = creativeSlugFromCanvasPath(filePath);
19802
20136
  if (attemptedSlug) await clearCreativeDirty(attemptedSlug);
19803
- parsed = resolveRelativeCanvasPaths(parsed, path12.dirname(filePath));
20137
+ parsed = resolveRelativeCanvasPaths(parsed, path13.dirname(filePath));
19804
20138
  try {
19805
20139
  await syncStyleProjection(parsed, (line) => process.stderr.write(`${line}
19806
20140
  `));
@@ -19906,7 +20240,7 @@ async function executeCanvasRun(opts) {
19906
20240
  const canvasSha = sha256Hex(Buffer.from(raw));
19907
20241
  const creativeSlug = creativeSlugFromCanvasPath(filePath) ?? void 0;
19908
20242
  const client = opts.record === false ? null : buildBackendClient();
19909
- const outputsDir = opts.outputsDir ? path12.resolve(opts.outputsDir) : path12.resolve("canvas");
20243
+ const outputsDir = opts.outputsDir ? path13.resolve(opts.outputsDir) : path13.resolve("canvas");
19910
20244
  const { runId, resumed, source, concurrentRunId } = await resolveRunId({
19911
20245
  explicitRunId: opts.runId,
19912
20246
  fresh: opts.fresh === true,
@@ -19940,7 +20274,7 @@ async function executeCanvasRun(opts) {
19940
20274
  const canvasSnapshotUrl = client && creativeSlug ? await uploadRunSnapshot(client, { canvasPath: filePath, raw, creativeSlug, parsed }) ?? void 0 : void 0;
19941
20275
  const recordMeta = {
19942
20276
  creativeSlug,
19943
- canvasPath: path12.relative(process.cwd(), filePath) || void 0,
20277
+ canvasPath: path13.relative(process.cwd(), filePath) || void 0,
19944
20278
  canvasSha,
19945
20279
  // The fingerprint the dashboard compares against the current source to flag
19946
20280
  // "edited since last render". Computed from the on-disk canvas.json +
@@ -20081,7 +20415,7 @@ async function postInitialRunRecord(client, payload) {
20081
20415
 
20082
20416
  // src/commands/canvas/rerun.ts
20083
20417
  var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
20084
- var rerunCommand = defineCommand89({
20418
+ var rerunCommand = defineCommand91({
20085
20419
  meta: {
20086
20420
  name: "rerun",
20087
20421
  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 +20444,24 @@ var rerunCommand = defineCommand89({
20110
20444
  async run({ args }) {
20111
20445
  const slug = String(args.slug);
20112
20446
  if (!SLUG_PATTERN.test(slug)) {
20113
- fail("invalid_slug", `"${slug}" is not a valid creative slug`);
20447
+ fail2("invalid_slug", `"${slug}" is not a valid creative slug`);
20114
20448
  }
20115
20449
  const client = buildBackendClient();
20116
20450
  if (!client) {
20117
- fail("missing_credentials", "rerun needs backend credentials (BAKER_API_URL / BAKER_API_KEY)");
20451
+ fail2("missing_credentials", "rerun needs backend credentials (BAKER_API_URL / BAKER_API_KEY)");
20118
20452
  }
20119
20453
  const latest = await client.getLatestSnapshotRun(slug);
20120
20454
  if (!latest) {
20121
- fail(
20455
+ fail2(
20122
20456
  "no_snapshot_run",
20123
20457
  `no rerunnable history for "${slug}" \u2014 the creative was never run by a CLI that records canvas snapshots`
20124
20458
  );
20125
20459
  }
20126
20460
  const manifest = await fetchManifest(latest.canvasSnapshotUrl);
20127
20461
  if (latest.canvasSha && manifest.canvasSha !== latest.canvasSha) {
20128
- fail("snapshot_mismatch", `snapshot manifest for run ${latest.runId} does not match its recorded canvas sha`);
20462
+ fail2("snapshot_mismatch", `snapshot manifest for run ${latest.runId} does not match its recorded canvas sha`);
20129
20463
  }
20130
- const targetDir = path13.resolve("src", "creatives", slug);
20464
+ const targetDir = path14.resolve("src", "creatives", slug);
20131
20465
  let restoredCanvasPath;
20132
20466
  try {
20133
20467
  const restore = await restoreRunSnapshot(manifest, targetDir, { force: args["force-remote"] === true });
@@ -20145,7 +20479,7 @@ var rerunCommand = defineCommand89({
20145
20479
  }
20146
20480
  } catch (e) {
20147
20481
  if (e instanceof SnapshotConflictError) {
20148
- fail(
20482
+ fail2(
20149
20483
  "local_files_differ",
20150
20484
  `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
20485
  );
@@ -20160,25 +20494,25 @@ var rerunCommand = defineCommand89({
20160
20494
  });
20161
20495
  }
20162
20496
  });
20163
- function fail(code, message) {
20497
+ function fail2(code, message) {
20164
20498
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code, message } }, null, 2)}
20165
20499
  `);
20166
20500
  process.exit(2);
20167
20501
  }
20168
20502
  async function fetchManifest(url) {
20169
20503
  const res = await fetch(url);
20170
- if (!res.ok) fail("snapshot_unavailable", `snapshot manifest download failed: ${res.status} ${res.statusText}`);
20504
+ if (!res.ok) fail2("snapshot_unavailable", `snapshot manifest download failed: ${res.status} ${res.statusText}`);
20171
20505
  const manifest = await res.json();
20172
20506
  if (manifest?.schema !== SNAPSHOT_SCHEMA || typeof manifest.canvas?.path !== "string") {
20173
- fail("snapshot_invalid", "snapshot manifest has an unexpected shape");
20507
+ fail2("snapshot_invalid", "snapshot manifest has an unexpected shape");
20174
20508
  }
20175
20509
  return manifest;
20176
20510
  }
20177
20511
 
20178
20512
  // 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";
20513
+ import { access, mkdir as mkdir6, readFile as readFile12, writeFile as writeFile7 } from "fs/promises";
20514
+ import path18 from "path";
20515
+ import { defineCommand as defineCommand93 } from "citty";
20182
20516
 
20183
20517
  // src/engine/scaffold/staticAd.ts
20184
20518
  import { z as z17 } from "zod";
@@ -20455,7 +20789,7 @@ function staticAdReport(input, elementsInput, opts) {
20455
20789
  }
20456
20790
 
20457
20791
  // src/commands/canvas/creative-definition.ts
20458
- import path14 from "path";
20792
+ import path15 from "path";
20459
20793
  var PLATFORM_VALUES = ["meta", "google", "linkedin", "tiktok", "youtube", "x", "other"];
20460
20794
  var FORMAT_VALUES = ["1:1", "4:5", "9:16", "16:9", "1.91:1"];
20461
20795
  function titleFromSlug(slug) {
@@ -20503,16 +20837,16 @@ function buildCreativeDefinition(input) {
20503
20837
  }
20504
20838
 
20505
20839
  // src/commands/canvas/scaffold-static-ad-paths.ts
20506
- import path15 from "path";
20840
+ import path16 from "path";
20507
20841
  function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd(), slug) {
20508
20842
  const file = rawFile.trim();
20509
20843
  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;
20844
+ const imageSource = imageIsUrl ? file : path16.resolve(cwd, file);
20845
+ 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");
20846
+ const blueprintPath = path16.join(path16.dirname(outPath), "prompt.json");
20847
+ const creativeDir = slug ? path16.dirname(outPath) : null;
20848
+ const definitionPath = creativeDir ? path16.join(creativeDir, "_definition.md") : null;
20849
+ const referencesDir = creativeDir ? path16.join(creativeDir, "references") : null;
20516
20850
  return { imageIsUrl, imageSource, outPath, blueprintPath, creativeDir, definitionPath, referencesDir };
20517
20851
  }
20518
20852
  var SCAFFOLD_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
@@ -20522,9 +20856,9 @@ function isValidScaffoldSlug(slug) {
20522
20856
  }
20523
20857
 
20524
20858
  // 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";
20859
+ import { readdir as readdir5, readFile as readFile11, stat as stat3 } from "fs/promises";
20860
+ import path17 from "path";
20861
+ import { defineCommand as defineCommand92 } from "citty";
20528
20862
 
20529
20863
  // src/commands/canvas/definition-graph.ts
20530
20864
  var MAX_NODES = 300;
@@ -20616,15 +20950,15 @@ async function syncCreativeDefinitionBestEffort(input) {
20616
20950
  }
20617
20951
  }
20618
20952
  async function resolveCanvasPath(inputPath) {
20619
- const resolved = path16.resolve(inputPath);
20953
+ const resolved = path17.resolve(inputPath);
20620
20954
  let dir = resolved;
20621
20955
  try {
20622
20956
  if ((await stat3(resolved)).isFile()) {
20623
20957
  if (resolved.endsWith(".canvas.json")) return resolved;
20624
- dir = path16.dirname(resolved);
20958
+ dir = path17.dirname(resolved);
20625
20959
  }
20626
20960
  } catch {
20627
- dir = resolved.endsWith(".canvas.json") ? path16.dirname(resolved) : resolved;
20961
+ dir = resolved.endsWith(".canvas.json") ? path17.dirname(resolved) : resolved;
20628
20962
  }
20629
20963
  let entries;
20630
20964
  try {
@@ -20635,9 +20969,9 @@ async function resolveCanvasPath(inputPath) {
20635
20969
  const canvases = entries.filter((name) => name.endsWith(".canvas.json"));
20636
20970
  const slug = creativeSlugFromCanvasPath(`${dir}/x/`);
20637
20971
  const chosen = (slug ? canvases.find((name) => name === `${slug}.canvas.json`) : void 0) ?? canvases[0];
20638
- return chosen ? path16.join(dir, chosen) : null;
20972
+ return chosen ? path17.join(dir, chosen) : null;
20639
20973
  }
20640
- var syncDefinitionCommand = defineCommand90({
20974
+ var syncDefinitionCommand = defineCommand92({
20641
20975
  meta: {
20642
20976
  name: "sync-definition",
20643
20977
  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 +20992,7 @@ var syncDefinitionCommand = defineCommand90({
20658
20992
  if (!slug) return;
20659
20993
  let canvas;
20660
20994
  try {
20661
- canvas = JSON.parse(await readFile10(canvasPath, "utf8"));
20995
+ canvas = JSON.parse(await readFile11(canvasPath, "utf8"));
20662
20996
  } catch {
20663
20997
  return;
20664
20998
  }
@@ -20683,7 +21017,7 @@ async function uploadSourceAsReference(source, isUrl, client) {
20683
21017
  if (!res.ok) throw new Error(`failed to download source image (${res.status})`);
20684
21018
  bytes = Buffer.from(await res.arrayBuffer());
20685
21019
  } else {
20686
- bytes = await readFile11(source);
21020
+ bytes = await readFile12(source);
20687
21021
  }
20688
21022
  const safe = await toModelSafeImage(bytes);
20689
21023
  const sha256 = sha256Hex(safe.bytes);
@@ -20738,7 +21072,7 @@ DROP background extras, decorative props, generic scenery, and anything small or
20738
21072
  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
21073
  async function loadAssetText(ref, label) {
20740
21074
  const r = ref;
20741
- if (typeof r?.path === "string") return readFile11(r.path, "utf8");
21075
+ if (typeof r?.path === "string") return readFile12(r.path, "utf8");
20742
21076
  if (typeof r?.url === "string") {
20743
21077
  const res = await fetch(r.url);
20744
21078
  if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
@@ -20762,7 +21096,7 @@ function parseLayout(raw) {
20762
21096
  }
20763
21097
  return null;
20764
21098
  }
20765
- function fail2(code, message) {
21099
+ function fail3(code, message) {
20766
21100
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code, message } }, null, 2)}
20767
21101
  `);
20768
21102
  process.exit(2);
@@ -20853,8 +21187,8 @@ async function runVisionPasses(canvas) {
20853
21187
  outputsByNode = result.outputs_by_node;
20854
21188
  creditsSpent = result.stats?.total_credits;
20855
21189
  } 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));
21190
+ if (e instanceof ValidationError) return fail3("validation", JSON.stringify(e.issues));
21191
+ return fail3("describe", e instanceof Error ? e.message : String(e));
20858
21192
  }
20859
21193
  try {
20860
21194
  const blueprint = JSON.parse(await loadAssetText(outputsByNode.describe?.description, "describe output"));
@@ -20862,10 +21196,10 @@ async function runVisionPasses(canvas) {
20862
21196
  const layout = parseLayout(await loadAssetText(outputsByNode.layout?.text, "layout output"));
20863
21197
  return { blueprint, elements, layout, creditsSpent };
20864
21198
  } catch (e) {
20865
- return fail2("read_outputs", e instanceof Error ? e.message : String(e));
21199
+ return fail3("read_outputs", e instanceof Error ? e.message : String(e));
20866
21200
  }
20867
21201
  }
20868
- var scaffoldStaticAdCommand = defineCommand91({
21202
+ var scaffoldStaticAdCommand = defineCommand93({
20869
21203
  meta: {
20870
21204
  name: "scaffold-static-ad",
20871
21205
  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 +21248,7 @@ var scaffoldStaticAdCommand = defineCommand91({
20914
21248
  process.cwd(),
20915
21249
  slug
20916
21250
  );
20917
- await mkdir5(path17.dirname(outPath), { recursive: true });
21251
+ await mkdir6(path18.dirname(outPath), { recursive: true });
20918
21252
  const { describeModel, selectModel, layoutModel, genModel } = resolveModels(args);
20919
21253
  let durableSourceUrl;
20920
21254
  if (referencesDir) {
@@ -20923,7 +21257,7 @@ var scaffoldStaticAdCommand = defineCommand91({
20923
21257
  try {
20924
21258
  durableSourceUrl = await uploadSourceAsReference(imageSource, imageIsUrl, client);
20925
21259
  } catch (e) {
20926
- return fail2("source_unavailable", e instanceof Error ? e.message : String(e));
21260
+ return fail3("source_unavailable", e instanceof Error ? e.message : String(e));
20927
21261
  }
20928
21262
  }
20929
21263
  }
@@ -20940,7 +21274,7 @@ var scaffoldStaticAdCommand = defineCommand91({
20940
21274
  if (layout && annotated && typeof annotated === "object") {
20941
21275
  annotated.layout = layout;
20942
21276
  }
20943
- await writeFile6(blueprintPath, `${JSON.stringify(annotated, null, 2)}
21277
+ await writeFile7(blueprintPath, `${JSON.stringify(annotated, null, 2)}
20944
21278
  `, "utf8");
20945
21279
  let canvasImagePath = imageSource;
20946
21280
  let canvasImageIsUrl = imageIsUrl;
@@ -20957,7 +21291,7 @@ var scaffoldStaticAdCommand = defineCommand91({
20957
21291
  args.placements ? String(args.placements) : void 0,
20958
21292
  definitionPlatform
20959
21293
  );
20960
- if (!placementsResult.ok) return fail2("invalid_placements", placementsResult.message);
21294
+ if (!placementsResult.ok) return fail3("invalid_placements", placementsResult.message);
20961
21295
  const placements = placementsResult.placements;
20962
21296
  const opts = {
20963
21297
  genModel,
@@ -20975,7 +21309,7 @@ var scaffoldStaticAdCommand = defineCommand91({
20975
21309
  canvas = scaffoldStaticAd(blueprint, elements, opts);
20976
21310
  report = staticAdReport(blueprint, elements, opts);
20977
21311
  } catch (e) {
20978
- return fail2("scaffold", e instanceof Error ? e.message : String(e));
21312
+ return fail3("scaffold", e instanceof Error ? e.message : String(e));
20979
21313
  }
20980
21314
  const validation = await validateCanvasDeep(canvas, defaultRegistry());
20981
21315
  if (!validation.ok) {
@@ -20985,10 +21319,10 @@ var scaffoldStaticAdCommand = defineCommand91({
20985
21319
  );
20986
21320
  process.exit(2);
20987
21321
  }
20988
- await writeFile6(outPath, `${JSON.stringify(canvas, null, 2)}
21322
+ await writeFile7(outPath, `${JSON.stringify(canvas, null, 2)}
20989
21323
  `, "utf8");
20990
21324
  if (definitionPath && !await fileExists(definitionPath)) {
20991
- await writeFile6(
21325
+ await writeFile7(
20992
21326
  definitionPath,
20993
21327
  buildCreativeDefinition({
20994
21328
  title: args.title ? String(args.title) : titleFromSlug(slug ?? ""),
@@ -21034,7 +21368,7 @@ var scaffoldStaticAdCommand = defineCommand91({
21034
21368
  run_estimated_credits: validation.estimatedCredits
21035
21369
  },
21036
21370
  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.`,
21371
+ 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
21372
  assets_to_supply: report.elements,
21039
21373
  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
21374
  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 +21385,10 @@ var scaffoldStaticAdCommand = defineCommand91({
21051
21385
  });
21052
21386
 
21053
21387
  // 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";
21388
+ import { access as access2, cp, mkdir as mkdir7, readFile as readFile15, rm as rm6, writeFile as writeFile8 } from "fs/promises";
21055
21389
  import { tmpdir as tmpdir2 } from "os";
21056
- import path20 from "path";
21057
- import { defineCommand as defineCommand92 } from "citty";
21390
+ import path21 from "path";
21391
+ import { defineCommand as defineCommand94 } from "citty";
21058
21392
 
21059
21393
  // src/engine/scaffold/lib/model-router.ts
21060
21394
  var SEEDANCE = "bytedance/seedance-2.0";
@@ -21092,7 +21426,7 @@ function routeVideoModel(input) {
21092
21426
 
21093
21427
  // src/engine/nodes/local/lib/sceneDetect.ts
21094
21428
  import { execFile as execFile2 } from "child_process";
21095
- import { mkdtemp, readdir as readdir6, readFile as readFile12, rm as rm5 } from "fs/promises";
21429
+ import { mkdtemp, readdir as readdir6, readFile as readFile13, rm as rm5 } from "fs/promises";
21096
21430
  import { tmpdir } from "os";
21097
21431
  import { join as join2 } from "path";
21098
21432
  import { promisify as promisify2 } from "util";
@@ -21168,7 +21502,7 @@ async function runSceneDetectOnce(filePath, threshold, minSceneLenS, timeoutMs)
21168
21502
  );
21169
21503
  const csvName = (await readdir6(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
21170
21504
  if (!csvName) return [];
21171
- return parsePySceneDetectCsvCuts(await readFile12(join2(outDir, csvName), "utf-8"));
21505
+ return parsePySceneDetectCsvCuts(await readFile13(join2(outDir, csvName), "utf-8"));
21172
21506
  } finally {
21173
21507
  await rm5(outDir, { recursive: true, force: true });
21174
21508
  }
@@ -21193,23 +21527,23 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
21193
21527
 
21194
21528
  // src/commands/canvas/composition-path.ts
21195
21529
  import { existsSync as existsSync3 } from "fs";
21196
- import path18 from "path";
21530
+ import path19 from "path";
21197
21531
  function resolveShippedCanvasDir(name, startDir, exists = existsSync3, maxDepth = 8) {
21198
- const rel = path18.join("canvas", name);
21532
+ const rel = path19.join("canvas", name);
21199
21533
  let dir = startDir;
21200
21534
  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);
21535
+ const candidate = path19.join(dir, rel);
21536
+ if (exists(path19.join(candidate, "meta.json"))) return candidate;
21537
+ const parent = path19.dirname(dir);
21204
21538
  if (parent === dir) break;
21205
21539
  dir = parent;
21206
21540
  }
21207
- return path18.resolve(startDir, "../../../", rel);
21541
+ return path19.resolve(startDir, "../../../", rel);
21208
21542
  }
21209
21543
 
21210
21544
  // src/commands/canvas/gitignore.ts
21211
- import { appendFile, readFile as readFile13 } from "fs/promises";
21212
- import path19 from "path";
21545
+ import { appendFile, readFile as readFile14 } from "fs/promises";
21546
+ import path20 from "path";
21213
21547
  function missingGitignoreEntries(existing, entries) {
21214
21548
  const present2 = new Set(
21215
21549
  existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
@@ -21217,10 +21551,10 @@ function missingGitignoreEntries(existing, entries) {
21217
21551
  return entries.filter((e) => !present2.has(e.trim().replace(/\/+$/, "")));
21218
21552
  }
21219
21553
  async function ensureGitignore(dir, entries) {
21220
- const file = path19.join(dir, ".gitignore");
21554
+ const file = path20.join(dir, ".gitignore");
21221
21555
  let existing;
21222
21556
  try {
21223
- existing = await readFile13(file, "utf8");
21557
+ existing = await readFile14(file, "utf8");
21224
21558
  } catch {
21225
21559
  return;
21226
21560
  }
@@ -21259,7 +21593,7 @@ ONE PERSON, MULTIPLE LOOKS: if a single individual plays MULTIPLE personas or wa
21259
21593
  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
21594
  async function loadAssetText2(ref, label) {
21261
21595
  const r = ref;
21262
- if (typeof r?.path === "string") return readFile14(r.path, "utf8");
21596
+ if (typeof r?.path === "string") return readFile15(r.path, "utf8");
21263
21597
  if (typeof r?.url === "string") {
21264
21598
  const res = await fetch(r.url);
21265
21599
  if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
@@ -21278,7 +21612,7 @@ async function loadTranscriptBestEffort(ref) {
21278
21612
  async function stageCaptions(outDir, transcript) {
21279
21613
  const text = transcript?.trim();
21280
21614
  if (!text || text === "[]") return {};
21281
- const compositionPath = path20.join(outDir, "tiktok-captions-composition");
21615
+ const compositionPath = path21.join(outDir, "tiktok-captions-composition");
21282
21616
  await cp(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
21283
21617
  return { compositionPath };
21284
21618
  }
@@ -21296,12 +21630,12 @@ function patchCompositionHtml(html, dims) {
21296
21630
  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
21631
  }
21298
21632
  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");
21633
+ const metaPath = path21.join(compositionDir, "meta.json");
21634
+ const rawMeta = await readFile15(metaPath, "utf8");
21635
+ await writeFile8(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
21636
+ const htmlPath = path21.join(compositionDir, "index.html");
21637
+ const rawHtml = await readFile15(htmlPath, "utf8");
21638
+ await writeFile8(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
21305
21639
  }
21306
21640
  function parseElements2(raw) {
21307
21641
  const parsed = JSON.parse(raw);
@@ -21336,7 +21670,7 @@ async function detectShotCutsBestEffort(videoPath, threshold) {
21336
21670
  return void 0;
21337
21671
  }
21338
21672
  }
21339
- function fail3(code, message) {
21673
+ function fail4(code, message) {
21340
21674
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code, message } }, null, 2)}
21341
21675
  `);
21342
21676
  process.exit(2);
@@ -21348,7 +21682,7 @@ var VIDEO_EXT_BY_MIME = {
21348
21682
  "video/x-matroska": ".mkv"
21349
21683
  };
21350
21684
  function referenceVideoExt(url, contentType) {
21351
- const fromPath = path20.extname(new URL(url).pathname).toLowerCase();
21685
+ const fromPath = path21.extname(new URL(url).pathname).toLowerCase();
21352
21686
  if (fromPath && fromPath.length <= 5) return fromPath;
21353
21687
  const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase();
21354
21688
  return mime && VIDEO_EXT_BY_MIME[mime] || ".mp4";
@@ -21374,7 +21708,7 @@ function videoDefinitionDescription(blueprint) {
21374
21708
  return typeof product === "string" && product.trim() ? product.trim() : void 0;
21375
21709
  }
21376
21710
  async function materializeReferenceVideo(fileArg2) {
21377
- if (!/^https?:\/\//i.test(fileArg2)) return path20.resolve(fileArg2);
21711
+ if (!/^https?:\/\//i.test(fileArg2)) return path21.resolve(fileArg2);
21378
21712
  let res;
21379
21713
  try {
21380
21714
  res = await fetch(fileArg2);
@@ -21384,11 +21718,11 @@ async function materializeReferenceVideo(fileArg2) {
21384
21718
  if (!res.ok) throw new Error(`failed to download reference video (${res.status} ${res.statusText})`);
21385
21719
  const bytes = Buffer.from(await res.arrayBuffer());
21386
21720
  if (bytes.length === 0) throw new Error("reference video download was empty");
21387
- const dest = path20.join(
21721
+ const dest = path21.join(
21388
21722
  tmpdir2(),
21389
21723
  `baker-ref-${sha256Hex(bytes).slice(0, 16)}${referenceVideoExt(fileArg2, res.headers.get("content-type"))}`
21390
21724
  );
21391
- await writeFile7(dest, bytes);
21725
+ await writeFile8(dest, bytes);
21392
21726
  return dest;
21393
21727
  }
21394
21728
  function resolveSeamDedup(raw) {
@@ -21496,9 +21830,9 @@ async function runAnalysisPasses(deconstructCanvas, selectModel) {
21496
21830
  blueprint = JSON.parse(await loadAssetText2(r1.outputs_by_node.deconstruct?.analysis, "deconstruct output"));
21497
21831
  transcript = await loadTranscriptBestEffort(r1.outputs_by_node.deconstruct?.transcript);
21498
21832
  } 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));
21833
+ if (e instanceof ValidationError) return fail4("validation", JSON.stringify(e.issues));
21834
+ if (e instanceof SyntaxError) return fail4("read_outputs", e.message);
21835
+ return fail4("deconstruct", e instanceof Error ? e.message : String(e));
21502
21836
  }
21503
21837
  const slimJson = JSON.stringify(slimBlueprintForSelection(blueprint));
21504
21838
  try {
@@ -21507,12 +21841,12 @@ async function runAnalysisPasses(deconstructCanvas, selectModel) {
21507
21841
  const elements = parseElements2(await loadAssetText2(r2.outputs_by_node.select?.text, "selection output"));
21508
21842
  return { blueprint, elements, transcript, creditsSpent: sawCredits ? credits : void 0 };
21509
21843
  } 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));
21844
+ if (e instanceof ValidationError) return fail4("validation", JSON.stringify(e.issues));
21845
+ if (e instanceof SyntaxError) return fail4("read_outputs", e.message);
21846
+ return fail4("deconstruct", e instanceof Error ? e.message : String(e));
21513
21847
  }
21514
21848
  }
21515
- var scaffoldVideoCommand = defineCommand92({
21849
+ var scaffoldVideoCommand = defineCommand94({
21516
21850
  meta: {
21517
21851
  name: "scaffold-video",
21518
21852
  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 +21927,7 @@ var scaffoldVideoCommand = defineCommand92({
21593
21927
  }
21594
21928
  const isUrl = /^https?:\/\//i.test(fileArg2);
21595
21929
  if (isUrl && !slug && !args.out) {
21596
- return fail3(
21930
+ return fail4(
21597
21931
  "missing_output_target",
21598
21932
  "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
21933
  );
@@ -21602,13 +21936,13 @@ var scaffoldVideoCommand = defineCommand92({
21602
21936
  try {
21603
21937
  videoPath = await materializeReferenceVideo(fileArg2);
21604
21938
  } catch (e) {
21605
- return fail3("download", e instanceof Error ? e.message : String(e));
21939
+ return fail4("download", e instanceof Error ? e.message : String(e));
21606
21940
  }
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");
21941
+ const base = path21.basename(videoPath, path21.extname(videoPath));
21942
+ 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`);
21943
+ const outDir = path21.dirname(outPath);
21944
+ const blueprintPath = path21.join(outDir, "prompt.json");
21945
+ const blueprintStylePath = path21.join(outDir, "prompt.style.json");
21612
21946
  const frames = args.frames === "reuse" ? "reuse" : "generate";
21613
21947
  const maxScenes = args["max-scenes"] ? Number(args["max-scenes"]) : void 0;
21614
21948
  if (Number.isFinite(maxScenes)) {
@@ -21632,10 +21966,10 @@ var scaffoldVideoCommand = defineCommand92({
21632
21966
  process.stderr.write(`\u{1F3AC} Photoreal on-camera cast detected \u2192 generating clips on ${videoModel} (dodges Seedance's real-person filter).
21633
21967
  `);
21634
21968
  }
21635
- await mkdir6(outDir, { recursive: true });
21969
+ await mkdir7(outDir, { recursive: true });
21636
21970
  const annotated = annotateBlueprintWithElements(blueprint, elements);
21637
21971
  await writeSceneFiles(outDir, annotated);
21638
- await writeFile7(blueprintStylePath, renderStyleProjectionFromValue(annotated), "utf8");
21972
+ await writeFile8(blueprintStylePath, renderStyleProjectionFromValue(annotated), "utf8");
21639
21973
  let aspect;
21640
21974
  try {
21641
21975
  aspect = resolveAspect(
@@ -21644,7 +21978,7 @@ var scaffoldVideoCommand = defineCommand92({
21644
21978
  genAspectsFor(videoModel)
21645
21979
  );
21646
21980
  } catch (e) {
21647
- return fail3("aspect", e instanceof Error ? e.message : String(e));
21981
+ return fail4("aspect", e instanceof Error ? e.message : String(e));
21648
21982
  }
21649
21983
  const outDims = canvasDims(aspect.outAr);
21650
21984
  if (aspect.remapped) {
@@ -21653,29 +21987,29 @@ var scaffoldVideoCommand = defineCommand92({
21653
21987
  `
21654
21988
  );
21655
21989
  }
21656
- const compositionDest = path20.join(outDir, "video-overlay-composition");
21990
+ const compositionDest = path21.join(outDir, "video-overlay-composition");
21657
21991
  await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
21658
21992
  await stampCompositionDims(compositionDest, outDims);
21659
- const indexPath = path20.join(compositionDest, "index.html");
21993
+ const indexPath = path21.join(compositionDest, "index.html");
21660
21994
  const overlayHtml = buildOverlayHtml(blueprint, { captionsActive: Boolean(transcript) });
21661
- const indexHtml = await readFile14(indexPath, "utf8");
21995
+ const indexHtml = await readFile15(indexPath, "utf8");
21662
21996
  const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
21663
21997
  if (injected === indexHtml && overlayHtml.trim()) {
21664
- fail3(
21998
+ fail4(
21665
21999
  "composition_marker_missing",
21666
22000
  `video-overlay-composition/index.html is missing the <!--OVERLAYS--> marker \u2014 cannot inject the overlay layer`
21667
22001
  );
21668
22002
  }
21669
- await writeFile7(indexPath, injected, "utf8");
22003
+ await writeFile8(indexPath, injected, "utf8");
21670
22004
  const captions = await stageCaptions(outDir, transcript);
21671
22005
  if (captions.compositionPath) await stampCompositionDims(captions.compositionPath, outDims);
21672
22006
  const opts = {
21673
22007
  imageModel,
21674
22008
  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),
22009
+ overlayCompositionPath: path21.relative(outDir, compositionDest),
22010
+ captionsCompositionPath: captions.compositionPath ? path21.relative(outDir, captions.compositionPath) : void 0,
22011
+ blueprintPath: path21.relative(outDir, blueprintPath),
22012
+ blueprintStylePath: path21.relative(outDir, blueprintStylePath),
21679
22013
  frames,
21680
22014
  ambient: Boolean(args.ambient),
21681
22015
  seamDedup: resolveSeamDedup(args["seam-dedup"]),
@@ -21688,7 +22022,7 @@ var scaffoldVideoCommand = defineCommand92({
21688
22022
  canvas = scaffoldVideoCanvas(blueprint, elements, opts);
21689
22023
  report = videoReport(blueprint, elements);
21690
22024
  } catch (e) {
21691
- return fail3("scaffold", e instanceof Error ? e.message : String(e));
22025
+ return fail4("scaffold", e instanceof Error ? e.message : String(e));
21692
22026
  }
21693
22027
  if (captions.compositionPath && !canvas.nodes.some((n) => n.id === "captions")) {
21694
22028
  await rm6(captions.compositionPath, { recursive: true, force: true });
@@ -21700,10 +22034,10 @@ var scaffoldVideoCommand = defineCommand92({
21700
22034
  todo.blocking_validation_issues = validation.issues;
21701
22035
  meta.todo = todo;
21702
22036
  }
21703
- await writeFile7(outPath, `${JSON.stringify(canvas, null, 2)}
22037
+ await writeFile8(outPath, `${JSON.stringify(canvas, null, 2)}
21704
22038
  `, "utf8");
21705
- await writeFile7(
21706
- path20.join(outDir, REBUILD_FILE),
22039
+ await writeFile8(
22040
+ path21.join(outDir, REBUILD_FILE),
21707
22041
  `${JSON.stringify({ elements, opts }, null, 2)}
21708
22042
  `,
21709
22043
  "utf8"
@@ -21728,9 +22062,9 @@ var scaffoldVideoCommand = defineCommand92({
21728
22062
  await ensureGitignore(process.cwd(), ["canvas/", ".context/"]);
21729
22063
  const sourceRef = videoSourceReference(blueprint, fileArg2);
21730
22064
  if (slug) {
21731
- const definitionPath = path20.join(outDir, "_definition.md");
22065
+ const definitionPath = path21.join(outDir, "_definition.md");
21732
22066
  if (!await fileExists2(definitionPath)) {
21733
- await writeFile7(
22067
+ await writeFile8(
21734
22068
  definitionPath,
21735
22069
  buildCreativeDefinition({
21736
22070
  title: titleFromSlug(slug),
@@ -21783,7 +22117,7 @@ var scaffoldVideoCommand = defineCommand92({
21783
22117
  graph: canvas.metadata?.video?.graph_stats
21784
22118
  },
21785
22119
  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.`,
22120
+ 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
22121
  recurring_elements_to_supply: report.elements,
21788
22122
  voices_to_confirm: report.dialogue.map((d) => ({
21789
22123
  scene: d.scene,
@@ -21820,9 +22154,9 @@ var scaffoldVideoCommand = defineCommand92({
21820
22154
  });
21821
22155
 
21822
22156
  // 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";
22157
+ import { readFile as readFile16, writeFile as writeFile9 } from "fs/promises";
22158
+ import path22 from "path";
22159
+ import { defineCommand as defineCommand95 } from "citty";
21826
22160
  function setNodePrompt(canvas, nodeId, text) {
21827
22161
  const nodes = canvas?.nodes;
21828
22162
  if (!Array.isArray(nodes)) throw new Error("canvas has no nodes array");
@@ -21837,7 +22171,7 @@ function setNodePrompt(canvas, nodeId, text) {
21837
22171
  newNodes[idx] = newNode;
21838
22172
  return { ...canvas, nodes: newNodes };
21839
22173
  }
21840
- var setPromptCommand = defineCommand93({
22174
+ var setPromptCommand = defineCommand95({
21841
22175
  meta: {
21842
22176
  name: "set-prompt",
21843
22177
  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 +22183,17 @@ var setPromptCommand = defineCommand93({
21849
22183
  "text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
21850
22184
  },
21851
22185
  async run({ args }) {
21852
- const filePath = path21.resolve(String(args.file));
22186
+ const filePath = path22.resolve(String(args.file));
21853
22187
  let canvas;
21854
22188
  try {
21855
- canvas = JSON.parse(await readFile15(filePath, "utf8"));
22189
+ canvas = JSON.parse(await readFile16(filePath, "utf8"));
21856
22190
  } catch (e) {
21857
22191
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "parse", message: String(e) } }, null, 2)}
21858
22192
  `);
21859
22193
  process.exit(2);
21860
22194
  }
21861
22195
  let text;
21862
- if (args["text-file"]) text = await readFile15(path21.resolve(String(args["text-file"])), "utf8");
22196
+ if (args["text-file"]) text = await readFile16(path22.resolve(String(args["text-file"])), "utf8");
21863
22197
  else if (args.text !== void 0) text = String(args.text);
21864
22198
  else {
21865
22199
  process.stderr.write(
@@ -21880,14 +22214,14 @@ var setPromptCommand = defineCommand93({
21880
22214
  process.exit(2);
21881
22215
  return;
21882
22216
  }
21883
- const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path21.dirname(filePath)), defaultRegistry());
22217
+ const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path22.dirname(filePath)), defaultRegistry());
21884
22218
  if (!validation.ok) {
21885
22219
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
21886
22220
  `);
21887
22221
  process.exit(2);
21888
22222
  return;
21889
22223
  }
21890
- await writeFile8(filePath, `${JSON.stringify(updated, null, 2)}
22224
+ await writeFile9(filePath, `${JSON.stringify(updated, null, 2)}
21891
22225
  `, "utf8");
21892
22226
  process.stdout.write(`${JSON.stringify({ ok: true, node: String(args.node), bytes: text.length }, null, 2)}
21893
22227
  `);
@@ -21895,18 +22229,18 @@ var setPromptCommand = defineCommand93({
21895
22229
  });
21896
22230
 
21897
22231
  // 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({
22232
+ import { readFile as readFile17 } from "fs/promises";
22233
+ import path23 from "path";
22234
+ import { defineCommand as defineCommand96 } from "citty";
22235
+ var validateCommand = defineCommand96({
21902
22236
  meta: {
21903
22237
  name: "validate",
21904
22238
  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
22239
  },
21906
22240
  args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
21907
22241
  async run({ args }) {
21908
- const filePath = path22.resolve(String(args.file));
21909
- const raw = await readFile16(filePath, "utf8");
22242
+ const filePath = path23.resolve(String(args.file));
22243
+ const raw = await readFile17(filePath, "utf8");
21910
22244
  let parsed;
21911
22245
  try {
21912
22246
  parsed = JSON.parse(raw);
@@ -21916,7 +22250,7 @@ var validateCommand = defineCommand94({
21916
22250
  `);
21917
22251
  process.exit(2);
21918
22252
  }
21919
- parsed = resolveRelativeCanvasPaths(parsed, path22.dirname(filePath));
22253
+ parsed = resolveRelativeCanvasPaths(parsed, path23.dirname(filePath));
21920
22254
  let styleProjection = "not_applicable";
21921
22255
  try {
21922
22256
  styleProjection = await syncStyleProjection(parsed, (line) => process.stderr.write(`${line}
@@ -21968,7 +22302,7 @@ var validateCommand = defineCommand94({
21968
22302
  });
21969
22303
 
21970
22304
  // src/commands/canvas/index.ts
21971
- var canvasCommand = defineCommand95({
22305
+ var canvasCommand = defineCommand97({
21972
22306
  meta: {
21973
22307
  name: "canvas",
21974
22308
  description: `Run Baker creative canvas JSON files locally. Local nodes execute in-process; remote nodes POST to the Convex backend gateway.
@@ -22002,7 +22336,7 @@ Full guide: __tooling__/docs/tools/baker/canvas.md`
22002
22336
  });
22003
22337
 
22004
22338
  // src/commands/chats/index.ts
22005
- import { defineCommand as defineCommand96 } from "citty";
22339
+ import { defineCommand as defineCommand98 } from "citty";
22006
22340
  var STATUS_GROUPS = ["active", "archived", "all"];
22007
22341
  var REPO_SURFACES = ["knowledge", "company", "brand", "landings", "flows", "creatives"];
22008
22342
  function parseBoundedInt(raw, name, min, max) {
@@ -22024,7 +22358,7 @@ registerSchema({
22024
22358
  full: { type: "boolean", description: "Include each Session's full change list", required: false, default: false }
22025
22359
  }
22026
22360
  });
22027
- var listCommand5 = defineCommand96({
22361
+ var listCommand5 = defineCommand98({
22028
22362
  meta: { name: "list", description: "List other Sessions on this account, newest first." },
22029
22363
  args: {
22030
22364
  status: { type: "string", description: "Filter: active|archived|all (default: all)", required: false },
@@ -22079,7 +22413,7 @@ registerSchema({
22079
22413
  }
22080
22414
  }
22081
22415
  });
22082
- var viewCommand = defineCommand96({
22416
+ var viewCommand = defineCommand98({
22083
22417
  meta: {
22084
22418
  name: "view",
22085
22419
  description: "Inspect one Session's full effects \u2014 git content + actions/tags/ads, kickoff, commits."
@@ -22123,7 +22457,7 @@ registerSchema({
22123
22457
  }
22124
22458
  }
22125
22459
  });
22126
- var transcriptCommand = defineCommand96({
22460
+ var transcriptCommand = defineCommand98({
22127
22461
  meta: { name: "transcript", description: "Read another Session's conversation." },
22128
22462
  args: {
22129
22463
  id: { type: "positional", description: "The Session id", required: true },
@@ -22175,7 +22509,7 @@ registerSchema({
22175
22509
  }
22176
22510
  }
22177
22511
  });
22178
- var diffCommand = defineCommand96({
22512
+ var diffCommand = defineCommand98({
22179
22513
  meta: { name: "diff", description: "See a published Session's real file-level changes." },
22180
22514
  args: {
22181
22515
  id: { type: "positional", description: "The Session id", required: true },
@@ -22219,7 +22553,7 @@ var diffCommand = defineCommand96({
22219
22553
  }
22220
22554
  }
22221
22555
  });
22222
- var chatsCommand = defineCommand96({
22556
+ var chatsCommand = defineCommand98({
22223
22557
  meta: {
22224
22558
  name: "chats",
22225
22559
  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 +22568,13 @@ Full guide: __tooling__/docs/tools/baker/chats.md`
22234
22568
  });
22235
22569
 
22236
22570
  // src/commands/creatives/index.ts
22237
- import { defineCommand as defineCommand98 } from "citty";
22571
+ import { defineCommand as defineCommand100 } from "citty";
22238
22572
 
22239
22573
  // src/commands/creatives/publish.ts
22240
- import { defineCommand as defineCommand97 } from "citty";
22574
+ import { defineCommand as defineCommand99 } from "citty";
22241
22575
 
22242
22576
  // src/commands/images/api.ts
22243
- import { readFile as readFile17 } from "fs/promises";
22577
+ import { readFile as readFile18 } from "fs/promises";
22244
22578
  import { extname } from "path";
22245
22579
  var imageProcessingTimeoutMs = 18e4;
22246
22580
  var imageReadyPollIntervalMs = 2e3;
@@ -22254,7 +22588,7 @@ var mimeMap = {
22254
22588
  ".avif": "image/avif"
22255
22589
  };
22256
22590
  var defaultImageApiDeps = {
22257
- readFile: readFile17,
22591
+ readFile: readFile18,
22258
22592
  post: apiPost,
22259
22593
  get: apiGet,
22260
22594
  sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
@@ -22381,7 +22715,7 @@ async function publishCreative(args, deps = defaultImageApiDeps) {
22381
22715
  chatId: chatIdFromEnv()
22382
22716
  });
22383
22717
  }
22384
- var publishCommand = defineCommand97({
22718
+ var publishCommand = defineCommand99({
22385
22719
  meta: {
22386
22720
  name: "publish",
22387
22721
  description: "Publish a final static creative image to Baker Creatives and print the creative reference JSON."
@@ -22439,7 +22773,7 @@ var publishCommand = defineCommand97({
22439
22773
  });
22440
22774
 
22441
22775
  // src/commands/creatives/index.ts
22442
- var creativesCommand3 = defineCommand98({
22776
+ var creativesCommand3 = defineCommand100({
22443
22777
  meta: {
22444
22778
  name: "creatives",
22445
22779
  description: `Publish static ad creatives as first-class Baker outputs.
@@ -22456,7 +22790,7 @@ Full guide: __tooling__/docs/tools/baker/creatives.md`
22456
22790
  });
22457
22791
 
22458
22792
  // src/commands/flows/index.ts
22459
- import { defineCommand as defineCommand99 } from "citty";
22793
+ import { defineCommand as defineCommand101 } from "citty";
22460
22794
 
22461
22795
  // src/commands/flows/shared.ts
22462
22796
  import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync9 } from "fs";
@@ -22489,12 +22823,12 @@ function listFlowSlugs() {
22489
22823
  return readdirSync2(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith("_") && entry.name !== ".gitkeep").map((entry) => entry.name).sort();
22490
22824
  }
22491
22825
  function readFlowTree(slug) {
22492
- const path27 = join3(flowsDir(), slug, "_data.json");
22493
- if (!existsSync4(path27)) {
22826
+ const path28 = join3(flowsDir(), slug, "_data.json");
22827
+ if (!existsSync4(path28)) {
22494
22828
  failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
22495
22829
  }
22496
22830
  try {
22497
- return JSON.parse(readFileSync9(path27, "utf-8"));
22831
+ return JSON.parse(readFileSync9(path28, "utf-8"));
22498
22832
  } catch (error) {
22499
22833
  failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
22500
22834
  }
@@ -22611,7 +22945,7 @@ function displayName(slug) {
22611
22945
  const name = tree.displayName;
22612
22946
  return typeof name === "string" && name.trim() ? name.trim() : slug;
22613
22947
  }
22614
- var listCommand6 = defineCommand99({
22948
+ var listCommand6 = defineCommand101({
22615
22949
  meta: {
22616
22950
  name: "list",
22617
22951
  description: "List Forms in this workspace with the count of confidential fields still needing setup. Example: baker flows list"
@@ -22625,7 +22959,7 @@ var listCommand6 = defineCommand99({
22625
22959
  writeJson({ ok: true, data: { flows } });
22626
22960
  }
22627
22961
  });
22628
- var showCommand2 = defineCommand99({
22962
+ var showCommand2 = defineCommand101({
22629
22963
  meta: {
22630
22964
  name: "show",
22631
22965
  description: "Show a Form's confidential fields and their configuration status (never secret values). Example: baker flows show contact"
@@ -22646,7 +22980,7 @@ var showCommand2 = defineCommand99({
22646
22980
  writeJson({ ok: true, data: response });
22647
22981
  }
22648
22982
  });
22649
- var flowsCommand = defineCommand99({
22983
+ var flowsCommand = defineCommand101({
22650
22984
  meta: {
22651
22985
  name: "flows",
22652
22986
  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 +23008,10 @@ Full guide: __tooling__/docs/tools/baker/flows.md`
22674
23008
  });
22675
23009
 
22676
23010
  // src/commands/ga4/index.ts
22677
- import { defineCommand as defineCommand103 } from "citty";
23011
+ import { defineCommand as defineCommand105 } from "citty";
22678
23012
 
22679
23013
  // src/commands/ga4/audit.ts
22680
- import { defineCommand as defineCommand100 } from "citty";
23014
+ import { defineCommand as defineCommand102 } from "citty";
22681
23015
 
22682
23016
  // src/commands/ga4/resolve.ts
22683
23017
  async function fetchProperties(useCache = true) {
@@ -22740,7 +23074,7 @@ registerSchema({
22740
23074
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
22741
23075
  }
22742
23076
  });
22743
- var auditCommand2 = defineCommand100({
23077
+ var auditCommand2 = defineCommand102({
22744
23078
  meta: {
22745
23079
  name: "audit",
22746
23080
  description: `Run all GA4 admin health checks. Returns property config with playbook warnings.
@@ -22792,7 +23126,7 @@ Examples:
22792
23126
  });
22793
23127
 
22794
23128
  // src/commands/ga4/properties.ts
22795
- import { defineCommand as defineCommand101 } from "citty";
23129
+ import { defineCommand as defineCommand103 } from "citty";
22796
23130
  registerSchema({
22797
23131
  command: "ga4.properties",
22798
23132
  description: "List all accessible GA4 properties. Returns property IDs needed for query and audit commands. Run this first to find property IDs.",
@@ -22800,7 +23134,7 @@ registerSchema({
22800
23134
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
22801
23135
  }
22802
23136
  });
22803
- var propertiesCommand = defineCommand101({
23137
+ var propertiesCommand = defineCommand103({
22804
23138
  meta: {
22805
23139
  name: "properties",
22806
23140
  description: `List accessible GA4 properties.
@@ -22850,7 +23184,7 @@ Examples:
22850
23184
  // src/commands/ga4/query.ts
22851
23185
  import { appendFileSync as appendFileSync2, existsSync as existsSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync3 } from "fs";
22852
23186
  import { resolve as resolve2 } from "path";
22853
- import { defineCommand as defineCommand102 } from "citty";
23187
+ import { defineCommand as defineCommand104 } from "citty";
22854
23188
 
22855
23189
  // src/commands/ga4/presets.ts
22856
23190
  var GA4_PRESETS = [
@@ -22982,7 +23316,7 @@ function handleError(err) {
22982
23316
  });
22983
23317
  process.exit(1);
22984
23318
  }
22985
- var queryCommand2 = defineCommand102({
23319
+ var queryCommand2 = defineCommand104({
22986
23320
  meta: {
22987
23321
  name: "query",
22988
23322
  description: `Run GA4 Data API reports. Preset-first with free-form escape hatch.
@@ -23053,7 +23387,7 @@ Free-form (escape hatch):
23053
23387
  });
23054
23388
 
23055
23389
  // src/commands/ga4/index.ts
23056
- var ga4Command = defineCommand103({
23390
+ var ga4Command = defineCommand105({
23057
23391
  meta: {
23058
23392
  name: "ga4",
23059
23393
  description: `Google Analytics 4 commands. Audit property config, run playbook-aligned reports.
@@ -23077,12 +23411,12 @@ Full guide: __tooling__/docs/tools/baker/ga4.md`
23077
23411
  });
23078
23412
 
23079
23413
  // src/commands/gsc/index.ts
23080
- import { defineCommand as defineCommand107 } from "citty";
23414
+ import { defineCommand as defineCommand109 } from "citty";
23081
23415
 
23082
23416
  // src/commands/gsc/query.ts
23083
23417
  import { appendFileSync as appendFileSync3, existsSync as existsSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "fs";
23084
23418
  import { resolve as resolve3 } from "path";
23085
- import { defineCommand as defineCommand104 } from "citty";
23419
+ import { defineCommand as defineCommand106 } from "citty";
23086
23420
 
23087
23421
  // src/commands/gsc/presets.ts
23088
23422
  var GSC_PRESETS = [
@@ -23270,7 +23604,7 @@ function handleError2(err) {
23270
23604
  });
23271
23605
  process.exit(1);
23272
23606
  }
23273
- var queryCommand3 = defineCommand104({
23607
+ var queryCommand3 = defineCommand106({
23274
23608
  meta: {
23275
23609
  name: "query",
23276
23610
  description: `Run GSC Search Analytics queries. Preset-first with free-form escape hatch.
@@ -23348,7 +23682,7 @@ Free-form (escape hatch):
23348
23682
  });
23349
23683
 
23350
23684
  // src/commands/gsc/sitemaps.ts
23351
- import { defineCommand as defineCommand105 } from "citty";
23685
+ import { defineCommand as defineCommand107 } from "citty";
23352
23686
  registerSchema({
23353
23687
  command: "gsc.sitemaps",
23354
23688
  description: "List sitemaps for a Search Console site. Check sitemap health and errors.",
@@ -23357,7 +23691,7 @@ registerSchema({
23357
23691
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
23358
23692
  }
23359
23693
  });
23360
- var sitemapsCommand = defineCommand105({
23694
+ var sitemapsCommand = defineCommand107({
23361
23695
  meta: {
23362
23696
  name: "sitemaps",
23363
23697
  description: `List sitemaps for a site. Check health and errors.
@@ -23407,7 +23741,7 @@ Examples:
23407
23741
  });
23408
23742
 
23409
23743
  // src/commands/gsc/sites.ts
23410
- import { defineCommand as defineCommand106 } from "citty";
23744
+ import { defineCommand as defineCommand108 } from "citty";
23411
23745
  registerSchema({
23412
23746
  command: "gsc.sites",
23413
23747
  description: "List all verified Google Search Console sites. Returns site URLs needed for query and sitemaps commands.",
@@ -23415,7 +23749,7 @@ registerSchema({
23415
23749
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
23416
23750
  }
23417
23751
  });
23418
- var sitesCommand = defineCommand106({
23752
+ var sitesCommand = defineCommand108({
23419
23753
  meta: {
23420
23754
  name: "sites",
23421
23755
  description: `List verified Search Console sites.
@@ -23463,7 +23797,7 @@ Examples:
23463
23797
  });
23464
23798
 
23465
23799
  // src/commands/gsc/index.ts
23466
- var gscCommand = defineCommand107({
23800
+ var gscCommand = defineCommand109({
23467
23801
  meta: {
23468
23802
  name: "gsc",
23469
23803
  description: `Google Search Console commands. PPC-SEO arbitrage, brand halo analysis, negative keyword discovery.
@@ -23487,7 +23821,7 @@ Full guide: __tooling__/docs/tools/baker/gsc.md`
23487
23821
  });
23488
23822
 
23489
23823
  // src/commands/history/index.ts
23490
- import { defineCommand as defineCommand108 } from "citty";
23824
+ import { defineCommand as defineCommand110 } from "citty";
23491
23825
  registerSchema({
23492
23826
  command: "history.list",
23493
23827
  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 +23876,7 @@ function parseBoundedInt2(raw, name, min, max) {
23542
23876
  }
23543
23877
  return value;
23544
23878
  }
23545
- var listCommand7 = defineCommand108({
23879
+ var listCommand7 = defineCommand110({
23546
23880
  meta: {
23547
23881
  name: "list",
23548
23882
  description: "List recent account changes (unified audit log), newest first."
@@ -23588,7 +23922,7 @@ var listCommand7 = defineCommand108({
23588
23922
  }
23589
23923
  }
23590
23924
  });
23591
- var historyCommand = defineCommand108({
23925
+ var historyCommand = defineCommand110({
23592
23926
  meta: {
23593
23927
  name: "history",
23594
23928
  description: `Unified account history (audit log): what changed, who did it, and when.
@@ -23598,10 +23932,10 @@ Full guide: __tooling__/docs/tools/baker/history.md`
23598
23932
  });
23599
23933
 
23600
23934
  // src/commands/images/index.ts
23601
- import { defineCommand as defineCommand132 } from "citty";
23935
+ import { defineCommand as defineCommand134 } from "citty";
23602
23936
 
23603
23937
  // src/commands/images/crop.ts
23604
- import { defineCommand as defineCommand109 } from "citty";
23938
+ import { defineCommand as defineCommand111 } from "citty";
23605
23939
 
23606
23940
  // src/lib/image/crop-sprite.ts
23607
23941
  import sharp from "sharp";
@@ -23616,7 +23950,7 @@ function cropSprite(input, region) {
23616
23950
 
23617
23951
  // src/lib/image/io.ts
23618
23952
  import { randomBytes } from "crypto";
23619
- import { glob as fsGlob, readFile as readFile18, rename, stat as stat4, writeFile as writeFile9 } from "fs/promises";
23953
+ import { glob as fsGlob, readFile as readFile19, rename, stat as stat4, writeFile as writeFile10 } from "fs/promises";
23620
23954
  import { dirname as dirname2, extname as extname2, join as join4, resolve as resolve4 } from "path";
23621
23955
  var REMOTE_RE = /^https?:\/\//i;
23622
23956
  var GLOB_RE = /[*?[\]{}]/;
@@ -23652,11 +23986,11 @@ async function readImageBuffer(pathOrUrl) {
23652
23986
  }
23653
23987
  return Buffer.from(await response.arrayBuffer());
23654
23988
  }
23655
- return readFile18(pathOrUrl);
23989
+ return readFile19(pathOrUrl);
23656
23990
  }
23657
- async function isDirectory(path27) {
23991
+ async function isDirectory(path28) {
23658
23992
  try {
23659
- const s = await stat4(path27);
23993
+ const s = await stat4(path28);
23660
23994
  return s.isDirectory();
23661
23995
  } catch {
23662
23996
  return false;
@@ -23675,7 +24009,7 @@ async function atomicWrite(targetPath, data) {
23675
24009
  const absolute = resolve4(targetPath);
23676
24010
  const dir = dirname2(absolute);
23677
24011
  const tmp = join4(dir, `.baker-image-${randomBytes(8).toString("hex")}.tmp`);
23678
- await writeFile9(tmp, data);
24012
+ await writeFile10(tmp, data);
23679
24013
  await rename(tmp, absolute);
23680
24014
  }
23681
24015
 
@@ -23726,7 +24060,7 @@ function emitError2(err) {
23726
24060
  }
23727
24061
  process.exit(1);
23728
24062
  }
23729
- var cropCommand = defineCommand109({
24063
+ var cropCommand = defineCommand111({
23730
24064
  meta: {
23731
24065
  name: "crop",
23732
24066
  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 +24096,7 @@ var cropCommand = defineCommand109({
23762
24096
  });
23763
24097
 
23764
24098
  // src/commands/images/delete.ts
23765
- import { defineCommand as defineCommand110 } from "citty";
24099
+ import { defineCommand as defineCommand112 } from "citty";
23766
24100
  registerSchema({
23767
24101
  command: "images.delete",
23768
24102
  description: "Delete an image by ID",
@@ -23776,7 +24110,7 @@ registerSchema({
23776
24110
  }
23777
24111
  }
23778
24112
  });
23779
- var deleteCommand = defineCommand110({
24113
+ var deleteCommand = defineCommand112({
23780
24114
  meta: {
23781
24115
  name: "delete",
23782
24116
  description: "Delete an image by ID. Use --dry-run to preview. Example: baker images delete j571abc123 --dry-run"
@@ -23817,7 +24151,7 @@ var deleteCommand = defineCommand110({
23817
24151
  });
23818
24152
 
23819
24153
  // src/commands/images/dimensions.ts
23820
- import { defineCommand as defineCommand111 } from "citty";
24154
+ import { defineCommand as defineCommand113 } from "citty";
23821
24155
 
23822
24156
  // src/lib/image/dimensions.ts
23823
24157
  import { imageSize } from "image-size";
@@ -23840,7 +24174,7 @@ registerSchema({
23840
24174
  target: { type: "string", description: "Local file path or remote http(s) URL", required: true }
23841
24175
  }
23842
24176
  });
23843
- var dimensionsCommand = defineCommand111({
24177
+ var dimensionsCommand = defineCommand113({
23844
24178
  meta: {
23845
24179
  name: "dimensions",
23846
24180
  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 +24218,7 @@ var dimensionsCommand = defineCommand111({
23884
24218
  });
23885
24219
 
23886
24220
  // src/commands/images/extract.ts
23887
- import { defineCommand as defineCommand112 } from "citty";
24221
+ import { defineCommand as defineCommand114 } from "citty";
23888
24222
  registerSchema({
23889
24223
  command: "images.extract",
23890
24224
  description: "Extract images from a URL via Firecrawl (formats: images).",
@@ -23900,7 +24234,7 @@ registerSchema({
23900
24234
  }
23901
24235
  }
23902
24236
  });
23903
- var extractCommand = defineCommand112({
24237
+ var extractCommand = defineCommand114({
23904
24238
  meta: {
23905
24239
  name: "extract",
23906
24240
  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 +24272,7 @@ var extractCommand = defineCommand112({
23938
24272
  });
23939
24273
 
23940
24274
  // src/commands/images/find.ts
23941
- import { defineCommand as defineCommand113 } from "citty";
24275
+ import { defineCommand as defineCommand115 } from "citty";
23942
24276
  registerSchema({
23943
24277
  command: "images.find",
23944
24278
  description: "Fanout image search: library first, then opted-in external providers.",
@@ -23970,7 +24304,7 @@ registerSchema({
23970
24304
  }
23971
24305
  }
23972
24306
  });
23973
- var findCommand = defineCommand113({
24307
+ var findCommand = defineCommand115({
23974
24308
  meta: {
23975
24309
  name: "find",
23976
24310
  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 +24352,8 @@ var findCommand = defineCommand113({
24018
24352
  });
24019
24353
 
24020
24354
  // src/commands/images/generate.ts
24021
- import { readFile as readFile19 } from "fs/promises";
24022
- import { defineCommand as defineCommand114 } from "citty";
24355
+ import { readFile as readFile20 } from "fs/promises";
24356
+ import { defineCommand as defineCommand116 } from "citty";
24023
24357
  import sharp2 from "sharp";
24024
24358
  var GENERATE_TIMEOUT_MS = 18e4;
24025
24359
  var REFERENCE_MAX_EDGE = 1536;
@@ -24108,7 +24442,7 @@ async function resolveReferences(spec) {
24108
24442
  }
24109
24443
  let raw;
24110
24444
  try {
24111
- raw = await readFile19(entry);
24445
+ raw = await readFile20(entry);
24112
24446
  } catch {
24113
24447
  throw new ApiError("VALIDATION_ERROR", `Reference file not found: ${entry}`);
24114
24448
  }
@@ -24122,7 +24456,7 @@ async function resolveReferences(spec) {
24122
24456
  }
24123
24457
  return out;
24124
24458
  }
24125
- var generateCommand = defineCommand114({
24459
+ var generateCommand = defineCommand116({
24126
24460
  meta: {
24127
24461
  name: "generate",
24128
24462
  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 +24508,7 @@ var generateCommand = defineCommand114({
24174
24508
  });
24175
24509
 
24176
24510
  // src/commands/images/get.ts
24177
- import { defineCommand as defineCommand115 } from "citty";
24511
+ import { defineCommand as defineCommand117 } from "citty";
24178
24512
  registerSchema({
24179
24513
  command: "images.get",
24180
24514
  description: "Get a single image by ID",
@@ -24182,7 +24516,7 @@ registerSchema({
24182
24516
  id: { type: "string", description: "Image ID", required: true }
24183
24517
  }
24184
24518
  });
24185
- var getCommand2 = defineCommand115({
24519
+ var getCommand2 = defineCommand117({
24186
24520
  meta: { name: "get", description: "Get a single image by ID. Example: baker images get j571abc123" },
24187
24521
  args: {
24188
24522
  id: { type: "positional", description: "Image ID", required: false },
@@ -24218,7 +24552,7 @@ var getCommand2 = defineCommand115({
24218
24552
  });
24219
24553
 
24220
24554
  // src/commands/images/gif.ts
24221
- import { defineCommand as defineCommand116 } from "citty";
24555
+ import { defineCommand as defineCommand118 } from "citty";
24222
24556
  registerSchema({
24223
24557
  command: "images.gif",
24224
24558
  description: "Search Giphy for GIFs / reaction memes (paid social creative).",
@@ -24250,7 +24584,7 @@ registerSchema({
24250
24584
  }
24251
24585
  }
24252
24586
  });
24253
- var gifCommand = defineCommand116({
24587
+ var gifCommand = defineCommand118({
24254
24588
  meta: {
24255
24589
  name: "gif",
24256
24590
  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 +24631,7 @@ var gifCommand = defineCommand116({
24297
24631
  });
24298
24632
 
24299
24633
  // src/commands/images/google.ts
24300
- import { defineCommand as defineCommand117 } from "citty";
24634
+ import { defineCommand as defineCommand119 } from "citty";
24301
24635
 
24302
24636
  // src/commands/images/searchHints.ts
24303
24637
  var FALLBACK = {
@@ -24361,7 +24695,7 @@ registerSchema({
24361
24695
  }
24362
24696
  }
24363
24697
  });
24364
- var googleCommand2 = defineCommand117({
24698
+ var googleCommand2 = defineCommand119({
24365
24699
  meta: {
24366
24700
  name: "google",
24367
24701
  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 +24751,7 @@ var googleCommand2 = defineCommand117({
24417
24751
  });
24418
24752
 
24419
24753
  // src/commands/images/icon.ts
24420
- import { defineCommand as defineCommand118 } from "citty";
24754
+ import { defineCommand as defineCommand120 } from "citty";
24421
24755
  registerSchema({
24422
24756
  command: "images.icon",
24423
24757
  description: "Icon lookup via Iconify (200+ icon sets, free CDN).",
@@ -24443,7 +24777,7 @@ registerSchema({
24443
24777
  }
24444
24778
  }
24445
24779
  });
24446
- var iconCommand = defineCommand118({
24780
+ var iconCommand = defineCommand120({
24447
24781
  meta: {
24448
24782
  name: "icon",
24449
24783
  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 +24817,7 @@ var iconCommand = defineCommand118({
24483
24817
  });
24484
24818
 
24485
24819
  // src/commands/images/ingest.ts
24486
- import { defineCommand as defineCommand119 } from "citty";
24820
+ import { defineCommand as defineCommand121 } from "citty";
24487
24821
  registerSchema({
24488
24822
  command: "images.ingest",
24489
24823
  description: "Ingest a remote image URL into the library (full describe + embed).",
@@ -24495,7 +24829,7 @@ registerSchema({
24495
24829
  context: { type: "string", description: "Description context hint", required: false }
24496
24830
  }
24497
24831
  });
24498
- var ingestCommand = defineCommand119({
24832
+ var ingestCommand = defineCommand121({
24499
24833
  meta: {
24500
24834
  name: "ingest",
24501
24835
  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 +24871,7 @@ var ingestCommand = defineCommand119({
24537
24871
  });
24538
24872
 
24539
24873
  // src/commands/images/library.ts
24540
- import { defineCommand as defineCommand120 } from "citty";
24874
+ import { defineCommand as defineCommand122 } from "citty";
24541
24875
  registerSchema({
24542
24876
  command: "images.library",
24543
24877
  description: "Search the company image library. Returns only ready images.",
@@ -24563,7 +24897,7 @@ registerSchema({
24563
24897
  }
24564
24898
  }
24565
24899
  });
24566
- var libraryCommand = defineCommand120({
24900
+ var libraryCommand = defineCommand122({
24567
24901
  meta: {
24568
24902
  name: "library",
24569
24903
  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 +24954,7 @@ var libraryCommand = defineCommand120({
24620
24954
  });
24621
24955
 
24622
24956
  // src/commands/images/logo.ts
24623
- import { defineCommand as defineCommand121 } from "citty";
24957
+ import { defineCommand as defineCommand123 } from "citty";
24624
24958
  registerSchema({
24625
24959
  command: "images.logo",
24626
24960
  description: "Brand logo lookup via Brandfetch CDN (fallback/404). Auto-ingests by default.",
@@ -24645,7 +24979,7 @@ registerSchema({
24645
24979
  }
24646
24980
  }
24647
24981
  });
24648
- var logoCommand = defineCommand121({
24982
+ var logoCommand = defineCommand123({
24649
24983
  meta: {
24650
24984
  name: "logo",
24651
24985
  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 +25017,7 @@ var logoCommand = defineCommand121({
24683
25017
  });
24684
25018
 
24685
25019
  // src/commands/images/normalize.ts
24686
- import { defineCommand as defineCommand122 } from "citty";
25020
+ import { defineCommand as defineCommand124 } from "citty";
24687
25021
 
24688
25022
  // src/lib/image/color-changer.ts
24689
25023
  import quantize from "quantize";
@@ -25415,7 +25749,7 @@ function coerceRawArgs(args) {
25415
25749
  "dry-run": bool(args["dry-run"])
25416
25750
  };
25417
25751
  }
25418
- var normalizeCommand = defineCommand122({
25752
+ var normalizeCommand = defineCommand124({
25419
25753
  meta: {
25420
25754
  name: "normalize",
25421
25755
  description: `Normalize logos / images: declarative recolor + bg removal + trim + resize. Operates on local files; writes in-place by default.
@@ -25470,7 +25804,7 @@ Examples:
25470
25804
  });
25471
25805
 
25472
25806
  // src/commands/images/pinterest.ts
25473
- import { defineCommand as defineCommand123 } from "citty";
25807
+ import { defineCommand as defineCommand125 } from "citty";
25474
25808
  registerSchema({
25475
25809
  command: "images.pinterest",
25476
25810
  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 +25824,7 @@ registerSchema({
25490
25824
  }
25491
25825
  }
25492
25826
  });
25493
- var pinterestCommand = defineCommand123({
25827
+ var pinterestCommand = defineCommand125({
25494
25828
  meta: {
25495
25829
  name: "pinterest",
25496
25830
  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 +25864,7 @@ var pinterestCommand = defineCommand123({
25530
25864
  });
25531
25865
 
25532
25866
  // src/commands/images/screenshot.ts
25533
- import { defineCommand as defineCommand124 } from "citty";
25867
+ import { defineCommand as defineCommand126 } from "citty";
25534
25868
  registerSchema({
25535
25869
  command: "images.screenshot",
25536
25870
  description: "Capture a website screenshot via ScreenshotOne. Auto-ingests on success.",
@@ -25546,7 +25880,7 @@ registerSchema({
25546
25880
  }
25547
25881
  }
25548
25882
  });
25549
- var screenshotCommand = defineCommand124({
25883
+ var screenshotCommand = defineCommand126({
25550
25884
  meta: {
25551
25885
  name: "screenshot",
25552
25886
  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 +25943,7 @@ var screenshotCommand = defineCommand124({
25609
25943
  });
25610
25944
 
25611
25945
  // src/commands/images/search.ts
25612
- import { defineCommand as defineCommand125 } from "citty";
25946
+ import { defineCommand as defineCommand127 } from "citty";
25613
25947
  registerSchema({
25614
25948
  command: "images.search",
25615
25949
  description: "Search images by text query. Only returns ready images.",
@@ -25625,7 +25959,7 @@ registerSchema({
25625
25959
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
25626
25960
  }
25627
25961
  });
25628
- var searchCommand = defineCommand125({
25962
+ var searchCommand = defineCommand127({
25629
25963
  meta: {
25630
25964
  name: "search",
25631
25965
  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 +26019,7 @@ var searchCommand = defineCommand125({
25685
26019
  });
25686
26020
 
25687
26021
  // src/commands/images/sticker.ts
25688
- import { defineCommand as defineCommand126 } from "citty";
26022
+ import { defineCommand as defineCommand128 } from "citty";
25689
26023
  registerSchema({
25690
26024
  command: "images.sticker",
25691
26025
  description: "Search Giphy stickers \u2014 transparent-background overlays for ad creative.",
@@ -25717,7 +26051,7 @@ registerSchema({
25717
26051
  }
25718
26052
  }
25719
26053
  });
25720
- var stickerCommand = defineCommand126({
26054
+ var stickerCommand = defineCommand128({
25721
26055
  meta: {
25722
26056
  name: "sticker",
25723
26057
  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 +26098,7 @@ var stickerCommand = defineCommand126({
25764
26098
  });
25765
26099
 
25766
26100
  // src/commands/images/stock.ts
25767
- import { defineCommand as defineCommand127 } from "citty";
26101
+ import { defineCommand as defineCommand129 } from "citty";
25768
26102
  var STOCK_ERROR_FIX = {
25769
26103
  action: "use_different_resource",
25770
26104
  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 +26175,7 @@ function buildStockRequest(query, args) {
25841
26175
  if (args.context) body.descriptionContext = args.context;
25842
26176
  return body;
25843
26177
  }
25844
- var stockCommand = defineCommand127({
26178
+ var stockCommand = defineCommand129({
25845
26179
  meta: {
25846
26180
  name: "stock",
25847
26181
  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 +26231,7 @@ var stockCommand = defineCommand127({
25897
26231
  });
25898
26232
 
25899
26233
  // src/lib/tags-command.ts
25900
- import { defineCommand as defineCommand128 } from "citty";
26234
+ import { defineCommand as defineCommand130 } from "citty";
25901
26235
  function makeTagsCommand(command, label, endpoint) {
25902
26236
  registerSchema({
25903
26237
  command: `${command}.tags`,
@@ -25906,7 +26240,7 @@ function makeTagsCommand(command, label, endpoint) {
25906
26240
  output: { type: "string", description: "Output format: md|json", required: false, default: "md" }
25907
26241
  }
25908
26242
  });
25909
- return defineCommand128({
26243
+ return defineCommand130({
25910
26244
  meta: {
25911
26245
  name: "tags",
25912
26246
  description: `List the available ${label} tag names (defaults + company custom tags). Use before filtering with --tags. Example: baker ${command} tags`
@@ -25942,7 +26276,7 @@ function makeTagsCommand(command, label, endpoint) {
25942
26276
  var tagsCommand2 = makeTagsCommand("images", "image", "/api/images/tags");
25943
26277
 
25944
26278
  // src/commands/images/upload.ts
25945
- import { defineCommand as defineCommand129 } from "citty";
26279
+ import { defineCommand as defineCommand131 } from "citty";
25946
26280
  registerSchema({
25947
26281
  command: "images.upload",
25948
26282
  description: "Upload an image to the library \u2014 local file path or remote http(s) URL.",
@@ -25980,7 +26314,7 @@ registerSchema({
25980
26314
  function isRemoteUrl2(value) {
25981
26315
  return /^https?:\/\//i.test(value);
25982
26316
  }
25983
- var uploadCommand = defineCommand129({
26317
+ var uploadCommand = defineCommand131({
25984
26318
  meta: {
25985
26319
  name: "upload",
25986
26320
  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 +26407,7 @@ async function uploadLocal(target, args) {
26073
26407
  }
26074
26408
 
26075
26409
  // src/commands/images/upscale.ts
26076
- import { defineCommand as defineCommand130 } from "citty";
26410
+ import { defineCommand as defineCommand132 } from "citty";
26077
26411
  registerSchema({
26078
26412
  command: "images.upscale",
26079
26413
  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 +26422,7 @@ registerSchema({
26088
26422
  }
26089
26423
  });
26090
26424
  var POLL_INTERVAL_MS3 = 1500;
26091
- var upscaleCommand = defineCommand130({
26425
+ var upscaleCommand = defineCommand132({
26092
26426
  meta: {
26093
26427
  name: "upscale",
26094
26428
  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 +26477,7 @@ var upscaleCommand = defineCommand130({
26143
26477
  });
26144
26478
 
26145
26479
  // src/commands/images/use.ts
26146
- import { defineCommand as defineCommand131 } from "citty";
26480
+ import { defineCommand as defineCommand133 } from "citty";
26147
26481
  registerSchema({
26148
26482
  command: "images.use",
26149
26483
  description: "Ingest a URL and wait for the library record to be ready.",
@@ -26159,7 +26493,7 @@ registerSchema({
26159
26493
  }
26160
26494
  });
26161
26495
  var POLL_INTERVAL_MS4 = 1500;
26162
- var useCommand = defineCommand131({
26496
+ var useCommand = defineCommand133({
26163
26497
  meta: {
26164
26498
  name: "use",
26165
26499
  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 +26539,7 @@ var useCommand = defineCommand131({
26205
26539
  });
26206
26540
 
26207
26541
  // src/commands/images/index.ts
26208
- var imagesCommand = defineCommand132({
26542
+ var imagesCommand = defineCommand134({
26209
26543
  meta: {
26210
26544
  name: "images",
26211
26545
  description: `Find, source, and normalize images. Subcommands route by provider so cost + license are explicit.
@@ -26276,16 +26610,16 @@ Full guide: __tooling__/docs/tools/baker/images.md`
26276
26610
  });
26277
26611
 
26278
26612
  // src/commands/landing/index.ts
26279
- import { defineCommand as defineCommand134 } from "citty";
26613
+ import { defineCommand as defineCommand136 } from "citty";
26280
26614
 
26281
26615
  // src/commands/landing/critique.ts
26282
26616
  import { readdir as readdir8, stat as stat6 } from "fs/promises";
26283
- import path26 from "path";
26284
- import { defineCommand as defineCommand133 } from "citty";
26617
+ import path27 from "path";
26618
+ import { defineCommand as defineCommand135 } from "citty";
26285
26619
 
26286
26620
  // src/engine/landing/lib/brand-tokens.ts
26287
- import { readFile as readFile20 } from "fs/promises";
26288
- import path23 from "path";
26621
+ import { readFile as readFile21 } from "fs/promises";
26622
+ import path24 from "path";
26289
26623
 
26290
26624
  // src/engine/landing/lib/color.ts
26291
26625
  var NEUTRAL_COLOR_KEYWORDS = /* @__PURE__ */ new Set([
@@ -26447,14 +26781,14 @@ function parseBrandMd(brandMd, fonts, colors) {
26447
26781
  }
26448
26782
  }
26449
26783
  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"));
26784
+ const globalCss = await safeRead(path24.join(projectRoot, "src", "styles", "global.css"));
26785
+ const brandMd = await safeRead(path24.join(projectRoot, "src", "brand", "BRAND.md"));
26452
26786
  if (!globalCss && !brandMd) return EMPTY;
26453
26787
  return parseBrandTokens(globalCss, brandMd);
26454
26788
  }
26455
26789
  async function safeRead(file) {
26456
26790
  try {
26457
- return await readFile20(file, "utf8");
26791
+ return await readFile21(file, "utf8");
26458
26792
  } catch {
26459
26793
  return "";
26460
26794
  }
@@ -27240,40 +27574,40 @@ function describeCounts(findings) {
27240
27574
  }
27241
27575
 
27242
27576
  // src/commands/landing/snapshot.ts
27243
- import { mkdir as mkdir7, rename as rename2, writeFile as writeFile10 } from "fs/promises";
27244
- import path24 from "path";
27577
+ import { mkdir as mkdir8, rename as rename2, writeFile as writeFile11 } from "fs/promises";
27578
+ import path25 from "path";
27245
27579
  var CRITIC_VERSION = "1";
27246
27580
  function critiqueCacheDir(projectRoot) {
27247
- return path24.join(projectRoot, ".cache", "landing-critique");
27581
+ return path25.join(projectRoot, ".cache", "landing-critique");
27248
27582
  }
27249
27583
  function snapshotPath(projectRoot, slug) {
27250
- return path24.join(critiqueCacheDir(projectRoot), `${slug}.json`);
27584
+ return path25.join(critiqueCacheDir(projectRoot), `${slug}.json`);
27251
27585
  }
27252
27586
  async function writeCritiqueSnapshot(projectRoot, snapshot) {
27253
- await mkdir7(critiqueCacheDir(projectRoot), { recursive: true });
27587
+ await mkdir8(critiqueCacheDir(projectRoot), { recursive: true });
27254
27588
  const dest = snapshotPath(projectRoot, snapshot.slug);
27255
27589
  const tmp = `${dest}.tmp`;
27256
- await writeFile10(tmp, `${JSON.stringify(snapshot, null, 2)}
27590
+ await writeFile11(tmp, `${JSON.stringify(snapshot, null, 2)}
27257
27591
  `, "utf8");
27258
27592
  await rename2(tmp, dest);
27259
27593
  }
27260
27594
 
27261
27595
  // 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";
27596
+ import { readdir as readdir7, readFile as readFile22, stat as stat5 } from "fs/promises";
27597
+ import path26 from "path";
27264
27598
  async function landingSourceRelPaths(landingDir) {
27265
27599
  const rel = [];
27266
- if (await isFile(path25.join(landingDir, "index.astro"))) rel.push("index.astro");
27267
- const componentsDir = path25.join(landingDir, "_components");
27600
+ if (await isFile(path26.join(landingDir, "index.astro"))) rel.push("index.astro");
27601
+ const componentsDir = path26.join(landingDir, "_components");
27268
27602
  for (const abs of await walkAstro(componentsDir)) {
27269
- rel.push(path25.relative(landingDir, abs).split(path25.sep).join("/"));
27603
+ rel.push(path26.relative(landingDir, abs).split(path26.sep).join("/"));
27270
27604
  }
27271
27605
  return rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
27272
27606
  }
27273
27607
  async function readLandingSources(landingDir) {
27274
27608
  const rel = await landingSourceRelPaths(landingDir);
27275
27609
  const out = [];
27276
- for (const r of rel) out.push({ path: r, text: await readFile21(path25.join(landingDir, r), "utf8") });
27610
+ for (const r of rel) out.push({ path: r, text: await readFile22(path26.join(landingDir, r), "utf8") });
27277
27611
  return out;
27278
27612
  }
27279
27613
  async function computeLandingSourceSha(landingDir) {
@@ -27282,7 +27616,7 @@ async function computeLandingSourceSha(landingDir) {
27282
27616
  for (const r of rel) {
27283
27617
  let bytes;
27284
27618
  try {
27285
- bytes = await readFile21(path25.join(landingDir, r));
27619
+ bytes = await readFile22(path26.join(landingDir, r));
27286
27620
  } catch {
27287
27621
  bytes = Buffer.alloc(0);
27288
27622
  }
@@ -27306,7 +27640,7 @@ async function walkAstro(dir) {
27306
27640
  }
27307
27641
  const out = [];
27308
27642
  for (const entry of entries) {
27309
- const abs = path25.join(dir, entry.name);
27643
+ const abs = path26.join(dir, entry.name);
27310
27644
  if (entry.isDirectory()) out.push(...await walkAstro(abs));
27311
27645
  else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
27312
27646
  }
@@ -27327,14 +27661,14 @@ registerSchema({
27327
27661
  }
27328
27662
  });
27329
27663
  var SLUG_RE = /^[a-z0-9][a-z0-9._-]*$/i;
27330
- function fail4(code, message, fix) {
27664
+ function fail5(code, message, fix) {
27331
27665
  process.stderr.write(
27332
27666
  `${JSON.stringify({ ok: false, error: { code, message, ...fix ? { fix } : {} } }, null, 2)}
27333
27667
  `
27334
27668
  );
27335
27669
  process.exit(2);
27336
27670
  }
27337
- var critiqueCommand2 = defineCommand133({
27671
+ var critiqueCommand2 = defineCommand135({
27338
27672
  meta: {
27339
27673
  name: "critique",
27340
27674
  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 +27681,15 @@ var critiqueCommand2 = defineCommand133({
27347
27681
  const slug = String(args.slug);
27348
27682
  const projectRoot = process.cwd();
27349
27683
  if (!SLUG_RE.test(slug) || slug.includes("..")) {
27350
- fail4(
27684
+ fail5(
27351
27685
  "INVALID_SLUG",
27352
27686
  `"${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
27687
  { availableSlugs: await listLandingSlugs(projectRoot) }
27354
27688
  );
27355
27689
  }
27356
- const landingDir = path26.resolve(projectRoot, "src", "pages", slug);
27690
+ const landingDir = path27.resolve(projectRoot, "src", "pages", slug);
27357
27691
  if (!await isDir(landingDir)) {
27358
- fail4("NOT_FOUND", `No landing at src/pages/${slug}/`, {
27692
+ fail5("NOT_FOUND", `No landing at src/pages/${slug}/`, {
27359
27693
  availableSlugs: await listLandingSlugs(projectRoot)
27360
27694
  });
27361
27695
  }
@@ -27408,7 +27742,7 @@ var critiqueCommand2 = defineCommand133({
27408
27742
  });
27409
27743
  async function listLandingSlugs(projectRoot) {
27410
27744
  try {
27411
- const entries = await readdir8(path26.join(projectRoot, "src", "pages"), { withFileTypes: true });
27745
+ const entries = await readdir8(path27.join(projectRoot, "src", "pages"), { withFileTypes: true });
27412
27746
  return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
27413
27747
  } catch {
27414
27748
  return [];
@@ -27434,7 +27768,7 @@ async function isDir(p) {
27434
27768
  }
27435
27769
 
27436
27770
  // src/commands/landing/index.ts
27437
- var landingCommand = defineCommand134({
27771
+ var landingCommand = defineCommand136({
27438
27772
  meta: {
27439
27773
  name: "landing",
27440
27774
  description: `Design-quality tools for landing pages (src/pages/<slug>/).
@@ -27450,7 +27784,7 @@ Subcommands:
27450
27784
  });
27451
27785
 
27452
27786
  // src/commands/mcp/index.ts
27453
- import { defineCommand as defineCommand135 } from "citty";
27787
+ import { defineCommand as defineCommand137 } from "citty";
27454
27788
  var SCOPES = ["user", "user_org", "company", "org"];
27455
27789
  function parseScope(raw) {
27456
27790
  const scope = raw === void 0 ? "company" : String(raw);
@@ -27476,7 +27810,7 @@ function parseHeaders(raw) {
27476
27810
  }
27477
27811
  return Object.keys(headers).length > 0 ? headers : void 0;
27478
27812
  }
27479
- function fail5(err) {
27813
+ function fail6(err) {
27480
27814
  if (err instanceof ApiError) {
27481
27815
  writeJson({ ok: false, error: { code: err.code, message: err.message } });
27482
27816
  process.exit(1);
@@ -27489,7 +27823,7 @@ registerSchema({
27489
27823
  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
27824
  args: {}
27491
27825
  });
27492
- var connectedCommand = defineCommand135({
27826
+ var connectedCommand = defineCommand137({
27493
27827
  meta: {
27494
27828
  name: "connected",
27495
27829
  description: `List all connected third-party tools \u2014 managed integrations plus custom MCP servers.
@@ -27527,7 +27861,7 @@ A tool the user names that is NOT listed here is simply not connected yet.`
27527
27861
  hints
27528
27862
  });
27529
27863
  } catch (err) {
27530
- fail5(err);
27864
+ fail6(err);
27531
27865
  }
27532
27866
  }
27533
27867
  });
@@ -27536,7 +27870,7 @@ registerSchema({
27536
27870
  description: "List the custom MCP servers this company's chats see (org + company + your own user scope).",
27537
27871
  args: {}
27538
27872
  });
27539
- var listCommand8 = defineCommand135({
27873
+ var listCommand8 = defineCommand137({
27540
27874
  meta: { name: "list", description: "List custom MCP servers visible to this company's chats." },
27541
27875
  run: async () => {
27542
27876
  try {
@@ -27547,7 +27881,7 @@ var listCommand8 = defineCommand135({
27547
27881
  );
27548
27882
  writeJson({ ok: true, data: data.servers });
27549
27883
  } catch (err) {
27550
- fail5(err);
27884
+ fail6(err);
27551
27885
  }
27552
27886
  }
27553
27887
  });
@@ -27561,7 +27895,7 @@ registerSchema({
27561
27895
  header: { type: "string", description: 'Auth header "Key: Value" (repeatable)', required: false }
27562
27896
  }
27563
27897
  });
27564
- var addCommand = defineCommand135({
27898
+ var addCommand = defineCommand137({
27565
27899
  meta: {
27566
27900
  name: "add",
27567
27901
  description: `Register a custom MCP server. Tools appear as mcp__<name>__* on the NEXT message.
@@ -27593,7 +27927,7 @@ Examples:
27593
27927
  });
27594
27928
  writeJson({ ok: true, data });
27595
27929
  } catch (err) {
27596
- fail5(err);
27930
+ fail6(err);
27597
27931
  }
27598
27932
  }
27599
27933
  });
@@ -27602,7 +27936,7 @@ registerSchema({
27602
27936
  description: "Remove a company custom MCP server by name.",
27603
27937
  args: { name: { type: "string", description: "Server name to remove", required: true } }
27604
27938
  });
27605
- var removeCommand4 = defineCommand135({
27939
+ var removeCommand4 = defineCommand137({
27606
27940
  meta: {
27607
27941
  name: "remove",
27608
27942
  description: `Remove a company custom MCP server by name.
@@ -27620,11 +27954,11 @@ Example:
27620
27954
  });
27621
27955
  writeJson({ ok: true, data });
27622
27956
  } catch (err) {
27623
- fail5(err);
27957
+ fail6(err);
27624
27958
  }
27625
27959
  }
27626
27960
  });
27627
- var mcpCommand = defineCommand135({
27961
+ var mcpCommand = defineCommand137({
27628
27962
  meta: {
27629
27963
  name: "mcp",
27630
27964
  description: `Third-party tools for this company \u2014 see what's connected, register custom HTTPS MCP endpoints.
@@ -27650,10 +27984,10 @@ Full guide: __tooling__/docs/tools/baker/mcp.md`
27650
27984
  });
27651
27985
 
27652
27986
  // src/commands/research/index.ts
27653
- import { defineCommand as defineCommand146 } from "citty";
27987
+ import { defineCommand as defineCommand148 } from "citty";
27654
27988
 
27655
27989
  // src/commands/research/advertisers.ts
27656
- import { defineCommand as defineCommand136 } from "citty";
27990
+ import { defineCommand as defineCommand138 } from "citty";
27657
27991
 
27658
27992
  // src/commands/research/output.ts
27659
27993
  var RESEARCH_DATA_NOTE = "Estimates based on third-party SERP data \u2014 not exact figures. Use for directional insights, not precise measurement.";
@@ -27766,7 +28100,7 @@ var FIELDS3 = {
27766
28100
  etv: "Estimated traffic value (USD)",
27767
28101
  visibility: "SERP visibility score (0-1)"
27768
28102
  };
27769
- var advertisersCommand = defineCommand136({
28103
+ var advertisersCommand = defineCommand138({
27770
28104
  meta: {
27771
28105
  name: "advertisers",
27772
28106
  description: `Find domains competing for a keyword in Google SERPs.
@@ -27813,7 +28147,7 @@ Examples:
27813
28147
  });
27814
28148
 
27815
28149
  // src/commands/research/autocomplete.ts
27816
- import { defineCommand as defineCommand137 } from "citty";
28150
+ import { defineCommand as defineCommand139 } from "citty";
27817
28151
  registerSchema({
27818
28152
  command: "research.autocomplete",
27819
28153
  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 +28170,7 @@ registerSchema({
27836
28170
  var FIELDS4 = {
27837
28171
  suggestion: "Autocomplete suggestion from Google"
27838
28172
  };
27839
- var autocompleteCommand = defineCommand137({
28173
+ var autocompleteCommand = defineCommand139({
27840
28174
  meta: {
27841
28175
  name: "autocomplete",
27842
28176
  description: `Get Google Autocomplete suggestions for keyword expansion.
@@ -27882,7 +28216,7 @@ Examples:
27882
28216
  });
27883
28217
 
27884
28218
  // src/commands/research/countries.ts
27885
- import { defineCommand as defineCommand138 } from "citty";
28219
+ import { defineCommand as defineCommand140 } from "citty";
27886
28220
  registerSchema({
27887
28221
  command: "research.countries",
27888
28222
  description: "List all supported country codes for --location flag in research commands.",
@@ -27939,7 +28273,7 @@ var FIELDS5 = {
27939
28273
  code: "Country code to pass as --location",
27940
28274
  name: "Country name"
27941
28275
  };
27942
- var countriesCommand = defineCommand138({
28276
+ var countriesCommand = defineCommand140({
27943
28277
  meta: {
27944
28278
  name: "countries",
27945
28279
  description: "List all supported country codes for --location flag."
@@ -27950,7 +28284,7 @@ var countriesCommand = defineCommand138({
27950
28284
  });
27951
28285
 
27952
28286
  // src/commands/research/intent.ts
27953
- import { defineCommand as defineCommand139 } from "citty";
28287
+ import { defineCommand as defineCommand141 } from "citty";
27954
28288
  registerSchema({
27955
28289
  command: "research.intent",
27956
28290
  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 +28307,7 @@ var FIELDS6 = {
27973
28307
  intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
27974
28308
  probability: "Confidence score 0.0-1.0"
27975
28309
  };
27976
- var intentCommand = defineCommand139({
28310
+ var intentCommand = defineCommand141({
27977
28311
  meta: {
27978
28312
  name: "intent",
27979
28313
  description: `Classify Google Search intent for keywords. Returns intent type and confidence.
@@ -28021,7 +28355,7 @@ Examples:
28021
28355
  });
28022
28356
 
28023
28357
  // src/commands/research/keyword-gap.ts
28024
- import { defineCommand as defineCommand140 } from "citty";
28358
+ import { defineCommand as defineCommand142 } from "citty";
28025
28359
  registerSchema({
28026
28360
  command: "research.keyword-gap",
28027
28361
  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 +28384,7 @@ var FIELDS7 = {
28050
28384
  cpc: "Cost per click USD",
28051
28385
  their_position: "Competitor's ranking position"
28052
28386
  };
28053
- var keywordGapCommand = defineCommand140({
28387
+ var keywordGapCommand = defineCommand142({
28054
28388
  meta: {
28055
28389
  name: "keyword-gap",
28056
28390
  description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
@@ -28124,7 +28458,7 @@ Examples:
28124
28458
  });
28125
28459
 
28126
28460
  // src/commands/research/keywords-for-site.ts
28127
- import { defineCommand as defineCommand141 } from "citty";
28461
+ import { defineCommand as defineCommand143 } from "citty";
28128
28462
  registerSchema({
28129
28463
  command: "research.keywords-for-site",
28130
28464
  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 +28491,7 @@ var FIELDS8 = {
28157
28491
  competition: "LOW, MEDIUM, or HIGH",
28158
28492
  competition_index: "Competition score 0-100"
28159
28493
  };
28160
- var keywordsForSiteCommand = defineCommand141({
28494
+ var keywordsForSiteCommand = defineCommand143({
28161
28495
  meta: {
28162
28496
  name: "keywords-for-site",
28163
28497
  description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
@@ -28210,7 +28544,7 @@ Examples:
28210
28544
  });
28211
28545
 
28212
28546
  // src/commands/research/languages.ts
28213
- import { defineCommand as defineCommand142 } from "citty";
28547
+ import { defineCommand as defineCommand144 } from "citty";
28214
28548
  registerSchema({
28215
28549
  command: "research.languages",
28216
28550
  description: "List all supported language codes for --language flag in research commands.",
@@ -28240,7 +28574,7 @@ var FIELDS9 = {
28240
28574
  code: "Language code to pass as --language",
28241
28575
  name: "Language name (also accepted by --language)"
28242
28576
  };
28243
- var languagesCommand2 = defineCommand142({
28577
+ var languagesCommand2 = defineCommand144({
28244
28578
  meta: {
28245
28579
  name: "languages",
28246
28580
  description: "List all supported language codes for --language flag."
@@ -28251,7 +28585,7 @@ var languagesCommand2 = defineCommand142({
28251
28585
  });
28252
28586
 
28253
28587
  // src/commands/research/lighthouse.ts
28254
- import { defineCommand as defineCommand143 } from "citty";
28588
+ import { defineCommand as defineCommand145 } from "citty";
28255
28589
  registerSchema({
28256
28590
  command: "research.lighthouse",
28257
28591
  description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
@@ -28270,7 +28604,7 @@ var FIELDS10 = {
28270
28604
  speed_index_ms: "Speed Index in ms (good: < 3400)",
28271
28605
  interactive_ms: "Time to Interactive in ms (good: < 3800)"
28272
28606
  };
28273
- var lighthouseCommand = defineCommand143({
28607
+ var lighthouseCommand = defineCommand145({
28274
28608
  meta: {
28275
28609
  name: "lighthouse",
28276
28610
  description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
@@ -28308,7 +28642,7 @@ Examples:
28308
28642
  });
28309
28643
 
28310
28644
  // src/commands/research/relevant-pages.ts
28311
- import { defineCommand as defineCommand144 } from "citty";
28645
+ import { defineCommand as defineCommand146 } from "citty";
28312
28646
  registerSchema({
28313
28647
  command: "research.relevant-pages",
28314
28648
  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 +28668,7 @@ var FIELDS11 = {
28334
28668
  keywords: "Total organic keywords the page ranks for",
28335
28669
  top_10: "Keywords in positions 1-10"
28336
28670
  };
28337
- var relevantPagesCommand = defineCommand144({
28671
+ var relevantPagesCommand = defineCommand146({
28338
28672
  meta: {
28339
28673
  name: "relevant-pages",
28340
28674
  description: `Get the top pages of a competitor domain with traffic data.
@@ -28380,7 +28714,7 @@ Examples:
28380
28714
  });
28381
28715
 
28382
28716
  // src/commands/research/web.ts
28383
- import { defineCommand as defineCommand145 } from "citty";
28717
+ import { defineCommand as defineCommand147 } from "citty";
28384
28718
  registerSchema({
28385
28719
  command: "research.web",
28386
28720
  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 +28765,7 @@ async function runDeepResearch(question) {
28431
28765
  }
28432
28766
  throw new Error("Deep research timed out");
28433
28767
  }
28434
- var webCommand = defineCommand145({
28768
+ var webCommand = defineCommand147({
28435
28769
  meta: {
28436
28770
  name: "web",
28437
28771
  description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
@@ -28491,7 +28825,7 @@ Examples:
28491
28825
  });
28492
28826
 
28493
28827
  // src/commands/research/index.ts
28494
- var researchCommand = defineCommand146({
28828
+ var researchCommand = defineCommand148({
28495
28829
  meta: {
28496
28830
  name: "research",
28497
28831
  description: `Competitive intelligence and AI-powered research commands.
@@ -28532,10 +28866,10 @@ Full guide: __tooling__/docs/tools/baker/research.md`
28532
28866
  });
28533
28867
 
28534
28868
  // src/commands/scheduled-actions/index.ts
28535
- import { defineCommand as defineCommand153 } from "citty";
28869
+ import { defineCommand as defineCommand155 } from "citty";
28536
28870
 
28537
28871
  // src/commands/scheduled-actions/create.ts
28538
- import { defineCommand as defineCommand147 } from "citty";
28872
+ import { defineCommand as defineCommand149 } from "citty";
28539
28873
 
28540
28874
  // src/commands/scheduled-actions/shared.ts
28541
28875
  var TEMP_SCHEDULED_ACTION_PREFIX = "temp_sched_";
@@ -28650,7 +28984,7 @@ registerSchema({
28650
28984
  prompt: { type: "string", description: "Additional prompt instructions for the spawned agent", required: false }
28651
28985
  }
28652
28986
  });
28653
- var createCommand2 = defineCommand147({
28987
+ var createCommand2 = defineCommand149({
28654
28988
  meta: {
28655
28989
  name: "create",
28656
28990
  description: 'Stage a scheduled action. Example: baker scheduled-actions create --name "Weekly report" --description "..." --cron "0 9 * * MON"'
@@ -28699,7 +29033,7 @@ var createCommand2 = defineCommand147({
28699
29033
  });
28700
29034
 
28701
29035
  // src/commands/scheduled-actions/delete.ts
28702
- import { defineCommand as defineCommand148 } from "citty";
29036
+ import { defineCommand as defineCommand150 } from "citty";
28703
29037
  registerSchema({
28704
29038
  command: "scheduled-actions.delete",
28705
29039
  description: "Stage deletion of a published scheduled action or cancellation of a temp_sched_* draft creation.",
@@ -28707,7 +29041,7 @@ registerSchema({
28707
29041
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
28708
29042
  }
28709
29043
  });
28710
- var deleteCommand2 = defineCommand148({
29044
+ var deleteCommand2 = defineCommand150({
28711
29045
  meta: {
28712
29046
  name: "delete",
28713
29047
  description: "Stage scheduled action deletion. Example: baker scheduled-actions delete <id-or-temp_sched_id>"
@@ -28736,7 +29070,7 @@ var deleteCommand2 = defineCommand148({
28736
29070
  });
28737
29071
 
28738
29072
  // src/commands/scheduled-actions/get.ts
28739
- import { defineCommand as defineCommand149 } from "citty";
29073
+ import { defineCommand as defineCommand151 } from "citty";
28740
29074
  registerSchema({
28741
29075
  command: "scheduled-actions.get",
28742
29076
  description: "Get a published scheduled action or a temp_sched_* draft-created scheduled action.",
@@ -28744,7 +29078,7 @@ registerSchema({
28744
29078
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
28745
29079
  }
28746
29080
  });
28747
- var getCommand3 = defineCommand149({
29081
+ var getCommand3 = defineCommand151({
28748
29082
  meta: {
28749
29083
  name: "get",
28750
29084
  description: "Get a scheduled action. Example: baker scheduled-actions get <id-or-temp_sched_id>"
@@ -28781,13 +29115,13 @@ var getCommand3 = defineCommand149({
28781
29115
  });
28782
29116
 
28783
29117
  // src/commands/scheduled-actions/list.ts
28784
- import { defineCommand as defineCommand150 } from "citty";
29118
+ import { defineCommand as defineCommand152 } from "citty";
28785
29119
  registerSchema({
28786
29120
  command: "scheduled-actions.list",
28787
29121
  description: "List published scheduled actions. Includes draft state when BAKER_CHAT_ID is set.",
28788
29122
  args: {}
28789
29123
  });
28790
- var listCommand9 = defineCommand150({
29124
+ var listCommand9 = defineCommand152({
28791
29125
  meta: {
28792
29126
  name: "list",
28793
29127
  description: "List scheduled actions. Includes staged draft ops when BAKER_CHAT_ID is set."
@@ -28808,7 +29142,7 @@ var listCommand9 = defineCommand150({
28808
29142
  });
28809
29143
 
28810
29144
  // src/commands/scheduled-actions/trigger.ts
28811
- import { defineCommand as defineCommand151 } from "citty";
29145
+ import { defineCommand as defineCommand153 } from "citty";
28812
29146
  registerSchema({
28813
29147
  command: "scheduled-actions.trigger",
28814
29148
  description: "Immediately trigger a published scheduled action. Does not require BAKER_CHAT_ID and rejects temp_sched_* IDs.",
@@ -28816,7 +29150,7 @@ registerSchema({
28816
29150
  id: { type: "string", description: "Published scheduled action ID", required: true }
28817
29151
  }
28818
29152
  });
28819
- var triggerCommand = defineCommand151({
29153
+ var triggerCommand = defineCommand153({
28820
29154
  meta: {
28821
29155
  name: "trigger",
28822
29156
  description: "Immediately trigger a published scheduled action. Example: baker scheduled-actions trigger <id>"
@@ -28853,7 +29187,7 @@ var triggerCommand = defineCommand151({
28853
29187
  });
28854
29188
 
28855
29189
  // src/commands/scheduled-actions/update.ts
28856
- import { defineCommand as defineCommand152 } from "citty";
29190
+ import { defineCommand as defineCommand154 } from "citty";
28857
29191
  registerSchema({
28858
29192
  command: "scheduled-actions.update",
28859
29193
  description: "Stage an update to a published scheduled action or temp_sched_* draft-created scheduled action.",
@@ -28878,7 +29212,7 @@ registerSchema({
28878
29212
  prompt: { type: "string", description: "Replacement additional spawned-agent instructions", required: false }
28879
29213
  }
28880
29214
  });
28881
- var updateCommand2 = defineCommand152({
29215
+ var updateCommand2 = defineCommand154({
28882
29216
  meta: {
28883
29217
  name: "update",
28884
29218
  description: "Stage a scheduled action update. Example: baker scheduled-actions update <id> --enabled false"
@@ -28949,7 +29283,7 @@ var updateCommand2 = defineCommand152({
28949
29283
  });
28950
29284
 
28951
29285
  // src/commands/scheduled-actions/index.ts
28952
- var scheduledActionsCommand = defineCommand153({
29286
+ var scheduledActionsCommand = defineCommand155({
28953
29287
  meta: {
28954
29288
  name: "scheduled-actions",
28955
29289
  description: `Manage Scheduled Actions. Subcommands: list, get, create, update, delete, trigger.
@@ -28976,8 +29310,8 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
28976
29310
  });
28977
29311
 
28978
29312
  // src/commands/schema.ts
28979
- import { defineCommand as defineCommand154 } from "citty";
28980
- var schemaCommand = defineCommand154({
29313
+ import { defineCommand as defineCommand156 } from "citty";
29314
+ var schemaCommand = defineCommand156({
28981
29315
  meta: {
28982
29316
  name: "schema",
28983
29317
  description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images.search"
@@ -29019,7 +29353,7 @@ var schemaCommand = defineCommand154({
29019
29353
  });
29020
29354
 
29021
29355
  // src/commands/tags/index.ts
29022
- import { defineCommand as defineCommand155 } from "citty";
29356
+ import { defineCommand as defineCommand157 } from "citty";
29023
29357
 
29024
29358
  // src/commands/tags/shared.ts
29025
29359
  function failApi3(err) {
@@ -29085,7 +29419,7 @@ async function listTags(json) {
29085
29419
  failApi3(err);
29086
29420
  }
29087
29421
  }
29088
- var listCommand10 = defineCommand155({
29422
+ var listCommand10 = defineCommand157({
29089
29423
  meta: {
29090
29424
  name: "list",
29091
29425
  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 +29438,7 @@ async function listDraft3() {
29104
29438
  failApi3(err);
29105
29439
  }
29106
29440
  }
29107
- var draftCommand3 = defineCommand155({
29441
+ var draftCommand3 = defineCommand157({
29108
29442
  meta: {
29109
29443
  name: "draft",
29110
29444
  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 +29447,7 @@ var draftCommand3 = defineCommand155({
29113
29447
  await listDraft3();
29114
29448
  }
29115
29449
  });
29116
- var tagsCommand3 = defineCommand155({
29450
+ var tagsCommand3 = defineCommand157({
29117
29451
  meta: {
29118
29452
  name: "tags",
29119
29453
  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 +29473,10 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
29139
29473
  });
29140
29474
 
29141
29475
  // src/commands/testimonials/index.ts
29142
- import { defineCommand as defineCommand159 } from "citty";
29476
+ import { defineCommand as defineCommand161 } from "citty";
29143
29477
 
29144
29478
  // src/commands/testimonials/get.ts
29145
- import { defineCommand as defineCommand156 } from "citty";
29479
+ import { defineCommand as defineCommand158 } from "citty";
29146
29480
  registerSchema({
29147
29481
  command: "testimonials.get",
29148
29482
  description: "Get a single testimonial by ID",
@@ -29150,7 +29484,7 @@ registerSchema({
29150
29484
  id: { type: "string", description: "Testimonial ID", required: true }
29151
29485
  }
29152
29486
  });
29153
- var getCommand4 = defineCommand156({
29487
+ var getCommand4 = defineCommand158({
29154
29488
  meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
29155
29489
  args: {
29156
29490
  id: { type: "positional", description: "Testimonial ID", required: false },
@@ -29187,7 +29521,7 @@ var getCommand4 = defineCommand156({
29187
29521
  });
29188
29522
 
29189
29523
  // src/commands/testimonials/list.ts
29190
- import { defineCommand as defineCommand157 } from "citty";
29524
+ import { defineCommand as defineCommand159 } from "citty";
29191
29525
  registerSchema({
29192
29526
  command: "testimonials.list",
29193
29527
  description: "List testimonials with optional filters.",
@@ -29217,7 +29551,7 @@ registerSchema({
29217
29551
  limit: { type: "number", description: "Max results (default 50)", required: false, default: 50 }
29218
29552
  }
29219
29553
  });
29220
- var listCommand11 = defineCommand157({
29554
+ var listCommand11 = defineCommand159({
29221
29555
  meta: {
29222
29556
  name: "list",
29223
29557
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
@@ -29266,7 +29600,7 @@ var listCommand11 = defineCommand157({
29266
29600
  });
29267
29601
 
29268
29602
  // src/commands/testimonials/search.ts
29269
- import { defineCommand as defineCommand158 } from "citty";
29603
+ import { defineCommand as defineCommand160 } from "citty";
29270
29604
  function languageBiasHint(results, requestedLanguage) {
29271
29605
  if (requestedLanguage) {
29272
29606
  return null;
@@ -29344,7 +29678,7 @@ function buildSearchRequest(query, args) {
29344
29678
  }
29345
29679
  return body;
29346
29680
  }
29347
- var searchCommand2 = defineCommand158({
29681
+ var searchCommand2 = defineCommand160({
29348
29682
  meta: {
29349
29683
  name: "search",
29350
29684
  description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
@@ -29400,7 +29734,7 @@ var searchCommand2 = defineCommand158({
29400
29734
  var tagsCommand4 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
29401
29735
 
29402
29736
  // src/commands/testimonials/index.ts
29403
- var testimonialsCommand = defineCommand159({
29737
+ var testimonialsCommand = defineCommand161({
29404
29738
  meta: {
29405
29739
  name: "testimonials",
29406
29740
  description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
@@ -29422,10 +29756,10 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
29422
29756
  });
29423
29757
 
29424
29758
  // src/commands/videos/index.ts
29425
- import { defineCommand as defineCommand164 } from "citty";
29759
+ import { defineCommand as defineCommand166 } from "citty";
29426
29760
 
29427
29761
  // src/commands/videos/delete.ts
29428
- import { defineCommand as defineCommand160 } from "citty";
29762
+ import { defineCommand as defineCommand162 } from "citty";
29429
29763
  registerSchema({
29430
29764
  command: "videos.delete",
29431
29765
  description: "Delete a video by ID",
@@ -29439,7 +29773,7 @@ registerSchema({
29439
29773
  }
29440
29774
  }
29441
29775
  });
29442
- var deleteCommand3 = defineCommand160({
29776
+ var deleteCommand3 = defineCommand162({
29443
29777
  meta: {
29444
29778
  name: "delete",
29445
29779
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -29480,7 +29814,7 @@ var deleteCommand3 = defineCommand160({
29480
29814
  });
29481
29815
 
29482
29816
  // src/commands/videos/get.ts
29483
- import { defineCommand as defineCommand161 } from "citty";
29817
+ import { defineCommand as defineCommand163 } from "citty";
29484
29818
  registerSchema({
29485
29819
  command: "videos.get",
29486
29820
  description: "Get a single video by ID",
@@ -29488,7 +29822,7 @@ registerSchema({
29488
29822
  id: { type: "string", description: "Video ID", required: true }
29489
29823
  }
29490
29824
  });
29491
- var getCommand5 = defineCommand161({
29825
+ var getCommand5 = defineCommand163({
29492
29826
  meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
29493
29827
  args: {
29494
29828
  id: { type: "positional", description: "Video ID", required: false },
@@ -29525,7 +29859,7 @@ var getCommand5 = defineCommand161({
29525
29859
  });
29526
29860
 
29527
29861
  // src/commands/videos/search.ts
29528
- import { defineCommand as defineCommand162 } from "citty";
29862
+ import { defineCommand as defineCommand164 } from "citty";
29529
29863
  registerSchema({
29530
29864
  command: "videos.search",
29531
29865
  description: "Search videos by text query. Only returns ready videos.",
@@ -29535,7 +29869,7 @@ registerSchema({
29535
29869
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
29536
29870
  }
29537
29871
  });
29538
- var searchCommand3 = defineCommand162({
29872
+ var searchCommand3 = defineCommand164({
29539
29873
  meta: {
29540
29874
  name: "search",
29541
29875
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -29585,9 +29919,9 @@ var searchCommand3 = defineCommand162({
29585
29919
  var tagsCommand5 = makeTagsCommand("videos", "video", "/api/videos/tags");
29586
29920
 
29587
29921
  // src/commands/videos/upload.ts
29588
- import { readFile as readFile22, stat as stat7 } from "fs/promises";
29922
+ import { readFile as readFile23, stat as stat7 } from "fs/promises";
29589
29923
  import { extname as extname3 } from "path";
29590
- import { defineCommand as defineCommand163 } from "citty";
29924
+ import { defineCommand as defineCommand165 } from "citty";
29591
29925
  var MIME_MAP = {
29592
29926
  ".mp4": "video/mp4",
29593
29927
  ".mov": "video/quicktime",
@@ -29621,7 +29955,7 @@ function detectContentType(filePath) {
29621
29955
  }
29622
29956
  return mime;
29623
29957
  }
29624
- var uploadCommand2 = defineCommand163({
29958
+ var uploadCommand2 = defineCommand165({
29625
29959
  meta: {
29626
29960
  name: "upload",
29627
29961
  description: "Upload a video file to Baker via Mux direct upload. Auto-detects content type. Example: baker videos upload ./demo.mp4"
@@ -29650,7 +29984,7 @@ var uploadCommand2 = defineCommand163({
29650
29984
  return;
29651
29985
  }
29652
29986
  const { uploadUrl, videoId } = await apiPost("/api/videos/upload", {});
29653
- const fileBuffer = await readFile22(filePath);
29987
+ const fileBuffer = await readFile23(filePath);
29654
29988
  const uploadResponse = await fetch(uploadUrl, {
29655
29989
  method: "PUT",
29656
29990
  headers: { "Content-Type": contentType },
@@ -29675,7 +30009,7 @@ var uploadCommand2 = defineCommand163({
29675
30009
  });
29676
30010
 
29677
30011
  // src/commands/videos/index.ts
29678
- var videosCommand = defineCommand164({
30012
+ var videosCommand = defineCommand166({
29679
30013
  meta: {
29680
30014
  name: "videos",
29681
30015
  description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete, tags.
@@ -29699,10 +30033,10 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
29699
30033
  });
29700
30034
 
29701
30035
  // src/commands/winning-ads/index.ts
29702
- import { defineCommand as defineCommand177 } from "citty";
30036
+ import { defineCommand as defineCommand179 } from "citty";
29703
30037
 
29704
30038
  // src/commands/winning-ads/advertisers.ts
29705
- import { defineCommand as defineCommand165 } from "citty";
30039
+ import { defineCommand as defineCommand167 } from "citty";
29706
30040
 
29707
30041
  // src/commands/winning-ads/shared.ts
29708
30042
  function splitList(value) {
@@ -29755,7 +30089,7 @@ function advertiserNormalizer(record, full) {
29755
30089
  last_synced_at: record.last_synced_at ?? null
29756
30090
  };
29757
30091
  }
29758
- var advertisersCommand2 = defineCommand165({
30092
+ var advertisersCommand2 = defineCommand167({
29759
30093
  meta: {
29760
30094
  name: "advertisers",
29761
30095
  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 +30147,7 @@ var advertisersCommand2 = defineCommand165({
29813
30147
  });
29814
30148
 
29815
30149
  // src/commands/winning-ads/brief.ts
29816
- import { defineCommand as defineCommand166 } from "citty";
30150
+ import { defineCommand as defineCommand168 } from "citty";
29817
30151
  registerSchema({
29818
30152
  command: "winning-ads.brief",
29819
30153
  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 +30193,7 @@ function parseDna(raw) {
29859
30193
  }
29860
30194
  return parsed;
29861
30195
  }
29862
- var briefCommand = defineCommand166({
30196
+ var briefCommand = defineCommand168({
29863
30197
  meta: {
29864
30198
  name: "brief",
29865
30199
  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 +30229,7 @@ var briefCommand = defineCommand166({
29895
30229
  });
29896
30230
 
29897
30231
  // src/commands/winning-ads/content.ts
29898
- import { defineCommand as defineCommand167 } from "citty";
30232
+ import { defineCommand as defineCommand169 } from "citty";
29899
30233
  registerSchema({
29900
30234
  command: "winning-ads.content",
29901
30235
  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 +30242,7 @@ registerSchema({
29908
30242
  }
29909
30243
  }
29910
30244
  });
29911
- var contentCommand = defineCommand167({
30245
+ var contentCommand = defineCommand169({
29912
30246
  meta: {
29913
30247
  name: "content",
29914
30248
  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 +30291,7 @@ var contentCommand = defineCommand167({
29957
30291
  });
29958
30292
 
29959
30293
  // src/commands/winning-ads/feed.ts
29960
- import { defineCommand as defineCommand168 } from "citty";
30294
+ import { defineCommand as defineCommand170 } from "citty";
29961
30295
  function buildFeedParams(input) {
29962
30296
  const params = {};
29963
30297
  const advertiser = splitList(input.advertiser);
@@ -30009,7 +30343,7 @@ registerSchema({
30009
30343
  format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
30010
30344
  }
30011
30345
  });
30012
- var feedCommand = defineCommand168({
30346
+ var feedCommand = defineCommand170({
30013
30347
  meta: {
30014
30348
  name: "feed",
30015
30349
  description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
@@ -30094,7 +30428,7 @@ var feedCommand = defineCommand168({
30094
30428
  });
30095
30429
 
30096
30430
  // src/commands/winning-ads/follow.ts
30097
- import { defineCommand as defineCommand169 } from "citty";
30431
+ import { defineCommand as defineCommand171 } from "citty";
30098
30432
  var PLATFORMS = ["meta", "linkedin"];
30099
30433
  registerSchema({
30100
30434
  command: "winning-ads.follow",
@@ -30109,7 +30443,7 @@ registerSchema({
30109
30443
  label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
30110
30444
  }
30111
30445
  });
30112
- var followCommand = defineCommand169({
30446
+ var followCommand = defineCommand171({
30113
30447
  meta: {
30114
30448
  name: "follow",
30115
30449
  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 +30490,7 @@ var followCommand = defineCommand169({
30156
30490
  });
30157
30491
 
30158
30492
  // src/commands/winning-ads/follow-competitors.ts
30159
- import { defineCommand as defineCommand170 } from "citty";
30493
+ import { defineCommand as defineCommand172 } from "citty";
30160
30494
  var PLATFORMS2 = ["meta", "linkedin"];
30161
30495
  var BATCH_TIMEOUT_MS = 3e5;
30162
30496
  function buildFollowBatchBody(input) {
@@ -30189,7 +30523,7 @@ registerSchema({
30189
30523
  }
30190
30524
  }
30191
30525
  });
30192
- var followCompetitorsCommand = defineCommand170({
30526
+ var followCompetitorsCommand = defineCommand172({
30193
30527
  meta: {
30194
30528
  name: "follow-competitors",
30195
30529
  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 +30598,7 @@ var followCompetitorsCommand = defineCommand170({
30264
30598
  });
30265
30599
 
30266
30600
  // src/commands/winning-ads/following.ts
30267
- import { defineCommand as defineCommand171 } from "citty";
30601
+ import { defineCommand as defineCommand173 } from "citty";
30268
30602
  registerSchema({
30269
30603
  command: "winning-ads.following",
30270
30604
  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 +30631,7 @@ function followingNormalizer(record, full) {
30297
30631
  platforms: Array.isArray(record.platforms) ? record.platforms : []
30298
30632
  };
30299
30633
  }
30300
- var followingCommand = defineCommand171({
30634
+ var followingCommand = defineCommand173({
30301
30635
  meta: {
30302
30636
  name: "following",
30303
30637
  description: "List brands you follow, with status (ready / adding\u2026) and cached counts. Example: baker winning-ads following --output md"
@@ -30332,7 +30666,7 @@ var followingCommand = defineCommand171({
30332
30666
  });
30333
30667
 
30334
30668
  // src/commands/winning-ads/patterns.ts
30335
- import { defineCommand as defineCommand172 } from "citty";
30669
+ import { defineCommand as defineCommand174 } from "citty";
30336
30670
  registerSchema({
30337
30671
  command: "winning-ads.patterns",
30338
30672
  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 +30705,7 @@ function discriminatorRow(record) {
30371
30705
  top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
30372
30706
  };
30373
30707
  }
30374
- var patternsCommand = defineCommand172({
30708
+ var patternsCommand = defineCommand174({
30375
30709
  meta: {
30376
30710
  name: "patterns",
30377
30711
  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 +30761,7 @@ var patternsCommand = defineCommand172({
30427
30761
  });
30428
30762
 
30429
30763
  // src/commands/winning-ads/search.ts
30430
- import { defineCommand as defineCommand173 } from "citty";
30764
+ import { defineCommand as defineCommand175 } from "citty";
30431
30765
  registerSchema({
30432
30766
  command: "winning-ads.search",
30433
30767
  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 +30869,7 @@ function buildSearchBody(args) {
30535
30869
  }
30536
30870
  return body;
30537
30871
  }
30538
- var searchCommand4 = defineCommand173({
30872
+ var searchCommand4 = defineCommand175({
30539
30873
  meta: {
30540
30874
  name: "search",
30541
30875
  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 +30984,7 @@ var searchCommand4 = defineCommand173({
30650
30984
  });
30651
30985
 
30652
30986
  // src/commands/winning-ads/seeds.ts
30653
- import { defineCommand as defineCommand174 } from "citty";
30987
+ import { defineCommand as defineCommand176 } from "citty";
30654
30988
  function leanRow(r) {
30655
30989
  return {
30656
30990
  key: r.key,
@@ -30678,7 +31012,7 @@ function makeSeedCommand(opts) {
30678
31012
  limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
30679
31013
  }
30680
31014
  });
30681
- return defineCommand174({
31015
+ return defineCommand176({
30682
31016
  meta: { name: opts.name, description: opts.description },
30683
31017
  args: {
30684
31018
  platform: { type: "string", description: "Single platform to segment on", required: false },
@@ -30727,7 +31061,7 @@ var formatsCommand = makeSeedCommand({
30727
31061
  });
30728
31062
 
30729
31063
  // src/commands/winning-ads/unfollow.ts
30730
- import { defineCommand as defineCommand175 } from "citty";
31064
+ import { defineCommand as defineCommand177 } from "citty";
30731
31065
  registerSchema({
30732
31066
  command: "winning-ads.unfollow",
30733
31067
  description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
@@ -30735,7 +31069,7 @@ registerSchema({
30735
31069
  advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
30736
31070
  }
30737
31071
  });
30738
- var unfollowCommand = defineCommand175({
31072
+ var unfollowCommand = defineCommand177({
30739
31073
  meta: {
30740
31074
  name: "unfollow",
30741
31075
  description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
@@ -30756,7 +31090,7 @@ var unfollowCommand = defineCommand175({
30756
31090
  });
30757
31091
 
30758
31092
  // src/commands/winning-ads/winners.ts
30759
- import { defineCommand as defineCommand176 } from "citty";
31093
+ import { defineCommand as defineCommand178 } from "citty";
30760
31094
  registerSchema({
30761
31095
  command: "winning-ads.winners",
30762
31096
  description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
@@ -30766,7 +31100,7 @@ registerSchema({
30766
31100
  platform: { type: "string", description: "Filter to a single platform: meta|linkedin", required: false }
30767
31101
  }
30768
31102
  });
30769
- var winnersCommand = defineCommand176({
31103
+ var winnersCommand = defineCommand178({
30770
31104
  meta: {
30771
31105
  name: "winners",
30772
31106
  description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
@@ -30816,7 +31150,7 @@ var winnersCommand = defineCommand176({
30816
31150
  });
30817
31151
 
30818
31152
  // src/commands/winning-ads/index.ts
30819
- var winningAdsCommand = defineCommand177({
31153
+ var winningAdsCommand = defineCommand179({
30820
31154
  meta: {
30821
31155
  name: "winning-ads",
30822
31156
  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 +31222,7 @@ function getCliVersion() {
30888
31222
  }
30889
31223
 
30890
31224
  // src/cli.ts
30891
- var main = defineCommand178({
31225
+ var main = defineCommand180({
30892
31226
  meta: {
30893
31227
  name: "baker",
30894
31228
  version: getCliVersion(),
@@ -30904,6 +31238,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
30904
31238
  actions: actionsCommand,
30905
31239
  "scheduled-actions": scheduledActionsCommand,
30906
31240
  ads: adsCommand2,
31241
+ brand: brandCommand,
30907
31242
  ga4: ga4Command,
30908
31243
  gsc: gscCommand,
30909
31244
  research: researchCommand,