@koda-sl/baker-cli 0.115.0-dev.3bcc79f9c → 0.116.0-dev.3bcc79f9c

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
@@ -157,9 +157,9 @@ async function handleResponse(response) {
157
157
  throw new ApiError("INTERNAL_ERROR", "Failed to parse API response as JSON");
158
158
  }
159
159
  }
160
- async function apiGet(path14, params) {
160
+ async function apiGet(path13, params) {
161
161
  const env = getEnv();
162
- const url = new URL(path14, env.BAKER_API_URL);
162
+ const url = new URL(path13, env.BAKER_API_URL);
163
163
  if (params) {
164
164
  const clean = sanitizeParams(params);
165
165
  for (const [key, value] of Object.entries(clean)) {
@@ -184,12 +184,12 @@ async function apiGet(path14, params) {
184
184
  }
185
185
  return handleResponse(response);
186
186
  }
187
- async function apiPost(path14, body, opts) {
187
+ async function apiPost(path13, body, opts) {
188
188
  const env = getEnv();
189
189
  const timeoutMs = opts?.timeoutMs ?? 6e4;
190
190
  let response;
191
191
  try {
192
- response = await fetchWithRateLimitRetry(new URL(path14, env.BAKER_API_URL).toString(), {
192
+ response = await fetchWithRateLimitRetry(new URL(path13, env.BAKER_API_URL).toString(), {
193
193
  method: "POST",
194
194
  headers: {
195
195
  Authorization: `Bearer ${env.BAKER_API_KEY}`,
@@ -2881,31 +2881,31 @@ function cachePath(category, key) {
2881
2881
  return join2(dir, `${hashKey(key)}.json`);
2882
2882
  }
2883
2883
  function cacheGet(category, key) {
2884
- const path14 = cachePath(category, key);
2885
- if (!existsSync2(path14)) {
2884
+ const path13 = cachePath(category, key);
2885
+ if (!existsSync2(path13)) {
2886
2886
  return null;
2887
2887
  }
2888
2888
  try {
2889
- const raw = readFileSync2(path14, "utf-8");
2889
+ const raw = readFileSync2(path13, "utf-8");
2890
2890
  const entry = JSON.parse(raw);
2891
2891
  if (entry.expiresAt < Date.now()) {
2892
- rmSync(path14, { force: true });
2892
+ rmSync(path13, { force: true });
2893
2893
  return null;
2894
2894
  }
2895
2895
  return entry;
2896
2896
  } catch {
2897
- rmSync(path14, { force: true });
2897
+ rmSync(path13, { force: true });
2898
2898
  return null;
2899
2899
  }
2900
2900
  }
2901
2901
  function cacheSet(category, key, data, ttlMs, fields) {
2902
- const path14 = cachePath(category, key);
2902
+ const path13 = cachePath(category, key);
2903
2903
  const entry = {
2904
2904
  expiresAt: Date.now() + ttlMs,
2905
2905
  data,
2906
2906
  fields
2907
2907
  };
2908
- writeFileSync(path14, JSON.stringify(entry), "utf-8");
2908
+ writeFileSync(path13, JSON.stringify(entry), "utf-8");
2909
2909
  }
2910
2910
  var HOUR = 60 * 60 * 1e3;
2911
2911
  var MINUTE = 60 * 1e3;
@@ -4818,11 +4818,11 @@ function rawTextEntries(value) {
4818
4818
  const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
4819
4819
  return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
4820
4820
  }
4821
- function rawFileEntries(path14) {
4822
- if (typeof path14 !== "string" || path14.length === 0) {
4821
+ function rawFileEntries(path13) {
4822
+ if (typeof path13 !== "string" || path13.length === 0) {
4823
4823
  return [];
4824
4824
  }
4825
- return readFileSync3(path14, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
4825
+ return readFileSync3(path13, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
4826
4826
  }
4827
4827
  function keywordEntries(args) {
4828
4828
  const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
@@ -4845,19 +4845,19 @@ function keywordEntries(args) {
4845
4845
  }
4846
4846
  return entries;
4847
4847
  }
4848
- function loadJsonFileArg(path14) {
4849
- if (typeof path14 !== "string" || path14.length === 0) {
4848
+ function loadJsonFileArg(path13) {
4849
+ if (typeof path13 !== "string" || path13.length === 0) {
4850
4850
  return {};
4851
4851
  }
4852
4852
  try {
4853
- const parsed = JSON.parse(readFileSync3(path14, "utf8"));
4853
+ const parsed = JSON.parse(readFileSync3(path13, "utf8"));
4854
4854
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
4855
- failWriteValidation(`${path14} must contain a JSON object`);
4855
+ failWriteValidation(`${path13} must contain a JSON object`);
4856
4856
  }
4857
4857
  return parsed;
4858
4858
  } catch (err) {
4859
4859
  if (err instanceof SyntaxError) {
4860
- failWriteValidation(`${path14} is not valid JSON: ${err.message}`);
4860
+ failWriteValidation(`${path13} is not valid JSON: ${err.message}`);
4861
4861
  }
4862
4862
  throw err;
4863
4863
  }
@@ -4946,10 +4946,10 @@ async function stageUpdate(kind, customerId, target, payload) {
4946
4946
  async function stageTarget(kind, customerId, target) {
4947
4947
  await stageGoogleOp({ kind, customerId, target });
4948
4948
  }
4949
- async function draftAction(path14, body) {
4949
+ async function draftAction(path13, body) {
4950
4950
  try {
4951
4951
  const chatId = requireChatId();
4952
- const response = await apiPost(path14, { chatId, ...body });
4952
+ const response = await apiPost(path13, { chatId, ...body });
4953
4953
  writeJsonEnvelope(response);
4954
4954
  } catch (err) {
4955
4955
  handleGoogleError(err);
@@ -8490,19 +8490,19 @@ function failWriteValidation2(message) {
8490
8490
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
8491
8491
  process.exit(1);
8492
8492
  }
8493
- function loadJsonFileArg2(path14) {
8494
- if (typeof path14 !== "string" || path14.length === 0) {
8493
+ function loadJsonFileArg2(path13) {
8494
+ if (typeof path13 !== "string" || path13.length === 0) {
8495
8495
  return {};
8496
8496
  }
8497
8497
  try {
8498
- const parsed = JSON.parse(readFileSync7(path14, "utf8"));
8498
+ const parsed = JSON.parse(readFileSync7(path13, "utf8"));
8499
8499
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
8500
- failWriteValidation2(`${path14} must contain a JSON object`);
8500
+ failWriteValidation2(`${path13} must contain a JSON object`);
8501
8501
  }
8502
8502
  return parsed;
8503
8503
  } catch (err) {
8504
8504
  if (err instanceof SyntaxError) {
8505
- failWriteValidation2(`${path14} is not valid JSON: ${err.message}`);
8505
+ failWriteValidation2(`${path13} is not valid JSON: ${err.message}`);
8506
8506
  }
8507
8507
  throw err;
8508
8508
  }
@@ -8565,15 +8565,15 @@ function parseLocaleFlag(value) {
8565
8565
  }
8566
8566
  return { language: match[1], country: match[2].toUpperCase() };
8567
8567
  }
8568
- function loadTargetingFileArg(path14) {
8569
- if (typeof path14 !== "string" || path14.length === 0) {
8568
+ function loadTargetingFileArg(path13) {
8569
+ if (typeof path13 !== "string" || path13.length === 0) {
8570
8570
  return void 0;
8571
8571
  }
8572
- const parsed = loadJsonFileArg2(path14);
8572
+ const parsed = loadJsonFileArg2(path13);
8573
8573
  const criteria = parsed.targetingCriteria ?? parsed;
8574
8574
  if (!criteria.include) {
8575
8575
  failWriteValidation2(
8576
- `${path14} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
8576
+ `${path13} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
8577
8577
  );
8578
8578
  }
8579
8579
  return criteria;
@@ -8608,14 +8608,14 @@ function parseCsvLine(line) {
8608
8608
  cells.push(current);
8609
8609
  return cells.map((cell) => cell.trim());
8610
8610
  }
8611
- function parseListFileArg(path14, maxRows) {
8612
- if (typeof path14 !== "string" || path14.length === 0) {
8611
+ function parseListFileArg(path13, maxRows) {
8612
+ if (typeof path13 !== "string" || path13.length === 0) {
8613
8613
  return void 0;
8614
8614
  }
8615
- const raw = readFileSync7(path14, "utf8");
8615
+ const raw = readFileSync7(path13, "utf8");
8616
8616
  const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
8617
8617
  if (lines.length < 2) {
8618
- failWriteValidation2(`${path14} needs a header row and at least one data row`);
8618
+ failWriteValidation2(`${path13} needs a header row and at least one data row`);
8619
8619
  }
8620
8620
  const columns = parseCsvLine(lines[0]).map((column) => column.trim());
8621
8621
  const rows = [];
@@ -8634,7 +8634,7 @@ function parseListFileArg(path14, maxRows) {
8634
8634
  }
8635
8635
  }
8636
8636
  if (rows.length > maxRows) {
8637
- failWriteValidation2(`${path14} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
8637
+ failWriteValidation2(`${path13} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
8638
8638
  }
8639
8639
  return { columns, rows };
8640
8640
  }
@@ -13154,8 +13154,8 @@ function buildRecorder() {
13154
13154
  }
13155
13155
 
13156
13156
  // src/commands/canvas/scaffold-static-ad.ts
13157
- import { access, cp, mkdir, readFile as readFile3, writeFile } from "fs/promises";
13158
- import path8 from "path";
13157
+ import { readFile as readFile3, writeFile } from "fs/promises";
13158
+ import path7 from "path";
13159
13159
  import { defineCommand as defineCommand86 } from "citty";
13160
13160
 
13161
13161
  // src/engine/scaffold/staticAd.ts
@@ -13341,77 +13341,15 @@ function staticAdReport(input, elementsInput, opts) {
13341
13341
  };
13342
13342
  }
13343
13343
 
13344
- // src/commands/canvas/creative-definition.ts
13345
- import path6 from "path";
13346
- var PLATFORM_VALUES = ["meta", "google", "linkedin", "tiktok", "youtube", "x", "other"];
13347
- var FORMAT_VALUES = ["1:1", "4:5", "9:16", "16:9", "1.91:1"];
13348
- function titleFromSlug(slug) {
13349
- const title = slug.split("-").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
13350
- return title || slug;
13351
- }
13352
- function resolvePlatform(platform) {
13353
- const value = platform?.trim();
13354
- return value && PLATFORM_VALUES.includes(value) ? value : "meta";
13355
- }
13356
- function resolveFormats(aspect) {
13357
- const value = aspect?.trim();
13358
- return value && FORMAT_VALUES.includes(value) ? [value] : ["4:5"];
13359
- }
13360
- function referenceRelativePath(kind, ext) {
13361
- const name = kind === "video" ? "source" : "original";
13362
- return `references/${name}${ext}`;
13363
- }
13364
- function sourceExtension(source, isUrl, kind) {
13365
- const raw = isUrl ? urlPathname(source) : source;
13366
- const ext = path6.extname(raw).toLowerCase();
13367
- if (/^\.[a-z0-9]{1,5}$/.test(ext)) return ext;
13368
- return kind === "video" ? ".mp4" : ".jpg";
13369
- }
13370
- function urlPathname(source) {
13371
- try {
13372
- return new URL(source).pathname;
13373
- } catch {
13374
- return source;
13375
- }
13376
- }
13377
- function describeBlueprintIntent(blueprint) {
13378
- const intent = blueprint?.ad_intent;
13379
- if (typeof intent === "string" && intent.trim()) return intent.trim();
13380
- if (intent && typeof intent === "object") {
13381
- const summary = intent.summary ?? intent.feeling;
13382
- if (typeof summary === "string" && summary.trim()) return summary.trim();
13383
- }
13384
- return void 0;
13385
- }
13386
- function yamlScalar(value) {
13387
- return JSON.stringify(value);
13388
- }
13389
- function buildCreativeDefinition(input) {
13390
- const lines = ["---", `title: ${yamlScalar(input.title)}`, `kind: ${input.kind}`, `platform: ${input.platform}`];
13391
- lines.push(`formats: [${input.formats.map(yamlScalar).join(", ")}]`);
13392
- lines.push(`status: ${input.status ?? "draft"}`);
13393
- if (input.sourceReferenceUrl) lines.push(`sourceReferenceUrl: ${yamlScalar(input.sourceReferenceUrl)}`);
13394
- if (input.sourceAdvertiser) lines.push(`sourceAdvertiser: ${yamlScalar(input.sourceAdvertiser)}`);
13395
- if (input.sourceKind) lines.push(`sourceKind: ${input.sourceKind}`);
13396
- if (input.sourcePath) lines.push(`sourcePath: ${yamlScalar(input.sourcePath)}`);
13397
- lines.push("---", "");
13398
- lines.push(input.description?.trim() || `${input.title} \u2014 canvas-built ${input.kind} ad for ${input.platform}.`);
13399
- lines.push("");
13400
- return lines.join("\n");
13401
- }
13402
-
13403
13344
  // src/commands/canvas/scaffold-static-ad-paths.ts
13404
- import path7 from "path";
13345
+ import path6 from "path";
13405
13346
  function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd(), slug) {
13406
13347
  const file = rawFile.trim();
13407
13348
  const imageIsUrl = /^https?:\/\//i.test(file);
13408
- const imageSource = imageIsUrl ? file : path7.resolve(cwd, file);
13409
- const outPath = out ? path7.resolve(cwd, out) : slug ? path7.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path7.join(cwd, "static-ad.canvas.json") : path7.join(path7.dirname(imageSource), "static-ad.canvas.json");
13410
- const blueprintPath = path7.join(path7.dirname(outPath), "prompt.json");
13411
- const creativeDir = slug ? path7.dirname(outPath) : null;
13412
- const definitionPath = creativeDir ? path7.join(creativeDir, "_definition.md") : null;
13413
- const referencesDir = creativeDir ? path7.join(creativeDir, "references") : null;
13414
- return { imageIsUrl, imageSource, outPath, blueprintPath, creativeDir, definitionPath, referencesDir };
13349
+ const imageSource = imageIsUrl ? file : path6.resolve(cwd, file);
13350
+ const outPath = out ? path6.resolve(cwd, out) : slug ? path6.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path6.join(cwd, "static-ad.canvas.json") : path6.join(path6.dirname(imageSource), "static-ad.canvas.json");
13351
+ const blueprintPath = path6.join(path6.dirname(outPath), "prompt.json");
13352
+ return { imageIsUrl, imageSource, outPath, blueprintPath };
13415
13353
  }
13416
13354
  var SCAFFOLD_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
13417
13355
  var SCAFFOLD_SLUG_MAX_LENGTH = 100;
@@ -13420,27 +13358,6 @@ function isValidScaffoldSlug(slug) {
13420
13358
  }
13421
13359
 
13422
13360
  // src/commands/canvas/scaffold-static-ad.ts
13423
- async function fileExists(target) {
13424
- try {
13425
- await access(target);
13426
- return true;
13427
- } catch {
13428
- return false;
13429
- }
13430
- }
13431
- async function copySourceIntoReferences(source, isUrl, referencesDir) {
13432
- await mkdir(referencesDir, { recursive: true });
13433
- const relPath = referenceRelativePath("image", sourceExtension(source, isUrl, "image"));
13434
- const dest = path8.join(referencesDir, path8.basename(relPath));
13435
- if (isUrl) {
13436
- const res = await fetch(source);
13437
- if (!res.ok) throw new Error(`failed to download source image (${res.status})`);
13438
- await writeFile(dest, Buffer.from(await res.arrayBuffer()));
13439
- } else {
13440
- await cp(source, dest);
13441
- }
13442
- return relPath;
13443
- }
13444
13361
  function resolveModel(kind, preferred) {
13445
13362
  const ids = Object.keys(MODEL_REGISTRY[kind]);
13446
13363
  return ids.includes(preferred) ? preferred : ids[0] ?? preferred;
@@ -13604,12 +13521,6 @@ var scaffoldStaticAdCommand = defineCommand86({
13604
13521
  type: "string",
13605
13522
  description: "Creative slug \u2014 writes the canvas to src/creatives/<slug>/<slug>.canvas.json (repo convention)"
13606
13523
  },
13607
- title: { type: "string", description: "Creative title for _definition.md (default: title-cased slug)" },
13608
- platform: {
13609
- type: "string",
13610
- description: "Ad platform for _definition.md (meta|google|linkedin|tiktok|youtube|x|other; default meta)"
13611
- },
13612
- advertiser: { type: "string", description: "Source advertiser recorded in _definition.md" },
13613
13524
  "describe-model": { type: "string", description: "Override the image_describe model id" },
13614
13525
  "select-model": { type: "string", description: "Override the text_generate model id for element selection" },
13615
13526
  "layout-model": { type: "string", description: "Override the text_generate model id for the layout pass" },
@@ -13626,13 +13537,12 @@ var scaffoldStaticAdCommand = defineCommand86({
13626
13537
  );
13627
13538
  process.exit(2);
13628
13539
  }
13629
- const { imageIsUrl, imageSource, outPath, blueprintPath, definitionPath, referencesDir } = resolveScaffoldStaticAdPaths(
13540
+ const { imageIsUrl, imageSource, outPath, blueprintPath } = resolveScaffoldStaticAdPaths(
13630
13541
  String(args.file),
13631
13542
  args.out ? String(args.out) : void 0,
13632
13543
  process.cwd(),
13633
13544
  slug
13634
13545
  );
13635
- await mkdir(path8.dirname(outPath), { recursive: true });
13636
13546
  const { describeModel, selectModel, layoutModel, genModel } = resolveModels(args);
13637
13547
  const describeCanvas = buildDescribeCanvas(
13638
13548
  imageSource,
@@ -13649,21 +13559,11 @@ var scaffoldStaticAdCommand = defineCommand86({
13649
13559
  }
13650
13560
  await writeFile(blueprintPath, `${JSON.stringify(annotated, null, 2)}
13651
13561
  `, "utf8");
13652
- let canvasImagePath = imageSource;
13653
- let canvasImageIsUrl = imageIsUrl;
13654
- let canvasBlueprintPath = blueprintPath;
13655
- let sourceRelPath;
13656
- if (referencesDir) {
13657
- sourceRelPath = await copySourceIntoReferences(imageSource, imageIsUrl, referencesDir);
13658
- canvasImagePath = sourceRelPath;
13659
- canvasImageIsUrl = false;
13660
- canvasBlueprintPath = "./prompt.json";
13661
- }
13662
13562
  const opts = {
13663
13563
  genModel,
13664
- imagePath: canvasImagePath,
13665
- imageIsUrl: canvasImageIsUrl,
13666
- blueprintPath: canvasBlueprintPath,
13564
+ imagePath: imageSource,
13565
+ imageIsUrl,
13566
+ blueprintPath,
13667
13567
  aspectRatio: args.aspect ? String(args.aspect) : void 0,
13668
13568
  includeFont: !args["skip-font"]
13669
13569
  };
@@ -13685,31 +13585,12 @@ var scaffoldStaticAdCommand = defineCommand86({
13685
13585
  }
13686
13586
  await writeFile(outPath, `${JSON.stringify(canvas, null, 2)}
13687
13587
  `, "utf8");
13688
- if (definitionPath && !await fileExists(definitionPath)) {
13689
- await writeFile(
13690
- definitionPath,
13691
- buildCreativeDefinition({
13692
- title: args.title ? String(args.title) : titleFromSlug(slug ?? ""),
13693
- kind: "static",
13694
- platform: resolvePlatform(args.platform ? String(args.platform) : void 0),
13695
- formats: resolveFormats(args.aspect ? String(args.aspect) : report.aspect_ratio),
13696
- sourceReferenceUrl: imageIsUrl ? imageSource : void 0,
13697
- sourceAdvertiser: args.advertiser ? String(args.advertiser) : args.context ? String(args.context) : void 0,
13698
- sourceKind: "image",
13699
- sourcePath: sourceRelPath,
13700
- description: describeBlueprintIntent(blueprint)
13701
- }),
13702
- "utf8"
13703
- );
13704
- }
13705
13588
  process.stdout.write(
13706
13589
  `${JSON.stringify(
13707
13590
  {
13708
13591
  ok: true,
13709
13592
  canvas_path: outPath,
13710
13593
  prompt_path: blueprintPath,
13711
- definition_path: definitionPath ?? void 0,
13712
- source_reference: sourceRelPath ?? void 0,
13713
13594
  output: canvas.output,
13714
13595
  models: { describe: describeModel, select: selectModel, layout: layoutModel, gen: opts.genModel },
13715
13596
  aspect_ratio: report.aspect_ratio,
@@ -13720,7 +13601,7 @@ var scaffoldStaticAdCommand = defineCommand86({
13720
13601
  run_estimated_credits: validation.estimatedCredits
13721
13602
  },
13722
13603
  checklist: {
13723
- edit_prompt: `Edit ${path8.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.`,
13604
+ edit_prompt: `Edit ${path7.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.`,
13724
13605
  assets_to_supply: report.elements,
13725
13606
  font_slot: report.includes_font ? "Drop a brand font at the [TODO] brandfont path, or delete the brandfont + type_ref nodes to skip it." : "skipped (--skip-font)",
13726
13607
  note: "Replace every [TODO] ingest path with a real file, then `baker canvas validate` and `baker canvas run`. Running generates a billed image \u2014 it is not free."
@@ -13735,8 +13616,8 @@ var scaffoldStaticAdCommand = defineCommand86({
13735
13616
  });
13736
13617
 
13737
13618
  // src/commands/canvas/scaffold-video.ts
13738
- import { cp as cp2, mkdir as mkdir2, readFile as readFile6, writeFile as writeFile2 } from "fs/promises";
13739
- import path11 from "path";
13619
+ import { cp, mkdir, readFile as readFile6, writeFile as writeFile2 } from "fs/promises";
13620
+ import path10 from "path";
13740
13621
  import { defineCommand as defineCommand87 } from "citty";
13741
13622
 
13742
13623
  // src/engine/nodes/local/lib/sceneDetect.ts
@@ -16520,23 +16401,23 @@ function videoReport(input, elementsInput) {
16520
16401
 
16521
16402
  // src/commands/canvas/composition-path.ts
16522
16403
  import { existsSync as existsSync4 } from "fs";
16523
- import path9 from "path";
16404
+ import path8 from "path";
16524
16405
  function resolveShippedCanvasDir(name, startDir, exists = existsSync4, maxDepth = 8) {
16525
- const rel = path9.join("canvas", name);
16406
+ const rel = path8.join("canvas", name);
16526
16407
  let dir = startDir;
16527
16408
  for (let i = 0; i < maxDepth; i++) {
16528
- const candidate = path9.join(dir, rel);
16529
- if (exists(path9.join(candidate, "meta.json"))) return candidate;
16530
- const parent = path9.dirname(dir);
16409
+ const candidate = path8.join(dir, rel);
16410
+ if (exists(path8.join(candidate, "meta.json"))) return candidate;
16411
+ const parent = path8.dirname(dir);
16531
16412
  if (parent === dir) break;
16532
16413
  dir = parent;
16533
16414
  }
16534
- return path9.resolve(startDir, "../../../", rel);
16415
+ return path8.resolve(startDir, "../../../", rel);
16535
16416
  }
16536
16417
 
16537
16418
  // src/commands/canvas/gitignore.ts
16538
16419
  import { appendFile, readFile as readFile5 } from "fs/promises";
16539
- import path10 from "path";
16420
+ import path9 from "path";
16540
16421
  function missingGitignoreEntries(existing, entries) {
16541
16422
  const present = new Set(
16542
16423
  existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
@@ -16544,7 +16425,7 @@ function missingGitignoreEntries(existing, entries) {
16544
16425
  return entries.filter((e) => !present.has(e.trim().replace(/\/+$/, "")));
16545
16426
  }
16546
16427
  async function ensureGitignore(dir, entries) {
16547
- const file = path10.join(dir, ".gitignore");
16428
+ const file = path9.join(dir, ".gitignore");
16548
16429
  let existing;
16549
16430
  try {
16550
16431
  existing = await readFile5(file, "utf8");
@@ -16605,8 +16486,8 @@ async function loadTranscriptBestEffort(ref) {
16605
16486
  async function stageCaptions(outDir, transcript) {
16606
16487
  const text = transcript?.trim();
16607
16488
  if (!text || text === "[]") return {};
16608
- const compositionPath = path11.join(outDir, "tiktok-captions-composition");
16609
- await cp2(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
16489
+ const compositionPath = path10.join(outDir, "tiktok-captions-composition");
16490
+ await cp(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
16610
16491
  return { compositionPath };
16611
16492
  }
16612
16493
  function patchCompositionMeta(metaJson, dims) {
@@ -16623,10 +16504,10 @@ function patchCompositionHtml(html, dims) {
16623
16504
  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`);
16624
16505
  }
16625
16506
  async function stampCompositionDims(compositionDir, dims) {
16626
- const metaPath = path11.join(compositionDir, "meta.json");
16507
+ const metaPath = path10.join(compositionDir, "meta.json");
16627
16508
  const rawMeta = await readFile6(metaPath, "utf8");
16628
16509
  await writeFile2(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
16629
- const htmlPath = path11.join(compositionDir, "index.html");
16510
+ const htmlPath = path10.join(compositionDir, "index.html");
16630
16511
  const rawHtml = await readFile6(htmlPath, "utf8");
16631
16512
  await writeFile2(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
16632
16513
  }
@@ -16796,8 +16677,8 @@ var scaffoldVideoCommand = defineCommand87({
16796
16677
  }
16797
16678
  },
16798
16679
  async run({ args }) {
16799
- const videoPath = path11.resolve(String(args.file));
16800
- const base = path11.basename(videoPath, path11.extname(videoPath));
16680
+ const videoPath = path10.resolve(String(args.file));
16681
+ const base = path10.basename(videoPath, path10.extname(videoPath));
16801
16682
  const slug = args.slug ? String(args.slug) : void 0;
16802
16683
  if (slug && !isValidScaffoldSlug(slug)) {
16803
16684
  process.stderr.write(
@@ -16806,9 +16687,9 @@ var scaffoldVideoCommand = defineCommand87({
16806
16687
  );
16807
16688
  process.exit(2);
16808
16689
  }
16809
- const outPath = args.out ? path11.resolve(String(args.out)) : slug ? path11.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path11.join(path11.dirname(videoPath), `${base}.video.canvas.json`);
16810
- const outDir = path11.dirname(outPath);
16811
- const blueprintPath = path11.join(outDir, "prompt.json");
16690
+ const outPath = args.out ? path10.resolve(String(args.out)) : slug ? path10.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path10.join(path10.dirname(videoPath), `${base}.video.canvas.json`);
16691
+ const outDir = path10.dirname(outPath);
16692
+ const blueprintPath = path10.join(outDir, "prompt.json");
16812
16693
  const frames = args.frames === "reuse" ? "reuse" : "generate";
16813
16694
  const maxScenes = args["max-scenes"] ? Number(args["max-scenes"]) : void 0;
16814
16695
  if (Number.isFinite(maxScenes)) {
@@ -16827,7 +16708,7 @@ var scaffoldVideoCommand = defineCommand87({
16827
16708
  shotCuts
16828
16709
  });
16829
16710
  const { blueprint, elements, transcript, creditsSpent } = await runAnalysisPasses(deconstructCanvas, selectModel);
16830
- await mkdir2(outDir, { recursive: true });
16711
+ await mkdir(outDir, { recursive: true });
16831
16712
  const annotated = annotateBlueprintWithElements(blueprint, elements);
16832
16713
  await writeFile2(blueprintPath, `${JSON.stringify(annotated, null, 2)}
16833
16714
  `, "utf8");
@@ -16848,10 +16729,10 @@ var scaffoldVideoCommand = defineCommand87({
16848
16729
  `
16849
16730
  );
16850
16731
  }
16851
- const compositionDest = path11.join(outDir, "video-overlay-composition");
16852
- await cp2(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
16732
+ const compositionDest = path10.join(outDir, "video-overlay-composition");
16733
+ await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
16853
16734
  await stampCompositionDims(compositionDest, outDims);
16854
- const indexPath = path11.join(compositionDest, "index.html");
16735
+ const indexPath = path10.join(compositionDest, "index.html");
16855
16736
  const overlayHtml = buildOverlayHtml(blueprint);
16856
16737
  const indexHtml = await readFile6(indexPath, "utf8");
16857
16738
  const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
@@ -16867,9 +16748,9 @@ var scaffoldVideoCommand = defineCommand87({
16867
16748
  const opts = {
16868
16749
  imageModel,
16869
16750
  videoModel,
16870
- overlayCompositionPath: path11.relative(outDir, compositionDest),
16871
- captionsCompositionPath: captions.compositionPath ? path11.relative(outDir, captions.compositionPath) : void 0,
16872
- blueprintPath: path11.relative(outDir, blueprintPath),
16751
+ overlayCompositionPath: path10.relative(outDir, compositionDest),
16752
+ captionsCompositionPath: captions.compositionPath ? path10.relative(outDir, captions.compositionPath) : void 0,
16753
+ blueprintPath: path10.relative(outDir, blueprintPath),
16873
16754
  frames,
16874
16755
  ambient: Boolean(args.ambient),
16875
16756
  ...args.aspect ? { aspect: String(args.aspect) } : {},
@@ -16911,7 +16792,7 @@ var scaffoldVideoCommand = defineCommand87({
16911
16792
  run_estimated_credits: validation.estimatedCredits
16912
16793
  },
16913
16794
  checklist: {
16914
- edit_prompt: `Edit ${path11.basename(blueprintPath)} \u2014 the blueprint deconstructed from your video; rewrite it into the ad you want (cast, palette, copy, claims). Every scene frame reads it via target_blueprint.`,
16795
+ edit_prompt: `Edit ${path10.basename(blueprintPath)} \u2014 the blueprint deconstructed from your video; rewrite it into the ad you want (cast, palette, copy, claims). Every scene frame reads it via target_blueprint.`,
16915
16796
  recurring_elements_to_supply: report.elements,
16916
16797
  voices_to_confirm: report.dialogue.map((d) => ({
16917
16798
  scene: d.scene,
@@ -16938,7 +16819,7 @@ var scaffoldVideoCommand = defineCommand87({
16938
16819
 
16939
16820
  // src/commands/canvas/set-prompt.ts
16940
16821
  import { readFile as readFile7, writeFile as writeFile3 } from "fs/promises";
16941
- import path12 from "path";
16822
+ import path11 from "path";
16942
16823
  import { defineCommand as defineCommand88 } from "citty";
16943
16824
  function setNodePrompt(canvas, nodeId, text) {
16944
16825
  const nodes = canvas?.nodes;
@@ -16966,7 +16847,7 @@ var setPromptCommand = defineCommand88({
16966
16847
  "text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
16967
16848
  },
16968
16849
  async run({ args }) {
16969
- const filePath = path12.resolve(String(args.file));
16850
+ const filePath = path11.resolve(String(args.file));
16970
16851
  let canvas;
16971
16852
  try {
16972
16853
  canvas = JSON.parse(await readFile7(filePath, "utf8"));
@@ -16976,7 +16857,7 @@ var setPromptCommand = defineCommand88({
16976
16857
  process.exit(2);
16977
16858
  }
16978
16859
  let text;
16979
- if (args["text-file"]) text = await readFile7(path12.resolve(String(args["text-file"])), "utf8");
16860
+ if (args["text-file"]) text = await readFile7(path11.resolve(String(args["text-file"])), "utf8");
16980
16861
  else if (args.text !== void 0) text = String(args.text);
16981
16862
  else {
16982
16863
  process.stderr.write(
@@ -16997,7 +16878,7 @@ var setPromptCommand = defineCommand88({
16997
16878
  process.exit(2);
16998
16879
  return;
16999
16880
  }
17000
- const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path12.dirname(filePath)), defaultRegistry());
16881
+ const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path11.dirname(filePath)), defaultRegistry());
17001
16882
  if (!validation.ok) {
17002
16883
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
17003
16884
  `);
@@ -17013,7 +16894,7 @@ var setPromptCommand = defineCommand88({
17013
16894
 
17014
16895
  // src/commands/canvas/validate.ts
17015
16896
  import { readFile as readFile8 } from "fs/promises";
17016
- import path13 from "path";
16897
+ import path12 from "path";
17017
16898
  import { defineCommand as defineCommand89 } from "citty";
17018
16899
  var validateCommand = defineCommand89({
17019
16900
  meta: {
@@ -17022,7 +16903,7 @@ var validateCommand = defineCommand89({
17022
16903
  },
17023
16904
  args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
17024
16905
  async run({ args }) {
17025
- const filePath = path13.resolve(String(args.file));
16906
+ const filePath = path12.resolve(String(args.file));
17026
16907
  const raw = await readFile8(filePath, "utf8");
17027
16908
  let parsed;
17028
16909
  try {
@@ -17033,7 +16914,7 @@ var validateCommand = defineCommand89({
17033
16914
  `);
17034
16915
  process.exit(2);
17035
16916
  }
17036
- parsed = resolveRelativeCanvasPaths(parsed, path13.dirname(filePath));
16917
+ parsed = resolveRelativeCanvasPaths(parsed, path12.dirname(filePath));
17037
16918
  const result = await validateCanvasDeep(parsed, defaultRegistry());
17038
16919
  if (!result.ok) {
17039
16920
  process.stderr.write(`${JSON.stringify({ ok: false, issues: result.issues }, null, 2)}
@@ -18174,9 +18055,9 @@ async function readImageBuffer(pathOrUrl) {
18174
18055
  }
18175
18056
  return readFile10(pathOrUrl);
18176
18057
  }
18177
- async function isDirectory(path14) {
18058
+ async function isDirectory(path13) {
18178
18059
  try {
18179
- const s = await stat2(path14);
18060
+ const s = await stat2(path13);
18180
18061
  return s.isDirectory();
18181
18062
  } catch {
18182
18063
  return false;