@koda-sl/baker-cli 0.200.0-dev.0a9adf6f7 → 0.200.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -18,7 +18,6 @@ import {
18
18
  SEEDANCE_PROFILE,
19
19
  VIDEO_GENERATE_MODELS,
20
20
  ValidationError,
21
- YtDlpError,
22
21
  clipParamRecipe,
23
22
  clipProfileFor,
24
23
  collectAssetRefLikes,
@@ -42,7 +41,6 @@ import {
42
41
  promptMaxLength,
43
42
  requireCredentialsFromEnv,
44
43
  resolveConcurrency,
45
- runYtDlp,
46
44
  sha256Hex,
47
45
  spineInputFlags,
48
46
  spineInputOps,
@@ -52,7 +50,7 @@ import {
52
50
  toModelSafeImage,
53
51
  ulid,
54
52
  validateCanvasDeep
55
- } from "./chunk-ONFW7HIK.js";
53
+ } from "./chunk-5MPIOGRO.js";
56
54
  import {
57
55
  csvOrJson,
58
56
  daysAgoIso,
@@ -101,7 +99,7 @@ import {
101
99
  } from "./chunk-6NZG2TCM.js";
102
100
 
103
101
  // src/cli.ts
104
- import { defineCommand as defineCommand217, runMain } from "citty";
102
+ import { defineCommand as defineCommand216, runMain } from "citty";
105
103
 
106
104
  // src/commands/actions/index.ts
107
105
  import { defineCommand as defineCommand18 } from "citty";
@@ -5688,7 +5686,6 @@ var testimonialsOutscraperWebhookResponseSchema = z18.object({
5688
5686
  // ../api/src/videos.ts
5689
5687
  import { z as z19 } from "zod";
5690
5688
  var videoStatusSchema = z19.enum(["uploading", "uploaded", "processing", "ready", "error"]);
5691
- var videoSourceSchema = z19.enum(["uploaded", "instagram", "url", "youtube"]);
5692
5689
  var videoTranscriptSegmentSchema = z19.object({
5693
5690
  text: z19.string(),
5694
5691
  startSecond: z19.number(),
@@ -5789,19 +5786,6 @@ var videosUploadRequestSchema = z19.object({
5789
5786
  descriptionContext: z19.string().optional()
5790
5787
  });
5791
5788
  var videosUploadResponseSchema = z19.object({ uploadUrl: z19.string(), videoId: z19.string() });
5792
- var videosIngestRequestSchema = z19.object({
5793
- // Must be a direct media file Mux can pull (mp4/mov/webm/…). A page URL —
5794
- // a YouTube watch link, a TikTok post — is not one: the CLI resolves those to
5795
- // a media file before calling this route.
5796
- url: z19.string().url(),
5797
- source: videoSourceSchema,
5798
- externalId: z19.string().optional(),
5799
- externalUrl: z19.string().optional()
5800
- });
5801
- var videosIngestResponseSchema = z19.object({
5802
- videoId: z19.string(),
5803
- deduped: z19.boolean()
5804
- });
5805
5789
  var videosDeleteRequestSchema = z19.object({ id: z19.string().min(1, "Missing video ID") });
5806
5790
  var videosDeleteResponseSchema = z19.object({ ok: z19.literal(true) });
5807
5791
 
@@ -20680,14 +20664,99 @@ async function probeDuration(filePath) {
20680
20664
  }
20681
20665
 
20682
20666
  // src/commands/canvas/rerun.ts
20683
- import path14 from "path";
20667
+ import path15 from "path";
20684
20668
  import { defineCommand as defineCommand96 } from "citty";
20685
20669
 
20686
20670
  // src/commands/canvas/run.ts
20687
20671
  import { readFile as readFile10 } from "fs/promises";
20688
- import path13 from "path";
20672
+ import path14 from "path";
20689
20673
  import { defineCommand as defineCommand95 } from "citty";
20690
20674
 
20675
+ // src/commands/canvas/normalize-paths.ts
20676
+ import { existsSync as existsSync3, realpathSync } from "fs";
20677
+ import { writeFile as writeFile2 } from "fs/promises";
20678
+ import path4 from "path";
20679
+ function findWorkspaceRoot(startDir, exists = existsSync3, maxDepth = 12) {
20680
+ let dir = path4.resolve(startDir);
20681
+ for (let i = 0; i < maxDepth; i++) {
20682
+ if (exists(path4.join(dir, "package.json"))) return dir;
20683
+ const parent = path4.dirname(dir);
20684
+ if (parent === dir) break;
20685
+ dir = parent;
20686
+ }
20687
+ return null;
20688
+ }
20689
+ function canonicalize(target) {
20690
+ const abs = path4.resolve(target);
20691
+ let dir = abs;
20692
+ for (; ; ) {
20693
+ try {
20694
+ const real = realpathSync(dir);
20695
+ return dir === abs ? real : path4.join(real, path4.relative(dir, abs));
20696
+ } catch {
20697
+ const parent = path4.dirname(dir);
20698
+ if (parent === dir) return abs;
20699
+ dir = parent;
20700
+ }
20701
+ }
20702
+ }
20703
+ function isInside(root, target) {
20704
+ const rel = path4.relative(root, target);
20705
+ return rel !== "" && !rel.startsWith("..") && !path4.isAbsolute(rel);
20706
+ }
20707
+ function toCanvasRelative(canvasDir, target) {
20708
+ return path4.relative(canvasDir, target).split(path4.sep).join("/");
20709
+ }
20710
+ var RUNTIME_WORKSPACE_ROOT = "/home/user/repo";
20711
+ function rewriteTarget(value, workspaceRoot) {
20712
+ if (typeof value !== "string" || value.length === 0) return null;
20713
+ if (value.includes("[TODO") || looksLikeHttpUrl(value) || !path4.isAbsolute(value)) return null;
20714
+ const target = canonicalize(value);
20715
+ if (isInside(workspaceRoot, target)) return target;
20716
+ const fromRuntime = path4.relative(RUNTIME_WORKSPACE_ROOT, path4.normalize(value));
20717
+ if (fromRuntime !== "" && !fromRuntime.startsWith("..") && !path4.isAbsolute(fromRuntime)) {
20718
+ return path4.join(workspaceRoot, fromRuntime);
20719
+ }
20720
+ return null;
20721
+ }
20722
+ function normalizeAbsoluteCanvasPaths(canvas, canvasDir, workspaceRoot) {
20723
+ if (!canvas || typeof canvas !== "object") return { canvas, rewrites: [] };
20724
+ const c = canvas;
20725
+ if (!Array.isArray(c.nodes)) return { canvas, rewrites: [] };
20726
+ const root = canonicalize(workspaceRoot);
20727
+ const dir = canonicalize(canvasDir);
20728
+ const rewrites = [];
20729
+ const nodes = c.nodes.map((node) => {
20730
+ if (!node || typeof node !== "object") return node;
20731
+ const n = node;
20732
+ const params = n.params;
20733
+ if (!params || typeof params !== "object") return node;
20734
+ const nodeId = typeof n.id === "string" ? n.id : "?";
20735
+ const key = n.type === "ingest" && params.source === "path" ? "path" : n.type === "hyperframe_render" ? "composition" : null;
20736
+ if (!key) return node;
20737
+ const target = rewriteTarget(params[key], root);
20738
+ if (target === null) return node;
20739
+ const to = toCanvasRelative(dir, target);
20740
+ rewrites.push({ nodeId, param: key, from: params[key], to });
20741
+ return { ...node, params: { ...params, [key]: to } };
20742
+ });
20743
+ return rewrites.length === 0 ? { canvas, rewrites } : { canvas: { ...canvas, nodes }, rewrites };
20744
+ }
20745
+ async function healAbsoluteCanvasPaths(filePath, canvas) {
20746
+ const canvasDir = path4.dirname(filePath);
20747
+ const workspaceRoot = findWorkspaceRoot(canvasDir);
20748
+ if (!workspaceRoot) return { canvas, rewrites: [], text: null };
20749
+ const normalized = normalizeAbsoluteCanvasPaths(canvas, canvasDir, workspaceRoot);
20750
+ if (normalized.rewrites.length === 0) return { ...normalized, text: null };
20751
+ const text2 = `${JSON.stringify(normalized.canvas, null, 2)}
20752
+ `;
20753
+ await writeFile2(filePath, text2, "utf8");
20754
+ return { ...normalized, text: text2 };
20755
+ }
20756
+ function describeRewrites(rewrites) {
20757
+ return rewrites.map((r) => ` ${r.nodeId}.${r.param}: ${r.from} \u2192 ${r.to}`).join("\n");
20758
+ }
20759
+
20691
20760
  // src/commands/canvas/placeholders.ts
20692
20761
  function unsuppliedPlaceholderAssets(canvas) {
20693
20762
  const nodes = canvas?.nodes;
@@ -20705,7 +20774,7 @@ function unsuppliedPlaceholderAssets(canvas) {
20705
20774
  }
20706
20775
 
20707
20776
  // src/commands/canvas/resolve-paths.ts
20708
- import path4 from "path";
20777
+ import path5 from "path";
20709
20778
  function resolveRelativeCanvasPaths(canvas, baseDir) {
20710
20779
  if (!canvas || typeof canvas !== "object") return canvas;
20711
20780
  const c = canvas;
@@ -20718,24 +20787,24 @@ function resolveNode(node, baseDir) {
20718
20787
  const params = n.params;
20719
20788
  if (!params || typeof params !== "object") return node;
20720
20789
  if (n.type === "ingest" && params.source === "path" && isResolvableRelative(params.path)) {
20721
- return { ...node, params: { ...params, path: path4.resolve(baseDir, params.path) } };
20790
+ return { ...node, params: { ...params, path: path5.resolve(baseDir, params.path) } };
20722
20791
  }
20723
20792
  if (n.type === "hyperframe_render" && isResolvableRelative(params.composition)) {
20724
- return { ...node, params: { ...params, composition: path4.resolve(baseDir, params.composition) } };
20793
+ return { ...node, params: { ...params, composition: path5.resolve(baseDir, params.composition) } };
20725
20794
  }
20726
20795
  return node;
20727
20796
  }
20728
20797
  function isResolvableRelative(value) {
20729
- return typeof value === "string" && value.length > 0 && !value.includes("[TODO") && !looksLikeHttpUrl(value) && !path4.isAbsolute(value);
20798
+ return typeof value === "string" && value.length > 0 && !value.includes("[TODO") && !looksLikeHttpUrl(value) && !path5.isAbsolute(value);
20730
20799
  }
20731
20800
 
20732
20801
  // src/commands/canvas/source-version.ts
20733
20802
  import { readFile as readFile5 } from "fs/promises";
20734
- import path6 from "path";
20803
+ import path7 from "path";
20735
20804
 
20736
20805
  // src/commands/canvas/scene-files.ts
20737
- import { mkdir as mkdir2, readFile as readFile4, readdir as readdir2, rm, writeFile as writeFile2 } from "fs/promises";
20738
- import path5 from "path";
20806
+ import { mkdir as mkdir2, readFile as readFile4, readdir as readdir2, rm, writeFile as writeFile3 } from "fs/promises";
20807
+ import path6 from "path";
20739
20808
  var SCENES_DIR = "scenes";
20740
20809
  var GLOBAL_PROMPT_FILE = "prompt.json";
20741
20810
  var REBUILD_FILE = "prompt.rebuild.json";
@@ -20752,19 +20821,19 @@ function splitBlueprint(blueprint) {
20752
20821
  }
20753
20822
  async function writeSceneFiles(outDir, blueprint) {
20754
20823
  const { global, scenes } = splitBlueprint(blueprint);
20755
- await writeFile2(path5.join(outDir, GLOBAL_PROMPT_FILE), `${JSON.stringify(global, null, 2)}
20824
+ await writeFile3(path6.join(outDir, GLOBAL_PROMPT_FILE), `${JSON.stringify(global, null, 2)}
20756
20825
  `, "utf8");
20757
- const scenesDir = path5.join(outDir, SCENES_DIR);
20826
+ const scenesDir = path6.join(outDir, SCENES_DIR);
20758
20827
  await mkdir2(scenesDir, { recursive: true });
20759
20828
  const written = /* @__PURE__ */ new Set();
20760
20829
  for (let i = 0; i < scenes.length; i++) {
20761
20830
  const name = sceneFileName(i, scenes.length);
20762
20831
  written.add(name);
20763
- await writeFile2(path5.join(scenesDir, name), `${JSON.stringify(scenes[i], null, 2)}
20832
+ await writeFile3(path6.join(scenesDir, name), `${JSON.stringify(scenes[i], null, 2)}
20764
20833
  `, "utf8");
20765
20834
  }
20766
20835
  for (const name of await listSceneFileNames(scenesDir)) {
20767
- if (!written.has(name)) await rm(path5.join(scenesDir, name), { force: true });
20836
+ if (!written.has(name)) await rm(path6.join(scenesDir, name), { force: true });
20768
20837
  }
20769
20838
  }
20770
20839
  async function listSceneFileNames(scenesDir) {
@@ -20777,13 +20846,13 @@ async function listSceneFileNames(scenesDir) {
20777
20846
  return entries.filter((n) => /^s\d+\.json$/.test(n)).sort(bySceneIndex);
20778
20847
  }
20779
20848
  async function listSceneFiles(creativeDir) {
20780
- const scenesDir = path5.join(creativeDir, SCENES_DIR);
20781
- return (await listSceneFileNames(scenesDir)).map((n) => path5.join(scenesDir, n));
20849
+ const scenesDir = path6.join(creativeDir, SCENES_DIR);
20850
+ return (await listSceneFileNames(scenesDir)).map((n) => path6.join(scenesDir, n));
20782
20851
  }
20783
20852
  async function reassembleBlueprint(creativeDir) {
20784
20853
  const files = await listSceneFiles(creativeDir);
20785
20854
  if (files.length === 0) return null;
20786
- const globalRaw = await readFile4(path5.join(creativeDir, GLOBAL_PROMPT_FILE), "utf8");
20855
+ const globalRaw = await readFile4(path6.join(creativeDir, GLOBAL_PROMPT_FILE), "utf8");
20787
20856
  const global = JSON.parse(globalRaw);
20788
20857
  const scenes = [];
20789
20858
  for (const file of files) {
@@ -20805,8 +20874,8 @@ async function computeSourceSha(canvasPath) {
20805
20874
  } catch {
20806
20875
  return void 0;
20807
20876
  }
20808
- const canvasDir = path6.dirname(canvasPath);
20809
- const promptPath = path6.join(canvasDir, "prompt.json");
20877
+ const canvasDir = path7.dirname(canvasPath);
20878
+ const promptPath = path7.join(canvasDir, "prompt.json");
20810
20879
  let promptBytes;
20811
20880
  try {
20812
20881
  promptBytes = await readFile5(promptPath);
@@ -20833,7 +20902,7 @@ async function computeSourceSha(canvasPath) {
20833
20902
 
20834
20903
  // src/commands/canvas/scene-projection.ts
20835
20904
  import { readFile as readFile6 } from "fs/promises";
20836
- import path7 from "path";
20905
+ import path8 from "path";
20837
20906
 
20838
20907
  // src/engine/scaffold/video.ts
20839
20908
  import { toCardinal as nwAr } from "n2words/ar-SA";
@@ -24119,12 +24188,12 @@ function nonPromptParamsDiverge(live = {}, rebuilt = {}) {
24119
24188
  return false;
24120
24189
  }
24121
24190
  async function syncSceneNodeParams(canvas, canvasPath, log) {
24122
- const creativeDir = path7.dirname(canvasPath);
24191
+ const creativeDir = path8.dirname(canvasPath);
24123
24192
  const blueprint = await reassembleBlueprint(creativeDir);
24124
24193
  if (!blueprint) return "not_applicable";
24125
24194
  let rebuildRaw;
24126
24195
  try {
24127
- rebuildRaw = await readFile6(path7.join(creativeDir, REBUILD_FILE), "utf8");
24196
+ rebuildRaw = await readFile6(path8.join(creativeDir, REBUILD_FILE), "utf8");
24128
24197
  } catch {
24129
24198
  return "not_applicable";
24130
24199
  }
@@ -24165,7 +24234,7 @@ async function syncSceneNodeParams(canvas, canvasPath, log) {
24165
24234
  }
24166
24235
 
24167
24236
  // src/commands/canvas/style-projection.ts
24168
- import { readFile as readFile7, writeFile as writeFile3 } from "fs/promises";
24237
+ import { readFile as readFile7, writeFile as writeFile4 } from "fs/promises";
24169
24238
  function findBlueprintProjection(canvas) {
24170
24239
  if (!canvas || typeof canvas !== "object") return null;
24171
24240
  const nodes = canvas.nodes;
@@ -24196,7 +24265,7 @@ async function syncStyleProjection(canvas, log) {
24196
24265
  const rendered = renderStyleProjection(await readFile7(pair.promptPath, "utf8"));
24197
24266
  const current = await readFile7(pair.stylePath, "utf8").catch(() => null);
24198
24267
  if (current === rendered) return "up_to_date";
24199
- await writeFile3(pair.stylePath, rendered, "utf8");
24268
+ await writeFile4(pair.stylePath, rendered, "utf8");
24200
24269
  log(
24201
24270
  "[style] prompt.style.json regenerated from prompt.json \u2014 every frame's shared ad spec changed; affected image frames will re-bill on the next run"
24202
24271
  );
@@ -24256,13 +24325,13 @@ ${body}` : header || body || compactJson(record);
24256
24325
  }
24257
24326
 
24258
24327
  // src/commands/canvas/run-record.ts
24259
- import path8 from "path";
24328
+ import path9 from "path";
24260
24329
  var MAX_RUN_NODES = 200;
24261
24330
  var MAX_OUTPUTS_PER_NODE = 10;
24262
24331
  var MAX_FINAL_OUTPUTS = 10;
24263
24332
  var MAX_CREATIVE_SLUG_LENGTH = 100;
24264
24333
  function creativeSlugFromCanvasPath(filePath) {
24265
- const normalized = filePath.split(path8.sep).join("/");
24334
+ const normalized = filePath.split(path9.sep).join("/");
24266
24335
  const match = normalized.match(/(?:^|\/)src\/creatives\/([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\//);
24267
24336
  const slug = match?.[1] ?? null;
24268
24337
  return slug && slug.length <= MAX_CREATIVE_SLUG_LENGTH ? slug : null;
@@ -24550,7 +24619,7 @@ var RunRecordPoster = class {
24550
24619
 
24551
24620
  // src/commands/canvas/run-retention.ts
24552
24621
  import { rm as rm2 } from "fs/promises";
24553
- import path9 from "path";
24622
+ import path10 from "path";
24554
24623
  function runDirsToPrune(entries, keep, currentRunId) {
24555
24624
  const runs = entries.filter((e) => /^r_[0-9A-Za-z]+$/.test(e) && e !== currentRunId).sort();
24556
24625
  if (keep <= 0) return runs;
@@ -24567,7 +24636,7 @@ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
24567
24636
  const toPrune = runDirsToPrune(entries, keep, currentRunId);
24568
24637
  if (toPrune.length === 0) return;
24569
24638
  for (const dir of toPrune) {
24570
- await rm2(path9.join(outputsDir, dir), { recursive: true, force: true }).catch(
24639
+ await rm2(path10.join(outputsDir, dir), { recursive: true, force: true }).catch(
24571
24640
  (e) => log(`[prune ] could not remove ${dir}: ${e.message}`)
24572
24641
  );
24573
24642
  }
@@ -24575,13 +24644,13 @@ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
24575
24644
  }
24576
24645
 
24577
24646
  // src/commands/canvas/dirty-marker.ts
24578
- import { mkdir as mkdir3, readdir as readdir3, rm as rm3, writeFile as writeFile4 } from "fs/promises";
24579
- import path10 from "path";
24647
+ import { mkdir as mkdir3, readdir as readdir3, rm as rm3, writeFile as writeFile5 } from "fs/promises";
24648
+ import path11 from "path";
24580
24649
  function creativeDirtyDir(base) {
24581
- return base ?? path10.resolve("canvas", ".dirty");
24650
+ return base ?? path11.resolve("canvas", ".dirty");
24582
24651
  }
24583
24652
  function dirtyMarkerFile(slug, base) {
24584
- return path10.join(creativeDirtyDir(base), `${slug}.json`);
24653
+ return path11.join(creativeDirtyDir(base), `${slug}.json`);
24585
24654
  }
24586
24655
  async function clearCreativeDirty(slug, base) {
24587
24656
  try {
@@ -24591,18 +24660,18 @@ async function clearCreativeDirty(slug, base) {
24591
24660
  }
24592
24661
 
24593
24662
  // src/commands/canvas/run-resume.ts
24594
- import { mkdir as mkdir4, readFile as readFile8, rm as rm4, writeFile as writeFile5 } from "fs/promises";
24595
- import path11 from "path";
24663
+ import { mkdir as mkdir4, readFile as readFile8, rm as rm4, writeFile as writeFile6 } from "fs/promises";
24664
+ import path12 from "path";
24596
24665
  function markerKey(canvasPath) {
24597
24666
  const slug = creativeSlugFromCanvasPath(canvasPath);
24598
- const identity = slug ?? path11.relative(process.cwd(), path11.resolve(canvasPath));
24667
+ const identity = slug ?? path12.relative(process.cwd(), path12.resolve(canvasPath));
24599
24668
  return sha256Hex(Buffer.from(identity)).slice(0, 32);
24600
24669
  }
24601
24670
  function legacyMarkerKey(canvasPath) {
24602
- return sha256Hex(Buffer.from(path11.resolve(canvasPath))).slice(0, 32);
24671
+ return sha256Hex(Buffer.from(path12.resolve(canvasPath))).slice(0, 32);
24603
24672
  }
24604
24673
  function markerFile(outputsDir, key) {
24605
- return path11.join(outputsDir, ".inflight", `${key}.json`);
24674
+ return path12.join(outputsDir, ".inflight", `${key}.json`);
24606
24675
  }
24607
24676
  var REMOTE_ADOPT_STALE_MS = 12e4;
24608
24677
  function classifyRemoteRun(run, now) {
@@ -24649,8 +24718,8 @@ async function readMarkerRunId(outputsDir, canvasPath) {
24649
24718
  async function markRunInFlight(outputsDir, canvasPath, runId) {
24650
24719
  try {
24651
24720
  const file = markerFile(outputsDir, markerKey(canvasPath));
24652
- await mkdir4(path11.dirname(file), { recursive: true });
24653
- await writeFile5(file, JSON.stringify({ runId, canvasPath: path11.resolve(canvasPath), startedAt: Date.now() }));
24721
+ await mkdir4(path12.dirname(file), { recursive: true });
24722
+ await writeFile6(file, JSON.stringify({ runId, canvasPath: path12.resolve(canvasPath), startedAt: Date.now() }));
24654
24723
  } catch {
24655
24724
  }
24656
24725
  }
@@ -24664,8 +24733,8 @@ async function clearRunMarker(outputsDir, canvasPath) {
24664
24733
  }
24665
24734
 
24666
24735
  // src/commands/canvas/run-snapshot.ts
24667
- import { mkdir as mkdir5, readdir as readdir4, readFile as readFile9, stat as stat2, writeFile as writeFile6 } from "fs/promises";
24668
- import path12 from "path";
24736
+ import { mkdir as mkdir5, readdir as readdir4, readFile as readFile9, stat as stat2, writeFile as writeFile7 } from "fs/promises";
24737
+ import path13 from "path";
24669
24738
  var SNAPSHOT_SCHEMA = "baker-canvas-snapshot/1";
24670
24739
  var MAX_SNAPSHOT_FILE_BYTES = 32 * 1024 * 1024;
24671
24740
  var EXT_TO_MIME = {
@@ -24692,15 +24761,15 @@ var EXT_TO_MIME = {
24692
24761
  woff2: "font/woff2"
24693
24762
  };
24694
24763
  function mimeForFile(filePath) {
24695
- const ext = path12.extname(filePath).slice(1).toLowerCase();
24764
+ const ext = path13.extname(filePath).slice(1).toLowerCase();
24696
24765
  return EXT_TO_MIME[ext] ?? "application/octet-stream";
24697
24766
  }
24698
24767
  function toPosix(p) {
24699
- return p.split(path12.sep).join("/");
24768
+ return p.split(path13.sep).join("/");
24700
24769
  }
24701
- function isInside(dir, target) {
24702
- const rel = path12.relative(dir, target);
24703
- return !rel.startsWith("..") && !path12.isAbsolute(rel);
24770
+ function isInside2(dir, target) {
24771
+ const rel = path13.relative(dir, target);
24772
+ return !rel.startsWith("..") && !path13.isAbsolute(rel);
24704
24773
  }
24705
24774
  function localPathRefsFromCanvas(parsed) {
24706
24775
  const nodes = parsed?.nodes;
@@ -24725,24 +24794,24 @@ function isSnapshotablePath(value) {
24725
24794
  async function sourceRefsToSnapshot(canvasDir, parsed) {
24726
24795
  const refs = new Set(localPathRefsFromCanvas(parsed));
24727
24796
  for (const sceneFile of await listSceneFiles(canvasDir)) {
24728
- refs.add(toPosix(path12.relative(canvasDir, sceneFile)));
24797
+ refs.add(toPosix(path13.relative(canvasDir, sceneFile)));
24729
24798
  }
24730
24799
  refs.add(REBUILD_FILE);
24731
24800
  return [...refs];
24732
24801
  }
24733
24802
  async function uploadRunSnapshot(client, opts) {
24734
24803
  try {
24735
- const canvasDir = path12.dirname(opts.canvasPath);
24804
+ const canvasDir = path13.dirname(opts.canvasPath);
24736
24805
  const put = (bytes, mime) => putContentAddressed(client, bytes, mime, opts.signal);
24737
24806
  const canvasBytes = Buffer.from(opts.raw);
24738
24807
  const canvasUpload = await put(canvasBytes, "application/json");
24739
24808
  const files = [];
24740
24809
  const skipped = [];
24741
24810
  for (const refPath of await sourceRefsToSnapshot(canvasDir, opts.parsed)) {
24742
- const abs = path12.isAbsolute(refPath) ? refPath : path12.resolve(canvasDir, refPath);
24743
- if (!isInside(canvasDir, abs)) {
24811
+ const abs = path13.isAbsolute(refPath) ? refPath : path13.resolve(canvasDir, refPath);
24812
+ if (!isInside2(canvasDir, abs)) {
24744
24813
  skipped.push({
24745
- path: toPosix(path12.relative(canvasDir, abs)),
24814
+ path: toPosix(path13.relative(canvasDir, abs)),
24746
24815
  reason: "outside the creative folder \u2014 read from the workspace on rerun"
24747
24816
  });
24748
24817
  continue;
@@ -24751,12 +24820,12 @@ async function uploadRunSnapshot(client, opts) {
24751
24820
  try {
24752
24821
  st = await stat2(abs);
24753
24822
  } catch {
24754
- skipped.push({ path: toPosix(path12.relative(canvasDir, abs)), reason: "missing" });
24823
+ skipped.push({ path: toPosix(path13.relative(canvasDir, abs)), reason: "missing" });
24755
24824
  continue;
24756
24825
  }
24757
24826
  const fileList = st.isDirectory() ? await listFilesRecursive(abs) : [abs];
24758
24827
  for (const file of fileList) {
24759
- const rel = toPosix(path12.relative(canvasDir, file));
24828
+ const rel = toPosix(path13.relative(canvasDir, file));
24760
24829
  const size = (await stat2(file)).size;
24761
24830
  if (size > MAX_SNAPSHOT_FILE_BYTES) {
24762
24831
  skipped.push({ path: rel, reason: `too large (${size} bytes)` });
@@ -24771,7 +24840,7 @@ async function uploadRunSnapshot(client, opts) {
24771
24840
  schema: SNAPSHOT_SCHEMA,
24772
24841
  creativeSlug: opts.creativeSlug,
24773
24842
  canvasSha: canvasUpload.sha256,
24774
- canvas: { path: path12.basename(opts.canvasPath), sha256: canvasUpload.sha256, url: canvasUpload.url },
24843
+ canvas: { path: path13.basename(opts.canvasPath), sha256: canvasUpload.sha256, url: canvasUpload.url },
24775
24844
  files,
24776
24845
  skipped: skipped.length > 0 ? skipped : void 0
24777
24846
  };
@@ -24798,7 +24867,7 @@ async function putContentAddressed(client, bytes, mime, signal) {
24798
24867
  }
24799
24868
  async function listFilesRecursive(dir) {
24800
24869
  const entries = await readdir4(dir, { recursive: true, withFileTypes: true });
24801
- return entries.filter((d) => d.isFile()).map((d) => path12.join(d.parentPath, d.name));
24870
+ return entries.filter((d) => d.isFile()).map((d) => path13.join(d.parentPath, d.name));
24802
24871
  }
24803
24872
  var SnapshotConflictError = class extends Error {
24804
24873
  conflicts;
@@ -24810,16 +24879,16 @@ var SnapshotConflictError = class extends Error {
24810
24879
  };
24811
24880
  async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
24812
24881
  const entries = [manifest.canvas, ...manifest.files];
24813
- const resolvedTarget = path12.resolve(targetDir);
24882
+ const resolvedTarget = path13.resolve(targetDir);
24814
24883
  const planned = [];
24815
24884
  const conflicts = [];
24816
24885
  const upToDate = [];
24817
24886
  for (const entry of entries) {
24818
- if (path12.isAbsolute(entry.path) || entry.path.split("/").includes("..")) {
24887
+ if (path13.isAbsolute(entry.path) || entry.path.split("/").includes("..")) {
24819
24888
  throw new Error(`snapshot entry escapes the creative directory: ${entry.path}`);
24820
24889
  }
24821
- const target = path12.resolve(resolvedTarget, entry.path);
24822
- if (target !== resolvedTarget && !target.startsWith(resolvedTarget + path12.sep)) {
24890
+ const target = path13.resolve(resolvedTarget, entry.path);
24891
+ if (target !== resolvedTarget && !target.startsWith(resolvedTarget + path13.sep)) {
24823
24892
  throw new Error(`snapshot entry escapes the creative directory: ${entry.path}`);
24824
24893
  }
24825
24894
  const existing = await readFile9(target).catch(() => null);
@@ -24844,11 +24913,11 @@ async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
24844
24913
  if (sha256Hex(bytes) !== entry.sha256) {
24845
24914
  throw new Error(`snapshot download for ${entry.path} does not match its recorded sha256`);
24846
24915
  }
24847
- await mkdir5(path12.dirname(target), { recursive: true });
24848
- await writeFile6(target, bytes);
24916
+ await mkdir5(path13.dirname(target), { recursive: true });
24917
+ await writeFile7(target, bytes);
24849
24918
  restored.push(entry.path);
24850
24919
  }
24851
- return { canvasPath: path12.resolve(resolvedTarget, manifest.canvas.path), restored, upToDate };
24920
+ return { canvasPath: path13.resolve(resolvedTarget, manifest.canvas.path), restored, upToDate };
24852
24921
  }
24853
24922
 
24854
24923
  // src/commands/canvas/run.ts
@@ -24928,7 +24997,7 @@ function resolveMaxCredits(...candidates) {
24928
24997
  return void 0;
24929
24998
  }
24930
24999
  async function executeCanvasRun(opts) {
24931
- const filePath = path13.resolve(opts.file);
25000
+ const filePath = path14.resolve(opts.file);
24932
25001
  const raw = await readFile10(filePath, "utf8");
24933
25002
  let parsed;
24934
25003
  try {
@@ -24941,7 +25010,17 @@ async function executeCanvasRun(opts) {
24941
25010
  }
24942
25011
  const attemptedSlug = creativeSlugFromCanvasPath(filePath);
24943
25012
  if (attemptedSlug) await clearCreativeDirty(attemptedSlug);
24944
- parsed = resolveRelativeCanvasPaths(parsed, path13.dirname(filePath));
25013
+ const healed = await healAbsoluteCanvasPaths(filePath, parsed);
25014
+ parsed = healed.canvas;
25015
+ const canvasText = healed.text ?? raw;
25016
+ if (healed.rewrites.length > 0) {
25017
+ process.stderr.write(
25018
+ `[canvas] rewrote ${healed.rewrites.length} absolute asset path(s) to canvas-relative \u2014 they resolve only in this workspace:
25019
+ ${describeRewrites(healed.rewrites)}
25020
+ `
25021
+ );
25022
+ }
25023
+ parsed = resolveRelativeCanvasPaths(parsed, path14.dirname(filePath));
24945
25024
  try {
24946
25025
  await syncStyleProjection(parsed, (line) => process.stderr.write(`${line}
24947
25026
  `));
@@ -25044,10 +25123,10 @@ async function executeCanvasRun(opts) {
25044
25123
  process.stdout.write(`[warn] ${w.code}: ${w.message}
25045
25124
  `);
25046
25125
  }
25047
- const canvasSha = sha256Hex(Buffer.from(raw));
25126
+ const canvasSha = sha256Hex(Buffer.from(canvasText));
25048
25127
  const creativeSlug = creativeSlugFromCanvasPath(filePath) ?? void 0;
25049
25128
  const client = opts.record === false ? null : buildBackendClient();
25050
- const outputsDir = opts.outputsDir ? path13.resolve(opts.outputsDir) : path13.resolve("canvas");
25129
+ const outputsDir = opts.outputsDir ? path14.resolve(opts.outputsDir) : path14.resolve("canvas");
25051
25130
  const { runId, resumed, source, concurrentRunId } = await resolveRunId({
25052
25131
  explicitRunId: opts.runId,
25053
25132
  fresh: opts.fresh === true,
@@ -25078,10 +25157,10 @@ async function executeCanvasRun(opts) {
25078
25157
  });
25079
25158
  }
25080
25159
  await markRunInFlight(outputsDir, filePath, runId);
25081
- const canvasSnapshotUrl = client && creativeSlug ? await uploadRunSnapshot(client, { canvasPath: filePath, raw, creativeSlug, parsed }) ?? void 0 : void 0;
25160
+ const canvasSnapshotUrl = client && creativeSlug ? await uploadRunSnapshot(client, { canvasPath: filePath, raw: canvasText, creativeSlug, parsed }) ?? void 0 : void 0;
25082
25161
  const recordMeta = {
25083
25162
  creativeSlug,
25084
- canvasPath: path13.relative(process.cwd(), filePath) || void 0,
25163
+ canvasPath: path14.relative(process.cwd(), filePath) || void 0,
25085
25164
  canvasSha,
25086
25165
  // The fingerprint the dashboard compares against the current source to flag
25087
25166
  // "edited since last render". Computed from the on-disk canvas.json +
@@ -25268,7 +25347,7 @@ var rerunCommand = defineCommand96({
25268
25347
  if (latest.canvasSha && manifest.canvasSha !== latest.canvasSha) {
25269
25348
  fail2("snapshot_mismatch", `snapshot manifest for run ${latest.runId} does not match its recorded canvas sha`);
25270
25349
  }
25271
- const targetDir = path14.resolve("src", "creatives", slug);
25350
+ const targetDir = path15.resolve("src", "creatives", slug);
25272
25351
  let restoredCanvasPath;
25273
25352
  try {
25274
25353
  const restore = await restoreRunSnapshot(manifest, targetDir, { force: args["force-remote"] === true });
@@ -25317,8 +25396,8 @@ async function fetchManifest(url) {
25317
25396
  }
25318
25397
 
25319
25398
  // src/commands/canvas/scaffold-static-ad.ts
25320
- import { access, mkdir as mkdir6, readFile as readFile12, writeFile as writeFile7 } from "fs/promises";
25321
- import path18 from "path";
25399
+ import { access, mkdir as mkdir6, readFile as readFile12, writeFile as writeFile8 } from "fs/promises";
25400
+ import path19 from "path";
25322
25401
  import { defineCommand as defineCommand98 } from "citty";
25323
25402
 
25324
25403
  // src/engine/scaffold/staticAd.ts
@@ -25600,7 +25679,7 @@ function staticAdReport(input, elementsInput, opts) {
25600
25679
  }
25601
25680
 
25602
25681
  // src/commands/canvas/creative-definition.ts
25603
- import path15 from "path";
25682
+ import path16 from "path";
25604
25683
  var PLATFORM_VALUES = ["meta", "google", "linkedin", "tiktok", "youtube", "x", "other"];
25605
25684
  var FORMAT_VALUES = ["1:1", "4:5", "9:16", "16:9", "1.91:1"];
25606
25685
  function titleFromSlug(slug) {
@@ -25648,16 +25727,16 @@ function buildCreativeDefinition(input) {
25648
25727
  }
25649
25728
 
25650
25729
  // src/commands/canvas/scaffold-static-ad-paths.ts
25651
- import path16 from "path";
25730
+ import path17 from "path";
25652
25731
  function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd(), slug) {
25653
25732
  const file = rawFile.trim();
25654
25733
  const imageIsUrl = /^https?:\/\//i.test(file);
25655
- const imageSource = imageIsUrl ? file : path16.resolve(cwd, file);
25656
- const outPath = out ? path16.resolve(cwd, out) : slug ? path16.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path16.join(cwd, "static-ad.canvas.json") : path16.join(path16.dirname(imageSource), "static-ad.canvas.json");
25657
- const blueprintPath = path16.join(path16.dirname(outPath), "prompt.json");
25658
- const creativeDir = slug ? path16.dirname(outPath) : null;
25659
- const definitionPath = creativeDir ? path16.join(creativeDir, "_definition.md") : null;
25660
- const referencesDir = creativeDir ? path16.join(creativeDir, "references") : null;
25734
+ const imageSource = imageIsUrl ? file : path17.resolve(cwd, file);
25735
+ const outPath = out ? path17.resolve(cwd, out) : slug ? path17.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path17.join(cwd, "static-ad.canvas.json") : path17.join(path17.dirname(imageSource), "static-ad.canvas.json");
25736
+ const blueprintPath = path17.join(path17.dirname(outPath), "prompt.json");
25737
+ const creativeDir = slug ? path17.dirname(outPath) : null;
25738
+ const definitionPath = creativeDir ? path17.join(creativeDir, "_definition.md") : null;
25739
+ const referencesDir = creativeDir ? path17.join(creativeDir, "references") : null;
25661
25740
  return { imageIsUrl, imageSource, outPath, blueprintPath, creativeDir, definitionPath, referencesDir };
25662
25741
  }
25663
25742
  var SCAFFOLD_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
@@ -25668,7 +25747,7 @@ function isValidScaffoldSlug(slug) {
25668
25747
 
25669
25748
  // src/commands/canvas/sync-definition.ts
25670
25749
  import { readdir as readdir5, readFile as readFile11, stat as stat3 } from "fs/promises";
25671
- import path17 from "path";
25750
+ import path18 from "path";
25672
25751
  import { defineCommand as defineCommand97 } from "citty";
25673
25752
 
25674
25753
  // src/commands/canvas/definition-graph.ts
@@ -25761,15 +25840,15 @@ async function syncCreativeDefinitionBestEffort(input) {
25761
25840
  }
25762
25841
  }
25763
25842
  async function resolveCanvasPath(inputPath) {
25764
- const resolved = path17.resolve(inputPath);
25843
+ const resolved = path18.resolve(inputPath);
25765
25844
  let dir = resolved;
25766
25845
  try {
25767
25846
  if ((await stat3(resolved)).isFile()) {
25768
25847
  if (resolved.endsWith(".canvas.json")) return resolved;
25769
- dir = path17.dirname(resolved);
25848
+ dir = path18.dirname(resolved);
25770
25849
  }
25771
25850
  } catch {
25772
- dir = resolved.endsWith(".canvas.json") ? path17.dirname(resolved) : resolved;
25851
+ dir = resolved.endsWith(".canvas.json") ? path18.dirname(resolved) : resolved;
25773
25852
  }
25774
25853
  let entries;
25775
25854
  try {
@@ -25780,7 +25859,7 @@ async function resolveCanvasPath(inputPath) {
25780
25859
  const canvases = entries.filter((name) => name.endsWith(".canvas.json"));
25781
25860
  const slug = creativeSlugFromCanvasPath(`${dir}/x/`);
25782
25861
  const chosen = (slug ? canvases.find((name) => name === `${slug}.canvas.json`) : void 0) ?? canvases[0];
25783
- return chosen ? path17.join(dir, chosen) : null;
25862
+ return chosen ? path18.join(dir, chosen) : null;
25784
25863
  }
25785
25864
  var syncDefinitionCommand = defineCommand97({
25786
25865
  meta: {
@@ -26061,7 +26140,7 @@ var scaffoldStaticAdCommand = defineCommand98({
26061
26140
  process.cwd(),
26062
26141
  slug
26063
26142
  );
26064
- await mkdir6(path18.dirname(outPath), { recursive: true });
26143
+ await mkdir6(path19.dirname(outPath), { recursive: true });
26065
26144
  const { describeModel, selectModel, layoutModel, genModel } = resolveModels(args);
26066
26145
  let durableSourceUrl;
26067
26146
  if (referencesDir) {
@@ -26087,7 +26166,7 @@ var scaffoldStaticAdCommand = defineCommand98({
26087
26166
  if (layout && annotated && typeof annotated === "object") {
26088
26167
  annotated.layout = layout;
26089
26168
  }
26090
- await writeFile7(blueprintPath, `${JSON.stringify(annotated, null, 2)}
26169
+ await writeFile8(blueprintPath, `${JSON.stringify(annotated, null, 2)}
26091
26170
  `, "utf8");
26092
26171
  let canvasImagePath = imageSource;
26093
26172
  let canvasImageIsUrl = imageIsUrl;
@@ -26132,10 +26211,10 @@ var scaffoldStaticAdCommand = defineCommand98({
26132
26211
  );
26133
26212
  process.exit(2);
26134
26213
  }
26135
- await writeFile7(outPath, `${JSON.stringify(canvas, null, 2)}
26214
+ await writeFile8(outPath, `${JSON.stringify(canvas, null, 2)}
26136
26215
  `, "utf8");
26137
26216
  if (definitionPath && !await fileExists(definitionPath)) {
26138
- await writeFile7(
26217
+ await writeFile8(
26139
26218
  definitionPath,
26140
26219
  buildCreativeDefinition({
26141
26220
  title: args.title ? String(args.title) : titleFromSlug(slug ?? ""),
@@ -26181,7 +26260,7 @@ var scaffoldStaticAdCommand = defineCommand98({
26181
26260
  run_estimated_credits: validation.estimatedCredits
26182
26261
  },
26183
26262
  checklist: {
26184
- edit_prompt: `Edit ${path18.basename(blueprintPath)} \u2014 it is the blueprint generated from your image; rewrite it into the ad you want (palette, copy, claims, subjects). It feeds the generator directly.`,
26263
+ edit_prompt: `Edit ${path19.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.`,
26185
26264
  assets_to_supply: report.elements,
26186
26265
  font_slot: report.includes_font ? "Drop a brand font at the [TODO] brandfont path (the describe pass recorded the ad's typefaces under `fonts` in prompt.json \u2014 match those). The font is wired into the render as a TYPE SPECIMEN reference so generated text takes the brand letterforms. Delete the brandfont + type_ref nodes to skip it." : "skipped (--skip-font)",
26187
26266
  actor_sheets: report.actor_sheets.length > 0 ? `Each living hero (${report.actor_sheets.join(", ")}) is fused into a generated multi-view reference sheet (image_reference_sheet) that the render grounds on \u2014 so drop ONE clean photo at that hero's ingest and the sheet builds the consistent turnaround. Pass --skip-actor-sheets to ground on the lone photo instead.` : "none (no person/animal heroes detected, or --skip-actor-sheets)",
@@ -26198,9 +26277,9 @@ var scaffoldStaticAdCommand = defineCommand98({
26198
26277
  });
26199
26278
 
26200
26279
  // src/commands/canvas/scaffold-video.ts
26201
- import { access as access2, cp, mkdir as mkdir7, readFile as readFile15, rm as rm6, writeFile as writeFile8 } from "fs/promises";
26280
+ import { access as access2, cp, mkdir as mkdir7, readFile as readFile15, rm as rm6, writeFile as writeFile9 } from "fs/promises";
26202
26281
  import { tmpdir as tmpdir2 } from "os";
26203
- import path21 from "path";
26282
+ import path22 from "path";
26204
26283
  import { defineCommand as defineCommand99 } from "citty";
26205
26284
 
26206
26285
  // src/engine/scaffold/lib/model-router.ts
@@ -26340,24 +26419,24 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
26340
26419
  }
26341
26420
 
26342
26421
  // src/commands/canvas/composition-path.ts
26343
- import { existsSync as existsSync3 } from "fs";
26344
- import path19 from "path";
26345
- function resolveShippedCanvasDir(name, startDir, exists = existsSync3, maxDepth = 8) {
26346
- const rel = path19.join("canvas", name);
26422
+ import { existsSync as existsSync4 } from "fs";
26423
+ import path20 from "path";
26424
+ function resolveShippedCanvasDir(name, startDir, exists = existsSync4, maxDepth = 8) {
26425
+ const rel = path20.join("canvas", name);
26347
26426
  let dir = startDir;
26348
26427
  for (let i = 0; i < maxDepth; i++) {
26349
- const candidate = path19.join(dir, rel);
26350
- if (exists(path19.join(candidate, "meta.json"))) return candidate;
26351
- const parent = path19.dirname(dir);
26428
+ const candidate = path20.join(dir, rel);
26429
+ if (exists(path20.join(candidate, "meta.json"))) return candidate;
26430
+ const parent = path20.dirname(dir);
26352
26431
  if (parent === dir) break;
26353
26432
  dir = parent;
26354
26433
  }
26355
- return path19.resolve(startDir, "../../../", rel);
26434
+ return path20.resolve(startDir, "../../../", rel);
26356
26435
  }
26357
26436
 
26358
26437
  // src/commands/canvas/gitignore.ts
26359
26438
  import { appendFile, readFile as readFile14 } from "fs/promises";
26360
- import path20 from "path";
26439
+ import path21 from "path";
26361
26440
  function missingGitignoreEntries(existing, entries) {
26362
26441
  const present2 = new Set(
26363
26442
  existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
@@ -26365,7 +26444,7 @@ function missingGitignoreEntries(existing, entries) {
26365
26444
  return entries.filter((e) => !present2.has(e.trim().replace(/\/+$/, "")));
26366
26445
  }
26367
26446
  async function ensureGitignore(dir, entries) {
26368
- const file = path20.join(dir, ".gitignore");
26447
+ const file = path21.join(dir, ".gitignore");
26369
26448
  let existing;
26370
26449
  try {
26371
26450
  existing = await readFile14(file, "utf8");
@@ -26426,7 +26505,7 @@ async function loadTranscriptBestEffort(ref) {
26426
26505
  async function stageCaptions(outDir, transcript) {
26427
26506
  const text2 = transcript?.trim();
26428
26507
  if (!text2 || text2 === "[]") return {};
26429
- const compositionPath = path21.join(outDir, "tiktok-captions-composition");
26508
+ const compositionPath = path22.join(outDir, "tiktok-captions-composition");
26430
26509
  await cp(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
26431
26510
  return { compositionPath };
26432
26511
  }
@@ -26444,12 +26523,12 @@ function patchCompositionHtml(html, dims) {
26444
26523
  return html.replace(/(<meta\s+name="viewport"\s+content="width=)\d+(,\s*height=)\d+(")/i, `$1${dims.w}$2${dims.h}$3`).replace(/(width:\s*)\d+(px;\s*height:\s*)\d+(px;)/i, `$1${dims.w}$2${dims.h}$3`).replace(/(data-width=")\d+(")/i, `$1${dims.w}$2`).replace(/(data-height=")\d+(")/i, `$1${dims.h}$2`);
26445
26524
  }
26446
26525
  async function stampCompositionDims(compositionDir, dims) {
26447
- const metaPath = path21.join(compositionDir, "meta.json");
26526
+ const metaPath = path22.join(compositionDir, "meta.json");
26448
26527
  const rawMeta = await readFile15(metaPath, "utf8");
26449
- await writeFile8(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
26450
- const htmlPath = path21.join(compositionDir, "index.html");
26528
+ await writeFile9(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
26529
+ const htmlPath = path22.join(compositionDir, "index.html");
26451
26530
  const rawHtml = await readFile15(htmlPath, "utf8");
26452
- await writeFile8(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
26531
+ await writeFile9(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
26453
26532
  }
26454
26533
  function parseElements2(raw) {
26455
26534
  const parsed = JSON.parse(raw);
@@ -26496,7 +26575,7 @@ var VIDEO_EXT_BY_MIME = {
26496
26575
  "video/x-matroska": ".mkv"
26497
26576
  };
26498
26577
  function referenceVideoExt(url, contentType) {
26499
- const fromPath = path21.extname(new URL(url).pathname).toLowerCase();
26578
+ const fromPath = path22.extname(new URL(url).pathname).toLowerCase();
26500
26579
  if (fromPath && fromPath.length <= 5) return fromPath;
26501
26580
  const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase();
26502
26581
  return mime && VIDEO_EXT_BY_MIME[mime] || ".mp4";
@@ -26522,7 +26601,7 @@ function videoDefinitionDescription(blueprint) {
26522
26601
  return typeof product === "string" && product.trim() ? product.trim() : void 0;
26523
26602
  }
26524
26603
  async function materializeReferenceVideo(fileArg2) {
26525
- if (!/^https?:\/\//i.test(fileArg2)) return path21.resolve(fileArg2);
26604
+ if (!/^https?:\/\//i.test(fileArg2)) return path22.resolve(fileArg2);
26526
26605
  let bytes;
26527
26606
  let contentType;
26528
26607
  try {
@@ -26534,11 +26613,11 @@ async function materializeReferenceVideo(fileArg2) {
26534
26613
  throw new Error(`failed to download reference video: ${e instanceof Error ? e.message : String(e)}`);
26535
26614
  }
26536
26615
  if (bytes.length === 0) throw new Error("reference video download was empty");
26537
- const dest = path21.join(
26616
+ const dest = path22.join(
26538
26617
  tmpdir2(),
26539
26618
  `baker-ref-${sha256Hex(bytes).slice(0, 16)}${referenceVideoExt(fileArg2, contentType)}`
26540
26619
  );
26541
- await writeFile8(dest, bytes);
26620
+ await writeFile9(dest, bytes);
26542
26621
  return dest;
26543
26622
  }
26544
26623
  function resolveSeamDedup(raw) {
@@ -26754,11 +26833,11 @@ var scaffoldVideoCommand = defineCommand99({
26754
26833
  } catch (e) {
26755
26834
  return fail4("download", e instanceof Error ? e.message : String(e));
26756
26835
  }
26757
- const base = path21.basename(videoPath, path21.extname(videoPath));
26758
- const outPath = args.out ? path21.resolve(String(args.out)) : slug ? path21.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path21.join(path21.dirname(videoPath), `${base}.video.canvas.json`);
26759
- const outDir = path21.dirname(outPath);
26760
- const blueprintPath = path21.join(outDir, "prompt.json");
26761
- const blueprintStylePath = path21.join(outDir, "prompt.style.json");
26836
+ const base = path22.basename(videoPath, path22.extname(videoPath));
26837
+ const outPath = args.out ? path22.resolve(String(args.out)) : slug ? path22.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path22.join(path22.dirname(videoPath), `${base}.video.canvas.json`);
26838
+ const outDir = path22.dirname(outPath);
26839
+ const blueprintPath = path22.join(outDir, "prompt.json");
26840
+ const blueprintStylePath = path22.join(outDir, "prompt.style.json");
26762
26841
  const frames = args.frames === "reuse" ? "reuse" : "generate";
26763
26842
  const maxScenes = args["max-scenes"] ? Number(args["max-scenes"]) : void 0;
26764
26843
  if (Number.isFinite(maxScenes)) {
@@ -26785,7 +26864,7 @@ var scaffoldVideoCommand = defineCommand99({
26785
26864
  await mkdir7(outDir, { recursive: true });
26786
26865
  const annotated = annotateBlueprintWithElements(blueprint, elements);
26787
26866
  await writeSceneFiles(outDir, annotated);
26788
- await writeFile8(blueprintStylePath, renderStyleProjectionFromValue(annotated), "utf8");
26867
+ await writeFile9(blueprintStylePath, renderStyleProjectionFromValue(annotated), "utf8");
26789
26868
  let aspect;
26790
26869
  try {
26791
26870
  aspect = resolveAspect(
@@ -26803,10 +26882,10 @@ var scaffoldVideoCommand = defineCommand99({
26803
26882
  `
26804
26883
  );
26805
26884
  }
26806
- const compositionDest = path21.join(outDir, "video-overlay-composition");
26885
+ const compositionDest = path22.join(outDir, "video-overlay-composition");
26807
26886
  await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
26808
26887
  await stampCompositionDims(compositionDest, outDims);
26809
- const indexPath = path21.join(compositionDest, "index.html");
26888
+ const indexPath = path22.join(compositionDest, "index.html");
26810
26889
  const overlayHtml = buildOverlayHtml(blueprint, { captionsActive: Boolean(transcript) });
26811
26890
  const indexHtml = await readFile15(indexPath, "utf8");
26812
26891
  const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
@@ -26816,16 +26895,16 @@ var scaffoldVideoCommand = defineCommand99({
26816
26895
  `video-overlay-composition/index.html is missing the <!--OVERLAYS--> marker \u2014 cannot inject the overlay layer`
26817
26896
  );
26818
26897
  }
26819
- await writeFile8(indexPath, injected, "utf8");
26898
+ await writeFile9(indexPath, injected, "utf8");
26820
26899
  const captions = await stageCaptions(outDir, transcript);
26821
26900
  if (captions.compositionPath) await stampCompositionDims(captions.compositionPath, outDims);
26822
26901
  const opts = {
26823
26902
  imageModel,
26824
26903
  videoModel,
26825
- overlayCompositionPath: path21.relative(outDir, compositionDest),
26826
- captionsCompositionPath: captions.compositionPath ? path21.relative(outDir, captions.compositionPath) : void 0,
26827
- blueprintPath: path21.relative(outDir, blueprintPath),
26828
- blueprintStylePath: path21.relative(outDir, blueprintStylePath),
26904
+ overlayCompositionPath: path22.relative(outDir, compositionDest),
26905
+ captionsCompositionPath: captions.compositionPath ? path22.relative(outDir, captions.compositionPath) : void 0,
26906
+ blueprintPath: path22.relative(outDir, blueprintPath),
26907
+ blueprintStylePath: path22.relative(outDir, blueprintStylePath),
26829
26908
  frames,
26830
26909
  ambient: Boolean(args.ambient),
26831
26910
  seamDedup: resolveSeamDedup(args["seam-dedup"]),
@@ -26850,10 +26929,10 @@ var scaffoldVideoCommand = defineCommand99({
26850
26929
  todo.blocking_validation_issues = validation.issues;
26851
26930
  meta.todo = todo;
26852
26931
  }
26853
- await writeFile8(outPath, `${JSON.stringify(canvas, null, 2)}
26932
+ await writeFile9(outPath, `${JSON.stringify(canvas, null, 2)}
26854
26933
  `, "utf8");
26855
- await writeFile8(
26856
- path21.join(outDir, REBUILD_FILE),
26934
+ await writeFile9(
26935
+ path22.join(outDir, REBUILD_FILE),
26857
26936
  `${JSON.stringify({ elements, opts }, null, 2)}
26858
26937
  `,
26859
26938
  "utf8"
@@ -26878,9 +26957,9 @@ var scaffoldVideoCommand = defineCommand99({
26878
26957
  await ensureGitignore(process.cwd(), ["canvas/", ".context/"]);
26879
26958
  const sourceRef = videoSourceReference(blueprint, fileArg2);
26880
26959
  if (slug) {
26881
- const definitionPath = path21.join(outDir, "_definition.md");
26960
+ const definitionPath = path22.join(outDir, "_definition.md");
26882
26961
  if (!await fileExists2(definitionPath)) {
26883
- await writeFile8(
26962
+ await writeFile9(
26884
26963
  definitionPath,
26885
26964
  buildCreativeDefinition({
26886
26965
  title: titleFromSlug(slug),
@@ -26933,7 +27012,7 @@ var scaffoldVideoCommand = defineCommand99({
26933
27012
  graph: canvas.metadata?.video?.graph_stats
26934
27013
  },
26935
27014
  checklist: {
26936
- edit_prompt: `The blueprint is split so you edit ONE small file at a time \u2014 never a giant one. Per-scene content (a scene's dialogue, action, frame prompts, overlays) lives in \`scenes/sNN.json\` \u2014 edit the single scene you want to change. Global cast/palette/brand/copy lives in \`${path21.basename(blueprintPath)}\`. \`baker canvas validate\`/\`run\` re-assemble the blueprint and re-flow every edited scene back into the render (and regenerate ${path21.basename(blueprintStylePath)}, the projection each frame's target_blueprint reads) \u2014 so your scene edits reach the render automatically. Never hand-edit the inlined node prompts in the canvas or the derived ${path21.basename(blueprintStylePath)}; both are regenerated.`,
27015
+ edit_prompt: `The blueprint is split so you edit ONE small file at a time \u2014 never a giant one. Per-scene content (a scene's dialogue, action, frame prompts, overlays) lives in \`scenes/sNN.json\` \u2014 edit the single scene you want to change. Global cast/palette/brand/copy lives in \`${path22.basename(blueprintPath)}\`. \`baker canvas validate\`/\`run\` re-assemble the blueprint and re-flow every edited scene back into the render (and regenerate ${path22.basename(blueprintStylePath)}, the projection each frame's target_blueprint reads) \u2014 so your scene edits reach the render automatically. Never hand-edit the inlined node prompts in the canvas or the derived ${path22.basename(blueprintStylePath)}; both are regenerated.`,
26937
27016
  recurring_elements_to_supply: report.elements,
26938
27017
  voices_to_confirm: report.dialogue.map((d) => ({
26939
27018
  scene: d.scene,
@@ -26970,8 +27049,8 @@ var scaffoldVideoCommand = defineCommand99({
26970
27049
  });
26971
27050
 
26972
27051
  // src/commands/canvas/set-prompt.ts
26973
- import { readFile as readFile16, writeFile as writeFile9 } from "fs/promises";
26974
- import path22 from "path";
27052
+ import { readFile as readFile16, writeFile as writeFile10 } from "fs/promises";
27053
+ import path23 from "path";
26975
27054
  import { defineCommand as defineCommand100 } from "citty";
26976
27055
  function setNodePrompt(canvas, nodeId, text2) {
26977
27056
  const nodes = canvas?.nodes;
@@ -26999,7 +27078,7 @@ var setPromptCommand = defineCommand100({
26999
27078
  "text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
27000
27079
  },
27001
27080
  async run({ args }) {
27002
- const filePath = path22.resolve(String(args.file));
27081
+ const filePath = path23.resolve(String(args.file));
27003
27082
  let canvas;
27004
27083
  try {
27005
27084
  canvas = JSON.parse(await readFile16(filePath, "utf8"));
@@ -27009,7 +27088,7 @@ var setPromptCommand = defineCommand100({
27009
27088
  process.exit(2);
27010
27089
  }
27011
27090
  let text2;
27012
- if (args["text-file"]) text2 = await readFile16(path22.resolve(String(args["text-file"])), "utf8");
27091
+ if (args["text-file"]) text2 = await readFile16(path23.resolve(String(args["text-file"])), "utf8");
27013
27092
  else if (args.text !== void 0) text2 = String(args.text);
27014
27093
  else {
27015
27094
  process.stderr.write(
@@ -27030,14 +27109,14 @@ var setPromptCommand = defineCommand100({
27030
27109
  process.exit(2);
27031
27110
  return;
27032
27111
  }
27033
- const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path22.dirname(filePath)), defaultRegistry());
27112
+ const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path23.dirname(filePath)), defaultRegistry());
27034
27113
  if (!validation.ok) {
27035
27114
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
27036
27115
  `);
27037
27116
  process.exit(2);
27038
27117
  return;
27039
27118
  }
27040
- await writeFile9(filePath, `${JSON.stringify(updated, null, 2)}
27119
+ await writeFile10(filePath, `${JSON.stringify(updated, null, 2)}
27041
27120
  `, "utf8");
27042
27121
  process.stdout.write(`${JSON.stringify({ ok: true, node: String(args.node), bytes: text2.length }, null, 2)}
27043
27122
  `);
@@ -27046,7 +27125,7 @@ var setPromptCommand = defineCommand100({
27046
27125
 
27047
27126
  // src/commands/canvas/validate.ts
27048
27127
  import { readFile as readFile17 } from "fs/promises";
27049
- import path23 from "path";
27128
+ import path24 from "path";
27050
27129
  import { defineCommand as defineCommand101 } from "citty";
27051
27130
  var validateCommand = defineCommand101({
27052
27131
  meta: {
@@ -27055,7 +27134,7 @@ var validateCommand = defineCommand101({
27055
27134
  },
27056
27135
  args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
27057
27136
  async run({ args }) {
27058
- const filePath = path23.resolve(String(args.file));
27137
+ const filePath = path24.resolve(String(args.file));
27059
27138
  const raw = await readFile17(filePath, "utf8");
27060
27139
  let parsed;
27061
27140
  try {
@@ -27066,7 +27145,9 @@ var validateCommand = defineCommand101({
27066
27145
  `);
27067
27146
  process.exit(2);
27068
27147
  }
27069
- parsed = resolveRelativeCanvasPaths(parsed, path23.dirname(filePath));
27148
+ const healed = await healAbsoluteCanvasPaths(filePath, parsed);
27149
+ parsed = healed.canvas;
27150
+ parsed = resolveRelativeCanvasPaths(parsed, path24.dirname(filePath));
27070
27151
  let styleProjection = "not_applicable";
27071
27152
  try {
27072
27153
  styleProjection = await syncStyleProjection(parsed, (line) => process.stderr.write(`${line}
@@ -27107,6 +27188,7 @@ var validateCommand = defineCommand101({
27107
27188
  cost_preview: result.perNodeCredits ?? [],
27108
27189
  style_projection: styleProjection,
27109
27190
  scene_projection: sceneProjection,
27191
+ normalized_paths: healed.rewrites,
27110
27192
  warnings: result.warnings ?? []
27111
27193
  },
27112
27194
  null,
@@ -27711,14 +27793,14 @@ Full guide: __tooling__/docs/tools/baker/creatives.md`
27711
27793
  import { defineCommand as defineCommand107 } from "citty";
27712
27794
 
27713
27795
  // src/commands/flows/shared.ts
27714
- import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync9 } from "fs";
27796
+ import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync9 } from "fs";
27715
27797
  import { join as join3 } from "path";
27716
27798
  var FLOWS_DIR = "src/lib/flow-engine/flows";
27717
27799
  function flowsDir() {
27718
27800
  let dir = process.cwd();
27719
27801
  for (let i = 0; i < 6; i++) {
27720
27802
  const candidate = join3(dir, FLOWS_DIR);
27721
- if (existsSync4(candidate)) {
27803
+ if (existsSync5(candidate)) {
27722
27804
  return candidate;
27723
27805
  }
27724
27806
  const parent = join3(dir, "..");
@@ -27735,14 +27817,14 @@ function failLocal(message) {
27735
27817
  }
27736
27818
  function listFlowSlugs() {
27737
27819
  const dir = flowsDir();
27738
- if (!existsSync4(dir)) {
27820
+ if (!existsSync5(dir)) {
27739
27821
  return [];
27740
27822
  }
27741
27823
  return readdirSync2(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith("_") && entry.name !== ".gitkeep").map((entry) => entry.name).sort();
27742
27824
  }
27743
27825
  function readFlowTree(slug) {
27744
27826
  const path36 = join3(flowsDir(), slug, "_data.json");
27745
- if (!existsSync4(path36)) {
27827
+ if (!existsSync5(path36)) {
27746
27828
  failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
27747
27829
  }
27748
27830
  try {
@@ -28408,7 +28490,7 @@ Examples:
28408
28490
  });
28409
28491
 
28410
28492
  // src/commands/ga4/query.ts
28411
- import { appendFileSync as appendFileSync2, existsSync as existsSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync3 } from "fs";
28493
+ import { appendFileSync as appendFileSync2, existsSync as existsSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync3 } from "fs";
28412
28494
  import { resolve as resolve2 } from "path";
28413
28495
  import { defineCommand as defineCommand112 } from "citty";
28414
28496
 
@@ -28482,7 +28564,7 @@ function writeRowsToFile2(filePath, rows, append) {
28482
28564
  const fields = extractFields2(rows);
28483
28565
  const ext = filePath.split(".").pop()?.toLowerCase();
28484
28566
  if (ext === "csv") {
28485
- if (!append || !existsSync5(filePath)) {
28567
+ if (!append || !existsSync6(filePath)) {
28486
28568
  writeFileSync3(filePath, `${toCsvRow(fields)}
28487
28569
  `, "utf-8");
28488
28570
  }
@@ -28493,12 +28575,12 @@ function writeRowsToFile2(filePath, rows, append) {
28493
28575
  const lines = rows.map((row) => JSON.stringify(row));
28494
28576
  const content = `${lines.join("\n")}
28495
28577
  `;
28496
- if (append && existsSync5(filePath)) {
28578
+ if (append && existsSync6(filePath)) {
28497
28579
  appendFileSync2(filePath, content, "utf-8");
28498
28580
  } else {
28499
28581
  writeFileSync3(filePath, content, "utf-8");
28500
28582
  }
28501
- } else if (append && existsSync5(filePath)) {
28583
+ } else if (append && existsSync6(filePath)) {
28502
28584
  const existing = JSON.parse(readFileSync11(filePath, "utf-8"));
28503
28585
  writeFileSync3(filePath, JSON.stringify([...existing, ...rows], null, 2), "utf-8");
28504
28586
  } else {
@@ -29022,7 +29104,7 @@ Full guide: __tooling__/docs/tools/baker/ga4.md`
29022
29104
  import { defineCommand as defineCommand118 } from "citty";
29023
29105
 
29024
29106
  // src/commands/gsc/query.ts
29025
- import { appendFileSync as appendFileSync3, existsSync as existsSync6, readFileSync as readFileSync12, writeFileSync as writeFileSync4 } from "fs";
29107
+ import { appendFileSync as appendFileSync3, existsSync as existsSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync4 } from "fs";
29026
29108
  import { resolve as resolve3 } from "path";
29027
29109
  import { defineCommand as defineCommand115 } from "citty";
29028
29110
 
@@ -29154,7 +29236,7 @@ function writeRowsToFile3(filePath, rows, append) {
29154
29236
  const fields = extractFields3(rows);
29155
29237
  const ext = filePath.split(".").pop()?.toLowerCase();
29156
29238
  if (ext === "csv") {
29157
- if (!append || !existsSync6(filePath)) {
29239
+ if (!append || !existsSync7(filePath)) {
29158
29240
  writeFileSync4(filePath, `${toCsvRow(fields)}
29159
29241
  `, "utf-8");
29160
29242
  }
@@ -29164,12 +29246,12 @@ function writeRowsToFile3(filePath, rows, append) {
29164
29246
  } else if (ext === "jsonl") {
29165
29247
  const content = `${rows.map((row) => JSON.stringify(row)).join("\n")}
29166
29248
  `;
29167
- if (append && existsSync6(filePath)) {
29249
+ if (append && existsSync7(filePath)) {
29168
29250
  appendFileSync3(filePath, content, "utf-8");
29169
29251
  } else {
29170
29252
  writeFileSync4(filePath, content, "utf-8");
29171
29253
  }
29172
- } else if (append && existsSync6(filePath)) {
29254
+ } else if (append && existsSync7(filePath)) {
29173
29255
  const existing = JSON.parse(readFileSync12(filePath, "utf-8"));
29174
29256
  writeFileSync4(filePath, JSON.stringify([...existing, ...rows], null, 2), "utf-8");
29175
29257
  } else {
@@ -30148,7 +30230,7 @@ function cropSprite(input, region) {
30148
30230
 
30149
30231
  // src/lib/image/io.ts
30150
30232
  import { randomBytes } from "crypto";
30151
- import { glob as fsGlob, readFile as readFile19, rename, stat as stat4, writeFile as writeFile10 } from "fs/promises";
30233
+ import { glob as fsGlob, readFile as readFile19, rename, stat as stat4, writeFile as writeFile11 } from "fs/promises";
30152
30234
  import { dirname as dirname2, extname as extname2, join as join4, resolve as resolve4 } from "path";
30153
30235
  var REMOTE_RE = /^https?:\/\//i;
30154
30236
  var GLOB_RE = /[*?[\]{}]/;
@@ -30204,7 +30286,7 @@ async function atomicWrite(targetPath, data) {
30204
30286
  const absolute = resolve4(targetPath);
30205
30287
  const dir = dirname2(absolute);
30206
30288
  const tmp = join4(dir, `.baker-image-${randomBytes(8).toString("hex")}.tmp`);
30207
- await writeFile10(tmp, data);
30289
+ await writeFile11(tmp, data);
30208
30290
  await rename(tmp, absolute);
30209
30291
  }
30210
30292
 
@@ -33545,12 +33627,12 @@ import { defineCommand as defineCommand157 } from "citty";
33545
33627
 
33546
33628
  // src/commands/landing/critique.ts
33547
33629
  import { readdir as readdir8, stat as stat6 } from "fs/promises";
33548
- import path28 from "path";
33630
+ import path29 from "path";
33549
33631
  import { defineCommand as defineCommand147 } from "citty";
33550
33632
 
33551
33633
  // src/engine/landing/lib/brand-tokens.ts
33552
33634
  import { readFile as readFile20 } from "fs/promises";
33553
- import path24 from "path";
33635
+ import path25 from "path";
33554
33636
 
33555
33637
  // src/engine/landing/lib/color.ts
33556
33638
  var NEUTRAL_COLOR_KEYWORDS = /* @__PURE__ */ new Set([
@@ -33727,7 +33809,7 @@ function brandTokensFromCanonical(tokens) {
33727
33809
  return { fonts, colors, hasTokens: fonts.size > 0 || colors.length > 0 };
33728
33810
  }
33729
33811
  async function loadBrandTokens(projectRoot) {
33730
- const tokensRaw = await safeRead(path24.join(projectRoot, "src", "brand", "tokens.json"));
33812
+ const tokensRaw = await safeRead(path25.join(projectRoot, "src", "brand", "tokens.json"));
33731
33813
  if (tokensRaw) {
33732
33814
  try {
33733
33815
  const canonical2 = brandTokensSchema.safeParse(JSON.parse(tokensRaw));
@@ -33735,8 +33817,8 @@ async function loadBrandTokens(projectRoot) {
33735
33817
  } catch {
33736
33818
  }
33737
33819
  }
33738
- const globalCss = await safeRead(path24.join(projectRoot, "src", "styles", "global.css"));
33739
- const brandMd = await safeRead(path24.join(projectRoot, "src", "brand", "BRAND.md"));
33820
+ const globalCss = await safeRead(path25.join(projectRoot, "src", "styles", "global.css"));
33821
+ const brandMd = await safeRead(path25.join(projectRoot, "src", "brand", "BRAND.md"));
33740
33822
  if (!globalCss && !brandMd) return EMPTY;
33741
33823
  return parseBrandTokens(globalCss, brandMd);
33742
33824
  }
@@ -34797,13 +34879,13 @@ function describeCounts(findings) {
34797
34879
  }
34798
34880
 
34799
34881
  // src/engine/landing/lib/referenceStore.ts
34800
- import { mkdir as mkdir8, readFile as readFile21, writeFile as writeFile11 } from "fs/promises";
34801
- import path25 from "path";
34882
+ import { mkdir as mkdir8, readFile as readFile21, writeFile as writeFile12 } from "fs/promises";
34883
+ import path26 from "path";
34802
34884
  var REFERENCES_FILE = ".cache/inspiration-refs.json";
34803
34885
  var REFERENCE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
34804
34886
  async function readReferences(projectRoot) {
34805
34887
  try {
34806
- const raw = await readFile21(path25.join(projectRoot, REFERENCES_FILE), "utf8");
34888
+ const raw = await readFile21(path26.join(projectRoot, REFERENCES_FILE), "utf8");
34807
34889
  const parsed = JSON.parse(raw);
34808
34890
  if (!Array.isArray(parsed)) return [];
34809
34891
  const cutoff = Date.now() - REFERENCE_TTL_MS;
@@ -34827,9 +34909,9 @@ async function recordReferences(projectRoot, references) {
34827
34909
  const existing = await readReferences(projectRoot);
34828
34910
  const replaced = new Set(references.map((reference) => reference.sectionId));
34829
34911
  const merged = [...existing.filter((entry) => !replaced.has(entry.sectionId)), ...references];
34830
- const file = path25.join(projectRoot, REFERENCES_FILE);
34831
- await mkdir8(path25.dirname(file), { recursive: true });
34832
- await writeFile11(file, `${JSON.stringify(merged, null, 2)}
34912
+ const file = path26.join(projectRoot, REFERENCES_FILE);
34913
+ await mkdir8(path26.dirname(file), { recursive: true });
34914
+ await writeFile12(file, `${JSON.stringify(merged, null, 2)}
34833
34915
  `);
34834
34916
  return true;
34835
34917
  } catch {
@@ -34838,40 +34920,40 @@ async function recordReferences(projectRoot, references) {
34838
34920
  }
34839
34921
 
34840
34922
  // src/commands/landing/snapshot.ts
34841
- import { mkdir as mkdir9, rename as rename2, writeFile as writeFile12 } from "fs/promises";
34842
- import path26 from "path";
34923
+ import { mkdir as mkdir9, rename as rename2, writeFile as writeFile13 } from "fs/promises";
34924
+ import path27 from "path";
34843
34925
  var CRITIC_VERSION = "2";
34844
34926
  function critiqueCacheDir(projectRoot) {
34845
- return path26.join(projectRoot, ".cache", "landing-critique");
34927
+ return path27.join(projectRoot, ".cache", "landing-critique");
34846
34928
  }
34847
34929
  function snapshotPath(projectRoot, slug) {
34848
- return path26.join(critiqueCacheDir(projectRoot), `${slug}.json`);
34930
+ return path27.join(critiqueCacheDir(projectRoot), `${slug}.json`);
34849
34931
  }
34850
34932
  async function writeCritiqueSnapshot(projectRoot, snapshot) {
34851
34933
  await mkdir9(critiqueCacheDir(projectRoot), { recursive: true });
34852
34934
  const dest = snapshotPath(projectRoot, snapshot.slug);
34853
34935
  const tmp = `${dest}.tmp`;
34854
- await writeFile12(tmp, `${JSON.stringify(snapshot, null, 2)}
34936
+ await writeFile13(tmp, `${JSON.stringify(snapshot, null, 2)}
34855
34937
  `, "utf8");
34856
34938
  await rename2(tmp, dest);
34857
34939
  }
34858
34940
 
34859
34941
  // src/commands/landing/source-version.ts
34860
34942
  import { readdir as readdir7, readFile as readFile22, stat as stat5 } from "fs/promises";
34861
- import path27 from "path";
34943
+ import path28 from "path";
34862
34944
  async function landingSourceRelPaths(landingDir) {
34863
34945
  const rel = [];
34864
- if (await isFile(path27.join(landingDir, "index.astro"))) rel.push("index.astro");
34865
- const componentsDir = path27.join(landingDir, "_components");
34946
+ if (await isFile(path28.join(landingDir, "index.astro"))) rel.push("index.astro");
34947
+ const componentsDir = path28.join(landingDir, "_components");
34866
34948
  for (const abs of await walkAstro(componentsDir)) {
34867
- rel.push(path27.relative(landingDir, abs).split(path27.sep).join("/"));
34949
+ rel.push(path28.relative(landingDir, abs).split(path28.sep).join("/"));
34868
34950
  }
34869
34951
  return rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
34870
34952
  }
34871
34953
  async function readLandingSources(landingDir) {
34872
34954
  const rel = await landingSourceRelPaths(landingDir);
34873
34955
  const out = [];
34874
- for (const r of rel) out.push({ path: r, text: await readFile22(path27.join(landingDir, r), "utf8") });
34956
+ for (const r of rel) out.push({ path: r, text: await readFile22(path28.join(landingDir, r), "utf8") });
34875
34957
  return out;
34876
34958
  }
34877
34959
  async function computeLandingSourceSha(landingDir) {
@@ -34880,7 +34962,7 @@ async function computeLandingSourceSha(landingDir) {
34880
34962
  for (const r of rel) {
34881
34963
  let bytes;
34882
34964
  try {
34883
- bytes = await readFile22(path27.join(landingDir, r));
34965
+ bytes = await readFile22(path28.join(landingDir, r));
34884
34966
  } catch {
34885
34967
  bytes = Buffer.alloc(0);
34886
34968
  }
@@ -34904,7 +34986,7 @@ async function walkAstro(dir) {
34904
34986
  }
34905
34987
  const out = [];
34906
34988
  for (const entry of entries) {
34907
- const abs = path27.join(dir, entry.name);
34989
+ const abs = path28.join(dir, entry.name);
34908
34990
  if (entry.isDirectory()) out.push(...await walkAstro(abs));
34909
34991
  else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
34910
34992
  }
@@ -34965,7 +35047,7 @@ var critiqueCommand2 = defineCommand147({
34965
35047
  { availableSlugs: await listLandingSlugs(projectRoot) }
34966
35048
  );
34967
35049
  }
34968
- if (!await isDir(path28.resolve(projectRoot, "src", "pages", slug))) {
35050
+ if (!await isDir(path29.resolve(projectRoot, "src", "pages", slug))) {
34969
35051
  fail5("NOT_FOUND", `No landing at src/pages/${slug}/`, {
34970
35052
  availableSlugs: await listLandingSlugs(projectRoot)
34971
35053
  });
@@ -35004,7 +35086,7 @@ var critiqueCommand2 = defineCommand147({
35004
35086
  }
35005
35087
  });
35006
35088
  async function critiqueOne(projectRoot, slug, brand, references) {
35007
- const landingDir = path28.resolve(projectRoot, "src", "pages", slug);
35089
+ const landingDir = path29.resolve(projectRoot, "src", "pages", slug);
35008
35090
  const [sources, sourceSha] = await Promise.all([readLandingSources(landingDir), computeLandingSourceSha(landingDir)]);
35009
35091
  const report = critiqueLanding({ slug, sources, brand, references });
35010
35092
  let snapshotFailed = false;
@@ -35024,7 +35106,7 @@ async function critiqueOne(projectRoot, slug, brand, references) {
35024
35106
  }
35025
35107
  async function listLandingSlugs(projectRoot) {
35026
35108
  try {
35027
- const entries = await readdir8(path28.join(projectRoot, "src", "pages"), { withFileTypes: true });
35109
+ const entries = await readdir8(path29.join(projectRoot, "src", "pages"), { withFileTypes: true });
35028
35110
  return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
35029
35111
  } catch {
35030
35112
  return [];
@@ -35175,8 +35257,8 @@ var addCommand = defineCommand148({
35175
35257
  });
35176
35258
 
35177
35259
  // src/commands/landing/inspiration/code.ts
35178
- import { mkdir as mkdir10, writeFile as writeFile13 } from "fs/promises";
35179
- import path29 from "path";
35260
+ import { mkdir as mkdir10, writeFile as writeFile14 } from "fs/promises";
35261
+ import path30 from "path";
35180
35262
  import { defineCommand as defineCommand149 } from "citty";
35181
35263
  registerSchema({
35182
35264
  command: "landing.inspiration.code",
@@ -35199,10 +35281,10 @@ var codeCommand = defineCommand149({
35199
35281
  try {
35200
35282
  const id = args.id;
35201
35283
  const data = await apiGet("/api/landing-inspiration/section-code", { id });
35202
- const dir = path29.join(process.cwd(), ".baker", "inspiration", id);
35284
+ const dir = path30.join(process.cwd(), ".baker", "inspiration", id);
35203
35285
  await mkdir10(dir, { recursive: true });
35204
- const file = path29.join(dir, "section.html");
35205
- await writeFile13(file, data.html);
35286
+ const file = path30.join(dir, "section.html");
35287
+ await writeFile14(file, data.html);
35206
35288
  const recorded = await recordReference(process.cwd(), {
35207
35289
  sectionId: id,
35208
35290
  sourceUrl: data.sourceUrl,
@@ -35222,7 +35304,7 @@ var codeCommand = defineCommand149({
35222
35304
  ok: true,
35223
35305
  data: {
35224
35306
  id,
35225
- file: path29.relative(process.cwd(), file),
35307
+ file: path30.relative(process.cwd(), file),
35226
35308
  bytes: data.html.length,
35227
35309
  fidelity: data.fidelity,
35228
35310
  reproduction_notes: data.reproductionNotes,
@@ -35478,7 +35560,7 @@ var pageCommand = defineCommand151({
35478
35560
 
35479
35561
  // src/commands/landing/inspiration/scrape.ts
35480
35562
  import { readFile as readFile23 } from "fs/promises";
35481
- import path32 from "path";
35563
+ import path33 from "path";
35482
35564
  import { defineCommand as defineCommand152 } from "citty";
35483
35565
 
35484
35566
  // src/engine/landing/lib/capturedReferences.ts
@@ -35670,8 +35752,8 @@ function classifyCaptureFailure(error) {
35670
35752
  }
35671
35753
 
35672
35754
  // src/engine/landing-library/run.ts
35673
- import { mkdir as mkdir11, writeFile as writeFile15 } from "fs/promises";
35674
- import path31 from "path";
35755
+ import { mkdir as mkdir11, writeFile as writeFile16 } from "fs/promises";
35756
+ import path32 from "path";
35675
35757
 
35676
35758
  // ../proxy/src/preflight.ts
35677
35759
  import http from "http";
@@ -37086,11 +37168,11 @@ async function renderBundleToPng(browser, html, viewportWidth, options = {}) {
37086
37168
  }
37087
37169
 
37088
37170
  // src/engine/landing-library/report.ts
37089
- import { writeFile as writeFile14 } from "fs/promises";
37090
- import path30 from "path";
37171
+ import { writeFile as writeFile15 } from "fs/promises";
37172
+ import path31 from "path";
37091
37173
  async function writeCaptureReport(manifest, outDir) {
37092
- const file = path30.join(outDir, "report.html");
37093
- await writeFile14(file, renderReport(manifest));
37174
+ const file = path31.join(outDir, "report.html");
37175
+ await writeFile15(file, renderReport(manifest));
37094
37176
  return file;
37095
37177
  }
37096
37178
  function escapeHtml3(value) {
@@ -37268,32 +37350,32 @@ async function reproducePage(args) {
37268
37350
  const { browser, page, outDir, pageUrl, livePageShot } = args;
37269
37351
  const built = await buildSectionBundle(page, "body", pageUrl).catch(() => null);
37270
37352
  if (!built) return { bundle: null, fidelity: null };
37271
- await writeFile15(path31.join(outDir, "page.html"), built.html);
37353
+ await writeFile16(path32.join(outDir, "page.html"), built.html);
37272
37354
  const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width, {
37273
37355
  wholePage: true,
37274
37356
  timeoutMs: 6e4
37275
37357
  });
37276
37358
  if (!rendered || !livePageShot) return { bundle: "page.html", fidelity: null };
37277
- await writeFile15(path31.join(outDir, "page-rendered.png"), rendered);
37359
+ await writeFile16(path32.join(outDir, "page-rendered.png"), rendered);
37278
37360
  const { score, note } = await scoreFidelity(livePageShot, rendered);
37279
37361
  return { bundle: "page.html", fidelity: score, ...note ? { fidelityNote: note } : {} };
37280
37362
  }
37281
37363
  async function captureOneSection(args) {
37282
37364
  const { browser, page, candidate, sectionsDir, outDir, pageUrl, withCode } = args;
37283
- const dir = path31.join(sectionsDir, String(candidate.index).padStart(2, "0"));
37365
+ const dir = path32.join(sectionsDir, String(candidate.index).padStart(2, "0"));
37284
37366
  await mkdir11(dir, { recursive: true });
37285
37367
  const desktop = await captureSection(page, candidate);
37286
- if (desktop) await writeFile15(path31.join(dir, "desktop.png"), desktop);
37368
+ if (desktop) await writeFile16(path32.join(dir, "desktop.png"), desktop);
37287
37369
  const visualHash = desktop ? await perceptualHash(desktop) : null;
37288
37370
  const motion = await collectMotion(page, candidate.selector);
37289
37371
  const built = withCode ? await buildSectionBundle(page, candidate.selector, pageUrl) : null;
37290
37372
  let fidelity = null;
37291
37373
  let fidelityNote;
37292
37374
  if (built) {
37293
- await writeFile15(path31.join(dir, "section.html"), built.html);
37375
+ await writeFile16(path32.join(dir, "section.html"), built.html);
37294
37376
  const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width);
37295
37377
  if (rendered && desktop) {
37296
- await writeFile15(path31.join(dir, "section-rendered.png"), rendered);
37378
+ await writeFile16(path32.join(dir, "section-rendered.png"), rendered);
37297
37379
  const result = await scoreFidelity(desktop, rendered);
37298
37380
  fidelity = result.score;
37299
37381
  fidelityNote = result.note;
@@ -37301,9 +37383,9 @@ async function captureOneSection(args) {
37301
37383
  }
37302
37384
  return {
37303
37385
  ...candidate,
37304
- desktopShot: desktop ? path31.relative(outDir, path31.join(dir, "desktop.png")) : null,
37386
+ desktopShot: desktop ? path32.relative(outDir, path32.join(dir, "desktop.png")) : null,
37305
37387
  mobileShot: null,
37306
- bundle: built ? path31.relative(outDir, path31.join(dir, "section.html")) : null,
37388
+ bundle: built ? path32.relative(outDir, path32.join(dir, "section.html")) : null,
37307
37389
  fidelity,
37308
37390
  ...fidelityNote ? { fidelityNote } : {},
37309
37391
  ...built ? { cssStats: built.stats } : {},
@@ -37322,9 +37404,9 @@ async function captureMobileShots(args) {
37322
37404
  for (const section of sections) {
37323
37405
  const shot = await captureSectionOnMobile(mobile.page, section);
37324
37406
  if (!shot) continue;
37325
- const file = path31.join(sectionsDir, String(section.index).padStart(2, "0"), "mobile.png");
37326
- await writeFile15(file, shot);
37327
- section.mobileShot = path31.relative(outDir, file);
37407
+ const file = path32.join(sectionsDir, String(section.index).padStart(2, "0"), "mobile.png");
37408
+ await writeFile16(file, shot);
37409
+ section.mobileShot = path32.relative(outDir, file);
37328
37410
  }
37329
37411
  } finally {
37330
37412
  await mobile.context.close();
@@ -37339,10 +37421,10 @@ async function captureMotionTakes(args) {
37339
37421
  const filmOne = async (section) => {
37340
37422
  const take = await captureMotionTake(browser, pageUrl, section.selector).catch(() => null);
37341
37423
  if (!take) return;
37342
- const dir = path31.join(sectionsDir, String(section.index).padStart(2, "0"));
37343
- const file = path31.join(dir, "motion-filmstrip.png");
37344
- await writeFile15(file, take.filmstrip);
37345
- section.motionFilmstrip = path31.relative(outDir, file);
37424
+ const dir = path32.join(sectionsDir, String(section.index).padStart(2, "0"));
37425
+ const file = path32.join(dir, "motion-filmstrip.png");
37426
+ await writeFile16(file, take.filmstrip);
37427
+ section.motionFilmstrip = path32.relative(outDir, file);
37346
37428
  log(` [${section.index}] ${section.motion.summary}`);
37347
37429
  };
37348
37430
  const queue = [...moving];
@@ -37399,7 +37481,7 @@ async function captureAlternateViews(args) {
37399
37481
  async function reproduceWholePage(args) {
37400
37482
  const { browser, page, outDir, pageUrl, withCode, log } = args;
37401
37483
  const fullPage = await page.screenshot({ type: "png", fullPage: true }).catch(() => null);
37402
- if (fullPage) await writeFile15(path31.join(outDir, "full-page.png"), fullPage);
37484
+ if (fullPage) await writeFile16(path32.join(outDir, "full-page.png"), fullPage);
37403
37485
  if (!withCode) return { bundle: null, fidelity: null };
37404
37486
  const reproduction = await reproducePage({ browser, page, outDir, pageUrl, livePageShot: fullPage });
37405
37487
  log(`page reproduction: ${reproduction.fidelity === null ? "unavailable" : reproduction.fidelity.toFixed(2)}`);
@@ -37470,7 +37552,7 @@ async function openViaLadder(args) {
37470
37552
  async function scrapeLanding(options) {
37471
37553
  const timeoutMs = options.timeoutMs ?? 45e3;
37472
37554
  const log = options.onProgress ?? (() => void 0);
37473
- const sectionsDir = path31.join(options.outDir, "sections");
37555
+ const sectionsDir = path32.join(options.outDir, "sections");
37474
37556
  const nonPublic = refuseNonPublicUrl(options.url);
37475
37557
  if (nonPublic) {
37476
37558
  throw new BlockedPageError({
@@ -37532,7 +37614,7 @@ async function scrapeLanding(options) {
37532
37614
  security: prepared.security,
37533
37615
  captureTier: tier
37534
37616
  };
37535
- await writeFile15(path31.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
37617
+ await writeFile16(path32.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
37536
37618
  `);
37537
37619
  if (options.report !== false) {
37538
37620
  const reportPath = await writeCaptureReport(manifest, options.outDir);
@@ -37559,7 +37641,7 @@ async function recordCapture(manifest, outDir) {
37559
37641
  for (const section of manifest.sections) {
37560
37642
  if (!section.bundle) continue;
37561
37643
  try {
37562
- const markup = await readFile23(path32.join(outDir, section.bundle), "utf8");
37644
+ const markup = await readFile23(path33.join(outDir, section.bundle), "utf8");
37563
37645
  const copyStrings = capturedCopyStrings(markup);
37564
37646
  if (copyStrings.length === 0) continue;
37565
37647
  references.push({
@@ -37721,8 +37803,8 @@ var scrapeCommand = defineCommand152({
37721
37803
  });
37722
37804
 
37723
37805
  // src/commands/landing/inspiration/search.ts
37724
- import { mkdir as mkdir12, writeFile as writeFile16 } from "fs/promises";
37725
- import path33 from "path";
37806
+ import { mkdir as mkdir12, writeFile as writeFile17 } from "fs/promises";
37807
+ import path34 from "path";
37726
37808
  import { defineCommand as defineCommand153 } from "citty";
37727
37809
  registerSchema({
37728
37810
  command: "landing.inspiration.search",
@@ -37809,7 +37891,7 @@ function buildSearchBody(args) {
37809
37891
  return body;
37810
37892
  }
37811
37893
  async function downloadShots(results) {
37812
- const dir = path33.join(process.cwd(), ".baker", "inspiration");
37894
+ const dir = path34.join(process.cwd(), ".baker", "inspiration");
37813
37895
  await mkdir12(dir, { recursive: true });
37814
37896
  const saved = /* @__PURE__ */ new Map();
37815
37897
  await Promise.all(
@@ -37818,9 +37900,9 @@ async function downloadShots(results) {
37818
37900
  try {
37819
37901
  const response = await fetch(result.desktopShotUrl);
37820
37902
  if (!response.ok) return;
37821
- const file = path33.join(dir, `${result.id}.png`);
37822
- await writeFile16(file, Buffer.from(await response.arrayBuffer()));
37823
- saved.set(result.id, path33.relative(process.cwd(), file));
37903
+ const file = path34.join(dir, `${result.id}.png`);
37904
+ await writeFile17(file, Buffer.from(await response.arrayBuffer()));
37905
+ saved.set(result.id, path34.relative(process.cwd(), file));
37824
37906
  } catch {
37825
37907
  }
37826
37908
  })
@@ -38048,8 +38130,8 @@ var sequencesCommand = defineCommand154({
38048
38130
  });
38049
38131
 
38050
38132
  // src/commands/landing/inspiration/view.ts
38051
- import { mkdir as mkdir13, writeFile as writeFile17 } from "fs/promises";
38052
- import path34 from "path";
38133
+ import { mkdir as mkdir13, writeFile as writeFile18 } from "fs/promises";
38134
+ import path35 from "path";
38053
38135
  import { defineCommand as defineCommand155 } from "citty";
38054
38136
  registerSchema({
38055
38137
  command: "landing.inspiration.view",
@@ -38068,9 +38150,9 @@ async function download(url, file) {
38068
38150
  try {
38069
38151
  const response = await fetch(url);
38070
38152
  if (!response.ok) return null;
38071
- await mkdir13(path34.dirname(file), { recursive: true });
38072
- await writeFile17(file, Buffer.from(await response.arrayBuffer()));
38073
- return path34.relative(process.cwd(), file);
38153
+ await mkdir13(path35.dirname(file), { recursive: true });
38154
+ await writeFile18(file, Buffer.from(await response.arrayBuffer()));
38155
+ return path35.relative(process.cwd(), file);
38074
38156
  } catch {
38075
38157
  return null;
38076
38158
  }
@@ -38094,11 +38176,11 @@ var viewCommand2 = defineCommand155({
38094
38176
  const id = args.id;
38095
38177
  const data = await apiGet("/api/landing-inspiration/section", { id });
38096
38178
  const section = data.section;
38097
- const dir = path34.join(process.cwd(), ".baker", "inspiration", id);
38179
+ const dir = path35.join(process.cwd(), ".baker", "inspiration", id);
38098
38180
  const [desktop, mobile, filmstrip] = await Promise.all([
38099
- download(section.desktopShotUrl, path34.join(dir, "desktop.png")),
38100
- download(section.mobileShotUrl, path34.join(dir, "mobile.png")),
38101
- download(section.motionFilmstripUrl, path34.join(dir, "motion-filmstrip.png"))
38181
+ download(section.desktopShotUrl, path35.join(dir, "desktop.png")),
38182
+ download(section.mobileShotUrl, path35.join(dir, "mobile.png")),
38183
+ download(section.motionFilmstripUrl, path35.join(dir, "motion-filmstrip.png"))
38102
38184
  ]);
38103
38185
  const full = args.full;
38104
38186
  const hints = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
@@ -42308,7 +42390,7 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
42308
42390
  });
42309
42391
 
42310
42392
  // src/commands/videos/index.ts
42311
- import { defineCommand as defineCommand203 } from "citty";
42393
+ import { defineCommand as defineCommand202 } from "citty";
42312
42394
 
42313
42395
  // src/commands/videos/delete.ts
42314
42396
  import { defineCommand as defineCommand197 } from "citty";
@@ -42439,214 +42521,8 @@ var groupCommand2 = defineCommand199({
42439
42521
  }
42440
42522
  });
42441
42523
 
42442
- // src/commands/videos/ingest.ts
42443
- import { mkdtemp as mkdtemp2, readFile as readFile24, rm as rm7, stat as stat7 } from "fs/promises";
42444
- import { tmpdir as tmpdir3 } from "os";
42445
- import path35 from "path";
42446
- import { defineCommand as defineCommand200 } from "citty";
42447
-
42448
- // src/commands/videos/ingestUrl.ts
42449
- var MAX_VIDEO_INGEST_BYTES = 2 * 1024 * 1024 * 1024;
42450
- var INGEST_MAX_HEIGHT = 1080;
42451
- var DIRECT_MEDIA_EXTENSIONS = [".mp4", ".mov", ".webm", ".m4v", ".mkv", ".avi"];
42452
- var YOUTUBE_HOSTS = ["youtube.com", "youtu.be", "m.youtube.com", "www.youtube.com"];
42453
- function isDirectMediaUrl(rawUrl) {
42454
- const pathname = safePathname(rawUrl);
42455
- return DIRECT_MEDIA_EXTENSIONS.some((ext) => pathname.endsWith(ext));
42456
- }
42457
- function resolveIngestSource(explicit, rawUrl) {
42458
- if (explicit) return explicit;
42459
- return isYouTubeUrl(rawUrl) ? "youtube" : "url";
42460
- }
42461
- function isYouTubeUrl(rawUrl) {
42462
- let host;
42463
- try {
42464
- host = new URL(rawUrl).hostname.toLowerCase();
42465
- } catch {
42466
- return false;
42467
- }
42468
- return YOUTUBE_HOSTS.includes(host);
42469
- }
42470
- var TOOLING_OR_BLOCK_SIGNATURES = [
42471
- "requested format is not available",
42472
- "only images are available",
42473
- "nsig",
42474
- "sign in to confirm",
42475
- // bot check — distinct from "Private video. Sign in if…"
42476
- "unable to download api page",
42477
- "http error 403"
42478
- ];
42479
- function isToolingOrBlockFailure(detail) {
42480
- const haystack = detail.toLowerCase();
42481
- return TOOLING_OR_BLOCK_SIGNATURES.some((signature) => haystack.includes(signature));
42482
- }
42483
- function safePathname(rawUrl) {
42484
- try {
42485
- return new URL(rawUrl).pathname.toLowerCase();
42486
- } catch {
42487
- return "";
42488
- }
42489
- }
42490
-
42491
- // src/commands/videos/ingest.ts
42492
- registerSchema({
42493
- command: "videos.ingest",
42494
- description: "Add a video to the library from a URL \u2014 a direct file, or a YouTube/TikTok/Vimeo page.",
42495
- args: {
42496
- url: { type: "string", description: "Video URL (direct file or a watch/post page)", required: true },
42497
- source: { type: "string", description: "Provenance: url | youtube", required: false },
42498
- "external-id": { type: "string", description: "Provider asset id (deduped on)", required: false },
42499
- "external-url": { type: "string", description: "Canonical page URL", required: false },
42500
- download: {
42501
- type: "boolean",
42502
- description: "Force the download path instead of letting Baker fetch the URL directly",
42503
- required: false,
42504
- default: false
42505
- },
42506
- "dry-run": { type: "boolean", description: "Preview the operation without executing", required: false }
42507
- }
42508
- });
42509
- var ingestCommand2 = defineCommand200({
42510
- meta: {
42511
- name: "ingest",
42512
- description: "Add a video to the library from a URL. A direct file URL is handed straight to Baker, which fetches it. A page URL (YouTube, TikTok, Vimeo, Instagram) is downloaded here first, then uploaded \u2014 and a direct URL that Baker cannot fetch falls back to that same path automatically.\n\nExample: baker videos ingest https://www.youtube.com/watch?v=abc123"
42513
- },
42514
- args: {
42515
- url: { type: "positional", description: "Video URL", required: false },
42516
- source: { type: "string", description: "Provenance: url | youtube", required: false },
42517
- "external-id": { type: "string", description: "Provider asset id", required: false },
42518
- "external-url": { type: "string", description: "Canonical page URL", required: false },
42519
- download: { type: "boolean", description: "Force the download path", required: false, default: false },
42520
- "dry-run": { type: "boolean", description: "Preview without executing", required: false, default: false }
42521
- },
42522
- run: ({ args }) => runVideoIngest({
42523
- url: args.url,
42524
- source: args.source,
42525
- externalId: args["external-id"],
42526
- externalUrl: args["external-url"],
42527
- forceDownload: args.download === true,
42528
- dryRun: args["dry-run"] === true
42529
- })
42530
- });
42531
- async function runVideoIngest(opts) {
42532
- const url = opts.url;
42533
- if (!url) {
42534
- writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "URL is required" } });
42535
- process.exit(1);
42536
- }
42537
- const direct = !opts.forceDownload && isDirectMediaUrl(url);
42538
- const args = {
42539
- url,
42540
- source: resolveIngestSource(opts.source, url),
42541
- externalId: opts.externalId,
42542
- externalUrl: opts.externalUrl ?? (direct ? void 0 : url)
42543
- };
42544
- if (opts.dryRun) {
42545
- writeJson({
42546
- ok: true,
42547
- dryRun: true,
42548
- operation: "videos.ingest",
42549
- params: {
42550
- url,
42551
- source: args.source,
42552
- externalId: args.externalId ?? null,
42553
- externalUrl: args.externalUrl ?? null,
42554
- direct
42555
- }
42556
- });
42557
- return;
42558
- }
42559
- try {
42560
- writeJson({ ok: true, data: await ingestByRoute(args, direct) });
42561
- } catch (err) {
42562
- reportIngestFailure(err);
42563
- }
42564
- }
42565
- async function ingestByRoute(args, direct) {
42566
- if (!direct) {
42567
- return { ...await downloadThenIngest(args), via: "download" };
42568
- }
42569
- try {
42570
- return { ...await ingestUrl(args), via: "direct" };
42571
- } catch (err) {
42572
- if (!(err instanceof ApiError)) throw err;
42573
- return { ...await downloadThenIngest(args), via: "download", fallbackFrom: err.message };
42574
- }
42575
- }
42576
- function reportIngestFailure(err) {
42577
- if (err instanceof ApiError) {
42578
- writeJson({ ok: false, error: { code: err.code, message: err.message } });
42579
- process.exit(1);
42580
- }
42581
- if (err instanceof YtDlpError) {
42582
- const toolingOrBlock = isToolingOrBlockFailure(`${err.stderrTail} ${err.message}`);
42583
- writeJson({
42584
- ok: false,
42585
- error: {
42586
- code: "INGEST_FAILED",
42587
- message: `Couldn't download that video. ${err.stderrTail || err.message}`,
42588
- fix: toolingOrBlock ? "The site wouldn't hand over the video \u2014 usually the downloader being out of date against a site that changed, or the request being blocked. Retrying won't help, and the link is probably fine. Report it so the tool can be updated; meanwhile download the file and use `baker videos upload <file>`." : "Check the link is public and playable. Private, age-restricted, or region-locked videos can't be downloaded \u2014 download the file yourself and use `baker videos upload <file>`."
42589
- }
42590
- });
42591
- process.exit(1);
42592
- }
42593
- writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
42594
- process.exit(1);
42595
- }
42596
- function ingestUrl(args) {
42597
- const body = {
42598
- url: args.url,
42599
- source: args.source
42600
- };
42601
- if (args.externalId) body.externalId = args.externalId;
42602
- if (args.externalUrl) body.externalUrl = args.externalUrl;
42603
- return apiPost("/api/videos/ingest", body);
42604
- }
42605
- async function downloadThenIngest(args) {
42606
- const workDir = await mkdtemp2(path35.join(tmpdir3(), "videos-ingest-"));
42607
- try {
42608
- const { filePath, info } = await runYtDlp({
42609
- url: args.url,
42610
- audioOnly: false,
42611
- workDir,
42612
- maxHeight: INGEST_MAX_HEIGHT
42613
- });
42614
- const stats = await stat7(filePath);
42615
- if (stats.size > MAX_VIDEO_INGEST_BYTES) {
42616
- throw new ApiError(
42617
- "VALIDATION_ERROR",
42618
- `That video is ${Math.round(stats.size / 1e6)} MB, over the ${Math.round(MAX_VIDEO_INGEST_BYTES / 1e6)} MB limit for this route.`
42619
- );
42620
- }
42621
- const bytes = await readFile24(filePath);
42622
- const publicUrl = await uploadToAssetStore(bytes);
42623
- const externalId = args.externalId ?? (typeof info.id === "string" ? `${args.source}:${info.id}` : void 0);
42624
- return await ingestUrl({ ...args, url: publicUrl, externalId, externalUrl: args.externalUrl ?? args.url });
42625
- } finally {
42626
- await rm7(workDir, { recursive: true, force: true }).catch(() => {
42627
- });
42628
- }
42629
- }
42630
- async function uploadToAssetStore(bytes) {
42631
- const sha256 = sha256Hex(bytes);
42632
- const { putUrl, publicUrl } = await apiPost("/api/canvas/assets/presign", {
42633
- sha256,
42634
- mime: "video/mp4",
42635
- purpose: "source"
42636
- });
42637
- const putRes = await fetch(putUrl, {
42638
- method: "PUT",
42639
- body: new Uint8Array(bytes),
42640
- headers: { "Content-Type": "video/mp4" }
42641
- });
42642
- if (!putRes.ok) {
42643
- throw new ApiError("INTERNAL_ERROR", `Storing the downloaded video failed: HTTP ${putRes.status}`);
42644
- }
42645
- return publicUrl;
42646
- }
42647
-
42648
42524
  // src/commands/videos/search.ts
42649
- import { defineCommand as defineCommand201 } from "citty";
42525
+ import { defineCommand as defineCommand200 } from "citty";
42650
42526
  registerSchema({
42651
42527
  command: "videos.search",
42652
42528
  description: "Search videos by text query. Only returns ready videos.",
@@ -42656,7 +42532,7 @@ registerSchema({
42656
42532
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
42657
42533
  }
42658
42534
  });
42659
- var searchCommand4 = defineCommand201({
42535
+ var searchCommand4 = defineCommand200({
42660
42536
  meta: {
42661
42537
  name: "search",
42662
42538
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -42706,9 +42582,9 @@ var searchCommand4 = defineCommand201({
42706
42582
  var tagsCommand5 = makeTagsCommand("videos", "video", "/api/videos/tags");
42707
42583
 
42708
42584
  // src/commands/videos/upload.ts
42709
- import { readFile as readFile25, stat as stat8 } from "fs/promises";
42585
+ import { readFile as readFile24, stat as stat7 } from "fs/promises";
42710
42586
  import { basename as basename3, extname as extname4 } from "path";
42711
- import { defineCommand as defineCommand202 } from "citty";
42587
+ import { defineCommand as defineCommand201 } from "citty";
42712
42588
  var MIME_MAP = {
42713
42589
  ".mp4": "video/mp4",
42714
42590
  ".mov": "video/quicktime",
@@ -42718,9 +42594,9 @@ var MIME_MAP = {
42718
42594
  };
42719
42595
  registerSchema({
42720
42596
  command: "videos.upload",
42721
- description: "Upload a video to Baker \u2014 a local file (via Mux direct upload) or a remote http(s) URL.",
42597
+ description: "Upload a video file to Baker (via Mux direct upload)",
42722
42598
  args: {
42723
- file: { type: "string", description: "Local video file path, or a remote http(s) URL", required: true },
42599
+ file: { type: "string", description: "Path to the video file", required: true },
42724
42600
  "content-type": {
42725
42601
  type: "string",
42726
42602
  description: "MIME type (auto-detected from extension if omitted)",
@@ -42747,49 +42623,33 @@ function detectContentType(filePath) {
42747
42623
  }
42748
42624
  return mime;
42749
42625
  }
42750
- function isRemoteUrl3(value) {
42751
- return /^https?:\/\//i.test(value);
42752
- }
42753
- var uploadCommand2 = defineCommand202({
42626
+ var uploadCommand2 = defineCommand201({
42754
42627
  meta: {
42755
42628
  name: "upload",
42756
- description: "Upload a video to Baker \u2014 accepts a local file path OR a remote http(s) URL.\n\nLocal: auto-detects content type and uploads via Mux direct upload.\nRemote: hands off to `videos ingest` (direct fetch, or download-then-upload for a YouTube/TikTok/Vimeo page).\n\nExamples:\n baker videos upload ./demo.mp4\n baker videos upload https://www.youtube.com/watch?v=abc123"
42629
+ description: "Upload a video file to Baker via Mux direct upload. Auto-detects content type. Example: baker videos upload ./demo.mp4"
42757
42630
  },
42758
42631
  args: {
42759
- file: { type: "positional", description: "Local video file path or remote http(s) URL", required: false },
42760
- "content-type": { type: "string", description: "MIME type (local only \u2014 auto-detected)", required: false },
42632
+ file: { type: "positional", description: "Path to the video file", required: false },
42633
+ "content-type": { type: "string", description: "MIME type (auto-detected if omitted)", required: false },
42761
42634
  context: {
42762
42635
  type: "string",
42763
42636
  description: "What this clip is and what it is for \u2014 who is on camera, which campaign, how it was shot. Start here: the analysis reads it, so it is what makes the clip findable later.",
42764
42637
  required: false
42765
42638
  },
42766
- source: { type: "string", description: "Provenance (URL mode only): url | youtube", required: false },
42767
- "external-id": { type: "string", description: "Provider asset id (URL mode only)", required: false },
42768
- "external-url": { type: "string", description: "Canonical page URL (URL mode only)", required: false },
42769
42639
  "dry-run": { type: "boolean", description: "Preview without executing", required: false, default: false }
42770
42640
  },
42771
42641
  run: async ({ args }) => {
42772
42642
  try {
42773
42643
  const filePath = args.file;
42774
42644
  if (!filePath) {
42775
- writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "File path or URL is required" } });
42645
+ writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "File path is required" } });
42776
42646
  process.exit(1);
42777
42647
  }
42778
- if (isRemoteUrl3(filePath)) {
42779
- await runVideoIngest({
42780
- url: filePath,
42781
- source: args.source,
42782
- externalId: args["external-id"],
42783
- externalUrl: args["external-url"],
42784
- dryRun: args["dry-run"] === true
42785
- });
42786
- return;
42787
- }
42788
42648
  const contentType = args["content-type"] || detectContentType(filePath);
42789
42649
  const originalFilename = basename3(filePath);
42790
42650
  const descriptionContext = args.context;
42791
42651
  if (args["dry-run"]) {
42792
- const fileStats = await stat8(filePath);
42652
+ const fileStats = await stat7(filePath);
42793
42653
  writeJson({
42794
42654
  ok: true,
42795
42655
  dryRun: true,
@@ -42802,7 +42662,7 @@ var uploadCommand2 = defineCommand202({
42802
42662
  originalFilename,
42803
42663
  descriptionContext
42804
42664
  });
42805
- const fileBuffer = await readFile25(filePath);
42665
+ const fileBuffer = await readFile24(filePath);
42806
42666
  const uploadResponse = await fetch(uploadUrl, {
42807
42667
  method: "PUT",
42808
42668
  headers: { "Content-Type": contentType },
@@ -42833,17 +42693,16 @@ var uploadCommand2 = defineCommand202({
42833
42693
  });
42834
42694
 
42835
42695
  // src/commands/videos/index.ts
42836
- var videosCommand = defineCommand203({
42696
+ var videosCommand = defineCommand202({
42837
42697
  meta: {
42838
42698
  name: "videos",
42839
- description: `Find and manage videos in Baker. Subcommands: search, get, upload, ingest, delete, tags.
42699
+ description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete, tags.
42840
42700
 
42841
42701
  Examples:
42842
42702
  baker videos search "product demo" --limit 5
42843
42703
  baker videos search "tutorial" --tags explainer
42844
42704
  baker videos get <video-id>
42845
42705
  baker videos upload ./demo.mp4
42846
- baker videos ingest https://www.youtube.com/watch?v=abc123
42847
42706
  baker videos delete <video-id> --dry-run
42848
42707
  baker videos tags
42849
42708
  Full guide: __tooling__/docs/tools/baker/videos.md`
@@ -42853,17 +42712,16 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
42853
42712
  group: groupCommand2,
42854
42713
  search: searchCommand4,
42855
42714
  upload: uploadCommand2,
42856
- ingest: ingestCommand2,
42857
42715
  delete: deleteCommand3,
42858
42716
  tags: tagsCommand5
42859
42717
  }
42860
42718
  });
42861
42719
 
42862
42720
  // src/commands/winning-ads/index.ts
42863
- import { defineCommand as defineCommand216 } from "citty";
42721
+ import { defineCommand as defineCommand215 } from "citty";
42864
42722
 
42865
42723
  // src/commands/winning-ads/advertisers.ts
42866
- import { defineCommand as defineCommand204 } from "citty";
42724
+ import { defineCommand as defineCommand203 } from "citty";
42867
42725
 
42868
42726
  // src/commands/winning-ads/shared.ts
42869
42727
  function splitList2(value) {
@@ -42916,7 +42774,7 @@ function advertiserNormalizer(record, full) {
42916
42774
  last_synced_at: record.last_synced_at ?? null
42917
42775
  };
42918
42776
  }
42919
- var advertisersCommand2 = defineCommand204({
42777
+ var advertisersCommand2 = defineCommand203({
42920
42778
  meta: {
42921
42779
  name: "advertisers",
42922
42780
  description: 'List corpus advertisers by name or domain. Find your own advertiser for --exclude-advertiser, or a competitor for --advertiser-id / winners. Example: baker winning-ads advertisers "Deel" --output md'
@@ -42974,7 +42832,7 @@ var advertisersCommand2 = defineCommand204({
42974
42832
  });
42975
42833
 
42976
42834
  // src/commands/winning-ads/brief.ts
42977
- import { defineCommand as defineCommand205 } from "citty";
42835
+ import { defineCommand as defineCommand204 } from "citty";
42978
42836
  registerSchema({
42979
42837
  command: "winning-ads.brief",
42980
42838
  description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
@@ -43020,7 +42878,7 @@ function parseDna(raw) {
43020
42878
  }
43021
42879
  return parsed;
43022
42880
  }
43023
- var briefCommand = defineCommand205({
42881
+ var briefCommand = defineCommand204({
43024
42882
  meta: {
43025
42883
  name: "brief",
43026
42884
  description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
@@ -43056,7 +42914,7 @@ var briefCommand = defineCommand205({
43056
42914
  });
43057
42915
 
43058
42916
  // src/commands/winning-ads/content.ts
43059
- import { defineCommand as defineCommand206 } from "citty";
42917
+ import { defineCommand as defineCommand205 } from "citty";
43060
42918
  registerSchema({
43061
42919
  command: "winning-ads.content",
43062
42920
  description: "Read what's INSIDE one winning ad: the spoken transcript, the on-screen text, and the ad copy. Use this after `search`/`winners`/`feed` return a shortlist \u2014 pass an ad_id to understand a reference before reproducing it. Add --full for speech, pacing, and soundtrack detail. Video ads carry the transcript/on-screen text; static ads carry only the copy.",
@@ -43069,7 +42927,7 @@ registerSchema({
43069
42927
  }
43070
42928
  }
43071
42929
  });
43072
- var contentCommand = defineCommand206({
42930
+ var contentCommand = defineCommand205({
43073
42931
  meta: {
43074
42932
  name: "content",
43075
42933
  description: "Read the transcript + on-screen text + copy of one winning ad. Example: baker winning-ads content adg_123 --platform meta --full --output md"
@@ -43118,7 +42976,7 @@ var contentCommand = defineCommand206({
43118
42976
  });
43119
42977
 
43120
42978
  // src/commands/winning-ads/feed.ts
43121
- import { defineCommand as defineCommand207 } from "citty";
42979
+ import { defineCommand as defineCommand206 } from "citty";
43122
42980
  function buildFeedParams(input) {
43123
42981
  const params = {};
43124
42982
  const advertiser = splitList2(input.advertiser);
@@ -43170,7 +43028,7 @@ registerSchema({
43170
43028
  format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
43171
43029
  }
43172
43030
  });
43173
- var feedCommand = defineCommand207({
43031
+ var feedCommand = defineCommand206({
43174
43032
  meta: {
43175
43033
  name: "feed",
43176
43034
  description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
@@ -43255,7 +43113,7 @@ var feedCommand = defineCommand207({
43255
43113
  });
43256
43114
 
43257
43115
  // src/commands/winning-ads/follow.ts
43258
- import { defineCommand as defineCommand208 } from "citty";
43116
+ import { defineCommand as defineCommand207 } from "citty";
43259
43117
  var PLATFORMS = ["meta", "linkedin"];
43260
43118
  registerSchema({
43261
43119
  command: "winning-ads.follow",
@@ -43270,7 +43128,7 @@ registerSchema({
43270
43128
  label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
43271
43129
  }
43272
43130
  });
43273
- var followCommand = defineCommand208({
43131
+ var followCommand = defineCommand207({
43274
43132
  meta: {
43275
43133
  name: "follow",
43276
43134
  description: 'Follow a brand to track ALL its ads \u2014 every platform and country. --platform is how we read your input, not a limit. A domain tracks both Meta + LinkedIn. Example: baker winning-ads follow "deel.com" --platform meta'
@@ -43317,7 +43175,7 @@ var followCommand = defineCommand208({
43317
43175
  });
43318
43176
 
43319
43177
  // src/commands/winning-ads/follow-competitors.ts
43320
- import { defineCommand as defineCommand209 } from "citty";
43178
+ import { defineCommand as defineCommand208 } from "citty";
43321
43179
  var PLATFORMS2 = ["meta", "linkedin"];
43322
43180
  var BATCH_TIMEOUT_MS = 3e5;
43323
43181
  function buildFollowBatchBody(input) {
@@ -43350,7 +43208,7 @@ registerSchema({
43350
43208
  }
43351
43209
  }
43352
43210
  });
43353
- var followCompetitorsCommand = defineCommand209({
43211
+ var followCompetitorsCommand = defineCommand208({
43354
43212
  meta: {
43355
43213
  name: "follow-competitors",
43356
43214
  description: 'Follow many brands at once by domain \u2014 add every competitor in one call. Example: baker winning-ads follow-competitors "deel.com,notion.so,hubspot.com"'
@@ -43425,7 +43283,7 @@ var followCompetitorsCommand = defineCommand209({
43425
43283
  });
43426
43284
 
43427
43285
  // src/commands/winning-ads/following.ts
43428
- import { defineCommand as defineCommand210 } from "citty";
43286
+ import { defineCommand as defineCommand209 } from "citty";
43429
43287
  registerSchema({
43430
43288
  command: "winning-ads.following",
43431
43289
  description: "List the brands you follow in your ad-dna library, with each one's status (ready vs still adding) and cached ad counts.",
@@ -43458,7 +43316,7 @@ function followingNormalizer(record, full) {
43458
43316
  platforms: Array.isArray(record.platforms) ? record.platforms : []
43459
43317
  };
43460
43318
  }
43461
- var followingCommand = defineCommand210({
43319
+ var followingCommand = defineCommand209({
43462
43320
  meta: {
43463
43321
  name: "following",
43464
43322
  description: "List brands you follow, with status (ready / adding\u2026) and cached counts. Example: baker winning-ads following --output md"
@@ -43493,7 +43351,7 @@ var followingCommand = defineCommand210({
43493
43351
  });
43494
43352
 
43495
43353
  // src/commands/winning-ads/patterns.ts
43496
- import { defineCommand as defineCommand211 } from "citty";
43354
+ import { defineCommand as defineCommand210 } from "citty";
43497
43355
  registerSchema({
43498
43356
  command: "winning-ads.patterns",
43499
43357
  description: "Mine what separates two cohorts of ads: pass a comma-list of winning ad ids (--winners) and a comma-list of weaker ad ids (--duds). Returns the discriminating DNA fields.",
@@ -43532,7 +43390,7 @@ function discriminatorRow(record) {
43532
43390
  top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
43533
43391
  };
43534
43392
  }
43535
- var patternsCommand = defineCommand211({
43393
+ var patternsCommand = defineCommand210({
43536
43394
  meta: {
43537
43395
  name: "patterns",
43538
43396
  description: "Discover what separates winning ads from weak ones. Example: baker winning-ads patterns --winners a_1,a_2,a_3 --duds a_9,a_8 --output md"
@@ -43588,7 +43446,7 @@ var patternsCommand = defineCommand211({
43588
43446
  });
43589
43447
 
43590
43448
  // src/commands/winning-ads/search.ts
43591
- import { defineCommand as defineCommand212 } from "citty";
43449
+ import { defineCommand as defineCommand211 } from "citty";
43592
43450
  registerSchema({
43593
43451
  command: "winning-ads.search",
43594
43452
  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.",
@@ -43696,7 +43554,7 @@ function buildSearchBody2(args) {
43696
43554
  }
43697
43555
  return body;
43698
43556
  }
43699
- var searchCommand5 = defineCommand212({
43557
+ var searchCommand5 = defineCommand211({
43700
43558
  meta: {
43701
43559
  name: "search",
43702
43560
  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"
@@ -43811,7 +43669,7 @@ var searchCommand5 = defineCommand212({
43811
43669
  });
43812
43670
 
43813
43671
  // src/commands/winning-ads/seeds.ts
43814
- import { defineCommand as defineCommand213 } from "citty";
43672
+ import { defineCommand as defineCommand212 } from "citty";
43815
43673
  function leanRow(r) {
43816
43674
  return {
43817
43675
  key: r.key,
@@ -43839,7 +43697,7 @@ function makeSeedCommand(opts) {
43839
43697
  limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
43840
43698
  }
43841
43699
  });
43842
- return defineCommand213({
43700
+ return defineCommand212({
43843
43701
  meta: { name: opts.name, description: opts.description },
43844
43702
  args: {
43845
43703
  platform: { type: "string", description: "Single platform to segment on", required: false },
@@ -43888,7 +43746,7 @@ var formatsCommand = makeSeedCommand({
43888
43746
  });
43889
43747
 
43890
43748
  // src/commands/winning-ads/unfollow.ts
43891
- import { defineCommand as defineCommand214 } from "citty";
43749
+ import { defineCommand as defineCommand213 } from "citty";
43892
43750
  registerSchema({
43893
43751
  command: "winning-ads.unfollow",
43894
43752
  description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
@@ -43896,7 +43754,7 @@ registerSchema({
43896
43754
  advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
43897
43755
  }
43898
43756
  });
43899
- var unfollowCommand = defineCommand214({
43757
+ var unfollowCommand = defineCommand213({
43900
43758
  meta: {
43901
43759
  name: "unfollow",
43902
43760
  description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
@@ -43917,7 +43775,7 @@ var unfollowCommand = defineCommand214({
43917
43775
  });
43918
43776
 
43919
43777
  // src/commands/winning-ads/winners.ts
43920
- import { defineCommand as defineCommand215 } from "citty";
43778
+ import { defineCommand as defineCommand214 } from "citty";
43921
43779
  registerSchema({
43922
43780
  command: "winning-ads.winners",
43923
43781
  description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
@@ -43927,7 +43785,7 @@ registerSchema({
43927
43785
  platform: { type: "string", description: "Filter to a single platform: meta|linkedin", required: false }
43928
43786
  }
43929
43787
  });
43930
- var winnersCommand = defineCommand215({
43788
+ var winnersCommand = defineCommand214({
43931
43789
  meta: {
43932
43790
  name: "winners",
43933
43791
  description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
@@ -43977,7 +43835,7 @@ var winnersCommand = defineCommand215({
43977
43835
  });
43978
43836
 
43979
43837
  // src/commands/winning-ads/index.ts
43980
- var winningAdsCommand = defineCommand216({
43838
+ var winningAdsCommand = defineCommand215({
43981
43839
  meta: {
43982
43840
  name: "winning-ads",
43983
43841
  description: `Search the ad-dna corpus of scored "winning" ads for reference creatives to reproduce, and manage the brands your library tracks. Proxied through the Baker backend (BAKER_API_KEY) \u2014 no separate token needed.
@@ -44049,7 +43907,7 @@ function getCliVersion() {
44049
43907
  }
44050
43908
 
44051
43909
  // src/cli.ts
44052
- var main = defineCommand217({
43910
+ var main = defineCommand216({
44053
43911
  meta: {
44054
43912
  name: "baker",
44055
43913
  version: getCliVersion(),