@koda-sl/baker-cli 0.92.0 → 0.92.1

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
@@ -9,10 +9,10 @@ import {
9
9
  defaultRegistry,
10
10
  generateCatalog,
11
11
  validateCanvasDeep
12
- } from "./chunk-LMVDA3EZ.js";
12
+ } from "./chunk-RCPMJKI7.js";
13
13
 
14
14
  // src/cli.ts
15
- import { defineCommand as defineCommand148, runMain } from "citty";
15
+ import { defineCommand as defineCommand141, runMain } from "citty";
16
16
 
17
17
  // src/commands/actions/index.ts
18
18
  import { defineCommand as defineCommand12 } from "citty";
@@ -8369,6 +8369,18 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
8369
8369
  }
8370
8370
 
8371
8371
  // src/engine/scaffold/video.ts
8372
+ import { toCardinal as nwAr } from "n2words/ar-SA";
8373
+ import { toCardinal as nwDe } from "n2words/de-DE";
8374
+ import { toCardinal as nwEn } from "n2words/en-US";
8375
+ import { toCardinal as nwEs } from "n2words/es-ES";
8376
+ import { toCardinal as nwFr } from "n2words/fr-FR";
8377
+ import { toCardinal as nwHi } from "n2words/hi-IN";
8378
+ import { toCardinal as nwIt } from "n2words/it-IT";
8379
+ import { toCardinal as nwJa } from "n2words/ja-JP";
8380
+ import { toCardinal as nwKo } from "n2words/ko-KR";
8381
+ import { toCardinal as nwNl } from "n2words/nl-NL";
8382
+ import { toCardinal as nwPl } from "n2words/pl-PL";
8383
+ import { toCardinal as nwPt } from "n2words/pt-PT";
8372
8384
  import { z as z3 } from "zod";
8373
8385
 
8374
8386
  // src/engine/scaffold/lib/shoot-modes.ts
@@ -8480,6 +8492,14 @@ var XFADE_BY_TYPE = {
8480
8492
  swipe: "wipeleft",
8481
8493
  zoom: "zoomin"
8482
8494
  };
8495
+ var DEFAULT_VIDEO_RESOLUTION = "1080p";
8496
+ var VIDEO_MODELS_WITH_RESOLUTION = new Set(
8497
+ Object.entries(MODEL_REGISTRY.video_generate).filter(([, spec]) => "resolution" in spec.params).map(([id]) => id)
8498
+ );
8499
+ function videoResolutionParam(videoModel, resolution) {
8500
+ if (!VIDEO_MODELS_WITH_RESOLUTION.has(videoModel)) return {};
8501
+ return { resolution: resolution ?? DEFAULT_VIDEO_RESOLUTION };
8502
+ }
8483
8503
  var WORDS_PER_SECOND = 2.5;
8484
8504
  function estSpeechS(text) {
8485
8505
  const words = text.trim().split(/\s+/).filter(Boolean).length;
@@ -8697,12 +8717,21 @@ var VideoBlueprint = z3.object({
8697
8717
  // reference track. We never reuse it — only style the regenerated bed.
8698
8718
  identified_track: z3.object({ title: z3.string().optional(), artist: z3.string().optional() }).loose().nullish()
8699
8719
  }).loose().optional(),
8700
- cast: z3.array(z3.object({ id: z3.string().optional(), description: z3.string().optional() }).loose()).optional(),
8720
+ cast: z3.array(
8721
+ z3.object({
8722
+ id: z3.string().optional(),
8723
+ description: z3.string().optional(),
8724
+ // The deconstruct's note on the target-market localization (e.g. "native
8725
+ // French speaker") — read to derive the spoken-track language code.
8726
+ market_localization_note: z3.string().optional()
8727
+ }).loose()
8728
+ ).optional(),
8701
8729
  voiceover: z3.object({
8702
8730
  // on_camera | mixed → mouths are on screen (lip-sync candidates);
8703
8731
  // voiceover | none → narration over the picture (no lip-sync).
8704
8732
  mode: z3.string().optional(),
8705
- voice_description: z3.string().optional()
8733
+ voice_description: z3.string().optional(),
8734
+ persona: z3.string().optional()
8706
8735
  }).loose().optional()
8707
8736
  }).loose().optional(),
8708
8737
  scenes: z3.array(Scene).min(1)
@@ -8885,11 +8914,18 @@ function slotsForFrame(slots, sceneIndex, edge) {
8885
8914
  return slots.filter((s) => s.presence.get(sceneIndex)?.has(edge));
8886
8915
  }
8887
8916
  var ACTOR_SHEET_MODEL = "google/gemini-3-pro-image-preview";
