@koda-sl/baker-cli 0.400.0 → 0.402.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -617,7 +617,7 @@ ${originalIndentation}`;
617
617
  });
618
618
 
619
619
  // src/engine/index.ts
620
- import path21 from "path";
620
+ import path22 from "path";
621
621
 
622
622
  // src/engine/client/http.ts
623
623
  var CONTENT_POLICY_CODE = "content_policy_blocked";
@@ -667,17 +667,17 @@ var HttpClient = class {
667
667
  this.fetchFn = opts.fetchFn ?? fetch;
668
668
  this.sleepFn = opts.sleepFn ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
669
669
  }
670
- async postJson(path22, body, signal) {
671
- return await this.requestJson("POST", path22, body, signal);
670
+ async postJson(path23, body, signal) {
671
+ return await this.requestJson("POST", path23, body, signal);
672
672
  }
673
- async putJson(path22, body, signal) {
674
- return await this.requestJson("PUT", path22, body, signal);
673
+ async putJson(path23, body, signal) {
674
+ return await this.requestJson("PUT", path23, body, signal);
675
675
  }
676
- async getJson(path22, signal) {
677
- return await this.requestJson("GET", path22, void 0, signal);
676
+ async getJson(path23, signal) {
677
+ return await this.requestJson("GET", path23, void 0, signal);
678
678
  }
679
- async requestJson(method, path22, body, signal) {
680
- const url = `${this.baseUrl}${path22.startsWith("/") ? path22 : `/${path22}`}`;
679
+ async requestJson(method, path23, body, signal) {
680
+ const url = `${this.baseUrl}${path23.startsWith("/") ? path23 : `/${path23}`}`;
681
681
  for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
682
682
  const outcome = await this.attempt(method, url, body, attempt, signal);
683
683
  if (outcome.kind === "value") return outcome.value;
@@ -869,12 +869,12 @@ var BackendClient = class {
869
869
  }
870
870
  async pollJob(jobId, signal) {
871
871
  const deadline = Date.now() + JOB_POLL_MAX_MS;
872
- const path22 = `/api/canvas/jobs/${encodeURIComponent(jobId)}`;
872
+ const path23 = `/api/canvas/jobs/${encodeURIComponent(jobId)}`;
873
873
  for (let attempt = 0; ; attempt++) {
874
874
  if (signal?.aborted) {
875
875
  throw new BackendHttpError({ kind: "network", cause: signal.reason ?? new Error("aborted") });
876
876
  }
877
- const job = await this.http.getJson(path22, signal);
877
+ const job = await this.http.getJson(path23, signal);
878
878
  if (job.status === "completed") return job.result;
879
879
  if (job.status === "failed") throw failedJobError(job.error);
880
880
  if (Date.now() > deadline) {
@@ -953,8 +953,8 @@ var BackendClient = class {
953
953
  await this.http.postJson("/api/creatives/definition", payload, signal);
954
954
  }
955
955
  getArtifact(kind, name, version, signal) {
956
- const path22 = version ? `/api/canvas/artifacts/${encodeURIComponent(kind)}/${encodeURIComponent(name)}/${encodeURIComponent(version)}` : `/api/canvas/artifacts/${encodeURIComponent(kind)}/${encodeURIComponent(name)}`;
957
- return this.http.getJson(path22, signal);
956
+ const path23 = version ? `/api/canvas/artifacts/${encodeURIComponent(kind)}/${encodeURIComponent(name)}/${encodeURIComponent(version)}` : `/api/canvas/artifacts/${encodeURIComponent(kind)}/${encodeURIComponent(name)}`;
957
+ return this.http.getJson(path23, signal);
958
958
  }
959
959
  };
960
960
 
@@ -3279,17 +3279,17 @@ async function mapWithConcurrency(items, limit, fn) {
3279
3279
  function isContainerObject(value) {
3280
3280
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3281
3281
  }
3282
- function forEachString(value, visit, path22 = "") {
3282
+ function forEachString(value, visit, path23 = "") {
3283
3283
  if (typeof value === "string") {
3284
- visit(value, path22);
3284
+ visit(value, path23);
3285
3285
  return;
3286
3286
  }
3287
3287
  if (Array.isArray(value)) {
3288
- for (let i = 0; i < value.length; i++) forEachString(value[i], visit, path22 ? `${path22}[${i}]` : `[${i}]`);
3288
+ for (let i = 0; i < value.length; i++) forEachString(value[i], visit, path23 ? `${path23}[${i}]` : `[${i}]`);
3289
3289
  return;
3290
3290
  }
3291
3291
  if (isContainerObject(value)) {
3292
- for (const [k, v] of Object.entries(value)) forEachString(v, visit, path22 ? `${path22}.${k}` : k);
3292
+ for (const [k, v] of Object.entries(value)) forEachString(v, visit, path23 ? `${path23}.${k}` : k);
3293
3293
  }
3294
3294
  }
3295
3295
  function forEachObject(value, visit) {
@@ -4282,6 +4282,7 @@ function nodesBeforeCheckpoint(graph, types, stopKinds) {
4282
4282
  }
4283
4283
 
4284
4284
  // src/engine/engine/validator.ts
4285
+ import path5 from "path";
4285
4286
  import { z as z5 } from "zod";
4286
4287
 
4287
4288
  // src/engine/lib/url.ts
@@ -5731,6 +5732,7 @@ async function validateCanvasDeep(input, registry) {
5731
5732
  const issues = [];
5732
5733
  const ctx = { canvas, registry, issues, idToIndex: buildIdToIndex(canvas) };
5733
5734
  const inputKindOf = (value) => staticInputKind(ctx, value);
5735
+ const inputSourceOf = (value) => staticInputSource(ctx, value);
5734
5736
  for (let i = 0; i < canvas.nodes.length; i++) {
5735
5737
  const n = canvas.nodes[i];
5736
5738
  if (!n) continue;
@@ -5738,7 +5740,13 @@ async function validateCanvasDeep(input, registry) {
5738
5740
  if (!def?.validateExtra) continue;
5739
5741
  let extra;
5740
5742
  try {
5741
- extra = await def.validateExtra({ rawParams: n.params, rawInputs: n.inputs, nodeId: n.id, inputKindOf });
5743
+ extra = await def.validateExtra({
5744
+ rawParams: n.params,
5745
+ rawInputs: n.inputs,
5746
+ nodeId: n.id,
5747
+ inputKindOf,
5748
+ inputSourceOf
5749
+ });
5742
5750
  } catch (e) {
5743
5751
  issues.push({
5744
5752
  path: `nodes[${i}].params`,
@@ -5933,6 +5941,18 @@ function staticInputKind(ctx, value) {
5933
5941
  const plain = outputSchema instanceof z5.ZodString || outputSchema instanceof z5.ZodNumber;
5934
5942
  return plain || outputSchema instanceof z5.ZodBoolean ? "value" : "unknown";
5935
5943
  }
5944
+ function staticInputSource(ctx, value) {
5945
+ if (typeof value !== "string" || !value.startsWith(REF_PREFIX)) return void 0;
5946
+ const target = refTarget(ctx, value);
5947
+ if (!target || target.node.type !== "ingest" || target.expr.output !== "asset" || target.expr.index !== void 0)
5948
+ return void 0;
5949
+ const parsed = target.def.params.safeParse(target.node.params ?? {});
5950
+ if (!parsed.success) return void 0;
5951
+ const params = parsed.data;
5952
+ if (params.source === "path" && typeof params.path === "string") return { path: path5.resolve(params.path) };
5953
+ if (params.source === "url" && typeof params.url === "string") return { url: params.url };
5954
+ return void 0;
5955
+ }
5936
5956
  function checkOneRef(ctx, n, i, refStr, jsonPath, field) {
5937
5957
  if (!refStr.startsWith(REF_PREFIX)) return;
5938
5958
  const expr = parseRefExpr(refStr);
@@ -6061,9 +6081,9 @@ function checkOutputRef(ctx) {
6061
6081
  function pushZodIssues(issues, err, pathPrefix, code, nodeId, nodeType) {
6062
6082
  for (const issue of err.issues) {
6063
6083
  const tail2 = pathToString(issue.path);
6064
- const path22 = pathPrefix ? tail2 ? `${pathPrefix}.${tail2}` : pathPrefix : tail2;
6084
+ const path23 = pathPrefix ? tail2 ? `${pathPrefix}.${tail2}` : pathPrefix : tail2;
6065
6085
  issues.push({
6066
- path: path22,
6086
+ path: path23,
6067
6087
  code,
6068
6088
  message: issue.message,
6069
6089
  received: issue.code === "invalid_type" ? issue.received : void 0,
@@ -6072,12 +6092,12 @@ function pushZodIssues(issues, err, pathPrefix, code, nodeId, nodeType) {
6072
6092
  });
6073
6093
  }
6074
6094
  }
6075
- function pathToString(path22) {
6076
- return path22.map((p) => typeof p === "number" ? `[${p}]` : `.${String(p)}`).join("").replace(/^\./, "");
6095
+ function pathToString(path23) {
6096
+ return path23.map((p) => typeof p === "number" ? `[${p}]` : `.${String(p)}`).join("").replace(/^\./, "");
6077
6097
  }
6078
6098
  function walkRefs(value, cb) {
6079
- forEachString(value, (s, path22) => {
6080
- if (s.startsWith(REF_PREFIX)) cb(s, path22);
6099
+ forEachString(value, (s, path23) => {
6100
+ if (s.startsWith(REF_PREFIX)) cb(s, path23);
6081
6101
  });
6082
6102
  }
6083
6103
  function listShapeKeys(schema) {
@@ -6623,7 +6643,7 @@ var NodeRegistry = class {
6623
6643
  import { execFile as execFileCb } from "child_process";
6624
6644
  import { mkdtemp, readFile as readFile5, rm, stat as stat2 } from "fs/promises";
6625
6645
  import { tmpdir } from "os";
6626
- import path7 from "path";
6646
+ import path8 from "path";
6627
6647
  import { promisify } from "util";
6628
6648
  import { z as z6 } from "zod";
6629
6649
 
@@ -6944,11 +6964,11 @@ async function fetchExternalBytes(url, options) {
6944
6964
  // src/lib/ytDlp.ts
6945
6965
  import { spawn } from "child_process";
6946
6966
  import { readdir, readFile as readFile4 } from "fs/promises";
6947
- import path6 from "path";
6967
+ import path7 from "path";
6948
6968
 
6949
6969
  // src/lib/vimeoCookie.ts
6950
6970
  import { writeFile as writeFile3 } from "fs/promises";
6951
- import path5 from "path";
6971
+ import path6 from "path";
6952
6972
  var COOKIE_NAME = "vimeo";
6953
6973
  async function writeVimeoCookieFile(cookie, workDir) {
6954
6974
  const value = cookie?.trim();
@@ -6959,7 +6979,7 @@ async function writeVimeoCookieFile(cookie, workDir) {
6959
6979
  [".vimeo.com", "TRUE", "/", "TRUE", String(expires), COOKIE_NAME, value].join(" "),
6960
6980
  ""
6961
6981
  ].join("\n");
6962
- const filePath = path5.join(workDir, "vimeo-cookies.txt");
6982
+ const filePath = path6.join(workDir, "vimeo-cookies.txt");
6963
6983
  await writeFile3(filePath, jar, { mode: 384 });
6964
6984
  return filePath;
6965
6985
  }
@@ -6970,6 +6990,12 @@ function isVimeoAuthFailure(detail) {
6970
6990
  return VIMEO_AUTH_SIGNATURES.some((signature) => haystack.includes(signature));
6971
6991
  }
6972
6992
 
6993
+ // src/lib/ytDlpFormat.ts
6994
+ function videoFormatArgs(maxResolution) {
6995
+ const sort = maxResolution ? ["-S", `res:${maxResolution}`] : [];
6996
+ return ["--format", "bv*+ba/b", ...sort, "--merge-output-format", "mp4"];
6997
+ }
6998
+
6973
6999
  // src/lib/ytDlpPlayerClient.ts
6974
7000
  var YOUTUBE_FALLBACK_CLIENT_ARGS = ["--extractor-args", "youtube:player_client=android"];
6975
7001
  var MEDIA_FETCH_REFUSED = "unable to download video data";
@@ -7023,8 +7049,8 @@ function tail(text, maxLines) {
7023
7049
  return text.split("\n").slice(-maxLines).join("\n");
7024
7050
  }
7025
7051
  async function runYtDlp(args) {
7026
- const outTemplate = path6.join(args.workDir, "out.%(ext)s");
7027
- const infoPath = path6.join(args.workDir, "out.info.json");
7052
+ const outTemplate = path7.join(args.workDir, "out.%(ext)s");
7053
+ const infoPath = path7.join(args.workDir, "out.info.json");
7028
7054
  const argv = [
7029
7055
  args.url,
7030
7056
  "--no-playlist",
@@ -7038,7 +7064,7 @@ async function runYtDlp(args) {
7038
7064
  if (args.audioOnly) {
7039
7065
  argv.push("-x", "--audio-format", "m4a");
7040
7066
  } else {
7041
- argv.push("--format", "bv*+ba/b", "--merge-output-format", "mp4");
7067
+ argv.push(...videoFormatArgs(args.maxHeight));
7042
7068
  }
7043
7069
  const cookieFile = await writeVimeoCookieFile(args.vimeoCookie, args.workDir);
7044
7070
  if (cookieFile) argv.push("--cookies", cookieFile);
@@ -7065,7 +7091,7 @@ async function runYtDlp(args) {
7065
7091
  info = JSON.parse(await readFile4(infoPath, "utf-8"));
7066
7092
  } catch {
7067
7093
  }
7068
- return { filePath: path6.join(args.workDir, downloaded), info, tier, reducedQuality };
7094
+ return { filePath: path7.join(args.workDir, downloaded), info, tier, reducedQuality };
7069
7095
  }
7070
7096
  async function probeYtDlp(args) {
7071
7097
  const argv = [
@@ -7537,8 +7563,8 @@ function resolveLocalPath(input) {
7537
7563
  `ingest: ~ path expansion is not supported (got "${input}"). Use an absolute path or a cwd-relative path.`
7538
7564
  );
7539
7565
  }
7540
- if (path7.isAbsolute(input)) return input;
7541
- return path7.resolve(process.cwd(), input);
7566
+ if (path8.isAbsolute(input)) return input;
7567
+ return path8.resolve(process.cwd(), input);
7542
7568
  }
7543
7569
  var EXT_TO_MIME = {
7544
7570
  png: "image/png",
@@ -7586,7 +7612,7 @@ function sniffSvg(buf) {
7586
7612
  return head.startsWith("<?xml") ? head.includes("<svg") : head.startsWith("<svg");
7587
7613
  }
7588
7614
  function inferMimeFromPath(absPath, sniffBytes) {
7589
- const ext = path7.extname(absPath).slice(1).toLowerCase();
7615
+ const ext = path8.extname(absPath).slice(1).toLowerCase();
7590
7616
  const fromExt = EXT_TO_MIME[ext];
7591
7617
  if (fromExt) return fromExt;
7592
7618
  const fromBytes = sniffImageMime(sniffBytes);
@@ -7823,7 +7849,7 @@ function localFileMetadata(args) {
7823
7849
  strategy: "local_file",
7824
7850
  ingested_at: (/* @__PURE__ */ new Date()).toISOString(),
7825
7851
  file_size: args.fileSize,
7826
- original_filename: path7.basename(args.absPath),
7852
+ original_filename: path8.basename(args.absPath),
7827
7853
  ...args.rasterizedFrom ? { rasterized_from: args.rasterizedFrom } : {},
7828
7854
  ...args.durationMs !== void 0 ? { duration_ms: args.durationMs } : {}
7829
7855
  };
@@ -7845,7 +7871,7 @@ async function execYtDlp(params, ctx) {
7845
7871
  if (params.expect !== "video" && params.expect !== "audio") {
7846
7872
  throw new Error(`ingest: yt_dlp only handles video/audio, got expect=${params.expect}`);
7847
7873
  }
7848
- const workDir = await mkdtemp(path7.join(tmpdir(), "ingest-yt-"));
7874
+ const workDir = await mkdtemp(path8.join(tmpdir(), "ingest-yt-"));
7849
7875
  try {
7850
7876
  const { filePath, info, reducedQuality } = await runYtDlpForNode({
7851
7877
  url: params.url,
@@ -7951,7 +7977,7 @@ import { z as z7 } from "zod";
7951
7977
  import { execFile as execFileCb2, spawn as spawn2 } from "child_process";
7952
7978
  import { copyFile as copyFile2, mkdtemp as mkdtemp2, readFile as readFile6, rm as rm2, stat as stat3 } from "fs/promises";
7953
7979
  import { tmpdir as tmpdir2 } from "os";
7954
- import path8 from "path";
7980
+ import path9 from "path";
7955
7981
  import { promisify as promisify2 } from "util";
7956
7982
  var execFile2 = promisify2(execFileCb2);
7957
7983
  var DEFAULT_TIMEOUT_MS2 = 10 * 60 * 1e3;
@@ -7978,7 +8004,7 @@ function mimeForExt(ext) {
7978
8004
  }
7979
8005
  function extForAssetRef(ref) {
7980
8006
  if (ref.path) {
7981
- const e = path8.extname(ref.path);
8007
+ const e = path9.extname(ref.path);
7982
8008
  if (e) return e;
7983
8009
  }
7984
8010
  const reverse = {
@@ -8000,14 +8026,14 @@ function planArrayInputSlot(tmpDir, slot, values, lookup, stagedInputs) {
8000
8026
  const ref = values[i];
8001
8027
  if (!ref) continue;
8002
8028
  if (!ref.path) throw new Error(`cli-runner: inputs.${slot}[${i}] has no local path`);
8003
- const dest = path8.join(tmpDir, `in_${slot}_${i}${extForAssetRef(ref)}`);
8029
+ const dest = path9.join(tmpDir, `in_${slot}_${i}${extForAssetRef(ref)}`);
8004
8030
  stagedInputs.push({ srcPath: ref.path, destPath: dest });
8005
8031
  lookup.set(`in.${slot}.${i}`, dest);
8006
8032
  }
8007
8033
  }
8008
8034
  function planSingleInputSlot(tmpDir, slot, ref, lookup, stagedInputs) {
8009
8035
  if (!ref.path) throw new Error(`cli-runner: inputs.${slot} has no local path`);
8010
- const dest = path8.join(tmpDir, `in_${slot}${extForAssetRef(ref)}`);
8036
+ const dest = path9.join(tmpDir, `in_${slot}${extForAssetRef(ref)}`);
8011
8037
  stagedInputs.push({ srcPath: ref.path, destPath: dest });
8012
8038
  lookup.set(`in.${slot}`, dest);
8013
8039
  }
@@ -8015,7 +8041,7 @@ function planOutputs(tmpDir, outputs, lookup) {
8015
8041
  const outputPaths = [];
8016
8042
  for (const [name, spec] of Object.entries(outputs)) {
8017
8043
  const ext = spec.ext.startsWith(".") ? spec.ext : `.${spec.ext}`;
8018
- const absPath = path8.join(tmpDir, `out_${name}${ext}`);
8044
+ const absPath = path9.join(tmpDir, `out_${name}${ext}`);
8019
8045
  outputPaths.push({ name, absPath, spec });
8020
8046
  lookup.set(`out.${name}`, absPath);
8021
8047
  }
@@ -8056,8 +8082,8 @@ function rejectRawPaths(substituted, original, stagingDir) {
8056
8082
  throw new Error(`cli-runner: home-relative path "${original}" not allowed in args.`);
8057
8083
  }
8058
8084
  if (substituted.startsWith("/")) {
8059
- const resolved = path8.resolve(substituted);
8060
- if (!resolved.startsWith(`${stagingDir}${path8.sep}`) && resolved !== stagingDir) {
8085
+ const resolved = path9.resolve(substituted);
8086
+ if (!resolved.startsWith(`${stagingDir}${path9.sep}`) && resolved !== stagingDir) {
8061
8087
  throw new Error(
8062
8088
  `cli-runner: raw filesystem path "${original}" not allowed \u2014 declare an input slot and use {{in.<slot>}} instead.`
8063
8089
  );
@@ -8107,7 +8133,7 @@ function tailLines(text, maxLines) {
8107
8133
  }
8108
8134
  async function runCli(opts) {
8109
8135
  const { bin, args, inputs, outputs, ctx, timeoutMs = DEFAULT_TIMEOUT_MS2 } = opts;
8110
- const tmpDir = await mkdtemp2(path8.join(tmpdir2(), `cli-${bin.replace(/[^a-z0-9]/gi, "")}-`));
8136
+ const tmpDir = await mkdtemp2(path9.join(tmpdir2(), `cli-${bin.replace(/[^a-z0-9]/gi, "")}-`));
8111
8137
  try {
8112
8138
  const { lookup, stagedInputs, outputPaths } = planPlaceholders(tmpDir, inputs, outputs);
8113
8139
  await stageInputs(stagedInputs);
@@ -8488,17 +8514,17 @@ var ffmpegNode = defineNode({
8488
8514
  import { mkdtemp as mkdtemp3, rm as rm3, writeFile as writeFile4 } from "fs/promises";
8489
8515
  import { createRequire } from "module";
8490
8516
  import { tmpdir as tmpdir3 } from "os";
8491
- import path10 from "path";
8517
+ import path11 from "path";
8492
8518
  import { z as z11 } from "zod";
8493
8519
 
8494
8520
  // src/engine/nodes/local/lib/assets.ts
8495
8521
  import { copyFile as copyFile3, readFile as readFile7 } from "fs/promises";
8496
- import path9 from "path";
8522
+ import path10 from "path";
8497
8523
  async function stageAsset(ref, destDir, filename) {
8498
8524
  if (!ref.path) {
8499
8525
  throw new Error(`stageAsset: ref (${ref.kind}/${ref.mime}) has no local path`);
8500
8526
  }
8501
- const dest = path9.join(destDir, filename);
8527
+ const dest = path10.join(destDir, filename);
8502
8528
  await copyFile3(ref.path, dest);
8503
8529
  return dest;
8504
8530
  }
@@ -8575,11 +8601,11 @@ var fontSpecimenNode = defineNode({
8575
8601
  outputKinds: { image: "image" },
8576
8602
  cost: () => ({ credits: 0, seconds_estimate: 5 }),
8577
8603
  async execute({ inputs, params, ctx }) {
8578
- const tmp = await mkdtemp3(path10.join(tmpdir3(), "font-specimen-"));
8604
+ const tmp = await mkdtemp3(path11.join(tmpdir3(), "font-specimen-"));
8579
8605
  try {
8580
8606
  const fontFilename = `font.${extForMime(inputs.font.mime)}`;
8581
8607
  await stageAsset(inputs.font, tmp, fontFilename);
8582
- const entryPath = path10.join(tmp, "index.html");
8608
+ const entryPath = path11.join(tmp, "index.html");
8583
8609
  await writeFile4(entryPath, buildSpecimenHtml(params, fontFilename), "utf-8");
8584
8610
  ctx.log(`rendering specimen (${params.font_size}px, ${params.text.split("\n").length} lines)`);
8585
8611
  const pwSpecifier = ["play", "wright"].join("");
@@ -8663,15 +8689,15 @@ var fontSpecimenNode = defineNode({
8663
8689
 
8664
8690
  // src/engine/nodes/local/hyperframe.ts
8665
8691
  import { execFile as execFile4 } from "child_process";
8666
- import { mkdtemp as mkdtemp5, readFile as readFile12, rm as rm5, stat as stat5, writeFile as writeFile7 } from "fs/promises";
8692
+ import { mkdtemp as mkdtemp5, readFile as readFile12, rm as rm5, stat as stat6, writeFile as writeFile7 } from "fs/promises";
8667
8693
  import { cpus, tmpdir as tmpdir5 } from "os";
8668
- import path17 from "path";
8694
+ import path18 from "path";
8669
8695
  import { promisify as promisify4 } from "util";
8670
8696
  import { z as z13 } from "zod";
8671
8697
 
8672
8698
  // src/engine/engine/composition-hash.ts
8673
8699
  import { readdir as readdir2, readFile as readFile8, stat as stat4 } from "fs/promises";
8674
- import path11 from "path";
8700
+ import path12 from "path";
8675
8701
  var SKIP_DIRS = /* @__PURE__ */ new Set([".cache", ".git", "node_modules", "dist", "build", ".next", ".turbo"]);
8676
8702
  function isSkippedName(name) {
8677
8703
  if (name.startsWith(".")) return true;
@@ -8688,7 +8714,7 @@ async function collectFiles(root, current) {
8688
8714
  const names = await readdir2(current);
8689
8715
  for (const name of names) {
8690
8716
  if (isSkippedName(name)) continue;
8691
- const abs = path11.join(current, name);
8717
+ const abs = path12.join(current, name);
8692
8718
  const s = await stat4(abs);
8693
8719
  if (s.isDirectory()) {
8694
8720
  out.push(...await collectFiles(root, abs));
@@ -8696,7 +8722,7 @@ async function collectFiles(root, current) {
8696
8722
  }
8697
8723
  if (!s.isFile()) continue;
8698
8724
  const bytes = await readFile8(abs);
8699
- const relPath = path11.relative(root, abs).split(path11.sep).join("/");
8725
+ const relPath = path12.relative(root, abs).split(path12.sep).join("/");
8700
8726
  out.push({ relPath, contentSha: sha256Hex(bytes) });
8701
8727
  }
8702
8728
  return out;
@@ -8704,7 +8730,7 @@ async function collectFiles(root, current) {
8704
8730
 
8705
8731
  // src/engine/engine/composition-meta.ts
8706
8732
  import { readFile as readFile9 } from "fs/promises";
8707
- import path12 from "path";
8733
+ import path13 from "path";
8708
8734
  import { z as z12 } from "zod";
8709
8735
  var InputKind = z12.enum(["video", "image", "audio", "json", "font"]);
8710
8736
  var InputSpec = z12.object({
@@ -8781,7 +8807,7 @@ var CompositionMetaSchema = z12.object({
8781
8807
  params: z12.record(z12.string(), ParamSpec).default({})
8782
8808
  }).strict();
8783
8809
  async function loadCompositionMeta(compositionDir) {
8784
- const metaPath = path12.join(compositionDir, "meta.json");
8810
+ const metaPath = path13.join(compositionDir, "meta.json");
8785
8811
  let raw;
8786
8812
  try {
8787
8813
  raw = await readFile9(metaPath, "utf-8");
@@ -8864,13 +8890,13 @@ function defaultFilenameForInput(key, kind) {
8864
8890
  // src/engine/nodes/local/lib/composition-stage.ts
8865
8891
  import { copyFile as copyFile4, cp } from "fs/promises";
8866
8892
  import { createRequire as createRequire2 } from "module";
8867
- import path13 from "path";
8893
+ import path14 from "path";
8868
8894
  var require_2 = createRequire2(import.meta.url);
8869
8895
  async function copyComposition(srcDir, destDir) {
8870
8896
  await cp(srcDir, destDir, {
8871
8897
  recursive: true,
8872
8898
  filter: (src) => {
8873
- const name = path13.basename(src);
8899
+ const name = path14.basename(src);
8874
8900
  if (name === ".cache" || name === "node_modules" || name === ".git") return false;
8875
8901
  return true;
8876
8902
  }
@@ -8879,21 +8905,21 @@ async function copyComposition(srcDir, destDir) {
8879
8905
  async function vendorGsap(dir, ctx) {
8880
8906
  try {
8881
8907
  const gsapMin = require_2.resolve("gsap/dist/gsap.min.js");
8882
- await copyFile4(gsapMin, path13.join(dir, "gsap.min.js"));
8908
+ await copyFile4(gsapMin, path14.join(dir, "gsap.min.js"));
8883
8909
  } catch (e) {
8884
8910
  ctx.log(`warning: could not vendor gsap.min.js (${e.message}); compositions must self-supply`);
8885
8911
  }
8886
8912
  }
8887
8913
 
8888
8914
  // src/engine/nodes/local/lib/composition-static-check.ts
8889
- import { mkdtemp as mkdtemp4, readFile as readFile11, rm as rm4, writeFile as writeFile6 } from "fs/promises";
8915
+ import { mkdtemp as mkdtemp4, readFile as readFile11, rm as rm4, stat as stat5, writeFile as writeFile6 } from "fs/promises";
8890
8916
  import { tmpdir as tmpdir4 } from "os";
8891
- import path16 from "path";
8917
+ import path17 from "path";
8892
8918
 
8893
8919
  // src/engine/nodes/local/lib/hyperframe-check.ts
8894
8920
  import { execFile as execFile3 } from "child_process";
8895
8921
  import { readFile as readFile10 } from "fs/promises";
8896
- import path14 from "path";
8922
+ import path15 from "path";
8897
8923
  import { promisify as promisify3 } from "util";
8898
8924
 
8899
8925
  // src/engine/nodes/local/lib/caption-style.ts
@@ -9076,6 +9102,13 @@ async function lintPreparedComposition(dir, timeoutMs) {
9076
9102
  const lintRaw = await runOne(buildLintArgsNoInstall(dir), timeoutMs);
9077
9103
  return lintRaw === null ? [] : lintBlockers(lintRaw);
9078
9104
  }
9105
+ function layoutBlockers(checkRaw) {
9106
+ return classifyGateOutput(checkRaw).blocking.filter((f) => f.source === "layout").map((f) => ({ code: f.code, message: explainGateFinding(f) }));
9107
+ }
9108
+ async function checkPreparedLayout(dir, timeoutMs) {
9109
+ const checkRaw = await runOne(["--no-install", ...buildCheckArgs(dir, 1)], timeoutMs);
9110
+ return checkRaw === null ? [] : layoutBlockers(checkRaw);
9111
+ }
9079
9112
  function buildInspectArgs(dir, samples) {
9080
9113
  return ["hyperframes", "inspect", dir, "--json", "--samples", String(samples)];
9081
9114
  }
@@ -9116,7 +9149,7 @@ async function runSnapshotSmoke(args, timeoutMs) {
9116
9149
  }
9117
9150
  async function runNestedCompositionSmoke(opts) {
9118
9151
  const { dir, nodeId, ctx, timeoutMs, samples } = opts;
9119
- const indexHtml = await readFile10(path14.join(dir, "index.html"), "utf-8").catch(() => "");
9152
+ const indexHtml = await readFile10(path15.join(dir, "index.html"), "utf-8").catch(() => "");
9120
9153
  if (!indexHtml || !usesNestedCompositions(indexHtml)) return;
9121
9154
  const snap = await runSnapshotSmoke(
9122
9155
  buildSnapshotArgs(dir, Math.min(samples, 3), hasFrameVisionKey()),
@@ -9132,7 +9165,7 @@ async function runNestedCompositionSmoke(opts) {
9132
9165
  ${snap.message}`
