@koda-sl/baker-cli 0.199.0-dev.6d2f498f5 → 0.199.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 +32 -2
- package/dist/{chunk-ZS6BR7CU.js → chunk-2R2DEPG7.js} +3 -3
- package/dist/{chunk-MMP7BRV7.js → chunk-5MPIOGRO.js} +259 -202
- package/dist/chunk-5MPIOGRO.js.map +1 -0
- package/dist/{chunk-6B5RAIAC.js → chunk-6NZG2TCM.js} +82 -4
- package/dist/chunk-6NZG2TCM.js.map +1 -0
- package/dist/{chunk-CVIGWKG2.js → chunk-KHHD26LZ.js} +3 -3
- package/dist/{chunk-VFRDAUQH.js → chunk-SVHFT7AN.js} +4 -4
- package/dist/{chunk-PGPHKRK4.js → chunk-UZYVSPCA.js} +2 -2
- package/dist/cli.js +5787 -5180
- package/dist/cli.js.map +1 -1
- package/dist/client-MP7BKFKM.js +15 -0
- package/dist/engine/index.js +3 -3
- package/dist/{env-S4LMU6EN.js → env-FF7ZB5W5.js} +2 -2
- package/dist/{output-VCUZ4EBX.js → output-YGPWST77.js} +5 -5
- package/dist/{shared-WG5FCIHH.js → shared-CRWB5B7P.js} +6 -6
- package/package.json +1 -1
- package/dist/chunk-6B5RAIAC.js.map +0 -1
- package/dist/chunk-MMP7BRV7.js.map +0 -1
- package/dist/client-BBH4AYOK.js +0 -15
- /package/dist/{chunk-ZS6BR7CU.js.map → chunk-2R2DEPG7.js.map} +0 -0
- /package/dist/{chunk-CVIGWKG2.js.map → chunk-KHHD26LZ.js.map} +0 -0
- /package/dist/{chunk-VFRDAUQH.js.map → chunk-SVHFT7AN.js.map} +0 -0
- /package/dist/{chunk-PGPHKRK4.js.map → chunk-UZYVSPCA.js.map} +0 -0
- /package/dist/{client-BBH4AYOK.js.map → client-MP7BKFKM.js.map} +0 -0
- /package/dist/{env-S4LMU6EN.js.map → env-FF7ZB5W5.js.map} +0 -0
- /package/dist/{output-VCUZ4EBX.js.map → output-YGPWST77.js.map} +0 -0
- /package/dist/{shared-WG5FCIHH.js.map → shared-CRWB5B7P.js.map} +0 -0
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
2
|
debugLogHttp,
|
|
3
3
|
readBodyForLog
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-UZYVSPCA.js";
|
|
5
5
|
import {
|
|
6
6
|
__commonJS,
|
|
7
7
|
__toESM,
|
|
8
8
|
captureProxyCredentials,
|
|
9
9
|
childEnvWith,
|
|
10
10
|
isChallengeBody,
|
|
11
|
+
isPrivateAddress,
|
|
11
12
|
isProxyFailure,
|
|
12
13
|
plannedRoutes,
|
|
13
14
|
refuseNonPublicUrl,
|
|
14
15
|
shouldEscalate
|
|
15
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-6NZG2TCM.js";
|
|
16
17
|
|
|
17
|
-
//
|
|
18
|
+
// ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/index.js
|
|
18
19
|
var require_safe_stable_stringify = __commonJS({
|
|
19
|
-
"
|
|
20
|
+
"../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/index.js"(exports, module) {
|
|
20
21
|
"use strict";
|
|
21
22
|
var { hasOwnProperty } = Object.prototype;
|
|
22
23
|
var stringify = configure2();
|
|
@@ -1090,7 +1091,7 @@ function resolveAdaptFormats(params) {
|
|
|
1090
1091
|
return params.formats ?? [];
|
|
1091
1092
|
}
|
|
1092
1093
|
|
|
1093
|
-
//
|
|
1094
|
+
// ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/esm/wrapper.js
|
|
1094
1095
|
var import__ = __toESM(require_safe_stable_stringify(), 1);
|
|
1095
1096
|
var configure = import__.default.configure;
|
|
1096
1097
|
var wrapper_default = import__.default;
|
|
@@ -4975,35 +4976,6 @@ function shouldSniffChallenge(contentType, contentLength, maxSniffBytes) {
|
|
|
4975
4976
|
return type.includes("text/") || type.includes("html") || type.includes("json") || type.includes("xml");
|
|
4976
4977
|
}
|
|
4977
4978
|
|
|
4978
|
-
// ../proxy/src/privateAddress.ts
|
|
4979
|
-
function isPrivateIpv4(ip) {
|
|
4980
|
-
const parts = ip.split(".");
|
|
4981
|
-
if (parts.length !== 4) return false;
|
|
4982
|
-
const octets = parts.map((part) => Number(part));
|
|
4983
|
-
if (octets.some((octet) => !Number.isInteger(octet) || octet < 0 || octet > 255)) return false;
|
|
4984
|
-
const [a, b] = octets;
|
|
4985
|
-
if (a === 0) return true;
|
|
4986
|
-
if (a === 10) return true;
|
|
4987
|
-
if (a === 127) return true;
|
|
4988
|
-
if (a === 169 && b === 254) return true;
|
|
4989
|
-
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
4990
|
-
if (a === 192 && b === 168) return true;
|
|
4991
|
-
if (a === 100 && b >= 64 && b <= 127) return true;
|
|
4992
|
-
if (a >= 224) return true;
|
|
4993
|
-
return false;
|
|
4994
|
-
}
|
|
4995
|
-
function isPrivateAddress(ip) {
|
|
4996
|
-
const address = ip.trim().toLowerCase();
|
|
4997
|
-
if (!address) return true;
|
|
4998
|
-
const mapped = address.startsWith("::ffff:") ? address.slice("::ffff:".length) : null;
|
|
4999
|
-
if (mapped?.includes(".")) return isPrivateIpv4(mapped);
|
|
5000
|
-
if (address.includes(".")) return isPrivateIpv4(address);
|
|
5001
|
-
if (address === "::" || address === "::1") return true;
|
|
5002
|
-
if (/^f[cd][0-9a-f]{2}:/.test(address)) return true;
|
|
5003
|
-
if (/^fe[89ab][0-9a-f]:/.test(address)) return true;
|
|
5004
|
-
return false;
|
|
5005
|
-
}
|
|
5006
|
-
|
|
5007
4979
|
// ../proxy/src/fetch.ts
|
|
5008
4980
|
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
5009
4981
|
var MAX_SNIFF_BYTES = 64 * 1024;
|
|
@@ -5037,6 +5009,39 @@ function directAgent() {
|
|
|
5037
5009
|
});
|
|
5038
5010
|
}
|
|
5039
5011
|
var PRIVATE_ADDRESS_CODE = "EPRIVATEADDR";
|
|
5012
|
+
var MAX_REDIRECTS = 20;
|
|
5013
|
+
var REDIRECT_STATUS = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
|
|
5014
|
+
function originOf(url) {
|
|
5015
|
+
try {
|
|
5016
|
+
return new URL(url).origin;
|
|
5017
|
+
} catch {
|
|
5018
|
+
return null;
|
|
5019
|
+
}
|
|
5020
|
+
}
|
|
5021
|
+
function refuseRedirect(entry, to) {
|
|
5022
|
+
if (refuseNonPublicUrl(to) === null) return false;
|
|
5023
|
+
const destination = originOf(to);
|
|
5024
|
+
return destination === null || destination !== originOf(entry);
|
|
5025
|
+
}
|
|
5026
|
+
async function followRedirects(url, init) {
|
|
5027
|
+
let current = url;
|
|
5028
|
+
let request = { ...init, redirect: "manual" };
|
|
5029
|
+
for (let hop = 0; hop <= MAX_REDIRECTS; hop++) {
|
|
5030
|
+
const response = await undiciFetch(current, request);
|
|
5031
|
+
const location = response.headers.get("location");
|
|
5032
|
+
if (!REDIRECT_STATUS.has(response.status) || location === null) return response;
|
|
5033
|
+
await response.body?.cancel();
|
|
5034
|
+
const next = new URL(location, current).toString();
|
|
5035
|
+
if (refuseRedirect(url, next)) {
|
|
5036
|
+
throw new Error(`${PRIVATE_ADDRESS_CODE}: redirected to ${next}`);
|
|
5037
|
+
}
|
|
5038
|
+
const method = (request.method ?? "GET").toUpperCase();
|
|
5039
|
+
const downgrade = response.status === 303 || (response.status === 301 || response.status === 302) && method === "POST";
|
|
5040
|
+
request = downgrade ? { ...request, method: "GET", body: void 0 } : request;
|
|
5041
|
+
current = next;
|
|
5042
|
+
}
|
|
5043
|
+
throw new Error(`too many redirects following ${url}`);
|
|
5044
|
+
}
|
|
5040
5045
|
var NULL_BODY_STATUS = /* @__PURE__ */ new Set([101, 103, 204, 205, 304]);
|
|
5041
5046
|
function rebuild(response, body) {
|
|
5042
5047
|
if (response.status < 200 || response.status > 599 || NULL_BODY_STATUS.has(response.status)) {
|
|
@@ -5072,7 +5077,7 @@ async function attemptRoute(url, init, route, options) {
|
|
|
5072
5077
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
5073
5078
|
const signal = init.signal ? AbortSignal.any([init.signal, AbortSignal.timeout(timeoutMs)]) : AbortSignal.timeout(timeoutMs);
|
|
5074
5079
|
try {
|
|
5075
|
-
const raw = await
|
|
5080
|
+
const raw = await followRedirects(url, { ...init, dispatcher: agent ?? void 0, signal });
|
|
5076
5081
|
const read = await readOnce(raw, options.sniffChallenge ?? true, options.maxBytes);
|
|
5077
5082
|
const outcome = classifyFetchOutcome({ status: raw.status, challenge: read.challenge });
|
|
5078
5083
|
return {
|
|
@@ -5151,6 +5156,7 @@ var BROWSER_HEADERS = {
|
|
|
5151
5156
|
"Accept-Language": "en-US,en;q=0.9"
|
|
5152
5157
|
};
|
|
5153
5158
|
var NOT_PUBLIC_MESSAGE = "That address is not reachable from the public web, so there is nothing to read.";
|
|
5159
|
+
var MAX_REMOTE_IMAGE_BYTES = 25e6;
|
|
5154
5160
|
var ExternalFetchError = class extends Error {
|
|
5155
5161
|
/** True when the fault was ours — a route we could not open, not a dead host. */
|
|
5156
5162
|
ourFault;
|
|
@@ -5190,7 +5196,22 @@ async function fetchExternal(url, options = {}) {
|
|
|
5190
5196
|
}
|
|
5191
5197
|
return { response: result.response, blocked: endedBlocked(result) };
|
|
5192
5198
|
}
|
|
5193
|
-
|
|
5199
|
+
function refuseOversize(bytes, maxBytes) {
|
|
5200
|
+
if (bytes === null || bytes <= maxBytes) return null;
|
|
5201
|
+
return `That file is ${humanBytes(bytes)} \u2014 the limit is ${humanBytes(maxBytes)}.`;
|
|
5202
|
+
}
|
|
5203
|
+
function humanBytes(n) {
|
|
5204
|
+
if (n >= 1024 ** 3) return `${(n / 1024 ** 3).toFixed(1)} GB`;
|
|
5205
|
+
if (n >= 1024 ** 2) return `${(n / 1024 ** 2).toFixed(1)} MB`;
|
|
5206
|
+
return `${Math.max(1, Math.round(n / 1024))} KB`;
|
|
5207
|
+
}
|
|
5208
|
+
function declaredBytes(response) {
|
|
5209
|
+
const raw = response.headers.get("content-length");
|
|
5210
|
+
if (raw === null) return null;
|
|
5211
|
+
const value = Number(raw);
|
|
5212
|
+
return Number.isFinite(value) ? value : null;
|
|
5213
|
+
}
|
|
5214
|
+
async function fetchExternalBytes(url, options) {
|
|
5194
5215
|
const { response, blocked } = await fetchExternal(url, options);
|
|
5195
5216
|
if (!response.ok) {
|
|
5196
5217
|
throw new ExternalFetchError(`${response.status} ${response.statusText}`, false);
|
|
@@ -5198,10 +5219,16 @@ async function fetchExternalBytes(url, options = {}) {
|
|
|
5198
5219
|
if (blocked) {
|
|
5199
5220
|
throw new ExternalFetchError("That host served a security check instead of the file.", false);
|
|
5200
5221
|
}
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
}
|
|
5222
|
+
const declared = refuseOversize(declaredBytes(response), options.maxBytes);
|
|
5223
|
+
if (declared !== null) {
|
|
5224
|
+
throw new ExternalFetchError(declared, false);
|
|
5225
|
+
}
|
|
5226
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
5227
|
+
const actual = refuseOversize(buffer.byteLength, options.maxBytes);
|
|
5228
|
+
if (actual !== null) {
|
|
5229
|
+
throw new ExternalFetchError(actual, false);
|
|
5230
|
+
}
|
|
5231
|
+
return { buffer, contentType: response.headers.get("content-type") };
|
|
5205
5232
|
}
|
|
5206
5233
|
|
|
5207
5234
|
// src/engine/schema/refs.ts
|
|
@@ -5541,9 +5568,6 @@ async function ingestImageUrl(url, ctx) {
|
|
|
5541
5568
|
} catch (e) {
|
|
5542
5569
|
throw localExecError(ctx, `fetch ${url} \u2192 ${e.message}`);
|
|
5543
5570
|
}
|
|
5544
|
-
if (bytes.byteLength > MAX_ASSET_BYTES) {
|
|
5545
|
-
throw localExecError(ctx, `file_too_large: ${url} is ${bytes.byteLength} bytes (limit ${MAX_ASSET_BYTES})`);
|
|
5546
|
-
}
|
|
5547
5571
|
let normalized;
|
|
5548
5572
|
try {
|
|
5549
5573
|
normalized = await toModelSafeImage(bytes);
|
|
@@ -7893,20 +7917,50 @@ var imageDescribeNode = delegated({
|
|
|
7893
7917
|
cost: () => ({ credits: 2, seconds_estimate: 10 })
|
|
7894
7918
|
});
|
|
7895
7919
|
|
|
7896
|
-
// src/engine/nodes/remote/
|
|
7920
|
+
// src/engine/nodes/remote/imageLayerize.ts
|
|
7897
7921
|
import { z as z21 } from "zod";
|
|
7922
|
+
var ImageLayerizeParams = z21.object({
|
|
7923
|
+
/**
|
|
7924
|
+
* What to separate. Read as extraction instructions, not as a generation
|
|
7925
|
+
* prompt — "keep the bottle and its shadow as one layer", not "make it blue".
|
|
7926
|
+
*/
|
|
7927
|
+
instructions: z21.string().max(600).optional()
|
|
7928
|
+
}).strict();
|
|
7929
|
+
var imageLayerizeNode = delegated({
|
|
7930
|
+
id: "image_layerize",
|
|
7931
|
+
version: "1.0.0",
|
|
7932
|
+
category: "image",
|
|
7933
|
+
summary: "Split an image into editable layers: a transparent PNG per element, plus any copy baked into the picture recovered as real text with its typography. Powered by Seedream 5.0 Pro Layerize, with Ideogram lifting the text off first.",
|
|
7934
|
+
when_to_use: "Use when you need ONE element of a picture on its own (a product with no background, a logo lockup), or when a shot is right but its copy is wrong and regenerating would change the picture too. The manifest gives you each layer's box and, for text, its exact wording, font, size and colour \u2014 enough to re-set a headline in the original's type. For a plain subject cutout with no text involved, `image_background_remove` is cheaper and one call.",
|
|
7935
|
+
inputs: z21.object({
|
|
7936
|
+
image: ImageRef
|
|
7937
|
+
}).strict(),
|
|
7938
|
+
params: ImageLayerizeParams,
|
|
7939
|
+
outputs: z21.object({
|
|
7940
|
+
layers: z21.array(ImageRef),
|
|
7941
|
+
base: ImageRef,
|
|
7942
|
+
manifest: JsonRef
|
|
7943
|
+
}).strict(),
|
|
7944
|
+
outputKinds: { layers: "image", base: "image", manifest: "json" },
|
|
7945
|
+
// Billed per returned layer (2-17) plus an optional text pass; the backend
|
|
7946
|
+
// charges the real count on completion. This is the pre-run estimate.
|
|
7947
|
+
cost: () => ({ credits: 30, seconds_estimate: 100 })
|
|
7948
|
+
});
|
|
7949
|
+
|
|
7950
|
+
// src/engine/nodes/remote/imageReferenceSheet.ts
|
|
7951
|
+
import { z as z22 } from "zod";
|
|
7898
7952
|
var REFERENCE_SHEET_MODELS = ["google/gemini-3-pro-image-preview", "google/gemini-3.1-flash-image-preview"];
|
|
7899
|
-
var ImageReferenceSheetParams =
|
|
7900
|
-
model:
|
|
7901
|
-
subject_description:
|
|
7953
|
+
var ImageReferenceSheetParams = z22.object({
|
|
7954
|
+
model: z22.enum(REFERENCE_SHEET_MODELS),
|
|
7955
|
+
subject_description: z22.string().min(1),
|
|
7902
7956
|
// `location` = a set/room shown from several camera ANGLES (not a rotated subject),
|
|
7903
7957
|
// so a multi-scene shoot keeps one consistent set.
|
|
7904
|
-
subject_type:
|
|
7905
|
-
views:
|
|
7906
|
-
style:
|
|
7907
|
-
prompt_override:
|
|
7908
|
-
aspect_ratio:
|
|
7909
|
-
image_size:
|
|
7958
|
+
subject_type: z22.enum(["character", "person", "product", "location"]),
|
|
7959
|
+
views: z22.array(z22.string().min(1)).min(2).max(8).optional(),
|
|
7960
|
+
style: z22.string().optional(),
|
|
7961
|
+
prompt_override: z22.string().min(1).optional(),
|
|
7962
|
+
aspect_ratio: z22.enum(["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3", "4:5", "5:4", "21:9", "1:4", "4:1", "1:8", "8:1"]).optional(),
|
|
7963
|
+
image_size: z22.enum(OPENROUTER_IMAGE_SIZES).optional()
|
|
7910
7964
|
}).strict();
|
|
7911
7965
|
var imageReferenceSheetNode = delegated({
|
|
7912
7966
|
id: "image_reference_sheet",
|
|
@@ -7914,9 +7968,9 @@ var imageReferenceSheetNode = delegated({
|
|
|
7914
7968
|
category: "image",
|
|
7915
7969
|
summary: "Fuse 1\u20136 images of a single subject (person, character, product, or location/set) into ONE multi-view reference sheet \u2014 a labeled grid in consistent style and lighting: a turnaround (FRONT / SIDE / BACK\u2026) for a person/character/product, or several camera angles of the same room (WIDE / REVERSE / DETAIL\u2026) for a location. Curated models: Gemini 3 Pro Image (best fusion + labels), Gemini 3.1 Flash Image (cheap iteration).",
|
|
7916
7970
|
when_to_use: "Use before image_generate / video_generate when a subject must stay consistent across many creatives \u2014 wire the `sheet` output into their `reference` input instead of re-describing the subject per prompt. `subject_description` should be the exact wording you reuse downstream. Pick `google/gemini-3-pro-image-preview` for final 6-view sheets at 2K+, `google/gemini-3.1-flash-image-preview` while iterating.",
|
|
7917
|
-
inputs:
|
|
7971
|
+
inputs: z22.object({ references: z22.array(ImageRef).min(1).max(6) }).loose(),
|
|
7918
7972
|
params: ImageReferenceSheetParams,
|
|
7919
|
-
outputs:
|
|
7973
|
+
outputs: z22.object({ sheet: ImageRef }).strict(),
|
|
7920
7974
|
outputKinds: { sheet: "image" },
|
|
7921
7975
|
cost: ({ params }) => ({
|
|
7922
7976
|
credits: params?.model === "google/gemini-3-pro-image-preview" ? 20 : 5,
|
|
@@ -7925,10 +7979,10 @@ var imageReferenceSheetNode = delegated({
|
|
|
7925
7979
|
});
|
|
7926
7980
|
|
|
7927
7981
|
// src/engine/nodes/remote/imageSearch.ts
|
|
7928
|
-
import { z as
|
|
7929
|
-
var ImageSearchParams =
|
|
7930
|
-
prompt:
|
|
7931
|
-
count:
|
|
7982
|
+
import { z as z23 } from "zod";
|
|
7983
|
+
var ImageSearchParams = z23.object({
|
|
7984
|
+
prompt: z23.string().min(1),
|
|
7985
|
+
count: z23.number().int().min(1).max(20).default(5)
|
|
7932
7986
|
}).strict();
|
|
7933
7987
|
var imageSearchNode = delegated({
|
|
7934
7988
|
id: "image_search",
|
|
@@ -7936,15 +7990,15 @@ var imageSearchNode = delegated({
|
|
|
7936
7990
|
category: "image",
|
|
7937
7991
|
summary: "Agentic image search across Google Images, stock photography (Freepik), and Pinterest. An LLM agent picks the search tools and queries, selects the best matches, and the results are downloaded into canvas assets.",
|
|
7938
7992
|
when_to_use: "Use to gather real-world reference or inspiration images for a prompt (e.g. several photos of an australian shepherd) so a later step or the user can pick the best one. Not for creating new imagery \u2014 use image_generate for that.",
|
|
7939
|
-
inputs:
|
|
7993
|
+
inputs: z23.object({}).loose(),
|
|
7940
7994
|
params: ImageSearchParams,
|
|
7941
|
-
outputs:
|
|
7995
|
+
outputs: z23.object({ images: z23.array(ImageRef).min(1) }).strict(),
|
|
7942
7996
|
outputKinds: { images: "image" },
|
|
7943
7997
|
cost: ({ params }) => ({ credits: Math.ceil(2 + params.count / 2), seconds_estimate: 30 })
|
|
7944
7998
|
});
|
|
7945
7999
|
|
|
7946
8000
|
// src/engine/nodes/remote/imageSelect.ts
|
|
7947
|
-
import { z as
|
|
8001
|
+
import { z as z24 } from "zod";
|
|
7948
8002
|
var IMAGE_SELECT_MODELS = ["~google/gemini-flash-latest", "~google/gemini-pro-latest"];
|
|
7949
8003
|
var imageSelectNode = delegated({
|
|
7950
8004
|
id: "image_select",
|
|
@@ -7952,15 +8006,15 @@ var imageSelectNode = delegated({
|
|
|
7952
8006
|
category: "vision",
|
|
7953
8007
|
summary: "Pick the best `count` images out of 2+ candidates with a vision LLM, judged against a prompt. Outputs a passthrough subset of the input refs (no new pixels) plus the model's comparative reasoning.",
|
|
7954
8008
|
when_to_use: "Use after fanning out several image_generate variants (or any pool of 2+ images) to keep only the strongest before expensive downstream steps \u2014 video generation, reference sheets, final delivery. `count` fixes the output size, so `images#0`\u2026`images#count-1` are always safe to wire. Pick `~google/gemini-flash-latest` for cheap/fast picks and `~google/gemini-pro-latest` for harder aesthetic judgement.",
|
|
7955
|
-
inputs:
|
|
7956
|
-
params:
|
|
7957
|
-
model:
|
|
7958
|
-
prompt:
|
|
7959
|
-
count:
|
|
7960
|
-
temperature:
|
|
7961
|
-
max_tokens:
|
|
8009
|
+
inputs: z24.object({ images: z24.array(ImageRef).min(2) }).loose(),
|
|
8010
|
+
params: z24.object({
|
|
8011
|
+
model: z24.enum(IMAGE_SELECT_MODELS),
|
|
8012
|
+
prompt: z24.string().min(1),
|
|
8013
|
+
count: z24.number().int().min(1).default(1),
|
|
8014
|
+
temperature: z24.number().min(0).max(2).optional(),
|
|
8015
|
+
max_tokens: z24.number().int().positive().optional()
|
|
7962
8016
|
}).strict(),
|
|
7963
|
-
outputs:
|
|
8017
|
+
outputs: z24.object({ images: z24.array(ImageRef).min(1), reasoning: TextRef }).strict(),
|
|
7964
8018
|
outputKinds: { images: "image", reasoning: "text" },
|
|
7965
8019
|
cost: () => ({ credits: 1, seconds_estimate: 5 }),
|
|
7966
8020
|
// Arity is only knowable at validate time when `images` is a literal array
|
|
@@ -7985,34 +8039,34 @@ var imageSelectNode = delegated({
|
|
|
7985
8039
|
});
|
|
7986
8040
|
|
|
7987
8041
|
// src/engine/nodes/remote/music.ts
|
|
7988
|
-
import { z as
|
|
8042
|
+
import { z as z25 } from "zod";
|
|
7989
8043
|
var MUSIC_MODELS = ["elevenlabs/music-v1", "elevenlabs/video-background-music-v1"];
|
|
7990
|
-
var MusicParams =
|
|
7991
|
-
model:
|
|
8044
|
+
var MusicParams = z25.object({
|
|
8045
|
+
model: z25.enum(MUSIC_MODELS),
|
|
7992
8046
|
/** Free-form prompt. Used by `elevenlabs/music-v1` (compose-detailed). */
|
|
7993
|
-
prompt:
|
|
8047
|
+
prompt: z25.string().optional(),
|
|
7994
8048
|
/**
|
|
7995
8049
|
* Structured composition plan (intro / hook / verse / outro sections with
|
|
7996
8050
|
* per-section styles + durations). Mutually exclusive with `prompt`.
|
|
7997
8051
|
*/
|
|
7998
|
-
composition_plan:
|
|
8052
|
+
composition_plan: z25.record(z25.string(), z25.unknown()).optional(),
|
|
7999
8053
|
/** Target length when using `prompt`. 3000–454545ms (capped by the $10 per-node cost limit). */
|
|
8000
|
-
music_length_ms:
|
|
8001
|
-
seed:
|
|
8054
|
+
music_length_ms: z25.number().int().min(3e3).max(ELEVENLABS_MAX_MUSIC_LENGTH_MS).optional(),
|
|
8055
|
+
seed: z25.number().int().optional(),
|
|
8002
8056
|
/** Prompt mode only — forces an instrumental (no vocals) track. */
|
|
8003
|
-
force_instrumental:
|
|
8057
|
+
force_instrumental: z25.boolean().optional(),
|
|
8004
8058
|
/** composition_plan only — honor exact section durations. */
|
|
8005
|
-
respect_sections_durations:
|
|
8059
|
+
respect_sections_durations: z25.boolean().optional(),
|
|
8006
8060
|
/** Emit word-level timestamps alongside the audio. */
|
|
8007
|
-
with_timestamps:
|
|
8061
|
+
with_timestamps: z25.boolean().optional(),
|
|
8008
8062
|
/**
|
|
8009
8063
|
* video-to-music only — short description of the desired score
|
|
8010
8064
|
* ("upbeat synth, fast cuts, 80s") used to bias the model.
|
|
8011
8065
|
*/
|
|
8012
|
-
description:
|
|
8066
|
+
description: z25.string().max(1e3).optional(),
|
|
8013
8067
|
/** video-to-music only — up to 10 style tags. */
|
|
8014
|
-
tags:
|
|
8015
|
-
output_format:
|
|
8068
|
+
tags: z25.array(z25.string()).max(10).optional(),
|
|
8069
|
+
output_format: z25.enum(ELEVENLABS_OUTPUT_FORMATS).optional()
|
|
8016
8070
|
}).strict();
|
|
8017
8071
|
var musicNode = delegated({
|
|
8018
8072
|
id: "music",
|
|
@@ -8020,9 +8074,9 @@ var musicNode = delegated({
|
|
|
8020
8074
|
category: "audio",
|
|
8021
8075
|
summary: "Generate music for ad creatives and website video content. `elevenlabs/music-v1` composes from a text prompt or structured composition plan; `elevenlabs/video-background-music-v1` scores an existing video clip provided via `inputs.video`.",
|
|
8022
8076
|
when_to_use: "Use to produce background music or a full score for video ads, hero-section reels, or any motion content. Prefer the video-to-music model when you already have a cut and want music timed to it; use compose-detailed when you have only a brief or want section-level control (intro / hook / outro). Pair the resulting audio with `video_generate` or `video_lipsync` at compose time.",
|
|
8023
|
-
inputs:
|
|
8077
|
+
inputs: z25.object({ video: VideoRef.optional() }).loose(),
|
|
8024
8078
|
params: MusicParams,
|
|
8025
|
-
outputs:
|
|
8079
|
+
outputs: z25.object({ audio: AudioRef, timestamps: JsonRef.optional() }).strict(),
|
|
8026
8080
|
outputKinds: { audio: "audio", timestamps: "json" },
|
|
8027
8081
|
cost: ({ params }) => {
|
|
8028
8082
|
const seconds = params.music_length_ms ? Math.ceil(params.music_length_ms / 1e3) : 30;
|
|
@@ -8053,25 +8107,25 @@ var musicNode = delegated({
|
|
|
8053
8107
|
});
|
|
8054
8108
|
|
|
8055
8109
|
// src/engine/nodes/remote/soundEffect.ts
|
|
8056
|
-
import { z as
|
|
8110
|
+
import { z as z26 } from "zod";
|
|
8057
8111
|
var SOUND_EFFECT_MODELS = ["elevenlabs/eleven_text_to_sound_v2"];
|
|
8058
|
-
var SoundEffectParams =
|
|
8059
|
-
model:
|
|
8112
|
+
var SoundEffectParams = z26.object({
|
|
8113
|
+
model: z26.enum(SOUND_EFFECT_MODELS),
|
|
8060
8114
|
/** Prompt describing the SFX ("metal door slam", "soft UI tap", "ocean waves"). */
|
|
8061
|
-
text:
|
|
8115
|
+
text: z26.string().min(1),
|
|
8062
8116
|
/**
|
|
8063
8117
|
* Target length in seconds. 0.5–30. Leave unset to let the model pick the
|
|
8064
8118
|
* natural length for the described effect.
|
|
8065
8119
|
*/
|
|
8066
|
-
duration_seconds:
|
|
8120
|
+
duration_seconds: z26.number().min(0.5).max(30).optional(),
|
|
8067
8121
|
/**
|
|
8068
8122
|
* 0–1. Higher = stick closer to the prompt at the cost of variety; lower
|
|
8069
8123
|
* = let the model interpret more freely. Defaults to 0.3 on the provider.
|
|
8070
8124
|
*/
|
|
8071
|
-
prompt_influence:
|
|
8125
|
+
prompt_influence: z26.number().min(0).max(1).optional(),
|
|
8072
8126
|
/** Only valid on `eleven_text_to_sound_v2` — produce a seamless loop. */
|
|
8073
|
-
loop:
|
|
8074
|
-
output_format:
|
|
8127
|
+
loop: z26.boolean().optional(),
|
|
8128
|
+
output_format: z26.enum(ELEVENLABS_OUTPUT_FORMATS).optional()
|
|
8075
8129
|
}).strict();
|
|
8076
8130
|
var soundEffectNode = delegated({
|
|
8077
8131
|
id: "sound_effect",
|
|
@@ -8079,9 +8133,9 @@ var soundEffectNode = delegated({
|
|
|
8079
8133
|
category: "audio",
|
|
8080
8134
|
summary: "Generate short sound effects from a text prompt via ElevenLabs Text-to-Sound. Use for whooshes, impacts, UI clicks, ambient beds, or signature stingers in ad creatives and product videos.",
|
|
8081
8135
|
when_to_use: "Reach for this when you need a punch-in SFX layered against `video_generate` or `hyperframe_render` output \u2014 e.g. a logo whoosh on a hero shot, a click on a CTA cut, a swelling ambient bed under VO. Set `loop: true` for atmospheric beds that need to tile under longer footage; leave `duration_seconds` unset and the model picks a natural length.",
|
|
8082
|
-
inputs:
|
|
8136
|
+
inputs: z26.object({}).loose(),
|
|
8083
8137
|
params: SoundEffectParams,
|
|
8084
|
-
outputs:
|
|
8138
|
+
outputs: z26.object({ audio: AudioRef }).strict(),
|
|
8085
8139
|
outputKinds: { audio: "audio" },
|
|
8086
8140
|
cost: ({ params }) => {
|
|
8087
8141
|
const seconds = params.duration_seconds ?? 5;
|
|
@@ -8090,7 +8144,7 @@ var soundEffectNode = delegated({
|
|
|
8090
8144
|
});
|
|
8091
8145
|
|
|
8092
8146
|
// src/engine/nodes/remote/textGenerate.ts
|
|
8093
|
-
import { z as
|
|
8147
|
+
import { z as z27 } from "zod";
|
|
8094
8148
|
var TEXT_GENERATE_MODELS = ["~google/gemini-flash-latest", "~google/gemini-pro-latest"];
|
|
8095
8149
|
var textGenerateNode = delegated({
|
|
8096
8150
|
id: "text_generate",
|
|
@@ -8098,58 +8152,58 @@ var textGenerateNode = delegated({
|
|
|
8098
8152
|
category: "language",
|
|
8099
8153
|
summary: "Single-turn LLM text generation via OpenRouter. Returns a text response.",
|
|
8100
8154
|
when_to_use: 'Use for any short text generation step in a canvas \u2014 ad copy, hooks, headlines, JSON outputs for downstream nodes. Pick `~google/gemini-flash-latest` for cheap/fast work and `~google/gemini-pro-latest` for harder reasoning. When the output must be JSON for a downstream `{{slot}}` (e.g. the ad-blueprint transform), set `response_format: "json_object"` so the model returns clean JSON with no markdown fences or prose. Set `web_search: true` to let the model search the live web first (OpenRouter `:online`) \u2014 useful when the transform must adapt copy to the target brand\'s real facts (current pricing, the trust signals it actually has) rather than guess.',
|
|
8101
|
-
inputs:
|
|
8102
|
-
params:
|
|
8103
|
-
model:
|
|
8104
|
-
prompt:
|
|
8105
|
-
system:
|
|
8106
|
-
response_format:
|
|
8107
|
-
web_search:
|
|
8108
|
-
temperature:
|
|
8109
|
-
max_tokens:
|
|
8155
|
+
inputs: z27.object({}).loose(),
|
|
8156
|
+
params: z27.object({
|
|
8157
|
+
model: z27.enum(TEXT_GENERATE_MODELS),
|
|
8158
|
+
prompt: z27.string().min(1),
|
|
8159
|
+
system: z27.string().optional(),
|
|
8160
|
+
response_format: z27.enum(["text", "json_object"]).optional(),
|
|
8161
|
+
web_search: z27.boolean().optional(),
|
|
8162
|
+
temperature: z27.number().min(0).max(2).optional(),
|
|
8163
|
+
max_tokens: z27.number().int().positive().optional()
|
|
8110
8164
|
}).strict(),
|
|
8111
|
-
outputs:
|
|
8165
|
+
outputs: z27.object({ text: TextRef }).strict(),
|
|
8112
8166
|
outputKinds: { text: "text" },
|
|
8113
8167
|
cost: () => ({ credits: 1, seconds_estimate: 3 })
|
|
8114
8168
|
});
|
|
8115
8169
|
|
|
8116
8170
|
// src/engine/nodes/remote/tts.ts
|
|
8117
|
-
import { z as
|
|
8171
|
+
import { z as z28 } from "zod";
|
|
8118
8172
|
var TTS_MODELS = ["elevenlabs/eleven_v3"];
|
|
8119
|
-
var TtsVoiceSettings =
|
|
8120
|
-
stability:
|
|
8121
|
-
similarity_boost:
|
|
8122
|
-
style:
|
|
8123
|
-
use_speaker_boost:
|
|
8124
|
-
speed:
|
|
8173
|
+
var TtsVoiceSettings = z28.object({
|
|
8174
|
+
stability: z28.number().min(0).max(1).optional(),
|
|
8175
|
+
similarity_boost: z28.number().min(0).max(1).optional(),
|
|
8176
|
+
style: z28.number().min(0).max(1).optional(),
|
|
8177
|
+
use_speaker_boost: z28.boolean().optional(),
|
|
8178
|
+
speed: z28.number().min(0.25).max(4).optional()
|
|
8125
8179
|
}).strict();
|
|
8126
|
-
var TtsPronunciationLocator =
|
|
8127
|
-
pronunciation_dictionary_id:
|
|
8128
|
-
version_id:
|
|
8180
|
+
var TtsPronunciationLocator = z28.object({
|
|
8181
|
+
pronunciation_dictionary_id: z28.string().min(1),
|
|
8182
|
+
version_id: z28.string().nullable().optional()
|
|
8129
8183
|
}).strict();
|
|
8130
|
-
var TtsParams =
|
|
8131
|
-
model:
|
|
8132
|
-
text:
|
|
8133
|
-
voice:
|
|
8184
|
+
var TtsParams = z28.object({
|
|
8185
|
+
model: z28.enum(TTS_MODELS),
|
|
8186
|
+
text: z28.string().min(1).max(ELEVENLABS_MAX_TEXT_CHARS),
|
|
8187
|
+
voice: z28.string().min(1),
|
|
8134
8188
|
/** Provider output_format (mp3 family only — assets are stored as audio/mpeg). */
|
|
8135
|
-
output_format:
|
|
8136
|
-
seed:
|
|
8189
|
+
output_format: z28.enum(ELEVENLABS_OUTPUT_FORMATS).optional(),
|
|
8190
|
+
seed: z28.number().int().min(0).max(4294967295).optional(),
|
|
8137
8191
|
// Top-level shortcuts; structured form is `voice_settings`.
|
|
8138
|
-
stability:
|
|
8139
|
-
similarity_boost:
|
|
8192
|
+
stability: z28.number().min(0).max(1).optional(),
|
|
8193
|
+
similarity_boost: z28.number().min(0).max(1).optional(),
|
|
8140
8194
|
voice_settings: TtsVoiceSettings.optional(),
|
|
8141
8195
|
/** ISO 639-1 language code. eleven_v3 supports language hints. */
|
|
8142
|
-
language_code:
|
|
8143
|
-
pronunciation_dictionary_locators:
|
|
8144
|
-
apply_text_normalization:
|
|
8196
|
+
language_code: z28.string().optional(),
|
|
8197
|
+
pronunciation_dictionary_locators: z28.array(TtsPronunciationLocator).max(3).optional(),
|
|
8198
|
+
apply_text_normalization: z28.enum(["auto", "on", "off"]).optional(),
|
|
8145
8199
|
/** Currently Japanese-only. Adds latency. */
|
|
8146
|
-
apply_language_text_normalization:
|
|
8200
|
+
apply_language_text_normalization: z28.boolean().optional(),
|
|
8147
8201
|
/**
|
|
8148
8202
|
* When true, hits `/v1/text-to-speech/{voice_id}/with-timestamps` and
|
|
8149
8203
|
* adds a `timestamps` output (character-level alignment) for caption
|
|
8150
8204
|
* rendering, lipsync, and beat-matched cuts.
|
|
8151
8205
|
*/
|
|
8152
|
-
with_timestamps:
|
|
8206
|
+
with_timestamps: z28.boolean().optional()
|
|
8153
8207
|
}).strict();
|
|
8154
8208
|
var ttsNode = delegated({
|
|
8155
8209
|
id: "tts",
|
|
@@ -8157,9 +8211,9 @@ var ttsNode = delegated({
|
|
|
8157
8211
|
category: "audio",
|
|
8158
8212
|
summary: "Single-voice text-to-speech via ElevenLabs Eleven v3. Optional character-level timestamps for caption rendering and beat-matched cuts.",
|
|
8159
8213
|
when_to_use: "Use for single-speaker VO \u2014 ad reads, hero-section narration, product walkthroughs. Reach for `dialogue` when you need multiple voices in one stitched track. Set `with_timestamps: true` when downstream needs character-level alignment (captions, lipsync).",
|
|
8160
|
-
inputs:
|
|
8214
|
+
inputs: z28.object({}).loose(),
|
|
8161
8215
|
params: TtsParams,
|
|
8162
|
-
outputs:
|
|
8216
|
+
outputs: z28.object({ audio: AudioRef, timestamps: JsonRef.optional() }).strict(),
|
|
8163
8217
|
outputKinds: { audio: "audio", timestamps: "json" },
|
|
8164
8218
|
cost: ({ params }) => ({
|
|
8165
8219
|
credits: Math.max(1, Math.ceil(params.text.length * 15e-4)),
|
|
@@ -8168,25 +8222,25 @@ var ttsNode = delegated({
|
|
|
8168
8222
|
});
|
|
8169
8223
|
|
|
8170
8224
|
// src/engine/nodes/remote/video.ts
|
|
8171
|
-
import { z as
|
|
8172
|
-
var videoModelEnum =
|
|
8173
|
-
var VideoGenerateParams =
|
|
8225
|
+
import { z as z29 } from "zod";
|
|
8226
|
+
var videoModelEnum = z29.enum(VIDEO_GENERATE_MODELS);
|
|
8227
|
+
var VideoGenerateParams = z29.object({
|
|
8174
8228
|
model: videoModelEnum,
|
|
8175
|
-
prompt:
|
|
8176
|
-
duration:
|
|
8177
|
-
resolution:
|
|
8229
|
+
prompt: z29.string().min(1),
|
|
8230
|
+
duration: z29.number().int().positive().optional(),
|
|
8231
|
+
resolution: z29.string().optional(),
|
|
8178
8232
|
// Union of ratios accepted by at least one curated model (registry gates
|
|
8179
8233
|
// per-model). 3:2/2:3 are deliberately absent: no registered model takes them.
|
|
8180
|
-
aspect_ratio:
|
|
8181
|
-
generate_audio:
|
|
8182
|
-
seed:
|
|
8234
|
+
aspect_ratio: z29.enum(["16:9", "9:16", "1:1", "4:3", "3:4", "21:9", "9:21"]).optional(),
|
|
8235
|
+
generate_audio: z29.boolean().optional(),
|
|
8236
|
+
seed: z29.number().int().nonnegative().optional(),
|
|
8183
8237
|
// Veo-only passthroughs (routed via `provider.options.google-vertex.parameters`).
|
|
8184
|
-
negative_prompt:
|
|
8185
|
-
person_generation:
|
|
8186
|
-
enhance_prompt:
|
|
8187
|
-
conditioning_scale:
|
|
8238
|
+
negative_prompt: z29.string().optional(),
|
|
8239
|
+
person_generation: z29.string().optional(),
|
|
8240
|
+
enhance_prompt: z29.boolean().optional(),
|
|
8241
|
+
conditioning_scale: z29.number().optional(),
|
|
8188
8242
|
// Kling-only passthrough (prompt-adherence dial, sent top-level).
|
|
8189
|
-
cfg_scale:
|
|
8243
|
+
cfg_scale: z29.number().optional()
|
|
8190
8244
|
}).strict();
|
|
8191
8245
|
var videoGenerateNode = delegated({
|
|
8192
8246
|
id: "video_generate",
|
|
@@ -8194,7 +8248,7 @@ var videoGenerateNode = delegated({
|
|
|
8194
8248
|
category: "video",
|
|
8195
8249
|
summary: "Generate video for ad creatives. Curated roster: `google/gemini-omni-flash` (DEFAULT \u2014 native audio + reference images in one call, 720p, 3-10s), `bytedance/seedance-2.0` (identity/product workhorse, NO photoreal human faces), `google/veo-3.1` (photoreal cine ceiling + the human-face model), `google/veo-3.1-fast` (cheap Veo iteration), `kwaivgi/kling-3.0` (motion-transfer/dynamic). Async with polling.",
|
|
8196
8250
|
when_to_use: "`google/gemini-omni-flash` is the DEFAULT: it renders picture and synchronized audio in one call, and is the only model here that takes reference images alongside a prompt. SET `duration` (3-10s) to the length the beat actually needs \u2014 it bills ~$0.101 PER SECOND, so leaving it out renders the 10s default and charges ~$1.01 for a beat that may need 3s (~$0.30). Its resolution is fixed at 720p and it accepts only `16:9` or `9:16`; it takes NO `resolution`, `seed`, `negative_prompt` or `generate_audio` param \u2014 sending one fails validation. Keep the default for ordinary beats; move off it when a beat needs 1080p, a length outside 3-10s, or a 1:1 frame. Use `bytedance/seedance-2.0` for identity/product output WITHOUT a photoreal human face on camera. Any clip whose frame shows a photoreal human face \u2014 a UGC creator, presenter, or testimonial speaker, REAL OR AI-GENERATED \u2014 must use `google/veo-3.1`: ByteDance's filter rejects photoreal human faces on Seedance (content_policy_blocked/E005), and a synthetic AI face does NOT clear it. Use `google/veo-3.1-fast` while iterating to keep cost low; `kwaivgi/kling-3.0` for motion-transfer/hyper-dynamic beats. `baker canvas scaffold-video` auto-routes a photoreal cast to Veo; a Seedance clip blocked at runtime also auto-recovers on Veo. Each model gates its own durations/resolutions/aspect ratios in the registry \u2014 see the README per-model section.",
|
|
8197
|
-
inputs:
|
|
8251
|
+
inputs: z29.object({
|
|
8198
8252
|
first_frame: ImageRef.optional(),
|
|
8199
8253
|
last_frame: ImageRef.optional(),
|
|
8200
8254
|
reference: ImageRef.optional(),
|
|
@@ -8204,10 +8258,10 @@ var videoGenerateNode = delegated({
|
|
|
8204
8258
|
* exclusive with `first_frame` — OpenRouter treats a request carrying both
|
|
8205
8259
|
* as image-to-video and ignores these, so wire one or the other.
|
|
8206
8260
|
*/
|
|
8207
|
-
references:
|
|
8261
|
+
references: z29.array(ImageRef).optional()
|
|
8208
8262
|
}).loose(),
|
|
8209
8263
|
params: VideoGenerateParams,
|
|
8210
|
-
outputs:
|
|
8264
|
+
outputs: z29.object({ video: VideoRef }).strict(),
|
|
8211
8265
|
outputKinds: { video: "video" },
|
|
8212
8266
|
// Priced from the shared contract, so `validate`'s quote and the charge the
|
|
8213
8267
|
// backend applies come from ONE table. A flat number here under-quoted a
|
|
@@ -8227,11 +8281,11 @@ var videoGenerateNode = delegated({
|
|
|
8227
8281
|
});
|
|
8228
8282
|
|
|
8229
8283
|
// src/engine/nodes/remote/videoBackgroundRemove.ts
|
|
8230
|
-
import { z as
|
|
8231
|
-
var VideoBackgroundRemoveParams =
|
|
8232
|
-
model:
|
|
8233
|
-
edge_refinement:
|
|
8234
|
-
output_codec:
|
|
8284
|
+
import { z as z30 } from "zod";
|
|
8285
|
+
var VideoBackgroundRemoveParams = z30.object({
|
|
8286
|
+
model: z30.literal("fal/veed-video-background-removal").optional().default("fal/veed-video-background-removal"),
|
|
8287
|
+
edge_refinement: z30.boolean().optional().default(true),
|
|
8288
|
+
output_codec: z30.enum(["vp9", "h264"]).optional().default("vp9")
|
|
8235
8289
|
}).strict();
|
|
8236
8290
|
var videoBackgroundRemoveNode = delegated({
|
|
8237
8291
|
id: "video_background_remove",
|
|
@@ -8239,18 +8293,18 @@ var videoBackgroundRemoveNode = delegated({
|
|
|
8239
8293
|
category: "video",
|
|
8240
8294
|
summary: "Remove the background from a video and return a transparent VP9-with-alpha WebM (or H264 RGB+alpha pair). Drops directly into a hyperframe composition as `<video src='...'>` for chroma-keyed picture-in-picture overlays. Powered by fal.ai `veed/video-background-removal/fast`.",
|
|
8241
8295
|
when_to_use: "Use when you need a talking-head or subject to float over a custom background in a hyperframe composition. Pair with hyperframe_render(composition: screencast-with-talker) for screencast-with-narrator videos. Output is `video/webm` with alpha \u2014 feed straight into `<video src>` in a composition.",
|
|
8242
|
-
inputs:
|
|
8296
|
+
inputs: z30.object({
|
|
8243
8297
|
video: VideoRef
|
|
8244
8298
|
}).strict(),
|
|
8245
8299
|
params: VideoBackgroundRemoveParams,
|
|
8246
|
-
outputs:
|
|
8300
|
+
outputs: z30.object({ video: VideoRef }).strict(),
|
|
8247
8301
|
outputKinds: { video: "video" },
|
|
8248
8302
|
// $0.012 per 30 frames (edge refinement on) — assume ~30fps; refine via fal dashboard.
|
|
8249
8303
|
cost: () => ({ credits: 50, seconds_estimate: 60 })
|
|
8250
8304
|
});
|
|
8251
8305
|
|
|
8252
8306
|
// src/engine/nodes/remote/videoDeconstruct.ts
|
|
8253
|
-
import { z as
|
|
8307
|
+
import { z as z31 } from "zod";
|
|
8254
8308
|
var VIDEO_DECONSTRUCT_MODELS = ["~google/gemini-flash-latest", "~google/gemini-pro-latest"];
|
|
8255
8309
|
var videoDeconstructNode = delegated({
|
|
8256
8310
|
id: "video_deconstruct",
|
|
@@ -8258,34 +8312,34 @@ var videoDeconstructNode = delegated({
|
|
|
8258
8312
|
category: "video",
|
|
8259
8313
|
summary: 'Deconstruct a video into a replication-grade blueprint: scene boundaries, the real start/end frame of every scene (extracted from the video as images), and an exhaustive JSON analysis \u2014 per-scene action detail, camera motion, generation-ready frame/motion prompts, overlay text with full typographic style, floating elements, deeply detailed cast (perceived demographics, ethnicity/skin-tone, styling, market-recasting notes), brand-identified logos (named by brand and what they signal, not by appearance, with on-screen timestamps), dialogue with voice descriptions, music spec, SFX list, plus a word-level transcript. `mode:"index"` is the cheap structure-first pass: scene boundaries + global blueprint only (one LLM call, no frames).',
|
|
8260
8314
|
when_to_use: 'Use to reverse-engineer a reference video (e.g. a competitor ad) so a new canvas can reproduce or remix it scene by scene. Agent loop: (1) optionally run `mode:"index"` to see the structure cheaply (scene count, boundaries, transcript) before planning; (2) run the full deconstruct; (3) read `analysis` and author the reproduction canvas. The blueprint maps 1:1 onto generation nodes: `analysis.scenes[i]` aligns positionally with `start_frames#i`/`end_frames#i`; per scene, `start_frame_prompt`/`end_frame_prompt` feed image_generate (overlay text is excluded from them by contract \u2014 recomposite it from `overlays`), `motion_prompt` + the two frames feed video_generate (first_frame/last_frame), `dialogue[].voice_description` casts tts/dialogue voices, `global.music.music_prompt` feeds music, `sfx[].sound_effect_prompt` feeds sound_effect, and `overlays`/`floating_elements` drive an ffmpeg/hyperframe overlay pass. Long videos (over ~8 min single-shot): run `mode:"index"` first, then several full nodes IN PARALLEL each with a `start_s`/`end_s` window (\u2264480s, snap edges to index scene boundaries), and merge by concatenating `analysis.scenes`; over-length errors include suggested windows. Inject fields into downstream prompts via `{{slot}}`. Pick `~google/gemini-pro-latest` for the densest extraction, `~google/gemini-flash-latest` for cheap/fast passes.',
|
|
8261
|
-
inputs:
|
|
8262
|
-
params:
|
|
8263
|
-
model:
|
|
8264
|
-
mode:
|
|
8265
|
-
language:
|
|
8266
|
-
max_scenes:
|
|
8267
|
-
focus:
|
|
8268
|
-
start_s:
|
|
8269
|
-
end_s:
|
|
8315
|
+
inputs: z31.object({ video: VideoRef }).loose(),
|
|
8316
|
+
params: z31.object({
|
|
8317
|
+
model: z31.enum(VIDEO_DECONSTRUCT_MODELS),
|
|
8318
|
+
mode: z31.enum(["full", "index"]).optional(),
|
|
8319
|
+
language: z31.string().min(2).max(8).optional(),
|
|
8320
|
+
max_scenes: z31.number().int().min(1).max(60).optional(),
|
|
8321
|
+
focus: z31.string().optional(),
|
|
8322
|
+
start_s: z31.number().min(0).optional(),
|
|
8323
|
+
end_s: z31.number().positive().optional(),
|
|
8270
8324
|
// Real visual shot-cut timestamps (absolute seconds), detected locally with
|
|
8271
8325
|
// ffmpeg before the deconstruct. The backend SNAPS its LLM scene boundaries
|
|
8272
8326
|
// onto these and SPLITS any scene that spans one, so a scene's frames never
|
|
8273
8327
|
// straddle a hard cut. `scaffold-video` populates this; omit for LLM-only cuts.
|
|
8274
|
-
shot_cuts:
|
|
8328
|
+
shot_cuts: z31.array(z31.number().min(0)).max(200).optional(),
|
|
8275
8329
|
// The video model's per-clip ceiling (seconds). A shot longer than this is
|
|
8276
8330
|
// split into seamless continuation sub-scenes (shared splice frame), so long
|
|
8277
8331
|
// shots reproduce in full instead of being truncated. `scaffold-video` sets
|
|
8278
8332
|
// the Seedance ceiling (15); omit to disable length splitting.
|
|
8279
|
-
max_clip_s:
|
|
8333
|
+
max_clip_s: z31.number().positive().max(60).optional(),
|
|
8280
8334
|
// Transcript provider for the blueprint's dialogue/transcript. Default
|
|
8281
8335
|
// Groq Whisper; "deepgram" routes to Nova-3 so words carry punctuation.
|
|
8282
|
-
transcriber:
|
|
8336
|
+
transcriber: z31.enum(["groq", "deepgram"]).optional()
|
|
8283
8337
|
}).strict(),
|
|
8284
|
-
outputs:
|
|
8338
|
+
outputs: z31.object({
|
|
8285
8339
|
analysis: JsonRef,
|
|
8286
8340
|
// Absent in mode:"index" (structure only, no Mux frame extraction).
|
|
8287
|
-
start_frames:
|
|
8288
|
-
end_frames:
|
|
8341
|
+
start_frames: z31.array(ImageRef).min(1).optional(),
|
|
8342
|
+
end_frames: z31.array(ImageRef).min(1).optional(),
|
|
8289
8343
|
transcript: JsonRef
|
|
8290
8344
|
}).strict(),
|
|
8291
8345
|
outputKinds: { analysis: "json", start_frames: "image", end_frames: "image", transcript: "json" },
|
|
@@ -8293,22 +8347,22 @@ var videoDeconstructNode = delegated({
|
|
|
8293
8347
|
});
|
|
8294
8348
|
|
|
8295
8349
|
// src/engine/nodes/remote/videoLipsync.ts
|
|
8296
|
-
import { z as
|
|
8297
|
-
var FalLipsyncParams =
|
|
8298
|
-
model:
|
|
8350
|
+
import { z as z32 } from "zod";
|
|
8351
|
+
var FalLipsyncParams = z32.object({
|
|
8352
|
+
model: z32.literal("fal/veed-lipsync")
|
|
8299
8353
|
}).strict();
|
|
8300
|
-
var VideoLipsyncParams =
|
|
8354
|
+
var VideoLipsyncParams = z32.discriminatedUnion("model", [FalLipsyncParams]);
|
|
8301
8355
|
var videoLipsyncNode = delegated({
|
|
8302
8356
|
id: "video_lipsync",
|
|
8303
8357
|
version: "1.0.0",
|
|
8304
8358
|
category: "video",
|
|
8305
8359
|
summary: "Lip-sync a video to an audio track. Currently backed by VEED via fal.ai (`fal/veed-lipsync`). $0.40/min of output.",
|
|
8306
|
-
inputs:
|
|
8360
|
+
inputs: z32.object({
|
|
8307
8361
|
video: VideoRef,
|
|
8308
8362
|
audio: AudioRef
|
|
8309
8363
|
}).strict(),
|
|
8310
8364
|
params: VideoLipsyncParams,
|
|
8311
|
-
outputs:
|
|
8365
|
+
outputs: z32.object({ video: VideoRef }).strict(),
|
|
8312
8366
|
outputKinds: { video: "video" },
|
|
8313
8367
|
cost: () => ({ credits: 20, seconds_estimate: 120 })
|
|
8314
8368
|
});
|
|
@@ -8317,7 +8371,7 @@ var videoLipsyncNode = delegated({
|
|
|
8317
8371
|
import { mkdtemp as mkdtemp6, readFile as readFile11, rm as rm6 } from "fs/promises";
|
|
8318
8372
|
import { tmpdir as tmpdir6 } from "os";
|
|
8319
8373
|
import path14 from "path";
|
|
8320
|
-
import { z as
|
|
8374
|
+
import { z as z33 } from "zod";
|
|
8321
8375
|
|
|
8322
8376
|
// src/engine/nodes/local/lib/ffmpeg.ts
|
|
8323
8377
|
import { execFile as execFile7 } from "child_process";
|
|
@@ -8396,24 +8450,24 @@ ${detail.slice(-4e3)}`);
|
|
|
8396
8450
|
}
|
|
8397
8451
|
|
|
8398
8452
|
// src/engine/nodes/remote/videoTranscribe.ts
|
|
8399
|
-
var VideoTranscribeParams =
|
|
8400
|
-
language:
|
|
8453
|
+
var VideoTranscribeParams = z33.object({
|
|
8454
|
+
language: z33.string().min(2).max(8).optional(),
|
|
8401
8455
|
// Provider choice is explicit (no env-based silent branching). Default Groq
|
|
8402
8456
|
// Whisper; "deepgram" routes to Deepgram Nova-3, which additionally emits a
|
|
8403
8457
|
// `rich` JSON output with punctuated words + paragraph/sentence grouping.
|
|
8404
|
-
transcriber:
|
|
8458
|
+
transcriber: z33.enum(["groq", "deepgram"]).optional()
|
|
8405
8459
|
}).strict();
|
|
8406
|
-
var VideoTranscribeInputs =
|
|
8460
|
+
var VideoTranscribeInputs = z33.object({
|
|
8407
8461
|
// A video (audio auto-extracted locally) OR a bare audio track. The key stays
|
|
8408
8462
|
// `video` for back-compat; the backend already accepts audio-kind refs on it —
|
|
8409
8463
|
// the local extraction path has been shipping one for every video input.
|
|
8410
|
-
video:
|
|
8464
|
+
video: z33.union([VideoRef, AudioRef])
|
|
8411
8465
|
}).strict();
|
|
8412
|
-
var VideoTranscribeOutputs =
|
|
8413
|
-
transcript:
|
|
8466
|
+
var VideoTranscribeOutputs = z33.object({
|
|
8467
|
+
transcript: z33.custom(),
|
|
8414
8468
|
// Only emitted by the Deepgram path: full punctuated words + paragraph /
|
|
8415
8469
|
// sentence grouping with speaker indices. Absent for the default Groq path.
|
|
8416
|
-
rich:
|
|
8470
|
+
rich: z33.custom().optional()
|
|
8417
8471
|
}).strict();
|
|
8418
8472
|
var AUDIO_EXTRACT_TIMEOUT_MS = 6e4;
|
|
8419
8473
|
var videoTranscribeNode = defineNode({
|
|
@@ -8498,29 +8552,29 @@ async function tryExtractAudio(inputs, ctx) {
|
|
|
8498
8552
|
}
|
|
8499
8553
|
|
|
8500
8554
|
// src/engine/nodes/remote/voiceSelect.ts
|
|
8501
|
-
import { z as
|
|
8555
|
+
import { z as z34 } from "zod";
|
|
8502
8556
|
var voiceSelectNode = delegated({
|
|
8503
8557
|
id: "voice_select",
|
|
8504
8558
|
version: "1.0.0",
|
|
8505
8559
|
category: "audio",
|
|
8506
8560
|
summary: 'Cast an ElevenLabs voice from a natural-language description (e.g. "warm, authoritative female narrator, American accent"). Lists the account\'s voices and ranks them against the brief, emitting the best `voice_id` as a bare-string text asset plus a ranked `candidates` JSON.',
|
|
8507
8561
|
when_to_use: 'Use to turn a voice description (e.g. from a `video_deconstruct` blueprint\'s `voice_description`) into a usable ElevenLabs voice id, then feed it into a `tts` node by wiring `inputs.voice_ref: $ref:<this>.voice_id` and setting `params.voice: "{{voice_ref}}"` \u2014 the engine splices the id in at run time. Review `candidates` (json) to pick a different voice. Optional `gender`/`age`/`accent`/`language` hints sharpen the ranking.',
|
|
8508
|
-
inputs:
|
|
8509
|
-
params:
|
|
8510
|
-
description:
|
|
8511
|
-
gender:
|
|
8512
|
-
age:
|
|
8513
|
-
accent:
|
|
8514
|
-
language:
|
|
8515
|
-
limit:
|
|
8562
|
+
inputs: z34.object({}).loose(),
|
|
8563
|
+
params: z34.object({
|
|
8564
|
+
description: z34.string().min(1),
|
|
8565
|
+
gender: z34.string().optional(),
|
|
8566
|
+
age: z34.string().optional(),
|
|
8567
|
+
accent: z34.string().optional(),
|
|
8568
|
+
language: z34.string().optional(),
|
|
8569
|
+
limit: z34.number().int().min(1).max(20).optional()
|
|
8516
8570
|
}).strict(),
|
|
8517
|
-
outputs:
|
|
8571
|
+
outputs: z34.object({ voice_id: TextRef, candidates: JsonRef }).strict(),
|
|
8518
8572
|
outputKinds: { voice_id: "text", candidates: "json" },
|
|
8519
8573
|
cost: () => ({ credits: 0, seconds_estimate: 5 })
|
|
8520
8574
|
});
|
|
8521
8575
|
|
|
8522
8576
|
// src/engine/schema/catalog.ts
|
|
8523
|
-
import { z as
|
|
8577
|
+
import { z as z35 } from "zod";
|
|
8524
8578
|
function generateCatalog(registry, opts = {}) {
|
|
8525
8579
|
const entries = registry.all().map((def) => {
|
|
8526
8580
|
const cost = def.cost ? safeCost(def) : void 0;
|
|
@@ -8531,9 +8585,9 @@ function generateCatalog(registry, opts = {}) {
|
|
|
8531
8585
|
summary: def.summary,
|
|
8532
8586
|
when_to_use: def.when_to_use,
|
|
8533
8587
|
location: def.location,
|
|
8534
|
-
inputs:
|
|
8535
|
-
params:
|
|
8536
|
-
outputs:
|
|
8588
|
+
inputs: z35.toJSONSchema(def.inputs, { unrepresentable: "any" }),
|
|
8589
|
+
params: z35.toJSONSchema(def.params, { unrepresentable: "any" }),
|
|
8590
|
+
outputs: z35.toJSONSchema(def.outputs, { unrepresentable: "any" }),
|
|
8537
8591
|
cost_estimate_credits: cost?.credits,
|
|
8538
8592
|
runtime_estimate_seconds: cost?.seconds_estimate
|
|
8539
8593
|
};
|
|
@@ -8631,7 +8685,8 @@ var REMOTE_NODES = [
|
|
|
8631
8685
|
videoBackgroundRemoveNode,
|
|
8632
8686
|
videoDeconstructNode,
|
|
8633
8687
|
voiceSelectNode,
|
|
8634
|
-
imageBackgroundRemoveNode
|
|
8688
|
+
imageBackgroundRemoveNode,
|
|
8689
|
+
imageLayerizeNode
|
|
8635
8690
|
];
|
|
8636
8691
|
function defaultRegistry() {
|
|
8637
8692
|
const r = new NodeRegistry();
|
|
@@ -8698,6 +8753,7 @@ export {
|
|
|
8698
8753
|
REF_PREFIX,
|
|
8699
8754
|
parseRefExpr,
|
|
8700
8755
|
sha256Hex,
|
|
8756
|
+
MAX_ASSET_BYTES,
|
|
8701
8757
|
looksLikeHttpUrl,
|
|
8702
8758
|
SEEDANCE_PROFILE,
|
|
8703
8759
|
clipProfileFor,
|
|
@@ -8706,6 +8762,7 @@ export {
|
|
|
8706
8762
|
spineInputFlags,
|
|
8707
8763
|
spineInputOps,
|
|
8708
8764
|
elementMentionKeywords,
|
|
8765
|
+
MAX_REMOTE_IMAGE_BYTES,
|
|
8709
8766
|
fetchExternalBytes,
|
|
8710
8767
|
toModelSafeImage,
|
|
8711
8768
|
BackendClient2,
|
|
@@ -8718,4 +8775,4 @@ export {
|
|
|
8718
8775
|
defaultRegistry,
|
|
8719
8776
|
createEngineFromEnv
|
|
8720
8777
|
};
|
|
8721
|
-
//# sourceMappingURL=chunk-
|
|
8778
|
+
//# sourceMappingURL=chunk-5MPIOGRO.js.map
|