@koda-sl/baker-cli 0.203.0 → 0.208.1

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.
Files changed (27) hide show
  1. package/README.md +64 -2
  2. package/dist/{chunk-UZYVSPCA.js → chunk-3E33AGNR.js} +2 -2
  3. package/dist/{chunk-SVHFT7AN.js → chunk-3WODSPNZ.js} +4 -4
  4. package/dist/{chunk-2R2DEPG7.js → chunk-CRXSLYEX.js} +3 -3
  5. package/dist/{chunk-5MPIOGRO.js → chunk-E2WYWHJJ.js} +370 -221
  6. package/dist/chunk-E2WYWHJJ.js.map +1 -0
  7. package/dist/{chunk-KHHD26LZ.js → chunk-FUSTYLP5.js} +3 -3
  8. package/dist/{chunk-6NZG2TCM.js → chunk-IRNM3T6B.js} +6 -1
  9. package/dist/{chunk-6NZG2TCM.js.map → chunk-IRNM3T6B.js.map} +1 -1
  10. package/dist/cli.js +767 -117
  11. package/dist/cli.js.map +1 -1
  12. package/dist/client-CF6ZORCO.js +15 -0
  13. package/dist/engine/index.js +3 -3
  14. package/dist/{env-FF7ZB5W5.js → env-7SCX7HAH.js} +4 -2
  15. package/dist/{output-YGPWST77.js → output-4YIOSWOG.js} +5 -5
  16. package/dist/{shared-CRWB5B7P.js → shared-EBF5DEDJ.js} +6 -6
  17. package/package.json +1 -1
  18. package/dist/chunk-5MPIOGRO.js.map +0 -1
  19. package/dist/client-MP7BKFKM.js +0 -15
  20. /package/dist/{chunk-UZYVSPCA.js.map → chunk-3E33AGNR.js.map} +0 -0
  21. /package/dist/{chunk-SVHFT7AN.js.map → chunk-3WODSPNZ.js.map} +0 -0
  22. /package/dist/{chunk-2R2DEPG7.js.map → chunk-CRXSLYEX.js.map} +0 -0
  23. /package/dist/{chunk-KHHD26LZ.js.map → chunk-FUSTYLP5.js.map} +0 -0
  24. /package/dist/{client-MP7BKFKM.js.map → client-CF6ZORCO.js.map} +0 -0
  25. /package/dist/{env-FF7ZB5W5.js.map → env-7SCX7HAH.js.map} +0 -0
  26. /package/dist/{output-YGPWST77.js.map → output-4YIOSWOG.js.map} +0 -0
  27. /package/dist/{shared-CRWB5B7P.js.map → shared-EBF5DEDJ.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  debugLogHttp,
3
3
  readBodyForLog
4
- } from "./chunk-UZYVSPCA.js";
4
+ } from "./chunk-3E33AGNR.js";
5
5
  import {
6
6
  __commonJS,
7
7
  __toESM,
@@ -13,7 +13,7 @@ import {
13
13
  plannedRoutes,
14
14
  refuseNonPublicUrl,
15
15
  shouldEscalate
16
- } from "./chunk-6NZG2TCM.js";
16
+ } from "./chunk-IRNM3T6B.js";
17
17
 
18
18
  // ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/index.js
