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

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