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