9133
9166
  );
9134
9167
  }
9135
- const described = await readFile10(path14.join(dir, "snapshots", "descriptions.md"), "utf-8").catch(() => "");
9168
+ const described = await readFile10(path15.join(dir, "snapshots", "descriptions.md"), "utf-8").catch(() => "");
9136
9169
  for (const line of condenseFrameDescriptions(described)) {
9137
9170
  ctx.log(`${nodeId}: frame check \u2014 ${line}`);
9138
9171
  }
@@ -9168,7 +9201,7 @@ async function runHyperframesCheck(opts) {
9168
9201
  ${detail}`
9169
9202
  );
9170
9203
  }
9171
- const composition = await readFile10(path14.join(dir, "index.html"), "utf-8").catch(() => "");
9204
+ const composition = await readFile10(path15.join(dir, "index.html"), "utf-8").catch(() => "");
9172
9205
  const captionComplaint = fragmentedCaptions(captionTextsFrom(composition));
9173
9206
  if (captionComplaint) {
9174
9207
  ctx.log(`${nodeId}: caption style \u2014 ${captionComplaint}`);
@@ -9179,9 +9212,9 @@ ${detail}`
9179
9212
 
9180
9213
  // src/engine/nodes/local/lib/hyperframe-meta.ts
9181
9214
  import { writeFile as writeFile5 } from "fs/promises";
9182
- import path15 from "path";
9215
+ import path16 from "path";
9183
9216
  async function ensureHyperframesMetaJson(tmp, nodeId, meta, duration) {
9184
- const metaPath = path15.join(tmp, "meta.json");
9217
+ const metaPath = path16.join(tmp, "meta.json");
9185
9218
  await writeFile5(
9186
9219
  metaPath,
9187
9220
  JSON.stringify(
@@ -9244,11 +9277,11 @@ function extractReferencedTokens(source) {
9244
9277
 
9245
9278
  // src/engine/nodes/local/lib/composition-static-check.ts
9246
9279
  async function staticCompositionIssues(args) {
9247
- const html = await readFile11(path16.join(args.dir, "index.html"), "utf-8").catch(() => null);
9280
+ const html = await readFile11(path17.join(args.dir, "index.html"), "utf-8").catch(() => null);
9248
9281
  const issues = inputIssues(args.meta, args.rawInputs, args.inputKindOf);
9249
9282
  if (html === null) return issues;
9250
9283
  issues.push(...undefinedVariableIssues(html, args.meta, args.compositionParams));
9251
- issues.push(...await lintIssues(args));
9284
+ issues.push(...await gateIssues(args));
9252
9285
  return issues;
9253
9286
  }
9254
9287
  function substitutionValues(meta, compositionParams) {
@@ -9312,34 +9345,92 @@ function kindMismatch(expected, got) {
9312
9345
  if (got === "value") return "got a plain value, not an asset";
9313
9346
  return `got "${got}"`;
9314
9347
  }
9315
- async function lintIssues(args) {
9348
+ async function gateIssues(args) {
9316
9349
  if (!await isHyperframesAvailable()) return [];
9317
- const tmp = await mkdtemp4(path16.join(tmpdir4(), "hf-validate-"));
9350
+ const tmp = await mkdtemp4(path17.join(tmpdir4(), "hf-validate-"));
9318
9351
  try {
9319
- await copyComposition(args.dir, tmp);
9320
- await vendorGsap(tmp, { log: () => void 0 });
9321
- await stagePlaceholderInputs(tmp, args.meta, args.rawInputs);
9322
- const entry = path16.join(tmp, "index.html");
9323
- const html = await readFile11(entry, "utf-8");
9324
- await writeFile6(entry, substituteVariables(html, substitutionValues(args.meta, args.compositionParams)).output);
9325
- await ensureHyperframesMetaJson(tmp, args.nodeId ?? args.meta.id, args.meta, args.meta.default_duration);
9326
- const blockers = await lintPreparedComposition(tmp, args.lintTimeoutMs);
9327
- return blockers.map((b) => ({
9328
- path: "params.composition",
9329
- message: `pre-render gate would fail: [${b.code}] ${b.message}`
9330
- }));
9352
+ const { faithful } = await prepareForGate(tmp, args);
9353
+ const lint = await lintPreparedComposition(tmp, args.lintTimeoutMs);
9354
+ const layout = args.checkLayout && faithful ? await checkPreparedLayout(tmp, LAYOUT_CHECK_TIMEOUT_MS) : [];
9355
+ return [
9356
+ ...lint.map((b) => ({
9357
+ path: "params.composition",
9358
+ message: `pre-render gate would fail: [${b.code}] ${b.message}`
9359
+ })),
9360
+ ...layout.map((b) => ({
9361
+ path: "params.composition",
9362
+ message: `pre-render gate would fail: [layout/${b.code}] ${b.message} Nothing was rendered or billed.`
9363
+ }))
9364
+ ];
9331
9365
  } finally {
9332
9366
  await rm4(tmp, { recursive: true, force: true }).catch(() => void 0);
9333
9367
  }
9334
9368
  }
9335
- async function stagePlaceholderInputs(dir, meta, rawInputs) {
9336
- const inputs = rawInputs && typeof rawInputs === "object" ? rawInputs : {};
9337
- for (const [key, spec] of Object.entries(meta.inputs)) {
9338
- if (!Object.hasOwn(inputs, key)) continue;
9339
- const filename = spec.staged_as ?? defaultFilenameForInput(key, spec.kind);
9340
- await writeFile6(path16.join(dir, filename), spec.kind === "json" ? "{}" : "");
9369
+ var LAYOUT_CHECK_TIMEOUT_MS = 6e4;
9370
+ var MAX_INPUT_BYTES = 20 * 1024 * 1024;
9371
+ var STAND_IN_PICTURE = Buffer.from(
9372
+ "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=",
9373
+ "base64"
9374
+ );
9375
+ async function readSource(source) {
9376
+ try {
9377
+ if ("path" in source) {
9378
+ const info = await stat5(source.path);
9379
+ return info.isFile() && info.size <= MAX_INPUT_BYTES ? await readFile11(source.path) : null;
9380
+ }
9381
+ return (await fetchExternalBytes(source.url, { maxBytes: MAX_INPUT_BYTES, timeoutMs: 15e3 })).buffer;
9382
+ } catch {
9383
+ return null;
9341
9384
  }
9342
9385
  }
9386
+ function standIn(kind) {
9387
+ if (kind === "image") return STAND_IN_PICTURE;
9388
+ return kind === "json" ? "{}" : "";
9389
+ }
9390
+ async function prepareForGate(target, args) {
9391
+ await copyComposition(args.dir, target);
9392
+ await vendorGsap(target, { log: () => void 0 });
9393
+ const staged = await stageGateInputs(target, args);
9394
+ const filled = fillParamSlots(args.meta, args.compositionParams, staged.slotTexts);
9395
+ const entry = path17.join(target, "index.html");
9396
+ const html = await readFile11(entry, "utf-8");
9397
+ await writeFile6(entry, substituteVariables(html, substitutionValues(args.meta, filled.params)).output);
9398
+ await ensureHyperframesMetaJson(target, args.meta.id, args.meta, args.meta.default_duration);
9399
+ return { faithful: staged.faithful && filled.faithful };
9400
+ }
9401
+ async function stageGateInputs(target, args) {
9402
+ const inputs = args.rawInputs && typeof args.rawInputs === "object" ? args.rawInputs : {};
9403
+ const wired = Object.entries(args.meta.inputs).filter(([key]) => Object.hasOwn(inputs, key));
9404
+ const staged = await Promise.all(
9405
+ wired.map(async ([key, spec]) => {
9406
+ const filename = path17.join(target, spec.staged_as ?? defaultFilenameForInput(key, spec.kind));
9407
+ const source = args.inputSourceOf?.(inputs[key]);
9408
+ const bytes = source ? await readSource(source) : null;
9409
+ await writeFile6(filename, bytes ?? standIn(spec.kind));
9410
+ const faithful = bytes !== null || !source && spec.kind === "image";
9411
+ return { key, faithful, text: bytes && spec.kind === "json" ? bytes.toString("utf8") : null };
9412
+ })
9413
+ );
9414
+ const slotTexts = {};
9415
+ for (const { key, text } of staged) if (text !== null) slotTexts[key] = text;
9416
+ return { faithful: staged.every((s) => s.faithful), slotTexts };
9417
+ }
9418
+ function fillParamSlots(meta, compositionParams, slotTexts) {
9419
+ let faithful = true;
9420
+ const params = {};
9421
+ for (const [name, value] of Object.entries(compositionParams)) {
9422
+ const slots = typeof value === "string" ? extractReferencedTokens(value) : [];
9423
+ if (typeof value !== "string" || slots.length === 0) {
9424
+ params[name] = value;
9425
+ } else if (slots.every((slot) => Object.hasOwn(slotTexts, slot))) {
9426
+ params[name] = renderTemplate(value, slotTexts);
9427
+ } else {
9428
+ faithful = false;
9429
+ params[name] = placeholderFor(meta.params[name]?.kind);
9430
+ }
9431
+ }
9432
+ return { faithful, params };
9433
+ }
9343
9434
 
9344
9435
  // src/engine/nodes/local/lib/hyperframe-errors.ts
9345
9436
  var KNOWN_ERROR_PATTERNS = [
@@ -9430,7 +9521,7 @@ var hyperframeRenderNode = defineNode({
9430
9521
  const compositionDir = await resolveCompositionDir(params.composition);
9431
9522
  const meta = await loadCompositionMeta(compositionDir);
9432
9523
  const compositionParams = validateAndParseDynamicParams(meta, params);
9433
- const tmp = await mkdtemp5(path17.join(tmpdir5(), "hf-render-"));
9524
+ const tmp = await mkdtemp5(path18.join(tmpdir5(), "hf-render-"));
9434
9525
  try {
9435
9526
  await copyComposition(compositionDir, tmp);
9436
9527
  await vendorGsap(tmp, ctx);
@@ -9440,7 +9531,7 @@ var hyperframeRenderNode = defineNode({
9440
9531
  await substituteCompositionFiles(tmp, substitutionValues2);
9441
9532
  await ensureHyperframesMetaJson(tmp, ctx.nodeId, meta, duration);
9442
9533
  await runHyperframesCheck({ dir: tmp, nodeId: "hyperframe_render", ctx, timeoutMs: params.timeout_ms });
9443
- const outputPath = path17.join(tmp, `output.${params.format}`);
9534
+ const outputPath = path18.join(tmp, `output.${params.format}`);
9444
9535
  await runRender({ tmp, outputPath, params, meta, ctx });
9445
9536
  const bytes = await readFile12(outputPath);
9446
9537
  ctx.log(`rendered ${bytes.length} bytes`);
@@ -9464,12 +9555,12 @@ var hyperframeRenderNode = defineNode({
9464
9555
  }
9465
9556
  });
9466
9557
  async function resolveCompositionDir(composition) {
9467
- const compositionPath = path17.isAbsolute(composition) ? composition : path17.resolve(process.cwd(), composition);
9468
- const s = await stat5(compositionPath);
9558
+ const compositionPath = path18.isAbsolute(composition) ? composition : path18.resolve(process.cwd(), composition);
9559
+ const s = await stat6(compositionPath);
9469
9560
  if (s.isDirectory()) return compositionPath;
9470
- return path17.dirname(compositionPath);
9561
+ return path18.dirname(compositionPath);
9471
9562
  }
9472
- async function validateCompositionNode(args, ownedKeys) {
9563
+ async function validateCompositionNode(args, ownedKeys, options = {}) {
9473
9564
  const issues = await validateCompositionParams(args.rawParams, ownedKeys);
9474
9565
  const params = args.rawParams ?? {};
9475
9566
  if (typeof params.composition !== "string") return issues;
@@ -9485,7 +9576,9 @@ async function validateCompositionNode(args, ownedKeys) {
9485
9576
  compositionParams,
9486
9577
  rawInputs: args.rawInputs,
9487
9578
  inputKindOf: args.inputKindOf,
9488
- lintTimeoutMs: 2e4
9579
+ inputSourceOf: args.inputSourceOf,
9580
+ lintTimeoutMs: 2e4,
9581
+ checkLayout: options.checkLayout
9489
9582
  })
9490
9583
  );
9491
9584
  return issues;
@@ -9560,7 +9653,7 @@ async function stageInputs2(tmp, inputs, meta, ctx) {
9560
9653
  await stageAsset(ref, tmp, filename);
9561
9654
  ctx.log(`staged ${spec.kind} \u2192 ${filename}`);
9562
9655
  if (spec.kind === "video" && primaryDuration === null) {
9563
- primaryDuration = await probeDurationSeconds(path17.join(tmp, filename));
9656
+ primaryDuration = await probeDurationSeconds(path18.join(tmp, filename));
9564
9657
  }
9565
9658
  }
9566
9659
  return primaryDuration;
@@ -9606,7 +9699,7 @@ function coerceImageParam(value) {
9606
9699
  throw new Error("hyperframe_render: image param must be a URL string or AssetRef");
9607
9700
  }
9608
9701
  async function substituteCompositionFiles(tmp, values) {
9609
- const entryPath = path17.join(tmp, "index.html");
9702
+ const entryPath = path18.join(tmp, "index.html");
9610
9703
  const original = await readFile12(entryPath, "utf-8");
9611
9704
  const { output, missing } = substituteVariables(original, values);
9612
9705
  if (missing.length > 0) {
@@ -9623,7 +9716,7 @@ function workerCount() {
9623
9716
  async function runRender(opts) {
9624
9717
  const { tmp, outputPath, params, meta, ctx } = opts;
9625
9718
  const args = buildRenderArgs(tmp, outputPath, meta, params.format);
9626
- ctx.log(`rendering ${meta.width}x${meta.height}@${meta.fps}fps ${params.format} from ${path17.basename(tmp)}`);
9719
+ ctx.log(`rendering ${meta.width}x${meta.height}@${meta.fps}fps ${params.format} from ${path18.basename(tmp)}`);
9627
9720
  try {
9628
9721
  await execFileAsync2("npx", args, { timeout: params.timeout_ms, maxBuffer: 64 * 1024 * 1024 });
9629
9722
  } catch (e) {
@@ -9670,7 +9763,7 @@ import { execFile as execFile5 } from "child_process";
9670
9763
  import { mkdtemp as mkdtemp6, readFile as readFile13, rm as rm6, writeFile as writeFile8 } from "fs/promises";
9671
9764
  import { createRequire as createRequire3 } from "module";
9672
9765
  import { tmpdir as tmpdir6 } from "os";
9673
- import path18 from "path";
9766
+ import path19 from "path";
9674
9767
  import { promisify as promisify5 } from "util";
9675
9768
  import { z as z14 } from "zod";
9676
9769
  var _execFileAsync = promisify5(execFile5);
@@ -9705,7 +9798,9 @@ var hyperframeSnapshotNode = defineNode({
9705
9798
  cost: () => ({ credits: 0, seconds_estimate: 3 }),
9706
9799
  // The snapshot's own keys (`wait_for`) are not composition params: checked against the
9707
9800
  // render node's key set, a canvas that waited on a selector failed validate every time.
9708
- validateExtra: (args) => validateCompositionNode(args, NODE_OWNED_PARAM_KEYS2),
9801
+ // A still has one frame and its copy is known before the run, so validate lays it out
9802
+ // and refuses colliding lines before a single picture is paid for.
9803
+ validateExtra: (args) => validateCompositionNode(args, NODE_OWNED_PARAM_KEYS2, { checkLayout: true }),
9709
9804
  cacheKeyExtras: async ({ params }) => {
9710
9805
  const out = {};
9711
9806
  const composition = params.composition;
@@ -9719,7 +9814,7 @@ var hyperframeSnapshotNode = defineNode({
9719
9814
  const compositionDir = await resolveCompositionDir(params.composition);
9720
9815
  const meta = await loadCompositionMeta(compositionDir);
9721
9816
  const compositionParams = validateAndParseDynamicParams2(meta, params);
9722
- const tmp = await mkdtemp6(path18.join(tmpdir6(), "hf-snap-"));
9817
+ const tmp = await mkdtemp6(path19.join(tmpdir6(), "hf-snap-"));
9723
9818
  try {
9724
9819
  await copyComposition(compositionDir, tmp);
9725
9820
  await vendorGsap(tmp, ctx);
@@ -9734,7 +9829,7 @@ var hyperframeSnapshotNode = defineNode({
9734
9829
  timeoutMs: params.timeout_ms,
9735
9830
  samples: 1
9736
9831
  });
9737
- const entryPath = path18.join(tmp, "index.html");
9832
+ const entryPath = path19.join(tmp, "index.html");
9738
9833
  const entryUrl = `file://${entryPath}`;
9739
9834
  ctx.log(`snapshotting ${meta.width}x${meta.height}@${DEVICE_SCALE_FACTOR2}x wait=${params.wait_for.kind}`);
9740
9835
  const pwSpecifier = ["play", "wright"].join("");
@@ -9819,7 +9914,7 @@ function coerceImageParam2(value) {
9819
9914
  throw new Error("hyperframe_snapshot: image param must be a URL string or AssetRef");
9820
9915
  }
9821
9916
  async function substituteCompositionFiles2(tmp, values) {
9822
- const entryPath = path18.join(tmp, "index.html");
9917
+ const entryPath = path19.join(tmp, "index.html");
9823
9918
  const original = await readFile13(entryPath, "utf-8");
9824
9919
  const { output, missing } = substituteVariables(original, values);
9825
9920
  if (missing.length > 0) {
@@ -10833,7 +10928,7 @@ var videoLipsyncNode = delegated({
10833
10928
  // src/engine/nodes/remote/videoTranscribe.ts
10834
10929
  import { mkdtemp as mkdtemp7, readFile as readFile14, rm as rm7 } from "fs/promises";
10835
10930
  import { tmpdir as tmpdir7 } from "os";
10836
- import path19 from "path";
10931
+ import path20 from "path";
10837
10932
  import { z as z37 } from "zod";
10838
10933
 
10839
10934
  // src/engine/nodes/local/lib/ffmpeg.ts
@@ -11161,8 +11256,8 @@ async function tryExtractAudio(inputs, ctx) {
11161
11256
  ctx.log("video_transcribe: no audio track detected, sending full video");
11162
11257
  return null;
11163
11258
  }
11164
- tmpDir = await mkdtemp7(path19.join(tmpdir7(), "vtx-"));
11165
- const audioPath = path19.join(tmpDir, "audio.mp3");
11259
+ tmpDir = await mkdtemp7(path20.join(tmpdir7(), "vtx-"));
11260
+ const audioPath = path20.join(tmpDir, "audio.mp3");
11166
11261
  ctx.log("video_transcribe: extracting audio (mono 16kHz mp3)");
11167
11262
  await runFfmpeg(
11168
11263
  ["-i", video.path, "-vn", "-ac", "1", "-ar", "16000", "-b:a", "64k", "-f", "mp3", "-y", audioPath],
@@ -11360,14 +11455,14 @@ function catalogCost(def) {
11360
11455
  // src/engine/storage/cache-store.ts
11361
11456
  import { randomUUID as randomUUID2 } from "crypto";
11362
11457
  import { mkdir as mkdir3, readFile as readFile15, rename as rename2, writeFile as writeFile9 } from "fs/promises";
11363
- import path20 from "path";
11458
+ import path21 from "path";
11364
11459
  var LocalCacheStore = class {
11365
11460
  rootDir;
11366
11461
  constructor(rootDir) {
11367
11462
  this.rootDir = rootDir;
11368
11463
  }
11369
11464
  filePath(cacheKey) {
11370
- return path20.join(this.rootDir, `${cacheKey}.json`);
11465
+ return path21.join(this.rootDir, `${cacheKey}.json`);
11371
11466
  }
11372
11467
  async get(cacheKey) {
11373
11468
  try {
@@ -11380,7 +11475,7 @@ var LocalCacheStore = class {
11380
11475
  }
11381
11476
  async put(entry) {
11382
11477
  const dest = this.filePath(entry.cacheKey);
11383
- await mkdir3(path20.dirname(dest), { recursive: true });
11478
+ await mkdir3(path21.dirname(dest), { recursive: true });
11384
11479
  const tmp = `${dest}.tmp-${process.pid}-${randomUUID2()}`;
11385
11480
  await writeFile9(tmp, JSON.stringify(entry, null, 0));
11386
11481
  await rename2(tmp, dest);
@@ -11438,12 +11533,12 @@ function defaultRegistry() {
11438
11533
  }
11439
11534
  function createEngineFromEnv(opts = {}) {
11440
11535
  const cwd = opts.cwd ?? process.cwd();
11441
- const cacheDir = opts.cacheDir ?? path21.join(cwd, "canvas", ".cache");
11442
- const outputsDir = opts.outputsDir ?? path21.join(cwd, "canvas");
11536
+ const cacheDir = opts.cacheDir ?? path22.join(cwd, "canvas", ".cache");
11537
+ const outputsDir = opts.outputsDir ?? path22.join(cwd, "canvas");
11443
11538
  const connection = requireConnection();
11444
11539
  const client = new BackendClient({ baseUrl: connection.BAKER_API_URL, apiKey: connection.BAKER_API_KEY });
11445
- const assets = new LocalAssetStore(path21.join(cacheDir, "assets"));
11446
- const localCache = new LocalCacheStore(path21.join(cacheDir, "index"));
11540
+ const assets = new LocalAssetStore(path22.join(cacheDir, "assets"));
11541
+ const localCache = new LocalCacheStore(path22.join(cacheDir, "index"));
11447
11542
  const remoteCacheEnabled = opts.remoteCache ?? remoteCacheEnabledFromEnv();
11448
11543
  const cache = remoteCacheEnabled ? new LayeredCacheStore({
11449
11544
  local: localCache,
@@ -11565,4 +11660,4 @@ export {
11565
11660
  defaultRegistry,
11566
11661
  createEngineFromEnv
11567
11662
  };
11568
- //# sourceMappingURL=chunk-UW62VPTH.js.map
11663
+ //# sourceMappingURL=chunk-X7PLV5QH.js.map