19
19
  var require_safe_stable_stringify = __commonJS({
@@ -612,7 +612,7 @@ ${originalIndentation}`;
612
612
  });
613
613
 
614
614
  // src/engine/index.ts
615
- import path16 from "path";
615
+ import path18 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(path19, body, signal) {
665
+ return await this.requestJson("POST", path19, body, signal);
666
666
  }
667
- async putJson(path17, body, signal) {
668
- return await this.requestJson("PUT", path17, body, signal);
667
+ async putJson(path19, body, signal) {
668
+ return await this.requestJson("PUT", path19, body, signal);
669
669
  }
670
- async getJson(path17, signal) {
671
- return await this.requestJson("GET", path17, void 0, signal);
670
+ async getJson(path19, signal) {
671
+ return await this.requestJson("GET", path19, 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, path19, body, signal) {
674
+ const url = `${this.baseUrl}${path19.startsWith("/") ? path19 : `/${path19}`}`;
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 path19 = `/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(path19, 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 path19 = version ? `/api/canvas/artifacts/${encodeURIComponent(kind)}/${encodeURIComponent(name)}/${encodeURIComponent(version)}` : `/api/canvas/artifacts/${encodeURIComponent(kind)}/${encodeURIComponent(name)}`;
922
+ return this.http.getJson(path19, signal);
923
923
  }
924
924
  };
925
925
 
@@ -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 path19 = pathPrefix ? tail2 ? `${pathPrefix}.${tail2}` : pathPrefix : tail2;
4293
4293
  issues.push({
4294
- path: path17,
4294
+ path: path19,
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(path19) {
4304
+ return path19.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 path6 from "path";
4917
4917
  import { promisify } from "util";
4918
4918
  import { z as z5 } from "zod";
4919
4919
 
@@ -5231,6 +5231,248 @@ 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 path5 from "path";
5238
+
5239
+ // src/lib/vimeoCookie.ts
5240
+ import { writeFile as writeFile3 } from "fs/promises";
5241
+ import path4 from "path";
5242
+ var COOKIE_NAME = "vimeo";
5243
+ async function writeVimeoCookieFile(cookie, workDir) {
5244
+ const value = cookie?.trim();
5245
+ if (!value) return void 0;
5246
+ const expires = Math.floor(Date.now() / 1e3) + 365 * 24 * 60 * 60;
5247
+ const jar = [
5248
+ "# Netscape HTTP Cookie File",
5249
+ [".vimeo.com", "TRUE", "/", "TRUE", String(expires), COOKIE_NAME, value].join(" "),
5250
+ ""
5251
+ ].join("\n");
5252
+ const filePath = path4.join(workDir, "vimeo-cookies.txt");
5253
+ await writeFile3(filePath, jar, { mode: 384 });
5254
+ return filePath;
5255
+ }
5256
+ var VIMEO_AUTH_SIGNATURES = ["oauth token", "only works when logged-in", "unable to fetch new oauth tokens"];
5257
+ function isVimeoAuthFailure(detail) {
5258
+ const haystack = detail.toLowerCase();
5259
+ if (!haystack.includes("[vimeo]")) return false;
5260
+ return VIMEO_AUTH_SIGNATURES.some((signature) => haystack.includes(signature));
5261
+ }
5262
+
5263
+ // src/lib/ytDlpSignal.ts
5264
+ var HTTP_ERROR = /HTTP Error (\d{3})/i;
5265
+ var CHALLENGE_PHRASES = [
5266
+ "sign in to confirm you're not a bot",
5267
+ "sign in to confirm you\u2019re not a bot",
5268
+ "confirm you are not a bot",
5269
+ "unusual traffic",
5270
+ "captcha"
5271
+ ];
5272
+ var NOT_A_BLOCK_PHRASES = ["private video", "video unavailable", "members-only", "this video is available to"];
5273
+ var PROXY_TUNNEL_FAILURE = /tunnel connection failed: 407|proxy authentication required|http error 407/i;
5274
+ function ytDlpBlockSignal(stderr) {
5275
+ const text = stderr.toLowerCase();
5276
+ if (PROXY_TUNNEL_FAILURE.test(stderr)) return { status: 407 };
5277
+ if (NOT_A_BLOCK_PHRASES.some((phrase) => text.includes(phrase))) return {};
5278
+ const status = HTTP_ERROR.exec(stderr);
5279
+ if (status) return { status: Number(status[1]) };
5280
+ if (CHALLENGE_PHRASES.some((phrase) => text.includes(phrase))) return { challenge: true };
5281
+ return {};
5282
+ }
5283
+
5284
+ // src/lib/ytDlp.ts
5285
+ var YT_DLP_BIN = "yt-dlp";
5286
+ var YT_DLP_TIMEOUT_MS = 10 * 60 * 1e3;
5287
+ var YT_DLP_PROBE_TIMEOUT_MS = 60 * 1e3;
5288
+ var YtDlpError = class extends Error {
5289
+ constructor(message2, stderrTail) {
5290
+ super(message2);
5291
+ this.stderrTail = stderrTail;
5292
+ this.name = "YtDlpError";
5293
+ }
5294
+ stderrTail;
5295
+ };
5296
+ function tail(text, maxLines) {
5297
+ return text.split("\n").slice(-maxLines).join("\n");
5298
+ }
5299
+ async function runYtDlp(args) {
5300
+ const outTemplate = path5.join(args.workDir, "out.%(ext)s");
5301
+ const infoPath = path5.join(args.workDir, "out.info.json");
5302
+ const argv = [
5303
+ args.url,
5304
+ "--no-playlist",
5305
+ "--no-progress",
5306
+ "--no-warnings",
5307
+ "--quiet",
5308
+ "--write-info-json",
5309
+ "-o",
5310
+ outTemplate
5311
+ ];
5312
+ if (args.audioOnly) {
5313
+ argv.push("-x", "--audio-format", "m4a");
5314
+ } else {
5315
+ const format = args.maxHeight ? `bv*[height<=${args.maxHeight}]+ba/b[height<=${args.maxHeight}]/bv*+ba/b` : "bv*+ba/b";
5316
+ argv.push("--format", format, "--merge-output-format", "mp4");
5317
+ }
5318
+ const cookieFile = await writeVimeoCookieFile(args.vimeoCookie, args.workDir);
5319
+ if (cookieFile) argv.push("--cookies", cookieFile);
5320
+ args.onLog?.(`yt-dlp: downloading ${args.audioOnly ? "audio" : "video"}`);
5321
+ const { code, stderr, tier } = await runViaLadder({
5322
+ url: args.url,
5323
+ argv,
5324
+ timeoutMs: args.timeoutMs ?? YT_DLP_TIMEOUT_MS,
5325
+ onLog: args.onLog,
5326
+ country: args.country
5327
+ });
5328
+ if (code !== 0) {
5329
+ throw new YtDlpError(`yt-dlp exited ${code}`, tail(stderr, 40));
5330
+ }
5331
+ const files = await readdir(args.workDir);
5332
+ const wanted = args.audioOnly ? ".m4a" : ".mp4";
5333
+ const downloaded = files.find((f) => f.startsWith("out.") && f.endsWith(wanted));
5334
+ if (!downloaded) {
5335
+ throw new YtDlpError(`yt-dlp finished but produced no ${wanted} file (saw: ${files.join(", ")})`, "");
5336
+ }
5337
+ let info = {};
5338
+ try {
5339
+ info = JSON.parse(await readFile3(infoPath, "utf-8"));
5340
+ } catch {
5341
+ }
5342
+ return { filePath: path5.join(args.workDir, downloaded), info, tier };
5343
+ }
5344
+ async function probeYtDlp(args) {
5345
+ const argv = [
5346
+ args.url,
5347
+ "--no-playlist",
5348
+ "--no-warnings",
5349
+ "--dump-single-json",
5350
+ "--skip-download",
5351
+ // Without this the probe fails wherever the *download* would fail, which
5352
+ // defeats the point of probing: yt-dlp still runs format selection for
5353
+ // `--dump-single-json`, so a binary too old to read a site's format list
5354
+ // exits "Requested format is not available" and we learn nothing — measured
5355
+ // against a stale build, which returned full metadata the moment this flag
5356
+ // was added. Duration and id come from the page, not from a format.
5357
+ "--ignore-no-formats-error"
5358
+ ];
5359
+ if (args.cookieDir) {
5360
+ const cookieFile = await writeVimeoCookieFile(args.vimeoCookie, args.cookieDir);
5361
+ if (cookieFile) argv.push("--cookies", cookieFile);
5362
+ }
5363
+ let stdout = "";
5364
+ const { code, stderr, tier } = await walkYtDlpLadder({
5365
+ url: args.url,
5366
+ credentials: captureProxyCredentials(),
5367
+ attempt: async (route) => {
5368
+ const result = await spawnAndWait(YT_DLP_BIN, argv, args.timeoutMs ?? YT_DLP_PROBE_TIMEOUT_MS, {
5369
+ extraEnv: proxyEnvFor(route),
5370
+ captureStdout: true
5371
+ });
5372
+ stdout = result.stdout ?? "";
5373
+ return result;
5374
+ },
5375
+ onLog: args.onLog,
5376
+ country: args.country
5377
+ });
5378
+ if (code !== 0) {
5379
+ throw new YtDlpError(`yt-dlp exited ${code}`, tail(stderr, 40));
5380
+ }
5381
+ let parsed;
5382
+ try {
5383
+ parsed = JSON.parse(stdout);
5384
+ } catch {
5385
+ throw new YtDlpError("yt-dlp returned metadata that could not be read", tail(stdout, 5));
5386
+ }
5387
+ if (parsed === null || typeof parsed !== "object") {
5388
+ throw new YtDlpError("yt-dlp returned no metadata for that link", tail(stderr, 40));
5389
+ }
5390
+ return { info: parsed, tier };
5391
+ }
5392
+ function isAudioOnly(info) {
5393
+ return info.vcodec === "none";
5394
+ }
5395
+ function durationSeconds(info) {
5396
+ const raw = info.duration;
5397
+ return typeof raw === "number" && Number.isFinite(raw) && raw > 0 ? raw : void 0;
5398
+ }
5399
+ function canonicalUrl(info) {
5400
+ const raw = info.webpage_url;
5401
+ return typeof raw === "string" && raw.length > 0 ? raw : void 0;
5402
+ }
5403
+ async function walkYtDlpLadder(args) {
5404
+ const routes = plannedRoutes(args.url, args.credentials, args.country ? { country: args.country } : void 0);
5405
+ let last = { code: -1, stderr: "", tier: "direct" };
5406
+ for (const route of routes) {
5407
+ const tier = route.kind === "proxy" ? route.tier : "direct";
5408
+ if (route.kind === "proxy") args.onLog?.(`yt-dlp: retrying via ${tier}`);
5409
+ last = { ...await args.attempt(route), tier };
5410
+ if (last.code === 0) return last;
5411
+ if (!shouldEscalate(ytDlpBlockSignal(last.stderr))) return last;
5412
+ }
5413
+ return last;
5414
+ }
5415
+ function runViaLadder(args) {
5416
+ return walkYtDlpLadder({
5417
+ url: args.url,
5418
+ credentials: captureProxyCredentials(),
5419
+ attempt: (route) => spawnAndWait(YT_DLP_BIN, args.argv, args.timeoutMs, { extraEnv: proxyEnvFor(route) }),
5420
+ onLog: args.onLog,
5421
+ country: args.country
5422
+ });
5423
+ }
5424
+ function proxyEnvFor(route) {
5425
+ if (route.kind !== "proxy") return void 0;
5426
+ const uri = new URL(route.server);
5427
+ uri.username = encodeURIComponent(route.username);
5428
+ uri.password = encodeURIComponent(route.password);
5429
+ return {
5430
+ HTTP_PROXY: uri.toString(),
5431
+ HTTPS_PROXY: uri.toString(),
5432
+ http_proxy: uri.toString(),
5433
+ https_proxy: uri.toString()
5434
+ };
5435
+ }
5436
+ function spawnAndWait(bin, argv, timeoutMs, options = {}) {
5437
+ return new Promise((resolve, reject) => {
5438
+ const child = spawn(bin, argv, {
5439
+ stdio: ["ignore", "pipe", "pipe"],
5440
+ // `childEnvWith` because `spawn`'s `env` REPLACES the environment rather
5441
+ // than extending it — passing only the additions would strip PATH.
5442
+ ...options.extraEnv ? { env: childEnvWith(options.extraEnv) } : {}
5443
+ });
5444
+ const stderrChunks = [];
5445
+ const stdoutChunks = [];
5446
+ let stderrLen = 0;
5447
+ const MAX_STDERR = 256 * 1024;
5448
+ child.stdout.on("data", (chunk) => {
5449
+ if (options.captureStdout) stdoutChunks.push(chunk);
5450
+ });
5451
+ child.stderr.on("data", (chunk) => {
5452
+ if (stderrLen < MAX_STDERR) {
5453
+ stderrChunks.push(chunk);
5454
+ stderrLen += chunk.length;
5455
+ }
5456
+ });
5457
+ const timer = setTimeout(() => {
5458
+ child.kill("SIGKILL");
5459
+ reject(new Error(`${bin} timed out after ${timeoutMs}ms`));
5460
+ }, timeoutMs);
5461
+ child.on("error", (err) => {
5462
+ clearTimeout(timer);
5463
+ reject(new Error(`failed to spawn "${bin}" \u2014 ${err.message}`));
5464
+ });
5465
+ child.on("close", (code) => {
5466
+ clearTimeout(timer);
5467
+ resolve({
5468
+ code: code ?? -1,
5469
+ stderr: Buffer.concat(stderrChunks).toString("utf-8"),
5470
+ ...options.captureStdout ? { stdout: Buffer.concat(stdoutChunks).toString("utf-8") } : {}
5471
+ });
5472
+ });
5473
+ });
5474
+ }
5475
+
5234
5476
  // src/engine/schema/refs.ts
5235
5477
  import { z as z4 } from "zod";
5236
5478
 
@@ -5426,25 +5668,6 @@ function mapClientError(ctx, e) {
5426
5668
  return new NodeExecutionError(ctx.nodeId, ctx.nodeType, { kind: "local", cause: e });
5427
5669
  }
5428
5670
 
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
5671
  // src/engine/nodes/ingest.ts
5449
5672
  var execFile = promisify(execFileCb);
5450
5673
  var ExpectEnum = z5.enum(["image", "video", "audio", "text", "json", "font"]);
@@ -5617,8 +5840,8 @@ function resolveLocalPath(input) {
5617
5840
  `ingest: ~ path expansion is not supported (got "${input}"). Use an absolute path or a cwd-relative path.`
5618
5841
  );
5619
5842
  }
5620
- if (path4.isAbsolute(input)) return input;
5621
- return path4.resolve(process.cwd(), input);
5843
+ if (path6.isAbsolute(input)) return input;
5844
+ return path6.resolve(process.cwd(), input);
5622
5845
  }
5623
5846
  var EXT_TO_MIME = {
5624
5847
  png: "image/png",
@@ -5666,7 +5889,7 @@ function sniffSvg(buf) {
5666
5889
  return head.startsWith("<?xml") ? head.includes("<svg") : head.startsWith("<svg");
5667
5890
  }
5668
5891
  function inferMimeFromPath(absPath, sniffBytes) {
5669
- const ext = path4.extname(absPath).slice(1).toLowerCase();
5892
+ const ext = path6.extname(absPath).slice(1).toLowerCase();
5670
5893
  const fromExt = EXT_TO_MIME[ext];
5671
5894
  if (fromExt) return fromExt;
5672
5895
  const fromBytes = sniffImageMime(sniffBytes);
@@ -5805,7 +6028,7 @@ async function execLocalFile(params, ctx) {
5805
6028
  }
5806
6029
  let bytes;
5807
6030
  try {
5808
- bytes = await readFile3(absPath);
6031
+ bytes = await readFile4(absPath);
5809
6032
  } catch (e) {
5810
6033
  if (e.code === "EACCES") {
5811
6034
  throw localExecError(ctx, `permission_denied: ${absPath}`);
@@ -5856,7 +6079,7 @@ function localFileMetadata(args) {
5856
6079
  strategy: "local_file",
5857
6080
  ingested_at: (/* @__PURE__ */ new Date()).toISOString(),
5858
6081
  file_size: args.fileSize,
5859
- original_filename: path4.basename(args.absPath),
6082
+ original_filename: path6.basename(args.absPath),
5860
6083
  ...args.rasterizedFrom ? { rasterized_from: args.rasterizedFrom } : {},
5861
6084
  ...args.durationMs !== void 0 ? { duration_ms: args.durationMs } : {}
5862
6085
  };
@@ -5865,8 +6088,6 @@ function withProbedDuration(ref, durationMs) {
5865
6088
  if (durationMs === void 0 || ref.kind !== "video") return ref;
5866
6089
  return { ...ref, duration_ms: durationMs };
5867
6090
  }
5868
- var YT_DLP_BIN = "yt-dlp";
5869
- var YT_DLP_TIMEOUT_MS = 10 * 60 * 1e3;
5870
6091
  var YT_DLP_MIME = {
5871
6092
  image: "image/png",
5872
6093
  video: "video/mp4",
@@ -5880,9 +6101,9 @@ async function execYtDlp(params, ctx) {
5880
6101
  if (params.expect !== "video" && params.expect !== "audio") {
5881
6102
  throw new Error(`ingest: yt_dlp only handles video/audio, got expect=${params.expect}`);
5882
6103
  }
5883
- const workDir = await mkdtemp(path4.join(tmpdir(), "ingest-yt-"));
6104
+ const workDir = await mkdtemp(path6.join(tmpdir(), "ingest-yt-"));
5884
6105
  try {
5885
- const { filePath, info } = await runYtDlp({
6106
+ const { filePath, info } = await runYtDlpForNode({
5886
6107
  url: params.url,
5887
6108
  audioOnly: params.expect === "audio",
5888
6109
  workDir,
@@ -5895,7 +6116,7 @@ async function execYtDlp(params, ctx) {
5895
6116
  `file_too_large: yt-dlp output for ${params.url} is ${downloadedStats.size} bytes (limit ${MAX_ASSET_BYTES})`
5896
6117
  );
5897
6118
  }
5898
- const bytes = await readFile3(filePath);
6119
+ const bytes = await readFile4(filePath);
5899
6120
  const kind = params.expect;
5900
6121
  const mime = YT_DLP_MIME[kind];
5901
6122
  const metadata = buildYtDlpMetadata(params.url, info);
@@ -5906,69 +6127,24 @@ async function execYtDlp(params, ctx) {
5906
6127
  });
5907
6128
  }
5908
6129
  }
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 = {};
6130
+ async function runYtDlpForNode(args) {
5966
6131
  try {
5967
- const raw = await readFile3(infoPath, "utf-8");
5968
- info = JSON.parse(raw);
5969
- } catch {
6132
+ return await runYtDlp({
6133
+ url: args.url,
6134
+ audioOnly: args.audioOnly,
6135
+ workDir: args.workDir,
6136
+ onLog: (message2) => args.ctx.log(`ingest: ${message2}`)
6137
+ });
6138
+ } catch (error) {
6139
+ if (error instanceof YtDlpError) {
6140
+ throw new NodeExecutionError(args.ctx.nodeId, args.ctx.nodeType, {
6141
+ kind: "local",
6142
+ cause: new Error(error.stderrTail ? `${error.message}
6143
+ ${error.stderrTail}` : error.message)
6144
+ });
6145
+ }
6146
+ throw error;
5970
6147
  }
5971
- return { filePath: path4.join(args.workDir, downloaded), info };
5972
6148
  }
5973
6149
  function buildYtDlpMetadata(sourceUrl, info) {
5974
6150
  const out = {
@@ -5995,41 +6171,6 @@ async function uploadAndIngest(args) {
5995
6171
  });
5996
6172
  return { ...localRef, url: publicUrl };
5997
6173
  }
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
6174
  async function ytDlpVersion() {
6034
6175
  try {
6035
6176
  const { stdout } = await execFile(YT_DLP_BIN, ["--version"], { encoding: "utf-8", maxBuffer: 64 * 1024 });
@@ -6060,9 +6201,9 @@ import { z as z6 } from "zod";
6060
6201
 
6061
6202
  // src/engine/nodes/local/lib/cli-runner.ts
6062
6203
  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";
6204
+ import { copyFile as copyFile2, mkdtemp as mkdtemp2, readFile as readFile5, rm as rm2, stat as stat3 } from "fs/promises";
6064
6205
  import { tmpdir as tmpdir2 } from "os";
6065
- import path5 from "path";
6206
+ import path7 from "path";
6066
6207
  import { promisify as promisify2 } from "util";
6067
6208
  var execFile2 = promisify2(execFileCb2);
6068
6209
  var DEFAULT_TIMEOUT_MS2 = 10 * 60 * 1e3;
@@ -6089,7 +6230,7 @@ function mimeForExt(ext) {
6089
6230
  }
6090
6231
  function extForAssetRef(ref) {
6091
6232
  if (ref.path) {
6092
- const e = path5.extname(ref.path);
6233
+ const e = path7.extname(ref.path);
6093
6234
  if (e) return e;
6094
6235
  }
6095
6236
  const reverse = {
@@ -6111,14 +6252,14 @@ function planArrayInputSlot(tmpDir, slot, values, lookup, stagedInputs) {
6111
6252
  const ref = values[i];
6112
6253
  if (!ref) continue;
6113
6254
  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)}`);
6255
+ const dest = path7.join(tmpDir, `in_${slot}_${i}${extForAssetRef(ref)}`);
6115
6256
  stagedInputs.push({ srcPath: ref.path, destPath: dest });
6116
6257
  lookup.set(`in.${slot}.${i}`, dest);
6117
6258
  }
6118
6259
  }
6119
6260
  function planSingleInputSlot(tmpDir, slot, ref, lookup, stagedInputs) {
6120
6261
  if (!ref.path) throw new Error(`cli-runner: inputs.${slot} has no local path`);
6121
- const dest = path5.join(tmpDir, `in_${slot}${extForAssetRef(ref)}`);
6262
+ const dest = path7.join(tmpDir, `in_${slot}${extForAssetRef(ref)}`);
6122
6263
  stagedInputs.push({ srcPath: ref.path, destPath: dest });
6123
6264
  lookup.set(`in.${slot}`, dest);
6124
6265
  }
@@ -6126,7 +6267,7 @@ function planOutputs(tmpDir, outputs, lookup) {
6126
6267
  const outputPaths = [];
6127
6268
  for (const [name, spec] of Object.entries(outputs)) {
6128
6269
  const ext = spec.ext.startsWith(".") ? spec.ext : `.${spec.ext}`;
6129
- const absPath = path5.join(tmpDir, `out_${name}${ext}`);
6270
+ const absPath = path7.join(tmpDir, `out_${name}${ext}`);
6130
6271
  outputPaths.push({ name, absPath, spec });
6131
6272
  lookup.set(`out.${name}`, absPath);
6132
6273
  }
@@ -6167,8 +6308,8 @@ function rejectRawPaths(substituted, original, stagingDir) {
6167
6308
  throw new Error(`cli-runner: home-relative path "${original}" not allowed in args.`);
6168
6309
  }
6169
6310
  if (substituted.startsWith("/")) {
6170
- const resolved = path5.resolve(substituted);
6171
- if (!resolved.startsWith(`${stagingDir}${path5.sep}`) && resolved !== stagingDir) {
6311
+ const resolved = path7.resolve(substituted);
6312
+ if (!resolved.startsWith(`${stagingDir}${path7.sep}`) && resolved !== stagingDir) {
6172
6313
  throw new Error(
6173
6314
  `cli-runner: raw filesystem path "${original}" not allowed \u2014 declare an input slot and use {{in.<slot>}} instead.`
6174
6315
  );
@@ -6218,7 +6359,7 @@ function tailLines(text, maxLines) {
6218
6359
  }
6219
6360
  async function runCli(opts) {
6220
6361
  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, "")}-`));
6362
+ const tmpDir = await mkdtemp2(path7.join(tmpdir2(), `cli-${bin.replace(/[^a-z0-9]/gi, "")}-`));
6222
6363
  try {
6223
6364
  const { lookup, stagedInputs, outputPaths } = planPlaceholders(tmpDir, inputs, outputs);
6224
6365
  await stageInputs(stagedInputs);
@@ -6240,7 +6381,7 @@ ${tailLines(stderr, 40)}`);
6240
6381
  if (!s?.isFile() || s.size === 0) {
6241
6382
  throw new Error(`cli-runner: declared output "${name}" missing or empty at ${absPath}`);
6242
6383
  }
6243
- const bytes = await readFile4(absPath);
6384
+ const bytes = await readFile5(absPath);
6244
6385
  const ref = await ctx.assets.ingestBytes({
6245
6386
  bytes: Buffer.from(bytes),
6246
6387
  kind: spec.kind,
@@ -6495,20 +6636,20 @@ var ffmpegNode = defineNode({
6495
6636
  });
6496
6637
 
6497
6638
  // src/engine/nodes/local/fontSpecimen.ts
6498
- import { mkdtemp as mkdtemp3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
6639
+ import { mkdtemp as mkdtemp3, rm as rm3, writeFile as writeFile4 } from "fs/promises";
6499
6640
  import { createRequire } from "module";
6500
6641
  import { tmpdir as tmpdir3 } from "os";
6501
- import path7 from "path";
6642
+ import path9 from "path";
6502
6643
  import { z as z9 } from "zod";
6503
6644
 
6504
6645
  // src/engine/nodes/local/lib/assets.ts
6505
- import { copyFile as copyFile3, readFile as readFile5 } from "fs/promises";
6506
- import path6 from "path";
6646
+ import { copyFile as copyFile3, readFile as readFile6 } from "fs/promises";
6647
+ import path8 from "path";
6507
6648
  async function stageAsset(ref, destDir, filename) {
6508
6649
  if (!ref.path) {
6509
6650
  throw new Error(`stageAsset: ref (${ref.kind}/${ref.mime}) has no local path`);
6510
6651
  }
6511
- const dest = path6.join(destDir, filename);
6652
+ const dest = path8.join(destDir, filename);
6512
6653
  await copyFile3(ref.path, dest);
6513
6654
  return dest;
6514
6655
  }
@@ -6517,7 +6658,7 @@ async function refToUrl(ref) {
6517
6658
  if (!ref.path) {
6518
6659
  throw new Error("refToUrl: AssetRef has neither url nor path");
6519
6660
  }
6520
- const bytes = await readFile5(ref.path);
6661
+ const bytes = await readFile6(ref.path);
6521
6662
  return `data:${ref.mime};base64,${bytes.toString("base64")}`;
6522
6663
  }
6523
6664
  var ASSET_KINDS = /* @__PURE__ */ new Set(["image", "video", "audio", "json", "text", "font"]);
@@ -6591,12 +6732,12 @@ var fontSpecimenNode = defineNode({
6591
6732
  outputKinds: { image: "image" },
6592
6733
  cost: () => ({ credits: 0, seconds_estimate: 5 }),
6593
6734
  async execute({ inputs, params, ctx }) {
6594
- const tmp = await mkdtemp3(path7.join(tmpdir3(), "font-specimen-"));
6735
+ const tmp = await mkdtemp3(path9.join(tmpdir3(), "font-specimen-"));
6595
6736
  try {
6596
6737
  const fontFilename = `font.${extForMime(inputs.font.mime)}`;
6597
6738
  await stageAsset(inputs.font, tmp, fontFilename);
6598
- const entryPath = path7.join(tmp, "index.html");
6599
- await writeFile3(entryPath, buildSpecimenHtml(params, fontFilename), "utf-8");
6739
+ const entryPath = path9.join(tmp, "index.html");
6740
+ await writeFile4(entryPath, buildSpecimenHtml(params, fontFilename), "utf-8");
6600
6741
  ctx.log(`rendering specimen (${params.font_size}px, ${params.text.split("\n").length} lines)`);
6601
6742
  const pwSpecifier = ["play", "wright"].join("");
6602
6743
  const playwright = require_(pwSpecifier);
@@ -6679,16 +6820,16 @@ var fontSpecimenNode = defineNode({
6679
6820
 
6680
6821
  // src/engine/nodes/local/hyperframe.ts
6681
6822
  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";
6823
+ import { copyFile as copyFile4, mkdtemp as mkdtemp4, readFile as readFile10, rm as rm4, stat as stat5, writeFile as writeFile6 } from "fs/promises";
6683
6824
  import { createRequire as createRequire2 } from "module";
6684
6825
  import { cpus, tmpdir as tmpdir4 } from "os";
6685
- import path12 from "path";
6826
+ import path14 from "path";
6686
6827
  import { promisify as promisify4 } from "util";
6687
6828
  import { z as z11 } from "zod";
6688
6829
 
6689
6830
  // 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";
6831
+ import { readdir as readdir2, readFile as readFile7, stat as stat4 } from "fs/promises";
6832
+ import path10 from "path";
6692
6833
  var SKIP_DIRS = /* @__PURE__ */ new Set([".cache", ".git", "node_modules", "dist", "build", ".next", ".turbo"]);
6693
6834
  function isSkippedName(name) {
6694
6835
  if (name.startsWith(".")) return true;
@@ -6705,23 +6846,23 @@ async function collectFiles(root, current) {
6705
6846
  const names = await readdir2(current);
6706
6847
  for (const name of names) {
6707
6848
  if (isSkippedName(name)) continue;
6708
- const abs = path8.join(current, name);
6849
+ const abs = path10.join(current, name);
6709
6850
  const s = await stat4(abs);
6710
6851
  if (s.isDirectory()) {
6711
6852
  out.push(...await collectFiles(root, abs));
6712
6853
  continue;
6713
6854
  }
6714
6855
  if (!s.isFile()) continue;
6715
- const bytes = await readFile6(abs);
6716
- const relPath = path8.relative(root, abs).split(path8.sep).join("/");
6856
+ const bytes = await readFile7(abs);
6857
+ const relPath = path10.relative(root, abs).split(path10.sep).join("/");
6717
6858
  out.push({ relPath, contentSha: sha256Hex(bytes) });
6718
6859
  }
6719
6860
  return out;
6720
6861
  }
6721
6862
 
6722
6863
  // src/engine/engine/composition-meta.ts
6723
- import { readFile as readFile7 } from "fs/promises";
6724
- import path9 from "path";
6864
+ import { readFile as readFile8 } from "fs/promises";
6865
+ import path11 from "path";
6725
6866
  import { z as z10 } from "zod";
6726
6867
  var InputKind = z10.enum(["video", "image", "audio", "json"]);
6727
6868
  var InputSpec = z10.object({
@@ -6798,10 +6939,10 @@ var CompositionMetaSchema = z10.object({
6798
6939
  params: z10.record(z10.string(), ParamSpec).default({})
6799
6940
  }).strict();
6800
6941
  async function loadCompositionMeta(compositionDir) {
6801
- const metaPath = path9.join(compositionDir, "meta.json");
6942
+ const metaPath = path11.join(compositionDir, "meta.json");
6802
6943
  let raw;
6803
6944
  try {
6804
- raw = await readFile7(metaPath, "utf-8");
6945
+ raw = await readFile8(metaPath, "utf-8");
6805
6946
  } catch (e) {
6806
6947
  throw new Error(`composition meta: cannot read ${metaPath} (${e.message})`);
6807
6948
  }
@@ -6879,8 +7020,8 @@ function defaultFilenameForInput(key, kind) {
6879
7020
 
6880
7021
  // src/engine/nodes/local/lib/hyperframe-check.ts
6881
7022
  import { execFile as execFile3 } from "child_process";
6882
- import { readFile as readFile8 } from "fs/promises";
6883
- import path10 from "path";
7023
+ import { readFile as readFile9 } from "fs/promises";
7024
+ import path12 from "path";
6884
7025
  import { promisify as promisify3 } from "util";
6885
7026
  var execFileAsync = promisify3(execFile3);
6886
7027
  var NEVER_BLOCK = [
@@ -7032,7 +7173,7 @@ ${detail}`);
7032
7173
  }
7033
7174
  let indexHtml = "";
7034
7175
  try {
7035
- indexHtml = await readFile8(path10.join(dir, "index.html"), "utf-8");
7176
+ indexHtml = await readFile9(path12.join(dir, "index.html"), "utf-8");
7036
7177
  } catch {
7037
7178
  indexHtml = "";
7038
7179
  }
@@ -7096,11 +7237,11 @@ ${stderr.slice(0, 1500)}`;
7096
7237
  }
7097
7238
 
7098
7239
  // src/engine/nodes/local/lib/hyperframe-meta.ts
7099
- import { writeFile as writeFile4 } from "fs/promises";
7100
- import path11 from "path";
7240
+ import { writeFile as writeFile5 } from "fs/promises";
7241
+ import path13 from "path";
7101
7242
  async function ensureHyperframesMetaJson(tmp, nodeId, meta, duration) {
7102
- const metaPath = path11.join(tmp, "meta.json");
7103
- await writeFile4(
7243
+ const metaPath = path13.join(tmp, "meta.json");
7244
+ await writeFile5(
7104
7245
  metaPath,
7105
7246
  JSON.stringify(
7106
7247
  {
@@ -7199,7 +7340,7 @@ var hyperframeRenderNode = defineNode({
7199
7340
  const compositionDir = await resolveCompositionDir(params.composition);
7200
7341
  const meta = await loadCompositionMeta(compositionDir);
7201
7342
  const compositionParams = validateAndParseDynamicParams(meta, params);
7202
- const tmp = await mkdtemp4(path12.join(tmpdir4(), "hf-render-"));
7343
+ const tmp = await mkdtemp4(path14.join(tmpdir4(), "hf-render-"));
7203
7344
  try {
7204
7345
  await copyComposition(compositionDir, tmp);
7205
7346
  await vendorGsap(tmp, ctx);
@@ -7209,9 +7350,9 @@ var hyperframeRenderNode = defineNode({
7209
7350
  await substituteCompositionFiles(tmp, substitutionValues);
7210
7351
  await ensureHyperframesMetaJson(tmp, ctx.nodeId, meta, duration);
7211
7352
  await runHyperframesCheck({ dir: tmp, nodeId: "hyperframe_render", ctx, timeoutMs: params.timeout_ms });
7212
- const outputPath = path12.join(tmp, `output.${params.format}`);
7353
+ const outputPath = path14.join(tmp, `output.${params.format}`);
7213
7354
  await runRender({ tmp, outputPath, params, meta, ctx });
7214
- const bytes = await readFile9(outputPath);
7355
+ const bytes = await readFile10(outputPath);
7215
7356
  ctx.log(`rendered ${bytes.length} bytes`);
7216
7357
  const ref = await ctx.assets.ingestBytes({
7217
7358
  bytes: Buffer.from(bytes),
@@ -7233,10 +7374,10 @@ var hyperframeRenderNode = defineNode({
7233
7374
  }
7234
7375
  });
7235
7376
  async function resolveCompositionDir(composition) {
7236
- const compositionPath = path12.isAbsolute(composition) ? composition : path12.resolve(process.cwd(), composition);
7377
+ const compositionPath = path14.isAbsolute(composition) ? composition : path14.resolve(process.cwd(), composition);
7237
7378
  const s = await stat5(compositionPath);
7238
7379
  if (s.isDirectory()) return compositionPath;
7239
- return path12.dirname(compositionPath);
7380
+ return path14.dirname(compositionPath);
7240
7381
  }
7241
7382
  async function validateComposition(rawParams) {
7242
7383
  const issues = await validateCompositionParams(rawParams);
@@ -7318,7 +7459,7 @@ async function copyComposition(srcDir, destDir) {
7318
7459
  await cp(srcDir, destDir, {
7319
7460
  recursive: true,
7320
7461
  filter: (src) => {
7321
- const name = path12.basename(src);
7462
+ const name = path14.basename(src);
7322
7463
  if (name === ".cache" || name === "node_modules" || name === ".git") return false;
7323
7464
  return true;
7324
7465
  }
@@ -7327,7 +7468,7 @@ async function copyComposition(srcDir, destDir) {
7327
7468
  async function vendorGsap(tmp, ctx) {
7328
7469
  try {
7329
7470
  const gsapMin = require_2.resolve("gsap/dist/gsap.min.js");
7330
- await copyFile4(gsapMin, path12.join(tmp, "gsap.min.js"));
7471
+ await copyFile4(gsapMin, path14.join(tmp, "gsap.min.js"));
7331
7472
  } catch (e) {
7332
7473
  ctx.log(`warning: could not vendor gsap.min.js (${e.message}); compositions must self-supply`);
7333
7474
  }
@@ -7342,7 +7483,7 @@ async function stageInputs2(tmp, inputs, meta, ctx) {
7342
7483
  await stageAsset(ref, tmp, filename);
7343
7484
  ctx.log(`staged ${spec.kind} \u2192 ${filename}`);
7344
7485
  if (spec.kind === "video" && primaryDuration === null) {
7345
- primaryDuration = await probeDurationSeconds(path12.join(tmp, filename));
7486
+ primaryDuration = await probeDurationSeconds(path14.join(tmp, filename));
7346
7487
  }
7347
7488
  }
7348
7489
  return primaryDuration;
@@ -7388,15 +7529,15 @@ function coerceImageParam(value) {
7388
7529
  throw new Error("hyperframe_render: image param must be a URL string or AssetRef");
7389
7530
  }
7390
7531
  async function substituteCompositionFiles(tmp, values) {
7391
- const entryPath = path12.join(tmp, "index.html");
7392
- const original = await readFile9(entryPath, "utf-8");
7532
+ const entryPath = path14.join(tmp, "index.html");
7533
+ const original = await readFile10(entryPath, "utf-8");
7393
7534
  const { output, missing } = substituteVariables(original, values);
7394
7535
  if (missing.length > 0) {
7395
7536
  throw new Error(
7396
7537
  `hyperframe_render: composition references undefined variables: ${missing.map((m) => `{{${m}}}`).join(", ")}. Add to params or to meta.json's params with a default.`
7397
7538
  );
7398
7539
  }
7399
- await writeFile5(entryPath, output, "utf-8");
7540
+ await writeFile6(entryPath, output, "utf-8");
7400
7541
  }
7401
7542
  var MAX_WORKERS = 4;
7402
7543
  function workerCount() {
@@ -7405,7 +7546,7 @@ function workerCount() {
7405
7546
  async function runRender(opts) {
7406
7547
  const { tmp, outputPath, params, meta, ctx } = opts;
7407
7548
  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)}`);
7549
+ ctx.log(`rendering ${meta.width}x${meta.height}@${meta.fps}fps ${params.format} from ${path14.basename(tmp)}`);
7409
7550
  try {
7410
7551
  await execFileAsync2("npx", args, { timeout: params.timeout_ms, maxBuffer: 64 * 1024 * 1024 });
7411
7552
  } catch (e) {
@@ -7449,10 +7590,10 @@ async function probeDurationSeconds(filePath) {
7449
7590
 
7450
7591
  // src/engine/nodes/local/hyperframe-snapshot.ts
7451
7592
  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";
7593
+ import { copyFile as copyFile5, mkdtemp as mkdtemp5, readFile as readFile11, rm as rm5, writeFile as writeFile7 } from "fs/promises";
7453
7594
  import { createRequire as createRequire3 } from "module";
7454
7595
  import { tmpdir as tmpdir5 } from "os";
7455
- import path13 from "path";
7596
+ import path15 from "path";
7456
7597
  import { promisify as promisify5 } from "util";
7457
7598
  import { z as z12 } from "zod";
7458
7599
  var _execFileAsync = promisify5(execFile5);
@@ -7499,7 +7640,7 @@ var hyperframeSnapshotNode = defineNode({
7499
7640
  const compositionDir = await resolveCompositionDir(params.composition);
7500
7641
  const meta = await loadCompositionMeta(compositionDir);
7501
7642
  const compositionParams = validateAndParseDynamicParams2(meta, params);
7502
- const tmp = await mkdtemp5(path13.join(tmpdir5(), "hf-snap-"));
7643
+ const tmp = await mkdtemp5(path15.join(tmpdir5(), "hf-snap-"));
7503
7644
  try {
7504
7645
  await copyComposition2(compositionDir, tmp);
7505
7646
  await vendorGsap2(tmp, ctx);
@@ -7514,7 +7655,7 @@ var hyperframeSnapshotNode = defineNode({
7514
7655
  timeoutMs: params.timeout_ms,
7515
7656
  samples: 1
7516
7657
  });
7517
- const entryPath = path13.join(tmp, "index.html");
7658
+ const entryPath = path15.join(tmp, "index.html");
7518
7659
  const entryUrl = `file://${entryPath}`;
7519
7660
  ctx.log(`snapshotting ${meta.width}x${meta.height}@${DEVICE_SCALE_FACTOR2}x wait=${params.wait_for.kind}`);
7520
7661
  const pwSpecifier = ["play", "wright"].join("");
@@ -7575,7 +7716,7 @@ async function copyComposition2(srcDir, destDir) {
7575
7716
  await cp(srcDir, destDir, {
7576
7717
  recursive: true,
7577
7718
  filter: (src) => {
7578
- const name = path13.basename(src);
7719
+ const name = path15.basename(src);
7579
7720
  if (name === ".cache" || name === "node_modules" || name === ".git") return false;
7580
7721
  return true;
7581
7722
  }
@@ -7584,7 +7725,7 @@ async function copyComposition2(srcDir, destDir) {
7584
7725
  async function vendorGsap2(tmp, ctx) {
7585
7726
  try {
7586
7727
  const gsapMin = require_3.resolve("gsap/dist/gsap.min.js");
7587
- await copyFile5(gsapMin, path13.join(tmp, "gsap.min.js"));
7728
+ await copyFile5(gsapMin, path15.join(tmp, "gsap.min.js"));
7588
7729
  } catch (e) {
7589
7730
  ctx.log(`warning: could not vendor gsap.min.js (${e.message}); compositions must self-supply`);
7590
7731
  }
@@ -7618,15 +7759,15 @@ function coerceImageParam2(value) {
7618
7759
  throw new Error("hyperframe_snapshot: image param must be a URL string or AssetRef");
7619
7760
  }
7620
7761
  async function substituteCompositionFiles2(tmp, values) {
7621
- const entryPath = path13.join(tmp, "index.html");
7622
- const original = await readFile10(entryPath, "utf-8");
7762
+ const entryPath = path15.join(tmp, "index.html");
7763
+ const original = await readFile11(entryPath, "utf-8");
7623
7764
  const { output, missing } = substituteVariables(original, values);
7624
7765
  if (missing.length > 0) {
7625
7766
  throw new Error(
7626
7767
  `hyperframe_snapshot: composition references undefined variables: ${missing.map((m) => `{{${m}}}`).join(", ")}.`
7627
7768
  );
7628
7769
  }
7629
- await writeFile6(entryPath, output, "utf-8");
7770
+ await writeFile7(entryPath, output, "utf-8");
7630
7771
  }
7631
7772
  async function waitForReady(page, waitFor, timeoutMs) {
7632
7773
  switch (waitFor.kind) {
@@ -8368,9 +8509,9 @@ var videoLipsyncNode = delegated({
8368
8509
  });
8369
8510
 
8370
8511
  // src/engine/nodes/remote/videoTranscribe.ts
8371
- import { mkdtemp as mkdtemp6, readFile as readFile11, rm as rm6 } from "fs/promises";
8512
+ import { mkdtemp as mkdtemp6, readFile as readFile12, rm as rm6 } from "fs/promises";
8372
8513
  import { tmpdir as tmpdir6 } from "os";
8373
- import path14 from "path";
8514
+ import path16 from "path";
8374
8515
  import { z as z33 } from "zod";
8375
8516
 
8376
8517
  // src/engine/nodes/local/lib/ffmpeg.ts
@@ -8505,14 +8646,14 @@ async function tryExtractAudio(inputs, ctx) {
8505
8646
  ctx.log("video_transcribe: no audio track detected, sending full video");
8506
8647
  return null;
8507
8648
  }
8508
- tmpDir = await mkdtemp6(path14.join(tmpdir6(), "vtx-"));
8509
- const audioPath = path14.join(tmpDir, "audio.mp3");
8649
+ tmpDir = await mkdtemp6(path16.join(tmpdir6(), "vtx-"));
8650
+ const audioPath = path16.join(tmpDir, "audio.mp3");
8510
8651
  ctx.log("video_transcribe: extracting audio (mono 16kHz mp3)");
8511
8652
  await runFfmpeg(
8512
8653
  ["-i", video.path, "-vn", "-ac", "1", "-ar", "16000", "-b:a", "64k", "-f", "mp3", "-y", audioPath],
8513
8654
  { timeout_ms: AUDIO_EXTRACT_TIMEOUT_MS }
8514
8655
  );
8515
- const bytes = await readFile11(audioPath);
8656
+ const bytes = await readFile12(audioPath);
8516
8657
  if (bytes.byteLength === 0) {
8517
8658
  ctx.log("video_transcribe: extracted audio is empty, sending full video");
8518
8659
  return null;
@@ -8619,19 +8760,19 @@ function safeCost(def) {
8619
8760
 
8620
8761
  // src/engine/storage/cache-store.ts
8621
8762
  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";
8763
+ import { mkdir as mkdir3, readFile as readFile13, rename as rename2, writeFile as writeFile8 } from "fs/promises";
8764
+ import path17 from "path";
8624
8765
  var LocalCacheStore = class {
8625
8766
  rootDir;
8626
8767
  constructor(rootDir) {
8627
8768
  this.rootDir = rootDir;
8628
8769
  }
8629
8770
  filePath(cacheKey) {
8630
- return path15.join(this.rootDir, `${cacheKey}.json`);
8771
+ return path17.join(this.rootDir, `${cacheKey}.json`);
8631
8772
  }
8632
8773
  async get(cacheKey) {
8633
8774
  try {
8634
- const buf = await readFile12(this.filePath(cacheKey), "utf8");
8775
+ const buf = await readFile13(this.filePath(cacheKey), "utf8");
8635
8776
  return JSON.parse(buf);
8636
8777
  } catch (e) {
8637
8778
  if (e.code === "ENOENT") return null;
@@ -8640,9 +8781,9 @@ var LocalCacheStore = class {
8640
8781
  }
8641
8782
  async put(entry) {
8642
8783
  const dest = this.filePath(entry.cacheKey);
8643
- await mkdir3(path15.dirname(dest), { recursive: true });
8784
+ await mkdir3(path17.dirname(dest), { recursive: true });
8644
8785
  const tmp = `${dest}.tmp-${process.pid}-${randomUUID2()}`;
8645
- await writeFile7(tmp, JSON.stringify(entry, null, 0));
8786
+ await writeFile8(tmp, JSON.stringify(entry, null, 0));
8646
8787
  await rename2(tmp, dest);
8647
8788
  }
8648
8789
  };
@@ -8696,12 +8837,12 @@ function defaultRegistry() {
8696
8837
  }
8697
8838
  function createEngineFromEnv(opts = {}) {
8698
8839
  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");
8840
+ const cacheDir = opts.cacheDir ?? path18.join(cwd, "canvas", ".cache");
8841
+ const outputsDir = opts.outputsDir ?? path18.join(cwd, "canvas");
8701
8842
  const creds = requireCredentialsFromEnv();
8702
8843
  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"));
8844
+ const assets = new LocalAssetStore(path18.join(cacheDir, "assets"));
8845
+ const localCache = new LocalCacheStore(path18.join(cacheDir, "index"));
8705
8846
  const remoteCacheEnabled = opts.remoteCache ?? remoteCacheEnabledFromEnv();
8706
8847
  const cache = remoteCacheEnabled ? new LayeredCacheStore({
8707
8848
  local: localCache,
@@ -8764,6 +8905,14 @@ export {
8764
8905
  elementMentionKeywords,
8765
8906
  MAX_REMOTE_IMAGE_BYTES,
8766
8907
  fetchExternalBytes,
8908
+ isVimeoAuthFailure,
8909
+ ytDlpBlockSignal,
8910
+ YtDlpError,
8911
+ runYtDlp,
8912
+ probeYtDlp,
8913
+ isAudioOnly,
8914
+ durationSeconds,
8915
+ canonicalUrl,
8767
8916
  toModelSafeImage,
8768
8917
  BackendClient2,
8769
8918
  Engine2 as Engine,
@@ -8775,4 +8924,4 @@ export {
8775
8924
  defaultRegistry,
8776
8925
  createEngineFromEnv
8777
8926
  };
8778
- //# sourceMappingURL=chunk-5MPIOGRO.js.map
8927
+ //# sourceMappingURL=chunk-E2WYWHJJ.js.map