@koda-sl/baker-cli 0.97.0 → 0.98.1-dev.662146ce

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-RCPMJKI7.js";
12
+ } from "./chunk-26K7V346.js";
13
13
 
14
14
  // src/cli.ts
15
- import { defineCommand as defineCommand148, runMain } from "citty";
15
+ import { defineCommand as defineCommand151, runMain } from "citty";
16
16
 
17
17
  // src/commands/actions/index.ts
18
18
  import { defineCommand as defineCommand17 } from "citty";
@@ -147,9 +147,9 @@ async function handleResponse(response) {
147
147
  throw new ApiError("INTERNAL_ERROR", "Failed to parse API response as JSON");
148
148
  }
149
149
  }
150
- async function apiGet(path7, params) {
150
+ async function apiGet(path11, params) {
151
151
  const env = getEnv();
152
- const url = new URL(path7, env.BAKER_API_URL);
152
+ const url = new URL(path11, env.BAKER_API_URL);
153
153
  if (params) {
154
154
  const clean = sanitizeParams(params);
155
155
  for (const [key, value] of Object.entries(clean)) {
@@ -174,12 +174,12 @@ async function apiGet(path7, params) {
174
174
  }
175
175
  return handleResponse(response);
176
176
  }
177
- async function apiPost(path7, body, opts) {
177
+ async function apiPost(path11, body, opts) {
178
178
  const env = getEnv();
179
179
  const timeoutMs = opts?.timeoutMs ?? 6e4;
180
180
  let response;
181
181
  try {
182
- response = await fetchWithRateLimitRetry(new URL(path7, env.BAKER_API_URL).toString(), {
182
+ response = await fetchWithRateLimitRetry(new URL(path11, env.BAKER_API_URL).toString(), {
183
183
  method: "POST",
184
184
  headers: {
185
185
  Authorization: `Bearer ${env.BAKER_API_KEY}`,
@@ -1327,31 +1327,31 @@ function cachePath(category, key) {
1327
1327
  return join(dir, `${hashKey(key)}.json`);
1328
1328
  }
1329
1329
  function cacheGet(category, key) {
1330
- const path7 = cachePath(category, key);
1331
- if (!existsSync(path7)) {
1330
+ const path11 = cachePath(category, key);
1331
+ if (!existsSync(path11)) {
1332
1332
  return null;
1333
1333
  }
1334
1334
  try {
1335
- const raw = readFileSync(path7, "utf-8");
1335
+ const raw = readFileSync(path11, "utf-8");
1336
1336
  const entry = JSON.parse(raw);
1337
1337
  if (entry.expiresAt < Date.now()) {
1338
- rmSync(path7, { force: true });
1338
+ rmSync(path11, { force: true });
1339
1339
  return null;
1340
1340
  }
1341
1341
  return entry;
1342
1342
  } catch {
1343
- rmSync(path7, { force: true });
1343
+ rmSync(path11, { force: true });
1344
1344
  return null;
1345
1345
  }
1346
1346
  }
1347
1347
  function cacheSet(category, key, data, ttlMs, fields) {
1348
- const path7 = cachePath(category, key);
1348
+ const path11 = cachePath(category, key);
1349
1349
  const entry = {
1350
1350
  expiresAt: Date.now() + ttlMs,
1351
1351
  data,
1352
1352
  fields
1353
1353
  };
1354
- writeFileSync(path7, JSON.stringify(entry), "utf-8");
1354
+ writeFileSync(path11, JSON.stringify(entry), "utf-8");
1355
1355
  }
1356
1356
  var HOUR = 60 * 60 * 1e3;
1357
1357
  var MINUTE = 60 * 1e3;
@@ -8045,7 +8045,7 @@ Examples:
8045
8045
  });
8046
8046
 
8047
8047
  // src/commands/canvas/index.ts
8048
- import { defineCommand as defineCommand84 } from "citty";
8048
+ import { defineCommand as defineCommand85 } from "citty";
8049
8049
 
8050
8050
  // src/commands/canvas/catalog.ts
8051
8051
  import { defineCommand as defineCommand78 } from "citty";
@@ -8176,7 +8176,7 @@ async function probeDuration(filePath) {
8176
8176
 
8177
8177
  // src/commands/canvas/run.ts
8178
8178
  import { readFile as readFile2 } from "fs/promises";
8179
- import path2 from "path";
8179
+ import path4 from "path";
8180
8180
  import { defineCommand as defineCommand80 } from "citty";
8181
8181
 
8182
8182
  // src/commands/canvas/placeholders.ts
@@ -8195,6 +8195,57 @@ function unsuppliedPlaceholderAssets(canvas) {
8195
8195
  return out;
8196
8196
  }
8197
8197
 
8198
+ // src/commands/canvas/resolve-paths.ts
8199
+ import path2 from "path";
8200
+ function resolveRelativeCanvasPaths(canvas, baseDir) {
8201
+ if (!canvas || typeof canvas !== "object") return canvas;
8202
+ const c = canvas;
8203
+ if (!Array.isArray(c.nodes)) return canvas;
8204
+ return { ...canvas, nodes: c.nodes.map((n) => resolveNode(n, baseDir)) };
8205
+ }
8206
+ function resolveNode(node, baseDir) {
8207
+ if (!node || typeof node !== "object") return node;
8208
+ const n = node;
8209
+ const params = n.params;
8210
+ if (!params || typeof params !== "object") return node;
8211
+ if (n.type === "ingest" && params.source === "path" && isResolvableRelative(params.path)) {
8212
+ return { ...node, params: { ...params, path: path2.resolve(baseDir, params.path) } };
8213
+ }
8214
+ if (n.type === "hyperframe_render" && isResolvableRelative(params.composition)) {
8215
+ return { ...node, params: { ...params, composition: path2.resolve(baseDir, params.composition) } };
8216
+ }
8217
+ return node;
8218
+ }
8219
+ function isResolvableRelative(value) {
8220
+ return typeof value === "string" && value.length > 0 && !value.includes("[TODO") && !path2.isAbsolute(value);
8221
+ }
8222
+
8223
+ // src/commands/canvas/run-retention.ts
8224
+ import { rm } from "fs/promises";
8225
+ import path3 from "path";
8226
+ function runDirsToPrune(entries, keep, currentRunId) {
8227
+ const runs = entries.filter((e) => /^r_[0-9A-Za-z]+$/.test(e) && e !== currentRunId).sort();
8228
+ if (keep <= 0) return runs;
8229
+ return runs.slice(0, Math.max(0, runs.length - keep));
8230
+ }
8231
+ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
8232
+ const { readdir: readdir3 } = await import("fs/promises");
8233
+ let entries;
8234
+ try {
8235
+ entries = await readdir3(outputsDir);
8236
+ } catch {
8237
+ return;
8238
+ }
8239
+ const toPrune = runDirsToPrune(entries, keep, currentRunId);
8240
+ if (toPrune.length === 0) return;
8241
+ for (const dir of toPrune) {
8242
+ await rm(path3.join(outputsDir, dir), { recursive: true, force: true }).catch(
8243
+ (e) => log(`[prune ] could not remove ${dir}: ${e.message}`)
8244
+ );
8245
+ }
8246
+ log(`[prune ] removed ${toPrune.length} old run dir(s), kept the ${keep} newest`);
8247
+ }
8248
+
8198
8249
  // src/commands/canvas/run.ts
8199
8250
  var runCommand = defineCommand80({
8200
8251
  meta: { name: "run", description: "Validate and execute a canvas JSON file." },
@@ -8203,10 +8254,14 @@ var runCommand = defineCommand80({
8203
8254
  "cache-dir": { type: "string", description: "Cache root (default ./canvas/.cache)" },
8204
8255
  "outputs-dir": { type: "string", description: "Per-run outputs root (default ./canvas)" },
8205
8256
  "run-id": { type: "string", description: "Override run id" },
8206
- "cache-policy": { type: "string", description: "read_write | bypass | read_only" }
8257
+ "cache-policy": { type: "string", description: "read_write | bypass | read_only" },
8258
+ "keep-runs": {
8259
+ type: "string",
8260
+ description: "After the run, prune old r_* run dirs, keeping the N newest (off by default)"
8261
+ }
8207
8262
  },
8208
8263
  async run({ args }) {
8209
- const filePath = path2.resolve(String(args.file));
8264
+ const filePath = path4.resolve(String(args.file));
8210
8265
  const raw = await readFile2(filePath, "utf8");
8211
8266
  let parsed;
8212
8267
  try {
@@ -8217,6 +8272,7 @@ var runCommand = defineCommand80({
8217
8272
  `);
8218
8273
  process.exit(2);
8219
8274
  }
8275
+ parsed = resolveRelativeCanvasPaths(parsed, path4.dirname(filePath));
8220
8276
  const pending = unsuppliedPlaceholderAssets(parsed);
8221
8277
  if (pending.length > 0) {
8222
8278
  process.stderr.write(
@@ -8248,6 +8304,12 @@ var runCommand = defineCommand80({
8248
8304
  run_id: args["run-id"] ? String(args["run-id"]) : void 0,
8249
8305
  cache_policy: policy
8250
8306
  });
8307
+ const keepRuns = args["keep-runs"] !== void 0 ? Number(args["keep-runs"]) : void 0;
8308
+ if (keepRuns !== void 0 && Number.isFinite(keepRuns)) {
8309
+ const outputsDir = args["outputs-dir"] ? path4.resolve(String(args["outputs-dir"])) : path4.resolve("canvas");
8310
+ await pruneOldRuns(outputsDir, keepRuns, result.run_id, (line) => process.stdout.write(`${line}
8311
+ `));
8312
+ }
8251
8313
  process.stdout.write(
8252
8314
  `${JSON.stringify(
8253
8315
  {
@@ -8280,7 +8342,7 @@ var runCommand = defineCommand80({
8280
8342
 
8281
8343
  // src/commands/canvas/scaffold-static-ad.ts
8282
8344
  import { readFile as readFile3, writeFile } from "fs/promises";
8283
- import path3 from "path";
8345
+ import path5 from "path";
8284
8346
  import { defineCommand as defineCommand81 } from "citty";
8285
8347
 
8286
8348
  // src/engine/scaffold/staticAd.ts
@@ -8617,10 +8679,10 @@ var scaffoldStaticAdCommand = defineCommand81({
8617
8679
  "skip-font": { type: "boolean", description: "Skip the brand-font \u2192 type-specimen slot" }
8618
8680
  },
8619
8681
  async run({ args }) {
8620
- const imagePath = path3.resolve(String(args.file));
8621
- const outPath = args.out ? path3.resolve(String(args.out)) : path3.join(path3.dirname(imagePath), "static-ad.canvas.json");
8622
- const outDir = path3.dirname(outPath);
8623
- const blueprintPath = path3.join(outDir, "prompt.json");
8682
+ const imagePath = path5.resolve(String(args.file));
8683
+ const outPath = args.out ? path5.resolve(String(args.out)) : path5.join(path5.dirname(imagePath), "static-ad.canvas.json");
8684
+ const outDir = path5.dirname(outPath);
8685
+ const blueprintPath = path5.join(outDir, "prompt.json");
8624
8686
  const { describeModel, selectModel, layoutModel, genModel } = resolveModels(args);
8625
8687
  const describeCanvas = buildDescribeCanvas(
8626
8688
  imagePath,
@@ -8677,7 +8739,7 @@ var scaffoldStaticAdCommand = defineCommand81({
8677
8739
  run_estimated_credits: validation.estimatedCredits
8678
8740
  },
8679
8741
  checklist: {
8680
- edit_prompt: `Edit ${path3.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.`,
8742
+ edit_prompt: `Edit ${path5.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.`,
8681
8743
  assets_to_supply: report.elements,
8682
8744
  font_slot: report.includes_font ? "Drop a brand font at the [TODO] brandfont path, or delete the brandfont + type_ref nodes to skip it." : "skipped (--skip-font)",
8683
8745
  note: "Replace every [TODO] ingest path with a real file, then `baker canvas validate` and `baker canvas run`. Running generates a billed image \u2014 it is not free."
@@ -8692,13 +8754,13 @@ var scaffoldStaticAdCommand = defineCommand81({
8692
8754
  });
8693
8755
 
8694
8756
  // src/commands/canvas/scaffold-video.ts
8695
- import { cp, mkdir, readFile as readFile5, writeFile as writeFile2 } from "fs/promises";
8696
- import path5 from "path";
8757
+ import { cp, mkdir, readFile as readFile6, writeFile as writeFile2 } from "fs/promises";
8758
+ import path8 from "path";
8697
8759
  import { defineCommand as defineCommand82 } from "citty";
8698
8760
 
8699
8761
  // src/engine/nodes/local/lib/sceneDetect.ts
8700
8762
  import { execFile as execFile2 } from "child_process";
8701
- import { mkdtemp, readdir as readdir2, readFile as readFile4, rm } from "fs/promises";
8763
+ import { mkdtemp, readdir as readdir2, readFile as readFile4, rm as rm2 } from "fs/promises";
8702
8764
  import { tmpdir } from "os";
8703
8765
  import { join as join2 } from "path";
8704
8766
  import { promisify as promisify2 } from "util";
@@ -8766,7 +8828,7 @@ async function runSceneDetectOnce(filePath, threshold, minSceneLenS, timeoutMs)
8766
8828
  if (!csvName) return [];
8767
8829
  return parsePySceneDetectCsvCuts(await readFile4(join2(outDir, csvName), "utf-8"));
8768
8830
  } finally {
8769
- await rm(outDir, { recursive: true, force: true });
8831
+ await rm2(outDir, { recursive: true, force: true });
8770
8832
  }
8771
8833
  }
8772
8834
  async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
@@ -9022,6 +9084,13 @@ function stillHoldArgs(durationS, dims) {
9022
9084
  `scale=${dims.w}:${dims.h}:force_original_aspect_ratio=increase,crop=${dims.w}:${dims.h},setsar=1,format=yuv420p`,
9023
9085
  "-c:v",
9024
9086
  "libx264",
9087
+ // Near-visually-lossless re-encode. libx264 DEFAULTS (crf 23, preset medium)
9088
+ // roughly halve the source bitrate and add banding on smooth surfaces; the
9089
+ // spine concats these by stream-copy, so any loss here ships to the final cut.
9090
+ "-crf",
9091
+ "18",
9092
+ "-preset",
9093
+ "slow",
9025
9094
  "-pix_fmt",
9026
9095
  "yuv420p",
9027
9096
  "{{out.video}}"
@@ -9037,6 +9106,13 @@ function trimArgs(durationS, offsetS = 0) {
9037
9106
  "-an",
9038
9107
  "-c:v",
9039
9108
  "libx264",
9109
+ // Preserve the seedance source quality through the trim. libx264 DEFAULTS
9110
+ // (crf 23) halve the bitrate (measured 9.57→4.40 Mbps) and band on motion;
9111
+ // the spine stream-copies the result, so the loss is permanent without this.
9112
+ "-crf",
9113
+ "18",
9114
+ "-preset",
9115
+ "slow",
9040
9116
  "-pix_fmt",
9041
9117
  "yuv420p",
9042
9118
  "{{out.video}}"
@@ -9103,6 +9179,13 @@ var Scene = z3.object({
9103
9179
  // The scene's role in the ad's persuasion arc (DECON-supplied); drives the
9104
9180
  // script re-craft checklist. Inferred from position when absent.
9105
9181
  narrative_role: z3.string().optional(),
9182
+ // DECON-supplied on the HOOK scene: the engineered physical/emotional state that
9183
+ // makes the first frame stop the scroll (sweaty/breathless/urgent …). Injected
9184
+ // into the hook's start-frame description so the generator renders that state,
9185
+ // not a calm influencer (CCA-11).
9186
+ hook_mechanic: z3.object({ mechanic: z3.string().optional(), why_it_stops_scroll: z3.string().optional() }).loose().optional(),
9187
+ // DECON-supplied per-scene location (so a gym hook isn't flattened to "home").
9188
+ scene_setting: z3.string().optional(),
9106
9189
  // How this scene cuts to the next (DECON-supplied). A recognized non-cut type
9107
9190
  // (fade/whip/zoom/dissolve/swipe) is reproduced as an ffmpeg xfade at the
9108
9191
  // boundary; cut/match_cut/none/other stay hard cuts. The last scene's value is
@@ -9150,10 +9233,21 @@ var VideoBlueprint = z3.object({
9150
9233
  mode: z3.string().optional(),
9151
9234
  voice_description: z3.string().optional(),
9152
9235
  persona: z3.string().optional()
9153
- }).loose().optional()
9236
+ }).loose().optional(),
9237
+ // Visual palette — read only to colour a clean brand-card/CTA plate (the
9238
+ // first hex is the dominant brand colour); never to drive frame generation.
9239
+ style: z3.object({ palette: z3.array(z3.object({ hex: z3.string().optional() }).loose()).optional() }).loose().optional()
9154
9240
  }).loose().optional(),
9155
9241
  scenes: z3.array(Scene).min(1)
9156
9242
  }).loose();
9243
+ function injectHookPhysicality(blueprint) {
9244
+ for (const scene of blueprint.scenes) {
9245
+ const why = scene.hook_mechanic?.why_it_stops_scroll?.trim();
9246
+ const prompt = scene.start_frame_prompt?.trim();
9247
+ if (!why || !prompt || prompt.includes(why)) continue;
9248
+ scene.start_frame_prompt = `${prompt} The subject's physical state IS the scroll-stopper \u2014 render it explicitly, not a calm pose: ${why}.`;
9249
+ }
9250
+ }
9157
9251
  var AppearsItem = z3.union([z3.number(), z3.object({ scene: z3.number(), edge: z3.string().optional() }).loose()]);
9158
9252
  var RecurringElement = z3.object({
9159
9253
  // person | animal | product | logo | badge | other
@@ -9179,7 +9273,8 @@ function sanitizeId2(raw, fallback) {
9179
9273
  return /^[a-z]/.test(id) ? id : `${fallback}_${id}`.replace(/_+$/g, "") || fallback;
9180
9274
  }
9181
9275
  function labelFor2(el, used) {
9182
- const base = (el.label ?? el.type ?? "ELEMENT").toUpperCase().replace(/[^A-Z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "ELEMENT";
9276
+ const raw = el.type?.toLowerCase() === "logo" ? "BRAND_LOGO" : el.label ?? el.type ?? "ELEMENT";
9277
+ const base = raw.toUpperCase().replace(/[^A-Z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "ELEMENT";
9183
9278
  let label = base;
9184
9279
  let n = 2;
9185
9280
  while (used.has(label)) label = `${base}_${n++}`;
@@ -9346,6 +9441,8 @@ function buildElementSheets(slots, nodes) {
9346
9441
  if (slot.sameAs) continue;
9347
9442
  if (slot.presence.size < 1) continue;
9348
9443
  const sheetId = `${slot.id}_sheet`;
9444
+ const slotType = slot.type.toLowerCase();
9445
+ const isCast = slotType === "person" || slotType === "animal";
9349
9446
  nodes.push({
9350
9447
  id: sheetId,
9351
9448
  type: "image_reference_sheet",
@@ -9358,7 +9455,14 @@ function buildElementSheets(slots, nodes) {
9358
9455
  // 4K: the sheet packs up to 8 cells (angles + tight face/detail close-ups), and
9359
9456
  // it's the ONE reference every frame grounds on — per-cell sharpness here
9360
9457
  // propagates to every clip, so it's worth the highest tier on this single asset.
9361
- image_size: "4K"
9458
+ image_size: "4K",
9459
+ // The sheet is the look that propagates to EVERY grounded frame, so a glossy
9460
+ // studio turnaround makes the whole UGC ad read as "produced" (the #1 AI tell).
9461
+ // Force a flat, real, front-camera look on the cast sheet so the actor stays
9462
+ // authentic, not an airbrushed influencer (CCA-02).
9463
+ ...isCast ? {
9464
+ style: "authentic UGC look: flat, even, natural front-camera lighting \u2014 no studio key/rim light, no seamless backdrop, no shallow depth of field; real skin texture and pores, no airbrushing or beauty retouch; true-to-life everyday styling"
9465
+ } : {}
9362
9466
  }
9363
9467
  });
9364
9468
  slot.ref = `$ref:${sheetId}.sheet`;
@@ -9466,8 +9570,7 @@ function buildFrameRef(edge, url, framePrompt, present, ctx, nodes) {
9466
9570
  const t = s.type.toLowerCase();
9467
9571
  return t === "person" || t === "animal";
9468
9572
  });
9469
- const castIdentityLocked = castSlots.every((s) => s.sheetBacked);
9470
- const useOriginalAnchor = Boolean(url) && (castSlots.length === 0 || castIdentityLocked);
9573
+ const useOriginalAnchor = Boolean(url) && castSlots.length === 0;
9471
9574
  const hasOriginal = useOriginalAnchor;
9472
9575
  const originalRef = useOriginalAnchor && url ? ingestFrameRef(url, edge, ctx, nodes) : void 0;
9473
9576
  const reference = [...present.map((s) => s.ref), ...originalRef ? [originalRef] : []];
@@ -9678,6 +9781,39 @@ function isUiOnlyComposite(regions) {
9678
9781
  const ui = regions.filter(regionIsUiSurface).length;
9679
9782
  return ui >= 1 && regions.length - ui <= 1;
9680
9783
  }
9784
+ function sceneIsFullScreenUi(scene, present) {
9785
+ if (scene.narrative_role?.trim() === "cta") return false;
9786
+ const hasCast = present.some((s) => {
9787
+ const t = s.type.toLowerCase();
9788
+ return t === "person" || t === "animal";
9789
+ });
9790
+ if (hasCast) return false;
9791
+ const hay = `${scene.summary ?? ""} ${scene.start_frame_prompt ?? ""} ${scene.end_frame_prompt ?? ""} ${scene.action_detail ?? ""}`;
9792
+ return UI_SURFACE_RE.test(hay);
9793
+ }
9794
+ function screenStillArgs(durationS, dims) {
9795
+ return [
9796
+ "-loop",
9797
+ "1",
9798
+ "-i",
9799
+ "{{in.frame}}",
9800
+ "-t",
9801
+ durationS.toFixed(3),
9802
+ "-r",
9803
+ "30",
9804
+ "-vf",
9805
+ `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`,
9806
+ "-c:v",
9807
+ "libx264",
9808
+ "-crf",
9809
+ "18",
9810
+ "-preset",
9811
+ "slow",
9812
+ "-pix_fmt",
9813
+ "yuv420p",
9814
+ "{{out.video}}"
9815
+ ];
9816
+ }
9681
9817
  function layeredComposition(scene) {
9682
9818
  const comp = scene.composition;
9683
9819
  const layout = (comp?.layout ?? "").toLowerCase();
@@ -9848,6 +9984,77 @@ function emitFlashHold(i, scene, slots, ctx, lengths, out, ar, nodes, clips) {
9848
9984
  });
9849
9985
  clips.push({ ref: `$ref:s${i}_clip.video`, scene_s: lengths.dur, out });
9850
9986
  }
9987
+ function emitScreenScene(i, scene, lengths, out, ar, nodes, clips) {
9988
+ const label = commentSafe((scene.summary || scene.start_frame_prompt || "the app screen").slice(0, 120));
9989
+ const refId = `s${i}_screen_ref`;
9990
+ nodes.push({
9991
+ id: refId,
9992
+ type: "ingest",
9993
+ params: {
9994
+ source: "path",
9995
+ path: `[TODO: supply the REAL screen for "${label}" \u2014 NEVER AI-generate a UI. Capture a clean, text-free screenshot with \`baker images screenshot https://<brand-domain>/<path>\` (image-library skill); spoken/overlay text rides the overlay layer, not the screenshot]`,
9996
+ expect: "image"
9997
+ }
9998
+ });
9999
+ nodes.push({
10000
+ id: `s${i}_clip`,
10001
+ type: "ffmpeg",
10002
+ inputs: { frame: `$ref:${refId}.asset` },
10003
+ params: {
10004
+ args: screenStillArgs(lengths.trimTarget, canvasDims(ar)),
10005
+ outputs: { video: { kind: "video", ext: "mp4" } }
10006
+ }
10007
+ });
10008
+ clips.push({ ref: `$ref:s${i}_clip.video`, scene_s: lengths.dur, out });
10009
+ }
10010
+ 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;
10011
+ function sceneIsBrandCard(scene, present, isCta) {
10012
+ if (!isCta) return false;
10013
+ const hasCast = present.some((s) => {
10014
+ const t = s.type.toLowerCase();
10015
+ return t === "person" || t === "animal";
10016
+ });
10017
+ if (hasCast) return false;
10018
+ const hay = `${scene.summary ?? ""} ${scene.start_frame_prompt ?? ""} ${scene.end_frame_prompt ?? ""}`;
10019
+ return BRAND_CARD_RE.test(hay);
10020
+ }
10021
+ var HEX6_RE = /^#?[0-9a-fA-F]{6}$/;
10022
+ function brandPlateColor(blueprint) {
10023
+ const palette = blueprint.global?.style?.palette;
10024
+ const hex = palette?.map((p) => p?.hex).find((h) => typeof h === "string" && HEX6_RE.test(h));
10025
+ return hex ? `0x${hex.replace(/^#/, "").toUpperCase()}` : "0x000000";
10026
+ }
10027
+ function colorPlateArgs(durationS, dims, color) {
10028
+ return [
10029
+ "-f",
10030
+ "lavfi",
10031
+ "-i",
10032
+ `color=c=${color}:s=${dims.w}x${dims.h}:r=30`,
10033
+ "-t",
10034
+ durationS.toFixed(3),
10035
+ "-c:v",
10036
+ "libx264",
10037
+ "-crf",
10038
+ "18",
10039
+ "-preset",
10040
+ "slow",
10041
+ "-pix_fmt",
10042
+ "yuv420p",
10043
+ "{{out.video}}"
10044
+ ];
10045
+ }
10046
+ function emitBrandCardScene(i, lengths, out, ar, color, nodes, clips) {
10047
+ nodes.push({
10048
+ id: `s${i}_clip`,
10049
+ type: "ffmpeg",
10050
+ inputs: {},
10051
+ params: {
10052
+ args: colorPlateArgs(lengths.trimTarget, canvasDims(ar), color),
10053
+ outputs: { video: { kind: "video", ext: "mp4" } }
10054
+ }
10055
+ });
10056
+ clips.push({ ref: `$ref:s${i}_clip.video`, scene_s: lengths.dur, out });
10057
+ }
9851
10058
  function musicArcDigest(blueprint) {
9852
10059
  const roles = blueprint.scenes.map((s) => s.narrative_role).filter((r) => Boolean(r));
9853
10060
  const arc = roles.length > 0 ? roles.join(" \u2192 ") : "";
@@ -9967,7 +10174,7 @@ function makePresenterPresent(slots, canonical, opts = {}) {
9967
10174
  const solePerson = !opts.strict && personSlots.length === 1 ? personSlots[0].presence : null;
9968
10175
  return (speaker, sceneIndex) => {
9969
10176
  const presence = bySpeaker.get(speaker) ?? solePerson;
9970
- if (!presence) return opts.strict ? false : true;
10177
+ if (!presence) return !opts.strict;
9971
10178
  return presence.has(sceneIndex);
9972
10179
  };
9973
10180
  }
@@ -10332,6 +10539,15 @@ function emitBrollScene(scene, i, isLast, env, nodes, out, prevEndFrame) {
10332
10539
  shootMode: mode,
10333
10540
  ingestCache: env.ingestCache
10334
10541
  };
10542
+ if (!env.reuse && sceneIsFullScreenUi(scene, present)) {
10543
+ emitScreenScene(i, scene, lengths, lengths.out, env.ar, nodes, out.clips);
10544
+ return void 0;
10545
+ }
10546
+ const isCta = scene.narrative_role?.trim() === "cta" || isLast;
10547
+ if (!env.reuse && sceneIsBrandCard(scene, present, isCta)) {
10548
+ emitBrandCardScene(i, lengths, lengths.out, env.ar, brandPlateColor(env.blueprint), nodes, out.clips);
10549
+ return void 0;
10550
+ }
10335
10551
  if (!ambientBroll && lengths.dur <= FLASH_HOLD_MAX_S) {
10336
10552
  emitFlashHold(i, scene, env.slots, ctx, lengths, lengths.out, env.ar, nodes, out.clips);
10337
10553
  return void 0;
@@ -10597,7 +10813,7 @@ function overlayElement(ov, at, dur) {
10597
10813
  const normAnim = normalizeAnim(ov.animation);
10598
10814
  const anim = normAnim ? ` data-anim="${normAnim}"` : "";
10599
10815
  const detail = ov.animation_detail ? ` data-anim-detail="${escapeHtml(ov.animation_detail)}"` : "";
10600
- return `<div class="ov ${positionClass(ov.position)}" data-start="${at}" data-dur="${dur}"${role}${anim}${detail}>${escapeHtml(ov.text.trim())}</div>`;
10816
+ return `<div class="ov clip ${positionClass(ov.position)}" data-start="${at}" data-dur="${dur}"${role}${anim}${detail}>${escapeHtml(ov.text.trim())}</div>`;
10601
10817
  }
10602
10818
  var RICH_OVERLAY_RE = /notif|tweet|\bx post\b|post\b|comment|message|chat|bubble|card|review|rating|stat|counter|toast|popup/;
10603
10819
  function sourceHint(fe) {
@@ -10627,7 +10843,7 @@ function floatingStub(fe, sceneStart) {
10627
10843
  const slug = (fe.kind ?? "element").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "element";
10628
10844
  return [
10629
10845
  `<!-- ${kind}: ${label} @ ${at}s for ${dur}s (${positionClass(fe.position)}). Source a real asset: ${hint} \u2014 drop it in this dir and uncomment:`,
10630
- `<img class="ov ${positionClass(fe.position)}" src="your-${slug}.png" data-start="${at}" data-dur="${dur}" alt="" /> -->`
10846
+ `<img class="ov clip ${positionClass(fe.position)}" src="your-${slug}.png" data-start="${at}" data-dur="${dur}" alt="" /> -->`
10631
10847
  ].join("\n");
10632
10848
  }
10633
10849
  function uiPipStub(scene) {
@@ -10647,7 +10863,7 @@ function uiPipStub(scene) {
10647
10863
  " \u2014 OR hand-build a brand-accurate HTML screen; then frame it in a phone mockup:",
10648
10864
  " npx hyperframes add phone-scroll (writes compositions/phone-scroll.html)",
10649
10865
  " drop the screenshot as screenshot.png in this dir and nest it as a PIP clip:",
10650
- ` <div data-composition-src="compositions/phone-scroll.html" data-start="${at}" data-duration="${dur}" data-track-index="2" data-width="1080" data-height="1920"></div> -->`
10866
+ ` <div class="clip" data-composition-src="compositions/phone-scroll.html" data-start="${at}" data-duration="${dur}" data-track-index="2" data-width="1080" data-height="1920"></div> -->`
10651
10867
  ].join("\n");
10652
10868
  }
10653
10869
  function buildOverlayHtml(input) {
@@ -10743,6 +10959,7 @@ function buildSpine(clips, nodes) {
10743
10959
  }
10744
10960
  function scaffoldVideoCanvas(input, elementsInput, opts) {
10745
10961
  const blueprint = VideoBlueprint.parse(input);
10962
+ injectHookPhysicality(blueprint);
10746
10963
  const elements = RecurringElements.parse(elementsInput);
10747
10964
  const nodes = [];
10748
10965
  nodes.push({
@@ -11154,18 +11371,44 @@ function videoReport(input, elementsInput) {
11154
11371
 
11155
11372
  // src/commands/canvas/composition-path.ts
11156
11373
  import { existsSync as existsSync3 } from "fs";
11157
- import path4 from "path";
11374
+ import path6 from "path";
11158
11375
  function resolveShippedCanvasDir(name, startDir, exists = existsSync3, maxDepth = 8) {
11159
- const rel = path4.join("canvas", name);
11376
+ const rel = path6.join("canvas", name);
11160
11377
  let dir = startDir;
11161
11378
  for (let i = 0; i < maxDepth; i++) {
11162
- const candidate = path4.join(dir, rel);
11163
- if (exists(path4.join(candidate, "meta.json"))) return candidate;
11164
- const parent = path4.dirname(dir);
11379
+ const candidate = path6.join(dir, rel);
11380
+ if (exists(path6.join(candidate, "meta.json"))) return candidate;
11381
+ const parent = path6.dirname(dir);
11165
11382
  if (parent === dir) break;
11166
11383
  dir = parent;
11167
11384
  }
11168
- return path4.resolve(startDir, "../../../", rel);
11385
+ return path6.resolve(startDir, "../../../", rel);
11386
+ }
11387
+
11388
+ // src/commands/canvas/gitignore.ts
11389
+ import { appendFile, readFile as readFile5 } from "fs/promises";
11390
+ import path7 from "path";
11391
+ function missingGitignoreEntries(existing, entries) {
11392
+ const present = new Set(
11393
+ existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
11394
+ );
11395
+ return entries.filter((e) => !present.has(e.trim().replace(/\/+$/, "")));
11396
+ }
11397
+ async function ensureGitignore(dir, entries) {
11398
+ const file = path7.join(dir, ".gitignore");
11399
+ let existing;
11400
+ try {
11401
+ existing = await readFile5(file, "utf8");
11402
+ } catch {
11403
+ return;
11404
+ }
11405
+ const missing = missingGitignoreEntries(existing, entries);
11406
+ if (missing.length === 0) return;
11407
+ const prefix = existing.endsWith("\n") || existing.length === 0 ? "" : "\n";
11408
+ await appendFile(file, `${prefix}
11409
+ # Baker canvas (engine cache + scaffold working files)
11410
+ ${missing.join("\n")}
11411
+ `);
11169
11412
  }
11170
11413
 
11171
11414
  // src/commands/canvas/scaffold-video.ts
@@ -11194,7 +11437,7 @@ ONE PERSON, MULTIPLE LOOKS: if a single individual plays MULTIPLE personas or wa
11194
11437
  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.`;
11195
11438
  async function loadAssetText2(ref, label) {
11196
11439
  const r = ref;
11197
- if (typeof r?.path === "string") return readFile5(r.path, "utf8");
11440
+ if (typeof r?.path === "string") return readFile6(r.path, "utf8");
11198
11441
  if (typeof r?.url === "string") {
11199
11442
  const res = await fetch(r.url);
11200
11443
  if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
@@ -11213,7 +11456,7 @@ async function loadTranscriptBestEffort(ref) {
11213
11456
  async function stageCaptions(outDir, transcript) {
11214
11457
  const text = transcript?.trim();
11215
11458
  if (!text || text === "[]") return {};
11216
- const compositionPath = path5.join(outDir, "tiktok-captions-composition");
11459
+ const compositionPath = path8.join(outDir, "tiktok-captions-composition");
11217
11460
  await cp(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
11218
11461
  return { compositionPath };
11219
11462
  }
@@ -11375,11 +11618,11 @@ var scaffoldVideoCommand = defineCommand82({
11375
11618
  }
11376
11619
  },
11377
11620
  async run({ args }) {
11378
- const videoPath = path5.resolve(String(args.file));
11379
- const base = path5.basename(videoPath, path5.extname(videoPath));
11380
- const outPath = args.out ? path5.resolve(String(args.out)) : path5.join(path5.dirname(videoPath), `${base}.video.canvas.json`);
11381
- const outDir = path5.dirname(outPath);
11382
- const blueprintPath = path5.join(outDir, "prompt.json");
11621
+ const videoPath = path8.resolve(String(args.file));
11622
+ const base = path8.basename(videoPath, path8.extname(videoPath));
11623
+ const outPath = args.out ? path8.resolve(String(args.out)) : path8.join(path8.dirname(videoPath), `${base}.video.canvas.json`);
11624
+ const outDir = path8.dirname(outPath);
11625
+ const blueprintPath = path8.join(outDir, "prompt.json");
11383
11626
  const frames = args.frames === "reuse" ? "reuse" : "generate";
11384
11627
  const maxScenes = args["max-scenes"] ? Number(args["max-scenes"]) : void 0;
11385
11628
  if (Number.isFinite(maxScenes)) {
@@ -11402,11 +11645,11 @@ var scaffoldVideoCommand = defineCommand82({
11402
11645
  const annotated = annotateBlueprintWithElements(blueprint, elements);
11403
11646
  await writeFile2(blueprintPath, `${JSON.stringify(annotated, null, 2)}
11404
11647
  `, "utf8");
11405
- const compositionDest = path5.join(outDir, "video-overlay-composition");
11648
+ const compositionDest = path8.join(outDir, "video-overlay-composition");
11406
11649
  await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
11407
- const indexPath = path5.join(compositionDest, "index.html");
11650
+ const indexPath = path8.join(compositionDest, "index.html");
11408
11651
  const overlayHtml = buildOverlayHtml(blueprint);
11409
- const indexHtml = await readFile5(indexPath, "utf8");
11652
+ const indexHtml = await readFile6(indexPath, "utf8");
11410
11653
  const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
11411
11654
  if (injected === indexHtml && overlayHtml.trim()) {
11412
11655
  fail2(
@@ -11419,9 +11662,9 @@ var scaffoldVideoCommand = defineCommand82({
11419
11662
  const opts = {
11420
11663
  imageModel,
11421
11664
  videoModel,
11422
- overlayCompositionPath: compositionDest,
11423
- captionsCompositionPath: captions.compositionPath,
11424
- blueprintPath,
11665
+ overlayCompositionPath: path8.relative(outDir, compositionDest),
11666
+ captionsCompositionPath: captions.compositionPath ? path8.relative(outDir, captions.compositionPath) : void 0,
11667
+ blueprintPath: path8.relative(outDir, blueprintPath),
11425
11668
  frames,
11426
11669
  ambient: Boolean(args.ambient),
11427
11670
  ...args.resolution ? { resolution: String(args.resolution) } : {}
@@ -11434,7 +11677,7 @@ var scaffoldVideoCommand = defineCommand82({
11434
11677
  } catch (e) {
11435
11678
  return fail2("scaffold", e instanceof Error ? e.message : String(e));
11436
11679
  }
11437
- const validation = await validateCanvasDeep(canvas, defaultRegistry());
11680
+ const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(canvas, outDir), defaultRegistry());
11438
11681
  if (!validation.ok) {
11439
11682
  process.stderr.write(
11440
11683
  `${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
@@ -11444,6 +11687,7 @@ var scaffoldVideoCommand = defineCommand82({
11444
11687
  }
11445
11688
  await writeFile2(outPath, `${JSON.stringify(canvas, null, 2)}
11446
11689
  `, "utf8");
11690
+ await ensureGitignore(process.cwd(), ["canvas/", ".context/"]);
11447
11691
  process.stdout.write(
11448
11692
  `${JSON.stringify(
11449
11693
  {
@@ -11461,7 +11705,7 @@ var scaffoldVideoCommand = defineCommand82({
11461
11705
  run_estimated_credits: validation.estimatedCredits
11462
11706
  },
11463
11707
  checklist: {
11464
- edit_prompt: `Edit ${path5.basename(blueprintPath)} \u2014 the blueprint deconstructed from your video; rewrite it into the ad you want (cast, palette, copy, claims). Every scene frame reads it via target_blueprint.`,
11708
+ edit_prompt: `Edit ${path8.basename(blueprintPath)} \u2014 the blueprint deconstructed from your video; rewrite it into the ad you want (cast, palette, copy, claims). Every scene frame reads it via target_blueprint.`,
11465
11709
  recurring_elements_to_supply: report.elements,
11466
11710
  voices_to_confirm: report.dialogue.map((d) => ({
11467
11711
  scene: d.scene,
@@ -11486,19 +11730,94 @@ var scaffoldVideoCommand = defineCommand82({
11486
11730
  }
11487
11731
  });
11488
11732
 
11489
- // src/commands/canvas/validate.ts
11490
- import { readFile as readFile6 } from "fs/promises";
11491
- import path6 from "path";
11733
+ // src/commands/canvas/set-prompt.ts
11734
+ import { readFile as readFile7, writeFile as writeFile3 } from "fs/promises";
11735
+ import path9 from "path";
11492
11736
  import { defineCommand as defineCommand83 } from "citty";
11493
- var validateCommand = defineCommand83({
11737
+ function setNodePrompt(canvas, nodeId, text) {
11738
+ const nodes = canvas?.nodes;
11739
+ if (!Array.isArray(nodes)) throw new Error("canvas has no nodes array");
11740
+ const idx = nodes.findIndex((n) => n?.id === nodeId);
11741
+ if (idx < 0) {
11742
+ const ids = nodes.map((n) => n?.id).filter((id) => typeof id === "string");
11743
+ throw new Error(`node "${nodeId}" not found. Known nodes: ${ids.join(", ")}`);
11744
+ }
11745
+ const node = nodes[idx];
11746
+ const newNode = { ...node, params: { ...node.params ?? {}, prompt: text } };
11747
+ const newNodes = [...nodes];
11748
+ newNodes[idx] = newNode;
11749
+ return { ...canvas, nodes: newNodes };
11750
+ }
11751
+ var setPromptCommand = defineCommand83({
11752
+ meta: {
11753
+ name: "set-prompt",
11754
+ 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."
11755
+ },
11756
+ args: {
11757
+ file: { type: "positional", required: true, description: "Path to canvas JSON" },
11758
+ node: { type: "positional", required: true, description: "Node id to edit (e.g. s0_start)" },
11759
+ text: { type: "string", description: "New prompt text (inline)" },
11760
+ "text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
11761
+ },
11762
+ async run({ args }) {
11763
+ const filePath = path9.resolve(String(args.file));
11764
+ let canvas;
11765
+ try {
11766
+ canvas = JSON.parse(await readFile7(filePath, "utf8"));
11767
+ } catch (e) {
11768
+ process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "parse", message: String(e) } }, null, 2)}
11769
+ `);
11770
+ process.exit(2);
11771
+ }
11772
+ let text;
11773
+ if (args["text-file"]) text = await readFile7(path9.resolve(String(args["text-file"])), "utf8");
11774
+ else if (args.text !== void 0) text = String(args.text);
11775
+ else {
11776
+ process.stderr.write(
11777
+ `${JSON.stringify({ ok: false, error: { code: "no_text", message: "pass --text or --text-file" } }, null, 2)}
11778
+ `
11779
+ );
11780
+ process.exit(2);
11781
+ return;
11782
+ }
11783
+ let updated;
11784
+ try {
11785
+ updated = setNodePrompt(canvas, String(args.node), text);
11786
+ } catch (e) {
11787
+ process.stderr.write(
11788
+ `${JSON.stringify({ ok: false, error: { code: "node_not_found", message: String(e.message) } }, null, 2)}
11789
+ `
11790
+ );
11791
+ process.exit(2);
11792
+ return;
11793
+ }
11794
+ const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path9.dirname(filePath)), defaultRegistry());
11795
+ if (!validation.ok) {
11796
+ process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
11797
+ `);
11798
+ process.exit(2);
11799
+ return;
11800
+ }
11801
+ await writeFile3(filePath, `${JSON.stringify(updated, null, 2)}
11802
+ `, "utf8");
11803
+ process.stdout.write(`${JSON.stringify({ ok: true, node: String(args.node), bytes: text.length }, null, 2)}
11804
+ `);
11805
+ }
11806
+ });
11807
+
11808
+ // src/commands/canvas/validate.ts
11809
+ import { readFile as readFile8 } from "fs/promises";
11810
+ import path10 from "path";
11811
+ import { defineCommand as defineCommand84 } from "citty";
11812
+ var validateCommand = defineCommand84({
11494
11813
  meta: {
11495
11814
  name: "validate",
11496
11815
  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)."
11497
11816
  },
11498
11817
  args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
11499
11818
  async run({ args }) {
11500
- const filePath = path6.resolve(String(args.file));
11501
- const raw = await readFile6(filePath, "utf8");
11819
+ const filePath = path10.resolve(String(args.file));
11820
+ const raw = await readFile8(filePath, "utf8");
11502
11821
  let parsed;
11503
11822
  try {
11504
11823
  parsed = JSON.parse(raw);
@@ -11508,6 +11827,7 @@ var validateCommand = defineCommand83({
11508
11827
  `);
11509
11828
  process.exit(2);
11510
11829
  }
11830
+ parsed = resolveRelativeCanvasPaths(parsed, path10.dirname(filePath));
11511
11831
  const result = await validateCanvasDeep(parsed, defaultRegistry());
11512
11832
  if (!result.ok) {
11513
11833
  process.stderr.write(`${JSON.stringify({ ok: false, issues: result.issues }, null, 2)}
@@ -11532,7 +11852,7 @@ var validateCommand = defineCommand83({
11532
11852
  });
11533
11853
 
11534
11854
  // src/commands/canvas/index.ts
11535
- var canvasCommand = defineCommand84({
11855
+ var canvasCommand = defineCommand85({
11536
11856
  meta: {
11537
11857
  name: "canvas",
11538
11858
  description: `Run Baker creative canvas JSON files locally. Local nodes execute in-process; remote nodes POST to the Convex backend gateway.
@@ -11553,15 +11873,199 @@ Subcommands:
11553
11873
  catalog: catalogCommand,
11554
11874
  inspect: inspectCommand,
11555
11875
  "scaffold-video": scaffoldVideoCommand,
11556
- "scaffold-static-ad": scaffoldStaticAdCommand
11876
+ "scaffold-static-ad": scaffoldStaticAdCommand,
11877
+ "set-prompt": setPromptCommand
11878
+ }
11879
+ });
11880
+
11881
+ // src/commands/creatives/index.ts
11882
+ import { defineCommand as defineCommand87 } from "citty";
11883
+
11884
+ // src/commands/creatives/publish.ts
11885
+ import { defineCommand as defineCommand86 } from "citty";
11886
+
11887
+ // src/commands/images/api.ts
11888
+ import { readFile as readFile9 } from "fs/promises";
11889
+ import { extname } from "path";
11890
+ var imageProcessingTimeoutMs = 18e4;
11891
+ var imageReadyPollIntervalMs = 2e3;
11892
+ var mimeMap = {
11893
+ ".png": "image/png",
11894
+ ".jpg": "image/jpeg",
11895
+ ".jpeg": "image/jpeg",
11896
+ ".gif": "image/gif",
11897
+ ".webp": "image/webp",
11898
+ ".svg": "image/svg+xml",
11899
+ ".avif": "image/avif"
11900
+ };
11901
+ var defaultImageApiDeps = {
11902
+ readFile: readFile9,
11903
+ post: apiPost,
11904
+ get: apiGet,
11905
+ sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
11906
+ };
11907
+ function detectImageContentType(filePath, opts = {}) {
11908
+ const ext = extname(filePath).toLowerCase();
11909
+ const contentType = mimeMap[ext];
11910
+ if (!contentType || opts.allowedContentTypes && !opts.allowedContentTypes.includes(contentType)) {
11911
+ throw new ApiError(
11912
+ "VALIDATION_ERROR",
11913
+ opts.unsupportedMessage ?? `Cannot detect content type for extension "${ext}". Use --content-type.`
11914
+ );
11915
+ }
11916
+ return contentType;
11917
+ }
11918
+ async function uploadLocalImage(args, deps = defaultImageApiDeps) {
11919
+ const fileBuffer = await deps.readFile(args.file);
11920
+ const body = {
11921
+ base64: fileBuffer.toString("base64"),
11922
+ contentType: args.contentType
11923
+ };
11924
+ if (args.source) body.source = args.source;
11925
+ if (args.descriptionContext) body.descriptionContext = args.descriptionContext;
11926
+ return deps.post("/api/images/upload", body, { timeoutMs: imageProcessingTimeoutMs });
11927
+ }
11928
+ function getImage(deps, imageId) {
11929
+ return deps.get("/api/images/get", { id: imageId });
11930
+ }
11931
+ function updateImageTags(deps, args) {
11932
+ return deps.post("/api/images/tag", args);
11933
+ }
11934
+ async function waitForReadyImage(deps, imageId, opts = {}) {
11935
+ const timeoutMs = opts.timeoutMs ?? imageProcessingTimeoutMs;
11936
+ const pollIntervalMs = opts.pollIntervalMs ?? imageReadyPollIntervalMs;
11937
+ const deadline = Date.now() + timeoutMs;
11938
+ let lastStatus = "unknown";
11939
+ while (Date.now() <= deadline) {
11940
+ const image = await getImage(deps, imageId);
11941
+ lastStatus = image.status ?? "unknown";
11942
+ if (image.status === "ready") {
11943
+ return image;
11944
+ }
11945
+ if (image.status === "error") {
11946
+ throw new ApiError("IMAGE_PROCESSING_ERROR", "Image processing failed");
11947
+ }
11948
+ await deps.sleep(pollIntervalMs);
11949
+ }
11950
+ throw new ApiError("TIMEOUT", `Image was not ready before timeout; last status: ${lastStatus}`);
11951
+ }
11952
+
11953
+ // src/commands/creatives/publish.ts
11954
+ var creativeTag = "creative";
11955
+ var creativeContentTypes = ["image/png", "image/jpeg", "image/webp"];
11956
+ registerSchema({
11957
+ command: "creatives.publish",
11958
+ description: "Publish a final static creative image to Baker Images, apply the official creative tag, and return an image reference.",
11959
+ args: {
11960
+ file: { type: "string", description: "Local PNG/JPG/WebP creative image path", required: true },
11961
+ title: { type: "string", description: "Human title for the creative output", required: true },
11962
+ context: { type: "string", description: "Optional describe context for the image row", required: false }
11963
+ }
11964
+ });
11965
+ function detectCreativeContentType(filePath) {
11966
+ return detectImageContentType(filePath, {
11967
+ allowedContentTypes: creativeContentTypes,
11968
+ unsupportedMessage: "Unsupported creative image extension. Use PNG, JPG, or WebP."
11969
+ });
11970
+ }
11971
+ function imageToCreativeReference(image, title) {
11972
+ if (!image.imageUrl) {
11973
+ throw new ApiError("IMAGE_PROCESSING_ERROR", "Published image is missing imageUrl");
11974
+ }
11975
+ return {
11976
+ type: "image",
11977
+ slug: image._id,
11978
+ title,
11979
+ tags: image.tags?.includes(creativeTag) ? image.tags : [...image.tags ?? [], creativeTag],
11980
+ imageUrl: image.imageUrl,
11981
+ thumbnailUrl: image.thumbnailUrl ?? image.imageUrl,
11982
+ storageKey: image.storageKey,
11983
+ width: image.width,
11984
+ height: image.height,
11985
+ aspectRatio: image.aspectRatio,
11986
+ source: image.source
11987
+ };
11988
+ }
11989
+ async function publishCreative(args, deps = defaultImageApiDeps) {
11990
+ const title = args.title.trim();
11991
+ if (!title) {
11992
+ throw new ApiError("VALIDATION_ERROR", "--title is required");
11993
+ }
11994
+ const contentType = detectCreativeContentType(args.file);
11995
+ const upload = await uploadLocalImage(
11996
+ {
11997
+ file: args.file,
11998
+ contentType,
11999
+ source: "ai_generated",
12000
+ descriptionContext: args.context ?? `Static ad creative: ${title}`
12001
+ },
12002
+ deps
12003
+ );
12004
+ const readyImage = await waitForReadyImage(deps, upload.imageId, { timeoutMs: imageProcessingTimeoutMs });
12005
+ await updateImageTags(deps, {
12006
+ imageIds: [upload.imageId],
12007
+ addTags: [creativeTag],
12008
+ removeTags: []
12009
+ });
12010
+ const taggedImage = await getImage(deps, upload.imageId);
12011
+ return { imageId: upload.imageId, reference: imageToCreativeReference({ ...readyImage, ...taggedImage }, title) };
12012
+ }
12013
+ var publishCommand = defineCommand86({
12014
+ meta: {
12015
+ name: "publish",
12016
+ description: "Publish a final static creative image to Baker Images, deterministically tag it as creative, and print the image reference JSON."
12017
+ },
12018
+ args: {
12019
+ file: { type: "positional", description: "Local PNG/JPG/WebP creative image path", required: false },
12020
+ title: { type: "string", description: "Human title for the creative output", required: false },
12021
+ context: { type: "string", description: "Optional describe context for the image row", required: false }
12022
+ },
12023
+ run: async ({ args }) => {
12024
+ try {
12025
+ const file = args.file;
12026
+ const title = args.title;
12027
+ if (!file) {
12028
+ writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "Image path is required" } });
12029
+ process.exit(1);
12030
+ }
12031
+ if (!title) {
12032
+ writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "--title is required" } });
12033
+ process.exit(1);
12034
+ }
12035
+ const data = await publishCreative({ file, title, context: args.context });
12036
+ writeJson({ ok: true, data });
12037
+ } catch (err) {
12038
+ if (err instanceof ApiError) {
12039
+ writeJson({ ok: false, error: { code: err.code, message: err.message } });
12040
+ process.exit(1);
12041
+ }
12042
+ writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
12043
+ process.exit(1);
12044
+ }
12045
+ }
12046
+ });
12047
+
12048
+ // src/commands/creatives/index.ts
12049
+ var creativesCommand3 = defineCommand87({
12050
+ meta: {
12051
+ name: "creatives",
12052
+ description: `Publish static ad creatives as first-class Baker outputs.
12053
+
12054
+ Static creative handoff:
12055
+ baker creatives publish ./canvas/run/final.png --title "Spring Offer Static Ad"
12056
+
12057
+ Publishing uploads the image to the Company image library, applies the official creative tag, and returns an image reference for chat previews.`
12058
+ },
12059
+ subCommands: {
12060
+ publish: publishCommand
11557
12061
  }
11558
12062
  });
11559
12063
 
11560
12064
  // src/commands/ga4/index.ts
11561
- import { defineCommand as defineCommand88 } from "citty";
12065
+ import { defineCommand as defineCommand91 } from "citty";
11562
12066
 
11563
12067
  // src/commands/ga4/audit.ts
11564
- import { defineCommand as defineCommand85 } from "citty";
12068
+ import { defineCommand as defineCommand88 } from "citty";
11565
12069
 
11566
12070
  // src/commands/ga4/resolve.ts
11567
12071
  async function fetchProperties(useCache = true) {
@@ -11624,7 +12128,7 @@ registerSchema({
11624
12128
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
11625
12129
  }
11626
12130
  });
11627
- var auditCommand2 = defineCommand85({
12131
+ var auditCommand2 = defineCommand88({
11628
12132
  meta: {
11629
12133
  name: "audit",
11630
12134
  description: `Run all GA4 admin health checks. Returns property config with playbook warnings.
@@ -11676,7 +12180,7 @@ Examples:
11676
12180
  });
11677
12181
 
11678
12182
  // src/commands/ga4/properties.ts
11679
- import { defineCommand as defineCommand86 } from "citty";
12183
+ import { defineCommand as defineCommand89 } from "citty";
11680
12184
  registerSchema({
11681
12185
  command: "ga4.properties",
11682
12186
  description: "List all accessible GA4 properties. Returns property IDs needed for query and audit commands. Run this first to find property IDs.",
@@ -11684,7 +12188,7 @@ registerSchema({
11684
12188
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
11685
12189
  }
11686
12190
  });
11687
- var propertiesCommand = defineCommand86({
12191
+ var propertiesCommand = defineCommand89({
11688
12192
  meta: {
11689
12193
  name: "properties",
11690
12194
  description: `List accessible GA4 properties.
@@ -11734,7 +12238,7 @@ Examples:
11734
12238
  // src/commands/ga4/query.ts
11735
12239
  import { appendFileSync as appendFileSync2, existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
11736
12240
  import { resolve as resolve2 } from "path";
11737
- import { defineCommand as defineCommand87 } from "citty";
12241
+ import { defineCommand as defineCommand90 } from "citty";
11738
12242
 
11739
12243
  // src/commands/ga4/presets.ts
11740
12244
  var GA4_PRESETS = [
@@ -11866,7 +12370,7 @@ function handleError(err) {
11866
12370
  });
11867
12371
  process.exit(1);
11868
12372
  }
11869
- var queryCommand2 = defineCommand87({
12373
+ var queryCommand2 = defineCommand90({
11870
12374
  meta: {
11871
12375
  name: "query",
11872
12376
  description: `Run GA4 Data API reports. Preset-first with free-form escape hatch.
@@ -11937,7 +12441,7 @@ Free-form (escape hatch):
11937
12441
  });
11938
12442
 
11939
12443
  // src/commands/ga4/index.ts
11940
- var ga4Command = defineCommand88({
12444
+ var ga4Command = defineCommand91({
11941
12445
  meta: {
11942
12446
  name: "ga4",
11943
12447
  description: `Google Analytics 4 commands. Audit property config, run playbook-aligned reports.
@@ -11960,12 +12464,12 @@ Examples:
11960
12464
  });
11961
12465
 
11962
12466
  // src/commands/gsc/index.ts
11963
- import { defineCommand as defineCommand92 } from "citty";
12467
+ import { defineCommand as defineCommand95 } from "citty";
11964
12468
 
11965
12469
  // src/commands/gsc/query.ts
11966
12470
  import { appendFileSync as appendFileSync3, existsSync as existsSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
11967
12471
  import { resolve as resolve3 } from "path";
11968
- import { defineCommand as defineCommand89 } from "citty";
12472
+ import { defineCommand as defineCommand92 } from "citty";
11969
12473
 
11970
12474
  // src/commands/gsc/presets.ts
11971
12475
  var GSC_PRESETS = [
@@ -12153,7 +12657,7 @@ function handleError2(err) {
12153
12657
  });
12154
12658
  process.exit(1);
12155
12659
  }
12156
- var queryCommand3 = defineCommand89({
12660
+ var queryCommand3 = defineCommand92({
12157
12661
  meta: {
12158
12662
  name: "query",
12159
12663
  description: `Run GSC Search Analytics queries. Preset-first with free-form escape hatch.
@@ -12231,7 +12735,7 @@ Free-form (escape hatch):
12231
12735
  });
12232
12736
 
12233
12737
  // src/commands/gsc/sitemaps.ts
12234
- import { defineCommand as defineCommand90 } from "citty";
12738
+ import { defineCommand as defineCommand93 } from "citty";
12235
12739
  registerSchema({
12236
12740
  command: "gsc.sitemaps",
12237
12741
  description: "List sitemaps for a Search Console site. Check sitemap health and errors.",
@@ -12240,7 +12744,7 @@ registerSchema({
12240
12744
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
12241
12745
  }
12242
12746
  });
12243
- var sitemapsCommand = defineCommand90({
12747
+ var sitemapsCommand = defineCommand93({
12244
12748
  meta: {
12245
12749
  name: "sitemaps",
12246
12750
  description: `List sitemaps for a site. Check health and errors.
@@ -12290,7 +12794,7 @@ Examples:
12290
12794
  });
12291
12795
 
12292
12796
  // src/commands/gsc/sites.ts
12293
- import { defineCommand as defineCommand91 } from "citty";
12797
+ import { defineCommand as defineCommand94 } from "citty";
12294
12798
  registerSchema({
12295
12799
  command: "gsc.sites",
12296
12800
  description: "List all verified Google Search Console sites. Returns site URLs needed for query and sitemaps commands.",
@@ -12298,7 +12802,7 @@ registerSchema({
12298
12802
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
12299
12803
  }
12300
12804
  });
12301
- var sitesCommand = defineCommand91({
12805
+ var sitesCommand = defineCommand94({
12302
12806
  meta: {
12303
12807
  name: "sites",
12304
12808
  description: `List verified Search Console sites.
@@ -12346,7 +12850,7 @@ Examples:
12346
12850
  });
12347
12851
 
12348
12852
  // src/commands/gsc/index.ts
12349
- var gscCommand = defineCommand92({
12853
+ var gscCommand = defineCommand95({
12350
12854
  meta: {
12351
12855
  name: "gsc",
12352
12856
  description: `Google Search Console commands. PPC-SEO arbitrage, brand halo analysis, negative keyword discovery.
@@ -12369,10 +12873,10 @@ Examples:
12369
12873
  });
12370
12874
 
12371
12875
  // src/commands/images/index.ts
12372
- import { defineCommand as defineCommand116 } from "citty";
12876
+ import { defineCommand as defineCommand119 } from "citty";
12373
12877
 
12374
12878
  // src/commands/images/crop.ts
12375
- import { defineCommand as defineCommand93 } from "citty";
12879
+ import { defineCommand as defineCommand96 } from "citty";
12376
12880
 
12377
12881
  // src/lib/image/crop-sprite.ts
12378
12882
  import sharp from "sharp";
@@ -12387,8 +12891,8 @@ function cropSprite(input, region) {
12387
12891
 
12388
12892
  // src/lib/image/io.ts
12389
12893
  import { randomBytes } from "crypto";
12390
- import { glob as fsGlob, readFile as readFile7, rename, stat as stat2, writeFile as writeFile3 } from "fs/promises";
12391
- import { dirname, extname, join as join3, resolve as resolve4 } from "path";
12894
+ import { glob as fsGlob, readFile as readFile10, rename, stat as stat2, writeFile as writeFile4 } from "fs/promises";
12895
+ import { dirname, extname as extname2, join as join3, resolve as resolve4 } from "path";
12392
12896
  var REMOTE_RE = /^https?:\/\//i;
12393
12897
  var GLOB_RE = /[*?[\]{}]/;
12394
12898
  function isRemoteUrl(value) {
@@ -12423,18 +12927,18 @@ async function readImageBuffer(pathOrUrl) {
12423
12927
  }
12424
12928
  return Buffer.from(await response.arrayBuffer());
12425
12929
  }
12426
- return readFile7(pathOrUrl);
12930
+ return readFile10(pathOrUrl);
12427
12931
  }
12428
- async function isDirectory(path7) {
12932
+ async function isDirectory(path11) {
12429
12933
  try {
12430
- const s = await stat2(path7);
12934
+ const s = await stat2(path11);
12431
12935
  return s.isDirectory();
12432
12936
  } catch {
12433
12937
  return false;
12434
12938
  }
12435
12939
  }
12436
12940
  async function resolveOutputPath(inputPath, outputArg, options) {
12437
- const base = options.newExtension ? inputPath.slice(0, -extname(inputPath).length) + options.newExtension : inputPath;
12941
+ const base = options.newExtension ? inputPath.slice(0, -extname2(inputPath).length) + options.newExtension : inputPath;
12438
12942
  if (!outputArg) return base;
12439
12943
  if (options.multipleInputs || await isDirectory(outputArg)) {
12440
12944
  const filename = base.split("/").pop() ?? "out.png";
@@ -12446,7 +12950,7 @@ async function atomicWrite(targetPath, data) {
12446
12950
  const absolute = resolve4(targetPath);
12447
12951
  const dir = dirname(absolute);
12448
12952
  const tmp = join3(dir, `.baker-image-${randomBytes(8).toString("hex")}.tmp`);
12449
- await writeFile3(tmp, data);
12953
+ await writeFile4(tmp, data);
12450
12954
  await rename(tmp, absolute);
12451
12955
  }
12452
12956
 
@@ -12497,7 +13001,7 @@ function emitError2(err) {
12497
13001
  }
12498
13002
  process.exit(1);
12499
13003
  }
12500
- var cropCommand = defineCommand93({
13004
+ var cropCommand = defineCommand96({
12501
13005
  meta: {
12502
13006
  name: "crop",
12503
13007
  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"
@@ -12533,7 +13037,7 @@ var cropCommand = defineCommand93({
12533
13037
  });
12534
13038
 
12535
13039
  // src/commands/images/delete.ts
12536
- import { defineCommand as defineCommand94 } from "citty";
13040
+ import { defineCommand as defineCommand97 } from "citty";
12537
13041
  registerSchema({
12538
13042
  command: "images.delete",
12539
13043
  description: "Delete an image by ID",
@@ -12547,7 +13051,7 @@ registerSchema({
12547
13051
  }
12548
13052
  }
12549
13053
  });
12550
- var deleteCommand = defineCommand94({
13054
+ var deleteCommand = defineCommand97({
12551
13055
  meta: {
12552
13056
  name: "delete",
12553
13057
  description: "Delete an image by ID. Use --dry-run to preview. Example: baker images delete j571abc123 --dry-run"
@@ -12588,7 +13092,7 @@ var deleteCommand = defineCommand94({
12588
13092
  });
12589
13093
 
12590
13094
  // src/commands/images/dimensions.ts
12591
- import { defineCommand as defineCommand95 } from "citty";
13095
+ import { defineCommand as defineCommand98 } from "citty";
12592
13096
 
12593
13097
  // src/lib/image/dimensions.ts
12594
13098
  import { imageSize } from "image-size";
@@ -12611,7 +13115,7 @@ registerSchema({
12611
13115
  target: { type: "string", description: "Local file path or remote http(s) URL", required: true }
12612
13116
  }
12613
13117
  });
12614
- var dimensionsCommand = defineCommand95({
13118
+ var dimensionsCommand = defineCommand98({
12615
13119
  meta: {
12616
13120
  name: "dimensions",
12617
13121
  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"
@@ -12655,7 +13159,7 @@ var dimensionsCommand = defineCommand95({
12655
13159
  });
12656
13160
 
12657
13161
  // src/commands/images/extract.ts
12658
- import { defineCommand as defineCommand96 } from "citty";
13162
+ import { defineCommand as defineCommand99 } from "citty";
12659
13163
  registerSchema({
12660
13164
  command: "images.extract",
12661
13165
  description: "Extract images from a URL via Firecrawl (formats: images).",
@@ -12671,7 +13175,7 @@ registerSchema({
12671
13175
  }
12672
13176
  }
12673
13177
  });
12674
- var extractCommand = defineCommand96({
13178
+ var extractCommand = defineCommand99({
12675
13179
  meta: {
12676
13180
  name: "extract",
12677
13181
  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"
@@ -12709,7 +13213,7 @@ var extractCommand = defineCommand96({
12709
13213
  });
12710
13214
 
12711
13215
  // src/commands/images/find.ts
12712
- import { defineCommand as defineCommand97 } from "citty";
13216
+ import { defineCommand as defineCommand100 } from "citty";
12713
13217
  registerSchema({
12714
13218
  command: "images.find",
12715
13219
  description: "Fanout image search: library first, then opted-in external providers.",
@@ -12741,7 +13245,7 @@ registerSchema({
12741
13245
  }
12742
13246
  }
12743
13247
  });
12744
- var findCommand = defineCommand97({
13248
+ var findCommand = defineCommand100({
12745
13249
  meta: {
12746
13250
  name: "find",
12747
13251
  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"
@@ -12787,8 +13291,8 @@ var findCommand = defineCommand97({
12787
13291
  });
12788
13292
 
12789
13293
  // src/commands/images/generate.ts
12790
- import { readFile as readFile8 } from "fs/promises";
12791
- import { defineCommand as defineCommand98 } from "citty";
13294
+ import { readFile as readFile11 } from "fs/promises";
13295
+ import { defineCommand as defineCommand101 } from "citty";
12792
13296
  import sharp2 from "sharp";
12793
13297
  var GENERATE_TIMEOUT_MS = 18e4;
12794
13298
  var REFERENCE_MAX_EDGE = 1536;
@@ -12870,7 +13374,7 @@ async function resolveReferences(spec) {
12870
13374
  }
12871
13375
  let raw;
12872
13376
  try {
12873
- raw = await readFile8(entry);
13377
+ raw = await readFile11(entry);
12874
13378
  } catch {
12875
13379
  throw new ApiError("VALIDATION_ERROR", `Reference file not found: ${entry}`);
12876
13380
  }
@@ -12884,7 +13388,7 @@ async function resolveReferences(spec) {
12884
13388
  }
12885
13389
  return out;
12886
13390
  }
12887
- var generateCommand = defineCommand98({
13391
+ var generateCommand = defineCommand101({
12888
13392
  meta: {
12889
13393
  name: "generate",
12890
13394
  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]]'"
@@ -12936,7 +13440,7 @@ var generateCommand = defineCommand98({
12936
13440
  });
12937
13441
 
12938
13442
  // src/commands/images/get.ts
12939
- import { defineCommand as defineCommand99 } from "citty";
13443
+ import { defineCommand as defineCommand102 } from "citty";
12940
13444
  registerSchema({
12941
13445
  command: "images.get",
12942
13446
  description: "Get a single image by ID",
@@ -12944,7 +13448,7 @@ registerSchema({
12944
13448
  id: { type: "string", description: "Image ID", required: true }
12945
13449
  }
12946
13450
  });
12947
- var getCommand2 = defineCommand99({
13451
+ var getCommand2 = defineCommand102({
12948
13452
  meta: { name: "get", description: "Get a single image by ID. Example: baker images get j571abc123" },
12949
13453
  args: {
12950
13454
  id: { type: "positional", description: "Image ID", required: false },
@@ -12980,7 +13484,7 @@ var getCommand2 = defineCommand99({
12980
13484
  });
12981
13485
 
12982
13486
  // src/commands/images/gif.ts
12983
- import { defineCommand as defineCommand100 } from "citty";
13487
+ import { defineCommand as defineCommand103 } from "citty";
12984
13488
  registerSchema({
12985
13489
  command: "images.gif",
12986
13490
  description: "Search Giphy for GIFs / reaction memes (paid social creative).",
@@ -13012,7 +13516,7 @@ registerSchema({
13012
13516
  }
13013
13517
  }
13014
13518
  });
13015
- var gifCommand = defineCommand100({
13519
+ var gifCommand = defineCommand103({
13016
13520
  meta: {
13017
13521
  name: "gif",
13018
13522
  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"
@@ -13059,7 +13563,7 @@ var gifCommand = defineCommand100({
13059
13563
  });
13060
13564
 
13061
13565
  // src/commands/images/google.ts
13062
- import { defineCommand as defineCommand101 } from "citty";
13566
+ import { defineCommand as defineCommand104 } from "citty";
13063
13567
  registerSchema({
13064
13568
  command: "images.google",
13065
13569
  description: "Google Images search via the official Custom Search JSON API. Unverified source \u2014 inspect before placing.",
@@ -13095,7 +13599,7 @@ registerSchema({
13095
13599
  }
13096
13600
  }
13097
13601
  });
13098
- var googleCommand2 = defineCommand101({
13602
+ var googleCommand2 = defineCommand104({
13099
13603
  meta: {
13100
13604
  name: "google",
13101
13605
  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"
@@ -13143,7 +13647,7 @@ var googleCommand2 = defineCommand101({
13143
13647
  });
13144
13648
 
13145
13649
  // src/commands/images/icon.ts
13146
- import { defineCommand as defineCommand102 } from "citty";
13650
+ import { defineCommand as defineCommand105 } from "citty";
13147
13651
  registerSchema({
13148
13652
  command: "images.icon",
13149
13653
  description: "Icon lookup via Iconify (200+ icon sets, free CDN).",
@@ -13169,7 +13673,7 @@ registerSchema({
13169
13673
  }
13170
13674
  }
13171
13675
  });
13172
- var iconCommand = defineCommand102({
13676
+ var iconCommand = defineCommand105({
13173
13677
  meta: {
13174
13678
  name: "icon",
13175
13679
  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'"
@@ -13209,7 +13713,7 @@ var iconCommand = defineCommand102({
13209
13713
  });
13210
13714
 
13211
13715
  // src/commands/images/ingest.ts
13212
- import { defineCommand as defineCommand103 } from "citty";
13716
+ import { defineCommand as defineCommand106 } from "citty";
13213
13717
  registerSchema({
13214
13718
  command: "images.ingest",
13215
13719
  description: "Ingest a remote image URL into the library (full describe + embed).",
@@ -13221,7 +13725,7 @@ registerSchema({
13221
13725
  context: { type: "string", description: "Description context hint", required: false }
13222
13726
  }
13223
13727
  });
13224
- var ingestCommand = defineCommand103({
13728
+ var ingestCommand = defineCommand106({
13225
13729
  meta: {
13226
13730
  name: "ingest",
13227
13731
  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"
@@ -13263,7 +13767,7 @@ var ingestCommand = defineCommand103({
13263
13767
  });
13264
13768
 
13265
13769
  // src/commands/images/library.ts
13266
- import { defineCommand as defineCommand104 } from "citty";
13770
+ import { defineCommand as defineCommand107 } from "citty";
13267
13771
  registerSchema({
13268
13772
  command: "images.library",
13269
13773
  description: "Search the company image library. Returns only ready images.",
@@ -13289,7 +13793,7 @@ registerSchema({
13289
13793
  }
13290
13794
  }
13291
13795
  });
13292
- var libraryCommand = defineCommand104({
13796
+ var libraryCommand = defineCommand107({
13293
13797
  meta: {
13294
13798
  name: "library",
13295
13799
  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"
@@ -13346,7 +13850,7 @@ var libraryCommand = defineCommand104({
13346
13850
  });
13347
13851
 
13348
13852
  // src/commands/images/logo.ts
13349
- import { defineCommand as defineCommand105 } from "citty";
13853
+ import { defineCommand as defineCommand108 } from "citty";
13350
13854
  registerSchema({
13351
13855
  command: "images.logo",
13352
13856
  description: "Brand logo lookup via Brandfetch CDN (fallback/404). Auto-ingests by default.",
@@ -13371,7 +13875,7 @@ registerSchema({
13371
13875
  }
13372
13876
  }
13373
13877
  });
13374
- var logoCommand = defineCommand105({
13878
+ var logoCommand = defineCommand108({
13375
13879
  meta: {
13376
13880
  name: "logo",
13377
13881
  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"
@@ -13409,7 +13913,7 @@ var logoCommand = defineCommand105({
13409
13913
  });
13410
13914
 
13411
13915
  // src/commands/images/normalize.ts
13412
- import { defineCommand as defineCommand106 } from "citty";
13916
+ import { defineCommand as defineCommand109 } from "citty";
13413
13917
 
13414
13918
  // src/lib/image/color-changer.ts
13415
13919
  import quantize from "quantize";
@@ -14141,7 +14645,7 @@ function coerceRawArgs(args) {
14141
14645
  "dry-run": bool(args["dry-run"])
14142
14646
  };
14143
14647
  }
14144
- var normalizeCommand = defineCommand106({
14648
+ var normalizeCommand = defineCommand109({
14145
14649
  meta: {
14146
14650
  name: "normalize",
14147
14651
  description: `Normalize logos / images: declarative recolor + bg removal + trim + resize. Operates on local files; writes in-place by default.
@@ -14196,7 +14700,7 @@ Examples:
14196
14700
  });
14197
14701
 
14198
14702
  // src/commands/images/pinterest.ts
14199
- import { defineCommand as defineCommand107 } from "citty";
14703
+ import { defineCommand as defineCommand110 } from "citty";
14200
14704
  registerSchema({
14201
14705
  command: "images.pinterest",
14202
14706
  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.",
@@ -14216,7 +14720,7 @@ registerSchema({
14216
14720
  }
14217
14721
  }
14218
14722
  });
14219
- var pinterestCommand = defineCommand107({
14723
+ var pinterestCommand = defineCommand110({
14220
14724
  meta: {
14221
14725
  name: "pinterest",
14222
14726
  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'"
@@ -14256,7 +14760,7 @@ var pinterestCommand = defineCommand107({
14256
14760
  });
14257
14761
 
14258
14762
  // src/commands/images/screenshot.ts
14259
- import { defineCommand as defineCommand108 } from "citty";
14763
+ import { defineCommand as defineCommand111 } from "citty";
14260
14764
  registerSchema({
14261
14765
  command: "images.screenshot",
14262
14766
  description: "Capture a website screenshot via ScreenshotOne. Auto-ingests on success.",
@@ -14272,7 +14776,7 @@ registerSchema({
14272
14776
  }
14273
14777
  }
14274
14778
  });
14275
- var screenshotCommand = defineCommand108({
14779
+ var screenshotCommand = defineCommand111({
14276
14780
  meta: {
14277
14781
  name: "screenshot",
14278
14782
  description: "Screenshot a URL via ScreenshotOne. $0.009/capture. Auto-ingests to library.\n\nExample: baker images screenshot https://stripe.com --full-page"
@@ -14322,7 +14826,7 @@ var screenshotCommand = defineCommand108({
14322
14826
  });
14323
14827
 
14324
14828
  // src/commands/images/search.ts
14325
- import { defineCommand as defineCommand109 } from "citty";
14829
+ import { defineCommand as defineCommand112 } from "citty";
14326
14830
  registerSchema({
14327
14831
  command: "images.search",
14328
14832
  description: "Search images by text query. Only returns ready images.",
@@ -14338,7 +14842,7 @@ registerSchema({
14338
14842
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
14339
14843
  }
14340
14844
  });
14341
- var searchCommand = defineCommand109({
14845
+ var searchCommand = defineCommand112({
14342
14846
  meta: {
14343
14847
  name: "search",
14344
14848
  description: "Semantic search images by text query. Uses hybrid BM25 + vector + reranking. Example: baker images search 'hero banner' --aspect-ratio 16:9 --tags logo"
@@ -14398,7 +14902,7 @@ var searchCommand = defineCommand109({
14398
14902
  });
14399
14903
 
14400
14904
  // src/commands/images/sticker.ts
14401
- import { defineCommand as defineCommand110 } from "citty";
14905
+ import { defineCommand as defineCommand113 } from "citty";
14402
14906
  registerSchema({
14403
14907
  command: "images.sticker",
14404
14908
  description: "Search Giphy stickers \u2014 transparent-background overlays for ad creative.",
@@ -14430,7 +14934,7 @@ registerSchema({
14430
14934
  }
14431
14935
  }
14432
14936
  });
14433
- var stickerCommand = defineCommand110({
14937
+ var stickerCommand = defineCommand113({
14434
14938
  meta: {
14435
14939
  name: "sticker",
14436
14940
  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"
@@ -14477,7 +14981,7 @@ var stickerCommand = defineCommand110({
14477
14981
  });
14478
14982
 
14479
14983
  // src/commands/images/stock.ts
14480
- import { defineCommand as defineCommand111 } from "citty";
14984
+ import { defineCommand as defineCommand114 } from "citty";
14481
14985
  registerSchema({
14482
14986
  command: "images.stock",
14483
14987
  description: "Stock photo, vector illustration, icon-set, and PSD search via Magnific (Freepik's developer API).",
@@ -14535,7 +15039,7 @@ registerSchema({
14535
15039
  }
14536
15040
  }
14537
15041
  });
14538
- var stockCommand = defineCommand111({
15042
+ var stockCommand = defineCommand114({
14539
15043
  meta: {
14540
15044
  name: "stock",
14541
15045
  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"
@@ -14591,7 +15095,7 @@ var stockCommand = defineCommand111({
14591
15095
  });
14592
15096
 
14593
15097
  // src/lib/tags-command.ts
14594
- import { defineCommand as defineCommand112 } from "citty";
15098
+ import { defineCommand as defineCommand115 } from "citty";
14595
15099
  function makeTagsCommand(command, label, endpoint) {
14596
15100
  registerSchema({
14597
15101
  command: `${command}.tags`,
@@ -14600,7 +15104,7 @@ function makeTagsCommand(command, label, endpoint) {
14600
15104
  output: { type: "string", description: "Output format: md|json", required: false, default: "md" }
14601
15105
  }
14602
15106
  });
14603
- return defineCommand112({
15107
+ return defineCommand115({
14604
15108
  meta: {
14605
15109
  name: "tags",
14606
15110
  description: `List the available ${label} tag names (defaults + company custom tags). Use before filtering with --tags. Example: baker ${command} tags`
@@ -14636,18 +15140,7 @@ function makeTagsCommand(command, label, endpoint) {
14636
15140
  var tagsCommand2 = makeTagsCommand("images", "image", "/api/images/tags");
14637
15141
 
14638
15142
  // src/commands/images/upload.ts
14639
- import { readFile as readFile9 } from "fs/promises";
14640
- import { extname as extname2 } from "path";
14641
- import { defineCommand as defineCommand113 } from "citty";
14642
- var MIME_MAP = {
14643
- ".png": "image/png",
14644
- ".jpg": "image/jpeg",
14645
- ".jpeg": "image/jpeg",
14646
- ".gif": "image/gif",
14647
- ".webp": "image/webp",
14648
- ".svg": "image/svg+xml",
14649
- ".avif": "image/avif"
14650
- };
15143
+ import { defineCommand as defineCommand116 } from "citty";
14651
15144
  registerSchema({
14652
15145
  command: "images.upload",
14653
15146
  description: "Upload an image to the library \u2014 local file path or remote http(s) URL.",
@@ -14685,15 +15178,7 @@ registerSchema({
14685
15178
  function isRemoteUrl2(value) {
14686
15179
  return /^https?:\/\//i.test(value);
14687
15180
  }
14688
- function detectContentType(filePath) {
14689
- const ext = extname2(filePath).toLowerCase();
14690
- const mime = MIME_MAP[ext];
14691
- if (!mime) {
14692
- throw new ApiError("VALIDATION_ERROR", `Cannot detect content type for extension "${ext}". Use --content-type.`);
14693
- }
14694
- return mime;
14695
- }
14696
- var uploadCommand = defineCommand113({
15181
+ var uploadCommand = defineCommand116({
14697
15182
  meta: {
14698
15183
  name: "upload",
14699
15184
  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'"
@@ -14761,7 +15246,7 @@ async function uploadRemote(target, args) {
14761
15246
  writeJson({ ok: true, data });
14762
15247
  }
14763
15248
  async function uploadLocal(target, args) {
14764
- const contentType = args["content-type"] || detectContentType(target);
15249
+ const contentType = args["content-type"] || detectImageContentType(target);
14765
15250
  if (args["dry-run"]) {
14766
15251
  writeJson({
14767
15252
  ok: true,
@@ -14776,17 +15261,17 @@ async function uploadLocal(target, args) {
14776
15261
  });
14777
15262
  return;
14778
15263
  }
14779
- const fileBuffer = await readFile9(target);
14780
- const base64 = fileBuffer.toString("base64");
14781
- const body = { base64, contentType };
14782
- if (args.source) body.source = args.source;
14783
- if (args.context) body.descriptionContext = args.context;
14784
- const data = await apiPost("/api/images/upload", body);
15264
+ const data = await uploadLocalImage({
15265
+ file: target,
15266
+ contentType,
15267
+ source: args.source,
15268
+ descriptionContext: args.context
15269
+ });
14785
15270
  writeJson({ ok: true, data });
14786
15271
  }
14787
15272
 
14788
15273
  // src/commands/images/upscale.ts
14789
- import { defineCommand as defineCommand114 } from "citty";
15274
+ import { defineCommand as defineCommand117 } from "citty";
14790
15275
  registerSchema({
14791
15276
  command: "images.upscale",
14792
15277
  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).",
@@ -14801,7 +15286,7 @@ registerSchema({
14801
15286
  }
14802
15287
  });
14803
15288
  var POLL_INTERVAL_MS3 = 1500;
14804
- var upscaleCommand = defineCommand114({
15289
+ var upscaleCommand = defineCommand117({
14805
15290
  meta: {
14806
15291
  name: "upscale",
14807
15292
  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"
@@ -14856,7 +15341,7 @@ var upscaleCommand = defineCommand114({
14856
15341
  });
14857
15342
 
14858
15343
  // src/commands/images/use.ts
14859
- import { defineCommand as defineCommand115 } from "citty";
15344
+ import { defineCommand as defineCommand118 } from "citty";
14860
15345
  registerSchema({
14861
15346
  command: "images.use",
14862
15347
  description: "Ingest a URL and wait for the library record to be ready.",
@@ -14872,7 +15357,7 @@ registerSchema({
14872
15357
  }
14873
15358
  });
14874
15359
  var POLL_INTERVAL_MS4 = 1500;
14875
- var useCommand = defineCommand115({
15360
+ var useCommand = defineCommand118({
14876
15361
  meta: {
14877
15362
  name: "use",
14878
15363
  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"
@@ -14918,7 +15403,7 @@ var useCommand = defineCommand115({
14918
15403
  });
14919
15404
 
14920
15405
  // src/commands/images/index.ts
14921
- var imagesCommand = defineCommand116({
15406
+ var imagesCommand = defineCommand119({
14922
15407
  meta: {
14923
15408
  name: "images",
14924
15409
  description: `Find, source, and normalize images. Subcommands route by provider so cost + license are explicit.
@@ -14988,10 +15473,10 @@ Paid transforms (run on the Convex backend, cost-tracked):
14988
15473
  });
14989
15474
 
14990
15475
  // src/commands/research/index.ts
14991
- import { defineCommand as defineCommand127 } from "citty";
15476
+ import { defineCommand as defineCommand130 } from "citty";
14992
15477
 
14993
15478
  // src/commands/research/advertisers.ts
14994
- import { defineCommand as defineCommand117 } from "citty";
15479
+ import { defineCommand as defineCommand120 } from "citty";
14995
15480
 
14996
15481
  // src/commands/research/output.ts
14997
15482
  var RESEARCH_DATA_NOTE = "Estimates based on third-party SERP data \u2014 not exact figures. Use for directional insights, not precise measurement.";
@@ -15104,7 +15589,7 @@ var FIELDS3 = {
15104
15589
  etv: "Estimated traffic value (USD)",
15105
15590
  visibility: "SERP visibility score (0-1)"
15106
15591
  };
15107
- var advertisersCommand = defineCommand117({
15592
+ var advertisersCommand = defineCommand120({
15108
15593
  meta: {
15109
15594
  name: "advertisers",
15110
15595
  description: `Find domains competing for a keyword in Google SERPs.
@@ -15151,7 +15636,7 @@ Examples:
15151
15636
  });
15152
15637
 
15153
15638
  // src/commands/research/autocomplete.ts
15154
- import { defineCommand as defineCommand118 } from "citty";
15639
+ import { defineCommand as defineCommand121 } from "citty";
15155
15640
  registerSchema({
15156
15641
  command: "research.autocomplete",
15157
15642
  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).",
@@ -15174,7 +15659,7 @@ registerSchema({
15174
15659
  var FIELDS4 = {
15175
15660
  suggestion: "Autocomplete suggestion from Google"
15176
15661
  };
15177
- var autocompleteCommand = defineCommand118({
15662
+ var autocompleteCommand = defineCommand121({
15178
15663
  meta: {
15179
15664
  name: "autocomplete",
15180
15665
  description: `Get Google Autocomplete suggestions for keyword expansion.
@@ -15220,7 +15705,7 @@ Examples:
15220
15705
  });
15221
15706
 
15222
15707
  // src/commands/research/countries.ts
15223
- import { defineCommand as defineCommand119 } from "citty";
15708
+ import { defineCommand as defineCommand122 } from "citty";
15224
15709
  registerSchema({
15225
15710
  command: "research.countries",
15226
15711
  description: "List all supported country codes for --location flag in research commands.",
@@ -15277,7 +15762,7 @@ var FIELDS5 = {
15277
15762
  code: "Country code to pass as --location",
15278
15763
  name: "Country name"
15279
15764
  };
15280
- var countriesCommand = defineCommand119({
15765
+ var countriesCommand = defineCommand122({
15281
15766
  meta: {
15282
15767
  name: "countries",
15283
15768
  description: "List all supported country codes for --location flag."
@@ -15288,7 +15773,7 @@ var countriesCommand = defineCommand119({
15288
15773
  });
15289
15774
 
15290
15775
  // src/commands/research/intent.ts
15291
- import { defineCommand as defineCommand120 } from "citty";
15776
+ import { defineCommand as defineCommand123 } from "citty";
15292
15777
  registerSchema({
15293
15778
  command: "research.intent",
15294
15779
  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.",
@@ -15311,7 +15796,7 @@ var FIELDS6 = {
15311
15796
  intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
15312
15797
  probability: "Confidence score 0.0-1.0"
15313
15798
  };
15314
- var intentCommand = defineCommand120({
15799
+ var intentCommand = defineCommand123({
15315
15800
  meta: {
15316
15801
  name: "intent",
15317
15802
  description: `Classify Google Search intent for keywords. Returns intent type and confidence.
@@ -15359,7 +15844,7 @@ Examples:
15359
15844
  });
15360
15845
 
15361
15846
  // src/commands/research/keyword-gap.ts
15362
- import { defineCommand as defineCommand121 } from "citty";
15847
+ import { defineCommand as defineCommand124 } from "citty";
15363
15848
  registerSchema({
15364
15849
  command: "research.keyword-gap",
15365
15850
  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.",
@@ -15388,7 +15873,7 @@ var FIELDS7 = {
15388
15873
  cpc: "Cost per click USD",
15389
15874
  their_position: "Competitor's ranking position"
15390
15875
  };
15391
- var keywordGapCommand = defineCommand121({
15876
+ var keywordGapCommand = defineCommand124({
15392
15877
  meta: {
15393
15878
  name: "keyword-gap",
15394
15879
  description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
@@ -15462,7 +15947,7 @@ Examples:
15462
15947
  });
15463
15948
 
15464
15949
  // src/commands/research/keywords-for-site.ts
15465
- import { defineCommand as defineCommand122 } from "citty";
15950
+ import { defineCommand as defineCommand125 } from "citty";
15466
15951
  registerSchema({
15467
15952
  command: "research.keywords-for-site",
15468
15953
  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.",
@@ -15495,7 +15980,7 @@ var FIELDS8 = {
15495
15980
  competition: "LOW, MEDIUM, or HIGH",
15496
15981
  competition_index: "Competition score 0-100"
15497
15982
  };
15498
- var keywordsForSiteCommand = defineCommand122({
15983
+ var keywordsForSiteCommand = defineCommand125({
15499
15984
  meta: {
15500
15985
  name: "keywords-for-site",
15501
15986
  description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
@@ -15548,7 +16033,7 @@ Examples:
15548
16033
  });
15549
16034
 
15550
16035
  // src/commands/research/languages.ts
15551
- import { defineCommand as defineCommand123 } from "citty";
16036
+ import { defineCommand as defineCommand126 } from "citty";
15552
16037
  registerSchema({
15553
16038
  command: "research.languages",
15554
16039
  description: "List all supported language codes for --language flag in research commands.",
@@ -15578,7 +16063,7 @@ var FIELDS9 = {
15578
16063
  code: "Language code to pass as --language",
15579
16064
  name: "Language name (also accepted by --language)"
15580
16065
  };
15581
- var languagesCommand2 = defineCommand123({
16066
+ var languagesCommand2 = defineCommand126({
15582
16067
  meta: {
15583
16068
  name: "languages",
15584
16069
  description: "List all supported language codes for --language flag."
@@ -15589,7 +16074,7 @@ var languagesCommand2 = defineCommand123({
15589
16074
  });
15590
16075
 
15591
16076
  // src/commands/research/lighthouse.ts
15592
- import { defineCommand as defineCommand124 } from "citty";
16077
+ import { defineCommand as defineCommand127 } from "citty";
15593
16078
  registerSchema({
15594
16079
  command: "research.lighthouse",
15595
16080
  description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
@@ -15608,7 +16093,7 @@ var FIELDS10 = {
15608
16093
  speed_index_ms: "Speed Index in ms (good: < 3400)",
15609
16094
  interactive_ms: "Time to Interactive in ms (good: < 3800)"
15610
16095
  };
15611
- var lighthouseCommand = defineCommand124({
16096
+ var lighthouseCommand = defineCommand127({
15612
16097
  meta: {
15613
16098
  name: "lighthouse",
15614
16099
  description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
@@ -15646,7 +16131,7 @@ Examples:
15646
16131
  });
15647
16132
 
15648
16133
  // src/commands/research/relevant-pages.ts
15649
- import { defineCommand as defineCommand125 } from "citty";
16134
+ import { defineCommand as defineCommand128 } from "citty";
15650
16135
  registerSchema({
15651
16136
  command: "research.relevant-pages",
15652
16137
  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).",
@@ -15672,7 +16157,7 @@ var FIELDS11 = {
15672
16157
  keywords: "Total organic keywords the page ranks for",
15673
16158
  top_10: "Keywords in positions 1-10"
15674
16159
  };
15675
- var relevantPagesCommand = defineCommand125({
16160
+ var relevantPagesCommand = defineCommand128({
15676
16161
  meta: {
15677
16162
  name: "relevant-pages",
15678
16163
  description: `Get the top pages of a competitor domain with traffic data.
@@ -15718,7 +16203,7 @@ Examples:
15718
16203
  });
15719
16204
 
15720
16205
  // src/commands/research/web.ts
15721
- import { defineCommand as defineCommand126 } from "citty";
16206
+ import { defineCommand as defineCommand129 } from "citty";
15722
16207
  registerSchema({
15723
16208
  command: "research.web",
15724
16209
  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).",
@@ -15769,7 +16254,7 @@ async function runDeepResearch(question) {
15769
16254
  }
15770
16255
  throw new Error("Deep research timed out");
15771
16256
  }
15772
- var webCommand = defineCommand126({
16257
+ var webCommand = defineCommand129({
15773
16258
  meta: {
15774
16259
  name: "web",
15775
16260
  description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
@@ -15829,7 +16314,7 @@ Examples:
15829
16314
  });
15830
16315
 
15831
16316
  // src/commands/research/index.ts
15832
- var researchCommand = defineCommand127({
16317
+ var researchCommand = defineCommand130({
15833
16318
  meta: {
15834
16319
  name: "research",
15835
16320
  description: `Competitive intelligence and AI-powered research commands.
@@ -15869,10 +16354,10 @@ Examples:
15869
16354
  });
15870
16355
 
15871
16356
  // src/commands/scheduled-actions/index.ts
15872
- import { defineCommand as defineCommand134 } from "citty";
16357
+ import { defineCommand as defineCommand137 } from "citty";
15873
16358
 
15874
16359
  // src/commands/scheduled-actions/create.ts
15875
- import { defineCommand as defineCommand128 } from "citty";
16360
+ import { defineCommand as defineCommand131 } from "citty";
15876
16361
 
15877
16362
  // src/commands/scheduled-actions/shared.ts
15878
16363
  var TEMP_SCHEDULED_ACTION_PREFIX = "temp_sched_";
@@ -15977,7 +16462,7 @@ registerSchema({
15977
16462
  prompt: { type: "string", description: "Additional prompt instructions for the spawned agent", required: false }
15978
16463
  }
15979
16464
  });
15980
- var createCommand2 = defineCommand128({
16465
+ var createCommand2 = defineCommand131({
15981
16466
  meta: {
15982
16467
  name: "create",
15983
16468
  description: 'Stage a scheduled action. Example: baker scheduled-actions create --name "Weekly report" --description "..." --cron "0 9 * * MON"'
@@ -16025,7 +16510,7 @@ var createCommand2 = defineCommand128({
16025
16510
  });
16026
16511
 
16027
16512
  // src/commands/scheduled-actions/delete.ts
16028
- import { defineCommand as defineCommand129 } from "citty";
16513
+ import { defineCommand as defineCommand132 } from "citty";
16029
16514
  registerSchema({
16030
16515
  command: "scheduled-actions.delete",
16031
16516
  description: "Stage deletion of a published scheduled action or cancellation of a temp_sched_* draft creation.",
@@ -16033,7 +16518,7 @@ registerSchema({
16033
16518
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
16034
16519
  }
16035
16520
  });
16036
- var deleteCommand2 = defineCommand129({
16521
+ var deleteCommand2 = defineCommand132({
16037
16522
  meta: {
16038
16523
  name: "delete",
16039
16524
  description: "Stage scheduled action deletion. Example: baker scheduled-actions delete <id-or-temp_sched_id>"
@@ -16062,7 +16547,7 @@ var deleteCommand2 = defineCommand129({
16062
16547
  });
16063
16548
 
16064
16549
  // src/commands/scheduled-actions/get.ts
16065
- import { defineCommand as defineCommand130 } from "citty";
16550
+ import { defineCommand as defineCommand133 } from "citty";
16066
16551
  registerSchema({
16067
16552
  command: "scheduled-actions.get",
16068
16553
  description: "Get a published scheduled action or a temp_sched_* draft-created scheduled action.",
@@ -16070,7 +16555,7 @@ registerSchema({
16070
16555
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
16071
16556
  }
16072
16557
  });
16073
- var getCommand3 = defineCommand130({
16558
+ var getCommand3 = defineCommand133({
16074
16559
  meta: {
16075
16560
  name: "get",
16076
16561
  description: "Get a scheduled action. Example: baker scheduled-actions get <id-or-temp_sched_id>"
@@ -16107,13 +16592,13 @@ var getCommand3 = defineCommand130({
16107
16592
  });
16108
16593
 
16109
16594
  // src/commands/scheduled-actions/list.ts
16110
- import { defineCommand as defineCommand131 } from "citty";
16595
+ import { defineCommand as defineCommand134 } from "citty";
16111
16596
  registerSchema({
16112
16597
  command: "scheduled-actions.list",
16113
16598
  description: "List published scheduled actions. Includes draft state when BAKER_CHAT_ID is set.",
16114
16599
  args: {}
16115
16600
  });
16116
- var listCommand3 = defineCommand131({
16601
+ var listCommand3 = defineCommand134({
16117
16602
  meta: {
16118
16603
  name: "list",
16119
16604
  description: "List scheduled actions. Includes staged draft ops when BAKER_CHAT_ID is set."
@@ -16134,7 +16619,7 @@ var listCommand3 = defineCommand131({
16134
16619
  });
16135
16620
 
16136
16621
  // src/commands/scheduled-actions/trigger.ts
16137
- import { defineCommand as defineCommand132 } from "citty";
16622
+ import { defineCommand as defineCommand135 } from "citty";
16138
16623
  registerSchema({
16139
16624
  command: "scheduled-actions.trigger",
16140
16625
  description: "Immediately trigger a published scheduled action. Does not require BAKER_CHAT_ID and rejects temp_sched_* IDs.",
@@ -16142,7 +16627,7 @@ registerSchema({
16142
16627
  id: { type: "string", description: "Published scheduled action ID", required: true }
16143
16628
  }
16144
16629
  });
16145
- var triggerCommand = defineCommand132({
16630
+ var triggerCommand = defineCommand135({
16146
16631
  meta: {
16147
16632
  name: "trigger",
16148
16633
  description: "Immediately trigger a published scheduled action. Example: baker scheduled-actions trigger <id>"
@@ -16179,7 +16664,7 @@ var triggerCommand = defineCommand132({
16179
16664
  });
16180
16665
 
16181
16666
  // src/commands/scheduled-actions/update.ts
16182
- import { defineCommand as defineCommand133 } from "citty";
16667
+ import { defineCommand as defineCommand136 } from "citty";
16183
16668
  registerSchema({
16184
16669
  command: "scheduled-actions.update",
16185
16670
  description: "Stage an update to a published scheduled action or temp_sched_* draft-created scheduled action.",
@@ -16204,7 +16689,7 @@ registerSchema({
16204
16689
  prompt: { type: "string", description: "Replacement additional spawned-agent instructions", required: false }
16205
16690
  }
16206
16691
  });
16207
- var updateCommand2 = defineCommand133({
16692
+ var updateCommand2 = defineCommand136({
16208
16693
  meta: {
16209
16694
  name: "update",
16210
16695
  description: "Stage a scheduled action update. Example: baker scheduled-actions update <id> --enabled false"
@@ -16274,7 +16759,7 @@ var updateCommand2 = defineCommand133({
16274
16759
  });
16275
16760
 
16276
16761
  // src/commands/scheduled-actions/index.ts
16277
- var scheduledActionsCommand = defineCommand134({
16762
+ var scheduledActionsCommand = defineCommand137({
16278
16763
  meta: {
16279
16764
  name: "scheduled-actions",
16280
16765
  description: `Manage Scheduled Actions. Subcommands: list, get, create, update, delete, trigger.
@@ -16300,8 +16785,8 @@ Examples:
16300
16785
  });
16301
16786
 
16302
16787
  // src/commands/schema.ts
16303
- import { defineCommand as defineCommand135 } from "citty";
16304
- var schemaCommand = defineCommand135({
16788
+ import { defineCommand as defineCommand138 } from "citty";
16789
+ var schemaCommand = defineCommand138({
16305
16790
  meta: {
16306
16791
  name: "schema",
16307
16792
  description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images.search"
@@ -16337,10 +16822,10 @@ var schemaCommand = defineCommand135({
16337
16822
  });
16338
16823
 
16339
16824
  // src/commands/testimonials/index.ts
16340
- import { defineCommand as defineCommand139 } from "citty";
16825
+ import { defineCommand as defineCommand142 } from "citty";
16341
16826
 
16342
16827
  // src/commands/testimonials/get.ts
16343
- import { defineCommand as defineCommand136 } from "citty";
16828
+ import { defineCommand as defineCommand139 } from "citty";
16344
16829
  registerSchema({
16345
16830
  command: "testimonials.get",
16346
16831
  description: "Get a single testimonial by ID",
@@ -16348,7 +16833,7 @@ registerSchema({
16348
16833
  id: { type: "string", description: "Testimonial ID", required: true }
16349
16834
  }
16350
16835
  });
16351
- var getCommand4 = defineCommand136({
16836
+ var getCommand4 = defineCommand139({
16352
16837
  meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
16353
16838
  args: {
16354
16839
  id: { type: "positional", description: "Testimonial ID", required: false },
@@ -16385,7 +16870,7 @@ var getCommand4 = defineCommand136({
16385
16870
  });
16386
16871
 
16387
16872
  // src/commands/testimonials/list.ts
16388
- import { defineCommand as defineCommand137 } from "citty";
16873
+ import { defineCommand as defineCommand140 } from "citty";
16389
16874
  registerSchema({
16390
16875
  command: "testimonials.list",
16391
16876
  description: "List testimonials with optional filters.",
@@ -16415,7 +16900,7 @@ registerSchema({
16415
16900
  limit: { type: "number", description: "Max results (default 50)", required: false, default: 50 }
16416
16901
  }
16417
16902
  });
16418
- var listCommand4 = defineCommand137({
16903
+ var listCommand4 = defineCommand140({
16419
16904
  meta: {
16420
16905
  name: "list",
16421
16906
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
@@ -16464,7 +16949,7 @@ var listCommand4 = defineCommand137({
16464
16949
  });
16465
16950
 
16466
16951
  // src/commands/testimonials/search.ts
16467
- import { defineCommand as defineCommand138 } from "citty";
16952
+ import { defineCommand as defineCommand141 } from "citty";
16468
16953
  registerSchema({
16469
16954
  command: "testimonials.search",
16470
16955
  description: "Search testimonials by text query. Uses hybrid BM25 + vector + reranking.",
@@ -16495,7 +16980,7 @@ registerSchema({
16495
16980
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
16496
16981
  }
16497
16982
  });
16498
- var searchCommand2 = defineCommand138({
16983
+ var searchCommand2 = defineCommand141({
16499
16984
  meta: {
16500
16985
  name: "search",
16501
16986
  description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
@@ -16569,7 +17054,7 @@ var searchCommand2 = defineCommand138({
16569
17054
  var tagsCommand3 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
16570
17055
 
16571
17056
  // src/commands/testimonials/index.ts
16572
- var testimonialsCommand = defineCommand139({
17057
+ var testimonialsCommand = defineCommand142({
16573
17058
  meta: {
16574
17059
  name: "testimonials",
16575
17060
  description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
@@ -16590,10 +17075,10 @@ Examples:
16590
17075
  });
16591
17076
 
16592
17077
  // src/commands/videos/index.ts
16593
- import { defineCommand as defineCommand144 } from "citty";
17078
+ import { defineCommand as defineCommand147 } from "citty";
16594
17079
 
16595
17080
  // src/commands/videos/delete.ts
16596
- import { defineCommand as defineCommand140 } from "citty";
17081
+ import { defineCommand as defineCommand143 } from "citty";
16597
17082
  registerSchema({
16598
17083
  command: "videos.delete",
16599
17084
  description: "Delete a video by ID",
@@ -16607,7 +17092,7 @@ registerSchema({
16607
17092
  }
16608
17093
  }
16609
17094
  });
16610
- var deleteCommand3 = defineCommand140({
17095
+ var deleteCommand3 = defineCommand143({
16611
17096
  meta: {
16612
17097
  name: "delete",
16613
17098
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -16648,7 +17133,7 @@ var deleteCommand3 = defineCommand140({
16648
17133
  });
16649
17134
 
16650
17135
  // src/commands/videos/get.ts
16651
- import { defineCommand as defineCommand141 } from "citty";
17136
+ import { defineCommand as defineCommand144 } from "citty";
16652
17137
  registerSchema({
16653
17138
  command: "videos.get",
16654
17139
  description: "Get a single video by ID",
@@ -16656,7 +17141,7 @@ registerSchema({
16656
17141
  id: { type: "string", description: "Video ID", required: true }
16657
17142
  }
16658
17143
  });
16659
- var getCommand5 = defineCommand141({
17144
+ var getCommand5 = defineCommand144({
16660
17145
  meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
16661
17146
  args: {
16662
17147
  id: { type: "positional", description: "Video ID", required: false },
@@ -16693,7 +17178,7 @@ var getCommand5 = defineCommand141({
16693
17178
  });
16694
17179
 
16695
17180
  // src/commands/videos/search.ts
16696
- import { defineCommand as defineCommand142 } from "citty";
17181
+ import { defineCommand as defineCommand145 } from "citty";
16697
17182
  registerSchema({
16698
17183
  command: "videos.search",
16699
17184
  description: "Search videos by text query. Only returns ready videos.",
@@ -16703,7 +17188,7 @@ registerSchema({
16703
17188
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
16704
17189
  }
16705
17190
  });
16706
- var searchCommand3 = defineCommand142({
17191
+ var searchCommand3 = defineCommand145({
16707
17192
  meta: {
16708
17193
  name: "search",
16709
17194
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -16753,10 +17238,10 @@ var searchCommand3 = defineCommand142({
16753
17238
  var tagsCommand4 = makeTagsCommand("videos", "video", "/api/videos/tags");
16754
17239
 
16755
17240
  // src/commands/videos/upload.ts
16756
- import { readFile as readFile10, stat as stat3 } from "fs/promises";
17241
+ import { readFile as readFile12, stat as stat3 } from "fs/promises";
16757
17242
  import { extname as extname3 } from "path";
16758
- import { defineCommand as defineCommand143 } from "citty";
16759
- var MIME_MAP2 = {
17243
+ import { defineCommand as defineCommand146 } from "citty";
17244
+ var MIME_MAP = {
16760
17245
  ".mp4": "video/mp4",
16761
17246
  ".mov": "video/quicktime",
16762
17247
  ".webm": "video/webm",
@@ -16781,15 +17266,15 @@ registerSchema({
16781
17266
  }
16782
17267
  }
16783
17268
  });
16784
- function detectContentType2(filePath) {
17269
+ function detectContentType(filePath) {
16785
17270
  const ext = extname3(filePath).toLowerCase();
16786
- const mime = MIME_MAP2[ext];
17271
+ const mime = MIME_MAP[ext];
16787
17272
  if (!mime) {
16788
17273
  throw new ApiError("VALIDATION_ERROR", `Cannot detect content type for extension "${ext}". Use --content-type.`);
16789
17274
  }
16790
17275
  return mime;
16791
17276
  }
16792
- var uploadCommand2 = defineCommand143({
17277
+ var uploadCommand2 = defineCommand146({
16793
17278
  meta: {
16794
17279
  name: "upload",
16795
17280
  description: "Upload a video file to Baker via Mux direct upload. Auto-detects content type. Example: baker videos upload ./demo.mp4"
@@ -16806,7 +17291,7 @@ var uploadCommand2 = defineCommand143({
16806
17291
  writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "File path is required" } });
16807
17292
  process.exit(1);
16808
17293
  }
16809
- const contentType = args["content-type"] || detectContentType2(filePath);
17294
+ const contentType = args["content-type"] || detectContentType(filePath);
16810
17295
  if (args["dry-run"]) {
16811
17296
  const fileStats = await stat3(filePath);
16812
17297
  writeJson({
@@ -16818,7 +17303,7 @@ var uploadCommand2 = defineCommand143({
16818
17303
  return;
16819
17304
  }
16820
17305
  const { uploadUrl, videoId } = await apiPost("/api/videos/upload", {});
16821
- const fileBuffer = await readFile10(filePath);
17306
+ const fileBuffer = await readFile12(filePath);
16822
17307
  const uploadResponse = await fetch(uploadUrl, {
16823
17308
  method: "PUT",
16824
17309
  headers: { "Content-Type": contentType },
@@ -16843,7 +17328,7 @@ var uploadCommand2 = defineCommand143({
16843
17328
  });
16844
17329
 
16845
17330
  // src/commands/videos/index.ts
16846
- var videosCommand = defineCommand144({
17331
+ var videosCommand = defineCommand147({
16847
17332
  meta: {
16848
17333
  name: "videos",
16849
17334
  description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete, tags.
@@ -16866,10 +17351,10 @@ Examples:
16866
17351
  });
16867
17352
 
16868
17353
  // src/commands/winning-ads/index.ts
16869
- import { defineCommand as defineCommand147 } from "citty";
17354
+ import { defineCommand as defineCommand150 } from "citty";
16870
17355
 
16871
17356
  // src/commands/winning-ads/advertisers.ts
16872
- import { defineCommand as defineCommand145 } from "citty";
17357
+ import { defineCommand as defineCommand148 } from "citty";
16873
17358
  registerSchema({
16874
17359
  command: "winning-ads.advertisers",
16875
17360
  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).",
@@ -16882,7 +17367,7 @@ registerSchema({
16882
17367
  function identity(record) {
16883
17368
  return record;
16884
17369
  }
16885
- var advertisersCommand2 = defineCommand145({
17370
+ var advertisersCommand2 = defineCommand148({
16886
17371
  meta: {
16887
17372
  name: "advertisers",
16888
17373
  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'
@@ -16933,7 +17418,7 @@ var advertisersCommand2 = defineCommand145({
16933
17418
  });
16934
17419
 
16935
17420
  // src/commands/winning-ads/search.ts
16936
- import { defineCommand as defineCommand146 } from "citty";
17421
+ import { defineCommand as defineCommand149 } from "citty";
16937
17422
  registerSchema({
16938
17423
  command: "winning-ads.search",
16939
17424
  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.",
@@ -17041,7 +17526,7 @@ function buildSearchBody(args) {
17041
17526
  }
17042
17527
  return body;
17043
17528
  }
17044
- var searchCommand4 = defineCommand146({
17529
+ var searchCommand4 = defineCommand149({
17045
17530
  meta: {
17046
17531
  name: "search",
17047
17532
  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"
@@ -17153,7 +17638,7 @@ var searchCommand4 = defineCommand146({
17153
17638
  });
17154
17639
 
17155
17640
  // src/commands/winning-ads/index.ts
17156
- var winningAdsCommand = defineCommand147({
17641
+ var winningAdsCommand = defineCommand150({
17157
17642
  meta: {
17158
17643
  name: "winning-ads",
17159
17644
  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.
@@ -17193,7 +17678,7 @@ function getCliVersion() {
17193
17678
  }
17194
17679
 
17195
17680
  // src/cli.ts
17196
- var main = defineCommand148({
17681
+ var main = defineCommand151({
17197
17682
  meta: {
17198
17683
  name: "baker",
17199
17684
  version: getCliVersion(),
@@ -17212,6 +17697,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
17212
17697
  ga4: ga4Command,
17213
17698
  gsc: gscCommand,
17214
17699
  research: researchCommand,
17700
+ creatives: creativesCommand3,
17215
17701
  images: imagesCommand,
17216
17702
  videos: videosCommand,
17217
17703
  testimonials: testimonialsCommand,