@koda-sl/baker-cli 0.198.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 +34 -2
- package/dist/{chunk-ZBS53HJF.js → chunk-2R2DEPG7.js} +3 -3
- package/dist/{chunk-CVJCXDWD.js → chunk-5MPIOGRO.js} +297 -176
- package/dist/chunk-5MPIOGRO.js.map +1 -0
- package/dist/{chunk-MVCCSWDE.js → chunk-6NZG2TCM.js} +82 -4
- package/dist/chunk-6NZG2TCM.js.map +1 -0
- package/dist/{chunk-UBTH7NPV.js → chunk-KHHD26LZ.js} +3 -3
- package/dist/{chunk-XVF27TBY.js → chunk-SVHFT7AN.js} +4 -4
- package/dist/{chunk-RN2XKQIJ.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-6PUX5HJY.js → env-FF7ZB5W5.js} +2 -2
- package/dist/{output-VRWY3V6Y.js → output-YGPWST77.js} +5 -5
- package/dist/{shared-T2HONXTH.js → shared-CRWB5B7P.js} +6 -6
- package/package.json +1 -1
- package/dist/chunk-CVJCXDWD.js.map +0 -1
- package/dist/chunk-MVCCSWDE.js.map +0 -1
- package/dist/client-YOLAMGIA.js +0 -15
- /package/dist/{chunk-ZBS53HJF.js.map → chunk-2R2DEPG7.js.map} +0 -0
- /package/dist/{chunk-UBTH7NPV.js.map → chunk-KHHD26LZ.js.map} +0 -0
- /package/dist/{chunk-XVF27TBY.js.map → chunk-SVHFT7AN.js.map} +0 -0
- /package/dist/{chunk-RN2XKQIJ.js.map → chunk-UZYVSPCA.js.map} +0 -0
- /package/dist/{client-YOLAMGIA.js.map → client-MP7BKFKM.js.map} +0 -0
- /package/dist/{env-6PUX5HJY.js.map → env-FF7ZB5W5.js.map} +0 -0
- /package/dist/{output-VRWY3V6Y.js.map → output-YGPWST77.js.map} +0 -0
- /package/dist/{shared-T2HONXTH.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;
|
|
@@ -4917,7 +4918,8 @@ import { promisify } from "util";
|
|
|
4917
4918
|
import { z as z5 } from "zod";
|
|
4918
4919
|
|
|
4919
4920
|
// ../proxy/src/fetch.ts
|
|
4920
|
-
import {
|
|
4921
|
+
import { lookup as dnsLookup } from "dns";
|
|
4922
|
+
import { Agent, ProxyAgent, Response, fetch as undiciFetch } from "undici";
|
|
4921
4923
|
|
|
4922
4924
|
// ../proxy/src/fetchDecision.ts
|
|
4923
4925
|
function classifyFetchOutcome(signal) {
|
|
@@ -4981,10 +4983,65 @@ function tierOf(route) {
|
|
|
4981
4983
|
return route.kind === "proxy" ? route.tier : "direct";
|
|
4982
4984
|
}
|
|
4983
4985
|
function agentFor(route) {
|
|
4984
|
-
if (route.kind !== "proxy") return
|
|
4986
|
+
if (route.kind !== "proxy") return directAgent();
|
|
4985
4987
|
const token = Buffer.from(`${route.username}:${route.password}`).toString("base64");
|
|
4986
4988
|
return new ProxyAgent({ uri: route.server, token: `Basic ${token}` });
|
|
4987
4989
|
}
|
|
4990
|
+
function directAgent() {
|
|
4991
|
+
return new Agent({
|
|
4992
|
+
connect: {
|
|
4993
|
+
lookup: (hostname, options, callback) => {
|
|
4994
|
+
dnsLookup(hostname, options, (error, address, family) => {
|
|
4995
|
+
if (error) {
|
|
4996
|
+
callback(error, address, family);
|
|
4997
|
+
return;
|
|
4998
|
+
}
|
|
4999
|
+
const found = Array.isArray(address) ? address.map((entry) => entry.address) : [address];
|
|
5000
|
+
const blocked = found.find((entry) => isPrivateAddress(entry));
|
|
5001
|
+
if (blocked !== void 0) {
|
|
5002
|
+
callback(new Error(`${PRIVATE_ADDRESS_CODE}: ${hostname} resolves to ${blocked}`), "", 4);
|
|
5003
|
+
return;
|
|
5004
|
+
}
|
|
5005
|
+
callback(null, address, family);
|
|
5006
|
+
});
|
|
5007
|
+
}
|
|
5008
|
+
}
|
|
5009
|
+
});
|
|
5010
|
+
}
|
|
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
|
+
}
|
|
4988
5045
|
var NULL_BODY_STATUS = /* @__PURE__ */ new Set([101, 103, 204, 205, 304]);
|
|
4989
5046
|
function rebuild(response, body) {
|
|
4990
5047
|
if (response.status < 200 || response.status > 599 || NULL_BODY_STATUS.has(response.status)) {
|
|
@@ -5020,7 +5077,7 @@ async function attemptRoute(url, init, route, options) {
|
|
|
5020
5077
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
5021
5078
|
const signal = init.signal ? AbortSignal.any([init.signal, AbortSignal.timeout(timeoutMs)]) : AbortSignal.timeout(timeoutMs);
|
|
5022
5079
|
try {
|
|
5023
|
-
const raw = await
|
|
5080
|
+
const raw = await followRedirects(url, { ...init, dispatcher: agent ?? void 0, signal });
|
|
5024
5081
|
const read = await readOnce(raw, options.sniffChallenge ?? true, options.maxBytes);
|
|
5025
5082
|
const outcome = classifyFetchOutcome({ status: raw.status, challenge: read.challenge });
|
|
5026
5083
|
return {
|
|
@@ -5083,6 +5140,14 @@ function endedProxyUnavailable(result) {
|
|
|
5083
5140
|
if (rescued(result) || endedBlocked(result)) return false;
|
|
5084
5141
|
return result.attempts.some((attempt) => attempt.outcome === "proxy_unavailable");
|
|
5085
5142
|
}
|
|
5143
|
+
function endedPrivateAddress(result) {
|
|
5144
|
+
if (rescued(result)) return false;
|
|
5145
|
+
for (let error = result.error, depth = 0; error instanceof Error && depth < 5; depth++) {
|
|
5146
|
+
if (error.message.includes(PRIVATE_ADDRESS_CODE)) return true;
|
|
5147
|
+
error = error.cause;
|
|
5148
|
+
}
|
|
5149
|
+
return false;
|
|
5150
|
+
}
|
|
5086
5151
|
|
|
5087
5152
|
// src/lib/externalFetch.ts
|
|
5088
5153
|
var BROWSER_HEADERS = {
|
|
@@ -5090,6 +5155,8 @@ var BROWSER_HEADERS = {
|
|
|
5090
5155
|
Accept: "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8",
|
|
5091
5156
|
"Accept-Language": "en-US,en;q=0.9"
|
|
5092
5157
|
};
|
|
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;
|
|
5093
5160
|
var ExternalFetchError = class extends Error {
|
|
5094
5161
|
/** True when the fault was ours — a route we could not open, not a dead host. */
|
|
5095
5162
|
ourFault;
|
|
@@ -5101,7 +5168,7 @@ var ExternalFetchError = class extends Error {
|
|
|
5101
5168
|
};
|
|
5102
5169
|
async function fetchExternal(url, options = {}) {
|
|
5103
5170
|
if (refuseNonPublicUrl(url) !== null) {
|
|
5104
|
-
throw new ExternalFetchError(
|
|
5171
|
+
throw new ExternalFetchError(NOT_PUBLIC_MESSAGE, false);
|
|
5105
5172
|
}
|
|
5106
5173
|
const result = await fetchWithEscalation(
|
|
5107
5174
|
url,
|
|
@@ -5118,6 +5185,9 @@ async function fetchExternal(url, options = {}) {
|
|
|
5118
5185
|
`);
|
|
5119
5186
|
}
|
|
5120
5187
|
if (result.response === null) {
|
|
5188
|
+
if (endedPrivateAddress(result)) {
|
|
5189
|
+
throw new ExternalFetchError(NOT_PUBLIC_MESSAGE, false);
|
|
5190
|
+
}
|
|
5121
5191
|
const ourFault = endedProxyUnavailable(result);
|
|
5122
5192
|
throw new ExternalFetchError(
|
|
5123
5193
|
ourFault ? "Could not open a route to that address." : "That address did not answer.",
|
|
@@ -5126,7 +5196,22 @@ async function fetchExternal(url, options = {}) {
|
|
|
5126
5196
|
}
|
|
5127
5197
|
return { response: result.response, blocked: endedBlocked(result) };
|
|
5128
5198
|
}
|
|
5129
|
-
|
|
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) {
|
|
5130
5215
|
const { response, blocked } = await fetchExternal(url, options);
|
|
5131
5216
|
if (!response.ok) {
|
|
5132
5217
|
throw new ExternalFetchError(`${response.status} ${response.statusText}`, false);
|
|
@@ -5134,10 +5219,16 @@ async function fetchExternalBytes(url, options = {}) {
|
|
|
5134
5219
|
if (blocked) {
|
|
5135
5220
|
throw new ExternalFetchError("That host served a security check instead of the file.", false);
|
|
5136
5221
|
}
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
}
|
|
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") };
|
|
5141
5232
|
}
|
|
5142
5233
|
|
|
5143
5234
|
// src/engine/schema/refs.ts
|
|
@@ -5477,9 +5568,6 @@ async function ingestImageUrl(url, ctx) {
|
|
|
5477
5568
|
} catch (e) {
|
|
5478
5569
|
throw localExecError(ctx, `fetch ${url} \u2192 ${e.message}`);
|
|
5479
5570
|
}
|
|
5480
|
-
if (bytes.byteLength > MAX_ASSET_BYTES) {
|
|
5481
|
-
throw localExecError(ctx, `file_too_large: ${url} is ${bytes.byteLength} bytes (limit ${MAX_ASSET_BYTES})`);
|
|
5482
|
-
}
|
|
5483
5571
|
let normalized;
|
|
5484
5572
|
try {
|
|
5485
5573
|
normalized = await toModelSafeImage(bytes);
|
|
@@ -7829,20 +7917,50 @@ var imageDescribeNode = delegated({
|
|
|
7829
7917
|
cost: () => ({ credits: 2, seconds_estimate: 10 })
|
|
7830
7918
|
});
|
|
7831
7919
|
|
|
7832
|
-
// src/engine/nodes/remote/
|
|
7920
|
+
// src/engine/nodes/remote/imageLayerize.ts
|
|
7833
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";
|
|
7834
7952
|
var REFERENCE_SHEET_MODELS = ["google/gemini-3-pro-image-preview", "google/gemini-3.1-flash-image-preview"];
|
|
7835
|
-
var ImageReferenceSheetParams =
|
|
7836
|
-
model:
|
|
7837
|
-
subject_description:
|
|
7953
|
+
var ImageReferenceSheetParams = z22.object({
|
|
7954
|
+
model: z22.enum(REFERENCE_SHEET_MODELS),
|
|
7955
|
+
subject_description: z22.string().min(1),
|
|
7838
7956
|
// `location` = a set/room shown from several camera ANGLES (not a rotated subject),
|
|
7839
7957
|
// so a multi-scene shoot keeps one consistent set.
|
|
7840
|
-
subject_type:
|
|
7841
|
-
views:
|
|
7842
|
-
style:
|
|
7843
|
-
prompt_override:
|
|
7844
|
-
aspect_ratio:
|
|
7845
|
-
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()
|
|
7846
7964
|
}).strict();
|
|
7847
7965
|
var imageReferenceSheetNode = delegated({
|
|
7848
7966
|
id: "image_reference_sheet",
|
|
@@ -7850,9 +7968,9 @@ var imageReferenceSheetNode = delegated({
|
|
|
7850
7968
|
category: "image",
|
|
7851
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).",
|
|
7852
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.",
|
|
7853
|
-
inputs:
|
|
7971
|
+
inputs: z22.object({ references: z22.array(ImageRef).min(1).max(6) }).loose(),
|
|
7854
7972
|
params: ImageReferenceSheetParams,
|
|
7855
|
-
outputs:
|
|
7973
|
+
outputs: z22.object({ sheet: ImageRef }).strict(),
|
|
7856
7974
|
outputKinds: { sheet: "image" },
|
|
7857
7975
|
cost: ({ params }) => ({
|
|
7858
7976
|
credits: params?.model === "google/gemini-3-pro-image-preview" ? 20 : 5,
|
|
@@ -7861,10 +7979,10 @@ var imageReferenceSheetNode = delegated({
|
|
|
7861
7979
|
});
|
|
7862
7980
|
|
|
7863
7981
|
// src/engine/nodes/remote/imageSearch.ts
|
|
7864
|
-
import { z as
|
|
7865
|
-
var ImageSearchParams =
|
|
7866
|
-
prompt:
|
|
7867
|
-
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)
|
|
7868
7986
|
}).strict();
|
|
7869
7987
|
var imageSearchNode = delegated({
|
|
7870
7988
|
id: "image_search",
|
|
@@ -7872,15 +7990,15 @@ var imageSearchNode = delegated({
|
|
|
7872
7990
|
category: "image",
|
|
7873
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.",
|
|
7874
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.",
|
|
7875
|
-
inputs:
|
|
7993
|
+
inputs: z23.object({}).loose(),
|
|
7876
7994
|
params: ImageSearchParams,
|
|
7877
|
-
outputs:
|
|
7995
|
+
outputs: z23.object({ images: z23.array(ImageRef).min(1) }).strict(),
|
|
7878
7996
|
outputKinds: { images: "image" },
|
|
7879
7997
|
cost: ({ params }) => ({ credits: Math.ceil(2 + params.count / 2), seconds_estimate: 30 })
|
|
7880
7998
|
});
|
|
7881
7999
|
|
|
7882
8000
|
// src/engine/nodes/remote/imageSelect.ts
|
|
7883
|
-
import { z as
|
|
8001
|
+
import { z as z24 } from "zod";
|
|
7884
8002
|
var IMAGE_SELECT_MODELS = ["~google/gemini-flash-latest", "~google/gemini-pro-latest"];
|
|
7885
8003
|
var imageSelectNode = delegated({
|
|
7886
8004
|
id: "image_select",
|
|
@@ -7888,15 +8006,15 @@ var imageSelectNode = delegated({
|
|
|
7888
8006
|
category: "vision",
|
|
7889
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.",
|
|
7890
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.",
|
|
7891
|
-
inputs:
|
|
7892
|
-
params:
|
|
7893
|
-
model:
|
|
7894
|
-
prompt:
|
|
7895
|
-
count:
|
|
7896
|
-
temperature:
|
|
7897
|
-
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()
|
|
7898
8016
|
}).strict(),
|
|
7899
|
-
outputs:
|
|
8017
|
+
outputs: z24.object({ images: z24.array(ImageRef).min(1), reasoning: TextRef }).strict(),
|
|
7900
8018
|
outputKinds: { images: "image", reasoning: "text" },
|
|
7901
8019
|
cost: () => ({ credits: 1, seconds_estimate: 5 }),
|
|
7902
8020
|
// Arity is only knowable at validate time when `images` is a literal array
|
|
@@ -7921,34 +8039,34 @@ var imageSelectNode = delegated({
|
|
|
7921
8039
|
});
|
|
7922
8040
|
|
|
7923
8041
|
// src/engine/nodes/remote/music.ts
|
|
7924
|
-
import { z as
|
|
8042
|
+
import { z as z25 } from "zod";
|
|
7925
8043
|
var MUSIC_MODELS = ["elevenlabs/music-v1", "elevenlabs/video-background-music-v1"];
|
|
7926
|
-
var MusicParams =
|
|
7927
|
-
model:
|
|
8044
|
+
var MusicParams = z25.object({
|
|
8045
|
+
model: z25.enum(MUSIC_MODELS),
|
|
7928
8046
|
/** Free-form prompt. Used by `elevenlabs/music-v1` (compose-detailed). */
|
|
7929
|
-
prompt:
|
|
8047
|
+
prompt: z25.string().optional(),
|
|
7930
8048
|
/**
|
|
7931
8049
|
* Structured composition plan (intro / hook / verse / outro sections with
|
|
7932
8050
|
* per-section styles + durations). Mutually exclusive with `prompt`.
|
|
7933
8051
|
*/
|
|
7934
|
-
composition_plan:
|
|
8052
|
+
composition_plan: z25.record(z25.string(), z25.unknown()).optional(),
|
|
7935
8053
|
/** Target length when using `prompt`. 3000–454545ms (capped by the $10 per-node cost limit). */
|
|
7936
|
-
music_length_ms:
|
|
7937
|
-
seed:
|
|
8054
|
+
music_length_ms: z25.number().int().min(3e3).max(ELEVENLABS_MAX_MUSIC_LENGTH_MS).optional(),
|
|
8055
|
+
seed: z25.number().int().optional(),
|
|
7938
8056
|
/** Prompt mode only — forces an instrumental (no vocals) track. */
|
|
7939
|
-
force_instrumental:
|
|
8057
|
+
force_instrumental: z25.boolean().optional(),
|
|
7940
8058
|
/** composition_plan only — honor exact section durations. */
|
|
7941
|
-
respect_sections_durations:
|
|
8059
|
+
respect_sections_durations: z25.boolean().optional(),
|
|
7942
8060
|
/** Emit word-level timestamps alongside the audio. */
|
|
7943
|
-
with_timestamps:
|
|
8061
|
+
with_timestamps: z25.boolean().optional(),
|
|
7944
8062
|
/**
|
|
7945
8063
|
* video-to-music only — short description of the desired score
|
|
7946
8064
|
* ("upbeat synth, fast cuts, 80s") used to bias the model.
|
|
7947
8065
|
*/
|
|
7948
|
-
description:
|
|
8066
|
+
description: z25.string().max(1e3).optional(),
|
|
7949
8067
|
/** video-to-music only — up to 10 style tags. */
|
|
7950
|
-
tags:
|
|
7951
|
-
output_format:
|
|
8068
|
+
tags: z25.array(z25.string()).max(10).optional(),
|
|
8069
|
+
output_format: z25.enum(ELEVENLABS_OUTPUT_FORMATS).optional()
|
|
7952
8070
|
}).strict();
|
|
7953
8071
|
var musicNode = delegated({
|
|
7954
8072
|
id: "music",
|
|
@@ -7956,9 +8074,9 @@ var musicNode = delegated({
|
|
|
7956
8074
|
category: "audio",
|
|
7957
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`.",
|
|
7958
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.",
|
|
7959
|
-
inputs:
|
|
8077
|
+
inputs: z25.object({ video: VideoRef.optional() }).loose(),
|
|
7960
8078
|
params: MusicParams,
|
|
7961
|
-
outputs:
|
|
8079
|
+
outputs: z25.object({ audio: AudioRef, timestamps: JsonRef.optional() }).strict(),
|
|
7962
8080
|
outputKinds: { audio: "audio", timestamps: "json" },
|
|
7963
8081
|
cost: ({ params }) => {
|
|
7964
8082
|
const seconds = params.music_length_ms ? Math.ceil(params.music_length_ms / 1e3) : 30;
|
|
@@ -7989,25 +8107,25 @@ var musicNode = delegated({
|
|
|
7989
8107
|
});
|
|
7990
8108
|
|
|
7991
8109
|
// src/engine/nodes/remote/soundEffect.ts
|
|
7992
|
-
import { z as
|
|
8110
|
+
import { z as z26 } from "zod";
|
|
7993
8111
|
var SOUND_EFFECT_MODELS = ["elevenlabs/eleven_text_to_sound_v2"];
|
|
7994
|
-
var SoundEffectParams =
|
|
7995
|
-
model:
|
|
8112
|
+
var SoundEffectParams = z26.object({
|
|
8113
|
+
model: z26.enum(SOUND_EFFECT_MODELS),
|
|
7996
8114
|
/** Prompt describing the SFX ("metal door slam", "soft UI tap", "ocean waves"). */
|
|
7997
|
-
text:
|
|
8115
|
+
text: z26.string().min(1),
|
|
7998
8116
|
/**
|
|
7999
8117
|
* Target length in seconds. 0.5–30. Leave unset to let the model pick the
|
|
8000
8118
|
* natural length for the described effect.
|
|
8001
8119
|
*/
|
|
8002
|
-
duration_seconds:
|
|
8120
|
+
duration_seconds: z26.number().min(0.5).max(30).optional(),
|
|
8003
8121
|
/**
|
|
8004
8122
|
* 0–1. Higher = stick closer to the prompt at the cost of variety; lower
|
|
8005
8123
|
* = let the model interpret more freely. Defaults to 0.3 on the provider.
|
|
8006
8124
|
*/
|
|
8007
|
-
prompt_influence:
|
|
8125
|
+
prompt_influence: z26.number().min(0).max(1).optional(),
|
|
8008
8126
|
/** Only valid on `eleven_text_to_sound_v2` — produce a seamless loop. */
|
|
8009
|
-
loop:
|
|
8010
|
-
output_format:
|
|
8127
|
+
loop: z26.boolean().optional(),
|
|
8128
|
+
output_format: z26.enum(ELEVENLABS_OUTPUT_FORMATS).optional()
|
|
8011
8129
|
}).strict();
|
|
8012
8130
|
var soundEffectNode = delegated({
|
|
8013
8131
|
id: "sound_effect",
|
|
@@ -8015,9 +8133,9 @@ var soundEffectNode = delegated({
|
|
|
8015
8133
|
category: "audio",
|
|
8016
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.",
|
|
8017
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.",
|
|
8018
|
-
inputs:
|
|
8136
|
+
inputs: z26.object({}).loose(),
|
|
8019
8137
|
params: SoundEffectParams,
|
|
8020
|
-
outputs:
|
|
8138
|
+
outputs: z26.object({ audio: AudioRef }).strict(),
|
|
8021
8139
|
outputKinds: { audio: "audio" },
|
|
8022
8140
|
cost: ({ params }) => {
|
|
8023
8141
|
const seconds = params.duration_seconds ?? 5;
|
|
@@ -8026,7 +8144,7 @@ var soundEffectNode = delegated({
|
|
|
8026
8144
|
});
|
|
8027
8145
|
|
|
8028
8146
|
// src/engine/nodes/remote/textGenerate.ts
|
|
8029
|
-
import { z as
|
|
8147
|
+
import { z as z27 } from "zod";
|
|
8030
8148
|
var TEXT_GENERATE_MODELS = ["~google/gemini-flash-latest", "~google/gemini-pro-latest"];
|
|
8031
8149
|
var textGenerateNode = delegated({
|
|
8032
8150
|
id: "text_generate",
|
|
@@ -8034,58 +8152,58 @@ var textGenerateNode = delegated({
|
|
|
8034
8152
|
category: "language",
|
|
8035
8153
|
summary: "Single-turn LLM text generation via OpenRouter. Returns a text response.",
|
|
8036
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.',
|
|
8037
|
-
inputs:
|
|
8038
|
-
params:
|
|
8039
|
-
model:
|
|
8040
|
-
prompt:
|
|
8041
|
-
system:
|
|
8042
|
-
response_format:
|
|
8043
|
-
web_search:
|
|
8044
|
-
temperature:
|
|
8045
|
-
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()
|
|
8046
8164
|
}).strict(),
|
|
8047
|
-
outputs:
|
|
8165
|
+
outputs: z27.object({ text: TextRef }).strict(),
|
|
8048
8166
|
outputKinds: { text: "text" },
|
|
8049
8167
|
cost: () => ({ credits: 1, seconds_estimate: 3 })
|
|
8050
8168
|
});
|
|
8051
8169
|
|
|
8052
8170
|
// src/engine/nodes/remote/tts.ts
|
|
8053
|
-
import { z as
|
|
8171
|
+
import { z as z28 } from "zod";
|
|
8054
8172
|
var TTS_MODELS = ["elevenlabs/eleven_v3"];
|
|
8055
|
-
var TtsVoiceSettings =
|
|
8056
|
-
stability:
|
|
8057
|
-
similarity_boost:
|
|
8058
|
-
style:
|
|
8059
|
-
use_speaker_boost:
|
|
8060
|
-
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()
|
|
8061
8179
|
}).strict();
|
|
8062
|
-
var TtsPronunciationLocator =
|
|
8063
|
-
pronunciation_dictionary_id:
|
|
8064
|
-
version_id:
|
|
8180
|
+
var TtsPronunciationLocator = z28.object({
|
|
8181
|
+
pronunciation_dictionary_id: z28.string().min(1),
|
|
8182
|
+
version_id: z28.string().nullable().optional()
|
|
8065
8183
|
}).strict();
|
|
8066
|
-
var TtsParams =
|
|
8067
|
-
model:
|
|
8068
|
-
text:
|
|
8069
|
-
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),
|
|
8070
8188
|
/** Provider output_format (mp3 family only — assets are stored as audio/mpeg). */
|
|
8071
|
-
output_format:
|
|
8072
|
-
seed:
|
|
8189
|
+
output_format: z28.enum(ELEVENLABS_OUTPUT_FORMATS).optional(),
|
|
8190
|
+
seed: z28.number().int().min(0).max(4294967295).optional(),
|
|
8073
8191
|
// Top-level shortcuts; structured form is `voice_settings`.
|
|
8074
|
-
stability:
|
|
8075
|
-
similarity_boost:
|
|
8192
|
+
stability: z28.number().min(0).max(1).optional(),
|
|
8193
|
+
similarity_boost: z28.number().min(0).max(1).optional(),
|
|
8076
8194
|
voice_settings: TtsVoiceSettings.optional(),
|
|
8077
8195
|
/** ISO 639-1 language code. eleven_v3 supports language hints. */
|
|
8078
|
-
language_code:
|
|
8079
|
-
pronunciation_dictionary_locators:
|
|
8080
|
-
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(),
|
|
8081
8199
|
/** Currently Japanese-only. Adds latency. */
|
|
8082
|
-
apply_language_text_normalization:
|
|
8200
|
+
apply_language_text_normalization: z28.boolean().optional(),
|
|
8083
8201
|
/**
|
|
8084
8202
|
* When true, hits `/v1/text-to-speech/{voice_id}/with-timestamps` and
|
|
8085
8203
|
* adds a `timestamps` output (character-level alignment) for caption
|
|
8086
8204
|
* rendering, lipsync, and beat-matched cuts.
|
|
8087
8205
|
*/
|
|
8088
|
-
with_timestamps:
|
|
8206
|
+
with_timestamps: z28.boolean().optional()
|
|
8089
8207
|
}).strict();
|
|
8090
8208
|
var ttsNode = delegated({
|
|
8091
8209
|
id: "tts",
|
|
@@ -8093,9 +8211,9 @@ var ttsNode = delegated({
|
|
|
8093
8211
|
category: "audio",
|
|
8094
8212
|
summary: "Single-voice text-to-speech via ElevenLabs Eleven v3. Optional character-level timestamps for caption rendering and beat-matched cuts.",
|
|
8095
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).",
|
|
8096
|
-
inputs:
|
|
8214
|
+
inputs: z28.object({}).loose(),
|
|
8097
8215
|
params: TtsParams,
|
|
8098
|
-
outputs:
|
|
8216
|
+
outputs: z28.object({ audio: AudioRef, timestamps: JsonRef.optional() }).strict(),
|
|
8099
8217
|
outputKinds: { audio: "audio", timestamps: "json" },
|
|
8100
8218
|
cost: ({ params }) => ({
|
|
8101
8219
|
credits: Math.max(1, Math.ceil(params.text.length * 15e-4)),
|
|
@@ -8104,25 +8222,25 @@ var ttsNode = delegated({
|
|
|
8104
8222
|
});
|
|
8105
8223
|
|
|
8106
8224
|
// src/engine/nodes/remote/video.ts
|
|
8107
|
-
import { z as
|
|
8108
|
-
var videoModelEnum =
|
|
8109
|
-
var VideoGenerateParams =
|
|
8225
|
+
import { z as z29 } from "zod";
|
|
8226
|
+
var videoModelEnum = z29.enum(VIDEO_GENERATE_MODELS);
|
|
8227
|
+
var VideoGenerateParams = z29.object({
|
|
8110
8228
|
model: videoModelEnum,
|
|
8111
|
-
prompt:
|
|
8112
|
-
duration:
|
|
8113
|
-
resolution:
|
|
8229
|
+
prompt: z29.string().min(1),
|
|
8230
|
+
duration: z29.number().int().positive().optional(),
|
|
8231
|
+
resolution: z29.string().optional(),
|
|
8114
8232
|
// Union of ratios accepted by at least one curated model (registry gates
|
|
8115
8233
|
// per-model). 3:2/2:3 are deliberately absent: no registered model takes them.
|
|
8116
|
-
aspect_ratio:
|
|
8117
|
-
generate_audio:
|
|
8118
|
-
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(),
|
|
8119
8237
|
// Veo-only passthroughs (routed via `provider.options.google-vertex.parameters`).
|
|
8120
|
-
negative_prompt:
|
|
8121
|
-
person_generation:
|
|
8122
|
-
enhance_prompt:
|
|
8123
|
-
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(),
|
|
8124
8242
|
// Kling-only passthrough (prompt-adherence dial, sent top-level).
|
|
8125
|
-
cfg_scale:
|
|
8243
|
+
cfg_scale: z29.number().optional()
|
|
8126
8244
|
}).strict();
|
|
8127
8245
|
var videoGenerateNode = delegated({
|
|
8128
8246
|
id: "video_generate",
|
|
@@ -8130,7 +8248,7 @@ var videoGenerateNode = delegated({
|
|
|
8130
8248
|
category: "video",
|
|
8131
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.",
|
|
8132
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.",
|
|
8133
|
-
inputs:
|
|
8251
|
+
inputs: z29.object({
|
|
8134
8252
|
first_frame: ImageRef.optional(),
|
|
8135
8253
|
last_frame: ImageRef.optional(),
|
|
8136
8254
|
reference: ImageRef.optional(),
|
|
@@ -8140,10 +8258,10 @@ var videoGenerateNode = delegated({
|
|
|
8140
8258
|
* exclusive with `first_frame` — OpenRouter treats a request carrying both
|
|
8141
8259
|
* as image-to-video and ignores these, so wire one or the other.
|
|
8142
8260
|
*/
|
|
8143
|
-
references:
|
|
8261
|
+
references: z29.array(ImageRef).optional()
|
|
8144
8262
|
}).loose(),
|
|
8145
8263
|
params: VideoGenerateParams,
|
|
8146
|
-
outputs:
|
|
8264
|
+
outputs: z29.object({ video: VideoRef }).strict(),
|
|
8147
8265
|
outputKinds: { video: "video" },
|
|
8148
8266
|
// Priced from the shared contract, so `validate`'s quote and the charge the
|
|
8149
8267
|
// backend applies come from ONE table. A flat number here under-quoted a
|
|
@@ -8163,11 +8281,11 @@ var videoGenerateNode = delegated({
|
|
|
8163
8281
|
});
|
|
8164
8282
|
|
|
8165
8283
|
// src/engine/nodes/remote/videoBackgroundRemove.ts
|
|
8166
|
-
import { z as
|
|
8167
|
-
var VideoBackgroundRemoveParams =
|
|
8168
|
-
model:
|
|
8169
|
-
edge_refinement:
|
|
8170
|
-
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")
|
|
8171
8289
|
}).strict();
|
|
8172
8290
|
var videoBackgroundRemoveNode = delegated({
|
|
8173
8291
|
id: "video_background_remove",
|
|
@@ -8175,18 +8293,18 @@ var videoBackgroundRemoveNode = delegated({
|
|
|
8175
8293
|
category: "video",
|
|
8176
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`.",
|
|
8177
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.",
|
|
8178
|
-
inputs:
|
|
8296
|
+
inputs: z30.object({
|
|
8179
8297
|
video: VideoRef
|
|
8180
8298
|
}).strict(),
|
|
8181
8299
|
params: VideoBackgroundRemoveParams,
|
|
8182
|
-
outputs:
|
|
8300
|
+
outputs: z30.object({ video: VideoRef }).strict(),
|
|
8183
8301
|
outputKinds: { video: "video" },
|
|
8184
8302
|
// $0.012 per 30 frames (edge refinement on) — assume ~30fps; refine via fal dashboard.
|
|
8185
8303
|
cost: () => ({ credits: 50, seconds_estimate: 60 })
|
|
8186
8304
|
});
|
|
8187
8305
|
|
|
8188
8306
|
// src/engine/nodes/remote/videoDeconstruct.ts
|
|
8189
|
-
import { z as
|
|
8307
|
+
import { z as z31 } from "zod";
|
|
8190
8308
|
var VIDEO_DECONSTRUCT_MODELS = ["~google/gemini-flash-latest", "~google/gemini-pro-latest"];
|
|
8191
8309
|
var videoDeconstructNode = delegated({
|
|
8192
8310
|
id: "video_deconstruct",
|
|
@@ -8194,34 +8312,34 @@ var videoDeconstructNode = delegated({
|
|
|
8194
8312
|
category: "video",
|
|
8195
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).',
|
|
8196
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.',
|
|
8197
|
-
inputs:
|
|
8198
|
-
params:
|
|
8199
|
-
model:
|
|
8200
|
-
mode:
|
|
8201
|
-
language:
|
|
8202
|
-
max_scenes:
|
|
8203
|
-
focus:
|
|
8204
|
-
start_s:
|
|
8205
|
-
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(),
|
|
8206
8324
|
// Real visual shot-cut timestamps (absolute seconds), detected locally with
|
|
8207
8325
|
// ffmpeg before the deconstruct. The backend SNAPS its LLM scene boundaries
|
|
8208
8326
|
// onto these and SPLITS any scene that spans one, so a scene's frames never
|
|
8209
8327
|
// straddle a hard cut. `scaffold-video` populates this; omit for LLM-only cuts.
|
|
8210
|
-
shot_cuts:
|
|
8328
|
+
shot_cuts: z31.array(z31.number().min(0)).max(200).optional(),
|
|
8211
8329
|
// The video model's per-clip ceiling (seconds). A shot longer than this is
|
|
8212
8330
|
// split into seamless continuation sub-scenes (shared splice frame), so long
|
|
8213
8331
|
// shots reproduce in full instead of being truncated. `scaffold-video` sets
|
|
8214
8332
|
// the Seedance ceiling (15); omit to disable length splitting.
|
|
8215
|
-
max_clip_s:
|
|
8333
|
+
max_clip_s: z31.number().positive().max(60).optional(),
|
|
8216
8334
|
// Transcript provider for the blueprint's dialogue/transcript. Default
|
|
8217
8335
|
// Groq Whisper; "deepgram" routes to Nova-3 so words carry punctuation.
|
|
8218
|
-
transcriber:
|
|
8336
|
+
transcriber: z31.enum(["groq", "deepgram"]).optional()
|
|
8219
8337
|
}).strict(),
|
|
8220
|
-
outputs:
|
|
8338
|
+
outputs: z31.object({
|
|
8221
8339
|
analysis: JsonRef,
|
|
8222
8340
|
// Absent in mode:"index" (structure only, no Mux frame extraction).
|
|
8223
|
-
start_frames:
|
|
8224
|
-
end_frames:
|
|
8341
|
+
start_frames: z31.array(ImageRef).min(1).optional(),
|
|
8342
|
+
end_frames: z31.array(ImageRef).min(1).optional(),
|
|
8225
8343
|
transcript: JsonRef
|
|
8226
8344
|
}).strict(),
|
|
8227
8345
|
outputKinds: { analysis: "json", start_frames: "image", end_frames: "image", transcript: "json" },
|
|
@@ -8229,22 +8347,22 @@ var videoDeconstructNode = delegated({
|
|
|
8229
8347
|
});
|
|
8230
8348
|
|
|
8231
8349
|
// src/engine/nodes/remote/videoLipsync.ts
|
|
8232
|
-
import { z as
|
|
8233
|
-
var FalLipsyncParams =
|
|
8234
|
-
model:
|
|
8350
|
+
import { z as z32 } from "zod";
|
|
8351
|
+
var FalLipsyncParams = z32.object({
|
|
8352
|
+
model: z32.literal("fal/veed-lipsync")
|
|
8235
8353
|
}).strict();
|
|
8236
|
-
var VideoLipsyncParams =
|
|
8354
|
+
var VideoLipsyncParams = z32.discriminatedUnion("model", [FalLipsyncParams]);
|
|
8237
8355
|
var videoLipsyncNode = delegated({
|
|
8238
8356
|
id: "video_lipsync",
|
|
8239
8357
|
version: "1.0.0",
|
|
8240
8358
|
category: "video",
|
|
8241
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.",
|
|
8242
|
-
inputs:
|
|
8360
|
+
inputs: z32.object({
|
|
8243
8361
|
video: VideoRef,
|
|
8244
8362
|
audio: AudioRef
|
|
8245
8363
|
}).strict(),
|
|
8246
8364
|
params: VideoLipsyncParams,
|
|
8247
|
-
outputs:
|
|
8365
|
+
outputs: z32.object({ video: VideoRef }).strict(),
|
|
8248
8366
|
outputKinds: { video: "video" },
|
|
8249
8367
|
cost: () => ({ credits: 20, seconds_estimate: 120 })
|
|
8250
8368
|
});
|
|
@@ -8253,7 +8371,7 @@ var videoLipsyncNode = delegated({
|
|
|
8253
8371
|
import { mkdtemp as mkdtemp6, readFile as readFile11, rm as rm6 } from "fs/promises";
|
|
8254
8372
|
import { tmpdir as tmpdir6 } from "os";
|
|
8255
8373
|
import path14 from "path";
|
|
8256
|
-
import { z as
|
|
8374
|
+
import { z as z33 } from "zod";
|
|
8257
8375
|
|
|
8258
8376
|
// src/engine/nodes/local/lib/ffmpeg.ts
|
|
8259
8377
|
import { execFile as execFile7 } from "child_process";
|
|
@@ -8332,24 +8450,24 @@ ${detail.slice(-4e3)}`);
|
|
|
8332
8450
|
}
|
|
8333
8451
|
|
|
8334
8452
|
// src/engine/nodes/remote/videoTranscribe.ts
|
|
8335
|
-
var VideoTranscribeParams =
|
|
8336
|
-
language:
|
|
8453
|
+
var VideoTranscribeParams = z33.object({
|
|
8454
|
+
language: z33.string().min(2).max(8).optional(),
|
|
8337
8455
|
// Provider choice is explicit (no env-based silent branching). Default Groq
|
|
8338
8456
|
// Whisper; "deepgram" routes to Deepgram Nova-3, which additionally emits a
|
|
8339
8457
|
// `rich` JSON output with punctuated words + paragraph/sentence grouping.
|
|
8340
|
-
transcriber:
|
|
8458
|
+
transcriber: z33.enum(["groq", "deepgram"]).optional()
|
|
8341
8459
|
}).strict();
|
|
8342
|
-
var VideoTranscribeInputs =
|
|
8460
|
+
var VideoTranscribeInputs = z33.object({
|
|
8343
8461
|
// A video (audio auto-extracted locally) OR a bare audio track. The key stays
|
|
8344
8462
|
// `video` for back-compat; the backend already accepts audio-kind refs on it —
|
|
8345
8463
|
// the local extraction path has been shipping one for every video input.
|
|
8346
|
-
video:
|
|
8464
|
+
video: z33.union([VideoRef, AudioRef])
|
|
8347
8465
|
}).strict();
|
|
8348
|
-
var VideoTranscribeOutputs =
|
|
8349
|
-
transcript:
|
|
8466
|
+
var VideoTranscribeOutputs = z33.object({
|
|
8467
|
+
transcript: z33.custom(),
|
|
8350
8468
|
// Only emitted by the Deepgram path: full punctuated words + paragraph /
|
|
8351
8469
|
// sentence grouping with speaker indices. Absent for the default Groq path.
|
|
8352
|
-
rich:
|
|
8470
|
+
rich: z33.custom().optional()
|
|
8353
8471
|
}).strict();
|
|
8354
8472
|
var AUDIO_EXTRACT_TIMEOUT_MS = 6e4;
|
|
8355
8473
|
var videoTranscribeNode = defineNode({
|
|
@@ -8434,29 +8552,29 @@ async function tryExtractAudio(inputs, ctx) {
|
|
|
8434
8552
|
}
|
|
8435
8553
|
|
|
8436
8554
|
// src/engine/nodes/remote/voiceSelect.ts
|
|
8437
|
-
import { z as
|
|
8555
|
+
import { z as z34 } from "zod";
|
|
8438
8556
|
var voiceSelectNode = delegated({
|
|
8439
8557
|
id: "voice_select",
|
|
8440
8558
|
version: "1.0.0",
|
|
8441
8559
|
category: "audio",
|
|
8442
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.',
|
|
8443
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.',
|
|
8444
|
-
inputs:
|
|
8445
|
-
params:
|
|
8446
|
-
description:
|
|
8447
|
-
gender:
|
|
8448
|
-
age:
|
|
8449
|
-
accent:
|
|
8450
|
-
language:
|
|
8451
|
-
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()
|
|
8452
8570
|
}).strict(),
|
|
8453
|
-
outputs:
|
|
8571
|
+
outputs: z34.object({ voice_id: TextRef, candidates: JsonRef }).strict(),
|
|
8454
8572
|
outputKinds: { voice_id: "text", candidates: "json" },
|
|
8455
8573
|
cost: () => ({ credits: 0, seconds_estimate: 5 })
|
|
8456
8574
|
});
|
|
8457
8575
|
|
|
8458
8576
|
// src/engine/schema/catalog.ts
|
|
8459
|
-
import { z as
|
|
8577
|
+
import { z as z35 } from "zod";
|
|
8460
8578
|
function generateCatalog(registry, opts = {}) {
|
|
8461
8579
|
const entries = registry.all().map((def) => {
|
|
8462
8580
|
const cost = def.cost ? safeCost(def) : void 0;
|
|
@@ -8467,9 +8585,9 @@ function generateCatalog(registry, opts = {}) {
|
|
|
8467
8585
|
summary: def.summary,
|
|
8468
8586
|
when_to_use: def.when_to_use,
|
|
8469
8587
|
location: def.location,
|
|
8470
|
-
inputs:
|
|
8471
|
-
params:
|
|
8472
|
-
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" }),
|
|
8473
8591
|
cost_estimate_credits: cost?.credits,
|
|
8474
8592
|
runtime_estimate_seconds: cost?.seconds_estimate
|
|
8475
8593
|
};
|
|
@@ -8567,7 +8685,8 @@ var REMOTE_NODES = [
|
|
|
8567
8685
|
videoBackgroundRemoveNode,
|
|
8568
8686
|
videoDeconstructNode,
|
|
8569
8687
|
voiceSelectNode,
|
|
8570
|
-
imageBackgroundRemoveNode
|
|
8688
|
+
imageBackgroundRemoveNode,
|
|
8689
|
+
imageLayerizeNode
|
|
8571
8690
|
];
|
|
8572
8691
|
function defaultRegistry() {
|
|
8573
8692
|
const r = new NodeRegistry();
|
|
@@ -8634,6 +8753,7 @@ export {
|
|
|
8634
8753
|
REF_PREFIX,
|
|
8635
8754
|
parseRefExpr,
|
|
8636
8755
|
sha256Hex,
|
|
8756
|
+
MAX_ASSET_BYTES,
|
|
8637
8757
|
looksLikeHttpUrl,
|
|
8638
8758
|
SEEDANCE_PROFILE,
|
|
8639
8759
|
clipProfileFor,
|
|
@@ -8642,6 +8762,7 @@ export {
|
|
|
8642
8762
|
spineInputFlags,
|
|
8643
8763
|
spineInputOps,
|
|
8644
8764
|
elementMentionKeywords,
|
|
8765
|
+
MAX_REMOTE_IMAGE_BYTES,
|
|
8645
8766
|
fetchExternalBytes,
|
|
8646
8767
|
toModelSafeImage,
|
|
8647
8768
|
BackendClient2,
|
|
@@ -8654,4 +8775,4 @@ export {
|
|
|
8654
8775
|
defaultRegistry,
|
|
8655
8776
|
createEngineFromEnv
|
|
8656
8777
|
};
|
|
8657
|
-
//# sourceMappingURL=chunk-
|
|
8778
|
+
//# sourceMappingURL=chunk-5MPIOGRO.js.map
|