@koda-sl/baker-cli 0.199.0 → 0.200.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.
@@ -15,9 +15,9 @@ import {
15
15
  shouldEscalate
16
16
  } from "./chunk-6NZG2TCM.js";
17
17
 
18
- // ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/index.js
18
+ // ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/node_modules/safe-stable-stringify/index.js
19
19
  var require_safe_stable_stringify = __commonJS({
20
- "../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/index.js"(exports, module) {
20
+ "../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/node_modules/safe-stable-stringify/index.js"(exports, module) {
21
21
  "use strict";
22
22
  var { hasOwnProperty } = Object.prototype;
23
23
  var stringify = configure2();
@@ -612,7 +612,7 @@ ${originalIndentation}`;
612
612
  });
613
613
 
614
614
  // src/engine/index.ts
615
- import path16 from "path";
615
+ import path17 from "path";
616
616
 
617
617
  // src/engine/client/http.ts
618
618
  var CONTENT_POLICY_CODE = "content_policy_blocked";
@@ -661,17 +661,17 @@ var HttpClient = class {
661
661
  this.fetchFn = opts.fetchFn ?? fetch;
662
662
  this.sleepFn = opts.sleepFn ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
663
663
  }
664
- async postJson(path17, body, signal) {
665
- return await this.requestJson("POST", path17, body, signal);
664
+ async postJson(path18, body, signal) {
665
+ return await this.requestJson("POST", path18, body, signal);
666
666
  }
667
- async putJson(path17, body, signal) {
668
- return await this.requestJson("PUT", path17, body, signal);
667
+ async putJson(path18, body, signal) {
668
+ return await this.requestJson("PUT", path18, body, signal);
669
669
  }
670
- async getJson(path17, signal) {
671
- return await this.requestJson("GET", path17, void 0, signal);
670
+ async getJson(path18, signal) {
671
+ return await this.requestJson("GET", path18, void 0, signal);
672
672
  }
673
- async requestJson(method, path17, body, signal) {
674
- const url = `${this.baseUrl}${path17.startsWith("/") ? path17 : `/${path17}`}`;
673
+ async requestJson(method, path18, body, signal) {
674
+ const url = `${this.baseUrl}${path18.startsWith("/") ? path18 : `/${path18}`}`;
675
675
  for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
676
676
  const outcome = await this.attempt(method, url, body, attempt, signal);
677
677
  if (outcome.kind === "value") return outcome.value;
@@ -834,12 +834,12 @@ var BackendClient = class {
834
834
  }
835
835
  async pollJob(jobId, signal) {
836
836
  const deadline = Date.now() + JOB_POLL_MAX_MS;
837
- const path17 = `/api/canvas/jobs/${encodeURIComponent(jobId)}`;
837
+ const path18 = `/api/canvas/jobs/${encodeURIComponent(jobId)}`;
838
838
  for (let attempt = 0; ; attempt++) {
839
839
  if (signal?.aborted) {
840
840
  throw new BackendHttpError({ kind: "network", cause: signal.reason ?? new Error("aborted") });
841
841
  }
842
- const job = await this.http.getJson(path17, signal);
842
+ const job = await this.http.getJson(path18, signal);
843
843
  if (job.status === "completed") return job.result;
844
844
  if (job.status === "failed") throw failedJobError(job.error);
845
845
  if (Date.now() > deadline) {
@@ -918,8 +918,8 @@ var BackendClient = class {
918
918
  await this.http.postJson("/api/creatives/definition", payload, signal);
919
919
  }
920
920
  getArtifact(kind, name, version, signal) {
921
- const path17 = version ? `/api/canvas/artifacts/${encodeURIComponent(kind)}/${encodeURIComponent(name)}/${encodeURIComponent(version)}` : `/api/canvas/artifacts/${encodeURIComponent(kind)}/${encodeURIComponent(name)}`;
922
- return this.http.getJson(path17, signal);
921
+ const path18 = version ? `/api/canvas/artifacts/${encodeURIComponent(kind)}/${encodeURIComponent(name)}/${encodeURIComponent(version)}` : `/api/canvas/artifacts/${encodeURIComponent(kind)}/${encodeURIComponent(name)}`;
922
+ return this.http.getJson(path18, signal);
923
923
  }
924
924
  };
925
925
 
@@ -1091,7 +1091,7 @@ function resolveAdaptFormats(params) {
1091
1091
  return params.formats ?? [];
1092
1092
  }
1093
1093
 
1094
- // ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/esm/wrapper.js
1094
+ // ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/node_modules/safe-stable-stringify/esm/wrapper.js
1095
1095
  var import__ = __toESM(require_safe_stable_stringify(), 1);
1096
1096
  var configure = import__.default.configure;
1097
1097
  var wrapper_default = import__.default;
@@ -4289,9 +4289,9 @@ function checkOutputRef(ctx) {
4289
4289
  function pushZodIssues(issues, err, pathPrefix, code, nodeId, nodeType) {
4290
4290
  for (const issue of err.issues) {
4291
4291
  const tail2 = pathToString(issue.path);
4292
- const path17 = pathPrefix ? tail2 ? `${pathPrefix}.${tail2}` : pathPrefix : tail2;
4292
+ const path18 = pathPrefix ? tail2 ? `${pathPrefix}.${tail2}` : pathPrefix : tail2;
4293
4293
  issues.push({
4294
- path: path17,
4294
+ path: path18,
4295
4295
  code,
4296
4296
  message: issue.message,
4297
4297
  received: issue.code === "invalid_type" ? issue.received : void 0,
@@ -4300,8 +4300,8 @@ function pushZodIssues(issues, err, pathPrefix, code, nodeId, nodeType) {
4300
4300
  });
4301
4301
  }
4302
4302
  }
4303
- function pathToString(path17) {
4304
- return path17.map((p) => typeof p === "number" ? `[${p}]` : `.${String(p)}`).join("").replace(/^\./, "");
4303
+ function pathToString(path18) {
4304
+ return path18.map((p) => typeof p === "number" ? `[${p}]` : `.${String(p)}`).join("").replace(/^\./, "");
4305
4305
  }
4306
4306
  function buildDepGraph(canvas) {
4307
4307
  const graph = /* @__PURE__ */ new Map();
@@ -4910,10 +4910,10 @@ var NodeRegistry = class {
4910
4910
  };
4911
4911
 
4912
4912
  // src/engine/nodes/ingest.ts
4913
- import { execFile as execFileCb, spawn } from "child_process";
4914
- import { mkdtemp, readdir, readFile as readFile3, rm, stat as stat2 } from "fs/promises";
4913
+ import { execFile as execFileCb } from "child_process";
4914
+ import { mkdtemp, readFile as readFile4, rm, stat as stat2 } from "fs/promises";
4915
4915
  import { tmpdir } from "os";
4916
- import path4 from "path";
4916
+ import path5 from "path";
4917
4917
  import { promisify } from "util";
4918
4918
  import { z as z5 } from "zod";
4919
4919
 
@@ -5231,6 +5231,151 @@ async function fetchExternalBytes(url, options) {
5231
5231
  return { buffer, contentType: response.headers.get("content-type") };
5232
5232
  }
5233
5233
 
5234
+ // src/lib/ytDlp.ts
5235
+ import { spawn } from "child_process";
5236
+ import { readdir, readFile as readFile3 } from "fs/promises";
5237
+ import path4 from "path";
5238
+
5239
+ // src/lib/ytDlpSignal.ts
5240
+ var HTTP_ERROR = /HTTP Error (\d{3})/i;
5241
+ var CHALLENGE_PHRASES = [
5242
+ "sign in to confirm you're not a bot",
5243
+ "sign in to confirm you\u2019re not a bot",
5244
+ "confirm you are not a bot",
5245
+ "unusual traffic",
5246
+ "captcha"
5247
+ ];
5248
+ var NOT_A_BLOCK_PHRASES = ["private video", "video unavailable", "members-only", "this video is available to"];
5249
+ function ytDlpBlockSignal(stderr) {
5250
+ const text = stderr.toLowerCase();
5251
+ if (NOT_A_BLOCK_PHRASES.some((phrase) => text.includes(phrase))) return {};
5252
+ const status = HTTP_ERROR.exec(stderr);
5253
+ if (status) return { status: Number(status[1]) };
5254
+ if (CHALLENGE_PHRASES.some((phrase) => text.includes(phrase))) return { challenge: true };
5255
+ return {};
5256
+ }
5257
+
5258
+ // src/lib/ytDlp.ts
5259
+ var YT_DLP_BIN = "yt-dlp";
5260
+ var YT_DLP_TIMEOUT_MS = 10 * 60 * 1e3;
5261
+ var YtDlpError = class extends Error {
5262
+ constructor(message2, stderrTail) {
5263
+ super(message2);
5264
+ this.stderrTail = stderrTail;
5265
+ this.name = "YtDlpError";
5266
+ }
5267
+ stderrTail;
5268
+ };
5269
+ function tail(text, maxLines) {
5270
+ return text.split("\n").slice(-maxLines).join("\n");
5271
+ }
5272
+ async function runYtDlp(args) {
5273
+ const outTemplate = path4.join(args.workDir, "out.%(ext)s");
5274
+ const infoPath = path4.join(args.workDir, "out.info.json");
5275
+ const argv = [
5276
+ args.url,
5277
+ "--no-playlist",
5278
+ "--no-progress",
5279
+ "--no-warnings",
5280
+ "--quiet",
5281
+ "--write-info-json",
5282
+ "-o",
5283
+ outTemplate
5284
+ ];
5285
+ if (args.audioOnly) {
5286
+ argv.push("-x", "--audio-format", "m4a");
5287
+ } else {
5288
+ const format = args.maxHeight ? `bv*[height<=${args.maxHeight}]+ba/b[height<=${args.maxHeight}]/bv*+ba/b` : "bv*+ba/b";
5289
+ argv.push("--format", format, "--merge-output-format", "mp4");
5290
+ }
5291
+ args.onLog?.(`yt-dlp: downloading ${args.audioOnly ? "audio" : "video"}`);
5292
+ const { code, stderr } = await runViaLadder({
5293
+ url: args.url,
5294
+ argv,
5295
+ timeoutMs: args.timeoutMs ?? YT_DLP_TIMEOUT_MS,
5296
+ onLog: args.onLog
5297
+ });
5298
+ if (code !== 0) {
5299
+ throw new YtDlpError(`yt-dlp exited ${code}`, tail(stderr, 40));
5300
+ }
5301
+ const files = await readdir(args.workDir);
5302
+ const wanted = args.audioOnly ? ".m4a" : ".mp4";
5303
+ const downloaded = files.find((f) => f.startsWith("out.") && f.endsWith(wanted));
5304
+ if (!downloaded) {
5305
+ throw new YtDlpError(`yt-dlp finished but produced no ${wanted} file (saw: ${files.join(", ")})`, "");
5306
+ }
5307
+ let info = {};
5308
+ try {
5309
+ info = JSON.parse(await readFile3(infoPath, "utf-8"));
5310
+ } catch {
5311
+ }
5312
+ return { filePath: path4.join(args.workDir, downloaded), info };
5313
+ }
5314
+ async function walkYtDlpLadder(args) {
5315
+ const routes = plannedRoutes(args.url, args.credentials);
5316
+ let last = { code: -1, stderr: "" };
5317
+ for (const route of routes) {
5318
+ if (route.kind === "proxy") args.onLog?.(`yt-dlp: retrying via ${route.tier}`);
5319
+ last = await args.attempt(route);
5320
+ if (last.code === 0) return last;
5321
+ if (!shouldEscalate(ytDlpBlockSignal(last.stderr))) return last;
5322
+ }
5323
+ return last;
5324
+ }
5325
+ function runViaLadder(args) {
5326
+ return walkYtDlpLadder({
5327
+ url: args.url,
5328
+ credentials: captureProxyCredentials(),
5329
+ attempt: (route) => spawnAndWait(YT_DLP_BIN, args.argv, args.timeoutMs, proxyEnvFor(route)),
5330
+ onLog: args.onLog
5331
+ });
5332
+ }
5333
+ function proxyEnvFor(route) {
5334
+ if (route.kind !== "proxy") return void 0;
5335
+ const uri = new URL(route.server);
5336
+ uri.username = encodeURIComponent(route.username);
5337
+ uri.password = encodeURIComponent(route.password);
5338
+ return {
5339
+ HTTP_PROXY: uri.toString(),
5340
+ HTTPS_PROXY: uri.toString(),
5341
+ http_proxy: uri.toString(),
5342
+ https_proxy: uri.toString()
5343
+ };
5344
+ }
5345
+ function spawnAndWait(bin, argv, timeoutMs, extraEnv) {
5346
+ return new Promise((resolve, reject) => {
5347
+ const child = spawn(bin, argv, {
5348
+ stdio: ["ignore", "pipe", "pipe"],
5349
+ // `childEnvWith` because `spawn`'s `env` REPLACES the environment rather
5350
+ // than extending it — passing only the additions would strip PATH.
5351
+ ...extraEnv ? { env: childEnvWith(extraEnv) } : {}
5352
+ });
5353
+ const stderrChunks = [];
5354
+ let stderrLen = 0;
5355
+ const MAX_STDERR = 256 * 1024;
5356
+ child.stdout.on("data", () => {
5357
+ });
5358
+ child.stderr.on("data", (chunk) => {
5359
+ if (stderrLen < MAX_STDERR) {
5360
+ stderrChunks.push(chunk);
5361
+ stderrLen += chunk.length;
5362
+ }
5363
+ });
5364
+ const timer = setTimeout(() => {
5365
+ child.kill("SIGKILL");
5366
+ reject(new Error(`${bin} timed out after ${timeoutMs}ms`));
5367
+ }, timeoutMs);
5368
+ child.on("error", (err) => {
5369
+ clearTimeout(timer);
5370
+ reject(new Error(`failed to spawn "${bin}" \u2014 ${err.message}`));
5371
+ });
5372
+ child.on("close", (code) => {
5373
+ clearTimeout(timer);
5374
+ resolve({ code: code ?? -1, stderr: Buffer.concat(stderrChunks).toString("utf-8") });
5375
+ });
5376
+ });
5377
+ }
5378
+
5234
5379
  // src/engine/schema/refs.ts
5235
5380
  import { z as z4 } from "zod";
5236
5381
 
@@ -5426,25 +5571,6 @@ function mapClientError(ctx, e) {
5426
5571
  return new NodeExecutionError(ctx.nodeId, ctx.nodeType, { kind: "local", cause: e });
5427
5572
  }
5428
5573
 
5429
- // src/engine/nodes/ytDlpSignal.ts
5430
- var HTTP_ERROR = /HTTP Error (\d{3})/i;
5431
- var CHALLENGE_PHRASES = [
5432
- "sign in to confirm you're not a bot",
5433
- "sign in to confirm you\u2019re not a bot",
5434
- "confirm you are not a bot",
5435
- "unusual traffic",
5436
- "captcha"
5437
- ];
5438
- var NOT_A_BLOCK_PHRASES = ["private video", "video unavailable", "members-only", "this video is available to"];
5439
- function ytDlpBlockSignal(stderr) {
5440
- const text = stderr.toLowerCase();
5441
- if (NOT_A_BLOCK_PHRASES.some((phrase) => text.includes(phrase))) return {};
5442
- const status = HTTP_ERROR.exec(stderr);
5443
- if (status) return { status: Number(status[1]) };
5444
- if (CHALLENGE_PHRASES.some((phrase) => text.includes(phrase))) return { challenge: true };
5445
- return {};
5446
- }
5447
-
5448
5574
  // src/engine/nodes/ingest.ts
5449
5575
  var execFile = promisify(execFileCb);
5450
5576
  var ExpectEnum = z5.enum(["image", "video", "audio", "text", "json", "font"]);
@@ -5617,8 +5743,8 @@ function resolveLocalPath(input) {
5617
5743
  `ingest: ~ path expansion is not supported (got "${input}"). Use an absolute path or a cwd-relative path.`
5618
5744
  );
5619
5745
  }
5620
- if (path4.isAbsolute(input)) return input;
5621
- return path4.resolve(process.cwd(), input);
5746
+ if (path5.isAbsolute(input)) return input;
5747
+ return path5.resolve(process.cwd(), input);
5622
5748
  }
5623
5749
  var EXT_TO_MIME = {
5624
5750
  png: "image/png",
@@ -5666,7 +5792,7 @@ function sniffSvg(buf) {
5666
5792
  return head.startsWith("<?xml") ? head.includes("<svg") : head.startsWith("<svg");
5667
5793
  }
5668
5794
  function inferMimeFromPath(absPath, sniffBytes) {
5669
- const ext = path4.extname(absPath).slice(1).toLowerCase();
5795
+ const ext = path5.extname(absPath).slice(1).toLowerCase();
5670
5796
  const fromExt = EXT_TO_MIME[ext];
5671
5797
  if (fromExt) return fromExt;
5672
5798
  const fromBytes = sniffImageMime(sniffBytes);
@@ -5805,7 +5931,7 @@ async function execLocalFile(params, ctx) {
5805
5931
  }
5806
5932
  let bytes;
5807
5933
  try {
5808
- bytes = await readFile3(absPath);
5934
+ bytes = await readFile4(absPath);
5809
5935
  } catch (e) {
5810
5936
  if (e.code === "EACCES") {
5811
5937
  throw localExecError(ctx, `permission_denied: ${absPath}`);
@@ -5856,7 +5982,7 @@ function localFileMetadata(args) {
5856
5982
  strategy: "local_file",
5857
5983
  ingested_at: (/* @__PURE__ */ new Date()).toISOString(),
5858
5984
  file_size: args.fileSize,
5859
- original_filename: path4.basename(args.absPath),
5985
+ original_filename: path5.basename(args.absPath),
5860
5986
  ...args.rasterizedFrom ? { rasterized_from: args.rasterizedFrom } : {},
5861
5987
  ...args.durationMs !== void 0 ? { duration_ms: args.durationMs } : {}
5862
5988
  };
@@ -5865,8 +5991,6 @@ function withProbedDuration(ref, durationMs) {
5865
5991
  if (durationMs === void 0 || ref.kind !== "video") return ref;
5866
5992
  return { ...ref, duration_ms: durationMs };
5867
5993
  }
5868
- var YT_DLP_BIN = "yt-dlp";
5869
- var YT_DLP_TIMEOUT_MS = 10 * 60 * 1e3;
5870
5994
  var YT_DLP_MIME = {
5871
5995
  image: "image/png",
5872
5996
  video: "video/mp4",
@@ -5880,9 +6004,9 @@ async function execYtDlp(params, ctx) {
5880
6004
  if (params.expect !== "video" && params.expect !== "audio") {
5881
6005
  throw new Error(`ingest: yt_dlp only handles video/audio, got expect=${params.expect}`);
5882
6006
  }
5883
- const workDir = await mkdtemp(path4.join(tmpdir(), "ingest-yt-"));
6007
+ const workDir = await mkdtemp(path5.join(tmpdir(), "ingest-yt-"));
5884
6008
  try {
5885
- const { filePath, info } = await runYtDlp({
6009
+ const { filePath, info } = await runYtDlpForNode({
5886
6010
  url: params.url,
5887
6011
  audioOnly: params.expect === "audio",
5888
6012
  workDir,
@@ -5895,7 +6019,7 @@ async function execYtDlp(params, ctx) {
5895
6019
  `file_too_large: yt-dlp output for ${params.url} is ${downloadedStats.size} bytes (limit ${MAX_ASSET_BYTES})`
5896
6020
  );
5897
6021
  }
5898
- const bytes = await readFile3(filePath);
6022
+ const bytes = await readFile4(filePath);
5899
6023
  const kind = params.expect;
5900
6024
  const mime = YT_DLP_MIME[kind];
5901
6025
  const metadata = buildYtDlpMetadata(params.url, info);
@@ -5906,69 +6030,24 @@ async function execYtDlp(params, ctx) {
5906
6030
  });
5907
6031
  }
5908
6032
  }
5909
- async function runYtDlpViaLadder(url, argv, ctx) {
5910
- const routes = plannedRoutes(url, captureProxyCredentials());
5911
- let last = { code: -1, stderr: "" };
5912
- for (const route of routes) {
5913
- if (route.kind === "proxy") ctx.log(`ingest: retrying yt-dlp via ${route.tier}`);
5914
- last = await spawnAndWait(YT_DLP_BIN, argv, YT_DLP_TIMEOUT_MS, proxyEnvFor(route));
5915
- if (last.code === 0) return last;
5916
- if (!shouldEscalate(ytDlpBlockSignal(last.stderr))) return last;
5917
- }
5918
- return last;
5919
- }
5920
- function proxyEnvFor(route) {
5921
- if (route.kind !== "proxy") return void 0;
5922
- const uri = new URL(route.server);
5923
- uri.username = encodeURIComponent(route.username);
5924
- uri.password = encodeURIComponent(route.password);
5925
- return {
5926
- HTTP_PROXY: uri.toString(),
5927
- HTTPS_PROXY: uri.toString(),
5928
- http_proxy: uri.toString(),
5929
- https_proxy: uri.toString()
5930
- };
5931
- }
5932
- async function runYtDlp(args) {
5933
- const outTemplate = path4.join(args.workDir, "out.%(ext)s");
5934
- const infoPath = path4.join(args.workDir, "out.info.json");
5935
- const argv = [
5936
- args.url,
5937
- "--no-playlist",
5938
- "--no-progress",
5939
- "--no-warnings",
5940
- "--quiet",
5941
- "--write-info-json",
5942
- "-o",
5943
- outTemplate
5944
- ];
5945
- if (args.audioOnly) {
5946
- argv.push("-x", "--audio-format", "m4a");
5947
- } else {
5948
- argv.push("--format", "bv*+ba/b", "--merge-output-format", "mp4");
5949
- }
5950
- args.ctx.log(`ingest: spawning yt-dlp (${args.audioOnly ? "audio" : "video"})`);
5951
- const { code, stderr } = await runYtDlpViaLadder(args.url, argv, args.ctx);
5952
- if (code !== 0) {
5953
- throw new NodeExecutionError(args.ctx.nodeId, args.ctx.nodeType, {
5954
- kind: "local",
5955
- cause: new Error(`yt-dlp exited ${code}
5956
- ${tail(stderr, 40)}`)
5957
- });
5958
- }
5959
- const files = await readdir(args.workDir);
5960
- const wanted = args.audioOnly ? ".m4a" : ".mp4";
5961
- const downloaded = files.find((f) => f.startsWith("out.") && f.endsWith(wanted));
5962
- if (!downloaded) {
5963
- throw new Error(`ingest: yt-dlp finished but no ${wanted} file in ${args.workDir} (saw: ${files.join(", ")})`);
5964
- }
5965
- let info = {};
6033
+ async function runYtDlpForNode(args) {
5966
6034
  try {
5967
- const raw = await readFile3(infoPath, "utf-8");
5968
- info = JSON.parse(raw);
5969
- } catch {
6035
+ return await runYtDlp({
6036
+ url: args.url,
6037
+ audioOnly: args.audioOnly,
6038
+ workDir: args.workDir,
6039
+ onLog: (message2) => args.ctx.log(`ingest: ${message2}`)
6040
+ });
6041
+ } catch (error) {
6042
+ if (error instanceof YtDlpError) {
6043
+ throw new NodeExecutionError(args.ctx.nodeId, args.ctx.nodeType, {
6044
+ kind: "local",
6045
+ cause: new Error(error.stderrTail ? `${error.message}
6046
+ ${error.stderrTail}` : error.message)
6047
+ });
6048
+ }
6049
+ throw error;
5970
6050
  }
5971
- return { filePath: path4.join(args.workDir, downloaded), info };
5972
6051
  }
5973
6052
  function buildYtDlpMetadata(sourceUrl, info) {
5974
6053
  const out = {
@@ -5995,41 +6074,6 @@ async function uploadAndIngest(args) {
5995
6074
  });
5996
6075
  return { ...localRef, url: publicUrl };
5997
6076
  }
5998
- function spawnAndWait(bin, argv, timeoutMs, env) {
5999
- return new Promise((resolve, reject) => {
6000
- const child = spawn(bin, argv, {
6001
- stdio: ["ignore", "pipe", "pipe"],
6002
- ...env ? { env: childEnvWith(env) } : {}
6003
- });
6004
- const stderrChunks = [];
6005
- let stderrLen = 0;
6006
- const MAX_STDERR = 256 * 1024;
6007
- child.stdout.on("data", () => {
6008
- });
6009
- child.stderr.on("data", (chunk) => {
6010
- if (stderrLen < MAX_STDERR) {
6011
- stderrChunks.push(chunk);
6012
- stderrLen += chunk.length;
6013
- }
6014
- });
6015
- const timer = setTimeout(() => {
6016
- child.kill("SIGKILL");
6017
- reject(new Error(`ingest: ${bin} timed out after ${timeoutMs}ms`));
6018
- }, timeoutMs);
6019
- child.on("error", (err) => {
6020
- clearTimeout(timer);
6021
- reject(new Error(`ingest: failed to spawn "${bin}" \u2014 ${err.message}`));
6022
- });
6023
- child.on("close", (code) => {
6024
- clearTimeout(timer);
6025
- resolve({ code: code ?? -1, stderr: Buffer.concat(stderrChunks).toString("utf8") });
6026
- });
6027
- });
6028
- }
6029
- function tail(text, maxLines) {
6030
- const lines = text.split("\n");
6031
- return lines.slice(-maxLines).join("\n");
6032
- }
6033
6077
  async function ytDlpVersion() {
6034
6078
  try {
6035
6079
  const { stdout } = await execFile(YT_DLP_BIN, ["--version"], { encoding: "utf-8", maxBuffer: 64 * 1024 });
@@ -6060,9 +6104,9 @@ import { z as z6 } from "zod";
6060
6104
 
6061
6105
  // src/engine/nodes/local/lib/cli-runner.ts
6062
6106
  import { execFile as execFileCb2, spawn as spawn2 } from "child_process";
6063
- import { copyFile as copyFile2, mkdtemp as mkdtemp2, readFile as readFile4, rm as rm2, stat as stat3 } from "fs/promises";
6107
+ import { copyFile as copyFile2, mkdtemp as mkdtemp2, readFile as readFile5, rm as rm2, stat as stat3 } from "fs/promises";
6064
6108
  import { tmpdir as tmpdir2 } from "os";
6065
- import path5 from "path";
6109
+ import path6 from "path";
6066
6110
  import { promisify as promisify2 } from "util";
6067
6111
  var execFile2 = promisify2(execFileCb2);
6068
6112
  var DEFAULT_TIMEOUT_MS2 = 10 * 60 * 1e3;
@@ -6089,7 +6133,7 @@ function mimeForExt(ext) {
6089
6133
  }
6090
6134
  function extForAssetRef(ref) {
6091
6135
  if (ref.path) {
6092
- const e = path5.extname(ref.path);
6136
+ const e = path6.extname(ref.path);
6093
6137
  if (e) return e;
6094
6138
  }
6095
6139
  const reverse = {
@@ -6111,14 +6155,14 @@ function planArrayInputSlot(tmpDir, slot, values, lookup, stagedInputs) {
6111
6155
  const ref = values[i];
6112
6156
  if (!ref) continue;
6113
6157
  if (!ref.path) throw new Error(`cli-runner: inputs.${slot}[${i}] has no local path`);
6114
- const dest = path5.join(tmpDir, `in_${slot}_${i}${extForAssetRef(ref)}`);
6158
+ const dest = path6.join(tmpDir, `in_${slot}_${i}${extForAssetRef(ref)}`);
6115
6159
  stagedInputs.push({ srcPath: ref.path, destPath: dest });
6116
6160
  lookup.set(`in.${slot}.${i}`, dest);
6117
6161
  }
6118
6162
  }
6119
6163
  function planSingleInputSlot(tmpDir, slot, ref, lookup, stagedInputs) {
6120
6164
  if (!ref.path) throw new Error(`cli-runner: inputs.${slot} has no local path`);
6121
- const dest = path5.join(tmpDir, `in_${slot}${extForAssetRef(ref)}`);
6165
+ const dest = path6.join(tmpDir, `in_${slot}${extForAssetRef(ref)}`);
6122
6166
  stagedInputs.push({ srcPath: ref.path, destPath: dest });
6123
6167
  lookup.set(`in.${slot}`, dest);
6124
6168
  }
@@ -6126,7 +6170,7 @@ function planOutputs(tmpDir, outputs, lookup) {
6126
6170
  const outputPaths = [];
6127
6171
  for (const [name, spec] of Object.entries(outputs)) {
6128
6172
  const ext = spec.ext.startsWith(".") ? spec.ext : `.${spec.ext}`;
6129
- const absPath = path5.join(tmpDir, `out_${name}${ext}`);
6173
+ const absPath = path6.join(tmpDir, `out_${name}${ext}`);
6130
6174
  outputPaths.push({ name, absPath, spec });
6131
6175
  lookup.set(`out.${name}`, absPath);
6132
6176
  }
@@ -6167,8 +6211,8 @@ function rejectRawPaths(substituted, original, stagingDir) {
6167
6211
  throw new Error(`cli-runner: home-relative path "${original}" not allowed in args.`);
6168
6212
  }
6169
6213
  if (substituted.startsWith("/")) {
6170
- const resolved = path5.resolve(substituted);
6171
- if (!resolved.startsWith(`${stagingDir}${path5.sep}`) && resolved !== stagingDir) {
6214
+ const resolved = path6.resolve(substituted);
6215
+ if (!resolved.startsWith(`${stagingDir}${path6.sep}`) && resolved !== stagingDir) {
6172
6216
  throw new Error(
6173
6217
  `cli-runner: raw filesystem path "${original}" not allowed \u2014 declare an input slot and use {{in.<slot>}} instead.`
6174
6218
  );
@@ -6218,7 +6262,7 @@ function tailLines(text, maxLines) {
6218
6262
  }
6219
6263
  async function runCli(opts) {
6220
6264
  const { bin, args, inputs, outputs, ctx, timeoutMs = DEFAULT_TIMEOUT_MS2 } = opts;
6221
- const tmpDir = await mkdtemp2(path5.join(tmpdir2(), `cli-${bin.replace(/[^a-z0-9]/gi, "")}-`));
6265
+ const tmpDir = await mkdtemp2(path6.join(tmpdir2(), `cli-${bin.replace(/[^a-z0-9]/gi, "")}-`));
6222
6266
  try {
6223
6267
  const { lookup, stagedInputs, outputPaths } = planPlaceholders(tmpDir, inputs, outputs);
6224
6268
  await stageInputs(stagedInputs);
@@ -6240,7 +6284,7 @@ ${tailLines(stderr, 40)}`);
6240
6284
  if (!s?.isFile() || s.size === 0) {
6241
6285
  throw new Error(`cli-runner: declared output "${name}" missing or empty at ${absPath}`);
6242
6286
  }
6243
- const bytes = await readFile4(absPath);
6287
+ const bytes = await readFile5(absPath);
6244
6288
  const ref = await ctx.assets.ingestBytes({
6245
6289
  bytes: Buffer.from(bytes),
6246
6290
  kind: spec.kind,
@@ -6498,17 +6542,17 @@ var ffmpegNode = defineNode({
6498
6542
  import { mkdtemp as mkdtemp3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
6499
6543
  import { createRequire } from "module";
6500
6544
  import { tmpdir as tmpdir3 } from "os";
6501
- import path7 from "path";
6545
+ import path8 from "path";
6502
6546
  import { z as z9 } from "zod";
6503
6547
 
6504
6548
  // src/engine/nodes/local/lib/assets.ts
6505
- import { copyFile as copyFile3, readFile as readFile5 } from "fs/promises";
6506
- import path6 from "path";
6549
+ import { copyFile as copyFile3, readFile as readFile6 } from "fs/promises";
6550
+ import path7 from "path";
6507
6551
  async function stageAsset(ref, destDir, filename) {
6508
6552
  if (!ref.path) {
6509
6553
  throw new Error(`stageAsset: ref (${ref.kind}/${ref.mime}) has no local path`);
6510
6554
  }
6511
- const dest = path6.join(destDir, filename);
6555
+ const dest = path7.join(destDir, filename);
6512
6556
  await copyFile3(ref.path, dest);
6513
6557
  return dest;
6514
6558
  }
@@ -6517,7 +6561,7 @@ async function refToUrl(ref) {
6517
6561
  if (!ref.path) {
6518
6562
  throw new Error("refToUrl: AssetRef has neither url nor path");
6519
6563
  }
6520
- const bytes = await readFile5(ref.path);
6564
+ const bytes = await readFile6(ref.path);
6521
6565
  return `data:${ref.mime};base64,${bytes.toString("base64")}`;
6522
6566
  }
6523
6567
  var ASSET_KINDS = /* @__PURE__ */ new Set(["image", "video", "audio", "json", "text", "font"]);
@@ -6591,11 +6635,11 @@ var fontSpecimenNode = defineNode({
6591
6635
  outputKinds: { image: "image" },
6592
6636
  cost: () => ({ credits: 0, seconds_estimate: 5 }),
6593
6637
  async execute({ inputs, params, ctx }) {
6594
- const tmp = await mkdtemp3(path7.join(tmpdir3(), "font-specimen-"));
6638
+ const tmp = await mkdtemp3(path8.join(tmpdir3(), "font-specimen-"));
6595
6639
  try {
6596
6640
  const fontFilename = `font.${extForMime(inputs.font.mime)}`;
6597
6641
  await stageAsset(inputs.font, tmp, fontFilename);
6598
- const entryPath = path7.join(tmp, "index.html");
6642
+ const entryPath = path8.join(tmp, "index.html");
6599
6643
  await writeFile3(entryPath, buildSpecimenHtml(params, fontFilename), "utf-8");
6600
6644
  ctx.log(`rendering specimen (${params.font_size}px, ${params.text.split("\n").length} lines)`);
6601
6645
  const pwSpecifier = ["play", "wright"].join("");
@@ -6679,16 +6723,16 @@ var fontSpecimenNode = defineNode({
6679
6723
 
6680
6724
  // src/engine/nodes/local/hyperframe.ts
6681
6725
  import { execFile as execFile4 } from "child_process";
6682
- import { copyFile as copyFile4, mkdtemp as mkdtemp4, readFile as readFile9, rm as rm4, stat as stat5, writeFile as writeFile5 } from "fs/promises";
6726
+ import { copyFile as copyFile4, mkdtemp as mkdtemp4, readFile as readFile10, rm as rm4, stat as stat5, writeFile as writeFile5 } from "fs/promises";
6683
6727
  import { createRequire as createRequire2 } from "module";
6684
6728
  import { cpus, tmpdir as tmpdir4 } from "os";
6685
- import path12 from "path";
6729
+ import path13 from "path";
6686
6730
  import { promisify as promisify4 } from "util";
6687
6731
  import { z as z11 } from "zod";
6688
6732
 
6689
6733
  // src/engine/engine/composition-hash.ts
6690
- import { readdir as readdir2, readFile as readFile6, stat as stat4 } from "fs/promises";
6691
- import path8 from "path";
6734
+ import { readdir as readdir2, readFile as readFile7, stat as stat4 } from "fs/promises";
6735
+ import path9 from "path";
6692
6736
  var SKIP_DIRS = /* @__PURE__ */ new Set([".cache", ".git", "node_modules", "dist", "build", ".next", ".turbo"]);
6693
6737
  function isSkippedName(name) {
6694
6738
  if (name.startsWith(".")) return true;
@@ -6705,23 +6749,23 @@ async function collectFiles(root, current) {
6705
6749
  const names = await readdir2(current);
6706
6750
  for (const name of names) {
6707
6751
  if (isSkippedName(name)) continue;
6708
- const abs = path8.join(current, name);
6752
+ const abs = path9.join(current, name);
6709
6753
  const s = await stat4(abs);
6710
6754
  if (s.isDirectory()) {
6711
6755
  out.push(...await collectFiles(root, abs));
6712
6756
  continue;
6713
6757
  }
6714
6758
  if (!s.isFile()) continue;
6715
- const bytes = await readFile6(abs);
6716
- const relPath = path8.relative(root, abs).split(path8.sep).join("/");
6759
+ const bytes = await readFile7(abs);
6760
+ const relPath = path9.relative(root, abs).split(path9.sep).join("/");
6717
6761
  out.push({ relPath, contentSha: sha256Hex(bytes) });
6718
6762
  }
6719
6763
  return out;
6720
6764
  }
6721
6765
 
6722
6766
  // src/engine/engine/composition-meta.ts
6723
- import { readFile as readFile7 } from "fs/promises";
6724
- import path9 from "path";
6767
+ import { readFile as readFile8 } from "fs/promises";
6768
+ import path10 from "path";
6725
6769
  import { z as z10 } from "zod";
6726
6770
  var InputKind = z10.enum(["video", "image", "audio", "json"]);
6727
6771
  var InputSpec = z10.object({
@@ -6798,10 +6842,10 @@ var CompositionMetaSchema = z10.object({
6798
6842
  params: z10.record(z10.string(), ParamSpec).default({})
6799
6843
  }).strict();
6800
6844
  async function loadCompositionMeta(compositionDir) {
6801
- const metaPath = path9.join(compositionDir, "meta.json");
6845
+ const metaPath = path10.join(compositionDir, "meta.json");
6802
6846
  let raw;
6803
6847
  try {
6804
- raw = await readFile7(metaPath, "utf-8");
6848
+ raw = await readFile8(metaPath, "utf-8");
6805
6849
  } catch (e) {
6806
6850
  throw new Error(`composition meta: cannot read ${metaPath} (${e.message})`);
6807
6851
  }
@@ -6879,8 +6923,8 @@ function defaultFilenameForInput(key, kind) {
6879
6923
 
6880
6924
  // src/engine/nodes/local/lib/hyperframe-check.ts
6881
6925
  import { execFile as execFile3 } from "child_process";
6882
- import { readFile as readFile8 } from "fs/promises";
6883
- import path10 from "path";
6926
+ import { readFile as readFile9 } from "fs/promises";
6927
+ import path11 from "path";
6884
6928
  import { promisify as promisify3 } from "util";
6885
6929
  var execFileAsync = promisify3(execFile3);
6886
6930
  var NEVER_BLOCK = [
@@ -7032,7 +7076,7 @@ ${detail}`);
7032
7076
  }
7033
7077
  let indexHtml = "";
7034
7078
  try {
7035
- indexHtml = await readFile8(path10.join(dir, "index.html"), "utf-8");
7079
+ indexHtml = await readFile9(path11.join(dir, "index.html"), "utf-8");
7036
7080
  } catch {
7037
7081
  indexHtml = "";
7038
7082
  }
@@ -7097,9 +7141,9 @@ ${stderr.slice(0, 1500)}`;
7097
7141
 
7098
7142
  // src/engine/nodes/local/lib/hyperframe-meta.ts
7099
7143
  import { writeFile as writeFile4 } from "fs/promises";
7100
- import path11 from "path";
7144
+ import path12 from "path";
7101
7145
  async function ensureHyperframesMetaJson(tmp, nodeId, meta, duration) {
7102
- const metaPath = path11.join(tmp, "meta.json");
7146
+ const metaPath = path12.join(tmp, "meta.json");
7103
7147
  await writeFile4(
7104
7148
  metaPath,
7105
7149
  JSON.stringify(
@@ -7199,7 +7243,7 @@ var hyperframeRenderNode = defineNode({
7199
7243
  const compositionDir = await resolveCompositionDir(params.composition);
7200
7244
  const meta = await loadCompositionMeta(compositionDir);
7201
7245
  const compositionParams = validateAndParseDynamicParams(meta, params);
7202
- const tmp = await mkdtemp4(path12.join(tmpdir4(), "hf-render-"));
7246
+ const tmp = await mkdtemp4(path13.join(tmpdir4(), "hf-render-"));
7203
7247
  try {
7204
7248
  await copyComposition(compositionDir, tmp);
7205
7249
  await vendorGsap(tmp, ctx);
@@ -7209,9 +7253,9 @@ var hyperframeRenderNode = defineNode({
7209
7253
  await substituteCompositionFiles(tmp, substitutionValues);
7210
7254
  await ensureHyperframesMetaJson(tmp, ctx.nodeId, meta, duration);
7211
7255
  await runHyperframesCheck({ dir: tmp, nodeId: "hyperframe_render", ctx, timeoutMs: params.timeout_ms });
7212
- const outputPath = path12.join(tmp, `output.${params.format}`);
7256
+ const outputPath = path13.join(tmp, `output.${params.format}`);
7213
7257
  await runRender({ tmp, outputPath, params, meta, ctx });
7214
- const bytes = await readFile9(outputPath);
7258
+ const bytes = await readFile10(outputPath);
7215
7259
  ctx.log(`rendered ${bytes.length} bytes`);
7216
7260
  const ref = await ctx.assets.ingestBytes({
7217
7261
  bytes: Buffer.from(bytes),
@@ -7233,10 +7277,10 @@ var hyperframeRenderNode = defineNode({
7233
7277
  }
7234
7278
  });
7235
7279
  async function resolveCompositionDir(composition) {
7236
- const compositionPath = path12.isAbsolute(composition) ? composition : path12.resolve(process.cwd(), composition);
7280
+ const compositionPath = path13.isAbsolute(composition) ? composition : path13.resolve(process.cwd(), composition);
7237
7281
  const s = await stat5(compositionPath);
7238
7282
  if (s.isDirectory()) return compositionPath;
7239
- return path12.dirname(compositionPath);
7283
+ return path13.dirname(compositionPath);
7240
7284
  }
7241
7285
  async function validateComposition(rawParams) {
7242
7286
  const issues = await validateCompositionParams(rawParams);
@@ -7318,7 +7362,7 @@ async function copyComposition(srcDir, destDir) {
7318
7362
  await cp(srcDir, destDir, {
7319
7363
  recursive: true,
7320
7364
  filter: (src) => {
7321
- const name = path12.basename(src);
7365
+ const name = path13.basename(src);
7322
7366
  if (name === ".cache" || name === "node_modules" || name === ".git") return false;
7323
7367
  return true;
7324
7368
  }
@@ -7327,7 +7371,7 @@ async function copyComposition(srcDir, destDir) {
7327
7371
  async function vendorGsap(tmp, ctx) {
7328
7372
  try {
7329
7373
  const gsapMin = require_2.resolve("gsap/dist/gsap.min.js");
7330
- await copyFile4(gsapMin, path12.join(tmp, "gsap.min.js"));
7374
+ await copyFile4(gsapMin, path13.join(tmp, "gsap.min.js"));
7331
7375
  } catch (e) {
7332
7376
  ctx.log(`warning: could not vendor gsap.min.js (${e.message}); compositions must self-supply`);
7333
7377
  }
@@ -7342,7 +7386,7 @@ async function stageInputs2(tmp, inputs, meta, ctx) {
7342
7386
  await stageAsset(ref, tmp, filename);
7343
7387
  ctx.log(`staged ${spec.kind} \u2192 ${filename}`);
7344
7388
  if (spec.kind === "video" && primaryDuration === null) {
7345
- primaryDuration = await probeDurationSeconds(path12.join(tmp, filename));
7389
+ primaryDuration = await probeDurationSeconds(path13.join(tmp, filename));
7346
7390
  }
7347
7391
  }
7348
7392
  return primaryDuration;
@@ -7388,8 +7432,8 @@ function coerceImageParam(value) {
7388
7432
  throw new Error("hyperframe_render: image param must be a URL string or AssetRef");
7389
7433
  }
7390
7434
  async function substituteCompositionFiles(tmp, values) {
7391
- const entryPath = path12.join(tmp, "index.html");
7392
- const original = await readFile9(entryPath, "utf-8");
7435
+ const entryPath = path13.join(tmp, "index.html");
7436
+ const original = await readFile10(entryPath, "utf-8");
7393
7437
  const { output, missing } = substituteVariables(original, values);
7394
7438
  if (missing.length > 0) {
7395
7439
  throw new Error(
@@ -7405,7 +7449,7 @@ function workerCount() {
7405
7449
  async function runRender(opts) {
7406
7450
  const { tmp, outputPath, params, meta, ctx } = opts;
7407
7451
  const args = buildRenderArgs(tmp, outputPath, meta, params.format);
7408
- ctx.log(`rendering ${meta.width}x${meta.height}@${meta.fps}fps ${params.format} from ${path12.basename(tmp)}`);
7452
+ ctx.log(`rendering ${meta.width}x${meta.height}@${meta.fps}fps ${params.format} from ${path13.basename(tmp)}`);
7409
7453
  try {
7410
7454
  await execFileAsync2("npx", args, { timeout: params.timeout_ms, maxBuffer: 64 * 1024 * 1024 });
7411
7455
  } catch (e) {
@@ -7449,10 +7493,10 @@ async function probeDurationSeconds(filePath) {
7449
7493
 
7450
7494
  // src/engine/nodes/local/hyperframe-snapshot.ts
7451
7495
  import { execFile as execFile5 } from "child_process";
7452
- import { copyFile as copyFile5, mkdtemp as mkdtemp5, readFile as readFile10, rm as rm5, writeFile as writeFile6 } from "fs/promises";
7496
+ import { copyFile as copyFile5, mkdtemp as mkdtemp5, readFile as readFile11, rm as rm5, writeFile as writeFile6 } from "fs/promises";
7453
7497
  import { createRequire as createRequire3 } from "module";
7454
7498
  import { tmpdir as tmpdir5 } from "os";
7455
- import path13 from "path";
7499
+ import path14 from "path";
7456
7500
  import { promisify as promisify5 } from "util";
7457
7501
  import { z as z12 } from "zod";
7458
7502
  var _execFileAsync = promisify5(execFile5);
@@ -7499,7 +7543,7 @@ var hyperframeSnapshotNode = defineNode({
7499
7543
  const compositionDir = await resolveCompositionDir(params.composition);
7500
7544
  const meta = await loadCompositionMeta(compositionDir);
7501
7545
  const compositionParams = validateAndParseDynamicParams2(meta, params);
7502
- const tmp = await mkdtemp5(path13.join(tmpdir5(), "hf-snap-"));
7546
+ const tmp = await mkdtemp5(path14.join(tmpdir5(), "hf-snap-"));
7503
7547
  try {
7504
7548
  await copyComposition2(compositionDir, tmp);
7505
7549
  await vendorGsap2(tmp, ctx);
@@ -7514,7 +7558,7 @@ var hyperframeSnapshotNode = defineNode({
7514
7558
  timeoutMs: params.timeout_ms,
7515
7559
  samples: 1
7516
7560
  });
7517
- const entryPath = path13.join(tmp, "index.html");
7561
+ const entryPath = path14.join(tmp, "index.html");
7518
7562
  const entryUrl = `file://${entryPath}`;
7519
7563
  ctx.log(`snapshotting ${meta.width}x${meta.height}@${DEVICE_SCALE_FACTOR2}x wait=${params.wait_for.kind}`);
7520
7564
  const pwSpecifier = ["play", "wright"].join("");
@@ -7575,7 +7619,7 @@ async function copyComposition2(srcDir, destDir) {
7575
7619
  await cp(srcDir, destDir, {
7576
7620
  recursive: true,
7577
7621
  filter: (src) => {
7578
- const name = path13.basename(src);
7622
+ const name = path14.basename(src);
7579
7623
  if (name === ".cache" || name === "node_modules" || name === ".git") return false;
7580
7624
  return true;
7581
7625
  }
@@ -7584,7 +7628,7 @@ async function copyComposition2(srcDir, destDir) {
7584
7628
  async function vendorGsap2(tmp, ctx) {
7585
7629
  try {
7586
7630
  const gsapMin = require_3.resolve("gsap/dist/gsap.min.js");
7587
- await copyFile5(gsapMin, path13.join(tmp, "gsap.min.js"));
7631
+ await copyFile5(gsapMin, path14.join(tmp, "gsap.min.js"));
7588
7632
  } catch (e) {
7589
7633
  ctx.log(`warning: could not vendor gsap.min.js (${e.message}); compositions must self-supply`);
7590
7634
  }
@@ -7618,8 +7662,8 @@ function coerceImageParam2(value) {
7618
7662
  throw new Error("hyperframe_snapshot: image param must be a URL string or AssetRef");
7619
7663
  }
7620
7664
  async function substituteCompositionFiles2(tmp, values) {
7621
- const entryPath = path13.join(tmp, "index.html");
7622
- const original = await readFile10(entryPath, "utf-8");
7665
+ const entryPath = path14.join(tmp, "index.html");
7666
+ const original = await readFile11(entryPath, "utf-8");
7623
7667
  const { output, missing } = substituteVariables(original, values);
7624
7668
  if (missing.length > 0) {
7625
7669
  throw new Error(
@@ -8368,9 +8412,9 @@ var videoLipsyncNode = delegated({
8368
8412
  });
8369
8413
 
8370
8414
  // src/engine/nodes/remote/videoTranscribe.ts
8371
- import { mkdtemp as mkdtemp6, readFile as readFile11, rm as rm6 } from "fs/promises";
8415
+ import { mkdtemp as mkdtemp6, readFile as readFile12, rm as rm6 } from "fs/promises";
8372
8416
  import { tmpdir as tmpdir6 } from "os";
8373
- import path14 from "path";
8417
+ import path15 from "path";
8374
8418
  import { z as z33 } from "zod";
8375
8419
 
8376
8420
  // src/engine/nodes/local/lib/ffmpeg.ts
@@ -8505,14 +8549,14 @@ async function tryExtractAudio(inputs, ctx) {
8505
8549
  ctx.log("video_transcribe: no audio track detected, sending full video");
8506
8550
  return null;
8507
8551
  }
8508
- tmpDir = await mkdtemp6(path14.join(tmpdir6(), "vtx-"));
8509
- const audioPath = path14.join(tmpDir, "audio.mp3");
8552
+ tmpDir = await mkdtemp6(path15.join(tmpdir6(), "vtx-"));
8553
+ const audioPath = path15.join(tmpDir, "audio.mp3");
8510
8554
  ctx.log("video_transcribe: extracting audio (mono 16kHz mp3)");
8511
8555
  await runFfmpeg(
8512
8556
  ["-i", video.path, "-vn", "-ac", "1", "-ar", "16000", "-b:a", "64k", "-f", "mp3", "-y", audioPath],
8513
8557
  { timeout_ms: AUDIO_EXTRACT_TIMEOUT_MS }
8514
8558
  );
8515
- const bytes = await readFile11(audioPath);
8559
+ const bytes = await readFile12(audioPath);
8516
8560
  if (bytes.byteLength === 0) {
8517
8561
  ctx.log("video_transcribe: extracted audio is empty, sending full video");
8518
8562
  return null;
@@ -8619,19 +8663,19 @@ function safeCost(def) {
8619
8663
 
8620
8664
  // src/engine/storage/cache-store.ts
8621
8665
  import { randomUUID as randomUUID2 } from "crypto";
8622
- import { mkdir as mkdir3, readFile as readFile12, rename as rename2, writeFile as writeFile7 } from "fs/promises";
8623
- import path15 from "path";
8666
+ import { mkdir as mkdir3, readFile as readFile13, rename as rename2, writeFile as writeFile7 } from "fs/promises";
8667
+ import path16 from "path";
8624
8668
  var LocalCacheStore = class {
8625
8669
  rootDir;
8626
8670
  constructor(rootDir) {
8627
8671
  this.rootDir = rootDir;
8628
8672
  }
8629
8673
  filePath(cacheKey) {
8630
- return path15.join(this.rootDir, `${cacheKey}.json`);
8674
+ return path16.join(this.rootDir, `${cacheKey}.json`);
8631
8675
  }
8632
8676
  async get(cacheKey) {
8633
8677
  try {
8634
- const buf = await readFile12(this.filePath(cacheKey), "utf8");
8678
+ const buf = await readFile13(this.filePath(cacheKey), "utf8");
8635
8679
  return JSON.parse(buf);
8636
8680
  } catch (e) {
8637
8681
  if (e.code === "ENOENT") return null;
@@ -8640,7 +8684,7 @@ var LocalCacheStore = class {
8640
8684
  }
8641
8685
  async put(entry) {
8642
8686
  const dest = this.filePath(entry.cacheKey);
8643
- await mkdir3(path15.dirname(dest), { recursive: true });
8687
+ await mkdir3(path16.dirname(dest), { recursive: true });
8644
8688
  const tmp = `${dest}.tmp-${process.pid}-${randomUUID2()}`;
8645
8689
  await writeFile7(tmp, JSON.stringify(entry, null, 0));
8646
8690
  await rename2(tmp, dest);
@@ -8696,12 +8740,12 @@ function defaultRegistry() {
8696
8740
  }
8697
8741
  function createEngineFromEnv(opts = {}) {
8698
8742
  const cwd = opts.cwd ?? process.cwd();
8699
- const cacheDir = opts.cacheDir ?? path16.join(cwd, "canvas", ".cache");
8700
- const outputsDir = opts.outputsDir ?? path16.join(cwd, "canvas");
8743
+ const cacheDir = opts.cacheDir ?? path17.join(cwd, "canvas", ".cache");
8744
+ const outputsDir = opts.outputsDir ?? path17.join(cwd, "canvas");
8701
8745
  const creds = requireCredentialsFromEnv();
8702
8746
  const client = new BackendClient({ baseUrl: creds.url, apiKey: creds.apiKey });
8703
- const assets = new LocalAssetStore(path16.join(cacheDir, "assets"));
8704
- const localCache = new LocalCacheStore(path16.join(cacheDir, "index"));
8747
+ const assets = new LocalAssetStore(path17.join(cacheDir, "assets"));
8748
+ const localCache = new LocalCacheStore(path17.join(cacheDir, "index"));
8705
8749
  const remoteCacheEnabled = opts.remoteCache ?? remoteCacheEnabledFromEnv();
8706
8750
  const cache = remoteCacheEnabled ? new LayeredCacheStore({
8707
8751
  local: localCache,
@@ -8764,6 +8808,8 @@ export {
8764
8808
  elementMentionKeywords,
8765
8809
  MAX_REMOTE_IMAGE_BYTES,
8766
8810
  fetchExternalBytes,
8811
+ YtDlpError,
8812
+ runYtDlp,
8767
8813
  toModelSafeImage,
8768
8814
  BackendClient2,
8769
8815
  Engine2 as Engine,
@@ -8775,4 +8821,4 @@ export {
8775
8821
  defaultRegistry,
8776
8822
  createEngineFromEnv
8777
8823
  };
8778
- //# sourceMappingURL=chunk-5MPIOGRO.js.map
8824
+ //# sourceMappingURL=chunk-ONFW7HIK.js.map