@koda-sl/baker-cli 0.97.0 → 0.98.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -9,10 +9,10 @@ import {
9
9
  defaultRegistry,
10
10
  generateCatalog,
11
11
  validateCanvasDeep
12
- } from "./chunk-RCPMJKI7.js";
12
+ } from "./chunk-3JVYU72O.js";
13
13
 
14
14
  // src/cli.ts
15
- import { defineCommand as defineCommand148, runMain } from "citty";
15
+ import { defineCommand as defineCommand149, 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,16 @@ 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
11557
11878
  }
11558
11879
  });
11559
11880
 
11560
11881
  // src/commands/ga4/index.ts
11561
- import { defineCommand as defineCommand88 } from "citty";
11882
+ import { defineCommand as defineCommand89 } from "citty";
11562
11883
 
11563
11884
  // src/commands/ga4/audit.ts
11564
- import { defineCommand as defineCommand85 } from "citty";
11885
+ import { defineCommand as defineCommand86 } from "citty";
11565
11886
 
11566
11887
  // src/commands/ga4/resolve.ts
11567
11888
  async function fetchProperties(useCache = true) {
@@ -11624,7 +11945,7 @@ registerSchema({
11624
11945
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
11625
11946
  }
11626
11947
  });
11627
- var auditCommand2 = defineCommand85({
11948
+ var auditCommand2 = defineCommand86({
11628
11949
  meta: {
11629
11950
  name: "audit",
11630
11951
  description: `Run all GA4 admin health checks. Returns property config with playbook warnings.
@@ -11676,7 +11997,7 @@ Examples:
11676
11997
  });
11677
11998
 
11678
11999
  // src/commands/ga4/properties.ts
11679
- import { defineCommand as defineCommand86 } from "citty";
12000
+ import { defineCommand as defineCommand87 } from "citty";
11680
12001
  registerSchema({
11681
12002
  command: "ga4.properties",
11682
12003
  description: "List all accessible GA4 properties. Returns property IDs needed for query and audit commands. Run this first to find property IDs.",
@@ -11684,7 +12005,7 @@ registerSchema({
11684
12005
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
11685
12006
  }
11686
12007
  });
11687
- var propertiesCommand = defineCommand86({
12008
+ var propertiesCommand = defineCommand87({
11688
12009
  meta: {
11689
12010
  name: "properties",
11690
12011
  description: `List accessible GA4 properties.
@@ -11734,7 +12055,7 @@ Examples:
11734
12055
  // src/commands/ga4/query.ts
11735
12056
  import { appendFileSync as appendFileSync2, existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
11736
12057
  import { resolve as resolve2 } from "path";
11737
- import { defineCommand as defineCommand87 } from "citty";
12058
+ import { defineCommand as defineCommand88 } from "citty";
11738
12059
 
11739
12060
  // src/commands/ga4/presets.ts
11740
12061
  var GA4_PRESETS = [
@@ -11866,7 +12187,7 @@ function handleError(err) {
11866
12187
  });
11867
12188
  process.exit(1);
11868
12189
  }
11869
- var queryCommand2 = defineCommand87({
12190
+ var queryCommand2 = defineCommand88({
11870
12191
  meta: {
11871
12192
  name: "query",
11872
12193
  description: `Run GA4 Data API reports. Preset-first with free-form escape hatch.
@@ -11937,7 +12258,7 @@ Free-form (escape hatch):
11937
12258
  });
11938
12259
 
11939
12260
  // src/commands/ga4/index.ts
11940
- var ga4Command = defineCommand88({
12261
+ var ga4Command = defineCommand89({
11941
12262
  meta: {
11942
12263
  name: "ga4",
11943
12264
  description: `Google Analytics 4 commands. Audit property config, run playbook-aligned reports.
@@ -11960,12 +12281,12 @@ Examples:
11960
12281
  });
11961
12282
 
11962
12283
  // src/commands/gsc/index.ts
11963
- import { defineCommand as defineCommand92 } from "citty";
12284
+ import { defineCommand as defineCommand93 } from "citty";
11964
12285
 
11965
12286
  // src/commands/gsc/query.ts
11966
12287
  import { appendFileSync as appendFileSync3, existsSync as existsSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
11967
12288
  import { resolve as resolve3 } from "path";
11968
- import { defineCommand as defineCommand89 } from "citty";
12289
+ import { defineCommand as defineCommand90 } from "citty";
11969
12290
 
11970
12291
  // src/commands/gsc/presets.ts
11971
12292
  var GSC_PRESETS = [
@@ -12153,7 +12474,7 @@ function handleError2(err) {
12153
12474
  });
12154
12475
  process.exit(1);
12155
12476
  }
12156
- var queryCommand3 = defineCommand89({
12477
+ var queryCommand3 = defineCommand90({
12157
12478
  meta: {
12158
12479
  name: "query",
12159
12480
  description: `Run GSC Search Analytics queries. Preset-first with free-form escape hatch.
@@ -12231,7 +12552,7 @@ Free-form (escape hatch):
12231
12552
  });
12232
12553
 
12233
12554
  // src/commands/gsc/sitemaps.ts
12234
- import { defineCommand as defineCommand90 } from "citty";
12555
+ import { defineCommand as defineCommand91 } from "citty";
12235
12556
  registerSchema({
12236
12557
  command: "gsc.sitemaps",
12237
12558
  description: "List sitemaps for a Search Console site. Check sitemap health and errors.",
@@ -12240,7 +12561,7 @@ registerSchema({
12240
12561
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
12241
12562
  }
12242
12563
  });
12243
- var sitemapsCommand = defineCommand90({
12564
+ var sitemapsCommand = defineCommand91({
12244
12565
  meta: {
12245
12566
  name: "sitemaps",
12246
12567
  description: `List sitemaps for a site. Check health and errors.
@@ -12290,7 +12611,7 @@ Examples:
12290
12611
  });
12291
12612
 
12292
12613
  // src/commands/gsc/sites.ts
12293
- import { defineCommand as defineCommand91 } from "citty";
12614
+ import { defineCommand as defineCommand92 } from "citty";
12294
12615
  registerSchema({
12295
12616
  command: "gsc.sites",
12296
12617
  description: "List all verified Google Search Console sites. Returns site URLs needed for query and sitemaps commands.",
@@ -12298,7 +12619,7 @@ registerSchema({
12298
12619
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
12299
12620
  }
12300
12621
  });
12301
- var sitesCommand = defineCommand91({
12622
+ var sitesCommand = defineCommand92({
12302
12623
  meta: {
12303
12624
  name: "sites",
12304
12625
  description: `List verified Search Console sites.
@@ -12346,7 +12667,7 @@ Examples:
12346
12667
  });
12347
12668
 
12348
12669
  // src/commands/gsc/index.ts
12349
- var gscCommand = defineCommand92({
12670
+ var gscCommand = defineCommand93({
12350
12671
  meta: {
12351
12672
  name: "gsc",
12352
12673
  description: `Google Search Console commands. PPC-SEO arbitrage, brand halo analysis, negative keyword discovery.
@@ -12369,10 +12690,10 @@ Examples:
12369
12690
  });
12370
12691
 
12371
12692
  // src/commands/images/index.ts
12372
- import { defineCommand as defineCommand116 } from "citty";
12693
+ import { defineCommand as defineCommand117 } from "citty";
12373
12694
 
12374
12695
  // src/commands/images/crop.ts
12375
- import { defineCommand as defineCommand93 } from "citty";
12696
+ import { defineCommand as defineCommand94 } from "citty";
12376
12697
 
12377
12698
  // src/lib/image/crop-sprite.ts
12378
12699
  import sharp from "sharp";
@@ -12387,7 +12708,7 @@ function cropSprite(input, region) {
12387
12708
 
12388
12709
  // src/lib/image/io.ts
12389
12710
  import { randomBytes } from "crypto";
12390
- import { glob as fsGlob, readFile as readFile7, rename, stat as stat2, writeFile as writeFile3 } from "fs/promises";
12711
+ import { glob as fsGlob, readFile as readFile9, rename, stat as stat2, writeFile as writeFile4 } from "fs/promises";
12391
12712
  import { dirname, extname, join as join3, resolve as resolve4 } from "path";
12392
12713
  var REMOTE_RE = /^https?:\/\//i;
12393
12714
  var GLOB_RE = /[*?[\]{}]/;
@@ -12423,11 +12744,11 @@ async function readImageBuffer(pathOrUrl) {
12423
12744
  }
12424
12745
  return Buffer.from(await response.arrayBuffer());
12425
12746
  }
12426
- return readFile7(pathOrUrl);
12747
+ return readFile9(pathOrUrl);
12427
12748
  }
12428
- async function isDirectory(path7) {
12749
+ async function isDirectory(path11) {
12429
12750
  try {
12430
- const s = await stat2(path7);
12751
+ const s = await stat2(path11);
12431
12752
  return s.isDirectory();
12432
12753
  } catch {
12433
12754
  return false;
@@ -12446,7 +12767,7 @@ async function atomicWrite(targetPath, data) {
12446
12767
  const absolute = resolve4(targetPath);
12447
12768
  const dir = dirname(absolute);
12448
12769
  const tmp = join3(dir, `.baker-image-${randomBytes(8).toString("hex")}.tmp`);
12449
- await writeFile3(tmp, data);
12770
+ await writeFile4(tmp, data);
12450
12771
  await rename(tmp, absolute);
12451
12772
  }
12452
12773
 
@@ -12497,7 +12818,7 @@ function emitError2(err) {
12497
12818
  }
12498
12819
  process.exit(1);
12499
12820
  }
12500
- var cropCommand = defineCommand93({
12821
+ var cropCommand = defineCommand94({
12501
12822
  meta: {
12502
12823
  name: "crop",
12503
12824
  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 +12854,7 @@ var cropCommand = defineCommand93({
12533
12854
  });
12534
12855
 
12535
12856
  // src/commands/images/delete.ts
12536
- import { defineCommand as defineCommand94 } from "citty";
12857
+ import { defineCommand as defineCommand95 } from "citty";
12537
12858
  registerSchema({
12538
12859
  command: "images.delete",
12539
12860
  description: "Delete an image by ID",
@@ -12547,7 +12868,7 @@ registerSchema({
12547
12868
  }
12548
12869
  }
12549
12870
  });
12550
- var deleteCommand = defineCommand94({
12871
+ var deleteCommand = defineCommand95({
12551
12872
  meta: {
12552
12873
  name: "delete",
12553
12874
  description: "Delete an image by ID. Use --dry-run to preview. Example: baker images delete j571abc123 --dry-run"
@@ -12588,7 +12909,7 @@ var deleteCommand = defineCommand94({
12588
12909
  });
12589
12910
 
12590
12911
  // src/commands/images/dimensions.ts
12591
- import { defineCommand as defineCommand95 } from "citty";
12912
+ import { defineCommand as defineCommand96 } from "citty";
12592
12913
 
12593
12914
  // src/lib/image/dimensions.ts
12594
12915
  import { imageSize } from "image-size";
@@ -12611,7 +12932,7 @@ registerSchema({
12611
12932
  target: { type: "string", description: "Local file path or remote http(s) URL", required: true }
12612
12933
  }
12613
12934
  });
12614
- var dimensionsCommand = defineCommand95({
12935
+ var dimensionsCommand = defineCommand96({
12615
12936
  meta: {
12616
12937
  name: "dimensions",
12617
12938
  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 +12976,7 @@ var dimensionsCommand = defineCommand95({
12655
12976
  });
12656
12977
 
12657
12978
  // src/commands/images/extract.ts
12658
- import { defineCommand as defineCommand96 } from "citty";
12979
+ import { defineCommand as defineCommand97 } from "citty";
12659
12980
  registerSchema({
12660
12981
  command: "images.extract",
12661
12982
  description: "Extract images from a URL via Firecrawl (formats: images).",
@@ -12671,7 +12992,7 @@ registerSchema({
12671
12992
  }
12672
12993
  }
12673
12994
  });
12674
- var extractCommand = defineCommand96({
12995
+ var extractCommand = defineCommand97({
12675
12996
  meta: {
12676
12997
  name: "extract",
12677
12998
  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 +13030,7 @@ var extractCommand = defineCommand96({
12709
13030
  });
12710
13031
 
12711
13032
  // src/commands/images/find.ts
12712
- import { defineCommand as defineCommand97 } from "citty";
13033
+ import { defineCommand as defineCommand98 } from "citty";
12713
13034
  registerSchema({
12714
13035
  command: "images.find",
12715
13036
  description: "Fanout image search: library first, then opted-in external providers.",
@@ -12741,7 +13062,7 @@ registerSchema({
12741
13062
  }
12742
13063
  }
12743
13064
  });
12744
- var findCommand = defineCommand97({
13065
+ var findCommand = defineCommand98({
12745
13066
  meta: {
12746
13067
  name: "find",
12747
13068
  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 +13108,8 @@ var findCommand = defineCommand97({
12787
13108
  });
12788
13109
 
12789
13110
  // src/commands/images/generate.ts
12790
- import { readFile as readFile8 } from "fs/promises";
12791
- import { defineCommand as defineCommand98 } from "citty";
13111
+ import { readFile as readFile10 } from "fs/promises";
13112
+ import { defineCommand as defineCommand99 } from "citty";
12792
13113
  import sharp2 from "sharp";
12793
13114
  var GENERATE_TIMEOUT_MS = 18e4;
12794
13115
  var REFERENCE_MAX_EDGE = 1536;
@@ -12870,7 +13191,7 @@ async function resolveReferences(spec) {
12870
13191
  }
12871
13192
  let raw;
12872
13193
  try {
12873
- raw = await readFile8(entry);
13194
+ raw = await readFile10(entry);
12874
13195
  } catch {
12875
13196
  throw new ApiError("VALIDATION_ERROR", `Reference file not found: ${entry}`);
12876
13197
  }
@@ -12884,7 +13205,7 @@ async function resolveReferences(spec) {
12884
13205
  }
12885
13206
  return out;
12886
13207
  }
12887
- var generateCommand = defineCommand98({
13208
+ var generateCommand = defineCommand99({
12888
13209
  meta: {
12889
13210
  name: "generate",
12890
13211
  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 +13257,7 @@ var generateCommand = defineCommand98({
12936
13257
  });
12937
13258
 
12938
13259
  // src/commands/images/get.ts
12939
- import { defineCommand as defineCommand99 } from "citty";
13260
+ import { defineCommand as defineCommand100 } from "citty";
12940
13261
  registerSchema({
12941
13262
  command: "images.get",
12942
13263
  description: "Get a single image by ID",
@@ -12944,7 +13265,7 @@ registerSchema({
12944
13265
  id: { type: "string", description: "Image ID", required: true }
12945
13266
  }
12946
13267
  });
12947
- var getCommand2 = defineCommand99({
13268
+ var getCommand2 = defineCommand100({
12948
13269
  meta: { name: "get", description: "Get a single image by ID. Example: baker images get j571abc123" },
12949
13270
  args: {
12950
13271
  id: { type: "positional", description: "Image ID", required: false },
@@ -12980,7 +13301,7 @@ var getCommand2 = defineCommand99({
12980
13301
  });
12981
13302
 
12982
13303
  // src/commands/images/gif.ts
12983
- import { defineCommand as defineCommand100 } from "citty";
13304
+ import { defineCommand as defineCommand101 } from "citty";
12984
13305
  registerSchema({
12985
13306
  command: "images.gif",
12986
13307
  description: "Search Giphy for GIFs / reaction memes (paid social creative).",
@@ -13012,7 +13333,7 @@ registerSchema({
13012
13333
  }
13013
13334
  }
13014
13335
  });
13015
- var gifCommand = defineCommand100({
13336
+ var gifCommand = defineCommand101({
13016
13337
  meta: {
13017
13338
  name: "gif",
13018
13339
  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 +13380,7 @@ var gifCommand = defineCommand100({
13059
13380
  });
13060
13381
 
13061
13382
  // src/commands/images/google.ts
13062
- import { defineCommand as defineCommand101 } from "citty";
13383
+ import { defineCommand as defineCommand102 } from "citty";
13063
13384
  registerSchema({
13064
13385
  command: "images.google",
13065
13386
  description: "Google Images search via the official Custom Search JSON API. Unverified source \u2014 inspect before placing.",
@@ -13095,7 +13416,7 @@ registerSchema({
13095
13416
  }
13096
13417
  }
13097
13418
  });
13098
- var googleCommand2 = defineCommand101({
13419
+ var googleCommand2 = defineCommand102({
13099
13420
  meta: {
13100
13421
  name: "google",
13101
13422
  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 +13464,7 @@ var googleCommand2 = defineCommand101({
13143
13464
  });
13144
13465
 
13145
13466
  // src/commands/images/icon.ts
13146
- import { defineCommand as defineCommand102 } from "citty";
13467
+ import { defineCommand as defineCommand103 } from "citty";
13147
13468
  registerSchema({
13148
13469
  command: "images.icon",
13149
13470
  description: "Icon lookup via Iconify (200+ icon sets, free CDN).",
@@ -13169,7 +13490,7 @@ registerSchema({
13169
13490
  }
13170
13491
  }
13171
13492
  });
13172
- var iconCommand = defineCommand102({
13493
+ var iconCommand = defineCommand103({
13173
13494
  meta: {
13174
13495
  name: "icon",
13175
13496
  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 +13530,7 @@ var iconCommand = defineCommand102({
13209
13530
  });
13210
13531
 
13211
13532
  // src/commands/images/ingest.ts
13212
- import { defineCommand as defineCommand103 } from "citty";
13533
+ import { defineCommand as defineCommand104 } from "citty";
13213
13534
  registerSchema({
13214
13535
  command: "images.ingest",
13215
13536
  description: "Ingest a remote image URL into the library (full describe + embed).",
@@ -13221,7 +13542,7 @@ registerSchema({
13221
13542
  context: { type: "string", description: "Description context hint", required: false }
13222
13543
  }
13223
13544
  });
13224
- var ingestCommand = defineCommand103({
13545
+ var ingestCommand = defineCommand104({
13225
13546
  meta: {
13226
13547
  name: "ingest",
13227
13548
  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 +13584,7 @@ var ingestCommand = defineCommand103({
13263
13584
  });
13264
13585
 
13265
13586
  // src/commands/images/library.ts
13266
- import { defineCommand as defineCommand104 } from "citty";
13587
+ import { defineCommand as defineCommand105 } from "citty";
13267
13588
  registerSchema({
13268
13589
  command: "images.library",
13269
13590
  description: "Search the company image library. Returns only ready images.",
@@ -13289,7 +13610,7 @@ registerSchema({
13289
13610
  }
13290
13611
  }
13291
13612
  });
13292
- var libraryCommand = defineCommand104({
13613
+ var libraryCommand = defineCommand105({
13293
13614
  meta: {
13294
13615
  name: "library",
13295
13616
  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 +13667,7 @@ var libraryCommand = defineCommand104({
13346
13667
  });
13347
13668
 
13348
13669
  // src/commands/images/logo.ts
13349
- import { defineCommand as defineCommand105 } from "citty";
13670
+ import { defineCommand as defineCommand106 } from "citty";
13350
13671
  registerSchema({
13351
13672
  command: "images.logo",
13352
13673
  description: "Brand logo lookup via Brandfetch CDN (fallback/404). Auto-ingests by default.",
@@ -13371,7 +13692,7 @@ registerSchema({
13371
13692
  }
13372
13693
  }
13373
13694
  });
13374
- var logoCommand = defineCommand105({
13695
+ var logoCommand = defineCommand106({
13375
13696
  meta: {
13376
13697
  name: "logo",
13377
13698
  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 +13730,7 @@ var logoCommand = defineCommand105({
13409
13730
  });
13410
13731
 
13411
13732
  // src/commands/images/normalize.ts
13412
- import { defineCommand as defineCommand106 } from "citty";
13733
+ import { defineCommand as defineCommand107 } from "citty";
13413
13734
 
13414
13735
  // src/lib/image/color-changer.ts
13415
13736
  import quantize from "quantize";
@@ -14141,7 +14462,7 @@ function coerceRawArgs(args) {
14141
14462
  "dry-run": bool(args["dry-run"])
14142
14463
  };
14143
14464
  }
14144
- var normalizeCommand = defineCommand106({
14465
+ var normalizeCommand = defineCommand107({
14145
14466
  meta: {
14146
14467
  name: "normalize",
14147
14468
  description: `Normalize logos / images: declarative recolor + bg removal + trim + resize. Operates on local files; writes in-place by default.
@@ -14196,7 +14517,7 @@ Examples:
14196
14517
  });
14197
14518
 
14198
14519
  // src/commands/images/pinterest.ts
14199
- import { defineCommand as defineCommand107 } from "citty";
14520
+ import { defineCommand as defineCommand108 } from "citty";
14200
14521
  registerSchema({
14201
14522
  command: "images.pinterest",
14202
14523
  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 +14537,7 @@ registerSchema({
14216
14537
  }
14217
14538
  }
14218
14539
  });
14219
- var pinterestCommand = defineCommand107({
14540
+ var pinterestCommand = defineCommand108({
14220
14541
  meta: {
14221
14542
  name: "pinterest",
14222
14543
  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 +14577,7 @@ var pinterestCommand = defineCommand107({
14256
14577
  });
14257
14578
 
14258
14579
  // src/commands/images/screenshot.ts
14259
- import { defineCommand as defineCommand108 } from "citty";
14580
+ import { defineCommand as defineCommand109 } from "citty";
14260
14581
  registerSchema({
14261
14582
  command: "images.screenshot",
14262
14583
  description: "Capture a website screenshot via ScreenshotOne. Auto-ingests on success.",
@@ -14272,7 +14593,7 @@ registerSchema({
14272
14593
  }
14273
14594
  }
14274
14595
  });
14275
- var screenshotCommand = defineCommand108({
14596
+ var screenshotCommand = defineCommand109({
14276
14597
  meta: {
14277
14598
  name: "screenshot",
14278
14599
  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 +14643,7 @@ var screenshotCommand = defineCommand108({
14322
14643
  });
14323
14644
 
14324
14645
  // src/commands/images/search.ts
14325
- import { defineCommand as defineCommand109 } from "citty";
14646
+ import { defineCommand as defineCommand110 } from "citty";
14326
14647
  registerSchema({
14327
14648
  command: "images.search",
14328
14649
  description: "Search images by text query. Only returns ready images.",
@@ -14338,7 +14659,7 @@ registerSchema({
14338
14659
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
14339
14660
  }
14340
14661
  });
14341
- var searchCommand = defineCommand109({
14662
+ var searchCommand = defineCommand110({
14342
14663
  meta: {
14343
14664
  name: "search",
14344
14665
  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 +14719,7 @@ var searchCommand = defineCommand109({
14398
14719
  });
14399
14720
 
14400
14721
  // src/commands/images/sticker.ts
14401
- import { defineCommand as defineCommand110 } from "citty";
14722
+ import { defineCommand as defineCommand111 } from "citty";
14402
14723
  registerSchema({
14403
14724
  command: "images.sticker",
14404
14725
  description: "Search Giphy stickers \u2014 transparent-background overlays for ad creative.",
@@ -14430,7 +14751,7 @@ registerSchema({
14430
14751
  }
14431
14752
  }
14432
14753
  });
14433
- var stickerCommand = defineCommand110({
14754
+ var stickerCommand = defineCommand111({
14434
14755
  meta: {
14435
14756
  name: "sticker",
14436
14757
  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 +14798,7 @@ var stickerCommand = defineCommand110({
14477
14798
  });
14478
14799
 
14479
14800
  // src/commands/images/stock.ts
14480
- import { defineCommand as defineCommand111 } from "citty";
14801
+ import { defineCommand as defineCommand112 } from "citty";
14481
14802
  registerSchema({
14482
14803
  command: "images.stock",
14483
14804
  description: "Stock photo, vector illustration, icon-set, and PSD search via Magnific (Freepik's developer API).",
@@ -14535,7 +14856,7 @@ registerSchema({
14535
14856
  }
14536
14857
  }
14537
14858
  });
14538
- var stockCommand = defineCommand111({
14859
+ var stockCommand = defineCommand112({
14539
14860
  meta: {
14540
14861
  name: "stock",
14541
14862
  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 +14912,7 @@ var stockCommand = defineCommand111({
14591
14912
  });
14592
14913
 
14593
14914
  // src/lib/tags-command.ts
14594
- import { defineCommand as defineCommand112 } from "citty";
14915
+ import { defineCommand as defineCommand113 } from "citty";
14595
14916
  function makeTagsCommand(command, label, endpoint) {
14596
14917
  registerSchema({
14597
14918
  command: `${command}.tags`,
@@ -14600,7 +14921,7 @@ function makeTagsCommand(command, label, endpoint) {
14600
14921
  output: { type: "string", description: "Output format: md|json", required: false, default: "md" }
14601
14922
  }
14602
14923
  });
14603
- return defineCommand112({
14924
+ return defineCommand113({
14604
14925
  meta: {
14605
14926
  name: "tags",
14606
14927
  description: `List the available ${label} tag names (defaults + company custom tags). Use before filtering with --tags. Example: baker ${command} tags`
@@ -14636,9 +14957,9 @@ function makeTagsCommand(command, label, endpoint) {
14636
14957
  var tagsCommand2 = makeTagsCommand("images", "image", "/api/images/tags");
14637
14958
 
14638
14959
  // src/commands/images/upload.ts
14639
- import { readFile as readFile9 } from "fs/promises";
14960
+ import { readFile as readFile11 } from "fs/promises";
14640
14961
  import { extname as extname2 } from "path";
14641
- import { defineCommand as defineCommand113 } from "citty";
14962
+ import { defineCommand as defineCommand114 } from "citty";
14642
14963
  var MIME_MAP = {
14643
14964
  ".png": "image/png",
14644
14965
  ".jpg": "image/jpeg",
@@ -14693,7 +15014,7 @@ function detectContentType(filePath) {
14693
15014
  }
14694
15015
  return mime;
14695
15016
  }
14696
- var uploadCommand = defineCommand113({
15017
+ var uploadCommand = defineCommand114({
14697
15018
  meta: {
14698
15019
  name: "upload",
14699
15020
  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'"
@@ -14776,7 +15097,7 @@ async function uploadLocal(target, args) {
14776
15097
  });
14777
15098
  return;
14778
15099
  }
14779
- const fileBuffer = await readFile9(target);
15100
+ const fileBuffer = await readFile11(target);
14780
15101
  const base64 = fileBuffer.toString("base64");
14781
15102
  const body = { base64, contentType };
14782
15103
  if (args.source) body.source = args.source;
@@ -14786,7 +15107,7 @@ async function uploadLocal(target, args) {
14786
15107
  }
14787
15108
 
14788
15109
  // src/commands/images/upscale.ts
14789
- import { defineCommand as defineCommand114 } from "citty";
15110
+ import { defineCommand as defineCommand115 } from "citty";
14790
15111
  registerSchema({
14791
15112
  command: "images.upscale",
14792
15113
  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 +15122,7 @@ registerSchema({
14801
15122
  }
14802
15123
  });
14803
15124
  var POLL_INTERVAL_MS3 = 1500;
14804
- var upscaleCommand = defineCommand114({
15125
+ var upscaleCommand = defineCommand115({
14805
15126
  meta: {
14806
15127
  name: "upscale",
14807
15128
  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 +15177,7 @@ var upscaleCommand = defineCommand114({
14856
15177
  });
14857
15178
 
14858
15179
  // src/commands/images/use.ts
14859
- import { defineCommand as defineCommand115 } from "citty";
15180
+ import { defineCommand as defineCommand116 } from "citty";
14860
15181
  registerSchema({
14861
15182
  command: "images.use",
14862
15183
  description: "Ingest a URL and wait for the library record to be ready.",
@@ -14872,7 +15193,7 @@ registerSchema({
14872
15193
  }
14873
15194
  });
14874
15195
  var POLL_INTERVAL_MS4 = 1500;
14875
- var useCommand = defineCommand115({
15196
+ var useCommand = defineCommand116({
14876
15197
  meta: {
14877
15198
  name: "use",
14878
15199
  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 +15239,7 @@ var useCommand = defineCommand115({
14918
15239
  });
14919
15240
 
14920
15241
  // src/commands/images/index.ts
14921
- var imagesCommand = defineCommand116({
15242
+ var imagesCommand = defineCommand117({
14922
15243
  meta: {
14923
15244
  name: "images",
14924
15245
  description: `Find, source, and normalize images. Subcommands route by provider so cost + license are explicit.
@@ -14988,10 +15309,10 @@ Paid transforms (run on the Convex backend, cost-tracked):
14988
15309
  });
14989
15310
 
14990
15311
  // src/commands/research/index.ts
14991
- import { defineCommand as defineCommand127 } from "citty";
15312
+ import { defineCommand as defineCommand128 } from "citty";
14992
15313
 
14993
15314
  // src/commands/research/advertisers.ts
14994
- import { defineCommand as defineCommand117 } from "citty";
15315
+ import { defineCommand as defineCommand118 } from "citty";
14995
15316
 
14996
15317
  // src/commands/research/output.ts
14997
15318
  var RESEARCH_DATA_NOTE = "Estimates based on third-party SERP data \u2014 not exact figures. Use for directional insights, not precise measurement.";
@@ -15104,7 +15425,7 @@ var FIELDS3 = {
15104
15425
  etv: "Estimated traffic value (USD)",
15105
15426
  visibility: "SERP visibility score (0-1)"
15106
15427
  };
15107
- var advertisersCommand = defineCommand117({
15428
+ var advertisersCommand = defineCommand118({
15108
15429
  meta: {
15109
15430
  name: "advertisers",
15110
15431
  description: `Find domains competing for a keyword in Google SERPs.
@@ -15151,7 +15472,7 @@ Examples:
15151
15472
  });
15152
15473
 
15153
15474
  // src/commands/research/autocomplete.ts
15154
- import { defineCommand as defineCommand118 } from "citty";
15475
+ import { defineCommand as defineCommand119 } from "citty";
15155
15476
  registerSchema({
15156
15477
  command: "research.autocomplete",
15157
15478
  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 +15495,7 @@ registerSchema({
15174
15495
  var FIELDS4 = {
15175
15496
  suggestion: "Autocomplete suggestion from Google"
15176
15497
  };
15177
- var autocompleteCommand = defineCommand118({
15498
+ var autocompleteCommand = defineCommand119({
15178
15499
  meta: {
15179
15500
  name: "autocomplete",
15180
15501
  description: `Get Google Autocomplete suggestions for keyword expansion.
@@ -15220,7 +15541,7 @@ Examples:
15220
15541
  });
15221
15542
 
15222
15543
  // src/commands/research/countries.ts
15223
- import { defineCommand as defineCommand119 } from "citty";
15544
+ import { defineCommand as defineCommand120 } from "citty";
15224
15545
  registerSchema({
15225
15546
  command: "research.countries",
15226
15547
  description: "List all supported country codes for --location flag in research commands.",
@@ -15277,7 +15598,7 @@ var FIELDS5 = {
15277
15598
  code: "Country code to pass as --location",
15278
15599
  name: "Country name"
15279
15600
  };
15280
- var countriesCommand = defineCommand119({
15601
+ var countriesCommand = defineCommand120({
15281
15602
  meta: {
15282
15603
  name: "countries",
15283
15604
  description: "List all supported country codes for --location flag."
@@ -15288,7 +15609,7 @@ var countriesCommand = defineCommand119({
15288
15609
  });
15289
15610
 
15290
15611
  // src/commands/research/intent.ts
15291
- import { defineCommand as defineCommand120 } from "citty";
15612
+ import { defineCommand as defineCommand121 } from "citty";
15292
15613
  registerSchema({
15293
15614
  command: "research.intent",
15294
15615
  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 +15632,7 @@ var FIELDS6 = {
15311
15632
  intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
15312
15633
  probability: "Confidence score 0.0-1.0"
15313
15634
  };
15314
- var intentCommand = defineCommand120({
15635
+ var intentCommand = defineCommand121({
15315
15636
  meta: {
15316
15637
  name: "intent",
15317
15638
  description: `Classify Google Search intent for keywords. Returns intent type and confidence.
@@ -15359,7 +15680,7 @@ Examples:
15359
15680
  });
15360
15681
 
15361
15682
  // src/commands/research/keyword-gap.ts
15362
- import { defineCommand as defineCommand121 } from "citty";
15683
+ import { defineCommand as defineCommand122 } from "citty";
15363
15684
  registerSchema({
15364
15685
  command: "research.keyword-gap",
15365
15686
  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 +15709,7 @@ var FIELDS7 = {
15388
15709
  cpc: "Cost per click USD",
15389
15710
  their_position: "Competitor's ranking position"
15390
15711
  };
15391
- var keywordGapCommand = defineCommand121({
15712
+ var keywordGapCommand = defineCommand122({
15392
15713
  meta: {
15393
15714
  name: "keyword-gap",
15394
15715
  description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
@@ -15462,7 +15783,7 @@ Examples:
15462
15783
  });
15463
15784
 
15464
15785
  // src/commands/research/keywords-for-site.ts
15465
- import { defineCommand as defineCommand122 } from "citty";
15786
+ import { defineCommand as defineCommand123 } from "citty";
15466
15787
  registerSchema({
15467
15788
  command: "research.keywords-for-site",
15468
15789
  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 +15816,7 @@ var FIELDS8 = {
15495
15816
  competition: "LOW, MEDIUM, or HIGH",
15496
15817
  competition_index: "Competition score 0-100"
15497
15818
  };
15498
- var keywordsForSiteCommand = defineCommand122({
15819
+ var keywordsForSiteCommand = defineCommand123({
15499
15820
  meta: {
15500
15821
  name: "keywords-for-site",
15501
15822
  description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
@@ -15548,7 +15869,7 @@ Examples:
15548
15869
  });
15549
15870
 
15550
15871
  // src/commands/research/languages.ts
15551
- import { defineCommand as defineCommand123 } from "citty";
15872
+ import { defineCommand as defineCommand124 } from "citty";
15552
15873
  registerSchema({
15553
15874
  command: "research.languages",
15554
15875
  description: "List all supported language codes for --language flag in research commands.",
@@ -15578,7 +15899,7 @@ var FIELDS9 = {
15578
15899
  code: "Language code to pass as --language",
15579
15900
  name: "Language name (also accepted by --language)"
15580
15901
  };
15581
- var languagesCommand2 = defineCommand123({
15902
+ var languagesCommand2 = defineCommand124({
15582
15903
  meta: {
15583
15904
  name: "languages",
15584
15905
  description: "List all supported language codes for --language flag."
@@ -15589,7 +15910,7 @@ var languagesCommand2 = defineCommand123({
15589
15910
  });
15590
15911
 
15591
15912
  // src/commands/research/lighthouse.ts
15592
- import { defineCommand as defineCommand124 } from "citty";
15913
+ import { defineCommand as defineCommand125 } from "citty";
15593
15914
  registerSchema({
15594
15915
  command: "research.lighthouse",
15595
15916
  description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
@@ -15608,7 +15929,7 @@ var FIELDS10 = {
15608
15929
  speed_index_ms: "Speed Index in ms (good: < 3400)",
15609
15930
  interactive_ms: "Time to Interactive in ms (good: < 3800)"
15610
15931
  };
15611
- var lighthouseCommand = defineCommand124({
15932
+ var lighthouseCommand = defineCommand125({
15612
15933
  meta: {
15613
15934
  name: "lighthouse",
15614
15935
  description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
@@ -15646,7 +15967,7 @@ Examples:
15646
15967
  });
15647
15968
 
15648
15969
  // src/commands/research/relevant-pages.ts
15649
- import { defineCommand as defineCommand125 } from "citty";
15970
+ import { defineCommand as defineCommand126 } from "citty";
15650
15971
  registerSchema({
15651
15972
  command: "research.relevant-pages",
15652
15973
  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 +15993,7 @@ var FIELDS11 = {
15672
15993
  keywords: "Total organic keywords the page ranks for",
15673
15994
  top_10: "Keywords in positions 1-10"
15674
15995
  };
15675
- var relevantPagesCommand = defineCommand125({
15996
+ var relevantPagesCommand = defineCommand126({
15676
15997
  meta: {
15677
15998
  name: "relevant-pages",
15678
15999
  description: `Get the top pages of a competitor domain with traffic data.
@@ -15718,7 +16039,7 @@ Examples:
15718
16039
  });
15719
16040
 
15720
16041
  // src/commands/research/web.ts
15721
- import { defineCommand as defineCommand126 } from "citty";
16042
+ import { defineCommand as defineCommand127 } from "citty";
15722
16043
  registerSchema({
15723
16044
  command: "research.web",
15724
16045
  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 +16090,7 @@ async function runDeepResearch(question) {
15769
16090
  }
15770
16091
  throw new Error("Deep research timed out");
15771
16092
  }
15772
- var webCommand = defineCommand126({
16093
+ var webCommand = defineCommand127({
15773
16094
  meta: {
15774
16095
  name: "web",
15775
16096
  description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
@@ -15829,7 +16150,7 @@ Examples:
15829
16150
  });
15830
16151
 
15831
16152
  // src/commands/research/index.ts
15832
- var researchCommand = defineCommand127({
16153
+ var researchCommand = defineCommand128({
15833
16154
  meta: {
15834
16155
  name: "research",
15835
16156
  description: `Competitive intelligence and AI-powered research commands.
@@ -15869,10 +16190,10 @@ Examples:
15869
16190
  });
15870
16191
 
15871
16192
  // src/commands/scheduled-actions/index.ts
15872
- import { defineCommand as defineCommand134 } from "citty";
16193
+ import { defineCommand as defineCommand135 } from "citty";
15873
16194
 
15874
16195
  // src/commands/scheduled-actions/create.ts
15875
- import { defineCommand as defineCommand128 } from "citty";
16196
+ import { defineCommand as defineCommand129 } from "citty";
15876
16197
 
15877
16198
  // src/commands/scheduled-actions/shared.ts
15878
16199
  var TEMP_SCHEDULED_ACTION_PREFIX = "temp_sched_";
@@ -15977,7 +16298,7 @@ registerSchema({
15977
16298
  prompt: { type: "string", description: "Additional prompt instructions for the spawned agent", required: false }
15978
16299
  }
15979
16300
  });
15980
- var createCommand2 = defineCommand128({
16301
+ var createCommand2 = defineCommand129({
15981
16302
  meta: {
15982
16303
  name: "create",
15983
16304
  description: 'Stage a scheduled action. Example: baker scheduled-actions create --name "Weekly report" --description "..." --cron "0 9 * * MON"'
@@ -16025,7 +16346,7 @@ var createCommand2 = defineCommand128({
16025
16346
  });
16026
16347
 
16027
16348
  // src/commands/scheduled-actions/delete.ts
16028
- import { defineCommand as defineCommand129 } from "citty";
16349
+ import { defineCommand as defineCommand130 } from "citty";
16029
16350
  registerSchema({
16030
16351
  command: "scheduled-actions.delete",
16031
16352
  description: "Stage deletion of a published scheduled action or cancellation of a temp_sched_* draft creation.",
@@ -16033,7 +16354,7 @@ registerSchema({
16033
16354
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
16034
16355
  }
16035
16356
  });
16036
- var deleteCommand2 = defineCommand129({
16357
+ var deleteCommand2 = defineCommand130({
16037
16358
  meta: {
16038
16359
  name: "delete",
16039
16360
  description: "Stage scheduled action deletion. Example: baker scheduled-actions delete <id-or-temp_sched_id>"
@@ -16062,7 +16383,7 @@ var deleteCommand2 = defineCommand129({
16062
16383
  });
16063
16384
 
16064
16385
  // src/commands/scheduled-actions/get.ts
16065
- import { defineCommand as defineCommand130 } from "citty";
16386
+ import { defineCommand as defineCommand131 } from "citty";
16066
16387
  registerSchema({
16067
16388
  command: "scheduled-actions.get",
16068
16389
  description: "Get a published scheduled action or a temp_sched_* draft-created scheduled action.",
@@ -16070,7 +16391,7 @@ registerSchema({
16070
16391
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
16071
16392
  }
16072
16393
  });
16073
- var getCommand3 = defineCommand130({
16394
+ var getCommand3 = defineCommand131({
16074
16395
  meta: {
16075
16396
  name: "get",
16076
16397
  description: "Get a scheduled action. Example: baker scheduled-actions get <id-or-temp_sched_id>"
@@ -16107,13 +16428,13 @@ var getCommand3 = defineCommand130({
16107
16428
  });
16108
16429
 
16109
16430
  // src/commands/scheduled-actions/list.ts
16110
- import { defineCommand as defineCommand131 } from "citty";
16431
+ import { defineCommand as defineCommand132 } from "citty";
16111
16432
  registerSchema({
16112
16433
  command: "scheduled-actions.list",
16113
16434
  description: "List published scheduled actions. Includes draft state when BAKER_CHAT_ID is set.",
16114
16435
  args: {}
16115
16436
  });
16116
- var listCommand3 = defineCommand131({
16437
+ var listCommand3 = defineCommand132({
16117
16438
  meta: {
16118
16439
  name: "list",
16119
16440
  description: "List scheduled actions. Includes staged draft ops when BAKER_CHAT_ID is set."
@@ -16134,7 +16455,7 @@ var listCommand3 = defineCommand131({
16134
16455
  });
16135
16456
 
16136
16457
  // src/commands/scheduled-actions/trigger.ts
16137
- import { defineCommand as defineCommand132 } from "citty";
16458
+ import { defineCommand as defineCommand133 } from "citty";
16138
16459
  registerSchema({
16139
16460
  command: "scheduled-actions.trigger",
16140
16461
  description: "Immediately trigger a published scheduled action. Does not require BAKER_CHAT_ID and rejects temp_sched_* IDs.",
@@ -16142,7 +16463,7 @@ registerSchema({
16142
16463
  id: { type: "string", description: "Published scheduled action ID", required: true }
16143
16464
  }
16144
16465
  });
16145
- var triggerCommand = defineCommand132({
16466
+ var triggerCommand = defineCommand133({
16146
16467
  meta: {
16147
16468
  name: "trigger",
16148
16469
  description: "Immediately trigger a published scheduled action. Example: baker scheduled-actions trigger <id>"
@@ -16179,7 +16500,7 @@ var triggerCommand = defineCommand132({
16179
16500
  });
16180
16501
 
16181
16502
  // src/commands/scheduled-actions/update.ts
16182
- import { defineCommand as defineCommand133 } from "citty";
16503
+ import { defineCommand as defineCommand134 } from "citty";
16183
16504
  registerSchema({
16184
16505
  command: "scheduled-actions.update",
16185
16506
  description: "Stage an update to a published scheduled action or temp_sched_* draft-created scheduled action.",
@@ -16204,7 +16525,7 @@ registerSchema({
16204
16525
  prompt: { type: "string", description: "Replacement additional spawned-agent instructions", required: false }
16205
16526
  }
16206
16527
  });
16207
- var updateCommand2 = defineCommand133({
16528
+ var updateCommand2 = defineCommand134({
16208
16529
  meta: {
16209
16530
  name: "update",
16210
16531
  description: "Stage a scheduled action update. Example: baker scheduled-actions update <id> --enabled false"
@@ -16274,7 +16595,7 @@ var updateCommand2 = defineCommand133({
16274
16595
  });
16275
16596
 
16276
16597
  // src/commands/scheduled-actions/index.ts
16277
- var scheduledActionsCommand = defineCommand134({
16598
+ var scheduledActionsCommand = defineCommand135({
16278
16599
  meta: {
16279
16600
  name: "scheduled-actions",
16280
16601
  description: `Manage Scheduled Actions. Subcommands: list, get, create, update, delete, trigger.
@@ -16300,8 +16621,8 @@ Examples:
16300
16621
  });
16301
16622
 
16302
16623
  // src/commands/schema.ts
16303
- import { defineCommand as defineCommand135 } from "citty";
16304
- var schemaCommand = defineCommand135({
16624
+ import { defineCommand as defineCommand136 } from "citty";
16625
+ var schemaCommand = defineCommand136({
16305
16626
  meta: {
16306
16627
  name: "schema",
16307
16628
  description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images.search"
@@ -16337,10 +16658,10 @@ var schemaCommand = defineCommand135({
16337
16658
  });
16338
16659
 
16339
16660
  // src/commands/testimonials/index.ts
16340
- import { defineCommand as defineCommand139 } from "citty";
16661
+ import { defineCommand as defineCommand140 } from "citty";
16341
16662
 
16342
16663
  // src/commands/testimonials/get.ts
16343
- import { defineCommand as defineCommand136 } from "citty";
16664
+ import { defineCommand as defineCommand137 } from "citty";
16344
16665
  registerSchema({
16345
16666
  command: "testimonials.get",
16346
16667
  description: "Get a single testimonial by ID",
@@ -16348,7 +16669,7 @@ registerSchema({
16348
16669
  id: { type: "string", description: "Testimonial ID", required: true }
16349
16670
  }
16350
16671
  });
16351
- var getCommand4 = defineCommand136({
16672
+ var getCommand4 = defineCommand137({
16352
16673
  meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
16353
16674
  args: {
16354
16675
  id: { type: "positional", description: "Testimonial ID", required: false },
@@ -16385,7 +16706,7 @@ var getCommand4 = defineCommand136({
16385
16706
  });
16386
16707
 
16387
16708
  // src/commands/testimonials/list.ts
16388
- import { defineCommand as defineCommand137 } from "citty";
16709
+ import { defineCommand as defineCommand138 } from "citty";
16389
16710
  registerSchema({
16390
16711
  command: "testimonials.list",
16391
16712
  description: "List testimonials with optional filters.",
@@ -16415,7 +16736,7 @@ registerSchema({
16415
16736
  limit: { type: "number", description: "Max results (default 50)", required: false, default: 50 }
16416
16737
  }
16417
16738
  });
16418
- var listCommand4 = defineCommand137({
16739
+ var listCommand4 = defineCommand138({
16419
16740
  meta: {
16420
16741
  name: "list",
16421
16742
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
@@ -16464,7 +16785,7 @@ var listCommand4 = defineCommand137({
16464
16785
  });
16465
16786
 
16466
16787
  // src/commands/testimonials/search.ts
16467
- import { defineCommand as defineCommand138 } from "citty";
16788
+ import { defineCommand as defineCommand139 } from "citty";
16468
16789
  registerSchema({
16469
16790
  command: "testimonials.search",
16470
16791
  description: "Search testimonials by text query. Uses hybrid BM25 + vector + reranking.",
@@ -16495,7 +16816,7 @@ registerSchema({
16495
16816
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
16496
16817
  }
16497
16818
  });
16498
- var searchCommand2 = defineCommand138({
16819
+ var searchCommand2 = defineCommand139({
16499
16820
  meta: {
16500
16821
  name: "search",
16501
16822
  description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
@@ -16569,7 +16890,7 @@ var searchCommand2 = defineCommand138({
16569
16890
  var tagsCommand3 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
16570
16891
 
16571
16892
  // src/commands/testimonials/index.ts
16572
- var testimonialsCommand = defineCommand139({
16893
+ var testimonialsCommand = defineCommand140({
16573
16894
  meta: {
16574
16895
  name: "testimonials",
16575
16896
  description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
@@ -16590,10 +16911,10 @@ Examples:
16590
16911
  });
16591
16912
 
16592
16913
  // src/commands/videos/index.ts
16593
- import { defineCommand as defineCommand144 } from "citty";
16914
+ import { defineCommand as defineCommand145 } from "citty";
16594
16915
 
16595
16916
  // src/commands/videos/delete.ts
16596
- import { defineCommand as defineCommand140 } from "citty";
16917
+ import { defineCommand as defineCommand141 } from "citty";
16597
16918
  registerSchema({
16598
16919
  command: "videos.delete",
16599
16920
  description: "Delete a video by ID",
@@ -16607,7 +16928,7 @@ registerSchema({
16607
16928
  }
16608
16929
  }
16609
16930
  });
16610
- var deleteCommand3 = defineCommand140({
16931
+ var deleteCommand3 = defineCommand141({
16611
16932
  meta: {
16612
16933
  name: "delete",
16613
16934
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -16648,7 +16969,7 @@ var deleteCommand3 = defineCommand140({
16648
16969
  });
16649
16970
 
16650
16971
  // src/commands/videos/get.ts
16651
- import { defineCommand as defineCommand141 } from "citty";
16972
+ import { defineCommand as defineCommand142 } from "citty";
16652
16973
  registerSchema({
16653
16974
  command: "videos.get",
16654
16975
  description: "Get a single video by ID",
@@ -16656,7 +16977,7 @@ registerSchema({
16656
16977
  id: { type: "string", description: "Video ID", required: true }
16657
16978
  }
16658
16979
  });
16659
- var getCommand5 = defineCommand141({
16980
+ var getCommand5 = defineCommand142({
16660
16981
  meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
16661
16982
  args: {
16662
16983
  id: { type: "positional", description: "Video ID", required: false },
@@ -16693,7 +17014,7 @@ var getCommand5 = defineCommand141({
16693
17014
  });
16694
17015
 
16695
17016
  // src/commands/videos/search.ts
16696
- import { defineCommand as defineCommand142 } from "citty";
17017
+ import { defineCommand as defineCommand143 } from "citty";
16697
17018
  registerSchema({
16698
17019
  command: "videos.search",
16699
17020
  description: "Search videos by text query. Only returns ready videos.",
@@ -16703,7 +17024,7 @@ registerSchema({
16703
17024
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
16704
17025
  }
16705
17026
  });
16706
- var searchCommand3 = defineCommand142({
17027
+ var searchCommand3 = defineCommand143({
16707
17028
  meta: {
16708
17029
  name: "search",
16709
17030
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -16753,9 +17074,9 @@ var searchCommand3 = defineCommand142({
16753
17074
  var tagsCommand4 = makeTagsCommand("videos", "video", "/api/videos/tags");
16754
17075
 
16755
17076
  // src/commands/videos/upload.ts
16756
- import { readFile as readFile10, stat as stat3 } from "fs/promises";
17077
+ import { readFile as readFile12, stat as stat3 } from "fs/promises";
16757
17078
  import { extname as extname3 } from "path";
16758
- import { defineCommand as defineCommand143 } from "citty";
17079
+ import { defineCommand as defineCommand144 } from "citty";
16759
17080
  var MIME_MAP2 = {
16760
17081
  ".mp4": "video/mp4",
16761
17082
  ".mov": "video/quicktime",
@@ -16789,7 +17110,7 @@ function detectContentType2(filePath) {
16789
17110
  }
16790
17111
  return mime;
16791
17112
  }
16792
- var uploadCommand2 = defineCommand143({
17113
+ var uploadCommand2 = defineCommand144({
16793
17114
  meta: {
16794
17115
  name: "upload",
16795
17116
  description: "Upload a video file to Baker via Mux direct upload. Auto-detects content type. Example: baker videos upload ./demo.mp4"
@@ -16818,7 +17139,7 @@ var uploadCommand2 = defineCommand143({
16818
17139
  return;
16819
17140
  }
16820
17141
  const { uploadUrl, videoId } = await apiPost("/api/videos/upload", {});
16821
- const fileBuffer = await readFile10(filePath);
17142
+ const fileBuffer = await readFile12(filePath);
16822
17143
  const uploadResponse = await fetch(uploadUrl, {
16823
17144
  method: "PUT",
16824
17145
  headers: { "Content-Type": contentType },
@@ -16843,7 +17164,7 @@ var uploadCommand2 = defineCommand143({
16843
17164
  });
16844
17165
 
16845
17166
  // src/commands/videos/index.ts
16846
- var videosCommand = defineCommand144({
17167
+ var videosCommand = defineCommand145({
16847
17168
  meta: {
16848
17169
  name: "videos",
16849
17170
  description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete, tags.
@@ -16866,10 +17187,10 @@ Examples:
16866
17187
  });
16867
17188
 
16868
17189
  // src/commands/winning-ads/index.ts
16869
- import { defineCommand as defineCommand147 } from "citty";
17190
+ import { defineCommand as defineCommand148 } from "citty";
16870
17191
 
16871
17192
  // src/commands/winning-ads/advertisers.ts
16872
- import { defineCommand as defineCommand145 } from "citty";
17193
+ import { defineCommand as defineCommand146 } from "citty";
16873
17194
  registerSchema({
16874
17195
  command: "winning-ads.advertisers",
16875
17196
  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 +17203,7 @@ registerSchema({
16882
17203
  function identity(record) {
16883
17204
  return record;
16884
17205
  }
16885
- var advertisersCommand2 = defineCommand145({
17206
+ var advertisersCommand2 = defineCommand146({
16886
17207
  meta: {
16887
17208
  name: "advertisers",
16888
17209
  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 +17254,7 @@ var advertisersCommand2 = defineCommand145({
16933
17254
  });
16934
17255
 
16935
17256
  // src/commands/winning-ads/search.ts
16936
- import { defineCommand as defineCommand146 } from "citty";
17257
+ import { defineCommand as defineCommand147 } from "citty";
16937
17258
  registerSchema({
16938
17259
  command: "winning-ads.search",
16939
17260
  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 +17362,7 @@ function buildSearchBody(args) {
17041
17362
  }
17042
17363
  return body;
17043
17364
  }
17044
- var searchCommand4 = defineCommand146({
17365
+ var searchCommand4 = defineCommand147({
17045
17366
  meta: {
17046
17367
  name: "search",
17047
17368
  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 +17474,7 @@ var searchCommand4 = defineCommand146({
17153
17474
  });
17154
17475
 
17155
17476
  // src/commands/winning-ads/index.ts
17156
- var winningAdsCommand = defineCommand147({
17477
+ var winningAdsCommand = defineCommand148({
17157
17478
  meta: {
17158
17479
  name: "winning-ads",
17159
17480
  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 +17514,7 @@ function getCliVersion() {
17193
17514
  }
17194
17515
 
17195
17516
  // src/cli.ts
17196
- var main = defineCommand148({
17517
+ var main = defineCommand149({
17197
17518
  meta: {
17198
17519
  name: "baker",
17199
17520
  version: getCliVersion(),