@koda-sl/baker-cli 0.129.1-dev.972f3c9aa → 0.131.0-dev.cad5e0bc7

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
@@ -26,10 +26,12 @@ import {
26
26
  requireCredentialsFromEnv,
27
27
  resolveConcurrency,
28
28
  sha256Hex,
29
+ spineInputFlags,
30
+ spineInputOps,
29
31
  toModelSafeImage,
30
32
  ulid,
31
33
  validateCanvasDeep
32
- } from "./chunk-J2LYFDVC.js";
34
+ } from "./chunk-5LCTMZJ5.js";
33
35
  import {
34
36
  csvOrJson,
35
37
  daysAgoIso,
@@ -65,7 +67,7 @@ import {
65
67
  import "./chunk-5WRI5ZAA.js";
66
68
 
67
69
  // src/cli.ts
68
- import { defineCommand as defineCommand165, runMain } from "citty";
70
+ import { defineCommand as defineCommand166, runMain } from "citty";
69
71
 
70
72
  // src/commands/actions/index.ts
71
73
  import { defineCommand as defineCommand18 } from "citty";
@@ -4924,11 +4926,11 @@ function rawTextEntries(value) {
4924
4926
  const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
4925
4927
  return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
4926
4928
  }
4927
- function rawFileEntries(path18) {
4928
- if (typeof path18 !== "string" || path18.length === 0) {
4929
+ function rawFileEntries(path21) {
4930
+ if (typeof path21 !== "string" || path21.length === 0) {
4929
4931
  return [];
4930
4932
  }
4931
- return readFileSync2(path18, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
4933
+ return readFileSync2(path21, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
4932
4934
  }
4933
4935
  function keywordEntries(args) {
4934
4936
  const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
@@ -4951,19 +4953,19 @@ function keywordEntries(args) {
4951
4953
  }
4952
4954
  return entries;
4953
4955
  }
4954
- function loadJsonFileArg(path18) {
4955
- if (typeof path18 !== "string" || path18.length === 0) {
4956
+ function loadJsonFileArg(path21) {
4957
+ if (typeof path21 !== "string" || path21.length === 0) {
4956
4958
  return {};
4957
4959
  }
4958
4960
  try {
4959
- const parsed = JSON.parse(readFileSync2(path18, "utf8"));
4961
+ const parsed = JSON.parse(readFileSync2(path21, "utf8"));
4960
4962
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
4961
- failWriteValidation(`${path18} must contain a JSON object`);
4963
+ failWriteValidation(`${path21} must contain a JSON object`);
4962
4964
  }
4963
4965
  return parsed;
4964
4966
  } catch (err) {
4965
4967
  if (err instanceof SyntaxError) {
4966
- failWriteValidation(`${path18} is not valid JSON: ${err.message}`);
4968
+ failWriteValidation(`${path21} is not valid JSON: ${err.message}`);
4967
4969
  }
4968
4970
  throw err;
4969
4971
  }
@@ -5074,10 +5076,10 @@ async function stageUpdate(kind, customerId, target, payload) {
5074
5076
  async function stageTarget(kind, customerId, target) {
5075
5077
  await stageGoogleOp({ kind, customerId, target });
5076
5078
  }
5077
- async function draftAction(path18, body) {
5079
+ async function draftAction(path21, body) {
5078
5080
  try {
5079
5081
  const chatId = requireChatId();
5080
- const response = await apiPost(path18, { chatId, ...body });
5082
+ const response = await apiPost(path21, { chatId, ...body });
5081
5083
  writeJsonEnvelope(response);
5082
5084
  } catch (err) {
5083
5085
  handleGoogleError(err);
@@ -8832,19 +8834,19 @@ function failWriteValidation2(message) {
8832
8834
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
8833
8835
  process.exit(1);
8834
8836
  }
8835
- function loadJsonFileArg2(path18) {
8836
- if (typeof path18 !== "string" || path18.length === 0) {
8837
+ function loadJsonFileArg2(path21) {
8838
+ if (typeof path21 !== "string" || path21.length === 0) {
8837
8839
  return {};
8838
8840
  }
8839
8841
  try {
8840
- const parsed = JSON.parse(readFileSync6(path18, "utf8"));
8842
+ const parsed = JSON.parse(readFileSync6(path21, "utf8"));
8841
8843
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
8842
- failWriteValidation2(`${path18} must contain a JSON object`);
8844
+ failWriteValidation2(`${path21} must contain a JSON object`);
8843
8845
  }
8844
8846
  return parsed;
8845
8847
  } catch (err) {
8846
8848
  if (err instanceof SyntaxError) {
8847
- failWriteValidation2(`${path18} is not valid JSON: ${err.message}`);
8849
+ failWriteValidation2(`${path21} is not valid JSON: ${err.message}`);
8848
8850
  }
8849
8851
  throw err;
8850
8852
  }
@@ -8929,15 +8931,15 @@ function parseLocaleFlag(value) {
8929
8931
  }
8930
8932
  return { language: match[1], country: match[2].toUpperCase() };
8931
8933
  }
8932
- function loadTargetingFileArg(path18) {
8933
- if (typeof path18 !== "string" || path18.length === 0) {
8934
+ function loadTargetingFileArg(path21) {
8935
+ if (typeof path21 !== "string" || path21.length === 0) {
8934
8936
  return void 0;
8935
8937
  }
8936
- const parsed = loadJsonFileArg2(path18);
8938
+ const parsed = loadJsonFileArg2(path21);
8937
8939
  const criteria = parsed.targetingCriteria ?? parsed;
8938
8940
  if (!criteria.include) {
8939
8941
  failWriteValidation2(
8940
- `${path18} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
8942
+ `${path21} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
8941
8943
  );
8942
8944
  }
8943
8945
  return criteria;
@@ -8972,14 +8974,14 @@ function parseCsvLine(line) {
8972
8974
  cells.push(current);
8973
8975
  return cells.map((cell) => cell.trim());
8974
8976
  }
8975
- function parseListFileArg(path18, maxRows) {
8976
- if (typeof path18 !== "string" || path18.length === 0) {
8977
+ function parseListFileArg(path21, maxRows) {
8978
+ if (typeof path21 !== "string" || path21.length === 0) {
8977
8979
  return void 0;
8978
8980
  }
8979
- const raw = readFileSync6(path18, "utf8");
8981
+ const raw = readFileSync6(path21, "utf8");
8980
8982
  const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
8981
8983
  if (lines.length < 2) {
8982
- failWriteValidation2(`${path18} needs a header row and at least one data row`);
8984
+ failWriteValidation2(`${path21} needs a header row and at least one data row`);
8983
8985
  }
8984
8986
  const columns = parseCsvLine(lines[0]).map((column) => column.trim());
8985
8987
  const rows = [];
@@ -8998,7 +9000,7 @@ function parseListFileArg(path18, maxRows) {
8998
9000
  }
8999
9001
  }
9000
9002
  if (rows.length > maxRows) {
9001
- failWriteValidation2(`${path18} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
9003
+ failWriteValidation2(`${path21} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
9002
9004
  }
9003
9005
  return { columns, rows };
9004
9006
  }
@@ -11081,11 +11083,11 @@ var updateStatusSchema = z11.enum(UPDATE_STATUSES);
11081
11083
  function currencyMinimums2(currencyCode) {
11082
11084
  return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
11083
11085
  }
11084
- function validateDailyBudgetFloor(money, ctx, path18) {
11086
+ function validateDailyBudgetFloor(money, ctx, path21) {
11085
11087
  if (money?.currencyCode) {
11086
11088
  const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
11087
11089
  if (Number(money.amount) < min) {
11088
- ctx.addIssue({ code: "custom", path: path18, message: `below the ${min} ${money.currencyCode} daily minimum` });
11090
+ ctx.addIssue({ code: "custom", path: path21, message: `below the ${min} ${money.currencyCode} daily minimum` });
11089
11091
  }
11090
11092
  }
11091
11093
  }
@@ -11573,19 +11575,19 @@ function failWriteValidation3(message) {
11573
11575
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
11574
11576
  process.exit(1);
11575
11577
  }
11576
- function loadJsonFileArg3(path18) {
11577
- if (typeof path18 !== "string" || path18.length === 0) {
11578
+ function loadJsonFileArg3(path21) {
11579
+ if (typeof path21 !== "string" || path21.length === 0) {
11578
11580
  return {};
11579
11581
  }
11580
11582
  try {
11581
- const parsed = JSON.parse(readFileSync8(path18, "utf8"));
11583
+ const parsed = JSON.parse(readFileSync8(path21, "utf8"));
11582
11584
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
11583
- failWriteValidation3(`${path18} must contain a JSON object`);
11585
+ failWriteValidation3(`${path21} must contain a JSON object`);
11584
11586
  }
11585
11587
  return parsed;
11586
11588
  } catch (err) {
11587
11589
  if (err instanceof SyntaxError) {
11588
- failWriteValidation3(`${path18} is not valid JSON: ${err.message}`);
11590
+ failWriteValidation3(`${path21} is not valid JSON: ${err.message}`);
11589
11591
  }
11590
11592
  throw err;
11591
11593
  }
@@ -14517,7 +14519,7 @@ Examples:
14517
14519
  });
14518
14520
 
14519
14521
  // src/commands/canvas/index.ts
14520
- import { defineCommand as defineCommand95 } from "citty";
14522
+ import { defineCommand as defineCommand96 } from "citty";
14521
14523
 
14522
14524
  // src/commands/canvas/catalog.ts
14523
14525
  import { defineCommand as defineCommand86 } from "citty";
@@ -14807,12 +14809,12 @@ async function probeDuration(filePath) {
14807
14809
  }
14808
14810
 
14809
14811
  // src/commands/canvas/rerun.ts
14810
- import path9 from "path";
14812
+ import path11 from "path";
14811
14813
  import { defineCommand as defineCommand90 } from "citty";
14812
14814
 
14813
14815
  // src/commands/canvas/run.ts
14814
- import { readFile as readFile6 } from "fs/promises";
14815
- import path8 from "path";
14816
+ import { readFile as readFile7 } from "fs/promises";
14817
+ import path10 from "path";
14816
14818
  import { defineCommand as defineCommand89 } from "citty";
14817
14819
 
14818
14820
  // src/commands/canvas/placeholders.ts
@@ -14856,8 +14858,35 @@ function isResolvableRelative(value) {
14856
14858
  return typeof value === "string" && value.length > 0 && !value.includes("[TODO") && !path3.isAbsolute(value);
14857
14859
  }
14858
14860
 
14861
+ // src/commands/canvas/source-version.ts
14862
+ import { readFile as readFile3 } from "fs/promises";
14863
+ import path4 from "path";
14864
+ async function computeSourceSha(canvasPath) {
14865
+ let canvasBytes;
14866
+ try {
14867
+ canvasBytes = await readFile3(canvasPath);
14868
+ } catch {
14869
+ return void 0;
14870
+ }
14871
+ const promptPath = path4.join(path4.dirname(canvasPath), "prompt.json");
14872
+ let promptBytes;
14873
+ try {
14874
+ promptBytes = await readFile3(promptPath);
14875
+ } catch {
14876
+ promptBytes = Buffer.alloc(0);
14877
+ }
14878
+ return sha256Hex(
14879
+ Buffer.concat([
14880
+ Buffer.from(`${canvasBytes.length}\0`),
14881
+ canvasBytes,
14882
+ Buffer.from(`${promptBytes.length}\0`),
14883
+ promptBytes
14884
+ ])
14885
+ );
14886
+ }
14887
+
14859
14888
  // src/commands/canvas/style-projection.ts
14860
- import { readFile as readFile3, writeFile } from "fs/promises";
14889
+ import { readFile as readFile4, writeFile } from "fs/promises";
14861
14890
 
14862
14891
  // src/engine/scaffold/video.ts
14863
14892
  import { toCardinal as nwAr } from "n2words/ar-SA";
@@ -15184,32 +15213,7 @@ function pipOverlayArgs(dims, insets) {
15184
15213
  return [...inputs, "-filter_complex", filt.join(";"), "-map", "[v]", "{{out.video}}"];
15185
15214
  }
15186
15215
  var FLASH_HOLD_MAX_S = 2;
15187
- function stillHoldArgs(durationS, dims) {
15188
- return [
15189
- "-loop",
15190
- "1",
15191
- "-i",
15192
- "{{in.frame}}",
15193
- "-t",
15194
- durationS.toFixed(3),
15195
- "-r",
15196
- "30",
15197
- "-vf",
15198
- `scale=${dims.w}:${dims.h}:force_original_aspect_ratio=increase,crop=${dims.w}:${dims.h},setsar=1,format=yuv420p`,
15199
- "-c:v",
15200
- "libx264",
15201
- // Near-visually-lossless re-encode. libx264 DEFAULTS (crf 23, preset medium)
15202
- // roughly halve the source bitrate and add banding on smooth surfaces; the
15203
- // spine concats these by stream-copy, so any loss here ships to the final cut.
15204
- "-crf",
15205
- "18",
15206
- "-preset",
15207
- "slow",
15208
- "-pix_fmt",
15209
- "yuv420p",
15210
- "{{out.video}}"
15211
- ];
15212
- }
15216
+ var MONTAGE_HOLD_MAX_S = 3.5;
15213
15217
  function trimArgs(durationS, offsetS = 0, dims) {
15214
15218
  return [
15215
15219
  "-i",
@@ -15995,8 +15999,9 @@ function sceneShootMode(scene, present, nativeTurn, cameraOn, casts) {
15995
15999
  hasProduct: present.some((s) => s.type.toLowerCase() === "product")
15996
16000
  });
15997
16001
  }
15998
- function emitSceneNativeAudio(i, nativeTurn, ambientBroll, lengths, clock, nodes, voTracks, nativeSegments, clipRef = `$ref:s${i}_clip.video`) {
16002
+ function emitSceneNativeAudio(i, nativeTurn, ambientBroll, lengths, clock, nodes, voTracks, nativeSegments, clipRef) {
15999
16003
  if (nativeTurn) {
16004
+ if (!clipRef) throw new Error(`scaffold invariant: scene ${i} has a native turn but no clip to extract audio from`);
16000
16005
  const speechWindow = Math.max(0.5, nativeTurn.end_s - nativeTurn.start_s);
16001
16006
  const extractLen = Math.min(speechWindow, lengths.genDur);
16002
16007
  nodes.push({
@@ -16017,7 +16022,7 @@ function emitSceneNativeAudio(i, nativeTurn, ambientBroll, lengths, clock, nodes
16017
16022
  nodes.push({
16018
16023
  id: `s${i}_ambient`,
16019
16024
  type: "ffmpeg",
16020
- inputs: { clip: `$ref:s${i}_clip.video` },
16025
+ inputs: { clip: clipRef },
16021
16026
  params: { args: audioExtractArgs(lengths.dur), outputs: { audio: { kind: "audio", ext: "mp3" } } }
16022
16027
  });
16023
16028
  voTracks.push({
@@ -16079,7 +16084,7 @@ function buildPerSpeakerVoiceConversion(segments, totalMs, nodes) {
16079
16084
  }
16080
16085
  return tracks;
16081
16086
  }
16082
- function emitSceneClip(i, scene, present, mode, nativeTurn, ambientBroll, frames, lengths, out, opts, nodes, tag = "") {
16087
+ function emitSceneClip(i, scene, present, mode, nativeTurn, ambientBroll, frames, lengths, out, opts, nodes, tag = "", defer = false) {
16083
16088
  const profile = clipProfileFor(opts.videoModel) ?? SEEDANCE_PROFILE;
16084
16089
  const clipParams = {
16085
16090
  model: opts.videoModel,
@@ -16116,6 +16121,14 @@ function emitSceneClip(i, scene, present, mode, nativeTurn, ambientBroll, frames
16116
16121
  const base = `$ref:s${i}${tag}_clip.video`;
16117
16122
  const normDims = opts.outAr && opts.genAr !== opts.outAr ? canvasDims(opts.outAr) : void 0;
16118
16123
  if (lengths.genDur === lengths.trimTarget && !normDims) return { ref: base, scene_s: lengths.dur, out };
16124
+ if (defer) {
16125
+ return {
16126
+ ref: base,
16127
+ scene_s: lengths.dur,
16128
+ out,
16129
+ ...lengths.genDur !== lengths.trimTarget ? { trim: { offset_s: 0 } } : {}
16130
+ };
16131
+ }
16119
16132
  nodes.push({
16120
16133
  id: `s${i}${tag}_clip_trim`,
16121
16134
  type: "ffmpeg",
@@ -16190,29 +16203,6 @@ function sceneIsFullScreenUi(scene, present) {
16190
16203
  const hay = `${scene.summary ?? ""} ${scene.start_frame_prompt ?? ""} ${scene.end_frame_prompt ?? ""} ${scene.action_detail ?? ""}`;
16191
16204
  return UI_SURFACE_RE.test(hay);
16192
16205
  }
16193
- function screenStillArgs(durationS, dims) {
16194
- return [
16195
- "-loop",
16196
- "1",
16197
- "-i",
16198
- "{{in.frame}}",
16199
- "-t",
16200
- durationS.toFixed(3),
16201
- "-r",
16202
- "30",
16203
- "-vf",
16204
- `scale=${dims.w}:${dims.h}:force_original_aspect_ratio=decrease,pad=${dims.w}:${dims.h}:(ow-iw)/2:(oh-ih)/2:color=black,setsar=1,format=yuv420p`,
16205
- "-c:v",
16206
- "libx264",
16207
- "-crf",
16208
- "18",
16209
- "-preset",
16210
- "slow",
16211
- "-pix_fmt",
16212
- "yuv420p",
16213
- "{{out.video}}"
16214
- ];
16215
- }
16216
16206
  function layeredComposition(scene, uiRouted) {
16217
16207
  const regions = compositeRegionsOf(scene);
16218
16208
  if (!regions) return null;
@@ -16383,7 +16373,7 @@ function emitCompositeScene(composite, scene, i, present, mode, nativeTurn, leng
16383
16373
  );
16384
16374
  clips.push(built.clip);
16385
16375
  }
16386
- function emitFlashHold(i, scene, slots, ctx, lengths, out, outAr, nodes, clips) {
16376
+ function emitFlashHold(i, scene, slots, ctx, lengths, out, nodes, clips) {
16387
16377
  const frame = buildFrameRef(
16388
16378
  "start",
16389
16379
  scene.start_frame_asset?.url,
@@ -16392,18 +16382,9 @@ function emitFlashHold(i, scene, slots, ctx, lengths, out, outAr, nodes, clips)
16392
16382
  ctx,
16393
16383
  nodes
16394
16384
  );
16395
- nodes.push({
16396
- id: `s${i}_clip`,
16397
- type: "ffmpeg",
16398
- inputs: { frame },
16399
- params: {
16400
- args: stillHoldArgs(lengths.trimTarget, canvasDims(outAr)),
16401
- outputs: { video: { kind: "video", ext: "mp4" } }
16402
- }
16403
- });
16404
- clips.push({ ref: `$ref:s${i}_clip.video`, scene_s: lengths.dur, out });
16385
+ clips.push({ ref: frame, scene_s: lengths.dur, out, still: { fit: "fill" } });
16405
16386
  }
16406
- function emitScreenScene(i, scene, lengths, out, outAr, surfaceIngests, nodes, clips) {
16387
+ function emitScreenScene(i, scene, lengths, out, surfaceIngests, nodes, clips) {
16407
16388
  const regions = (scene.composition?.regions ?? []).filter((r) => Boolean(r) && typeof r === "object");
16408
16389
  const surfaceId = regions.find((r) => r.surface_id)?.surface_id;
16409
16390
  let refId = surfaceId ? surfaceIngests.get(surfaceId) : void 0;
@@ -16421,16 +16402,7 @@ function emitScreenScene(i, scene, lengths, out, outAr, surfaceIngests, nodes, c
16421
16402
  });
16422
16403
  if (surfaceId) surfaceIngests.set(surfaceId, refId);
16423
16404
  }
16424
- nodes.push({
16425
- id: `s${i}_clip`,
16426
- type: "ffmpeg",
16427
- inputs: { frame: `$ref:${refId}.asset` },
16428
- params: {
16429
- args: screenStillArgs(lengths.trimTarget, canvasDims(outAr)),
16430
- outputs: { video: { kind: "video", ext: "mp4" } }
16431
- }
16432
- });
16433
- clips.push({ ref: `$ref:s${i}_clip.video`, scene_s: lengths.dur, out });
16405
+ clips.push({ ref: `$ref:${refId}.asset`, scene_s: lengths.dur, out, still: { fit: "pad" } });
16434
16406
  }
16435
16407
  function sceneIsAllGraphic(scene) {
16436
16408
  const regions = (scene.composition?.regions ?? []).filter((r) => Boolean(r) && typeof r === "object");
@@ -16464,7 +16436,7 @@ function fullFrameGraphicScenes(blueprint) {
16464
16436
  }
16465
16437
  return graphic;
16466
16438
  }
16467
- function emitGraphicScene(i, scene, lengths, out, outAr, surfaceIngests, nodes, clips) {
16439
+ function emitGraphicScene(i, scene, lengths, out, surfaceIngests, nodes, clips) {
16468
16440
  const regions = (scene.composition?.regions ?? []).filter((r) => Boolean(r) && typeof r === "object");
16469
16441
  const surfaceId = regions.find((r) => r.surface_id)?.surface_id;
16470
16442
  let refId = surfaceId ? surfaceIngests.get(surfaceId) : void 0;
@@ -16482,16 +16454,7 @@ function emitGraphicScene(i, scene, lengths, out, outAr, surfaceIngests, nodes,
16482
16454
  });
16483
16455
  if (surfaceId) surfaceIngests.set(surfaceId, refId);
16484
16456
  }
16485
- nodes.push({
16486
- id: `s${i}_clip`,
16487
- type: "ffmpeg",
16488
- inputs: { frame: `$ref:${refId}.asset` },
16489
- params: {
16490
- args: screenStillArgs(lengths.trimTarget, canvasDims(outAr)),
16491
- outputs: { video: { kind: "video", ext: "mp4" } }
16492
- }
16493
- });
16494
- clips.push({ ref: `$ref:s${i}_clip.video`, scene_s: lengths.dur, out });
16457
+ clips.push({ ref: `$ref:${refId}.asset`, scene_s: lengths.dur, out, still: { fit: "pad" } });
16495
16458
  }
16496
16459
  var BRAND_CARD_RE = /\b(?:solid|plain|flat|brand|logo|wordmark|end[- ]?card|cta card|title card|colou?r background|background colou?r)\b/i;
16497
16460
  function sceneIsBrandCard(scene, present, isCta) {
@@ -17095,6 +17058,27 @@ function emitCompositeMultiSpeakerVoice(onCam, scene, i, canonical, ensureVoiceN
17095
17058
  );
17096
17059
  }
17097
17060
  }
17061
+ function brollKeyframes(scene, i, env, ctx, lengths, prevEndFrame, nodes) {
17062
+ const sharesPrevFrame = Boolean(scene.continues_previous && prevEndFrame);
17063
+ const first = sharesPrevFrame && prevEndFrame ? prevEndFrame : buildFrameRef(
17064
+ "start",
17065
+ scene.start_frame_asset?.url,
17066
+ scene.start_frame_prompt,
17067
+ slotsForFrame(env.slots, i, "start"),
17068
+ ctx,
17069
+ nodes
17070
+ );
17071
+ const nextContinues = Boolean(env.blueprint.scenes[i + 1]?.continues_previous);
17072
+ const last = lengths.trimTarget <= 4 && !nextContinues ? void 0 : buildFrameRef(
17073
+ "end",
17074
+ scene.end_frame_asset?.url,
17075
+ scene.end_frame_prompt,
17076
+ slotsForFrame(env.slots, i, "end"),
17077
+ ctx,
17078
+ nodes
17079
+ );
17080
+ return { first, last, sharesPrevFrame };
17081
+ }
17098
17082
  function emitBrollScene(scene, i, isLast, env, nodes, out, prevEndFrame) {
17099
17083
  const present = slotsForScene(env.slots, i);
17100
17084
  const mode = sceneShootMode(scene, present, void 0, env.cameraOn, env.casts);
@@ -17110,39 +17094,28 @@ function emitBrollScene(scene, i, isLast, env, nodes, out, prevEndFrame) {
17110
17094
  ingestCache: env.ingestCache
17111
17095
  };
17112
17096
  if (!env.reuse && env.graphicScenes.has(i)) {
17113
- emitGraphicScene(i, scene, lengths, lengths.out, env.outAr, env.surfaceIngests, nodes, out.clips);
17097
+ emitGraphicScene(i, scene, lengths, lengths.out, env.surfaceIngests, nodes, out.clips);
17098
+ out.routes.set(i, "graphic_plate");
17114
17099
  return void 0;
17115
17100
  }
17116
17101
  if (!env.reuse && sceneIsFullScreenUi(scene, present)) {
17117
- emitScreenScene(i, scene, lengths, lengths.out, env.outAr, env.surfaceIngests, nodes, out.clips);
17102
+ emitScreenScene(i, scene, lengths, lengths.out, env.surfaceIngests, nodes, out.clips);
17103
+ out.routes.set(i, "screen_still");
17118
17104
  return void 0;
17119
17105
  }
17120
17106
  const isCta = scene.narrative_role?.trim() === "cta" || isLast;
17121
17107
  if (!env.reuse && sceneIsBrandCard(scene, present, isCta)) {
17122
17108
  emitBrandCardScene(i, lengths, lengths.out, env.outAr, brandPlateColor(env.blueprint), nodes, out.clips);
17109
+ out.routes.set(i, "brand_card");
17123
17110
  return void 0;
17124
17111
  }
17125
- if (!ambientBroll && lengths.dur <= FLASH_HOLD_MAX_S && scene.motion_level !== "dynamic") {
17126
- emitFlashHold(i, scene, env.slots, ctx, lengths, lengths.out, env.outAr, nodes, out.clips);
17112
+ const holdCeiling = scene.motion_level === "static" || scene.motion_level === "subtle" ? MONTAGE_HOLD_MAX_S : FLASH_HOLD_MAX_S;
17113
+ if (!ambientBroll && lengths.dur <= holdCeiling && scene.motion_level !== "dynamic") {
17114
+ emitFlashHold(i, scene, env.slots, ctx, lengths, lengths.out, nodes, out.clips);
17115
+ out.routes.set(i, "still_hold");
17127
17116
  return void 0;
17128
17117
  }
17129
- const sharesPrevFrame = Boolean(scene.continues_previous && prevEndFrame);
17130
- const first = sharesPrevFrame && prevEndFrame ? prevEndFrame : buildFrameRef(
17131
- "start",
17132
- scene.start_frame_asset?.url,
17133
- scene.start_frame_prompt,
17134
- slotsForFrame(env.slots, i, "start"),
17135
- ctx,
17136
- nodes
17137
- );
17138
- const last = buildFrameRef(
17139
- "end",
17140
- scene.end_frame_asset?.url,
17141
- scene.end_frame_prompt,
17142
- slotsForFrame(env.slots, i, "end"),
17143
- ctx,
17144
- nodes
17145
- );
17118
+ const { first, last, sharesPrevFrame } = brollKeyframes(scene, i, env, ctx, lengths, prevEndFrame, nodes);
17146
17119
  const clip = emitSceneClip(
17147
17120
  i,
17148
17121
  scene,
@@ -17161,7 +17134,9 @@ function emitBrollScene(scene, i, isLast, env, nodes, out, prevEndFrame) {
17161
17134
  nativeLang: env.ttsLanguageCode,
17162
17135
  uiRouted: env.uiRouted.has(i)
17163
17136
  },
17164
- nodes
17137
+ nodes,
17138
+ "",
17139
+ true
17165
17140
  );
17166
17141
  if (ambientBroll) {
17167
17142
  emitSceneNativeAudio(
@@ -17172,28 +17147,25 @@ function emitBrollScene(scene, i, isLast, env, nodes, out, prevEndFrame) {
17172
17147
  env.clock,
17173
17148
  nodes,
17174
17149
  out.voTracks,
17175
- out.nativeSegments
17150
+ out.nativeSegments,
17151
+ `$ref:s${i}_clip.video`
17176
17152
  );
17177
17153
  }
17178
17154
  out.clips.push(sharesPrevFrame ? { ...clip, continuesFrame: true } : clip);
17155
+ out.routes.set(i, "clip");
17179
17156
  return last;
17180
17157
  }
17181
- function emitPresenterSliceClip(i, slice, env, nodes, out) {
17158
+ function emitPresenterSliceClip(slice, out) {
17182
17159
  if (slice.skip) return;
17183
17160
  const cont = slice.continuesFrame ? { continuesFrame: true } : {};
17184
- const normDims = env.genAr !== env.outAr ? canvasDims(env.outAr) : void 0;
17185
- const whole = slice.offset === 0 && Math.abs(slice.len - slice.clipDur) <= 0.05 && !normDims;
17186
- if (whole) {
17187
- out.clips.push({ ref: slice.clipRef, scene_s: slice.len, out: null, ...cont });
17188
- return;
17189
- }
17190
- nodes.push({
17191
- id: `s${i}_seg`,
17192
- type: "ffmpeg",
17193
- inputs: { clip: slice.clipRef },
17194
- params: { args: trimArgs(slice.len, slice.offset, normDims), outputs: { video: { kind: "video", ext: "mp4" } } }
17161
+ const whole = slice.offset === 0 && Math.abs(slice.len - slice.clipDur) <= 0.05;
17162
+ out.clips.push({
17163
+ ref: slice.clipRef,
17164
+ scene_s: slice.len,
17165
+ out: null,
17166
+ ...cont,
17167
+ ...whole ? {} : { trim: { offset_s: slice.offset } }
17195
17168
  });
17196
- out.clips.push({ ref: `$ref:s${i}_seg.video`, scene_s: slice.len, out: null, ...cont });
17197
17169
  }
17198
17170
  function buildTimeline(blueprint, slots, opts, nodes) {
17199
17171
  const reuse = opts.frames === "reuse";
@@ -17224,6 +17196,7 @@ function buildTimeline(blueprint, slots, opts, nodes) {
17224
17196
  ttsLanguageCode: deriveTtsLanguageCode(blueprint)
17225
17197
  };
17226
17198
  const out = {
17199
+ routes: /* @__PURE__ */ new Map(),
17227
17200
  clips: [],
17228
17201
  voTracks: [],
17229
17202
  voSegments: [],
@@ -17263,12 +17236,14 @@ function buildTimeline(blueprint, slots, opts, nodes) {
17263
17236
  nodes,
17264
17237
  out
17265
17238
  );
17239
+ out.routes.set(i, "composite");
17266
17240
  prevEndFrame = void 0;
17267
17241
  return;
17268
17242
  }
17269
17243
  const slice = out.sceneSlice.get(i);
17270
17244
  if (slice) {
17271
- emitPresenterSliceClip(i, slice, env, nodes, out);
17245
+ emitPresenterSliceClip(slice, out);
17246
+ out.routes.set(i, slice.skip ? "phrase_run" : "phrase_slice");
17272
17247
  prevEndFrame = void 0;
17273
17248
  return;
17274
17249
  }
@@ -17276,7 +17251,13 @@ function buildTimeline(blueprint, slots, opts, nodes) {
17276
17251
  });
17277
17252
  const totalMs = Math.round(env.clock.totalS * 1e3);
17278
17253
  out.voTracks.push(...buildPerSpeakerVoiceConversion(out.nativeSegments, totalMs, nodes));
17279
- return { clips: out.clips, voTracks: out.voTracks, vo_segments: out.voSegments, talking_scenes: out.talkingScenes };
17254
+ return {
17255
+ clips: out.clips,
17256
+ voTracks: out.voTracks,
17257
+ vo_segments: out.voSegments,
17258
+ talking_scenes: out.talkingScenes,
17259
+ routes: out.routes
17260
+ };
17280
17261
  }
17281
17262
  function buildSfxMusic(blueprint, clock, nodes) {
17282
17263
  const tracks = [];
@@ -17563,18 +17544,20 @@ function seamDropOps(clips, i, seam) {
17563
17544
  return `reverse,trim=start_frame=1,setpts=PTS-STARTPTS,reverse,${SEAM_FRAME_PAD}`;
17564
17545
  return null;
17565
17546
  }
17566
- function spineInputNormalize(dims) {
17567
- return `scale=${dims.w}:${dims.h}:force_original_aspect_ratio=increase,crop=${dims.w}:${dims.h},format=yuv420p,fps=30,setsar=1,settb=AVTB`;
17547
+ function spineInputChain(clips, i, seam, dims) {
17548
+ const c = clips[i];
17549
+ const ops = spineInputOps(c, clipInputLen(c), dims);
17550
+ const seamOps = seamDropOps(clips, i, seam);
17551
+ return `[${i}:v]${ops}${seamOps ? `,${seamOps}` : ""}[c${i}]`;
17568
17552
  }
17569
17553
  var SPINE_OUTPUT_ENCODE = ["-r", "30", "-c:v", "libx264", "-crf", "18", "-preset", "slow", "-pix_fmt", "yuv420p"];
17570
17554
  function concatArgs(clips, seam, dims) {
17571
17555
  const inputs = [];
17572
17556
  const pre = [];
17573
17557
  const labels = [];
17574
- clips.forEach((_, i) => {
17575
- inputs.push("-i", `{{in.c${i}}}`);
17576
- const ops = seamDropOps(clips, i, seam);
17577
- pre.push(`[${i}:v]${spineInputNormalize(dims)}${ops ? `,${ops}` : ""}[c${i}]`);
17558
+ clips.forEach((c, i) => {
17559
+ inputs.push(...spineInputFlags(c, clipInputLen(c)), "-i", `{{in.c${i}}}`);
17560
+ pre.push(spineInputChain(clips, i, seam, dims));
17578
17561
  labels.push(`[c${i}]`);
17579
17562
  });
17580
17563
  const graph = [...pre, `${labels.join("")}concat=n=${clips.length}:v=1:a=0[v]`].join(";");
@@ -17588,9 +17571,8 @@ function xfadeSpineArgs(clips, seam, dims) {
17588
17571
  const inputs = [];
17589
17572
  const filt = [];
17590
17573
  for (let i = 0; i < n; i++) {
17591
- inputs.push("-i", `{{in.c${i}}}`);
17592
- const ops = seamDropOps(clips, i, seam);
17593
- filt.push(`[${i}:v]${spineInputNormalize(dims)}${ops ? `,${ops}` : ""}[c${i}]`);
17574
+ inputs.push(...spineInputFlags(clips[i], clipInputLen(clips[i])), "-i", `{{in.c${i}}}`);
17575
+ filt.push(spineInputChain(clips, i, seam, dims));
17594
17576
  }
17595
17577
  let cur = "c0";
17596
17578
  let accLen = clipInputLen(clips[0]);
@@ -17694,7 +17676,7 @@ function scaffoldVideoCanvas(input, elementsInput, opts) {
17694
17676
  });
17695
17677
  });
17696
17678
  buildElementSheets(slots, nodes);
17697
- const { clips, voTracks, vo_segments, talking_scenes } = buildTimeline(blueprint, slots, opts, nodes);
17679
+ const { clips, voTracks, vo_segments, talking_scenes, routes } = buildTimeline(blueprint, slots, opts, nodes);
17698
17680
  const outAspect = resolveAspect(blueprint.source?.aspect_ratio, opts.aspect, genAspectsFor(opts.videoModel));
17699
17681
  let videoRef = buildSpine(clips, opts.seamDedup ?? "head", canvasDims(outAspect.outAr), nodes);
17700
17682
  let videoNode = "spine";
@@ -17793,14 +17775,39 @@ function scaffoldVideoCanvas(input, elementsInput, opts) {
17793
17775
  // The timing plan `baker canvas validate` checks before any billed render:
17794
17776
  // sequenced voiceover turns (no overlap), audio ≈ video length, and which
17795
17777
  // scenes must be lip-synced.
17796
- video: buildVideoMeta(blueprint, clock, { vo_segments, talking_scenes }, slots, nodes, Boolean(audioMixRef))
17778
+ video: buildVideoMeta(
17779
+ blueprint,
17780
+ clock,
17781
+ { vo_segments, talking_scenes, routes },
17782
+ slots,
17783
+ nodes,
17784
+ Boolean(audioMixRef)
17785
+ )
17797
17786
  },
17798
17787
  nodes,
17799
17788
  output: { node: videoNode, output: "video" }
17800
17789
  };
17801
17790
  }
17791
+ function buildGraphStats(blueprint, routes, nodes) {
17792
+ const histogram = {};
17793
+ for (const n of nodes) histogram[n.type] = (histogram[n.type] ?? 0) + 1;
17794
+ const billable = nodes.filter((n) => n.type === "image_generate" || n.type === "video_generate").length;
17795
+ return {
17796
+ nodes_total: nodes.length,
17797
+ nodes_billable: billable,
17798
+ node_types: histogram,
17799
+ scene_routes: blueprint.scenes.map((scene, i) => ({
17800
+ scene: i,
17801
+ route: routes.get(i) ?? "clip",
17802
+ window_s: Math.round(sceneDurationS(scene) * 100) / 100
17803
+ }))
17804
+ };
17805
+ }
17802
17806
  function buildVideoMeta(blueprint, clock, meta, slots, nodes, hasAudioMix) {
17803
17807
  return {
17808
+ // WHY the graph has the nodes it has: one route per scene (the dispatcher's own
17809
+ // record) + a node-type histogram. The first thing to read when a canvas looks big.
17810
+ graph_stats: buildGraphStats(blueprint, meta.routes, nodes),
17804
17811
  // The SPINE length (Σ scene durations) — what the picture and the mix are pinned to.
17805
17812
  duration_s: clock.totalS,
17806
17813
  // The one-clock contract the validator enforces on the LIVE graph: the spine's
@@ -17996,7 +18003,7 @@ function buildVideoTodo(report, overlayCount, floatingCount, opts, blueprint) {
17996
18003
  voice_description: d.voice_description,
17997
18004
  line: d.line
17998
18005
  })),
17999
- talking_head_note: "SHOT-NATIVE: a presenter shot is ONE Seedance clip (its line quoted in s<anchor>_clip's prompt + generate_audio) so lips+voice are generated together \u2014 no tts, no veed-lipsync. Two adjacent presenter shots at a hard cut are SEPARATE clips; a cutaway phrase (the presenter on camera, cut to b-roll, back on camera) stays one clip and slices its on-camera windows (s<i>_seg). Edit the line in the s<anchor>_clip prompt to re-author it. A pure-voiceover phrase (speaker never shown) is one ElevenLabs tts read instead.",
18006
+ talking_head_note: "SHOT-NATIVE: a presenter shot is ONE Seedance clip (its line quoted in s<anchor>_clip's prompt + generate_audio) so lips+voice are generated together \u2014 no tts, no veed-lipsync. Two adjacent presenter shots at a hard cut are SEPARATE clips; a cutaway phrase (the presenter on camera, cut to b-roll, back on camera) stays one clip whose on-camera windows are cut inside the spine's per-input chains (no extra nodes). Edit the line in the s<anchor>_clip prompt to re-author it. A pure-voiceover phrase (speaker never shown) is one ElevenLabs tts read instead.",
18000
18007
  voice_note: "ONE voice per person: a single voice_select is reused across all that person's shots (on-camera AND off \u2014 the deconstruct's `voiceover` label folds into the sole presenter). Every presenter clip's native audio is extracted and re-voiced to that brand voice through a SINGLE merged audio_voice_convert per speaker (<voice>_conv, eleven_multilingual_sts_v2, timing preserved so lips stay matched) \u2014 so timbre stays consistent across the separate shot clips. Set voice_select.voice_id's gender/language to match the creator.",
18001
18008
  native_timing: "Clips separate at COMPLETE BREAKS between shots, but the VOICE stays continuous where it should: a voiceover narration is ONE read across the b-roll it plays over, and a cutaway leaves the presenter's read continuous under the insert. Each clip is generated long enough for its estimated speech. `metadata.video.talking_scenes` carries each shot's scene_s vs est_speech_s. CAVEAT: a b-roll cutaway INSIDE a phrase lands at an approximate (proportional) time \u2014 Seedance exposes no word timing \u2014 so if a cutaway is off its beat, nudge the scene boundary (it's a starting point). NOTE: a native clip's voice is extracted for the SPOKEN window only (`s<i>_voextract`'s `-t`) \u2014 when the scene runs longer than the line, the picture tail is deliberately silent (extracting the full clip would put post-line breathing/babble on the voice bus); trim the scene or extend the line if the dead air reads wrong.",
18002
18009
  craft: {
@@ -18135,8 +18142,8 @@ function renderStyleProjectionFromValue(blueprint) {
18135
18142
  async function syncStyleProjection(canvas, log) {
18136
18143
  const pair = findBlueprintProjection(canvas);
18137
18144
  if (!pair) return "not_applicable";
18138
- const rendered = renderStyleProjection(await readFile3(pair.promptPath, "utf8"));
18139
- const current = await readFile3(pair.stylePath, "utf8").catch(() => null);
18145
+ const rendered = renderStyleProjection(await readFile4(pair.promptPath, "utf8"));
18146
+ const current = await readFile4(pair.stylePath, "utf8").catch(() => null);
18140
18147
  if (current === rendered) return "up_to_date";
18141
18148
  await writeFile(pair.stylePath, rendered, "utf8");
18142
18149
  log(
@@ -18198,13 +18205,13 @@ ${body}` : header || body || compactJson(record);
18198
18205
  }
18199
18206
 
18200
18207
  // src/commands/canvas/run-record.ts
18201
- import path4 from "path";
18208
+ import path5 from "path";
18202
18209
  var MAX_RUN_NODES = 200;
18203
18210
  var MAX_OUTPUTS_PER_NODE = 10;
18204
18211
  var MAX_FINAL_OUTPUTS = 10;
18205
18212
  var MAX_CREATIVE_SLUG_LENGTH = 100;
18206
18213
  function creativeSlugFromCanvasPath(filePath) {
18207
- const normalized = filePath.split(path4.sep).join("/");
18214
+ const normalized = filePath.split(path5.sep).join("/");
18208
18215
  const match = normalized.match(/(?:^|\/)src\/creatives\/([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\//);
18209
18216
  const slug = match?.[1] ?? null;
18210
18217
  return slug && slug.length <= MAX_CREATIVE_SLUG_LENGTH ? slug : null;
@@ -18489,43 +18496,59 @@ var RunRecordPoster = class {
18489
18496
 
18490
18497
  // src/commands/canvas/run-retention.ts
18491
18498
  import { rm } from "fs/promises";
18492
- import path5 from "path";
18499
+ import path6 from "path";
18493
18500
  function runDirsToPrune(entries, keep, currentRunId) {
18494
18501
  const runs = entries.filter((e) => /^r_[0-9A-Za-z]+$/.test(e) && e !== currentRunId).sort();
18495
18502
  if (keep <= 0) return runs;
18496
18503
  return runs.slice(0, Math.max(0, runs.length - keep));
18497
18504
  }
18498
18505
  async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
18499
- const { readdir: readdir4 } = await import("fs/promises");
18506
+ const { readdir: readdir6 } = await import("fs/promises");
18500
18507
  let entries;
18501
18508
  try {
18502
- entries = await readdir4(outputsDir);
18509
+ entries = await readdir6(outputsDir);
18503
18510
  } catch {
18504
18511
  return;
18505
18512
  }
18506
18513
  const toPrune = runDirsToPrune(entries, keep, currentRunId);
18507
18514
  if (toPrune.length === 0) return;
18508
18515
  for (const dir of toPrune) {
18509
- await rm(path5.join(outputsDir, dir), { recursive: true, force: true }).catch(
18516
+ await rm(path6.join(outputsDir, dir), { recursive: true, force: true }).catch(
18510
18517
  (e) => log(`[prune ] could not remove ${dir}: ${e.message}`)
18511
18518
  );
18512
18519
  }
18513
18520
  log(`[prune ] removed ${toPrune.length} old run dir(s), kept the ${keep} newest`);
18514
18521
  }
18515
18522
 
18523
+ // src/commands/canvas/dirty-marker.ts
18524
+ import { mkdir, readdir as readdir2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
18525
+ import path7 from "path";
18526
+ function creativeDirtyDir(base) {
18527
+ return base ?? path7.resolve("canvas", ".dirty");
18528
+ }
18529
+ function dirtyMarkerFile(slug, base) {
18530
+ return path7.join(creativeDirtyDir(base), `${slug}.json`);
18531
+ }
18532
+ async function clearCreativeDirty(slug, base) {
18533
+ try {
18534
+ await rm2(dirtyMarkerFile(slug, base), { force: true });
18535
+ } catch {
18536
+ }
18537
+ }
18538
+
18516
18539
  // src/commands/canvas/run-resume.ts
18517
- import { mkdir, readFile as readFile4, rm as rm2, writeFile as writeFile2 } from "fs/promises";
18518
- import path6 from "path";
18540
+ import { mkdir as mkdir2, readFile as readFile5, rm as rm3, writeFile as writeFile3 } from "fs/promises";
18541
+ import path8 from "path";
18519
18542
  function markerKey(canvasPath) {
18520
18543
  const slug = creativeSlugFromCanvasPath(canvasPath);
18521
- const identity2 = slug ?? path6.relative(process.cwd(), path6.resolve(canvasPath));
18544
+ const identity2 = slug ?? path8.relative(process.cwd(), path8.resolve(canvasPath));
18522
18545
  return sha256Hex(Buffer.from(identity2)).slice(0, 32);
18523
18546
  }
18524
18547
  function legacyMarkerKey(canvasPath) {
18525
- return sha256Hex(Buffer.from(path6.resolve(canvasPath))).slice(0, 32);
18548
+ return sha256Hex(Buffer.from(path8.resolve(canvasPath))).slice(0, 32);
18526
18549
  }
18527
18550
  function markerFile(outputsDir, key) {
18528
- return path6.join(outputsDir, ".inflight", `${key}.json`);
18551
+ return path8.join(outputsDir, ".inflight", `${key}.json`);
18529
18552
  }
18530
18553
  var REMOTE_ADOPT_STALE_MS = 12e4;
18531
18554
  function classifyRemoteRun(run, now) {
@@ -18561,7 +18584,7 @@ async function resolveRunId(opts) {
18561
18584
  async function readMarkerRunId(outputsDir, canvasPath) {
18562
18585
  for (const key of [markerKey(canvasPath), legacyMarkerKey(canvasPath)]) {
18563
18586
  try {
18564
- const raw = await readFile4(markerFile(outputsDir, key), "utf8");
18587
+ const raw = await readFile5(markerFile(outputsDir, key), "utf8");
18565
18588
  const parsed = JSON.parse(raw);
18566
18589
  if (typeof parsed.runId === "string" && parsed.runId.length > 0) return parsed.runId;
18567
18590
  } catch {
@@ -18572,23 +18595,23 @@ async function readMarkerRunId(outputsDir, canvasPath) {
18572
18595
  async function markRunInFlight(outputsDir, canvasPath, runId) {
18573
18596
  try {
18574
18597
  const file = markerFile(outputsDir, markerKey(canvasPath));
18575
- await mkdir(path6.dirname(file), { recursive: true });
18576
- await writeFile2(file, JSON.stringify({ runId, canvasPath: path6.resolve(canvasPath), startedAt: Date.now() }));
18598
+ await mkdir2(path8.dirname(file), { recursive: true });
18599
+ await writeFile3(file, JSON.stringify({ runId, canvasPath: path8.resolve(canvasPath), startedAt: Date.now() }));
18577
18600
  } catch {
18578
18601
  }
18579
18602
  }
18580
18603
  async function clearRunMarker(outputsDir, canvasPath) {
18581
18604
  for (const key of [markerKey(canvasPath), legacyMarkerKey(canvasPath)]) {
18582
18605
  try {
18583
- await rm2(markerFile(outputsDir, key), { force: true });
18606
+ await rm3(markerFile(outputsDir, key), { force: true });
18584
18607
  } catch {
18585
18608
  }
18586
18609
  }
18587
18610
  }
18588
18611
 
18589
18612
  // src/commands/canvas/run-snapshot.ts
18590
- import { mkdir as mkdir2, readdir as readdir2, readFile as readFile5, stat as stat2, writeFile as writeFile3 } from "fs/promises";
18591
- import path7 from "path";
18613
+ import { mkdir as mkdir3, readdir as readdir3, readFile as readFile6, stat as stat2, writeFile as writeFile4 } from "fs/promises";
18614
+ import path9 from "path";
18592
18615
  var SNAPSHOT_SCHEMA = "baker-canvas-snapshot/1";
18593
18616
  var MAX_SNAPSHOT_FILE_BYTES = 32 * 1024 * 1024;
18594
18617
  var EXT_TO_MIME = {
@@ -18615,11 +18638,11 @@ var EXT_TO_MIME = {
18615
18638
  woff2: "font/woff2"
18616
18639
  };
18617
18640
  function mimeForFile(filePath) {
18618
- const ext = path7.extname(filePath).slice(1).toLowerCase();
18641
+ const ext = path9.extname(filePath).slice(1).toLowerCase();
18619
18642
  return EXT_TO_MIME[ext] ?? "application/octet-stream";
18620
18643
  }
18621
18644
  function toPosix(p) {
18622
- return p.split(path7.sep).join("/");
18645
+ return p.split(path9.sep).join("/");
18623
18646
  }
18624
18647
  function localPathRefsFromCanvas(parsed) {
18625
18648
  const nodes = parsed?.nodes;
@@ -18643,30 +18666,30 @@ function isSnapshotablePath(value) {
18643
18666
  }
18644
18667
  async function uploadRunSnapshot(client, opts) {
18645
18668
  try {
18646
- const canvasDir = path7.dirname(opts.canvasPath);
18669
+ const canvasDir = path9.dirname(opts.canvasPath);
18647
18670
  const put = (bytes, mime) => putContentAddressed(client, bytes, mime, opts.signal);
18648
18671
  const canvasBytes = Buffer.from(opts.raw);
18649
18672
  const canvasUpload = await put(canvasBytes, "application/json");
18650
18673
  const files = [];
18651
18674
  const skipped = [];
18652
18675
  for (const refPath of localPathRefsFromCanvas(opts.parsed)) {
18653
- const abs = path7.isAbsolute(refPath) ? refPath : path7.resolve(canvasDir, refPath);
18676
+ const abs = path9.isAbsolute(refPath) ? refPath : path9.resolve(canvasDir, refPath);
18654
18677
  let st;
18655
18678
  try {
18656
18679
  st = await stat2(abs);
18657
18680
  } catch {
18658
- skipped.push({ path: toPosix(path7.relative(canvasDir, abs)), reason: "missing" });
18681
+ skipped.push({ path: toPosix(path9.relative(canvasDir, abs)), reason: "missing" });
18659
18682
  continue;
18660
18683
  }
18661
18684
  const fileList = st.isDirectory() ? await listFilesRecursive(abs) : [abs];
18662
18685
  for (const file of fileList) {
18663
- const rel = toPosix(path7.relative(canvasDir, file));
18686
+ const rel = toPosix(path9.relative(canvasDir, file));
18664
18687
  const size = (await stat2(file)).size;
18665
18688
  if (size > MAX_SNAPSHOT_FILE_BYTES) {
18666
18689
  skipped.push({ path: rel, reason: `too large (${size} bytes)` });
18667
18690
  continue;
18668
18691
  }
18669
- const bytes = await readFile5(file);
18692
+ const bytes = await readFile6(file);
18670
18693
  const upload = await put(bytes, mimeForFile(file));
18671
18694
  files.push({ path: rel, sha256: upload.sha256, url: upload.url });
18672
18695
  }
@@ -18675,7 +18698,7 @@ async function uploadRunSnapshot(client, opts) {
18675
18698
  schema: SNAPSHOT_SCHEMA,
18676
18699
  creativeSlug: opts.creativeSlug,
18677
18700
  canvasSha: canvasUpload.sha256,
18678
- canvas: { path: path7.basename(opts.canvasPath), sha256: canvasUpload.sha256, url: canvasUpload.url },
18701
+ canvas: { path: path9.basename(opts.canvasPath), sha256: canvasUpload.sha256, url: canvasUpload.url },
18679
18702
  files,
18680
18703
  skipped: skipped.length > 0 ? skipped : void 0
18681
18704
  };
@@ -18701,8 +18724,8 @@ async function putContentAddressed(client, bytes, mime, signal) {
18701
18724
  return { sha256, url: publicUrl };
18702
18725
  }
18703
18726
  async function listFilesRecursive(dir) {
18704
- const entries = await readdir2(dir, { recursive: true, withFileTypes: true });
18705
- return entries.filter((d) => d.isFile()).map((d) => path7.join(d.parentPath, d.name));
18727
+ const entries = await readdir3(dir, { recursive: true, withFileTypes: true });
18728
+ return entries.filter((d) => d.isFile()).map((d) => path9.join(d.parentPath, d.name));
18706
18729
  }
18707
18730
  var SnapshotConflictError = class extends Error {
18708
18731
  conflicts;
@@ -18714,19 +18737,19 @@ var SnapshotConflictError = class extends Error {
18714
18737
  };
18715
18738
  async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
18716
18739
  const entries = [manifest.canvas, ...manifest.files];
18717
- const resolvedTarget = path7.resolve(targetDir);
18740
+ const resolvedTarget = path9.resolve(targetDir);
18718
18741
  const planned = [];
18719
18742
  const conflicts = [];
18720
18743
  const upToDate = [];
18721
18744
  for (const entry of entries) {
18722
- if (path7.isAbsolute(entry.path) || entry.path.split("/").includes("..")) {
18745
+ if (path9.isAbsolute(entry.path) || entry.path.split("/").includes("..")) {
18723
18746
  throw new Error(`snapshot entry escapes the creative directory: ${entry.path}`);
18724
18747
  }
18725
- const target = path7.resolve(resolvedTarget, entry.path);
18726
- if (target !== resolvedTarget && !target.startsWith(resolvedTarget + path7.sep)) {
18748
+ const target = path9.resolve(resolvedTarget, entry.path);
18749
+ if (target !== resolvedTarget && !target.startsWith(resolvedTarget + path9.sep)) {
18727
18750
  throw new Error(`snapshot entry escapes the creative directory: ${entry.path}`);
18728
18751
  }
18729
- const existing = await readFile5(target).catch(() => null);
18752
+ const existing = await readFile6(target).catch(() => null);
18730
18753
  if (existing) {
18731
18754
  if (sha256Hex(existing) === entry.sha256) {
18732
18755
  upToDate.push(entry.path);
@@ -18748,11 +18771,11 @@ async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
18748
18771
  if (sha256Hex(bytes) !== entry.sha256) {
18749
18772
  throw new Error(`snapshot download for ${entry.path} does not match its recorded sha256`);
18750
18773
  }
18751
- await mkdir2(path7.dirname(target), { recursive: true });
18752
- await writeFile3(target, bytes);
18774
+ await mkdir3(path9.dirname(target), { recursive: true });
18775
+ await writeFile4(target, bytes);
18753
18776
  restored.push(entry.path);
18754
18777
  }
18755
- return { canvasPath: path7.resolve(resolvedTarget, manifest.canvas.path), restored, upToDate };
18778
+ return { canvasPath: path9.resolve(resolvedTarget, manifest.canvas.path), restored, upToDate };
18756
18779
  }
18757
18780
 
18758
18781
  // src/commands/canvas/run.ts
@@ -18832,8 +18855,8 @@ function resolveMaxCredits(...candidates) {
18832
18855
  return void 0;
18833
18856
  }
18834
18857
  async function executeCanvasRun(opts) {
18835
- const filePath = path8.resolve(opts.file);
18836
- const raw = await readFile6(filePath, "utf8");
18858
+ const filePath = path10.resolve(opts.file);
18859
+ const raw = await readFile7(filePath, "utf8");
18837
18860
  let parsed;
18838
18861
  try {
18839
18862
  parsed = JSON.parse(raw);
@@ -18843,7 +18866,9 @@ async function executeCanvasRun(opts) {
18843
18866
  `);
18844
18867
  process.exit(2);
18845
18868
  }
18846
- parsed = resolveRelativeCanvasPaths(parsed, path8.dirname(filePath));
18869
+ const attemptedSlug = creativeSlugFromCanvasPath(filePath);
18870
+ if (attemptedSlug) await clearCreativeDirty(attemptedSlug);
18871
+ parsed = resolveRelativeCanvasPaths(parsed, path10.dirname(filePath));
18847
18872
  try {
18848
18873
  await syncStyleProjection(parsed, (line) => process.stderr.write(`${line}
18849
18874
  `));
@@ -18927,7 +18952,7 @@ async function executeCanvasRun(opts) {
18927
18952
  const canvasSha = sha256Hex(Buffer.from(raw));
18928
18953
  const creativeSlug = creativeSlugFromCanvasPath(filePath) ?? void 0;
18929
18954
  const client = opts.record === false ? null : buildBackendClient();
18930
- const outputsDir = opts.outputsDir ? path8.resolve(opts.outputsDir) : path8.resolve("canvas");
18955
+ const outputsDir = opts.outputsDir ? path10.resolve(opts.outputsDir) : path10.resolve("canvas");
18931
18956
  const { runId, resumed, source, concurrentRunId } = await resolveRunId({
18932
18957
  explicitRunId: opts.runId,
18933
18958
  fresh: opts.fresh === true,
@@ -18961,8 +18986,12 @@ async function executeCanvasRun(opts) {
18961
18986
  const canvasSnapshotUrl = client && creativeSlug ? await uploadRunSnapshot(client, { canvasPath: filePath, raw, creativeSlug, parsed }) ?? void 0 : void 0;
18962
18987
  const recordMeta = {
18963
18988
  creativeSlug,
18964
- canvasPath: path8.relative(process.cwd(), filePath) || void 0,
18989
+ canvasPath: path10.relative(process.cwd(), filePath) || void 0,
18965
18990
  canvasSha,
18991
+ // The fingerprint the dashboard compares against the current source to flag
18992
+ // "edited since last render". Computed from the on-disk canvas.json +
18993
+ // prompt.json — the same helper the edit-sync uses, so the two are comparable.
18994
+ sourceSha: await computeSourceSha(filePath),
18966
18995
  chatId: getEnv().BAKER_CHAT_ID || void 0,
18967
18996
  canvasSnapshotUrl
18968
18997
  };
@@ -19144,7 +19173,7 @@ var rerunCommand = defineCommand90({
19144
19173
  if (latest.canvasSha && manifest.canvasSha !== latest.canvasSha) {
19145
19174
  fail("snapshot_mismatch", `snapshot manifest for run ${latest.runId} does not match its recorded canvas sha`);
19146
19175
  }
19147
- const targetDir = path9.resolve("src", "creatives", slug);
19176
+ const targetDir = path11.resolve("src", "creatives", slug);
19148
19177
  let restoredCanvasPath;
19149
19178
  try {
19150
19179
  const restore = await restoreRunSnapshot(manifest, targetDir, { force: args["force-remote"] === true });
@@ -19193,9 +19222,9 @@ async function fetchManifest(url) {
19193
19222
  }
19194
19223
 
19195
19224
  // src/commands/canvas/scaffold-static-ad.ts
19196
- import { access, mkdir as mkdir3, readFile as readFile7, writeFile as writeFile4 } from "fs/promises";
19197
- import path12 from "path";
19198
- import { defineCommand as defineCommand91 } from "citty";
19225
+ import { access, mkdir as mkdir4, readFile as readFile9, writeFile as writeFile5 } from "fs/promises";
19226
+ import path15 from "path";
19227
+ import { defineCommand as defineCommand92 } from "citty";
19199
19228
 
19200
19229
  // src/engine/scaffold/staticAd.ts
19201
19230
  import { z as z14 } from "zod";
@@ -19438,7 +19467,7 @@ function staticAdReport(input, elementsInput, opts) {
19438
19467
  }
19439
19468
 
19440
19469
  // src/commands/canvas/creative-definition.ts
19441
- import path10 from "path";
19470
+ import path12 from "path";
19442
19471
  var PLATFORM_VALUES = ["meta", "google", "linkedin", "tiktok", "youtube", "x", "other"];
19443
19472
  var FORMAT_VALUES = ["1:1", "4:5", "9:16", "16:9", "1.91:1"];
19444
19473
  function titleFromSlug(slug) {
@@ -19480,16 +19509,16 @@ function buildCreativeDefinition(input) {
19480
19509
  }
19481
19510
 
19482
19511
  // src/commands/canvas/scaffold-static-ad-paths.ts
19483
- import path11 from "path";
19512
+ import path13 from "path";
19484
19513
  function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd(), slug) {
19485
19514
  const file = rawFile.trim();
19486
19515
  const imageIsUrl = /^https?:\/\//i.test(file);
19487
- const imageSource = imageIsUrl ? file : path11.resolve(cwd, file);
19488
- const outPath = out ? path11.resolve(cwd, out) : slug ? path11.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path11.join(cwd, "static-ad.canvas.json") : path11.join(path11.dirname(imageSource), "static-ad.canvas.json");
19489
- const blueprintPath = path11.join(path11.dirname(outPath), "prompt.json");
19490
- const creativeDir = slug ? path11.dirname(outPath) : null;
19491
- const definitionPath = creativeDir ? path11.join(creativeDir, "_definition.md") : null;
19492
- const referencesDir = creativeDir ? path11.join(creativeDir, "references") : null;
19516
+ const imageSource = imageIsUrl ? file : path13.resolve(cwd, file);
19517
+ const outPath = out ? path13.resolve(cwd, out) : slug ? path13.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path13.join(cwd, "static-ad.canvas.json") : path13.join(path13.dirname(imageSource), "static-ad.canvas.json");
19518
+ const blueprintPath = path13.join(path13.dirname(outPath), "prompt.json");
19519
+ const creativeDir = slug ? path13.dirname(outPath) : null;
19520
+ const definitionPath = creativeDir ? path13.join(creativeDir, "_definition.md") : null;
19521
+ const referencesDir = creativeDir ? path13.join(creativeDir, "references") : null;
19493
19522
  return { imageIsUrl, imageSource, outPath, blueprintPath, creativeDir, definitionPath, referencesDir };
19494
19523
  }
19495
19524
  var SCAFFOLD_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
@@ -19498,6 +19527,11 @@ function isValidScaffoldSlug(slug) {
19498
19527
  return slug.length <= SCAFFOLD_SLUG_MAX_LENGTH && SCAFFOLD_SLUG_PATTERN.test(slug);
19499
19528
  }
19500
19529
 
19530
+ // src/commands/canvas/sync-definition.ts
19531
+ import { readdir as readdir4, readFile as readFile8, stat as stat3 } from "fs/promises";
19532
+ import path14 from "path";
19533
+ import { defineCommand as defineCommand91 } from "citty";
19534
+
19501
19535
  // src/commands/canvas/definition-graph.ts
19502
19536
  var MAX_NODES = 300;
19503
19537
  function walkStrings(value, cb) {
@@ -19513,6 +19547,15 @@ function walkStrings(value, cb) {
19513
19547
  for (const v of Object.values(value)) walkStrings(v, cb);
19514
19548
  }
19515
19549
  }
19550
+ var PREVIEWABLE_INGEST_EXPECTS = /* @__PURE__ */ new Set(["image"]);
19551
+ function nodeInputPreviewUrl(type, params) {
19552
+ if (type !== "ingest" || !params || typeof params !== "object") return void 0;
19553
+ const p = params;
19554
+ if (p.source !== "url" || typeof p.url !== "string") return void 0;
19555
+ if (!/^https?:\/\//i.test(p.url)) return void 0;
19556
+ if (typeof p.expect === "string" && !PREVIEWABLE_INGEST_EXPECTS.has(p.expect)) return void 0;
19557
+ return p.url;
19558
+ }
19516
19559
  function canvasToDefinitionGraph(canvas) {
19517
19560
  const rawNodes = canvas?.nodes;
19518
19561
  if (!Array.isArray(rawNodes)) return null;
@@ -19540,6 +19583,8 @@ function canvasToDefinitionGraph(canvas) {
19540
19583
  const base = { id, type };
19541
19584
  if (depList.length > 0) base.deps = depList;
19542
19585
  if (paramsPreview) base.paramsPreview = paramsPreview;
19586
+ const inputPreviewUrl = nodeInputPreviewUrl(type, params);
19587
+ if (inputPreviewUrl) base.inputPreviewUrl = inputPreviewUrl;
19543
19588
  return base;
19544
19589
  });
19545
19590
  const rawOutput = canvas?.output;
@@ -19565,6 +19610,7 @@ async function syncCreativeDefinitionBestEffort(input) {
19565
19610
  formats: input.formats,
19566
19611
  sourceReferenceUrl: input.sourceReferenceUrl,
19567
19612
  graph,
19613
+ sourceSha: input.sourceSha,
19568
19614
  chatId
19569
19615
  });
19570
19616
  process.stdout.write(`[definition] synced workflow graph (${graph.nodes.length} nodes) \u2014 view it in the dashboard
@@ -19575,6 +19621,57 @@ async function syncCreativeDefinitionBestEffort(input) {
19575
19621
  `);
19576
19622
  }
19577
19623
  }
19624
+ async function resolveCanvasPath(inputPath) {
19625
+ const resolved = path14.resolve(inputPath);
19626
+ let dir = resolved;
19627
+ try {
19628
+ if ((await stat3(resolved)).isFile()) {
19629
+ if (resolved.endsWith(".canvas.json")) return resolved;
19630
+ dir = path14.dirname(resolved);
19631
+ }
19632
+ } catch {
19633
+ dir = resolved.endsWith(".canvas.json") ? path14.dirname(resolved) : resolved;
19634
+ }
19635
+ let entries;
19636
+ try {
19637
+ entries = await readdir4(dir);
19638
+ } catch {
19639
+ return null;
19640
+ }
19641
+ const canvases = entries.filter((name) => name.endsWith(".canvas.json"));
19642
+ const slug = creativeSlugFromCanvasPath(`${dir}/x/`);
19643
+ const chosen = (slug ? canvases.find((name) => name === `${slug}.canvas.json`) : void 0) ?? canvases[0];
19644
+ return chosen ? path14.join(dir, chosen) : null;
19645
+ }
19646
+ var syncDefinitionCommand = defineCommand91({
19647
+ meta: {
19648
+ name: "sync-definition",
19649
+ 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."
19650
+ },
19651
+ args: {
19652
+ file: {
19653
+ type: "positional",
19654
+ required: true,
19655
+ description: "A path under src/creatives/<slug>/ (the .canvas.json, its prompt.json, or the folder)"
19656
+ }
19657
+ },
19658
+ async run({ args }) {
19659
+ const canvasPath = await resolveCanvasPath(String(args.file));
19660
+ if (!canvasPath) {
19661
+ return;
19662
+ }
19663
+ const slug = creativeSlugFromCanvasPath(canvasPath);
19664
+ if (!slug) return;
19665
+ let canvas;
19666
+ try {
19667
+ canvas = JSON.parse(await readFile8(canvasPath, "utf8"));
19668
+ } catch {
19669
+ return;
19670
+ }
19671
+ const sourceSha = await computeSourceSha(canvasPath);
19672
+ await syncCreativeDefinitionBestEffort({ slug, canvas, sourceSha });
19673
+ }
19674
+ });
19578
19675
 
19579
19676
  // src/commands/canvas/scaffold-static-ad.ts
19580
19677
  async function fileExists(target) {
@@ -19592,7 +19689,7 @@ async function uploadSourceAsReference(source, isUrl, client) {
19592
19689
  if (!res.ok) throw new Error(`failed to download source image (${res.status})`);
19593
19690
  bytes = Buffer.from(await res.arrayBuffer());
19594
19691
  } else {
19595
- bytes = await readFile7(source);
19692
+ bytes = await readFile9(source);
19596
19693
  }
19597
19694
  const safe = await toModelSafeImage(bytes);
19598
19695
  const sha256 = sha256Hex(safe.bytes);
@@ -19647,7 +19744,7 @@ DROP background extras, decorative props, generic scenery, and anything small or
19647
19744
  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.`;
19648
19745
  async function loadAssetText(ref, label) {
19649
19746
  const r = ref;
19650
- if (typeof r?.path === "string") return readFile7(r.path, "utf8");
19747
+ if (typeof r?.path === "string") return readFile9(r.path, "utf8");
19651
19748
  if (typeof r?.url === "string") {
19652
19749
  const res = await fetch(r.url);
19653
19750
  if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
@@ -19755,7 +19852,7 @@ async function runVisionPasses(canvas) {
19755
19852
  return fail2("read_outputs", e instanceof Error ? e.message : String(e));
19756
19853
  }
19757
19854
  }
19758
- var scaffoldStaticAdCommand = defineCommand91({
19855
+ var scaffoldStaticAdCommand = defineCommand92({
19759
19856
  meta: {
19760
19857
  name: "scaffold-static-ad",
19761
19858
  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. Edit prompt.json and drop the real assets, then `baker canvas run` it."
@@ -19800,7 +19897,7 @@ var scaffoldStaticAdCommand = defineCommand91({
19800
19897
  process.cwd(),
19801
19898
  slug
19802
19899
  );
19803
- await mkdir3(path12.dirname(outPath), { recursive: true });
19900
+ await mkdir4(path15.dirname(outPath), { recursive: true });
19804
19901
  const { describeModel, selectModel, layoutModel, genModel } = resolveModels(args);
19805
19902
  let durableSourceUrl;
19806
19903
  if (referencesDir) {
@@ -19826,7 +19923,7 @@ var scaffoldStaticAdCommand = defineCommand91({
19826
19923
  if (layout && annotated && typeof annotated === "object") {
19827
19924
  annotated.layout = layout;
19828
19925
  }
19829
- await writeFile4(blueprintPath, `${JSON.stringify(annotated, null, 2)}
19926
+ await writeFile5(blueprintPath, `${JSON.stringify(annotated, null, 2)}
19830
19927
  `, "utf8");
19831
19928
  let canvasImagePath = imageSource;
19832
19929
  let canvasImageIsUrl = imageIsUrl;
@@ -19863,10 +19960,10 @@ var scaffoldStaticAdCommand = defineCommand91({
19863
19960
  );
19864
19961
  process.exit(2);
19865
19962
  }
19866
- await writeFile4(outPath, `${JSON.stringify(canvas, null, 2)}
19963
+ await writeFile5(outPath, `${JSON.stringify(canvas, null, 2)}
19867
19964
  `, "utf8");
19868
19965
  if (definitionPath && !await fileExists(definitionPath)) {
19869
- await writeFile4(
19966
+ await writeFile5(
19870
19967
  definitionPath,
19871
19968
  buildCreativeDefinition({
19872
19969
  title: args.title ? String(args.title) : titleFromSlug(slug ?? ""),
@@ -19888,7 +19985,8 @@ var scaffoldStaticAdCommand = defineCommand91({
19888
19985
  platform: resolvePlatform(args.platform ? String(args.platform) : void 0),
19889
19986
  formats: resolveFormats(args.aspect ? String(args.aspect) : report.aspect_ratio),
19890
19987
  sourceReferenceUrl: durableSourceUrl ?? (imageIsUrl ? imageSource : void 0),
19891
- canvas
19988
+ canvas,
19989
+ sourceSha: await computeSourceSha(outPath)
19892
19990
  });
19893
19991
  }
19894
19992
  process.stdout.write(
@@ -19909,7 +20007,7 @@ var scaffoldStaticAdCommand = defineCommand91({
19909
20007
  run_estimated_credits: validation.estimatedCredits
19910
20008
  },
19911
20009
  checklist: {
19912
- edit_prompt: `Edit ${path12.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.`,
20010
+ edit_prompt: `Edit ${path15.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.`,
19913
20011
  assets_to_supply: report.elements,
19914
20012
  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)",
19915
20013
  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)",
@@ -19925,10 +20023,10 @@ var scaffoldStaticAdCommand = defineCommand91({
19925
20023
  });
19926
20024
 
19927
20025
  // src/commands/canvas/scaffold-video.ts
19928
- import { access as access2, cp, mkdir as mkdir4, readFile as readFile10, rm as rm4, writeFile as writeFile5 } from "fs/promises";
20026
+ import { access as access2, cp, mkdir as mkdir5, readFile as readFile12, rm as rm5, writeFile as writeFile6 } from "fs/promises";
19929
20027
  import { tmpdir as tmpdir2 } from "os";
19930
- import path15 from "path";
19931
- import { defineCommand as defineCommand92 } from "citty";
20028
+ import path18 from "path";
20029
+ import { defineCommand as defineCommand93 } from "citty";
19932
20030
 
19933
20031
  // src/engine/scaffold/lib/model-router.ts
19934
20032
  var SEEDANCE = "bytedance/seedance-2.0";
@@ -19966,7 +20064,7 @@ function routeVideoModel(input) {
19966
20064
 
19967
20065
  // src/engine/nodes/local/lib/sceneDetect.ts
19968
20066
  import { execFile as execFile2 } from "child_process";
19969
- import { mkdtemp, readdir as readdir3, readFile as readFile8, rm as rm3 } from "fs/promises";
20067
+ import { mkdtemp, readdir as readdir5, readFile as readFile10, rm as rm4 } from "fs/promises";
19970
20068
  import { tmpdir } from "os";
19971
20069
  import { join as join2 } from "path";
19972
20070
  import { promisify as promisify2 } from "util";
@@ -20040,11 +20138,11 @@ async function runSceneDetectOnce(filePath, threshold, minSceneLenS, timeoutMs)
20040
20138
  ],
20041
20139
  { encoding: "utf-8", maxBuffer: 32 * 1024 * 1024, timeout: timeoutMs }
20042
20140
  );
20043
- const csvName = (await readdir3(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
20141
+ const csvName = (await readdir5(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
20044
20142
  if (!csvName) return [];
20045
- return parsePySceneDetectCsvCuts(await readFile8(join2(outDir, csvName), "utf-8"));
20143
+ return parsePySceneDetectCsvCuts(await readFile10(join2(outDir, csvName), "utf-8"));
20046
20144
  } finally {
20047
- await rm3(outDir, { recursive: true, force: true });
20145
+ await rm4(outDir, { recursive: true, force: true });
20048
20146
  }
20049
20147
  }
20050
20148
  async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
@@ -20067,23 +20165,23 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
20067
20165
 
20068
20166
  // src/commands/canvas/composition-path.ts
20069
20167
  import { existsSync as existsSync3 } from "fs";
20070
- import path13 from "path";
20168
+ import path16 from "path";
20071
20169
  function resolveShippedCanvasDir(name, startDir, exists = existsSync3, maxDepth = 8) {
20072
- const rel = path13.join("canvas", name);
20170
+ const rel = path16.join("canvas", name);
20073
20171
  let dir = startDir;
20074
20172
  for (let i = 0; i < maxDepth; i++) {
20075
- const candidate = path13.join(dir, rel);
20076
- if (exists(path13.join(candidate, "meta.json"))) return candidate;
20077
- const parent = path13.dirname(dir);
20173
+ const candidate = path16.join(dir, rel);
20174
+ if (exists(path16.join(candidate, "meta.json"))) return candidate;
20175
+ const parent = path16.dirname(dir);
20078
20176
  if (parent === dir) break;
20079
20177
  dir = parent;
20080
20178
  }
20081
- return path13.resolve(startDir, "../../../", rel);
20179
+ return path16.resolve(startDir, "../../../", rel);
20082
20180
  }
20083
20181
 
20084
20182
  // src/commands/canvas/gitignore.ts
20085
- import { appendFile, readFile as readFile9 } from "fs/promises";
20086
- import path14 from "path";
20183
+ import { appendFile, readFile as readFile11 } from "fs/promises";
20184
+ import path17 from "path";
20087
20185
  function missingGitignoreEntries(existing, entries) {
20088
20186
  const present = new Set(
20089
20187
  existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
@@ -20091,10 +20189,10 @@ function missingGitignoreEntries(existing, entries) {
20091
20189
  return entries.filter((e) => !present.has(e.trim().replace(/\/+$/, "")));
20092
20190
  }
20093
20191
  async function ensureGitignore(dir, entries) {
20094
- const file = path14.join(dir, ".gitignore");
20192
+ const file = path17.join(dir, ".gitignore");
20095
20193
  let existing;
20096
20194
  try {
20097
- existing = await readFile9(file, "utf8");
20195
+ existing = await readFile11(file, "utf8");
20098
20196
  } catch {
20099
20197
  return;
20100
20198
  }
@@ -20133,7 +20231,7 @@ ONE PERSON, MULTIPLE LOOKS: if a single individual plays MULTIPLE personas or wa
20133
20231
  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.`;
20134
20232
  async function loadAssetText2(ref, label) {
20135
20233
  const r = ref;
20136
- if (typeof r?.path === "string") return readFile10(r.path, "utf8");
20234
+ if (typeof r?.path === "string") return readFile12(r.path, "utf8");
20137
20235
  if (typeof r?.url === "string") {
20138
20236
  const res = await fetch(r.url);
20139
20237
  if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
@@ -20152,7 +20250,7 @@ async function loadTranscriptBestEffort(ref) {
20152
20250
  async function stageCaptions(outDir, transcript) {
20153
20251
  const text = transcript?.trim();
20154
20252
  if (!text || text === "[]") return {};
20155
- const compositionPath = path15.join(outDir, "tiktok-captions-composition");
20253
+ const compositionPath = path18.join(outDir, "tiktok-captions-composition");
20156
20254
  await cp(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
20157
20255
  return { compositionPath };
20158
20256
  }
@@ -20170,12 +20268,12 @@ function patchCompositionHtml(html, dims) {
20170
20268
  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`);
20171
20269
  }
20172
20270
  async function stampCompositionDims(compositionDir, dims) {
20173
- const metaPath = path15.join(compositionDir, "meta.json");
20174
- const rawMeta = await readFile10(metaPath, "utf8");
20175
- await writeFile5(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
20176
- const htmlPath = path15.join(compositionDir, "index.html");
20177
- const rawHtml = await readFile10(htmlPath, "utf8");
20178
- await writeFile5(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
20271
+ const metaPath = path18.join(compositionDir, "meta.json");
20272
+ const rawMeta = await readFile12(metaPath, "utf8");
20273
+ await writeFile6(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
20274
+ const htmlPath = path18.join(compositionDir, "index.html");
20275
+ const rawHtml = await readFile12(htmlPath, "utf8");
20276
+ await writeFile6(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
20179
20277
  }
20180
20278
  function parseElements2(raw) {
20181
20279
  const parsed = JSON.parse(raw);
@@ -20222,7 +20320,7 @@ var VIDEO_EXT_BY_MIME = {
20222
20320
  "video/x-matroska": ".mkv"
20223
20321
  };
20224
20322
  function referenceVideoExt(url, contentType) {
20225
- const fromPath = path15.extname(new URL(url).pathname).toLowerCase();
20323
+ const fromPath = path18.extname(new URL(url).pathname).toLowerCase();
20226
20324
  if (fromPath && fromPath.length <= 5) return fromPath;
20227
20325
  const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase();
20228
20326
  return mime && VIDEO_EXT_BY_MIME[mime] || ".mp4";
@@ -20248,7 +20346,7 @@ function videoDefinitionDescription(blueprint) {
20248
20346
  return typeof product === "string" && product.trim() ? product.trim() : void 0;
20249
20347
  }
20250
20348
  async function materializeReferenceVideo(fileArg2) {
20251
- if (!/^https?:\/\//i.test(fileArg2)) return path15.resolve(fileArg2);
20349
+ if (!/^https?:\/\//i.test(fileArg2)) return path18.resolve(fileArg2);
20252
20350
  let res;
20253
20351
  try {
20254
20352
  res = await fetch(fileArg2);
@@ -20258,11 +20356,11 @@ async function materializeReferenceVideo(fileArg2) {
20258
20356
  if (!res.ok) throw new Error(`failed to download reference video (${res.status} ${res.statusText})`);
20259
20357
  const bytes = Buffer.from(await res.arrayBuffer());
20260
20358
  if (bytes.length === 0) throw new Error("reference video download was empty");
20261
- const dest = path15.join(
20359
+ const dest = path18.join(
20262
20360
  tmpdir2(),
20263
20361
  `baker-ref-${sha256Hex(bytes).slice(0, 16)}${referenceVideoExt(fileArg2, res.headers.get("content-type"))}`
20264
20362
  );
20265
- await writeFile5(dest, bytes);
20363
+ await writeFile6(dest, bytes);
20266
20364
  return dest;
20267
20365
  }
20268
20366
  function resolveSeamDedup(raw) {
@@ -20382,7 +20480,7 @@ async function runAnalysisPasses(deconstructCanvas, selectModel) {
20382
20480
  return fail3("deconstruct", e instanceof Error ? e.message : String(e));
20383
20481
  }
20384
20482
  }
20385
- var scaffoldVideoCommand = defineCommand92({
20483
+ var scaffoldVideoCommand = defineCommand93({
20386
20484
  meta: {
20387
20485
  name: "scaffold-video",
20388
20486
  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 prompt.json as the editable 'prompt') 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 prompt.json, drop the real source images, then `baker canvas run`."
@@ -20474,11 +20572,11 @@ var scaffoldVideoCommand = defineCommand92({
20474
20572
  } catch (e) {
20475
20573
  return fail3("download", e instanceof Error ? e.message : String(e));
20476
20574
  }
20477
- const base = path15.basename(videoPath, path15.extname(videoPath));
20478
- const outPath = args.out ? path15.resolve(String(args.out)) : slug ? path15.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path15.join(path15.dirname(videoPath), `${base}.video.canvas.json`);
20479
- const outDir = path15.dirname(outPath);
20480
- const blueprintPath = path15.join(outDir, "prompt.json");
20481
- const blueprintStylePath = path15.join(outDir, "prompt.style.json");
20575
+ const base = path18.basename(videoPath, path18.extname(videoPath));
20576
+ const outPath = args.out ? path18.resolve(String(args.out)) : slug ? path18.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path18.join(path18.dirname(videoPath), `${base}.video.canvas.json`);
20577
+ const outDir = path18.dirname(outPath);
20578
+ const blueprintPath = path18.join(outDir, "prompt.json");
20579
+ const blueprintStylePath = path18.join(outDir, "prompt.style.json");
20482
20580
  const frames = args.frames === "reuse" ? "reuse" : "generate";
20483
20581
  const maxScenes = args["max-scenes"] ? Number(args["max-scenes"]) : void 0;
20484
20582
  if (Number.isFinite(maxScenes)) {
@@ -20497,11 +20595,11 @@ var scaffoldVideoCommand = defineCommand92({
20497
20595
  shotCuts
20498
20596
  });
20499
20597
  const { blueprint, elements, transcript, creditsSpent } = await runAnalysisPasses(deconstructCanvas, selectModel);
20500
- await mkdir4(outDir, { recursive: true });
20598
+ await mkdir5(outDir, { recursive: true });
20501
20599
  const annotated = annotateBlueprintWithElements(blueprint, elements);
20502
- await writeFile5(blueprintPath, `${JSON.stringify(annotated, null, 2)}
20600
+ await writeFile6(blueprintPath, `${JSON.stringify(annotated, null, 2)}
20503
20601
  `, "utf8");
20504
- await writeFile5(blueprintStylePath, renderStyleProjectionFromValue(annotated), "utf8");
20602
+ await writeFile6(blueprintStylePath, renderStyleProjectionFromValue(annotated), "utf8");
20505
20603
  let aspect;
20506
20604
  try {
20507
20605
  aspect = resolveAspect(
@@ -20519,12 +20617,12 @@ var scaffoldVideoCommand = defineCommand92({
20519
20617
  `
20520
20618
  );
20521
20619
  }
20522
- const compositionDest = path15.join(outDir, "video-overlay-composition");
20620
+ const compositionDest = path18.join(outDir, "video-overlay-composition");
20523
20621
  await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
20524
20622
  await stampCompositionDims(compositionDest, outDims);
20525
- const indexPath = path15.join(compositionDest, "index.html");
20623
+ const indexPath = path18.join(compositionDest, "index.html");
20526
20624
  const overlayHtml = buildOverlayHtml(blueprint, { captionsActive: Boolean(transcript) });
20527
- const indexHtml = await readFile10(indexPath, "utf8");
20625
+ const indexHtml = await readFile12(indexPath, "utf8");
20528
20626
  const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
20529
20627
  if (injected === indexHtml && overlayHtml.trim()) {
20530
20628
  fail3(
@@ -20532,16 +20630,16 @@ var scaffoldVideoCommand = defineCommand92({
20532
20630
  `video-overlay-composition/index.html is missing the <!--OVERLAYS--> marker \u2014 cannot inject the overlay layer`
20533
20631
  );
20534
20632
  }
20535
- await writeFile5(indexPath, injected, "utf8");
20633
+ await writeFile6(indexPath, injected, "utf8");
20536
20634
  const captions = await stageCaptions(outDir, transcript);
20537
20635
  if (captions.compositionPath) await stampCompositionDims(captions.compositionPath, outDims);
20538
20636
  const opts = {
20539
20637
  imageModel,
20540
20638
  videoModel,
20541
- overlayCompositionPath: path15.relative(outDir, compositionDest),
20542
- captionsCompositionPath: captions.compositionPath ? path15.relative(outDir, captions.compositionPath) : void 0,
20543
- blueprintPath: path15.relative(outDir, blueprintPath),
20544
- blueprintStylePath: path15.relative(outDir, blueprintStylePath),
20639
+ overlayCompositionPath: path18.relative(outDir, compositionDest),
20640
+ captionsCompositionPath: captions.compositionPath ? path18.relative(outDir, captions.compositionPath) : void 0,
20641
+ blueprintPath: path18.relative(outDir, blueprintPath),
20642
+ blueprintStylePath: path18.relative(outDir, blueprintStylePath),
20545
20643
  frames,
20546
20644
  ambient: Boolean(args.ambient),
20547
20645
  seamDedup: resolveSeamDedup(args["seam-dedup"]),
@@ -20557,7 +20655,7 @@ var scaffoldVideoCommand = defineCommand92({
20557
20655
  return fail3("scaffold", e instanceof Error ? e.message : String(e));
20558
20656
  }
20559
20657
  if (captions.compositionPath && !canvas.nodes.some((n) => n.id === "captions")) {
20560
- await rm4(captions.compositionPath, { recursive: true, force: true });
20658
+ await rm5(captions.compositionPath, { recursive: true, force: true });
20561
20659
  }
20562
20660
  const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(canvas, outDir), defaultRegistry());
20563
20661
  if (!validation.ok) {
@@ -20566,7 +20664,7 @@ var scaffoldVideoCommand = defineCommand92({
20566
20664
  todo.blocking_validation_issues = validation.issues;
20567
20665
  meta.todo = todo;
20568
20666
  }
20569
- await writeFile5(outPath, `${JSON.stringify(canvas, null, 2)}
20667
+ await writeFile6(outPath, `${JSON.stringify(canvas, null, 2)}
20570
20668
  `, "utf8");
20571
20669
  if (!validation.ok) {
20572
20670
  process.stderr.write(
@@ -20588,9 +20686,9 @@ var scaffoldVideoCommand = defineCommand92({
20588
20686
  await ensureGitignore(process.cwd(), ["canvas/", ".context/"]);
20589
20687
  const sourceRef = videoSourceReference(blueprint, fileArg2);
20590
20688
  if (slug) {
20591
- const definitionPath = path15.join(outDir, "_definition.md");
20689
+ const definitionPath = path18.join(outDir, "_definition.md");
20592
20690
  if (!await fileExists2(definitionPath)) {
20593
- await writeFile5(
20691
+ await writeFile6(
20594
20692
  definitionPath,
20595
20693
  buildCreativeDefinition({
20596
20694
  title: titleFromSlug(slug),
@@ -20612,7 +20710,8 @@ var scaffoldVideoCommand = defineCommand92({
20612
20710
  title: titleFromSlug(slug),
20613
20711
  formats: [aspect.outAr],
20614
20712
  canvas,
20615
- sourceReferenceUrl: sourceRef.url
20713
+ sourceReferenceUrl: sourceRef.url,
20714
+ sourceSha: await computeSourceSha(outPath)
20616
20715
  });
20617
20716
  }
20618
20717
  process.stdout.write(
@@ -20636,10 +20735,13 @@ var scaffoldVideoCommand = defineCommand92({
20636
20735
  scene_count: report.scene_count,
20637
20736
  total_nodes: canvas.nodes.length,
20638
20737
  analysis_credits_spent: creditsSpent,
20639
- run_estimated_credits: validation.estimatedCredits
20738
+ run_estimated_credits: validation.estimatedCredits,
20739
+ // WHY the graph has its nodes: per-scene routes + the node-type histogram
20740
+ // (mirrors metadata.video.graph_stats in the canvas).
20741
+ graph: canvas.metadata?.video?.graph_stats
20640
20742
  },
20641
20743
  checklist: {
20642
- edit_prompt: `Edit ${path15.basename(blueprintPath)} \u2014 the blueprint deconstructed from your video; rewrite it into the ad you want (cast, palette, copy, claims). Global cast/palette/brand edits flow into every frame automatically: \`baker canvas validate\`/\`run\` regenerate ${path15.basename(blueprintStylePath)} (the projection each frame's target_blueprint reads) from it \u2014 never edit that derived file by hand. Per-scene content is edited on each frame node's own prompt.`,
20744
+ edit_prompt: `Edit ${path18.basename(blueprintPath)} \u2014 the blueprint deconstructed from your video; rewrite it into the ad you want (cast, palette, copy, claims). Global cast/palette/brand edits flow into every frame automatically: \`baker canvas validate\`/\`run\` regenerate ${path18.basename(blueprintStylePath)} (the projection each frame's target_blueprint reads) from it \u2014 never edit that derived file by hand. Per-scene content is edited on each frame node's own prompt.`,
20643
20745
  recurring_elements_to_supply: report.elements,
20644
20746
  voices_to_confirm: report.dialogue.map((d) => ({
20645
20747
  scene: d.scene,
@@ -20676,9 +20778,9 @@ var scaffoldVideoCommand = defineCommand92({
20676
20778
  });
20677
20779
 
20678
20780
  // src/commands/canvas/set-prompt.ts
20679
- import { readFile as readFile11, writeFile as writeFile6 } from "fs/promises";
20680
- import path16 from "path";
20681
- import { defineCommand as defineCommand93 } from "citty";
20781
+ import { readFile as readFile13, writeFile as writeFile7 } from "fs/promises";
20782
+ import path19 from "path";
20783
+ import { defineCommand as defineCommand94 } from "citty";
20682
20784
  function setNodePrompt(canvas, nodeId, text) {
20683
20785
  const nodes = canvas?.nodes;
20684
20786
  if (!Array.isArray(nodes)) throw new Error("canvas has no nodes array");
@@ -20693,7 +20795,7 @@ function setNodePrompt(canvas, nodeId, text) {
20693
20795
  newNodes[idx] = newNode;
20694
20796
  return { ...canvas, nodes: newNodes };
20695
20797
  }
20696
- var setPromptCommand = defineCommand93({
20798
+ var setPromptCommand = defineCommand94({
20697
20799
  meta: {
20698
20800
  name: "set-prompt",
20699
20801
  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."
@@ -20705,17 +20807,17 @@ var setPromptCommand = defineCommand93({
20705
20807
  "text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
20706
20808
  },
20707
20809
  async run({ args }) {
20708
- const filePath = path16.resolve(String(args.file));
20810
+ const filePath = path19.resolve(String(args.file));
20709
20811
  let canvas;
20710
20812
  try {
20711
- canvas = JSON.parse(await readFile11(filePath, "utf8"));
20813
+ canvas = JSON.parse(await readFile13(filePath, "utf8"));
20712
20814
  } catch (e) {
20713
20815
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "parse", message: String(e) } }, null, 2)}
20714
20816
  `);
20715
20817
  process.exit(2);
20716
20818
  }
20717
20819
  let text;
20718
- if (args["text-file"]) text = await readFile11(path16.resolve(String(args["text-file"])), "utf8");
20820
+ if (args["text-file"]) text = await readFile13(path19.resolve(String(args["text-file"])), "utf8");
20719
20821
  else if (args.text !== void 0) text = String(args.text);
20720
20822
  else {
20721
20823
  process.stderr.write(
@@ -20736,14 +20838,14 @@ var setPromptCommand = defineCommand93({
20736
20838
  process.exit(2);
20737
20839
  return;
20738
20840
  }
20739
- const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path16.dirname(filePath)), defaultRegistry());
20841
+ const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path19.dirname(filePath)), defaultRegistry());
20740
20842
  if (!validation.ok) {
20741
20843
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
20742
20844
  `);
20743
20845
  process.exit(2);
20744
20846
  return;
20745
20847
  }
20746
- await writeFile6(filePath, `${JSON.stringify(updated, null, 2)}
20848
+ await writeFile7(filePath, `${JSON.stringify(updated, null, 2)}
20747
20849
  `, "utf8");
20748
20850
  process.stdout.write(`${JSON.stringify({ ok: true, node: String(args.node), bytes: text.length }, null, 2)}
20749
20851
  `);
@@ -20751,18 +20853,18 @@ var setPromptCommand = defineCommand93({
20751
20853
  });
20752
20854
 
20753
20855
  // src/commands/canvas/validate.ts
20754
- import { readFile as readFile12 } from "fs/promises";
20755
- import path17 from "path";
20756
- import { defineCommand as defineCommand94 } from "citty";
20757
- var validateCommand = defineCommand94({
20856
+ import { readFile as readFile14 } from "fs/promises";
20857
+ import path20 from "path";
20858
+ import { defineCommand as defineCommand95 } from "citty";
20859
+ var validateCommand = defineCommand95({
20758
20860
  meta: {
20759
20861
  name: "validate",
20760
20862
  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)."
20761
20863
  },
20762
20864
  args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
20763
20865
  async run({ args }) {
20764
- const filePath = path17.resolve(String(args.file));
20765
- const raw = await readFile12(filePath, "utf8");
20866
+ const filePath = path20.resolve(String(args.file));
20867
+ const raw = await readFile14(filePath, "utf8");
20766
20868
  let parsed;
20767
20869
  try {
20768
20870
  parsed = JSON.parse(raw);
@@ -20772,7 +20874,7 @@ var validateCommand = defineCommand94({
20772
20874
  `);
20773
20875
  process.exit(2);
20774
20876
  }
20775
- parsed = resolveRelativeCanvasPaths(parsed, path17.dirname(filePath));
20877
+ parsed = resolveRelativeCanvasPaths(parsed, path20.dirname(filePath));
20776
20878
  let styleProjection = "not_applicable";
20777
20879
  try {
20778
20880
  styleProjection = await syncStyleProjection(parsed, (line) => process.stderr.write(`${line}
@@ -20811,7 +20913,7 @@ var validateCommand = defineCommand94({
20811
20913
  });
20812
20914
 
20813
20915
  // src/commands/canvas/index.ts
20814
- var canvasCommand = defineCommand95({
20916
+ var canvasCommand = defineCommand96({
20815
20917
  meta: {
20816
20918
  name: "canvas",
20817
20919
  description: `Run Baker creative canvas JSON files locally. Local nodes execute in-process; remote nodes POST to the Convex backend gateway.
@@ -20823,6 +20925,7 @@ Subcommands:
20823
20925
  baker canvas critique <file.json> \u2014 pre-spend creative critic (advisory): video hook/legibility/retention or static text/grounding/type scores, never blocks
20824
20926
  baker canvas run <file.json> \u2014 execute the canvas, write outputs to ./canvas/<run_id>/
20825
20927
  baker canvas rerun <slug> \u2014 restore a creative's latest recorded canvas from run history and run it (works in a fresh workspace)
20928
+ baker canvas sync-definition <path> \u2014 push a creative's current node-graph + input thumbnails to the dashboard without rendering (runs automatically on canvas/prompt edits)
20826
20929
  baker canvas catalog \u2014 print the agent-facing node + composition catalog (JSON Schema)
20827
20930
  baker canvas inspect <run_id> \u2014 one-page summary of a completed run
20828
20931
  baker canvas scaffold-video <video> \u2014 turn a reference video into a runnable reproduction canvas (deconstruct + recurring-element detection)
@@ -20837,18 +20940,19 @@ Subcommands:
20837
20940
  inspect: inspectCommand,
20838
20941
  "scaffold-video": scaffoldVideoCommand,
20839
20942
  "scaffold-static-ad": scaffoldStaticAdCommand,
20840
- "set-prompt": setPromptCommand
20943
+ "set-prompt": setPromptCommand,
20944
+ "sync-definition": syncDefinitionCommand
20841
20945
  }
20842
20946
  });
20843
20947
 
20844
20948
  // src/commands/creatives/index.ts
20845
- import { defineCommand as defineCommand97 } from "citty";
20949
+ import { defineCommand as defineCommand98 } from "citty";
20846
20950
 
20847
20951
  // src/commands/creatives/publish.ts
20848
- import { defineCommand as defineCommand96 } from "citty";
20952
+ import { defineCommand as defineCommand97 } from "citty";
20849
20953
 
20850
20954
  // src/commands/images/api.ts
20851
- import { readFile as readFile13 } from "fs/promises";
20955
+ import { readFile as readFile15 } from "fs/promises";
20852
20956
  import { extname } from "path";
20853
20957
  var imageProcessingTimeoutMs = 18e4;
20854
20958
  var imageReadyPollIntervalMs = 2e3;
@@ -20862,7 +20966,7 @@ var mimeMap = {
20862
20966
  ".avif": "image/avif"
20863
20967
  };
20864
20968
  var defaultImageApiDeps = {
20865
- readFile: readFile13,
20969
+ readFile: readFile15,
20866
20970
  post: apiPost,
20867
20971
  get: apiGet,
20868
20972
  sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
@@ -20989,7 +21093,7 @@ async function publishCreative(args, deps = defaultImageApiDeps) {
20989
21093
  chatId: chatIdFromEnv()
20990
21094
  });
20991
21095
  }
20992
- var publishCommand = defineCommand96({
21096
+ var publishCommand = defineCommand97({
20993
21097
  meta: {
20994
21098
  name: "publish",
20995
21099
  description: "Publish a final static creative image to Baker Creatives and print the creative reference JSON."
@@ -21047,7 +21151,7 @@ var publishCommand = defineCommand96({
21047
21151
  });
21048
21152
 
21049
21153
  // src/commands/creatives/index.ts
21050
- var creativesCommand3 = defineCommand97({
21154
+ var creativesCommand3 = defineCommand98({
21051
21155
  meta: {
21052
21156
  name: "creatives",
21053
21157
  description: `Publish static ad creatives as first-class Baker outputs.
@@ -21063,7 +21167,7 @@ Publishing uploads the image to the Company image library, applies the official
21063
21167
  });
21064
21168
 
21065
21169
  // src/commands/flows/index.ts
21066
- import { defineCommand as defineCommand98 } from "citty";
21170
+ import { defineCommand as defineCommand99 } from "citty";
21067
21171
 
21068
21172
  // src/commands/flows/shared.ts
21069
21173
  import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync9 } from "fs";
@@ -21096,12 +21200,12 @@ function listFlowSlugs() {
21096
21200
  return readdirSync2(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith("_") && entry.name !== ".gitkeep").map((entry) => entry.name).sort();
21097
21201
  }
21098
21202
  function readFlowTree(slug) {
21099
- const path18 = join3(flowsDir(), slug, "_data.json");
21100
- if (!existsSync4(path18)) {
21203
+ const path21 = join3(flowsDir(), slug, "_data.json");
21204
+ if (!existsSync4(path21)) {
21101
21205
  failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
21102
21206
  }
21103
21207
  try {
21104
- return JSON.parse(readFileSync9(path18, "utf-8"));
21208
+ return JSON.parse(readFileSync9(path21, "utf-8"));
21105
21209
  } catch (error) {
21106
21210
  failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
21107
21211
  }
@@ -21218,7 +21322,7 @@ function displayName(slug) {
21218
21322
  const name = tree.displayName;
21219
21323
  return typeof name === "string" && name.trim() ? name.trim() : slug;
21220
21324
  }
21221
- var listCommand5 = defineCommand98({
21325
+ var listCommand5 = defineCommand99({
21222
21326
  meta: {
21223
21327
  name: "list",
21224
21328
  description: "List Forms in this workspace with the count of confidential fields still needing setup. Example: baker flows list"
@@ -21232,7 +21336,7 @@ var listCommand5 = defineCommand98({
21232
21336
  writeJson({ ok: true, data: { flows } });
21233
21337
  }
21234
21338
  });
21235
- var showCommand2 = defineCommand98({
21339
+ var showCommand2 = defineCommand99({
21236
21340
  meta: {
21237
21341
  name: "show",
21238
21342
  description: "Show a Form's confidential fields and their configuration status (never secret values). Example: baker flows show contact"
@@ -21253,7 +21357,7 @@ var showCommand2 = defineCommand98({
21253
21357
  writeJson({ ok: true, data: response });
21254
21358
  }
21255
21359
  });
21256
- var flowsCommand = defineCommand98({
21360
+ var flowsCommand = defineCommand99({
21257
21361
  meta: {
21258
21362
  name: "flows",
21259
21363
  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).
@@ -21280,10 +21384,10 @@ Examples:
21280
21384
  });
21281
21385
 
21282
21386
  // src/commands/ga4/index.ts
21283
- import { defineCommand as defineCommand102 } from "citty";
21387
+ import { defineCommand as defineCommand103 } from "citty";
21284
21388
 
21285
21389
  // src/commands/ga4/audit.ts
21286
- import { defineCommand as defineCommand99 } from "citty";
21390
+ import { defineCommand as defineCommand100 } from "citty";
21287
21391
 
21288
21392
  // src/commands/ga4/resolve.ts
21289
21393
  async function fetchProperties(useCache = true) {
@@ -21346,7 +21450,7 @@ registerSchema({
21346
21450
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
21347
21451
  }
21348
21452
  });
21349
- var auditCommand2 = defineCommand99({
21453
+ var auditCommand2 = defineCommand100({
21350
21454
  meta: {
21351
21455
  name: "audit",
21352
21456
  description: `Run all GA4 admin health checks. Returns property config with playbook warnings.
@@ -21398,7 +21502,7 @@ Examples:
21398
21502
  });
21399
21503
 
21400
21504
  // src/commands/ga4/properties.ts
21401
- import { defineCommand as defineCommand100 } from "citty";
21505
+ import { defineCommand as defineCommand101 } from "citty";
21402
21506
  registerSchema({
21403
21507
  command: "ga4.properties",
21404
21508
  description: "List all accessible GA4 properties. Returns property IDs needed for query and audit commands. Run this first to find property IDs.",
@@ -21406,7 +21510,7 @@ registerSchema({
21406
21510
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
21407
21511
  }
21408
21512
  });
21409
- var propertiesCommand = defineCommand100({
21513
+ var propertiesCommand = defineCommand101({
21410
21514
  meta: {
21411
21515
  name: "properties",
21412
21516
  description: `List accessible GA4 properties.
@@ -21456,7 +21560,7 @@ Examples:
21456
21560
  // src/commands/ga4/query.ts
21457
21561
  import { appendFileSync as appendFileSync2, existsSync as existsSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync3 } from "fs";
21458
21562
  import { resolve as resolve2 } from "path";
21459
- import { defineCommand as defineCommand101 } from "citty";
21563
+ import { defineCommand as defineCommand102 } from "citty";
21460
21564
 
21461
21565
  // src/commands/ga4/presets.ts
21462
21566
  var GA4_PRESETS = [
@@ -21588,7 +21692,7 @@ function handleError(err) {
21588
21692
  });
21589
21693
  process.exit(1);
21590
21694
  }
21591
- var queryCommand2 = defineCommand101({
21695
+ var queryCommand2 = defineCommand102({
21592
21696
  meta: {
21593
21697
  name: "query",
21594
21698
  description: `Run GA4 Data API reports. Preset-first with free-form escape hatch.
@@ -21659,7 +21763,7 @@ Free-form (escape hatch):
21659
21763
  });
21660
21764
 
21661
21765
  // src/commands/ga4/index.ts
21662
- var ga4Command = defineCommand102({
21766
+ var ga4Command = defineCommand103({
21663
21767
  meta: {
21664
21768
  name: "ga4",
21665
21769
  description: `Google Analytics 4 commands. Audit property config, run playbook-aligned reports.
@@ -21682,12 +21786,12 @@ Examples:
21682
21786
  });
21683
21787
 
21684
21788
  // src/commands/gsc/index.ts
21685
- import { defineCommand as defineCommand106 } from "citty";
21789
+ import { defineCommand as defineCommand107 } from "citty";
21686
21790
 
21687
21791
  // src/commands/gsc/query.ts
21688
21792
  import { appendFileSync as appendFileSync3, existsSync as existsSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "fs";
21689
21793
  import { resolve as resolve3 } from "path";
21690
- import { defineCommand as defineCommand103 } from "citty";
21794
+ import { defineCommand as defineCommand104 } from "citty";
21691
21795
 
21692
21796
  // src/commands/gsc/presets.ts
21693
21797
  var GSC_PRESETS = [
@@ -21875,7 +21979,7 @@ function handleError2(err) {
21875
21979
  });
21876
21980
  process.exit(1);
21877
21981
  }
21878
- var queryCommand3 = defineCommand103({
21982
+ var queryCommand3 = defineCommand104({
21879
21983
  meta: {
21880
21984
  name: "query",
21881
21985
  description: `Run GSC Search Analytics queries. Preset-first with free-form escape hatch.
@@ -21953,7 +22057,7 @@ Free-form (escape hatch):
21953
22057
  });
21954
22058
 
21955
22059
  // src/commands/gsc/sitemaps.ts
21956
- import { defineCommand as defineCommand104 } from "citty";
22060
+ import { defineCommand as defineCommand105 } from "citty";
21957
22061
  registerSchema({
21958
22062
  command: "gsc.sitemaps",
21959
22063
  description: "List sitemaps for a Search Console site. Check sitemap health and errors.",
@@ -21962,7 +22066,7 @@ registerSchema({
21962
22066
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
21963
22067
  }
21964
22068
  });
21965
- var sitemapsCommand = defineCommand104({
22069
+ var sitemapsCommand = defineCommand105({
21966
22070
  meta: {
21967
22071
  name: "sitemaps",
21968
22072
  description: `List sitemaps for a site. Check health and errors.
@@ -22012,7 +22116,7 @@ Examples:
22012
22116
  });
22013
22117
 
22014
22118
  // src/commands/gsc/sites.ts
22015
- import { defineCommand as defineCommand105 } from "citty";
22119
+ import { defineCommand as defineCommand106 } from "citty";
22016
22120
  registerSchema({
22017
22121
  command: "gsc.sites",
22018
22122
  description: "List all verified Google Search Console sites. Returns site URLs needed for query and sitemaps commands.",
@@ -22020,7 +22124,7 @@ registerSchema({
22020
22124
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
22021
22125
  }
22022
22126
  });
22023
- var sitesCommand = defineCommand105({
22127
+ var sitesCommand = defineCommand106({
22024
22128
  meta: {
22025
22129
  name: "sites",
22026
22130
  description: `List verified Search Console sites.
@@ -22068,7 +22172,7 @@ Examples:
22068
22172
  });
22069
22173
 
22070
22174
  // src/commands/gsc/index.ts
22071
- var gscCommand = defineCommand106({
22175
+ var gscCommand = defineCommand107({
22072
22176
  meta: {
22073
22177
  name: "gsc",
22074
22178
  description: `Google Search Console commands. PPC-SEO arbitrage, brand halo analysis, negative keyword discovery.
@@ -22091,10 +22195,10 @@ Examples:
22091
22195
  });
22092
22196
 
22093
22197
  // src/commands/images/index.ts
22094
- import { defineCommand as defineCommand130 } from "citty";
22198
+ import { defineCommand as defineCommand131 } from "citty";
22095
22199
 
22096
22200
  // src/commands/images/crop.ts
22097
- import { defineCommand as defineCommand107 } from "citty";
22201
+ import { defineCommand as defineCommand108 } from "citty";
22098
22202
 
22099
22203
  // src/lib/image/crop-sprite.ts
22100
22204
  import sharp from "sharp";
@@ -22109,7 +22213,7 @@ function cropSprite(input, region) {
22109
22213
 
22110
22214
  // src/lib/image/io.ts
22111
22215
  import { randomBytes } from "crypto";
22112
- import { glob as fsGlob, readFile as readFile14, rename, stat as stat3, writeFile as writeFile7 } from "fs/promises";
22216
+ import { glob as fsGlob, readFile as readFile16, rename, stat as stat4, writeFile as writeFile8 } from "fs/promises";
22113
22217
  import { dirname as dirname2, extname as extname2, join as join4, resolve as resolve4 } from "path";
22114
22218
  var REMOTE_RE = /^https?:\/\//i;
22115
22219
  var GLOB_RE = /[*?[\]{}]/;
@@ -22145,11 +22249,11 @@ async function readImageBuffer(pathOrUrl) {
22145
22249
  }
22146
22250
  return Buffer.from(await response.arrayBuffer());
22147
22251
  }
22148
- return readFile14(pathOrUrl);
22252
+ return readFile16(pathOrUrl);
22149
22253
  }
22150
- async function isDirectory(path18) {
22254
+ async function isDirectory(path21) {
22151
22255
  try {
22152
- const s = await stat3(path18);
22256
+ const s = await stat4(path21);
22153
22257
  return s.isDirectory();
22154
22258
  } catch {
22155
22259
  return false;
@@ -22168,7 +22272,7 @@ async function atomicWrite(targetPath, data) {
22168
22272
  const absolute = resolve4(targetPath);
22169
22273
  const dir = dirname2(absolute);
22170
22274
  const tmp = join4(dir, `.baker-image-${randomBytes(8).toString("hex")}.tmp`);
22171
- await writeFile7(tmp, data);
22275
+ await writeFile8(tmp, data);
22172
22276
  await rename(tmp, absolute);
22173
22277
  }
22174
22278
 
@@ -22219,7 +22323,7 @@ function emitError2(err) {
22219
22323
  }
22220
22324
  process.exit(1);
22221
22325
  }
22222
- var cropCommand = defineCommand107({
22326
+ var cropCommand = defineCommand108({
22223
22327
  meta: {
22224
22328
  name: "crop",
22225
22329
  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"
@@ -22255,7 +22359,7 @@ var cropCommand = defineCommand107({
22255
22359
  });
22256
22360
 
22257
22361
  // src/commands/images/delete.ts
22258
- import { defineCommand as defineCommand108 } from "citty";
22362
+ import { defineCommand as defineCommand109 } from "citty";
22259
22363
  registerSchema({
22260
22364
  command: "images.delete",
22261
22365
  description: "Delete an image by ID",
@@ -22269,7 +22373,7 @@ registerSchema({
22269
22373
  }
22270
22374
  }
22271
22375
  });
22272
- var deleteCommand = defineCommand108({
22376
+ var deleteCommand = defineCommand109({
22273
22377
  meta: {
22274
22378
  name: "delete",
22275
22379
  description: "Delete an image by ID. Use --dry-run to preview. Example: baker images delete j571abc123 --dry-run"
@@ -22310,7 +22414,7 @@ var deleteCommand = defineCommand108({
22310
22414
  });
22311
22415
 
22312
22416
  // src/commands/images/dimensions.ts
22313
- import { defineCommand as defineCommand109 } from "citty";
22417
+ import { defineCommand as defineCommand110 } from "citty";
22314
22418
 
22315
22419
  // src/lib/image/dimensions.ts
22316
22420
  import { imageSize } from "image-size";
@@ -22333,7 +22437,7 @@ registerSchema({
22333
22437
  target: { type: "string", description: "Local file path or remote http(s) URL", required: true }
22334
22438
  }
22335
22439
  });
22336
- var dimensionsCommand = defineCommand109({
22440
+ var dimensionsCommand = defineCommand110({
22337
22441
  meta: {
22338
22442
  name: "dimensions",
22339
22443
  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"
@@ -22377,7 +22481,7 @@ var dimensionsCommand = defineCommand109({
22377
22481
  });
22378
22482
 
22379
22483
  // src/commands/images/extract.ts
22380
- import { defineCommand as defineCommand110 } from "citty";
22484
+ import { defineCommand as defineCommand111 } from "citty";
22381
22485
  registerSchema({
22382
22486
  command: "images.extract",
22383
22487
  description: "Extract images from a URL via Firecrawl (formats: images).",
@@ -22393,7 +22497,7 @@ registerSchema({
22393
22497
  }
22394
22498
  }
22395
22499
  });
22396
- var extractCommand = defineCommand110({
22500
+ var extractCommand = defineCommand111({
22397
22501
  meta: {
22398
22502
  name: "extract",
22399
22503
  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"
@@ -22431,7 +22535,7 @@ var extractCommand = defineCommand110({
22431
22535
  });
22432
22536
 
22433
22537
  // src/commands/images/find.ts
22434
- import { defineCommand as defineCommand111 } from "citty";
22538
+ import { defineCommand as defineCommand112 } from "citty";
22435
22539
  registerSchema({
22436
22540
  command: "images.find",
22437
22541
  description: "Fanout image search: library first, then opted-in external providers.",
@@ -22463,7 +22567,7 @@ registerSchema({
22463
22567
  }
22464
22568
  }
22465
22569
  });
22466
- var findCommand = defineCommand111({
22570
+ var findCommand = defineCommand112({
22467
22571
  meta: {
22468
22572
  name: "find",
22469
22573
  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"
@@ -22511,8 +22615,8 @@ var findCommand = defineCommand111({
22511
22615
  });
22512
22616
 
22513
22617
  // src/commands/images/generate.ts
22514
- import { readFile as readFile15 } from "fs/promises";
22515
- import { defineCommand as defineCommand112 } from "citty";
22618
+ import { readFile as readFile17 } from "fs/promises";
22619
+ import { defineCommand as defineCommand113 } from "citty";
22516
22620
  import sharp2 from "sharp";
22517
22621
  var GENERATE_TIMEOUT_MS = 18e4;
22518
22622
  var REFERENCE_MAX_EDGE = 1536;
@@ -22601,7 +22705,7 @@ async function resolveReferences(spec) {
22601
22705
  }
22602
22706
  let raw;
22603
22707
  try {
22604
- raw = await readFile15(entry);
22708
+ raw = await readFile17(entry);
22605
22709
  } catch {
22606
22710
  throw new ApiError("VALIDATION_ERROR", `Reference file not found: ${entry}`);
22607
22711
  }
@@ -22615,7 +22719,7 @@ async function resolveReferences(spec) {
22615
22719
  }
22616
22720
  return out;
22617
22721
  }
22618
- var generateCommand = defineCommand112({
22722
+ var generateCommand = defineCommand113({
22619
22723
  meta: {
22620
22724
  name: "generate",
22621
22725
  description: "Generate an image with AI and store it in the library (cost-tracked per request via OpenRouter usage). Models mirror the canvas: openai/gpt-5.4-image-2 (default \u2014 photoreal, cleanest text, best for ad/landing reproduction), google/gemini-3-pro-image-preview (Nano Banana Pro), google/gemini-3.5-flash & google/gemini-3.1-flash-image-preview (fast, extreme aspect ratios), 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 google/gemini-3-pro-image-preview --image-size 2K\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]]'"
@@ -22667,7 +22771,7 @@ var generateCommand = defineCommand112({
22667
22771
  });
22668
22772
 
22669
22773
  // src/commands/images/get.ts
22670
- import { defineCommand as defineCommand113 } from "citty";
22774
+ import { defineCommand as defineCommand114 } from "citty";
22671
22775
  registerSchema({
22672
22776
  command: "images.get",
22673
22777
  description: "Get a single image by ID",
@@ -22675,7 +22779,7 @@ registerSchema({
22675
22779
  id: { type: "string", description: "Image ID", required: true }
22676
22780
  }
22677
22781
  });
22678
- var getCommand2 = defineCommand113({
22782
+ var getCommand2 = defineCommand114({
22679
22783
  meta: { name: "get", description: "Get a single image by ID. Example: baker images get j571abc123" },
22680
22784
  args: {
22681
22785
  id: { type: "positional", description: "Image ID", required: false },
@@ -22711,7 +22815,7 @@ var getCommand2 = defineCommand113({
22711
22815
  });
22712
22816
 
22713
22817
  // src/commands/images/gif.ts
22714
- import { defineCommand as defineCommand114 } from "citty";
22818
+ import { defineCommand as defineCommand115 } from "citty";
22715
22819
  registerSchema({
22716
22820
  command: "images.gif",
22717
22821
  description: "Search Giphy for GIFs / reaction memes (paid social creative).",
@@ -22743,7 +22847,7 @@ registerSchema({
22743
22847
  }
22744
22848
  }
22745
22849
  });
22746
- var gifCommand = defineCommand114({
22850
+ var gifCommand = defineCommand115({
22747
22851
  meta: {
22748
22852
  name: "gif",
22749
22853
  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"
@@ -22790,7 +22894,7 @@ var gifCommand = defineCommand114({
22790
22894
  });
22791
22895
 
22792
22896
  // src/commands/images/google.ts
22793
- import { defineCommand as defineCommand115 } from "citty";
22897
+ import { defineCommand as defineCommand116 } from "citty";
22794
22898
  registerSchema({
22795
22899
  command: "images.google",
22796
22900
  description: "Google Images search via the official Custom Search JSON API. Unverified source \u2014 inspect before placing.",
@@ -22826,7 +22930,7 @@ registerSchema({
22826
22930
  }
22827
22931
  }
22828
22932
  });
22829
- var googleCommand2 = defineCommand115({
22933
+ var googleCommand2 = defineCommand116({
22830
22934
  meta: {
22831
22935
  name: "google",
22832
22936
  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"
@@ -22874,7 +22978,7 @@ var googleCommand2 = defineCommand115({
22874
22978
  });
22875
22979
 
22876
22980
  // src/commands/images/icon.ts
22877
- import { defineCommand as defineCommand116 } from "citty";
22981
+ import { defineCommand as defineCommand117 } from "citty";
22878
22982
  registerSchema({
22879
22983
  command: "images.icon",
22880
22984
  description: "Icon lookup via Iconify (200+ icon sets, free CDN).",
@@ -22900,7 +23004,7 @@ registerSchema({
22900
23004
  }
22901
23005
  }
22902
23006
  });
22903
- var iconCommand = defineCommand116({
23007
+ var iconCommand = defineCommand117({
22904
23008
  meta: {
22905
23009
  name: "icon",
22906
23010
  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'"
@@ -22940,7 +23044,7 @@ var iconCommand = defineCommand116({
22940
23044
  });
22941
23045
 
22942
23046
  // src/commands/images/ingest.ts
22943
- import { defineCommand as defineCommand117 } from "citty";
23047
+ import { defineCommand as defineCommand118 } from "citty";
22944
23048
  registerSchema({
22945
23049
  command: "images.ingest",
22946
23050
  description: "Ingest a remote image URL into the library (full describe + embed).",
@@ -22952,7 +23056,7 @@ registerSchema({
22952
23056
  context: { type: "string", description: "Description context hint", required: false }
22953
23057
  }
22954
23058
  });
22955
- var ingestCommand = defineCommand117({
23059
+ var ingestCommand = defineCommand118({
22956
23060
  meta: {
22957
23061
  name: "ingest",
22958
23062
  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"
@@ -22994,7 +23098,7 @@ var ingestCommand = defineCommand117({
22994
23098
  });
22995
23099
 
22996
23100
  // src/commands/images/library.ts
22997
- import { defineCommand as defineCommand118 } from "citty";
23101
+ import { defineCommand as defineCommand119 } from "citty";
22998
23102
  registerSchema({
22999
23103
  command: "images.library",
23000
23104
  description: "Search the company image library. Returns only ready images.",
@@ -23020,7 +23124,7 @@ registerSchema({
23020
23124
  }
23021
23125
  }
23022
23126
  });
23023
- var libraryCommand = defineCommand118({
23127
+ var libraryCommand = defineCommand119({
23024
23128
  meta: {
23025
23129
  name: "library",
23026
23130
  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"
@@ -23077,7 +23181,7 @@ var libraryCommand = defineCommand118({
23077
23181
  });
23078
23182
 
23079
23183
  // src/commands/images/logo.ts
23080
- import { defineCommand as defineCommand119 } from "citty";
23184
+ import { defineCommand as defineCommand120 } from "citty";
23081
23185
  registerSchema({
23082
23186
  command: "images.logo",
23083
23187
  description: "Brand logo lookup via Brandfetch CDN (fallback/404). Auto-ingests by default.",
@@ -23102,7 +23206,7 @@ registerSchema({
23102
23206
  }
23103
23207
  }
23104
23208
  });
23105
- var logoCommand = defineCommand119({
23209
+ var logoCommand = defineCommand120({
23106
23210
  meta: {
23107
23211
  name: "logo",
23108
23212
  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"
@@ -23140,7 +23244,7 @@ var logoCommand = defineCommand119({
23140
23244
  });
23141
23245
 
23142
23246
  // src/commands/images/normalize.ts
23143
- import { defineCommand as defineCommand120 } from "citty";
23247
+ import { defineCommand as defineCommand121 } from "citty";
23144
23248
 
23145
23249
  // src/lib/image/color-changer.ts
23146
23250
  import quantize from "quantize";
@@ -23872,7 +23976,7 @@ function coerceRawArgs(args) {
23872
23976
  "dry-run": bool(args["dry-run"])
23873
23977
  };
23874
23978
  }
23875
- var normalizeCommand = defineCommand120({
23979
+ var normalizeCommand = defineCommand121({
23876
23980
  meta: {
23877
23981
  name: "normalize",
23878
23982
  description: `Normalize logos / images: declarative recolor + bg removal + trim + resize. Operates on local files; writes in-place by default.
@@ -23927,7 +24031,7 @@ Examples:
23927
24031
  });
23928
24032
 
23929
24033
  // src/commands/images/pinterest.ts
23930
- import { defineCommand as defineCommand121 } from "citty";
24034
+ import { defineCommand as defineCommand122 } from "citty";
23931
24035
  registerSchema({
23932
24036
  command: "images.pinterest",
23933
24037
  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.",
@@ -23947,7 +24051,7 @@ registerSchema({
23947
24051
  }
23948
24052
  }
23949
24053
  });
23950
- var pinterestCommand = defineCommand121({
24054
+ var pinterestCommand = defineCommand122({
23951
24055
  meta: {
23952
24056
  name: "pinterest",
23953
24057
  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'"
@@ -23987,7 +24091,7 @@ var pinterestCommand = defineCommand121({
23987
24091
  });
23988
24092
 
23989
24093
  // src/commands/images/screenshot.ts
23990
- import { defineCommand as defineCommand122 } from "citty";
24094
+ import { defineCommand as defineCommand123 } from "citty";
23991
24095
  registerSchema({
23992
24096
  command: "images.screenshot",
23993
24097
  description: "Capture a website screenshot via ScreenshotOne. Auto-ingests on success.",
@@ -24003,7 +24107,7 @@ registerSchema({
24003
24107
  }
24004
24108
  }
24005
24109
  });
24006
- var screenshotCommand = defineCommand122({
24110
+ var screenshotCommand = defineCommand123({
24007
24111
  meta: {
24008
24112
  name: "screenshot",
24009
24113
  description: "Screenshot a URL via ScreenshotOne. $0.009/capture. Auto-ingests to library.\n\nExample: baker images screenshot https://stripe.com --full-page"
@@ -24066,7 +24170,7 @@ var screenshotCommand = defineCommand122({
24066
24170
  });
24067
24171
 
24068
24172
  // src/commands/images/search.ts
24069
- import { defineCommand as defineCommand123 } from "citty";
24173
+ import { defineCommand as defineCommand124 } from "citty";
24070
24174
  registerSchema({
24071
24175
  command: "images.search",
24072
24176
  description: "Search images by text query. Only returns ready images.",
@@ -24082,7 +24186,7 @@ registerSchema({
24082
24186
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
24083
24187
  }
24084
24188
  });
24085
- var searchCommand = defineCommand123({
24189
+ var searchCommand = defineCommand124({
24086
24190
  meta: {
24087
24191
  name: "search",
24088
24192
  description: "Semantic search images by text query. Uses hybrid BM25 + vector + reranking. Example: baker images search 'hero banner' --aspect-ratio 16:9 --tags logo"
@@ -24142,7 +24246,7 @@ var searchCommand = defineCommand123({
24142
24246
  });
24143
24247
 
24144
24248
  // src/commands/images/sticker.ts
24145
- import { defineCommand as defineCommand124 } from "citty";
24249
+ import { defineCommand as defineCommand125 } from "citty";
24146
24250
  registerSchema({
24147
24251
  command: "images.sticker",
24148
24252
  description: "Search Giphy stickers \u2014 transparent-background overlays for ad creative.",
@@ -24174,7 +24278,7 @@ registerSchema({
24174
24278
  }
24175
24279
  }
24176
24280
  });
24177
- var stickerCommand = defineCommand124({
24281
+ var stickerCommand = defineCommand125({
24178
24282
  meta: {
24179
24283
  name: "sticker",
24180
24284
  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"
@@ -24221,7 +24325,7 @@ var stickerCommand = defineCommand124({
24221
24325
  });
24222
24326
 
24223
24327
  // src/commands/images/stock.ts
24224
- import { defineCommand as defineCommand125 } from "citty";
24328
+ import { defineCommand as defineCommand126 } from "citty";
24225
24329
  registerSchema({
24226
24330
  command: "images.stock",
24227
24331
  description: "Stock photo, vector illustration, icon-set, and PSD search via Magnific (Freepik's developer API).",
@@ -24279,7 +24383,7 @@ registerSchema({
24279
24383
  }
24280
24384
  }
24281
24385
  });
24282
- var stockCommand = defineCommand125({
24386
+ var stockCommand = defineCommand126({
24283
24387
  meta: {
24284
24388
  name: "stock",
24285
24389
  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"
@@ -24335,7 +24439,7 @@ var stockCommand = defineCommand125({
24335
24439
  });
24336
24440
 
24337
24441
  // src/lib/tags-command.ts
24338
- import { defineCommand as defineCommand126 } from "citty";
24442
+ import { defineCommand as defineCommand127 } from "citty";
24339
24443
  function makeTagsCommand(command, label, endpoint) {
24340
24444
  registerSchema({
24341
24445
  command: `${command}.tags`,
@@ -24344,7 +24448,7 @@ function makeTagsCommand(command, label, endpoint) {
24344
24448
  output: { type: "string", description: "Output format: md|json", required: false, default: "md" }
24345
24449
  }
24346
24450
  });
24347
- return defineCommand126({
24451
+ return defineCommand127({
24348
24452
  meta: {
24349
24453
  name: "tags",
24350
24454
  description: `List the available ${label} tag names (defaults + company custom tags). Use before filtering with --tags. Example: baker ${command} tags`
@@ -24380,7 +24484,7 @@ function makeTagsCommand(command, label, endpoint) {
24380
24484
  var tagsCommand2 = makeTagsCommand("images", "image", "/api/images/tags");
24381
24485
 
24382
24486
  // src/commands/images/upload.ts
24383
- import { defineCommand as defineCommand127 } from "citty";
24487
+ import { defineCommand as defineCommand128 } from "citty";
24384
24488
  registerSchema({
24385
24489
  command: "images.upload",
24386
24490
  description: "Upload an image to the library \u2014 local file path or remote http(s) URL.",
@@ -24418,7 +24522,7 @@ registerSchema({
24418
24522
  function isRemoteUrl2(value) {
24419
24523
  return /^https?:\/\//i.test(value);
24420
24524
  }
24421
- var uploadCommand = defineCommand127({
24525
+ var uploadCommand = defineCommand128({
24422
24526
  meta: {
24423
24527
  name: "upload",
24424
24528
  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'"
@@ -24511,7 +24615,7 @@ async function uploadLocal(target, args) {
24511
24615
  }
24512
24616
 
24513
24617
  // src/commands/images/upscale.ts
24514
- import { defineCommand as defineCommand128 } from "citty";
24618
+ import { defineCommand as defineCommand129 } from "citty";
24515
24619
  registerSchema({
24516
24620
  command: "images.upscale",
24517
24621
  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).",
@@ -24526,7 +24630,7 @@ registerSchema({
24526
24630
  }
24527
24631
  });
24528
24632
  var POLL_INTERVAL_MS3 = 1500;
24529
- var upscaleCommand = defineCommand128({
24633
+ var upscaleCommand = defineCommand129({
24530
24634
  meta: {
24531
24635
  name: "upscale",
24532
24636
  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"
@@ -24581,7 +24685,7 @@ var upscaleCommand = defineCommand128({
24581
24685
  });
24582
24686
 
24583
24687
  // src/commands/images/use.ts
24584
- import { defineCommand as defineCommand129 } from "citty";
24688
+ import { defineCommand as defineCommand130 } from "citty";
24585
24689
  registerSchema({
24586
24690
  command: "images.use",
24587
24691
  description: "Ingest a URL and wait for the library record to be ready.",
@@ -24597,7 +24701,7 @@ registerSchema({
24597
24701
  }
24598
24702
  });
24599
24703
  var POLL_INTERVAL_MS4 = 1500;
24600
- var useCommand = defineCommand129({
24704
+ var useCommand = defineCommand130({
24601
24705
  meta: {
24602
24706
  name: "use",
24603
24707
  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"
@@ -24643,7 +24747,7 @@ var useCommand = defineCommand129({
24643
24747
  });
24644
24748
 
24645
24749
  // src/commands/images/index.ts
24646
- var imagesCommand = defineCommand130({
24750
+ var imagesCommand = defineCommand131({
24647
24751
  meta: {
24648
24752
  name: "images",
24649
24753
  description: `Find, source, and normalize images. Subcommands route by provider so cost + license are explicit.
@@ -24713,7 +24817,7 @@ Paid transforms (run on the Convex backend, cost-tracked):
24713
24817
  });
24714
24818
 
24715
24819
  // src/commands/mcp/index.ts
24716
- import { defineCommand as defineCommand131 } from "citty";
24820
+ import { defineCommand as defineCommand132 } from "citty";
24717
24821
  var SCOPES = ["user", "user_org", "company", "org"];
24718
24822
  function parseScope(raw) {
24719
24823
  const scope = raw === void 0 ? "company" : String(raw);
@@ -24752,7 +24856,7 @@ registerSchema({
24752
24856
  description: "List the custom MCP servers this company's chats see (org + company + your own user scope).",
24753
24857
  args: {}
24754
24858
  });
24755
- var listCommand6 = defineCommand131({
24859
+ var listCommand6 = defineCommand132({
24756
24860
  meta: { name: "list", description: "List custom MCP servers visible to this company's chats." },
24757
24861
  run: async () => {
24758
24862
  try {
@@ -24777,7 +24881,7 @@ registerSchema({
24777
24881
  header: { type: "string", description: 'Auth header "Key: Value" (repeatable)', required: false }
24778
24882
  }
24779
24883
  });
24780
- var addCommand = defineCommand131({
24884
+ var addCommand = defineCommand132({
24781
24885
  meta: {
24782
24886
  name: "add",
24783
24887
  description: `Register a custom MCP server. Tools appear as mcp__<name>__* on the NEXT message.
@@ -24818,7 +24922,7 @@ registerSchema({
24818
24922
  description: "Remove a company custom MCP server by name.",
24819
24923
  args: { name: { type: "string", description: "Server name to remove", required: true } }
24820
24924
  });
24821
- var removeCommand4 = defineCommand131({
24925
+ var removeCommand4 = defineCommand132({
24822
24926
  meta: {
24823
24927
  name: "remove",
24824
24928
  description: `Remove a company custom MCP server by name.
@@ -24840,7 +24944,7 @@ Example:
24840
24944
  }
24841
24945
  }
24842
24946
  });
24843
- var mcpCommand = defineCommand131({
24947
+ var mcpCommand = defineCommand132({
24844
24948
  meta: {
24845
24949
  name: "mcp",
24846
24950
  description: `Custom MCP servers for this company \u2014 point the agent at any HTTPS MCP endpoint.
@@ -24864,10 +24968,10 @@ Examples:
24864
24968
  });
24865
24969
 
24866
24970
  // src/commands/research/index.ts
24867
- import { defineCommand as defineCommand142 } from "citty";
24971
+ import { defineCommand as defineCommand143 } from "citty";
24868
24972
 
24869
24973
  // src/commands/research/advertisers.ts
24870
- import { defineCommand as defineCommand132 } from "citty";
24974
+ import { defineCommand as defineCommand133 } from "citty";
24871
24975
 
24872
24976
  // src/commands/research/output.ts
24873
24977
  var RESEARCH_DATA_NOTE = "Estimates based on third-party SERP data \u2014 not exact figures. Use for directional insights, not precise measurement.";
@@ -24980,7 +25084,7 @@ var FIELDS3 = {
24980
25084
  etv: "Estimated traffic value (USD)",
24981
25085
  visibility: "SERP visibility score (0-1)"
24982
25086
  };
24983
- var advertisersCommand = defineCommand132({
25087
+ var advertisersCommand = defineCommand133({
24984
25088
  meta: {
24985
25089
  name: "advertisers",
24986
25090
  description: `Find domains competing for a keyword in Google SERPs.
@@ -25027,7 +25131,7 @@ Examples:
25027
25131
  });
25028
25132
 
25029
25133
  // src/commands/research/autocomplete.ts
25030
- import { defineCommand as defineCommand133 } from "citty";
25134
+ import { defineCommand as defineCommand134 } from "citty";
25031
25135
  registerSchema({
25032
25136
  command: "research.autocomplete",
25033
25137
  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).",
@@ -25050,7 +25154,7 @@ registerSchema({
25050
25154
  var FIELDS4 = {
25051
25155
  suggestion: "Autocomplete suggestion from Google"
25052
25156
  };
25053
- var autocompleteCommand = defineCommand133({
25157
+ var autocompleteCommand = defineCommand134({
25054
25158
  meta: {
25055
25159
  name: "autocomplete",
25056
25160
  description: `Get Google Autocomplete suggestions for keyword expansion.
@@ -25096,7 +25200,7 @@ Examples:
25096
25200
  });
25097
25201
 
25098
25202
  // src/commands/research/countries.ts
25099
- import { defineCommand as defineCommand134 } from "citty";
25203
+ import { defineCommand as defineCommand135 } from "citty";
25100
25204
  registerSchema({
25101
25205
  command: "research.countries",
25102
25206
  description: "List all supported country codes for --location flag in research commands.",
@@ -25153,7 +25257,7 @@ var FIELDS5 = {
25153
25257
  code: "Country code to pass as --location",
25154
25258
  name: "Country name"
25155
25259
  };
25156
- var countriesCommand = defineCommand134({
25260
+ var countriesCommand = defineCommand135({
25157
25261
  meta: {
25158
25262
  name: "countries",
25159
25263
  description: "List all supported country codes for --location flag."
@@ -25164,7 +25268,7 @@ var countriesCommand = defineCommand134({
25164
25268
  });
25165
25269
 
25166
25270
  // src/commands/research/intent.ts
25167
- import { defineCommand as defineCommand135 } from "citty";
25271
+ import { defineCommand as defineCommand136 } from "citty";
25168
25272
  registerSchema({
25169
25273
  command: "research.intent",
25170
25274
  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.",
@@ -25187,7 +25291,7 @@ var FIELDS6 = {
25187
25291
  intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
25188
25292
  probability: "Confidence score 0.0-1.0"
25189
25293
  };
25190
- var intentCommand = defineCommand135({
25294
+ var intentCommand = defineCommand136({
25191
25295
  meta: {
25192
25296
  name: "intent",
25193
25297
  description: `Classify Google Search intent for keywords. Returns intent type and confidence.
@@ -25235,7 +25339,7 @@ Examples:
25235
25339
  });
25236
25340
 
25237
25341
  // src/commands/research/keyword-gap.ts
25238
- import { defineCommand as defineCommand136 } from "citty";
25342
+ import { defineCommand as defineCommand137 } from "citty";
25239
25343
  registerSchema({
25240
25344
  command: "research.keyword-gap",
25241
25345
  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.",
@@ -25264,7 +25368,7 @@ var FIELDS7 = {
25264
25368
  cpc: "Cost per click USD",
25265
25369
  their_position: "Competitor's ranking position"
25266
25370
  };
25267
- var keywordGapCommand = defineCommand136({
25371
+ var keywordGapCommand = defineCommand137({
25268
25372
  meta: {
25269
25373
  name: "keyword-gap",
25270
25374
  description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
@@ -25338,7 +25442,7 @@ Examples:
25338
25442
  });
25339
25443
 
25340
25444
  // src/commands/research/keywords-for-site.ts
25341
- import { defineCommand as defineCommand137 } from "citty";
25445
+ import { defineCommand as defineCommand138 } from "citty";
25342
25446
  registerSchema({
25343
25447
  command: "research.keywords-for-site",
25344
25448
  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.",
@@ -25371,7 +25475,7 @@ var FIELDS8 = {
25371
25475
  competition: "LOW, MEDIUM, or HIGH",
25372
25476
  competition_index: "Competition score 0-100"
25373
25477
  };
25374
- var keywordsForSiteCommand = defineCommand137({
25478
+ var keywordsForSiteCommand = defineCommand138({
25375
25479
  meta: {
25376
25480
  name: "keywords-for-site",
25377
25481
  description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
@@ -25424,7 +25528,7 @@ Examples:
25424
25528
  });
25425
25529
 
25426
25530
  // src/commands/research/languages.ts
25427
- import { defineCommand as defineCommand138 } from "citty";
25531
+ import { defineCommand as defineCommand139 } from "citty";
25428
25532
  registerSchema({
25429
25533
  command: "research.languages",
25430
25534
  description: "List all supported language codes for --language flag in research commands.",
@@ -25454,7 +25558,7 @@ var FIELDS9 = {
25454
25558
  code: "Language code to pass as --language",
25455
25559
  name: "Language name (also accepted by --language)"
25456
25560
  };
25457
- var languagesCommand2 = defineCommand138({
25561
+ var languagesCommand2 = defineCommand139({
25458
25562
  meta: {
25459
25563
  name: "languages",
25460
25564
  description: "List all supported language codes for --language flag."
@@ -25465,7 +25569,7 @@ var languagesCommand2 = defineCommand138({
25465
25569
  });
25466
25570
 
25467
25571
  // src/commands/research/lighthouse.ts
25468
- import { defineCommand as defineCommand139 } from "citty";
25572
+ import { defineCommand as defineCommand140 } from "citty";
25469
25573
  registerSchema({
25470
25574
  command: "research.lighthouse",
25471
25575
  description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
@@ -25484,7 +25588,7 @@ var FIELDS10 = {
25484
25588
  speed_index_ms: "Speed Index in ms (good: < 3400)",
25485
25589
  interactive_ms: "Time to Interactive in ms (good: < 3800)"
25486
25590
  };
25487
- var lighthouseCommand = defineCommand139({
25591
+ var lighthouseCommand = defineCommand140({
25488
25592
  meta: {
25489
25593
  name: "lighthouse",
25490
25594
  description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
@@ -25522,7 +25626,7 @@ Examples:
25522
25626
  });
25523
25627
 
25524
25628
  // src/commands/research/relevant-pages.ts
25525
- import { defineCommand as defineCommand140 } from "citty";
25629
+ import { defineCommand as defineCommand141 } from "citty";
25526
25630
  registerSchema({
25527
25631
  command: "research.relevant-pages",
25528
25632
  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).",
@@ -25548,7 +25652,7 @@ var FIELDS11 = {
25548
25652
  keywords: "Total organic keywords the page ranks for",
25549
25653
  top_10: "Keywords in positions 1-10"
25550
25654
  };
25551
- var relevantPagesCommand = defineCommand140({
25655
+ var relevantPagesCommand = defineCommand141({
25552
25656
  meta: {
25553
25657
  name: "relevant-pages",
25554
25658
  description: `Get the top pages of a competitor domain with traffic data.
@@ -25594,7 +25698,7 @@ Examples:
25594
25698
  });
25595
25699
 
25596
25700
  // src/commands/research/web.ts
25597
- import { defineCommand as defineCommand141 } from "citty";
25701
+ import { defineCommand as defineCommand142 } from "citty";
25598
25702
  registerSchema({
25599
25703
  command: "research.web",
25600
25704
  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).",
@@ -25645,7 +25749,7 @@ async function runDeepResearch(question) {
25645
25749
  }
25646
25750
  throw new Error("Deep research timed out");
25647
25751
  }
25648
- var webCommand = defineCommand141({
25752
+ var webCommand = defineCommand142({
25649
25753
  meta: {
25650
25754
  name: "web",
25651
25755
  description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
@@ -25705,7 +25809,7 @@ Examples:
25705
25809
  });
25706
25810
 
25707
25811
  // src/commands/research/index.ts
25708
- var researchCommand = defineCommand142({
25812
+ var researchCommand = defineCommand143({
25709
25813
  meta: {
25710
25814
  name: "research",
25711
25815
  description: `Competitive intelligence and AI-powered research commands.
@@ -25745,10 +25849,10 @@ Examples:
25745
25849
  });
25746
25850
 
25747
25851
  // src/commands/scheduled-actions/index.ts
25748
- import { defineCommand as defineCommand149 } from "citty";
25852
+ import { defineCommand as defineCommand150 } from "citty";
25749
25853
 
25750
25854
  // src/commands/scheduled-actions/create.ts
25751
- import { defineCommand as defineCommand143 } from "citty";
25855
+ import { defineCommand as defineCommand144 } from "citty";
25752
25856
 
25753
25857
  // src/commands/scheduled-actions/shared.ts
25754
25858
  var TEMP_SCHEDULED_ACTION_PREFIX = "temp_sched_";
@@ -25863,7 +25967,7 @@ registerSchema({
25863
25967
  prompt: { type: "string", description: "Additional prompt instructions for the spawned agent", required: false }
25864
25968
  }
25865
25969
  });
25866
- var createCommand2 = defineCommand143({
25970
+ var createCommand2 = defineCommand144({
25867
25971
  meta: {
25868
25972
  name: "create",
25869
25973
  description: 'Stage a scheduled action. Example: baker scheduled-actions create --name "Weekly report" --description "..." --cron "0 9 * * MON"'
@@ -25912,7 +26016,7 @@ var createCommand2 = defineCommand143({
25912
26016
  });
25913
26017
 
25914
26018
  // src/commands/scheduled-actions/delete.ts
25915
- import { defineCommand as defineCommand144 } from "citty";
26019
+ import { defineCommand as defineCommand145 } from "citty";
25916
26020
  registerSchema({
25917
26021
  command: "scheduled-actions.delete",
25918
26022
  description: "Stage deletion of a published scheduled action or cancellation of a temp_sched_* draft creation.",
@@ -25920,7 +26024,7 @@ registerSchema({
25920
26024
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
25921
26025
  }
25922
26026
  });
25923
- var deleteCommand2 = defineCommand144({
26027
+ var deleteCommand2 = defineCommand145({
25924
26028
  meta: {
25925
26029
  name: "delete",
25926
26030
  description: "Stage scheduled action deletion. Example: baker scheduled-actions delete <id-or-temp_sched_id>"
@@ -25949,7 +26053,7 @@ var deleteCommand2 = defineCommand144({
25949
26053
  });
25950
26054
 
25951
26055
  // src/commands/scheduled-actions/get.ts
25952
- import { defineCommand as defineCommand145 } from "citty";
26056
+ import { defineCommand as defineCommand146 } from "citty";
25953
26057
  registerSchema({
25954
26058
  command: "scheduled-actions.get",
25955
26059
  description: "Get a published scheduled action or a temp_sched_* draft-created scheduled action.",
@@ -25957,7 +26061,7 @@ registerSchema({
25957
26061
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
25958
26062
  }
25959
26063
  });
25960
- var getCommand3 = defineCommand145({
26064
+ var getCommand3 = defineCommand146({
25961
26065
  meta: {
25962
26066
  name: "get",
25963
26067
  description: "Get a scheduled action. Example: baker scheduled-actions get <id-or-temp_sched_id>"
@@ -25994,13 +26098,13 @@ var getCommand3 = defineCommand145({
25994
26098
  });
25995
26099
 
25996
26100
  // src/commands/scheduled-actions/list.ts
25997
- import { defineCommand as defineCommand146 } from "citty";
26101
+ import { defineCommand as defineCommand147 } from "citty";
25998
26102
  registerSchema({
25999
26103
  command: "scheduled-actions.list",
26000
26104
  description: "List published scheduled actions. Includes draft state when BAKER_CHAT_ID is set.",
26001
26105
  args: {}
26002
26106
  });
26003
- var listCommand7 = defineCommand146({
26107
+ var listCommand7 = defineCommand147({
26004
26108
  meta: {
26005
26109
  name: "list",
26006
26110
  description: "List scheduled actions. Includes staged draft ops when BAKER_CHAT_ID is set."
@@ -26021,7 +26125,7 @@ var listCommand7 = defineCommand146({
26021
26125
  });
26022
26126
 
26023
26127
  // src/commands/scheduled-actions/trigger.ts
26024
- import { defineCommand as defineCommand147 } from "citty";
26128
+ import { defineCommand as defineCommand148 } from "citty";
26025
26129
  registerSchema({
26026
26130
  command: "scheduled-actions.trigger",
26027
26131
  description: "Immediately trigger a published scheduled action. Does not require BAKER_CHAT_ID and rejects temp_sched_* IDs.",
@@ -26029,7 +26133,7 @@ registerSchema({
26029
26133
  id: { type: "string", description: "Published scheduled action ID", required: true }
26030
26134
  }
26031
26135
  });
26032
- var triggerCommand = defineCommand147({
26136
+ var triggerCommand = defineCommand148({
26033
26137
  meta: {
26034
26138
  name: "trigger",
26035
26139
  description: "Immediately trigger a published scheduled action. Example: baker scheduled-actions trigger <id>"
@@ -26066,7 +26170,7 @@ var triggerCommand = defineCommand147({
26066
26170
  });
26067
26171
 
26068
26172
  // src/commands/scheduled-actions/update.ts
26069
- import { defineCommand as defineCommand148 } from "citty";
26173
+ import { defineCommand as defineCommand149 } from "citty";
26070
26174
  registerSchema({
26071
26175
  command: "scheduled-actions.update",
26072
26176
  description: "Stage an update to a published scheduled action or temp_sched_* draft-created scheduled action.",
@@ -26091,7 +26195,7 @@ registerSchema({
26091
26195
  prompt: { type: "string", description: "Replacement additional spawned-agent instructions", required: false }
26092
26196
  }
26093
26197
  });
26094
- var updateCommand2 = defineCommand148({
26198
+ var updateCommand2 = defineCommand149({
26095
26199
  meta: {
26096
26200
  name: "update",
26097
26201
  description: "Stage a scheduled action update. Example: baker scheduled-actions update <id> --enabled false"
@@ -26162,7 +26266,7 @@ var updateCommand2 = defineCommand148({
26162
26266
  });
26163
26267
 
26164
26268
  // src/commands/scheduled-actions/index.ts
26165
- var scheduledActionsCommand = defineCommand149({
26269
+ var scheduledActionsCommand = defineCommand150({
26166
26270
  meta: {
26167
26271
  name: "scheduled-actions",
26168
26272
  description: `Manage Scheduled Actions. Subcommands: list, get, create, update, delete, trigger.
@@ -26188,8 +26292,8 @@ Examples:
26188
26292
  });
26189
26293
 
26190
26294
  // src/commands/schema.ts
26191
- import { defineCommand as defineCommand150 } from "citty";
26192
- var schemaCommand = defineCommand150({
26295
+ import { defineCommand as defineCommand151 } from "citty";
26296
+ var schemaCommand = defineCommand151({
26193
26297
  meta: {
26194
26298
  name: "schema",
26195
26299
  description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images.search"
@@ -26225,7 +26329,7 @@ var schemaCommand = defineCommand150({
26225
26329
  });
26226
26330
 
26227
26331
  // src/commands/tags/index.ts
26228
- import { defineCommand as defineCommand151 } from "citty";
26332
+ import { defineCommand as defineCommand152 } from "citty";
26229
26333
 
26230
26334
  // src/commands/tags/shared.ts
26231
26335
  function failApi3(err) {
@@ -26291,7 +26395,7 @@ async function listTags(json) {
26291
26395
  failApi3(err);
26292
26396
  }
26293
26397
  }
26294
- var listCommand8 = defineCommand151({
26398
+ var listCommand8 = defineCommand152({
26295
26399
  meta: {
26296
26400
  name: "list",
26297
26401
  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"
@@ -26310,7 +26414,7 @@ async function listDraft3() {
26310
26414
  failApi3(err);
26311
26415
  }
26312
26416
  }
26313
- var draftCommand3 = defineCommand151({
26417
+ var draftCommand3 = defineCommand152({
26314
26418
  meta: {
26315
26419
  name: "draft",
26316
26420
  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)."
@@ -26319,7 +26423,7 @@ var draftCommand3 = defineCommand151({
26319
26423
  await listDraft3();
26320
26424
  }
26321
26425
  });
26322
- var tagsCommand3 = defineCommand151({
26426
+ var tagsCommand3 = defineCommand152({
26323
26427
  meta: {
26324
26428
  name: "tags",
26325
26429
  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.
@@ -26344,10 +26448,10 @@ Examples:
26344
26448
  });
26345
26449
 
26346
26450
  // src/commands/testimonials/index.ts
26347
- import { defineCommand as defineCommand155 } from "citty";
26451
+ import { defineCommand as defineCommand156 } from "citty";
26348
26452
 
26349
26453
  // src/commands/testimonials/get.ts
26350
- import { defineCommand as defineCommand152 } from "citty";
26454
+ import { defineCommand as defineCommand153 } from "citty";
26351
26455
  registerSchema({
26352
26456
  command: "testimonials.get",
26353
26457
  description: "Get a single testimonial by ID",
@@ -26355,7 +26459,7 @@ registerSchema({
26355
26459
  id: { type: "string", description: "Testimonial ID", required: true }
26356
26460
  }
26357
26461
  });
26358
- var getCommand4 = defineCommand152({
26462
+ var getCommand4 = defineCommand153({
26359
26463
  meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
26360
26464
  args: {
26361
26465
  id: { type: "positional", description: "Testimonial ID", required: false },
@@ -26392,7 +26496,7 @@ var getCommand4 = defineCommand152({
26392
26496
  });
26393
26497
 
26394
26498
  // src/commands/testimonials/list.ts
26395
- import { defineCommand as defineCommand153 } from "citty";
26499
+ import { defineCommand as defineCommand154 } from "citty";
26396
26500
  registerSchema({
26397
26501
  command: "testimonials.list",
26398
26502
  description: "List testimonials with optional filters.",
@@ -26422,7 +26526,7 @@ registerSchema({
26422
26526
  limit: { type: "number", description: "Max results (default 50)", required: false, default: 50 }
26423
26527
  }
26424
26528
  });
26425
- var listCommand9 = defineCommand153({
26529
+ var listCommand9 = defineCommand154({
26426
26530
  meta: {
26427
26531
  name: "list",
26428
26532
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
@@ -26471,7 +26575,7 @@ var listCommand9 = defineCommand153({
26471
26575
  });
26472
26576
 
26473
26577
  // src/commands/testimonials/search.ts
26474
- import { defineCommand as defineCommand154 } from "citty";
26578
+ import { defineCommand as defineCommand155 } from "citty";
26475
26579
  registerSchema({
26476
26580
  command: "testimonials.search",
26477
26581
  description: "Search testimonials by text query. Uses hybrid BM25 + vector + reranking.",
@@ -26502,7 +26606,7 @@ registerSchema({
26502
26606
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
26503
26607
  }
26504
26608
  });
26505
- var searchCommand2 = defineCommand154({
26609
+ var searchCommand2 = defineCommand155({
26506
26610
  meta: {
26507
26611
  name: "search",
26508
26612
  description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
@@ -26576,7 +26680,7 @@ var searchCommand2 = defineCommand154({
26576
26680
  var tagsCommand4 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
26577
26681
 
26578
26682
  // src/commands/testimonials/index.ts
26579
- var testimonialsCommand = defineCommand155({
26683
+ var testimonialsCommand = defineCommand156({
26580
26684
  meta: {
26581
26685
  name: "testimonials",
26582
26686
  description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
@@ -26597,10 +26701,10 @@ Examples:
26597
26701
  });
26598
26702
 
26599
26703
  // src/commands/videos/index.ts
26600
- import { defineCommand as defineCommand160 } from "citty";
26704
+ import { defineCommand as defineCommand161 } from "citty";
26601
26705
 
26602
26706
  // src/commands/videos/delete.ts
26603
- import { defineCommand as defineCommand156 } from "citty";
26707
+ import { defineCommand as defineCommand157 } from "citty";
26604
26708
  registerSchema({
26605
26709
  command: "videos.delete",
26606
26710
  description: "Delete a video by ID",
@@ -26614,7 +26718,7 @@ registerSchema({
26614
26718
  }
26615
26719
  }
26616
26720
  });
26617
- var deleteCommand3 = defineCommand156({
26721
+ var deleteCommand3 = defineCommand157({
26618
26722
  meta: {
26619
26723
  name: "delete",
26620
26724
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -26655,7 +26759,7 @@ var deleteCommand3 = defineCommand156({
26655
26759
  });
26656
26760
 
26657
26761
  // src/commands/videos/get.ts
26658
- import { defineCommand as defineCommand157 } from "citty";
26762
+ import { defineCommand as defineCommand158 } from "citty";
26659
26763
  registerSchema({
26660
26764
  command: "videos.get",
26661
26765
  description: "Get a single video by ID",
@@ -26663,7 +26767,7 @@ registerSchema({
26663
26767
  id: { type: "string", description: "Video ID", required: true }
26664
26768
  }
26665
26769
  });
26666
- var getCommand5 = defineCommand157({
26770
+ var getCommand5 = defineCommand158({
26667
26771
  meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
26668
26772
  args: {
26669
26773
  id: { type: "positional", description: "Video ID", required: false },
@@ -26700,7 +26804,7 @@ var getCommand5 = defineCommand157({
26700
26804
  });
26701
26805
 
26702
26806
  // src/commands/videos/search.ts
26703
- import { defineCommand as defineCommand158 } from "citty";
26807
+ import { defineCommand as defineCommand159 } from "citty";
26704
26808
  registerSchema({
26705
26809
  command: "videos.search",
26706
26810
  description: "Search videos by text query. Only returns ready videos.",
@@ -26710,7 +26814,7 @@ registerSchema({
26710
26814
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
26711
26815
  }
26712
26816
  });
26713
- var searchCommand3 = defineCommand158({
26817
+ var searchCommand3 = defineCommand159({
26714
26818
  meta: {
26715
26819
  name: "search",
26716
26820
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -26760,9 +26864,9 @@ var searchCommand3 = defineCommand158({
26760
26864
  var tagsCommand5 = makeTagsCommand("videos", "video", "/api/videos/tags");
26761
26865
 
26762
26866
  // src/commands/videos/upload.ts
26763
- import { readFile as readFile16, stat as stat4 } from "fs/promises";
26867
+ import { readFile as readFile18, stat as stat5 } from "fs/promises";
26764
26868
  import { extname as extname3 } from "path";
26765
- import { defineCommand as defineCommand159 } from "citty";
26869
+ import { defineCommand as defineCommand160 } from "citty";
26766
26870
  var MIME_MAP = {
26767
26871
  ".mp4": "video/mp4",
26768
26872
  ".mov": "video/quicktime",
@@ -26796,7 +26900,7 @@ function detectContentType(filePath) {
26796
26900
  }
26797
26901
  return mime;
26798
26902
  }
26799
- var uploadCommand2 = defineCommand159({
26903
+ var uploadCommand2 = defineCommand160({
26800
26904
  meta: {
26801
26905
  name: "upload",
26802
26906
  description: "Upload a video file to Baker via Mux direct upload. Auto-detects content type. Example: baker videos upload ./demo.mp4"
@@ -26815,7 +26919,7 @@ var uploadCommand2 = defineCommand159({
26815
26919
  }
26816
26920
  const contentType = args["content-type"] || detectContentType(filePath);
26817
26921
  if (args["dry-run"]) {
26818
- const fileStats = await stat4(filePath);
26922
+ const fileStats = await stat5(filePath);
26819
26923
  writeJson({
26820
26924
  ok: true,
26821
26925
  dryRun: true,
@@ -26825,7 +26929,7 @@ var uploadCommand2 = defineCommand159({
26825
26929
  return;
26826
26930
  }
26827
26931
  const { uploadUrl, videoId } = await apiPost("/api/videos/upload", {});
26828
- const fileBuffer = await readFile16(filePath);
26932
+ const fileBuffer = await readFile18(filePath);
26829
26933
  const uploadResponse = await fetch(uploadUrl, {
26830
26934
  method: "PUT",
26831
26935
  headers: { "Content-Type": contentType },
@@ -26850,7 +26954,7 @@ var uploadCommand2 = defineCommand159({
26850
26954
  });
26851
26955
 
26852
26956
  // src/commands/videos/index.ts
26853
- var videosCommand = defineCommand160({
26957
+ var videosCommand = defineCommand161({
26854
26958
  meta: {
26855
26959
  name: "videos",
26856
26960
  description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete, tags.
@@ -26873,10 +26977,10 @@ Examples:
26873
26977
  });
26874
26978
 
26875
26979
  // src/commands/winning-ads/index.ts
26876
- import { defineCommand as defineCommand164 } from "citty";
26980
+ import { defineCommand as defineCommand165 } from "citty";
26877
26981
 
26878
26982
  // src/commands/winning-ads/advertisers.ts
26879
- import { defineCommand as defineCommand161 } from "citty";
26983
+ import { defineCommand as defineCommand162 } from "citty";
26880
26984
  registerSchema({
26881
26985
  command: "winning-ads.advertisers",
26882
26986
  description: "Resolve a brand name to advertiser_id(s) in the ad-dna corpus \u2014 to find your OWN advertiser (to --exclude-advertiser) or a competitor (to --advertiser-id).",
@@ -26889,7 +26993,7 @@ registerSchema({
26889
26993
  function identity(record) {
26890
26994
  return record;
26891
26995
  }
26892
- var advertisersCommand2 = defineCommand161({
26996
+ var advertisersCommand2 = defineCommand162({
26893
26997
  meta: {
26894
26998
  name: "advertisers",
26895
26999
  description: 'Resolve a brand name to advertiser_id(s). Use it to find your own advertiser for --exclude-advertiser, or a competitor for --advertiser-id. Example: baker winning-ads advertisers "Deel" --output md'
@@ -26940,7 +27044,7 @@ var advertisersCommand2 = defineCommand161({
26940
27044
  });
26941
27045
 
26942
27046
  // src/commands/winning-ads/patterns.ts
26943
- import { defineCommand as defineCommand162 } from "citty";
27047
+ import { defineCommand as defineCommand163 } from "citty";
26944
27048
  function leanRow(r) {
26945
27049
  return {
26946
27050
  key: r.key,
@@ -26960,7 +27064,7 @@ function makePatternsCommand(opts) {
26960
27064
  limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
26961
27065
  }
26962
27066
  });
26963
- return defineCommand162({
27067
+ return defineCommand163({
26964
27068
  meta: { name: opts.name, description: opts.description },
26965
27069
  args: {
26966
27070
  platform: { type: "string", description: "Single platform to segment on", required: false },
@@ -27015,7 +27119,7 @@ var patternsCommand = makePatternsCommand({
27015
27119
  });
27016
27120
 
27017
27121
  // src/commands/winning-ads/search.ts
27018
- import { defineCommand as defineCommand163 } from "citty";
27122
+ import { defineCommand as defineCommand164 } from "citty";
27019
27123
  registerSchema({
27020
27124
  command: "winning-ads.search",
27021
27125
  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.",
@@ -27129,7 +27233,7 @@ function buildSearchBody(args) {
27129
27233
  }
27130
27234
  return body;
27131
27235
  }
27132
- var searchCommand4 = defineCommand163({
27236
+ var searchCommand4 = defineCommand164({
27133
27237
  meta: {
27134
27238
  name: "search",
27135
27239
  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"
@@ -27247,7 +27351,7 @@ var searchCommand4 = defineCommand163({
27247
27351
  });
27248
27352
 
27249
27353
  // src/commands/winning-ads/index.ts
27250
- var winningAdsCommand = defineCommand164({
27354
+ var winningAdsCommand = defineCommand165({
27251
27355
  meta: {
27252
27356
  name: "winning-ads",
27253
27357
  description: `Search the ad-dna corpus of scored "winning" ads for reference creatives to reproduce. Proxied through the Baker backend (BAKER_API_KEY) \u2014 no separate token needed.
@@ -27294,7 +27398,7 @@ function getCliVersion() {
27294
27398
  }
27295
27399
 
27296
27400
  // src/cli.ts
27297
- var main = defineCommand165({
27401
+ var main = defineCommand166({
27298
27402
  meta: {
27299
27403
  name: "baker",
27300
27404
  version: getCliVersion(),