@koda-sl/baker-cli 0.200.0 → 0.201.0-dev.0a9adf6f7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -15
- package/dist/{chunk-5MPIOGRO.js → chunk-CAWFJ5X3.js} +265 -215
- package/dist/chunk-CAWFJ5X3.js.map +1 -0
- package/dist/cli.js +542 -368
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-5MPIOGRO.js.map +0 -1
|
@@ -15,9 +15,9 @@ import {
|
|
|
15
15
|
shouldEscalate
|
|
16
16
|
} from "./chunk-6NZG2TCM.js";
|
|
17
17
|
|
|
18
|
-
//
|
|
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
|
-
"
|
|
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
|
|
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(
|
|
665
|
-
return await this.requestJson("POST",
|
|
664
|
+
async postJson(path18, body, signal) {
|
|
665
|
+
return await this.requestJson("POST", path18, body, signal);
|
|
666
666
|
}
|
|
667
|
-
async putJson(
|
|
668
|
-
return await this.requestJson("PUT",
|
|
667
|
+
async putJson(path18, body, signal) {
|
|
668
|
+
return await this.requestJson("PUT", path18, body, signal);
|
|
669
669
|
}
|
|
670
|
-
async getJson(
|
|
671
|
-
return await this.requestJson("GET",
|
|
670
|
+
async getJson(path18, signal) {
|
|
671
|
+
return await this.requestJson("GET", path18, void 0, signal);
|
|
672
672
|
}
|
|
673
|
-
async requestJson(method,
|
|
674
|
-
const url = `${this.baseUrl}${
|
|
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
|
|
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(
|
|
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
|
|
922
|
-
return this.http.getJson(
|
|
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
|
-
//
|
|
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
|
|
4292
|
+
const path18 = pathPrefix ? tail2 ? `${pathPrefix}.${tail2}` : pathPrefix : tail2;
|
|
4293
4293
|
issues.push({
|
|
4294
|
-
path:
|
|
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(
|
|
4304
|
-
return
|
|
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
|
|
4914
|
-
import { mkdtemp,
|
|
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
|
|
4916
|
+
import path5 from "path";
|
|
4917
4917
|
import { promisify } from "util";
|
|
4918
4918
|
import { z as z5 } from "zod";
|
|
4919
4919
|
|
|
@@ -5231,6 +5231,154 @@ 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
|
+
var PROXY_TUNNEL_FAILURE = /tunnel connection failed: 407|proxy authentication required|http error 407/i;
|
|
5250
|
+
function ytDlpBlockSignal(stderr) {
|
|
5251
|
+
const text = stderr.toLowerCase();
|
|
5252
|
+
if (PROXY_TUNNEL_FAILURE.test(stderr)) return { status: 407 };
|
|
5253
|
+
if (NOT_A_BLOCK_PHRASES.some((phrase) => text.includes(phrase))) return {};
|
|
5254
|
+
const status = HTTP_ERROR.exec(stderr);
|
|
5255
|
+
if (status) return { status: Number(status[1]) };
|
|
5256
|
+
if (CHALLENGE_PHRASES.some((phrase) => text.includes(phrase))) return { challenge: true };
|
|
5257
|
+
return {};
|
|
5258
|
+
}
|
|
5259
|
+
|
|
5260
|
+
// src/lib/ytDlp.ts
|
|
5261
|
+
var YT_DLP_BIN = "yt-dlp";
|
|
5262
|
+
var YT_DLP_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
5263
|
+
var YtDlpError = class extends Error {
|
|
5264
|
+
constructor(message2, stderrTail) {
|
|
5265
|
+
super(message2);
|
|
5266
|
+
this.stderrTail = stderrTail;
|
|
5267
|
+
this.name = "YtDlpError";
|
|
5268
|
+
}
|
|
5269
|
+
stderrTail;
|
|
5270
|
+
};
|
|
5271
|
+
function tail(text, maxLines) {
|
|
5272
|
+
return text.split("\n").slice(-maxLines).join("\n");
|
|
5273
|
+
}
|
|
5274
|
+
async function runYtDlp(args) {
|
|
5275
|
+
const outTemplate = path4.join(args.workDir, "out.%(ext)s");
|
|
5276
|
+
const infoPath = path4.join(args.workDir, "out.info.json");
|
|
5277
|
+
const argv = [
|
|
5278
|
+
args.url,
|
|
5279
|
+
"--no-playlist",
|
|
5280
|
+
"--no-progress",
|
|
5281
|
+
"--no-warnings",
|
|
5282
|
+
"--quiet",
|
|
5283
|
+
"--write-info-json",
|
|
5284
|
+
"-o",
|
|
5285
|
+
outTemplate
|
|
5286
|
+
];
|
|
5287
|
+
if (args.audioOnly) {
|
|
5288
|
+
argv.push("-x", "--audio-format", "m4a");
|
|
5289
|
+
} else {
|
|
5290
|
+
const format = args.maxHeight ? `bv*[height<=${args.maxHeight}]+ba/b[height<=${args.maxHeight}]/bv*+ba/b` : "bv*+ba/b";
|
|
5291
|
+
argv.push("--format", format, "--merge-output-format", "mp4");
|
|
5292
|
+
}
|
|
5293
|
+
args.onLog?.(`yt-dlp: downloading ${args.audioOnly ? "audio" : "video"}`);
|
|
5294
|
+
const { code, stderr, tier } = await runViaLadder({
|
|
5295
|
+
url: args.url,
|
|
5296
|
+
argv,
|
|
5297
|
+
timeoutMs: args.timeoutMs ?? YT_DLP_TIMEOUT_MS,
|
|
5298
|
+
onLog: args.onLog
|
|
5299
|
+
});
|
|
5300
|
+
if (code !== 0) {
|
|
5301
|
+
throw new YtDlpError(`yt-dlp exited ${code}`, tail(stderr, 40));
|
|
5302
|
+
}
|
|
5303
|
+
const files = await readdir(args.workDir);
|
|
5304
|
+
const wanted = args.audioOnly ? ".m4a" : ".mp4";
|
|
5305
|
+
const downloaded = files.find((f) => f.startsWith("out.") && f.endsWith(wanted));
|
|
5306
|
+
if (!downloaded) {
|
|
5307
|
+
throw new YtDlpError(`yt-dlp finished but produced no ${wanted} file (saw: ${files.join(", ")})`, "");
|
|
5308
|
+
}
|
|
5309
|
+
let info = {};
|
|
5310
|
+
try {
|
|
5311
|
+
info = JSON.parse(await readFile3(infoPath, "utf-8"));
|
|
5312
|
+
} catch {
|
|
5313
|
+
}
|
|
5314
|
+
return { filePath: path4.join(args.workDir, downloaded), info, tier };
|
|
5315
|
+
}
|
|
5316
|
+
async function walkYtDlpLadder(args) {
|
|
5317
|
+
const routes = plannedRoutes(args.url, args.credentials);
|
|
5318
|
+
let last = { code: -1, stderr: "", tier: "direct" };
|
|
5319
|
+
for (const route of routes) {
|
|
5320
|
+
const tier = route.kind === "proxy" ? route.tier : "direct";
|
|
5321
|
+
if (route.kind === "proxy") args.onLog?.(`yt-dlp: retrying via ${tier}`);
|
|
5322
|
+
last = { ...await args.attempt(route), tier };
|
|
5323
|
+
if (last.code === 0) return last;
|
|
5324
|
+
if (!shouldEscalate(ytDlpBlockSignal(last.stderr))) return last;
|
|
5325
|
+
}
|
|
5326
|
+
return last;
|
|
5327
|
+
}
|
|
5328
|
+
function runViaLadder(args) {
|
|
5329
|
+
return walkYtDlpLadder({
|
|
5330
|
+
url: args.url,
|
|
5331
|
+
credentials: captureProxyCredentials(),
|
|
5332
|
+
attempt: (route) => spawnAndWait(YT_DLP_BIN, args.argv, args.timeoutMs, proxyEnvFor(route)),
|
|
5333
|
+
onLog: args.onLog
|
|
5334
|
+
});
|
|
5335
|
+
}
|
|
5336
|
+
function proxyEnvFor(route) {
|
|
5337
|
+
if (route.kind !== "proxy") return void 0;
|
|
5338
|
+
const uri = new URL(route.server);
|
|
5339
|
+
uri.username = encodeURIComponent(route.username);
|
|
5340
|
+
uri.password = encodeURIComponent(route.password);
|
|
5341
|
+
return {
|
|
5342
|
+
HTTP_PROXY: uri.toString(),
|
|
5343
|
+
HTTPS_PROXY: uri.toString(),
|
|
5344
|
+
http_proxy: uri.toString(),
|
|
5345
|
+
https_proxy: uri.toString()
|
|
5346
|
+
};
|
|
5347
|
+
}
|
|
5348
|
+
function spawnAndWait(bin, argv, timeoutMs, extraEnv) {
|
|
5349
|
+
return new Promise((resolve, reject) => {
|
|
5350
|
+
const child = spawn(bin, argv, {
|
|
5351
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
5352
|
+
// `childEnvWith` because `spawn`'s `env` REPLACES the environment rather
|
|
5353
|
+
// than extending it — passing only the additions would strip PATH.
|
|
5354
|
+
...extraEnv ? { env: childEnvWith(extraEnv) } : {}
|
|
5355
|
+
});
|
|
5356
|
+
const stderrChunks = [];
|
|
5357
|
+
let stderrLen = 0;
|
|
5358
|
+
const MAX_STDERR = 256 * 1024;
|
|
5359
|
+
child.stdout.on("data", () => {
|
|
5360
|
+
});
|
|
5361
|
+
child.stderr.on("data", (chunk) => {
|
|
5362
|
+
if (stderrLen < MAX_STDERR) {
|
|
5363
|
+
stderrChunks.push(chunk);
|
|
5364
|
+
stderrLen += chunk.length;
|
|
5365
|
+
}
|
|
5366
|
+
});
|
|
5367
|
+
const timer = setTimeout(() => {
|
|
5368
|
+
child.kill("SIGKILL");
|
|
5369
|
+
reject(new Error(`${bin} timed out after ${timeoutMs}ms`));
|
|
5370
|
+
}, timeoutMs);
|
|
5371
|
+
child.on("error", (err) => {
|
|
5372
|
+
clearTimeout(timer);
|
|
5373
|
+
reject(new Error(`failed to spawn "${bin}" \u2014 ${err.message}`));
|
|
5374
|
+
});
|
|
5375
|
+
child.on("close", (code) => {
|
|
5376
|
+
clearTimeout(timer);
|
|
5377
|
+
resolve({ code: code ?? -1, stderr: Buffer.concat(stderrChunks).toString("utf-8") });
|
|
5378
|
+
});
|
|
5379
|
+
});
|
|
5380
|
+
}
|
|
5381
|
+
|
|
5234
5382
|
// src/engine/schema/refs.ts
|
|
5235
5383
|
import { z as z4 } from "zod";
|
|
5236
5384
|
|
|
@@ -5426,25 +5574,6 @@ function mapClientError(ctx, e) {
|
|
|
5426
5574
|
return new NodeExecutionError(ctx.nodeId, ctx.nodeType, { kind: "local", cause: e });
|
|
5427
5575
|
}
|
|
5428
5576
|
|
|
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
5577
|
// src/engine/nodes/ingest.ts
|
|
5449
5578
|
var execFile = promisify(execFileCb);
|
|
5450
5579
|
var ExpectEnum = z5.enum(["image", "video", "audio", "text", "json", "font"]);
|
|
@@ -5617,8 +5746,8 @@ function resolveLocalPath(input) {
|
|
|
5617
5746
|
`ingest: ~ path expansion is not supported (got "${input}"). Use an absolute path or a cwd-relative path.`
|
|
5618
5747
|
);
|
|
5619
5748
|
}
|
|
5620
|
-
if (
|
|
5621
|
-
return
|
|
5749
|
+
if (path5.isAbsolute(input)) return input;
|
|
5750
|
+
return path5.resolve(process.cwd(), input);
|
|
5622
5751
|
}
|
|
5623
5752
|
var EXT_TO_MIME = {
|
|
5624
5753
|
png: "image/png",
|
|
@@ -5666,7 +5795,7 @@ function sniffSvg(buf) {
|
|
|
5666
5795
|
return head.startsWith("<?xml") ? head.includes("<svg") : head.startsWith("<svg");
|
|
5667
5796
|
}
|
|
5668
5797
|
function inferMimeFromPath(absPath, sniffBytes) {
|
|
5669
|
-
const ext =
|
|
5798
|
+
const ext = path5.extname(absPath).slice(1).toLowerCase();
|
|
5670
5799
|
const fromExt = EXT_TO_MIME[ext];
|
|
5671
5800
|
if (fromExt) return fromExt;
|
|
5672
5801
|
const fromBytes = sniffImageMime(sniffBytes);
|
|
@@ -5805,7 +5934,7 @@ async function execLocalFile(params, ctx) {
|
|
|
5805
5934
|
}
|
|
5806
5935
|
let bytes;
|
|
5807
5936
|
try {
|
|
5808
|
-
bytes = await
|
|
5937
|
+
bytes = await readFile4(absPath);
|
|
5809
5938
|
} catch (e) {
|
|
5810
5939
|
if (e.code === "EACCES") {
|
|
5811
5940
|
throw localExecError(ctx, `permission_denied: ${absPath}`);
|
|
@@ -5856,7 +5985,7 @@ function localFileMetadata(args) {
|
|
|
5856
5985
|
strategy: "local_file",
|
|
5857
5986
|
ingested_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5858
5987
|
file_size: args.fileSize,
|
|
5859
|
-
original_filename:
|
|
5988
|
+
original_filename: path5.basename(args.absPath),
|
|
5860
5989
|
...args.rasterizedFrom ? { rasterized_from: args.rasterizedFrom } : {},
|
|
5861
5990
|
...args.durationMs !== void 0 ? { duration_ms: args.durationMs } : {}
|
|
5862
5991
|
};
|
|
@@ -5865,8 +5994,6 @@ function withProbedDuration(ref, durationMs) {
|
|
|
5865
5994
|
if (durationMs === void 0 || ref.kind !== "video") return ref;
|
|
5866
5995
|
return { ...ref, duration_ms: durationMs };
|
|
5867
5996
|
}
|
|
5868
|
-
var YT_DLP_BIN = "yt-dlp";
|
|
5869
|
-
var YT_DLP_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
5870
5997
|
var YT_DLP_MIME = {
|
|
5871
5998
|
image: "image/png",
|
|
5872
5999
|
video: "video/mp4",
|
|
@@ -5880,9 +6007,9 @@ async function execYtDlp(params, ctx) {
|
|
|
5880
6007
|
if (params.expect !== "video" && params.expect !== "audio") {
|
|
5881
6008
|
throw new Error(`ingest: yt_dlp only handles video/audio, got expect=${params.expect}`);
|
|
5882
6009
|
}
|
|
5883
|
-
const workDir = await mkdtemp(
|
|
6010
|
+
const workDir = await mkdtemp(path5.join(tmpdir(), "ingest-yt-"));
|
|
5884
6011
|
try {
|
|
5885
|
-
const { filePath, info } = await
|
|
6012
|
+
const { filePath, info } = await runYtDlpForNode({
|
|
5886
6013
|
url: params.url,
|
|
5887
6014
|
audioOnly: params.expect === "audio",
|
|
5888
6015
|
workDir,
|
|
@@ -5895,7 +6022,7 @@ async function execYtDlp(params, ctx) {
|
|
|
5895
6022
|
`file_too_large: yt-dlp output for ${params.url} is ${downloadedStats.size} bytes (limit ${MAX_ASSET_BYTES})`
|
|
5896
6023
|
);
|
|
5897
6024
|
}
|
|
5898
|
-
const bytes = await
|
|
6025
|
+
const bytes = await readFile4(filePath);
|
|
5899
6026
|
const kind = params.expect;
|
|
5900
6027
|
const mime = YT_DLP_MIME[kind];
|
|
5901
6028
|
const metadata = buildYtDlpMetadata(params.url, info);
|
|
@@ -5906,69 +6033,24 @@ async function execYtDlp(params, ctx) {
|
|
|
5906
6033
|
});
|
|
5907
6034
|
}
|
|
5908
6035
|
}
|
|
5909
|
-
async function
|
|
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 = {};
|
|
6036
|
+
async function runYtDlpForNode(args) {
|
|
5966
6037
|
try {
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
6038
|
+
return await runYtDlp({
|
|
6039
|
+
url: args.url,
|
|
6040
|
+
audioOnly: args.audioOnly,
|
|
6041
|
+
workDir: args.workDir,
|
|
6042
|
+
onLog: (message2) => args.ctx.log(`ingest: ${message2}`)
|
|
6043
|
+
});
|
|
6044
|
+
} catch (error) {
|
|
6045
|
+
if (error instanceof YtDlpError) {
|
|
6046
|
+
throw new NodeExecutionError(args.ctx.nodeId, args.ctx.nodeType, {
|
|
6047
|
+
kind: "local",
|
|
6048
|
+
cause: new Error(error.stderrTail ? `${error.message}
|
|
6049
|
+
${error.stderrTail}` : error.message)
|
|
6050
|
+
});
|
|
6051
|
+
}
|
|
6052
|
+
throw error;
|
|
5970
6053
|
}
|
|
5971
|
-
return { filePath: path4.join(args.workDir, downloaded), info };
|
|
5972
6054
|
}
|
|
5973
6055
|
function buildYtDlpMetadata(sourceUrl, info) {
|
|
5974
6056
|
const out = {
|
|
@@ -5995,41 +6077,6 @@ async function uploadAndIngest(args) {
|
|
|
5995
6077
|
});
|
|
5996
6078
|
return { ...localRef, url: publicUrl };
|
|
5997
6079
|
}
|
|
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
6080
|
async function ytDlpVersion() {
|
|
6034
6081
|
try {
|
|
6035
6082
|
const { stdout } = await execFile(YT_DLP_BIN, ["--version"], { encoding: "utf-8", maxBuffer: 64 * 1024 });
|
|
@@ -6060,9 +6107,9 @@ import { z as z6 } from "zod";
|
|
|
6060
6107
|
|
|
6061
6108
|
// src/engine/nodes/local/lib/cli-runner.ts
|
|
6062
6109
|
import { execFile as execFileCb2, spawn as spawn2 } from "child_process";
|
|
6063
|
-
import { copyFile as copyFile2, mkdtemp as mkdtemp2, readFile as
|
|
6110
|
+
import { copyFile as copyFile2, mkdtemp as mkdtemp2, readFile as readFile5, rm as rm2, stat as stat3 } from "fs/promises";
|
|
6064
6111
|
import { tmpdir as tmpdir2 } from "os";
|
|
6065
|
-
import
|
|
6112
|
+
import path6 from "path";
|
|
6066
6113
|
import { promisify as promisify2 } from "util";
|
|
6067
6114
|
var execFile2 = promisify2(execFileCb2);
|
|
6068
6115
|
var DEFAULT_TIMEOUT_MS2 = 10 * 60 * 1e3;
|
|
@@ -6089,7 +6136,7 @@ function mimeForExt(ext) {
|
|
|
6089
6136
|
}
|
|
6090
6137
|
function extForAssetRef(ref) {
|
|
6091
6138
|
if (ref.path) {
|
|
6092
|
-
const e =
|
|
6139
|
+
const e = path6.extname(ref.path);
|
|
6093
6140
|
if (e) return e;
|
|
6094
6141
|
}
|
|
6095
6142
|
const reverse = {
|
|
@@ -6111,14 +6158,14 @@ function planArrayInputSlot(tmpDir, slot, values, lookup, stagedInputs) {
|
|
|
6111
6158
|
const ref = values[i];
|
|
6112
6159
|
if (!ref) continue;
|
|
6113
6160
|
if (!ref.path) throw new Error(`cli-runner: inputs.${slot}[${i}] has no local path`);
|
|
6114
|
-
const dest =
|
|
6161
|
+
const dest = path6.join(tmpDir, `in_${slot}_${i}${extForAssetRef(ref)}`);
|
|
6115
6162
|
stagedInputs.push({ srcPath: ref.path, destPath: dest });
|
|
6116
6163
|
lookup.set(`in.${slot}.${i}`, dest);
|
|
6117
6164
|
}
|
|
6118
6165
|
}
|
|
6119
6166
|
function planSingleInputSlot(tmpDir, slot, ref, lookup, stagedInputs) {
|
|
6120
6167
|
if (!ref.path) throw new Error(`cli-runner: inputs.${slot} has no local path`);
|
|
6121
|
-
const dest =
|
|
6168
|
+
const dest = path6.join(tmpDir, `in_${slot}${extForAssetRef(ref)}`);
|
|
6122
6169
|
stagedInputs.push({ srcPath: ref.path, destPath: dest });
|
|
6123
6170
|
lookup.set(`in.${slot}`, dest);
|
|
6124
6171
|
}
|
|
@@ -6126,7 +6173,7 @@ function planOutputs(tmpDir, outputs, lookup) {
|
|
|
6126
6173
|
const outputPaths = [];
|
|
6127
6174
|
for (const [name, spec] of Object.entries(outputs)) {
|
|
6128
6175
|
const ext = spec.ext.startsWith(".") ? spec.ext : `.${spec.ext}`;
|
|
6129
|
-
const absPath =
|
|
6176
|
+
const absPath = path6.join(tmpDir, `out_${name}${ext}`);
|
|
6130
6177
|
outputPaths.push({ name, absPath, spec });
|
|
6131
6178
|
lookup.set(`out.${name}`, absPath);
|
|
6132
6179
|
}
|
|
@@ -6167,8 +6214,8 @@ function rejectRawPaths(substituted, original, stagingDir) {
|
|
|
6167
6214
|
throw new Error(`cli-runner: home-relative path "${original}" not allowed in args.`);
|
|
6168
6215
|
}
|
|
6169
6216
|
if (substituted.startsWith("/")) {
|
|
6170
|
-
const resolved =
|
|
6171
|
-
if (!resolved.startsWith(`${stagingDir}${
|
|
6217
|
+
const resolved = path6.resolve(substituted);
|
|
6218
|
+
if (!resolved.startsWith(`${stagingDir}${path6.sep}`) && resolved !== stagingDir) {
|
|
6172
6219
|
throw new Error(
|
|
6173
6220
|
`cli-runner: raw filesystem path "${original}" not allowed \u2014 declare an input slot and use {{in.<slot>}} instead.`
|
|
6174
6221
|
);
|
|
@@ -6218,7 +6265,7 @@ function tailLines(text, maxLines) {
|
|
|
6218
6265
|
}
|
|
6219
6266
|
async function runCli(opts) {
|
|
6220
6267
|
const { bin, args, inputs, outputs, ctx, timeoutMs = DEFAULT_TIMEOUT_MS2 } = opts;
|
|
6221
|
-
const tmpDir = await mkdtemp2(
|
|
6268
|
+
const tmpDir = await mkdtemp2(path6.join(tmpdir2(), `cli-${bin.replace(/[^a-z0-9]/gi, "")}-`));
|
|
6222
6269
|
try {
|
|
6223
6270
|
const { lookup, stagedInputs, outputPaths } = planPlaceholders(tmpDir, inputs, outputs);
|
|
6224
6271
|
await stageInputs(stagedInputs);
|
|
@@ -6240,7 +6287,7 @@ ${tailLines(stderr, 40)}`);
|
|
|
6240
6287
|
if (!s?.isFile() || s.size === 0) {
|
|
6241
6288
|
throw new Error(`cli-runner: declared output "${name}" missing or empty at ${absPath}`);
|
|
6242
6289
|
}
|
|
6243
|
-
const bytes = await
|
|
6290
|
+
const bytes = await readFile5(absPath);
|
|
6244
6291
|
const ref = await ctx.assets.ingestBytes({
|
|
6245
6292
|
bytes: Buffer.from(bytes),
|
|
6246
6293
|
kind: spec.kind,
|
|
@@ -6498,17 +6545,17 @@ var ffmpegNode = defineNode({
|
|
|
6498
6545
|
import { mkdtemp as mkdtemp3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
|
|
6499
6546
|
import { createRequire } from "module";
|
|
6500
6547
|
import { tmpdir as tmpdir3 } from "os";
|
|
6501
|
-
import
|
|
6548
|
+
import path8 from "path";
|
|
6502
6549
|
import { z as z9 } from "zod";
|
|
6503
6550
|
|
|
6504
6551
|
// src/engine/nodes/local/lib/assets.ts
|
|
6505
|
-
import { copyFile as copyFile3, readFile as
|
|
6506
|
-
import
|
|
6552
|
+
import { copyFile as copyFile3, readFile as readFile6 } from "fs/promises";
|
|
6553
|
+
import path7 from "path";
|
|
6507
6554
|
async function stageAsset(ref, destDir, filename) {
|
|
6508
6555
|
if (!ref.path) {
|
|
6509
6556
|
throw new Error(`stageAsset: ref (${ref.kind}/${ref.mime}) has no local path`);
|
|
6510
6557
|
}
|
|
6511
|
-
const dest =
|
|
6558
|
+
const dest = path7.join(destDir, filename);
|
|
6512
6559
|
await copyFile3(ref.path, dest);
|
|
6513
6560
|
return dest;
|
|
6514
6561
|
}
|
|
@@ -6517,7 +6564,7 @@ async function refToUrl(ref) {
|
|
|
6517
6564
|
if (!ref.path) {
|
|
6518
6565
|
throw new Error("refToUrl: AssetRef has neither url nor path");
|
|
6519
6566
|
}
|
|
6520
|
-
const bytes = await
|
|
6567
|
+
const bytes = await readFile6(ref.path);
|
|
6521
6568
|
return `data:${ref.mime};base64,${bytes.toString("base64")}`;
|
|
6522
6569
|
}
|
|
6523
6570
|
var ASSET_KINDS = /* @__PURE__ */ new Set(["image", "video", "audio", "json", "text", "font"]);
|
|
@@ -6591,11 +6638,11 @@ var fontSpecimenNode = defineNode({
|
|
|
6591
6638
|
outputKinds: { image: "image" },
|
|
6592
6639
|
cost: () => ({ credits: 0, seconds_estimate: 5 }),
|
|
6593
6640
|
async execute({ inputs, params, ctx }) {
|
|
6594
|
-
const tmp = await mkdtemp3(
|
|
6641
|
+
const tmp = await mkdtemp3(path8.join(tmpdir3(), "font-specimen-"));
|
|
6595
6642
|
try {
|
|
6596
6643
|
const fontFilename = `font.${extForMime(inputs.font.mime)}`;
|
|
6597
6644
|
await stageAsset(inputs.font, tmp, fontFilename);
|
|
6598
|
-
const entryPath =
|
|
6645
|
+
const entryPath = path8.join(tmp, "index.html");
|
|
6599
6646
|
await writeFile3(entryPath, buildSpecimenHtml(params, fontFilename), "utf-8");
|
|
6600
6647
|
ctx.log(`rendering specimen (${params.font_size}px, ${params.text.split("\n").length} lines)`);
|
|
6601
6648
|
const pwSpecifier = ["play", "wright"].join("");
|
|
@@ -6679,16 +6726,16 @@ var fontSpecimenNode = defineNode({
|
|
|
6679
6726
|
|
|
6680
6727
|
// src/engine/nodes/local/hyperframe.ts
|
|
6681
6728
|
import { execFile as execFile4 } from "child_process";
|
|
6682
|
-
import { copyFile as copyFile4, mkdtemp as mkdtemp4, readFile as
|
|
6729
|
+
import { copyFile as copyFile4, mkdtemp as mkdtemp4, readFile as readFile10, rm as rm4, stat as stat5, writeFile as writeFile5 } from "fs/promises";
|
|
6683
6730
|
import { createRequire as createRequire2 } from "module";
|
|
6684
6731
|
import { cpus, tmpdir as tmpdir4 } from "os";
|
|
6685
|
-
import
|
|
6732
|
+
import path13 from "path";
|
|
6686
6733
|
import { promisify as promisify4 } from "util";
|
|
6687
6734
|
import { z as z11 } from "zod";
|
|
6688
6735
|
|
|
6689
6736
|
// src/engine/engine/composition-hash.ts
|
|
6690
|
-
import { readdir as readdir2, readFile as
|
|
6691
|
-
import
|
|
6737
|
+
import { readdir as readdir2, readFile as readFile7, stat as stat4 } from "fs/promises";
|
|
6738
|
+
import path9 from "path";
|
|
6692
6739
|
var SKIP_DIRS = /* @__PURE__ */ new Set([".cache", ".git", "node_modules", "dist", "build", ".next", ".turbo"]);
|
|
6693
6740
|
function isSkippedName(name) {
|
|
6694
6741
|
if (name.startsWith(".")) return true;
|
|
@@ -6705,23 +6752,23 @@ async function collectFiles(root, current) {
|
|
|
6705
6752
|
const names = await readdir2(current);
|
|
6706
6753
|
for (const name of names) {
|
|
6707
6754
|
if (isSkippedName(name)) continue;
|
|
6708
|
-
const abs =
|
|
6755
|
+
const abs = path9.join(current, name);
|
|
6709
6756
|
const s = await stat4(abs);
|
|
6710
6757
|
if (s.isDirectory()) {
|
|
6711
6758
|
out.push(...await collectFiles(root, abs));
|
|
6712
6759
|
continue;
|
|
6713
6760
|
}
|
|
6714
6761
|
if (!s.isFile()) continue;
|
|
6715
|
-
const bytes = await
|
|
6716
|
-
const relPath =
|
|
6762
|
+
const bytes = await readFile7(abs);
|
|
6763
|
+
const relPath = path9.relative(root, abs).split(path9.sep).join("/");
|
|
6717
6764
|
out.push({ relPath, contentSha: sha256Hex(bytes) });
|
|
6718
6765
|
}
|
|
6719
6766
|
return out;
|
|
6720
6767
|
}
|
|
6721
6768
|
|
|
6722
6769
|
// src/engine/engine/composition-meta.ts
|
|
6723
|
-
import { readFile as
|
|
6724
|
-
import
|
|
6770
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
6771
|
+
import path10 from "path";
|
|
6725
6772
|
import { z as z10 } from "zod";
|
|
6726
6773
|
var InputKind = z10.enum(["video", "image", "audio", "json"]);
|
|
6727
6774
|
var InputSpec = z10.object({
|
|
@@ -6798,10 +6845,10 @@ var CompositionMetaSchema = z10.object({
|
|
|
6798
6845
|
params: z10.record(z10.string(), ParamSpec).default({})
|
|
6799
6846
|
}).strict();
|
|
6800
6847
|
async function loadCompositionMeta(compositionDir) {
|
|
6801
|
-
const metaPath =
|
|
6848
|
+
const metaPath = path10.join(compositionDir, "meta.json");
|
|
6802
6849
|
let raw;
|
|
6803
6850
|
try {
|
|
6804
|
-
raw = await
|
|
6851
|
+
raw = await readFile8(metaPath, "utf-8");
|
|
6805
6852
|
} catch (e) {
|
|
6806
6853
|
throw new Error(`composition meta: cannot read ${metaPath} (${e.message})`);
|
|
6807
6854
|
}
|
|
@@ -6879,8 +6926,8 @@ function defaultFilenameForInput(key, kind) {
|
|
|
6879
6926
|
|
|
6880
6927
|
// src/engine/nodes/local/lib/hyperframe-check.ts
|
|
6881
6928
|
import { execFile as execFile3 } from "child_process";
|
|
6882
|
-
import { readFile as
|
|
6883
|
-
import
|
|
6929
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
6930
|
+
import path11 from "path";
|
|
6884
6931
|
import { promisify as promisify3 } from "util";
|
|
6885
6932
|
var execFileAsync = promisify3(execFile3);
|
|
6886
6933
|
var NEVER_BLOCK = [
|
|
@@ -7032,7 +7079,7 @@ ${detail}`);
|
|
|
7032
7079
|
}
|
|
7033
7080
|
let indexHtml = "";
|
|
7034
7081
|
try {
|
|
7035
|
-
indexHtml = await
|
|
7082
|
+
indexHtml = await readFile9(path11.join(dir, "index.html"), "utf-8");
|
|
7036
7083
|
} catch {
|
|
7037
7084
|
indexHtml = "";
|
|
7038
7085
|
}
|
|
@@ -7097,9 +7144,9 @@ ${stderr.slice(0, 1500)}`;
|
|
|
7097
7144
|
|
|
7098
7145
|
// src/engine/nodes/local/lib/hyperframe-meta.ts
|
|
7099
7146
|
import { writeFile as writeFile4 } from "fs/promises";
|
|
7100
|
-
import
|
|
7147
|
+
import path12 from "path";
|
|
7101
7148
|
async function ensureHyperframesMetaJson(tmp, nodeId, meta, duration) {
|
|
7102
|
-
const metaPath =
|
|
7149
|
+
const metaPath = path12.join(tmp, "meta.json");
|
|
7103
7150
|
await writeFile4(
|
|
7104
7151
|
metaPath,
|
|
7105
7152
|
JSON.stringify(
|
|
@@ -7199,7 +7246,7 @@ var hyperframeRenderNode = defineNode({
|
|
|
7199
7246
|
const compositionDir = await resolveCompositionDir(params.composition);
|
|
7200
7247
|
const meta = await loadCompositionMeta(compositionDir);
|
|
7201
7248
|
const compositionParams = validateAndParseDynamicParams(meta, params);
|
|
7202
|
-
const tmp = await mkdtemp4(
|
|
7249
|
+
const tmp = await mkdtemp4(path13.join(tmpdir4(), "hf-render-"));
|
|
7203
7250
|
try {
|
|
7204
7251
|
await copyComposition(compositionDir, tmp);
|
|
7205
7252
|
await vendorGsap(tmp, ctx);
|
|
@@ -7209,9 +7256,9 @@ var hyperframeRenderNode = defineNode({
|
|
|
7209
7256
|
await substituteCompositionFiles(tmp, substitutionValues);
|
|
7210
7257
|
await ensureHyperframesMetaJson(tmp, ctx.nodeId, meta, duration);
|
|
7211
7258
|
await runHyperframesCheck({ dir: tmp, nodeId: "hyperframe_render", ctx, timeoutMs: params.timeout_ms });
|
|
7212
|
-
const outputPath =
|
|
7259
|
+
const outputPath = path13.join(tmp, `output.${params.format}`);
|
|
7213
7260
|
await runRender({ tmp, outputPath, params, meta, ctx });
|
|
7214
|
-
const bytes = await
|
|
7261
|
+
const bytes = await readFile10(outputPath);
|
|
7215
7262
|
ctx.log(`rendered ${bytes.length} bytes`);
|
|
7216
7263
|
const ref = await ctx.assets.ingestBytes({
|
|
7217
7264
|
bytes: Buffer.from(bytes),
|
|
@@ -7233,10 +7280,10 @@ var hyperframeRenderNode = defineNode({
|
|
|
7233
7280
|
}
|
|
7234
7281
|
});
|
|
7235
7282
|
async function resolveCompositionDir(composition) {
|
|
7236
|
-
const compositionPath =
|
|
7283
|
+
const compositionPath = path13.isAbsolute(composition) ? composition : path13.resolve(process.cwd(), composition);
|
|
7237
7284
|
const s = await stat5(compositionPath);
|
|
7238
7285
|
if (s.isDirectory()) return compositionPath;
|
|
7239
|
-
return
|
|
7286
|
+
return path13.dirname(compositionPath);
|
|
7240
7287
|
}
|
|
7241
7288
|
async function validateComposition(rawParams) {
|
|
7242
7289
|
const issues = await validateCompositionParams(rawParams);
|
|
@@ -7318,7 +7365,7 @@ async function copyComposition(srcDir, destDir) {
|
|
|
7318
7365
|
await cp(srcDir, destDir, {
|
|
7319
7366
|
recursive: true,
|
|
7320
7367
|
filter: (src) => {
|
|
7321
|
-
const name =
|
|
7368
|
+
const name = path13.basename(src);
|
|
7322
7369
|
if (name === ".cache" || name === "node_modules" || name === ".git") return false;
|
|
7323
7370
|
return true;
|
|
7324
7371
|
}
|
|
@@ -7327,7 +7374,7 @@ async function copyComposition(srcDir, destDir) {
|
|
|
7327
7374
|
async function vendorGsap(tmp, ctx) {
|
|
7328
7375
|
try {
|
|
7329
7376
|
const gsapMin = require_2.resolve("gsap/dist/gsap.min.js");
|
|
7330
|
-
await copyFile4(gsapMin,
|
|
7377
|
+
await copyFile4(gsapMin, path13.join(tmp, "gsap.min.js"));
|
|
7331
7378
|
} catch (e) {
|
|
7332
7379
|
ctx.log(`warning: could not vendor gsap.min.js (${e.message}); compositions must self-supply`);
|
|
7333
7380
|
}
|
|
@@ -7342,7 +7389,7 @@ async function stageInputs2(tmp, inputs, meta, ctx) {
|
|
|
7342
7389
|
await stageAsset(ref, tmp, filename);
|
|
7343
7390
|
ctx.log(`staged ${spec.kind} \u2192 ${filename}`);
|
|
7344
7391
|
if (spec.kind === "video" && primaryDuration === null) {
|
|
7345
|
-
primaryDuration = await probeDurationSeconds(
|
|
7392
|
+
primaryDuration = await probeDurationSeconds(path13.join(tmp, filename));
|
|
7346
7393
|
}
|
|
7347
7394
|
}
|
|
7348
7395
|
return primaryDuration;
|
|
@@ -7388,8 +7435,8 @@ function coerceImageParam(value) {
|
|
|
7388
7435
|
throw new Error("hyperframe_render: image param must be a URL string or AssetRef");
|
|
7389
7436
|
}
|
|
7390
7437
|
async function substituteCompositionFiles(tmp, values) {
|
|
7391
|
-
const entryPath =
|
|
7392
|
-
const original = await
|
|
7438
|
+
const entryPath = path13.join(tmp, "index.html");
|
|
7439
|
+
const original = await readFile10(entryPath, "utf-8");
|
|
7393
7440
|
const { output, missing } = substituteVariables(original, values);
|
|
7394
7441
|
if (missing.length > 0) {
|
|
7395
7442
|
throw new Error(
|
|
@@ -7405,7 +7452,7 @@ function workerCount() {
|
|
|
7405
7452
|
async function runRender(opts) {
|
|
7406
7453
|
const { tmp, outputPath, params, meta, ctx } = opts;
|
|
7407
7454
|
const args = buildRenderArgs(tmp, outputPath, meta, params.format);
|
|
7408
|
-
ctx.log(`rendering ${meta.width}x${meta.height}@${meta.fps}fps ${params.format} from ${
|
|
7455
|
+
ctx.log(`rendering ${meta.width}x${meta.height}@${meta.fps}fps ${params.format} from ${path13.basename(tmp)}`);
|
|
7409
7456
|
try {
|
|
7410
7457
|
await execFileAsync2("npx", args, { timeout: params.timeout_ms, maxBuffer: 64 * 1024 * 1024 });
|
|
7411
7458
|
} catch (e) {
|
|
@@ -7449,10 +7496,10 @@ async function probeDurationSeconds(filePath) {
|
|
|
7449
7496
|
|
|
7450
7497
|
// src/engine/nodes/local/hyperframe-snapshot.ts
|
|
7451
7498
|
import { execFile as execFile5 } from "child_process";
|
|
7452
|
-
import { copyFile as copyFile5, mkdtemp as mkdtemp5, readFile as
|
|
7499
|
+
import { copyFile as copyFile5, mkdtemp as mkdtemp5, readFile as readFile11, rm as rm5, writeFile as writeFile6 } from "fs/promises";
|
|
7453
7500
|
import { createRequire as createRequire3 } from "module";
|
|
7454
7501
|
import { tmpdir as tmpdir5 } from "os";
|
|
7455
|
-
import
|
|
7502
|
+
import path14 from "path";
|
|
7456
7503
|
import { promisify as promisify5 } from "util";
|
|
7457
7504
|
import { z as z12 } from "zod";
|
|
7458
7505
|
var _execFileAsync = promisify5(execFile5);
|
|
@@ -7499,7 +7546,7 @@ var hyperframeSnapshotNode = defineNode({
|
|
|
7499
7546
|
const compositionDir = await resolveCompositionDir(params.composition);
|
|
7500
7547
|
const meta = await loadCompositionMeta(compositionDir);
|
|
7501
7548
|
const compositionParams = validateAndParseDynamicParams2(meta, params);
|
|
7502
|
-
const tmp = await mkdtemp5(
|
|
7549
|
+
const tmp = await mkdtemp5(path14.join(tmpdir5(), "hf-snap-"));
|
|
7503
7550
|
try {
|
|
7504
7551
|
await copyComposition2(compositionDir, tmp);
|
|
7505
7552
|
await vendorGsap2(tmp, ctx);
|
|
@@ -7514,7 +7561,7 @@ var hyperframeSnapshotNode = defineNode({
|
|
|
7514
7561
|
timeoutMs: params.timeout_ms,
|
|
7515
7562
|
samples: 1
|
|
7516
7563
|
});
|
|
7517
|
-
const entryPath =
|
|
7564
|
+
const entryPath = path14.join(tmp, "index.html");
|
|
7518
7565
|
const entryUrl = `file://${entryPath}`;
|
|
7519
7566
|
ctx.log(`snapshotting ${meta.width}x${meta.height}@${DEVICE_SCALE_FACTOR2}x wait=${params.wait_for.kind}`);
|
|
7520
7567
|
const pwSpecifier = ["play", "wright"].join("");
|
|
@@ -7575,7 +7622,7 @@ async function copyComposition2(srcDir, destDir) {
|
|
|
7575
7622
|
await cp(srcDir, destDir, {
|
|
7576
7623
|
recursive: true,
|
|
7577
7624
|
filter: (src) => {
|
|
7578
|
-
const name =
|
|
7625
|
+
const name = path14.basename(src);
|
|
7579
7626
|
if (name === ".cache" || name === "node_modules" || name === ".git") return false;
|
|
7580
7627
|
return true;
|
|
7581
7628
|
}
|
|
@@ -7584,7 +7631,7 @@ async function copyComposition2(srcDir, destDir) {
|
|
|
7584
7631
|
async function vendorGsap2(tmp, ctx) {
|
|
7585
7632
|
try {
|
|
7586
7633
|
const gsapMin = require_3.resolve("gsap/dist/gsap.min.js");
|
|
7587
|
-
await copyFile5(gsapMin,
|
|
7634
|
+
await copyFile5(gsapMin, path14.join(tmp, "gsap.min.js"));
|
|
7588
7635
|
} catch (e) {
|
|
7589
7636
|
ctx.log(`warning: could not vendor gsap.min.js (${e.message}); compositions must self-supply`);
|
|
7590
7637
|
}
|
|
@@ -7618,8 +7665,8 @@ function coerceImageParam2(value) {
|
|
|
7618
7665
|
throw new Error("hyperframe_snapshot: image param must be a URL string or AssetRef");
|
|
7619
7666
|
}
|
|
7620
7667
|
async function substituteCompositionFiles2(tmp, values) {
|
|
7621
|
-
const entryPath =
|
|
7622
|
-
const original = await
|
|
7668
|
+
const entryPath = path14.join(tmp, "index.html");
|
|
7669
|
+
const original = await readFile11(entryPath, "utf-8");
|
|
7623
7670
|
const { output, missing } = substituteVariables(original, values);
|
|
7624
7671
|
if (missing.length > 0) {
|
|
7625
7672
|
throw new Error(
|
|
@@ -8368,9 +8415,9 @@ var videoLipsyncNode = delegated({
|
|
|
8368
8415
|
});
|
|
8369
8416
|
|
|
8370
8417
|
// src/engine/nodes/remote/videoTranscribe.ts
|
|
8371
|
-
import { mkdtemp as mkdtemp6, readFile as
|
|
8418
|
+
import { mkdtemp as mkdtemp6, readFile as readFile12, rm as rm6 } from "fs/promises";
|
|
8372
8419
|
import { tmpdir as tmpdir6 } from "os";
|
|
8373
|
-
import
|
|
8420
|
+
import path15 from "path";
|
|
8374
8421
|
import { z as z33 } from "zod";
|
|
8375
8422
|
|
|
8376
8423
|
// src/engine/nodes/local/lib/ffmpeg.ts
|
|
@@ -8505,14 +8552,14 @@ async function tryExtractAudio(inputs, ctx) {
|
|
|
8505
8552
|
ctx.log("video_transcribe: no audio track detected, sending full video");
|
|
8506
8553
|
return null;
|
|
8507
8554
|
}
|
|
8508
|
-
tmpDir = await mkdtemp6(
|
|
8509
|
-
const audioPath =
|
|
8555
|
+
tmpDir = await mkdtemp6(path15.join(tmpdir6(), "vtx-"));
|
|
8556
|
+
const audioPath = path15.join(tmpDir, "audio.mp3");
|
|
8510
8557
|
ctx.log("video_transcribe: extracting audio (mono 16kHz mp3)");
|
|
8511
8558
|
await runFfmpeg(
|
|
8512
8559
|
["-i", video.path, "-vn", "-ac", "1", "-ar", "16000", "-b:a", "64k", "-f", "mp3", "-y", audioPath],
|
|
8513
8560
|
{ timeout_ms: AUDIO_EXTRACT_TIMEOUT_MS }
|
|
8514
8561
|
);
|
|
8515
|
-
const bytes = await
|
|
8562
|
+
const bytes = await readFile12(audioPath);
|
|
8516
8563
|
if (bytes.byteLength === 0) {
|
|
8517
8564
|
ctx.log("video_transcribe: extracted audio is empty, sending full video");
|
|
8518
8565
|
return null;
|
|
@@ -8619,19 +8666,19 @@ function safeCost(def) {
|
|
|
8619
8666
|
|
|
8620
8667
|
// src/engine/storage/cache-store.ts
|
|
8621
8668
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
8622
|
-
import { mkdir as mkdir3, readFile as
|
|
8623
|
-
import
|
|
8669
|
+
import { mkdir as mkdir3, readFile as readFile13, rename as rename2, writeFile as writeFile7 } from "fs/promises";
|
|
8670
|
+
import path16 from "path";
|
|
8624
8671
|
var LocalCacheStore = class {
|
|
8625
8672
|
rootDir;
|
|
8626
8673
|
constructor(rootDir) {
|
|
8627
8674
|
this.rootDir = rootDir;
|
|
8628
8675
|
}
|
|
8629
8676
|
filePath(cacheKey) {
|
|
8630
|
-
return
|
|
8677
|
+
return path16.join(this.rootDir, `${cacheKey}.json`);
|
|
8631
8678
|
}
|
|
8632
8679
|
async get(cacheKey) {
|
|
8633
8680
|
try {
|
|
8634
|
-
const buf = await
|
|
8681
|
+
const buf = await readFile13(this.filePath(cacheKey), "utf8");
|
|
8635
8682
|
return JSON.parse(buf);
|
|
8636
8683
|
} catch (e) {
|
|
8637
8684
|
if (e.code === "ENOENT") return null;
|
|
@@ -8640,7 +8687,7 @@ var LocalCacheStore = class {
|
|
|
8640
8687
|
}
|
|
8641
8688
|
async put(entry) {
|
|
8642
8689
|
const dest = this.filePath(entry.cacheKey);
|
|
8643
|
-
await mkdir3(
|
|
8690
|
+
await mkdir3(path16.dirname(dest), { recursive: true });
|
|
8644
8691
|
const tmp = `${dest}.tmp-${process.pid}-${randomUUID2()}`;
|
|
8645
8692
|
await writeFile7(tmp, JSON.stringify(entry, null, 0));
|
|
8646
8693
|
await rename2(tmp, dest);
|
|
@@ -8696,12 +8743,12 @@ function defaultRegistry() {
|
|
|
8696
8743
|
}
|
|
8697
8744
|
function createEngineFromEnv(opts = {}) {
|
|
8698
8745
|
const cwd = opts.cwd ?? process.cwd();
|
|
8699
|
-
const cacheDir = opts.cacheDir ??
|
|
8700
|
-
const outputsDir = opts.outputsDir ??
|
|
8746
|
+
const cacheDir = opts.cacheDir ?? path17.join(cwd, "canvas", ".cache");
|
|
8747
|
+
const outputsDir = opts.outputsDir ?? path17.join(cwd, "canvas");
|
|
8701
8748
|
const creds = requireCredentialsFromEnv();
|
|
8702
8749
|
const client = new BackendClient({ baseUrl: creds.url, apiKey: creds.apiKey });
|
|
8703
|
-
const assets = new LocalAssetStore(
|
|
8704
|
-
const localCache = new LocalCacheStore(
|
|
8750
|
+
const assets = new LocalAssetStore(path17.join(cacheDir, "assets"));
|
|
8751
|
+
const localCache = new LocalCacheStore(path17.join(cacheDir, "index"));
|
|
8705
8752
|
const remoteCacheEnabled = opts.remoteCache ?? remoteCacheEnabledFromEnv();
|
|
8706
8753
|
const cache = remoteCacheEnabled ? new LayeredCacheStore({
|
|
8707
8754
|
local: localCache,
|
|
@@ -8764,6 +8811,9 @@ export {
|
|
|
8764
8811
|
elementMentionKeywords,
|
|
8765
8812
|
MAX_REMOTE_IMAGE_BYTES,
|
|
8766
8813
|
fetchExternalBytes,
|
|
8814
|
+
ytDlpBlockSignal,
|
|
8815
|
+
YtDlpError,
|
|
8816
|
+
runYtDlp,
|
|
8767
8817
|
toModelSafeImage,
|
|
8768
8818
|
BackendClient2,
|
|
8769
8819
|
Engine2 as Engine,
|
|
@@ -8775,4 +8825,4 @@ export {
|
|
|
8775
8825
|
defaultRegistry,
|
|
8776
8826
|
createEngineFromEnv
|
|
8777
8827
|
};
|
|
8778
|
-
//# sourceMappingURL=chunk-
|
|
8828
|
+
//# sourceMappingURL=chunk-CAWFJ5X3.js.map
|