8888
- function applyActorSheets(slots, nodes) {
8917
+ var SHEET_SUBJECT_TYPE = {
8918
+ person: "person",
8919
+ animal: "character",
8920
+ product: "product",
8921
+ location: "location"
8922
+ };
8923
+ function buildElementSheets(slots, nodes) {
8889
8924
  for (const slot of slots) {
8890
- const t = slot.type.toLowerCase();
8891
- if (t !== "person" && t !== "animal") continue;
8892
- if (slot.presence.size < 2) continue;
8925
+ const subjectType = SHEET_SUBJECT_TYPE[slot.type.toLowerCase()];
8926
+ if (!subjectType) continue;
8927
+ if (slot.sameAs) continue;
8928
+ if (slot.presence.size < 1) continue;
8893
8929
  const sheetId = `${slot.id}_sheet`;
8894
8930
  nodes.push({
8895
8931
  id: sheetId,
@@ -8899,11 +8935,15 @@ function applyActorSheets(slots, nodes) {
8899
8935
  params: {
8900
8936
  model: ACTOR_SHEET_MODEL,
8901
8937
  subject_description: slot.description ?? `the ${slot.type}`,
8902
- subject_type: t === "person" ? "person" : "character",
8903
- image_size: "2K"
8938
+ subject_type: subjectType,
8939
+ // 4K: the sheet packs up to 8 cells (angles + tight face/detail close-ups), and
8940
+ // it's the ONE reference every frame grounds on — per-cell sharpness here
8941
+ // propagates to every clip, so it's worth the highest tier on this single asset.
8942
+ image_size: "4K"
8904
8943
  }
8905
8944
  });
8906
8945
  slot.ref = `$ref:${sheetId}.sheet`;
8946
+ slot.sheetBacked = true;
8907
8947
  }
8908
8948
  }
8909
8949
  function slotsForScene(slots, sceneIndex) {
@@ -8914,7 +8954,7 @@ function buildFramePrompt(edge, sceneIndex, framePrompt, present, hasAnchor, mod
8914
8954
  const legend = [
8915
8955
  ...present.map((s) => `- ${s.label} \u2014 ${roleForSlot(s)}`),
8916
8956
  ...hasAnchor ? [
8917
- "- ORIGINAL_FRAME \u2014 use ONLY for composition, framing, pose, and proportions. IGNORE its text, its logo, its brand name, and its colors entirely \u2014 it is a DIFFERENT brand's footage, here only to anchor layout/pose, never identity or palette."
8957
+ "- ORIGINAL_FRAME \u2014 use ONLY for composition: framing, camera angle, shot size, subject placement, pose, and proportions. IGNORE its text, logo, brand name, colors, AND the identity of every person/animal/object in it \u2014 those come from the labeled reference images above, never from this frame. It is a DIFFERENT brand's footage with DIFFERENT actors, here ONLY to anchor where things sit and how the shot is framed (e.g. a profile/side angle stays a profile/side angle), never who they are or what palette to use."
8918
8958
  ] : []
8919
8959
  ].join("\n");
8920
8960
  const description = framePrompt?.trim() || `the ${edge} frame of scene ${sceneIndex + 1} \u2014 describe the full composition, subjects, setting, action, lighting, and palette here. (Edit this line to change ONLY this frame.)`;
@@ -9003,11 +9043,12 @@ function ingestFrameRef(url, edge, ctx, nodes) {
9003
9043
  function buildFrameRef(edge, url, framePrompt, present, ctx, nodes) {
9004
9044
  const tag = ctx.tag ?? "";
9005
9045
  if (ctx.reuse && url) return ingestFrameRef(url, edge, ctx, nodes);
9006
- const hasPersonOrAnimal = present.some((s) => {
9046
+ const castSlots = present.filter((s) => {
9007
9047
  const t = s.type.toLowerCase();
9008
9048
  return t === "person" || t === "animal";
9009
9049
  });
9010
- const useOriginalAnchor = Boolean(url) && !hasPersonOrAnimal;
9050
+ const castIdentityLocked = castSlots.every((s) => s.sheetBacked);
9051
+ const useOriginalAnchor = Boolean(url) && (castSlots.length === 0 || castIdentityLocked);
9011
9052
  const hasOriginal = useOriginalAnchor;
9012
9053
  const originalRef = useOriginalAnchor && url ? ingestFrameRef(url, edge, ctx, nodes) : void 0;
9013
9054
  const reference = [...present.map((s) => s.ref), ...originalRef ? [originalRef] : []];
@@ -9039,17 +9080,18 @@ function seedanceAudioLine(scene, mode, audio, nativeLine) {
9039
9080
  }
9040
9081
  return null;
9041
9082
  }
9042
- function buildSeedancePrompt(scene, sceneIndex, present, mode, audio, nativeLine) {
9083
+ function buildSeedancePrompt(scene, sceneIndex, present, mode, audio, nativeLine, nativeLang) {
9084
+ const loc = (s) => nativeLine ? localizeNumeralsForNative(s, nativeLang) : s;
9043
9085
  const parts = [];
9044
9086
  const summary = scene.summary?.trim();
9045
- parts.push(summary ? `Scene ${sceneIndex + 1}: ${summary}` : `Scene ${sceneIndex + 1}`);
9046
- if (scene.action_detail) parts.push(`Action: ${scene.action_detail}`);
9087
+ parts.push(summary ? `Scene ${sceneIndex + 1}: ${loc(summary)}` : `Scene ${sceneIndex + 1}`);
9088
+ if (scene.action_detail) parts.push(`Action: ${loc(scene.action_detail)}`);
9047
9089
  const cm = scene.camera_motion;
9048
9090
  if (cm) {
9049
9091
  const camera = [cm.movement, cm.detail].filter(Boolean).join(" \u2014 ");
9050
9092
  if (camera) parts.push(`Camera: ${camera}`);
9051
9093
  }
9052
- if (scene.motion_prompt) parts.push(`Motion: ${scene.motion_prompt}`);
9094
+ if (scene.motion_prompt) parts.push(`Motion: ${loc(scene.motion_prompt)}`);
9053
9095
  if (present.length > 0) {
9054
9096
  parts.push(
9055
9097
  `Keep these consistent with their references: ${present.map((s) => `${s.label} (${s.description ?? s.type})`).join("; ")}`
@@ -9057,7 +9099,7 @@ function buildSeedancePrompt(scene, sceneIndex, present, mode, audio, nativeLine
9057
9099
  }
9058
9100
  if (nativeLine) {
9059
9101
  parts.push(
9060
- `The person speaks to camera. Lip-sync follows the dialogue verbatim; put delivery/emotion cues in [brackets]. Dialogue: "${nativeLine}"`
9102
+ `The person speaks to camera. Lip-sync follows the dialogue verbatim; put delivery/emotion cues in [brackets]. Dialogue: "${loc(nativeLine)}"`
9061
9103
  );
9062
9104
  } else {
9063
9105
  const lines = (scene.dialogue ?? []).map((d) => d.line?.trim()).filter((l) => Boolean(l));
@@ -9065,7 +9107,7 @@ function buildSeedancePrompt(scene, sceneIndex, present, mode, audio, nativeLine
9065
9107
  parts.push(`Spoken context (do not render as audio): ${lines.map((l) => `"${l}"`).join(" ")}`);
9066
9108
  }
9067
9109
  const transcript = (scene.transcript_slice ?? []).map((w) => w.text?.trim()).filter(Boolean).join(" ").trim();
9068
- if (transcript) parts.push(`Transcript: ${transcript}`);
9110
+ if (transcript) parts.push(`Transcript: ${loc(transcript)}`);
9069
9111
  const audioLine = seedanceAudioLine(scene, mode, audio, nativeLine);
9070
9112
  if (audioLine) parts.push(audioLine);
9071
9113
  parts.push(
@@ -9176,8 +9218,17 @@ function buildPerSpeakerVoiceConversion(segments, totalMs, nodes) {
9176
9218
  function emitSceneClip(i, scene, present, mode, nativeTurn, ambientBroll, frames, lengths, out, opts, nodes, tag = "") {
9177
9219
  const clipParams = {
9178
9220
  model: opts.videoModel,
9179
- prompt: buildSeedancePrompt(scene, i, present, mode, Boolean(nativeTurn) || ambientBroll, nativeTurn?.text),
9221
+ prompt: buildSeedancePrompt(
9222
+ scene,
9223
+ i,
9224
+ present,
9225
+ mode,
9226
+ Boolean(nativeTurn) || ambientBroll,
9227
+ nativeTurn?.text,
9228
+ opts.nativeLang
9229
+ ),
9180
9230
  duration: lengths.genDur,
9231
+ ...videoResolutionParam(opts.videoModel, opts.resolution),
9181
9232
  // Native talking scene → Seedance generates the spoken audio + lip-sync; an opt-in
9182
9233
  // ambient b-roll beat generates diegetic ambient only; otherwise the clip is silent.
9183
9234
  generate_audio: Boolean(nativeTurn) || ambientBroll
@@ -9281,7 +9332,7 @@ function buildCompositeScene(layout, regions, comp, scene, i, present, mode, nat
9281
9332
  { first, last },
9282
9333
  lengths,
9283
9334
  null,
9284
- { ar: opts.ar, videoModel: opts.videoModel },
9335
+ { ar: opts.ar, videoModel: opts.videoModel, resolution: opts.resolution, nativeLang: opts.nativeLang },
9285
9336
  nodes,
9286
9337
  tag
9287
9338
  );
@@ -9413,6 +9464,65 @@ var LANGUAGE_WORDS = [
9413
9464
  [/\b(hindi)\b/, "hindi"],
9414
9465
  [/\b(polish)\b/, "polish"]
9415
9466
  ];
9467
+ var LANGUAGE_ISO = {
9468
+ french: "fr",
9469
+ spanish: "es",
9470
+ english: "en",
9471
+ german: "de",
9472
+ italian: "it",
9473
+ portuguese: "pt",
9474
+ dutch: "nl",
9475
+ arabic: "ar",
9476
+ japanese: "ja",
9477
+ korean: "ko",
9478
+ hindi: "hi",
9479
+ polish: "pl"
9480
+ };
9481
+ function languageHaystacks(blueprint) {
9482
+ const vo = blueprint.global?.voiceover;
9483
+ const cast = blueprint.global?.cast ?? [];
9484
+ const dialogue = blueprint.scenes.flatMap((s) => s.dialogue ?? []);
9485
+ return [
9486
+ vo?.voice_description,
9487
+ vo?.persona,
9488
+ ...cast.flatMap((c) => [c.market_localization_note, c.description]),
9489
+ ...dialogue.map((l) => l.voice_description)
9490
+ ].filter((s) => Boolean(s));
9491
+ }
9492
+ function deriveTtsLanguageCode(blueprint) {
9493
+ for (const text of languageHaystacks(blueprint)) {
9494
+ const name = parseVoiceTraits(text).language;
9495
+ if (name && LANGUAGE_ISO[name]) return LANGUAGE_ISO[name];
9496
+ }
9497
+ return void 0;
9498
+ }
9499
+ var INTEGER_SPELLERS = {
9500
+ fr: nwFr,
9501
+ es: nwEs,
9502
+ en: nwEn,
9503
+ de: nwDe,
9504
+ it: nwIt,
9505
+ pt: nwPt,
9506
+ nl: nwNl,
9507
+ pl: nwPl,
9508
+ ar: nwAr,
9509
+ ja: nwJa,
9510
+ ko: nwKo,
9511
+ hi: nwHi
9512
+ };
9513
+ function spellNumber(langCode, n) {
9514
+ const spell = langCode ? INTEGER_SPELLERS[langCode] : void 0;
9515
+ if (!spell || !Number.isFinite(n)) return String(n);
9516
+ try {
9517
+ return spell(n);
9518
+ } catch {
9519
+ return String(n);
9520
+ }
9521
+ }
9522
+ function localizeNumeralsForNative(text, langCode) {
9523
+ if (!langCode || !INTEGER_SPELLERS[langCode]) return text;
9524
+ return text.replace(/(?<![\w.,-])\d{1,9}(?![\w.,-])/g, (m) => spellNumber(langCode, Number.parseInt(m, 10)));
9525
+ }
9416
9526
  function parseVoiceTraits(description) {
9417
9527
  const d = description.toLowerCase();
9418
9528
  const out = {};
@@ -9431,14 +9541,14 @@ function isOnCameraSpeaker(speaker, casts, cameraOn) {
9431
9541
  if (NARRATOR_SPEAKERS.has(speaker.toLowerCase())) return false;
9432
9542
  return casts.has(speaker);
9433
9543
  }
9434
- function makePresenterPresent(slots, canonical) {
9544
+ function makePresenterPresent(slots, canonical, opts = {}) {
9435
9545
  const personSlots = slots.filter((s) => s.type.toLowerCase() === "person");
9436
9546
  const bySpeaker = /* @__PURE__ */ new Map();
9437
9547
  for (const slot of personSlots) if (slot.castId) bySpeaker.set(canonical(slot.castId), slot.presence);
9438
- const solePerson = personSlots.length === 1 ? personSlots[0].presence : null;
9548
+ const solePerson = !opts.strict && personSlots.length === 1 ? personSlots[0].presence : null;
9439
9549
  return (speaker, sceneIndex) => {
9440
9550
  const presence = bySpeaker.get(speaker) ?? solePerson;
9441
- if (!presence) return true;
9551
+ if (!presence) return opts.strict ? false : true;
9442
9552
  return presence.has(sceneIndex);
9443
9553
  };
9444
9554
  }
@@ -9457,16 +9567,18 @@ function collapseVoiceover(blueprint) {
9457
9567
  const presenter = [...presenters][0];
9458
9568
  return (speaker) => NARRATOR_SPEAKERS.has(speaker.toLowerCase()) ? presenter : speaker;
9459
9569
  }
9460
- function buildPhrases(blueprint, canonical, compositeScenes, presenterPresent) {
9570
+ function buildPhrases(blueprint, canonical, compositeScenes, presenterPresent, presentStrict) {
9461
9571
  const casts = castIdSet(blueprint);
9462
9572
  const cameraOn = onCameraDialogue(blueprint);
9463
9573
  const sceneEndS = (i) => blueprint.scenes[i]?.end_s ?? blueprint.scenes[i]?.start_s ?? 0;
9464
9574
  const multiSpeaker = /* @__PURE__ */ new Set();
9465
9575
  blueprint.scenes.forEach((scene, i) => {
9466
- const onCam = new Set(
9576
+ const onCamAll = new Set(
9467
9577
  (scene.dialogue ?? []).map((l) => l.speaker ?? "voiceover").filter((sp) => isOnCameraSpeaker(sp, casts, cameraOn))
9468
9578
  );
9469
- if (onCam.size >= 2) multiSpeaker.add(i);
9579
+ const onCamPresent = [...onCamAll].filter((sp) => presentStrict(canonical(sp), i));
9580
+ const effective = onCamPresent.length > 0 ? new Set(onCamPresent) : onCamAll;
9581
+ if (effective.size >= 2) multiSpeaker.add(i);
9470
9582
  });
9471
9583
  const lines = blueprint.scenes.flatMap(
9472
9584
  (scene, sceneIndex) => compositeScenes.has(sceneIndex) ? [] : (scene.dialogue ?? []).filter((l) => Boolean(l.line?.trim())).map((l) => {
@@ -9605,8 +9717,9 @@ function emitPhraseClip(phrase, voiceNode, env, nodes, out) {
9605
9717
  const genDur = ceilToSeedance(phraseLen);
9606
9718
  const clipParams = {
9607
9719
  model: env.opts.videoModel,
9608
- prompt: buildSeedancePrompt(anchorScene, anchor, present, mode, true, phrase.text),
9720
+ prompt: buildSeedancePrompt(anchorScene, anchor, present, mode, true, phrase.text, env.ttsLanguageCode),
9609
9721
  duration: genDur,
9722
+ ...videoResolutionParam(env.opts.videoModel, env.opts.resolution),
9610
9723
  generate_audio: true
9611
9724
  };
9612
9725
  if (env.ar) clipParams.aspect_ratio = env.ar;
@@ -9666,7 +9779,7 @@ function emitPhraseClip(phrase, voiceNode, env, nodes, out) {
9666
9779
  });
9667
9780
  }
9668
9781
  }
9669
- function emitPhraseTts(phrase, voiceNode, idx, used, nodes, out) {
9782
+ function emitPhraseTts(phrase, voiceNode, idx, used, nodes, out, languageCode) {
9670
9783
  let id = sanitizeId2(`vo_ph${idx}_${phrase.speaker}`, `vo_ph${idx}`);
9671
9784
  while (used.has(id)) id = `${id}_x`;
9672
9785
  used.add(id);
@@ -9674,7 +9787,12 @@ function emitPhraseTts(phrase, voiceNode, idx, used, nodes, out) {
9674
9787
  id,
9675
9788
  type: "tts",
9676
9789
  inputs: { voice_ref: `$ref:${voiceNode}.voice_id` },
9677
- params: { model: FIXED_TTS_MODEL, text: phrase.text, voice: "{{voice_ref}}" }
9790
+ params: {
9791
+ model: FIXED_TTS_MODEL,
9792
+ text: phrase.text,
9793
+ voice: "{{voice_ref}}",
9794
+ ...languageCode ? { language_code: languageCode } : {}
9795
+ }
9678
9796
  });
9679
9797
  out.voTracks.push({ slot: id, ref: `$ref:${id}.audio`, start_s: phrase.start_s, end_s: phrase.end_s, kind: "vo" });
9680
9798
  out.voSegments.push({
@@ -9717,17 +9835,34 @@ function emitCompositeInTimeline(composite, scene, i, isLast, env, canonical, en
9717
9835
  nativeTurn,
9718
9836
  lengths,
9719
9837
  lengths.out,
9720
- { ar: env.ar, reuse: env.reuse, imageModel: env.opts.imageModel, videoModel: env.opts.videoModel },
9838
+ {
9839
+ ar: env.ar,
9840
+ reuse: env.reuse,
9841
+ imageModel: env.opts.imageModel,
9842
+ videoModel: env.opts.videoModel,
9843
+ resolution: env.opts.resolution,
9844
+ nativeLang: env.ttsLanguageCode
9845
+ },
9721
9846
  nodes,
9722
9847
  out.voTracks,
9723
9848
  out.nativeSegments,
9724
9849
  out.clips
9725
9850
  );
9726
9851
  if (!nativeTurn && distinctSpeakers.size >= 2) {
9727
- emitCompositeMultiSpeakerVoice(onCam, scene, i, canonical, ensureVoiceNode, usedVoIds, nodes, out);
9852
+ emitCompositeMultiSpeakerVoice(
9853
+ onCam,
9854
+ scene,
9855
+ i,
9856
+ canonical,
9857
+ ensureVoiceNode,
9858
+ usedVoIds,
9859
+ nodes,
9860
+ out,
9861
+ env.ttsLanguageCode
9862
+ );
9728
9863
  }
9729
9864
  }
9730
- function emitCompositeMultiSpeakerVoice(onCam, scene, i, canonical, ensureVoiceNode, usedVoIds, nodes, out) {
9865
+ function emitCompositeMultiSpeakerVoice(onCam, scene, i, canonical, ensureVoiceNode, usedVoIds, nodes, out, languageCode) {
9731
9866
  const bySpeaker = /* @__PURE__ */ new Map();
9732
9867
  for (const l of onCam) {
9733
9868
  const speaker = canonical(l.speaker ?? "voiceover");
@@ -9759,7 +9894,8 @@ function emitCompositeMultiSpeakerVoice(onCam, scene, i, canonical, ensureVoiceN
9759
9894
  i,
9760
9895
  usedVoIds,
9761
9896
  nodes,
9762
- out
9897
+ out,
9898
+ languageCode
9763
9899
  );
9764
9900
  }
9765
9901
  }
@@ -9806,7 +9942,7 @@ function emitBrollScene(scene, i, isLast, env, nodes, out, prevEndFrame) {
9806
9942
  { first, last },
9807
9943
  { dur: lengths.dur, trimTarget: lengths.trimTarget, genDur: lengths.genDur },
9808
9944
  lengths.out,
9809
- { ar: env.ar, videoModel: env.opts.videoModel },
9945
+ { ar: env.ar, videoModel: env.opts.videoModel, resolution: env.opts.resolution, nativeLang: env.ttsLanguageCode },
9810
9946
  nodes
9811
9947
  );
9812
9948
  if (ambientBroll) {
@@ -9842,7 +9978,8 @@ function buildTimeline(blueprint, slots, opts, nodes) {
9842
9978
  reuse,
9843
9979
  cameraOn: onCameraDialogue(blueprint),
9844
9980
  casts: castIdSet(blueprint),
9845
- ingestCache: /* @__PURE__ */ new Map()
9981
+ ingestCache: /* @__PURE__ */ new Map(),
9982
+ ttsLanguageCode: deriveTtsLanguageCode(blueprint)
9846
9983
  };
9847
9984
  const out = {
9848
9985
  clips: [],
@@ -9853,7 +9990,8 @@ function buildTimeline(blueprint, slots, opts, nodes) {
9853
9990
  sceneSlice: /* @__PURE__ */ new Map()
9854
9991
  };
9855
9992
  const presenterPresent = makePresenterPresent(slots, canonical);
9856
- const phrases = buildPhrases(blueprint, canonical, compositeScenes, presenterPresent);
9993
+ const presentStrict = makePresenterPresent(slots, canonical, { strict: true });
9994
+ const phrases = buildPhrases(blueprint, canonical, compositeScenes, presenterPresent, presentStrict);
9857
9995
  const usedVoIds = /* @__PURE__ */ new Set();
9858
9996
  const claimed = /* @__PURE__ */ new Set();
9859
9997
  phrases.forEach((phrase, k) => {
@@ -9863,7 +10001,7 @@ function buildTimeline(blueprint, slots, opts, nodes) {
9863
10001
  for (const s of available) claimed.add(s);
9864
10002
  emitPhraseClip({ ...phrase, shownScenes: available }, voiceNode, env, nodes, out);
9865
10003
  } else {
9866
- emitPhraseTts(phrase, voiceNode, k, usedVoIds, nodes, out);
10004
+ emitPhraseTts(phrase, voiceNode, k, usedVoIds, nodes, out, env.ttsLanguageCode);
9867
10005
  }
9868
10006
  });
9869
10007
  const lastIndex = blueprint.scenes.length - 1;
@@ -10200,7 +10338,7 @@ function scaffoldVideoCanvas(input, elementsInput, opts) {
10200
10338
  params: { source: "path", path: todoPath2(elements[i], slot.label), expect: "image" }
10201
10339
  });
10202
10340
  });
10203
- applyActorSheets(slots, nodes);
10341
+ buildElementSheets(slots, nodes);
10204
10342
  const { clips, voTracks, vo_segments, talking_scenes } = buildTimeline(blueprint, slots, opts, nodes);
10205
10343
  let videoRef = buildSpine(clips, nodes);
10206
10344
  let videoNode = "spine";
@@ -10309,9 +10447,27 @@ function buildVideoMeta(blueprint, meta) {
10309
10447
  duration_s: blueprint.source?.duration_s ?? lastSceneEnd(blueprint),
10310
10448
  vo_segments: [...meta.vo_segments].sort((a, b) => a.start_s - b.start_s),
10311
10449
  talking_scenes: meta.talking_scenes,
10450
+ lip_sync_caution: buildLipSyncCaution(meta.vo_segments),
10312
10451
  motion_board: buildMotionBoard(blueprint)
10313
10452
  };
10314
10453
  }
10454
+ function buildLipSyncCaution(segments) {
10455
+ const out = [];
10456
+ const byScene = /* @__PURE__ */ new Map();
10457
+ for (const s of segments) {
10458
+ const arr = byScene.get(s.scene) ?? [];
10459
+ arr.push(s);
10460
+ byScene.set(s.scene, arr);
10461
+ }
10462
+ for (const [scene, segs] of [...byScene.entries()].sort((a, b) => a[0] - b[0])) {
10463
+ const nativeSpeakers = new Set(segs.filter((s) => s.slot.endsWith("_conv")).map((s) => s.speaker));
10464
+ for (const speaker of nativeSpeakers) {
10465
+ const ttsOver = segs.filter((s) => !s.slot.endsWith("_conv") && s.speaker === speaker).map((s) => s.slot);
10466
+ if (ttsOver.length > 0) out.push({ scene, speaker, tts_over_native: ttsOver });
10467
+ }
10468
+ }
10469
+ return out;
10470
+ }
10315
10471
  function sceneSpokenText(scene) {
10316
10472
  return (scene.dialogue ?? []).map((d) => d.line?.trim()).filter((l) => Boolean(l)).join(" ") || null;
10317
10473
  }
@@ -10743,7 +10899,11 @@ var scaffoldVideoCommand = defineCommand76({
10743
10899
  "deconstruct-model": { type: "string", description: "Override the video_deconstruct model id" },
10744
10900
  "select-model": { type: "string", description: "Override the text_generate model id for element selection" },
10745
10901
  "image-model": { type: "string", description: "Override the image_generate model id for frames" },
10746
- "video-model": { type: "string", description: "Override the video_generate model id for clips" }
10902
+ "video-model": { type: "string", description: "Override the video_generate model id for clips" },
10903
+ resolution: {
10904
+ type: "string",
10905
+ description: `Output resolution for generated clips (e.g. "1080p"). Default 1080p \u2014 the highest the video model supports \u2014 so clips keep the keyframe sharpness instead of the model's low default.`
10906
+ }
10747
10907
  },
10748
10908
  async run({ args }) {
10749
10909
  const videoPath = path5.resolve(String(args.file));
@@ -10795,7 +10955,8 @@ var scaffoldVideoCommand = defineCommand76({
10795
10955
  transcriptPath: captions.transcriptPath,
10796
10956
  blueprintPath,
10797
10957
  frames,
10798
- ambient: Boolean(args.ambient)
10958
+ ambient: Boolean(args.ambient),
10959
+ ...args.resolution ? { resolution: String(args.resolution) } : {}
10799
10960
  };
10800
10961
  let canvas;
10801
10962
  let report;
@@ -14307,292 +14468,11 @@ Paid transforms (run on the Convex backend, cost-tracked):
14307
14468
  }
14308
14469
  });
14309
14470
 
14310
- // src/commands/missions/index.ts
14311
- import { defineCommand as defineCommand116 } from "citty";
14312
-
14313
- // src/commands/missions/add-action.ts
14314
- import { defineCommand as defineCommand110 } from "citty";
14315
- registerSchema({
14316
- command: "missions.add-action",
14317
- description: "Attach an action to a mission as an ordered step. Both --mission and --action accept a real id or a temp_* ref from a create op in the same draft. --order is the 0-based position in the plan. Applies on chat publish.",
14318
- args: {
14319
- mission: { type: "string", description: "Mission id or missionTempId", required: true },
14320
- action: { type: "string", description: "Action id or tempId", required: true },
14321
- order: { type: "string", description: "0-based position within the mission", required: true }
14322
- }
14323
- });
14324
- var addActionCommand = defineCommand110({
14325
- meta: {
14326
- name: "add-action",
14327
- description: "Attach an action to a mission as an ordered step. Example: baker missions add-action --mission <ref> --action <ref> --order 0"
14328
- },
14329
- args: {
14330
- mission: { type: "string", description: "Mission ref", required: false },
14331
- action: { type: "string", description: "Action ref", required: false },
14332
- order: { type: "string", description: "0-based order", required: false }
14333
- },
14334
- run: async ({ args }) => {
14335
- try {
14336
- const mission = args.mission;
14337
- const action = args.action;
14338
- if (!mission || !action) {
14339
- failValidation("--mission and --action are required.");
14340
- }
14341
- const order = Number.parseInt(args.order ?? "", 10);
14342
- if (Number.isNaN(order) || order < 0) {
14343
- failValidation("--order must be a non-negative integer.");
14344
- }
14345
- const chatId = requireChatId();
14346
- await apiPost("/api/missions/add-action", {
14347
- chatId,
14348
- missionRef: mission,
14349
- actionRef: action,
14350
- order
14351
- });
14352
- writeOk();
14353
- } catch (err) {
14354
- failApi(err);
14355
- }
14356
- }
14357
- });
14358
-
14359
- // src/commands/missions/create.ts
14360
- import { defineCommand as defineCommand111 } from "citty";
14361
- registerSchema({
14362
- command: "missions.create",
14363
- description: "Open a Mission to group the ordered steps of a multi-step request. A Mission has a title and a markdown overview (what you're doing and why \u2014 the human-readable plan, written as numbered Phases with NO dates and NO effort sizing). Returns a missionTempId. Next: create the action steps with `baker actions create`, then attach each in order with `baker missions add-action`. Applies when the chat is published \u2014 publishing = the user approving the plan.",
14364
- args: {
14365
- title: { type: "string", description: "Mission title \u2014 the goal, short", required: true },
14366
- overview: {
14367
- type: "string",
14368
- description: "Markdown overview: the plan as numbered Phases (goal / what it produces / what unlocks next). No calendar or effort framing.",
14369
- required: false
14370
- },
14371
- "temp-id": { type: "string", description: "Custom missionTempId (auto-generated if omitted)", required: false }
14372
- }
14373
- });
14374
- var createCommand2 = defineCommand111({
14375
- meta: {
14376
- name: "create",
14377
- description: 'Open a Mission to group ordered steps. Example: baker missions create --title "Audit Google Ads" --overview "..."'
14378
- },
14379
- args: {
14380
- title: { type: "string", description: "Mission title", required: false },
14381
- overview: { type: "string", description: "Markdown overview (numbered Phases)", required: false, default: "" },
14382
- "temp-id": { type: "string", description: "Optional custom missionTempId", required: false }
14383
- },
14384
- run: async ({ args }) => {
14385
- try {
14386
- const title = args.title;
14387
- if (!title || title.trim().length === 0) {
14388
- failValidation("--title is required.");
14389
- }
14390
- const chatId = requireChatId();
14391
- const missionTempId = args["temp-id"] || generateTempId();
14392
- const response = await apiPost("/api/missions/create", {
14393
- chatId,
14394
- missionTempId,
14395
- title,
14396
- overview: args.overview ?? ""
14397
- });
14398
- const hints = [
14399
- `Add ordered steps: create each action, then baker missions add-action --mission ${missionTempId} --action <tempId> --order 0`
14400
- ];
14401
- if (!args.overview) {
14402
- hints.push(
14403
- "Tip: pass --overview with the plan as numbered Phases (goal / produces / unlocks; no dates or effort sizing)."
14404
- );
14405
- }
14406
- writeJson({ ...response, hints });
14407
- } catch (err) {
14408
- failApi(err);
14409
- }
14410
- }
14411
- });
14412
-
14413
- // src/commands/missions/get.ts
14414
- import { defineCommand as defineCommand112 } from "citty";
14415
- registerSchema({
14416
- command: "missions.get",
14417
- description: "Get one mission with its overview, progress, and ordered steps.",
14418
- args: {
14419
- id: { type: "string", description: "Mission ID", required: true }
14420
- }
14421
- });
14422
- var getCommand3 = defineCommand112({
14423
- meta: { name: "get", description: "Get a mission by ID. Example: baker missions get <mission-id>" },
14424
- args: {
14425
- id: { type: "positional", description: "Mission ID", required: false },
14426
- "mission-id": { type: "string", description: "Mission ID (alternative to positional)", required: false }
14427
- },
14428
- run: async ({ args }) => {
14429
- try {
14430
- const id = args.id || args["mission-id"];
14431
- if (!id) {
14432
- failValidation("Mission ID is required.");
14433
- }
14434
- validateConvexId(id);
14435
- const response = await apiPost("/api/missions/get", { missionId: id });
14436
- writeJson(response);
14437
- } catch (err) {
14438
- failApi(err);
14439
- }
14440
- }
14441
- });
14442
-
14443
- // src/commands/missions/list.ts
14444
- import { defineCommand as defineCommand113 } from "citty";
14445
- registerSchema({
14446
- command: "missions.list",
14447
- description: "List the company's missions with per-mission progress (done/total) and ordered steps. Aborted missions are hidden unless --include-aborted.",
14448
- args: {
14449
- "include-aborted": {
14450
- type: "boolean",
14451
- description: "Include aborted missions (default: false)",
14452
- required: false,
14453
- default: false
14454
- }
14455
- }
14456
- });
14457
- var listCommand2 = defineCommand113({
14458
- meta: {
14459
- name: "list",
14460
- description: "List missions with progress and ordered steps. Example: baker missions list"
14461
- },
14462
- args: {
14463
- "include-aborted": { type: "boolean", description: "Include aborted missions", required: false, default: false }
14464
- },
14465
- run: async ({ args }) => {
14466
- try {
14467
- const body = {};
14468
- if (args["include-aborted"] === true) {
14469
- body.includeAborted = true;
14470
- }
14471
- const response = await apiPost("/api/missions/list", body);
14472
- writeJson(response);
14473
- } catch (err) {
14474
- failApi(err);
14475
- }
14476
- }
14477
- });
14478
-
14479
- // src/commands/missions/set-status.ts
14480
- import { defineCommand as defineCommand114 } from "citty";
14481
- registerSchema({
14482
- command: "missions.set-status",
14483
- description: "Set a mission's status: accomplished (the goal is genuinely met), aborted (abandoned), or active (reopen). Do NOT mark accomplished just because all steps are closed \u2014 only when the goal is done. Applies on chat publish.",
14484
- args: {
14485
- id: { type: "string", description: "Mission ID", required: true },
14486
- status: { type: "string", description: "active | accomplished | aborted", required: true }
14487
- }
14488
- });
14489
- var setStatusCommand = defineCommand114({
14490
- meta: {
14491
- name: "set-status",
14492
- description: "Set mission status. Example: baker missions set-status <id> --status accomplished"
14493
- },
14494
- args: {
14495
- id: { type: "positional", description: "Mission ID", required: false },
14496
- "mission-id": { type: "string", description: "Mission ID", required: false },
14497
- status: { type: "string", description: "active | accomplished | aborted", required: false }
14498
- },
14499
- run: async ({ args }) => {
14500
- try {
14501
- const id = args.id || args["mission-id"];
14502
- if (!id) {
14503
- failValidation("Mission ID is required.");
14504
- }
14505
- validateConvexId(id);
14506
- const status = args.status;
14507
- if (status !== "active" && status !== "accomplished" && status !== "aborted") {
14508
- failValidation("--status must be one of: active, accomplished, aborted.");
14509
- }
14510
- const chatId = requireChatId();
14511
- await apiPost("/api/missions/set-status", { chatId, missionId: id, status });
14512
- writeOk();
14513
- } catch (err) {
14514
- failApi(err);
14515
- }
14516
- }
14517
- });
14518
-
14519
- // src/commands/missions/update.ts
14520
- import { defineCommand as defineCommand115 } from "citty";
14521
- registerSchema({
14522
- command: "missions.update",
14523
- description: "Stage an update to a mission's title and/or overview (real mission id only). Applies on chat publish.",
14524
- args: {
14525
- id: { type: "string", description: "Mission ID", required: true },
14526
- title: { type: "string", description: "New title", required: false },
14527
- overview: { type: "string", description: "New markdown overview (numbered Phases)", required: false }
14528
- }
14529
- });
14530
- var updateCommand2 = defineCommand115({
14531
- meta: {
14532
- name: "update",
14533
- description: 'Stage a mission update. Example: baker missions update <id> --overview "..."'
14534
- },
14535
- args: {
14536
- id: { type: "positional", description: "Mission ID", required: false },
14537
- "mission-id": { type: "string", description: "Mission ID", required: false },
14538
- title: { type: "string", description: "New title", required: false },
14539
- overview: { type: "string", description: "New markdown overview", required: false }
14540
- },
14541
- run: async ({ args }) => {
14542
- try {
14543
- const id = args.id || args["mission-id"];
14544
- if (!id) {
14545
- failValidation("Mission ID is required.");
14546
- }
14547
- validateConvexId(id);
14548
- if (args.title === void 0 && args.overview === void 0) {
14549
- failValidation("Provide at least one of --title, --overview.");
14550
- }
14551
- const chatId = requireChatId();
14552
- await apiPost("/api/missions/update", {
14553
- chatId,
14554
- missionId: id,
14555
- title: args.title,
14556
- overview: args.overview
14557
- });
14558
- writeOk();
14559
- } catch (err) {
14560
- failApi(err);
14561
- }
14562
- }
14563
- });
14564
-
14565
- // src/commands/missions/index.ts
14566
- var missionsCommand = defineCommand116({
14567
- meta: {
14568
- name: "missions",
14569
- description: `Group ordered actions into a Mission \u2014 one goal with a markdown overview (the plan) and ordered action "steps". Subcommands: list, get, create, add-action, update, set-status.
14570
-
14571
- Use a mission whenever a request decomposes into 2+ ordered actions (audits, campaigns, multi-step plans). A single one-off capture stays a loose action.
14572
-
14573
- Flow: create the mission \u2192 create each action step in order \u2192 attach each with add-action \u2192 wire hard dependencies with 'baker actions link' \u2192 publish the chat (= the user approving the plan). The overview is forward planning: write it as numbered Phases, no dates, no effort sizing. Mark accomplished/aborted explicitly \u2014 never auto-conclude from step status.
14574
-
14575
- Examples:
14576
- baker missions create --title "Audit Google Ads" --overview "..."
14577
- baker missions add-action --mission <ref> --action <ref> --order 0
14578
- baker missions list
14579
- baker missions set-status <id> --status accomplished`
14580
- },
14581
- subCommands: {
14582
- list: listCommand2,
14583
- get: getCommand3,
14584
- create: createCommand2,
14585
- "add-action": addActionCommand,
14586
- update: updateCommand2,
14587
- "set-status": setStatusCommand
14588
- }
14589
- });
14590
-
14591
14471
  // src/commands/research/index.ts
14592
- import { defineCommand as defineCommand127 } from "citty";
14472
+ import { defineCommand as defineCommand120 } from "citty";
14593
14473
 
14594
14474
  // src/commands/research/advertisers.ts
14595
- import { defineCommand as defineCommand117 } from "citty";
14475
+ import { defineCommand as defineCommand110 } from "citty";
14596
14476
 
14597
14477
  // src/commands/research/output.ts
14598
14478
  var RESEARCH_DATA_NOTE = "Estimates based on third-party SERP data \u2014 not exact figures. Use for directional insights, not precise measurement.";
@@ -14705,7 +14585,7 @@ var FIELDS3 = {
14705
14585
  etv: "Estimated traffic value (USD)",
14706
14586
  visibility: "SERP visibility score (0-1)"
14707
14587
  };
14708
- var advertisersCommand = defineCommand117({
14588
+ var advertisersCommand = defineCommand110({
14709
14589
  meta: {
14710
14590
  name: "advertisers",
14711
14591
  description: `Find domains competing for a keyword in Google SERPs.
@@ -14752,7 +14632,7 @@ Examples:
14752
14632
  });
14753
14633
 
14754
14634
  // src/commands/research/autocomplete.ts
14755
- import { defineCommand as defineCommand118 } from "citty";
14635
+ import { defineCommand as defineCommand111 } from "citty";
14756
14636
  registerSchema({
14757
14637
  command: "research.autocomplete",
14758
14638
  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).",
@@ -14775,7 +14655,7 @@ registerSchema({
14775
14655
  var FIELDS4 = {
14776
14656
  suggestion: "Autocomplete suggestion from Google"
14777
14657
  };
14778
- var autocompleteCommand = defineCommand118({
14658
+ var autocompleteCommand = defineCommand111({
14779
14659
  meta: {
14780
14660
  name: "autocomplete",
14781
14661
  description: `Get Google Autocomplete suggestions for keyword expansion.
@@ -14821,7 +14701,7 @@ Examples:
14821
14701
  });
14822
14702
 
14823
14703
  // src/commands/research/countries.ts
14824
- import { defineCommand as defineCommand119 } from "citty";
14704
+ import { defineCommand as defineCommand112 } from "citty";
14825
14705
  registerSchema({
14826
14706
  command: "research.countries",
14827
14707
  description: "List all supported country codes for --location flag in research commands.",
@@ -14878,7 +14758,7 @@ var FIELDS5 = {
14878
14758
  code: "Country code to pass as --location",
14879
14759
  name: "Country name"
14880
14760
  };
14881
- var countriesCommand = defineCommand119({
14761
+ var countriesCommand = defineCommand112({
14882
14762
  meta: {
14883
14763
  name: "countries",
14884
14764
  description: "List all supported country codes for --location flag."
@@ -14889,7 +14769,7 @@ var countriesCommand = defineCommand119({
14889
14769
  });
14890
14770
 
14891
14771
  // src/commands/research/intent.ts
14892
- import { defineCommand as defineCommand120 } from "citty";
14772
+ import { defineCommand as defineCommand113 } from "citty";
14893
14773
  registerSchema({
14894
14774
  command: "research.intent",
14895
14775
  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.",
@@ -14912,7 +14792,7 @@ var FIELDS6 = {
14912
14792
  intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
14913
14793
  probability: "Confidence score 0.0-1.0"
14914
14794
  };
14915
- var intentCommand = defineCommand120({
14795
+ var intentCommand = defineCommand113({
14916
14796
  meta: {
14917
14797
  name: "intent",
14918
14798
  description: `Classify Google Search intent for keywords. Returns intent type and confidence.
@@ -14960,7 +14840,7 @@ Examples:
14960
14840
  });
14961
14841
 
14962
14842
  // src/commands/research/keyword-gap.ts
14963
- import { defineCommand as defineCommand121 } from "citty";
14843
+ import { defineCommand as defineCommand114 } from "citty";
14964
14844
  registerSchema({
14965
14845
  command: "research.keyword-gap",
14966
14846
  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.",
@@ -14989,7 +14869,7 @@ var FIELDS7 = {
14989
14869
  cpc: "Cost per click USD",
14990
14870
  their_position: "Competitor's ranking position"
14991
14871
  };
14992
- var keywordGapCommand = defineCommand121({
14872
+ var keywordGapCommand = defineCommand114({
14993
14873
  meta: {
14994
14874
  name: "keyword-gap",
14995
14875
  description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
@@ -15063,7 +14943,7 @@ Examples:
15063
14943
  });
15064
14944
 
15065
14945
  // src/commands/research/keywords-for-site.ts
15066
- import { defineCommand as defineCommand122 } from "citty";
14946
+ import { defineCommand as defineCommand115 } from "citty";
15067
14947
  registerSchema({
15068
14948
  command: "research.keywords-for-site",
15069
14949
  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.",
@@ -15096,7 +14976,7 @@ var FIELDS8 = {
15096
14976
  competition: "LOW, MEDIUM, or HIGH",
15097
14977
  competition_index: "Competition score 0-100"
15098
14978
  };
15099
- var keywordsForSiteCommand = defineCommand122({
14979
+ var keywordsForSiteCommand = defineCommand115({
15100
14980
  meta: {
15101
14981
  name: "keywords-for-site",
15102
14982
  description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
@@ -15149,7 +15029,7 @@ Examples:
15149
15029
  });
15150
15030
 
15151
15031
  // src/commands/research/languages.ts
15152
- import { defineCommand as defineCommand123 } from "citty";
15032
+ import { defineCommand as defineCommand116 } from "citty";
15153
15033
  registerSchema({
15154
15034
  command: "research.languages",
15155
15035
  description: "List all supported language codes for --language flag in research commands.",
@@ -15179,7 +15059,7 @@ var FIELDS9 = {
15179
15059
  code: "Language code to pass as --language",
15180
15060
  name: "Language name (also accepted by --language)"
15181
15061
  };
15182
- var languagesCommand2 = defineCommand123({
15062
+ var languagesCommand2 = defineCommand116({
15183
15063
  meta: {
15184
15064
  name: "languages",
15185
15065
  description: "List all supported language codes for --language flag."
@@ -15190,7 +15070,7 @@ var languagesCommand2 = defineCommand123({
15190
15070
  });
15191
15071
 
15192
15072
  // src/commands/research/lighthouse.ts
15193
- import { defineCommand as defineCommand124 } from "citty";
15073
+ import { defineCommand as defineCommand117 } from "citty";
15194
15074
  registerSchema({
15195
15075
  command: "research.lighthouse",
15196
15076
  description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
@@ -15209,7 +15089,7 @@ var FIELDS10 = {
15209
15089
  speed_index_ms: "Speed Index in ms (good: < 3400)",
15210
15090
  interactive_ms: "Time to Interactive in ms (good: < 3800)"
15211
15091
  };
15212
- var lighthouseCommand = defineCommand124({
15092
+ var lighthouseCommand = defineCommand117({
15213
15093
  meta: {
15214
15094
  name: "lighthouse",
15215
15095
  description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
@@ -15247,7 +15127,7 @@ Examples:
15247
15127
  });
15248
15128
 
15249
15129
  // src/commands/research/relevant-pages.ts
15250
- import { defineCommand as defineCommand125 } from "citty";
15130
+ import { defineCommand as defineCommand118 } from "citty";
15251
15131
  registerSchema({
15252
15132
  command: "research.relevant-pages",
15253
15133
  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).",
@@ -15273,7 +15153,7 @@ var FIELDS11 = {
15273
15153
  keywords: "Total organic keywords the page ranks for",
15274
15154
  top_10: "Keywords in positions 1-10"
15275
15155
  };
15276
- var relevantPagesCommand = defineCommand125({
15156
+ var relevantPagesCommand = defineCommand118({
15277
15157
  meta: {
15278
15158
  name: "relevant-pages",
15279
15159
  description: `Get the top pages of a competitor domain with traffic data.
@@ -15319,7 +15199,7 @@ Examples:
15319
15199
  });
15320
15200
 
15321
15201
  // src/commands/research/web.ts
15322
- import { defineCommand as defineCommand126 } from "citty";
15202
+ import { defineCommand as defineCommand119 } from "citty";
15323
15203
  registerSchema({
15324
15204
  command: "research.web",
15325
15205
  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).",
@@ -15370,7 +15250,7 @@ async function runDeepResearch(question) {
15370
15250
  }
15371
15251
  throw new Error("Deep research timed out");
15372
15252
  }
15373
- var webCommand = defineCommand126({
15253
+ var webCommand = defineCommand119({
15374
15254
  meta: {
15375
15255
  name: "web",
15376
15256
  description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
@@ -15430,7 +15310,7 @@ Examples:
15430
15310
  });
15431
15311
 
15432
15312
  // src/commands/research/index.ts
15433
- var researchCommand = defineCommand127({
15313
+ var researchCommand = defineCommand120({
15434
15314
  meta: {
15435
15315
  name: "research",
15436
15316
  description: `Competitive intelligence and AI-powered research commands.
@@ -15470,10 +15350,10 @@ Examples:
15470
15350
  });
15471
15351
 
15472
15352
  // src/commands/scheduled-actions/index.ts
15473
- import { defineCommand as defineCommand134 } from "citty";
15353
+ import { defineCommand as defineCommand127 } from "citty";
15474
15354
 
15475
15355
  // src/commands/scheduled-actions/create.ts
15476
- import { defineCommand as defineCommand128 } from "citty";
15356
+ import { defineCommand as defineCommand121 } from "citty";
15477
15357
 
15478
15358
  // src/commands/scheduled-actions/shared.ts
15479
15359
  var TEMP_SCHEDULED_ACTION_PREFIX = "temp_sched_";
@@ -15578,7 +15458,7 @@ registerSchema({
15578
15458
  prompt: { type: "string", description: "Additional prompt instructions for the spawned agent", required: false }
15579
15459
  }
15580
15460
  });
15581
- var createCommand3 = defineCommand128({
15461
+ var createCommand2 = defineCommand121({
15582
15462
  meta: {
15583
15463
  name: "create",
15584
15464
  description: 'Stage a scheduled action. Example: baker scheduled-actions create --name "Weekly report" --description "..." --cron "0 9 * * MON"'
@@ -15626,7 +15506,7 @@ var createCommand3 = defineCommand128({
15626
15506
  });
15627
15507
 
15628
15508
  // src/commands/scheduled-actions/delete.ts
15629
- import { defineCommand as defineCommand129 } from "citty";
15509
+ import { defineCommand as defineCommand122 } from "citty";
15630
15510
  registerSchema({
15631
15511
  command: "scheduled-actions.delete",
15632
15512
  description: "Stage deletion of a published scheduled action or cancellation of a temp_sched_* draft creation.",
@@ -15634,7 +15514,7 @@ registerSchema({
15634
15514
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
15635
15515
  }
15636
15516
  });
15637
- var deleteCommand2 = defineCommand129({
15517
+ var deleteCommand2 = defineCommand122({
15638
15518
  meta: {
15639
15519
  name: "delete",
15640
15520
  description: "Stage scheduled action deletion. Example: baker scheduled-actions delete <id-or-temp_sched_id>"
@@ -15663,7 +15543,7 @@ var deleteCommand2 = defineCommand129({
15663
15543
  });
15664
15544
 
15665
15545
  // src/commands/scheduled-actions/get.ts
15666
- import { defineCommand as defineCommand130 } from "citty";
15546
+ import { defineCommand as defineCommand123 } from "citty";
15667
15547
  registerSchema({
15668
15548
  command: "scheduled-actions.get",
15669
15549
  description: "Get a published scheduled action or a temp_sched_* draft-created scheduled action.",
@@ -15671,7 +15551,7 @@ registerSchema({
15671
15551
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
15672
15552
  }
15673
15553
  });
15674
- var getCommand4 = defineCommand130({
15554
+ var getCommand3 = defineCommand123({
15675
15555
  meta: {
15676
15556
  name: "get",
15677
15557
  description: "Get a scheduled action. Example: baker scheduled-actions get <id-or-temp_sched_id>"
@@ -15708,13 +15588,13 @@ var getCommand4 = defineCommand130({
15708
15588
  });
15709
15589
 
15710
15590
  // src/commands/scheduled-actions/list.ts
15711
- import { defineCommand as defineCommand131 } from "citty";
15591
+ import { defineCommand as defineCommand124 } from "citty";
15712
15592
  registerSchema({
15713
15593
  command: "scheduled-actions.list",
15714
15594
  description: "List published scheduled actions. Includes draft state when BAKER_CHAT_ID is set.",
15715
15595
  args: {}
15716
15596
  });
15717
- var listCommand3 = defineCommand131({
15597
+ var listCommand2 = defineCommand124({
15718
15598
  meta: {
15719
15599
  name: "list",
15720
15600
  description: "List scheduled actions. Includes staged draft ops when BAKER_CHAT_ID is set."
@@ -15735,7 +15615,7 @@ var listCommand3 = defineCommand131({
15735
15615
  });
15736
15616
 
15737
15617
  // src/commands/scheduled-actions/trigger.ts
15738
- import { defineCommand as defineCommand132 } from "citty";
15618
+ import { defineCommand as defineCommand125 } from "citty";
15739
15619
  registerSchema({
15740
15620
  command: "scheduled-actions.trigger",
15741
15621
  description: "Immediately trigger a published scheduled action. Does not require BAKER_CHAT_ID and rejects temp_sched_* IDs.",
@@ -15743,7 +15623,7 @@ registerSchema({
15743
15623
  id: { type: "string", description: "Published scheduled action ID", required: true }
15744
15624
  }
15745
15625
  });
15746
- var triggerCommand = defineCommand132({
15626
+ var triggerCommand = defineCommand125({
15747
15627
  meta: {
15748
15628
  name: "trigger",
15749
15629
  description: "Immediately trigger a published scheduled action. Example: baker scheduled-actions trigger <id>"
@@ -15780,7 +15660,7 @@ var triggerCommand = defineCommand132({
15780
15660
  });
15781
15661
 
15782
15662
  // src/commands/scheduled-actions/update.ts
15783
- import { defineCommand as defineCommand133 } from "citty";
15663
+ import { defineCommand as defineCommand126 } from "citty";
15784
15664
  registerSchema({
15785
15665
  command: "scheduled-actions.update",
15786
15666
  description: "Stage an update to a published scheduled action or temp_sched_* draft-created scheduled action.",
@@ -15805,7 +15685,7 @@ registerSchema({
15805
15685
  prompt: { type: "string", description: "Replacement additional spawned-agent instructions", required: false }
15806
15686
  }
15807
15687
  });
15808
- var updateCommand3 = defineCommand133({
15688
+ var updateCommand2 = defineCommand126({
15809
15689
  meta: {
15810
15690
  name: "update",
15811
15691
  description: "Stage a scheduled action update. Example: baker scheduled-actions update <id> --enabled false"
@@ -15875,7 +15755,7 @@ var updateCommand3 = defineCommand133({
15875
15755
  });
15876
15756
 
15877
15757
  // src/commands/scheduled-actions/index.ts
15878
- var scheduledActionsCommand = defineCommand134({
15758
+ var scheduledActionsCommand = defineCommand127({
15879
15759
  meta: {
15880
15760
  name: "scheduled-actions",
15881
15761
  description: `Manage Scheduled Actions. Subcommands: list, get, create, update, delete, trigger.
@@ -15891,18 +15771,18 @@ Examples:
15891
15771
  baker scheduled-actions trigger <id>`
15892
15772
  },
15893
15773
  subCommands: {
15894
- list: listCommand3,
15895
- get: getCommand4,
15896
- create: createCommand3,
15897
- update: updateCommand3,
15774
+ list: listCommand2,
15775
+ get: getCommand3,
15776
+ create: createCommand2,
15777
+ update: updateCommand2,
15898
15778
  delete: deleteCommand2,
15899
15779
  trigger: triggerCommand
15900
15780
  }
15901
15781
  });
15902
15782
 
15903
15783
  // src/commands/schema.ts
15904
- import { defineCommand as defineCommand135 } from "citty";
15905
- var schemaCommand = defineCommand135({
15784
+ import { defineCommand as defineCommand128 } from "citty";
15785
+ var schemaCommand = defineCommand128({
15906
15786
  meta: {
15907
15787
  name: "schema",
15908
15788
  description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images.search"
@@ -15938,10 +15818,10 @@ var schemaCommand = defineCommand135({
15938
15818
  });
15939
15819
 
15940
15820
  // src/commands/testimonials/index.ts
15941
- import { defineCommand as defineCommand139 } from "citty";
15821
+ import { defineCommand as defineCommand132 } from "citty";
15942
15822
 
15943
15823
  // src/commands/testimonials/get.ts
15944
- import { defineCommand as defineCommand136 } from "citty";
15824
+ import { defineCommand as defineCommand129 } from "citty";
15945
15825
  registerSchema({
15946
15826
  command: "testimonials.get",
15947
15827
  description: "Get a single testimonial by ID",
@@ -15949,7 +15829,7 @@ registerSchema({
15949
15829
  id: { type: "string", description: "Testimonial ID", required: true }
15950
15830
  }
15951
15831
  });
15952
- var getCommand5 = defineCommand136({
15832
+ var getCommand4 = defineCommand129({
15953
15833
  meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
15954
15834
  args: {
15955
15835
  id: { type: "positional", description: "Testimonial ID", required: false },
@@ -15986,7 +15866,7 @@ var getCommand5 = defineCommand136({
15986
15866
  });
15987
15867
 
15988
15868
  // src/commands/testimonials/list.ts
15989
- import { defineCommand as defineCommand137 } from "citty";
15869
+ import { defineCommand as defineCommand130 } from "citty";
15990
15870
  registerSchema({
15991
15871
  command: "testimonials.list",
15992
15872
  description: "List testimonials with optional filters.",
@@ -16016,7 +15896,7 @@ registerSchema({
16016
15896
  limit: { type: "number", description: "Max results (default 50)", required: false, default: 50 }
16017
15897
  }
16018
15898
  });
16019
- var listCommand4 = defineCommand137({
15899
+ var listCommand3 = defineCommand130({
16020
15900
  meta: {
16021
15901
  name: "list",
16022
15902
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
@@ -16065,7 +15945,7 @@ var listCommand4 = defineCommand137({
16065
15945
  });
16066
15946
 
16067
15947
  // src/commands/testimonials/search.ts
16068
- import { defineCommand as defineCommand138 } from "citty";
15948
+ import { defineCommand as defineCommand131 } from "citty";
16069
15949
  registerSchema({
16070
15950
  command: "testimonials.search",
16071
15951
  description: "Search testimonials by text query. Uses hybrid BM25 + vector + reranking.",
@@ -16096,7 +15976,7 @@ registerSchema({
16096
15976
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
16097
15977
  }
16098
15978
  });
16099
- var searchCommand2 = defineCommand138({
15979
+ var searchCommand2 = defineCommand131({
16100
15980
  meta: {
16101
15981
  name: "search",
16102
15982
  description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
@@ -16167,7 +16047,7 @@ var searchCommand2 = defineCommand138({
16167
16047
  });
16168
16048
 
16169
16049
  // src/commands/testimonials/index.ts
16170
- var testimonialsCommand = defineCommand139({
16050
+ var testimonialsCommand = defineCommand132({
16171
16051
  meta: {
16172
16052
  name: "testimonials",
16173
16053
  description: `Find and browse testimonials in Baker. Subcommands: search, get, list.
@@ -16179,17 +16059,17 @@ Examples:
16179
16059
  baker testimonials list --source google --sentiment positive`
16180
16060
  },
16181
16061
  subCommands: {
16182
- get: getCommand5,
16062
+ get: getCommand4,
16183
16063
  search: searchCommand2,
16184
- list: listCommand4
16064
+ list: listCommand3
16185
16065
  }
16186
16066
  });
16187
16067
 
16188
16068
  // src/commands/videos/index.ts
16189
- import { defineCommand as defineCommand144 } from "citty";
16069
+ import { defineCommand as defineCommand137 } from "citty";
16190
16070
 
16191
16071
  // src/commands/videos/delete.ts
16192
- import { defineCommand as defineCommand140 } from "citty";
16072
+ import { defineCommand as defineCommand133 } from "citty";
16193
16073
  registerSchema({
16194
16074
  command: "videos.delete",
16195
16075
  description: "Delete a video by ID",
@@ -16203,7 +16083,7 @@ registerSchema({
16203
16083
  }
16204
16084
  }
16205
16085
  });
16206
- var deleteCommand3 = defineCommand140({
16086
+ var deleteCommand3 = defineCommand133({
16207
16087
  meta: {
16208
16088
  name: "delete",
16209
16089
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -16244,7 +16124,7 @@ var deleteCommand3 = defineCommand140({
16244
16124
  });
16245
16125
 
16246
16126
  // src/commands/videos/get.ts
16247
- import { defineCommand as defineCommand141 } from "citty";
16127
+ import { defineCommand as defineCommand134 } from "citty";
16248
16128
  registerSchema({
16249
16129
  command: "videos.get",
16250
16130
  description: "Get a single video by ID",
@@ -16252,7 +16132,7 @@ registerSchema({
16252
16132
  id: { type: "string", description: "Video ID", required: true }
16253
16133
  }
16254
16134
  });
16255
- var getCommand6 = defineCommand141({
16135
+ var getCommand5 = defineCommand134({
16256
16136
  meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
16257
16137
  args: {
16258
16138
  id: { type: "positional", description: "Video ID", required: false },
@@ -16289,7 +16169,7 @@ var getCommand6 = defineCommand141({
16289
16169
  });
16290
16170
 
16291
16171
  // src/commands/videos/search.ts
16292
- import { defineCommand as defineCommand142 } from "citty";
16172
+ import { defineCommand as defineCommand135 } from "citty";
16293
16173
  registerSchema({
16294
16174
  command: "videos.search",
16295
16175
  description: "Search videos by text query. Only returns ready videos.",
@@ -16299,7 +16179,7 @@ registerSchema({
16299
16179
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
16300
16180
  }
16301
16181
  });
16302
- var searchCommand3 = defineCommand142({
16182
+ var searchCommand3 = defineCommand135({
16303
16183
  meta: {
16304
16184
  name: "search",
16305
16185
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -16348,7 +16228,7 @@ var searchCommand3 = defineCommand142({
16348
16228
  // src/commands/videos/upload.ts
16349
16229
  import { readFile as readFile10, stat as stat3 } from "fs/promises";
16350
16230
  import { extname as extname3 } from "path";
16351
- import { defineCommand as defineCommand143 } from "citty";
16231
+ import { defineCommand as defineCommand136 } from "citty";
16352
16232
  var MIME_MAP2 = {
16353
16233
  ".mp4": "video/mp4",
16354
16234
  ".mov": "video/quicktime",
@@ -16382,7 +16262,7 @@ function detectContentType2(filePath) {
16382
16262
  }
16383
16263
  return mime;
16384
16264
  }
16385
- var uploadCommand2 = defineCommand143({
16265
+ var uploadCommand2 = defineCommand136({
16386
16266
  meta: {
16387
16267
  name: "upload",
16388
16268
  description: "Upload a video file to Baker via Mux direct upload. Auto-detects content type. Example: baker videos upload ./demo.mp4"
@@ -16436,7 +16316,7 @@ var uploadCommand2 = defineCommand143({
16436
16316
  });
16437
16317
 
16438
16318
  // src/commands/videos/index.ts
16439
- var videosCommand = defineCommand144({
16319
+ var videosCommand = defineCommand137({
16440
16320
  meta: {
16441
16321
  name: "videos",
16442
16322
  description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete.
@@ -16449,7 +16329,7 @@ Examples:
16449
16329
  baker videos delete <video-id> --dry-run`
16450
16330
  },
16451
16331
  subCommands: {
16452
- get: getCommand6,
16332
+ get: getCommand5,
16453
16333
  search: searchCommand3,
16454
16334
  upload: uploadCommand2,
16455
16335
  delete: deleteCommand3
@@ -16457,10 +16337,10 @@ Examples:
16457
16337
  });
16458
16338
 
16459
16339
  // src/commands/winning-ads/index.ts
16460
- import { defineCommand as defineCommand147 } from "citty";
16340
+ import { defineCommand as defineCommand140 } from "citty";
16461
16341
 
16462
16342
  // src/commands/winning-ads/advertisers.ts
16463
- import { defineCommand as defineCommand145 } from "citty";
16343
+ import { defineCommand as defineCommand138 } from "citty";
16464
16344
  registerSchema({
16465
16345
  command: "winning-ads.advertisers",
16466
16346
  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).",
@@ -16473,7 +16353,7 @@ registerSchema({
16473
16353
  function identity(record) {
16474
16354
  return record;
16475
16355
  }
16476
- var advertisersCommand2 = defineCommand145({
16356
+ var advertisersCommand2 = defineCommand138({
16477
16357
  meta: {
16478
16358
  name: "advertisers",
16479
16359
  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'
@@ -16524,7 +16404,7 @@ var advertisersCommand2 = defineCommand145({
16524
16404
  });
16525
16405
 
16526
16406
  // src/commands/winning-ads/search.ts
16527
- import { defineCommand as defineCommand146 } from "citty";
16407
+ import { defineCommand as defineCommand139 } from "citty";
16528
16408
  registerSchema({
16529
16409
  command: "winning-ads.search",
16530
16410
  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.",
@@ -16632,7 +16512,7 @@ function buildSearchBody(args) {
16632
16512
  }
16633
16513
  return body;
16634
16514
  }
16635
- var searchCommand4 = defineCommand146({
16515
+ var searchCommand4 = defineCommand139({
16636
16516
  meta: {
16637
16517
  name: "search",
16638
16518
  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"
@@ -16744,7 +16624,7 @@ var searchCommand4 = defineCommand146({
16744
16624
  });
16745
16625
 
16746
16626
  // src/commands/winning-ads/index.ts
16747
- var winningAdsCommand = defineCommand147({
16627
+ var winningAdsCommand = defineCommand140({
16748
16628
  meta: {
16749
16629
  name: "winning-ads",
16750
16630
  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.
@@ -16784,7 +16664,7 @@ function getCliVersion() {
16784
16664
  }
16785
16665
 
16786
16666
  // src/cli.ts
16787
- var main = defineCommand148({
16667
+ var main = defineCommand141({
16788
16668
  meta: {
16789
16669
  name: "baker",
16790
16670
  version: getCliVersion(),
@@ -16798,7 +16678,6 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
16798
16678
  },
16799
16679
  subCommands: {
16800
16680
  actions: actionsCommand,
16801
- missions: missionsCommand,
16802
16681
  "scheduled-actions": scheduledActionsCommand,
16803
16682
  ads: adsCommand,
16804
16683
  ga4: ga4Command,