@koda-sl/baker-cli 0.300.0-dev.e0610b71d → 0.300.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 +68 -87
- package/canvas/tiktok-captions-composition/index.html +2 -23
- package/dist/{chunk-2IUN3S3E.js → chunk-2IB3IL4K.js} +2 -2
- package/dist/{chunk-4YF56EGK.js → chunk-D3HHUBIL.js} +4 -4
- package/dist/{chunk-WFWU3CHS.js → chunk-DZUVUGEP.js} +1 -9
- package/dist/{chunk-WFWU3CHS.js.map → chunk-DZUVUGEP.js.map} +1 -1
- package/dist/{chunk-A2VZOTCV.js → chunk-EX2OIOVL.js} +3 -3
- package/dist/{chunk-KDTHRRAC.js → chunk-GU7IWEBC.js} +3 -3
- package/dist/{chunk-FENRH7OF.js → chunk-YFQWCJX4.js} +123 -412
- package/dist/chunk-YFQWCJX4.js.map +1 -0
- package/dist/cli.js +1725 -2854
- package/dist/cli.js.map +1 -1
- package/dist/{client-6KQHCXS2.js → client-VDCCDEHE.js} +4 -4
- package/dist/engine/index.d.ts +0 -13
- package/dist/engine/index.js +3 -3
- package/dist/{env-C7YXL6C6.js → env-FWMZXMQK.js} +2 -6
- package/dist/{output-RBM32FKJ.js → output-4H2RNBXL.js} +5 -5
- package/dist/{shared-WJIJTWST.js → shared-UKL36AXT.js} +6 -6
- package/package.json +2 -5
- package/dist/chunk-FENRH7OF.js.map +0 -1
- /package/dist/{chunk-2IUN3S3E.js.map → chunk-2IB3IL4K.js.map} +0 -0
- /package/dist/{chunk-4YF56EGK.js.map → chunk-D3HHUBIL.js.map} +0 -0
- /package/dist/{chunk-A2VZOTCV.js.map → chunk-EX2OIOVL.js.map} +0 -0
- /package/dist/{chunk-KDTHRRAC.js.map → chunk-GU7IWEBC.js.map} +0 -0
- /package/dist/{client-6KQHCXS2.js.map → client-VDCCDEHE.js.map} +0 -0
- /package/dist/{env-C7YXL6C6.js.map → env-FWMZXMQK.js.map} +0 -0
- /package/dist/{output-RBM32FKJ.js.map → output-4H2RNBXL.js.map} +0 -0
- /package/dist/{shared-WJIJTWST.js.map → shared-UKL36AXT.js.map} +0 -0
|
@@ -1,25 +1,24 @@
|
|
|
1
1
|
import {
|
|
2
2
|
debugLogHttp,
|
|
3
3
|
readBodyForLog
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-2IB3IL4K.js";
|
|
5
5
|
import {
|
|
6
6
|
DIRECT_ROUTE,
|
|
7
7
|
__commonJS,
|
|
8
8
|
__toESM,
|
|
9
9
|
captureProxyCredentials,
|
|
10
10
|
childEnvWith,
|
|
11
|
-
hasFrameVisionKey,
|
|
12
11
|
isChallengeBody,
|
|
13
12
|
isPrivateAddress,
|
|
14
13
|
isProxyFailure,
|
|
15
14
|
plannedRoutes,
|
|
16
15
|
refuseNonPublicUrl,
|
|
17
16
|
shouldEscalate
|
|
18
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-DZUVUGEP.js";
|
|
19
18
|
|
|
20
|
-
//
|
|
19
|
+
// ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/index.js
|
|
21
20
|
var require_safe_stable_stringify = __commonJS({
|
|
22
|
-
"
|
|
21
|
+
"../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/index.js"(exports, module) {
|
|
23
22
|
"use strict";
|
|
24
23
|
var { hasOwnProperty } = Object.prototype;
|
|
25
24
|
var stringify = configure2();
|
|
@@ -758,6 +757,19 @@ async function parseErrorBody(res) {
|
|
|
758
757
|
}
|
|
759
758
|
return classifyHttpError(res.status, errObj, message2);
|
|
760
759
|
}
|
|
760
|
+
function providerVerdict(status, errObj, message2) {
|
|
761
|
+
if (errObj.provider === void 0 && errObj.code !== "provider_error") return null;
|
|
762
|
+
return {
|
|
763
|
+
kind: "provider",
|
|
764
|
+
status,
|
|
765
|
+
provider: errObj.provider,
|
|
766
|
+
code: errObj.code ?? "provider_error",
|
|
767
|
+
message: message2,
|
|
768
|
+
// A 4xx is terminal unless the backend says otherwise — the opposite
|
|
769
|
+
// default from the 502 branch, where the status itself means "try again".
|
|
770
|
+
retryable: errObj.retryable ?? false
|
|
771
|
+
};
|
|
772
|
+
}
|
|
761
773
|
function classifyHttpError(status, errObj, message2) {
|
|
762
774
|
if (errObj.code === CONTENT_POLICY_CODE) {
|
|
763
775
|
return { kind: "content_policy", status, provider: errObj.provider, message: message2 };
|
|
@@ -766,7 +778,7 @@ function classifyHttpError(status, errObj, message2) {
|
|
|
766
778
|
return { kind: "unauthorized", status, message: message2 };
|
|
767
779
|
}
|
|
768
780
|
if (status === 400 || status === 422) {
|
|
769
|
-
return { kind: "validation", status, message: message2, details: errObj.details };
|
|
781
|
+
return providerVerdict(status, errObj, message2) ?? { kind: "validation", status, message: message2, details: errObj.details };
|
|
770
782
|
}
|
|
771
783
|
if (status === 502 || status === 504) {
|
|
772
784
|
if (errObj.code === "provider_timeout" || status === 504) {
|
|
@@ -1034,17 +1046,12 @@ function failureDetail(reason) {
|
|
|
1034
1046
|
}
|
|
1035
1047
|
return { message: describeFailureReason(reason) };
|
|
1036
1048
|
}
|
|
1037
|
-
function retryVerdict(retryable) {
|
|
1038
|
-
if (retryable === true) return "transient \u2014 retry the run";
|
|
1039
|
-
if (retryable === false) return "not retryable \u2014 retrying will not help, fix the input";
|
|
1040
|
-
return "retryability unknown";
|
|
1041
|
-
}
|
|
1042
1049
|
function describeCause(c) {
|
|
1043
1050
|
switch (c.kind) {
|
|
1044
1051
|
case "local":
|
|
1045
1052
|
return c.cause instanceof Error ? c.cause.message : String(c.cause);
|
|
1046
1053
|
case "remote":
|
|
1047
|
-
return `[${c.code}] ${c.message}${c.provider ? ` (${c.provider})` : ""}
|
|
1054
|
+
return `[${c.code}] ${c.message}${c.provider ? ` (${c.provider})` : ""}`;
|
|
1048
1055
|
case "timeout":
|
|
1049
1056
|
return `timeout${c.provider ? ` (${c.provider})` : ""}`;
|
|
1050
1057
|
case "network":
|
|
@@ -1098,7 +1105,7 @@ function resolveAdaptFormats(params) {
|
|
|
1098
1105
|
return params.formats ?? [];
|
|
1099
1106
|
}
|
|
1100
1107
|
|
|
1101
|
-
//
|
|
1108
|
+
// ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/esm/wrapper.js
|
|
1102
1109
|
var import__ = __toESM(require_safe_stable_stringify(), 1);
|
|
1103
1110
|
var configure = import__.default.configure;
|
|
1104
1111
|
var wrapper_default = import__.default;
|
|
@@ -1139,14 +1146,6 @@ function normalizeForCanonical(value) {
|
|
|
1139
1146
|
return void 0;
|
|
1140
1147
|
}
|
|
1141
1148
|
|
|
1142
|
-
// ../canvas-contract/src/frameRealism.ts
|
|
1143
|
-
function frameRealismDirection(opts = {}) {
|
|
1144
|
-
return " Everything obeys real-world physics: paper, card and screens are OPAQUE with nothing showing through from behind, every object is at believable real-world scale next to the people handling it, and every object has its real-world form and construction \u2014 a phone has ONE screen and it is on the front. NO readable text or numbers anywhere in frame \u2014 phone screens, documents and signage stay illegible or out of focus, because any figure the model invents will contradict the script. Hands are kept simple: no close-up of fingers manipulating small parts, no hand gripping the edge of an object, and each person has exactly TWO arms and TWO legs, all attached and all visible or all out of frame. Anyone working does so the way the trade actually does it: nobody stands or kneels on the equipment being installed, nothing is fitted overhanging an edge or floating unsupported, and every part rests on the structure that would really carry it." + // The newest clause, and the one no route had. A testimonial came back with the customer
|
|
1145
|
-
// repairing the panel herself: nothing said she was not the installer, so the model cast
|
|
1146
|
-
// her as one. Who someone IS in the picture has to be stated, or it is guessed.
|
|
1147
|
-
(opts.role ? ` The person on camera is ${opts.role} \u2014 they are shown as that and never doing somebody else's job.` : "") + (opts.currency ? ` If a currency is unavoidably visible it is ${opts.currency}.` : "");
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
1149
|
// ../canvas-contract/src/registry.ts
|
|
1151
1150
|
var OPENROUTER_IMAGE_AR = ["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"];
|
|
1152
1151
|
var OPENROUTER_IMAGE_AR_EXTREME = [...OPENROUTER_IMAGE_AR, "1:4", "4:1", "1:8", "8:1"];
|
|
@@ -1166,7 +1165,6 @@ var OPENROUTER_IMAGE_SIZES = ["0.5K", ...OPENROUTER_IMAGE_SIZE];
|
|
|
1166
1165
|
var OPENROUTER_IMAGE_SIZE_EXTENDED = OPENROUTER_IMAGE_SIZES;
|
|
1167
1166
|
var GEMINI_LITE_IMAGE_SIZE = ["1K"];
|
|
1168
1167
|
var OPENROUTER_IMAGE_QUALITY = ["auto", "low", "medium", "high"];
|
|
1169
|
-
var OPENROUTER_IMAGE_QUALITY_25 = ["auto", "low", "medium", "high", "xhigh", "max"];
|
|
1170
1168
|
var RECRAFT_IMAGE_AR = ["1:1", "4:3", "3:4", "16:9", "9:16"];
|
|
1171
1169
|
var SEEDANCE_DURATIONS = [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
|
|
1172
1170
|
var SEEDANCE_25_DURATIONS = [
|
|
@@ -1211,39 +1209,6 @@ var GEMINI_OMNI_IMAGE_MIMES = ["image/png", "image/jpeg", "image/webp"];
|
|
|
1211
1209
|
var GROK_IMAGINE_DURATIONS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
1212
1210
|
var WAN_27_DURATIONS = [2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
1213
1211
|
var HAPPYHORSE_DURATIONS = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
|
|
1214
|
-
var WAN_30_DURATIONS = [
|
|
1215
|
-
2,
|
|
1216
|
-
3,
|
|
1217
|
-
4,
|
|
1218
|
-
5,
|
|
1219
|
-
6,
|
|
1220
|
-
7,
|
|
1221
|
-
8,
|
|
1222
|
-
9,
|
|
1223
|
-
10,
|
|
1224
|
-
11,
|
|
1225
|
-
12,
|
|
1226
|
-
13,
|
|
1227
|
-
14,
|
|
1228
|
-
15,
|
|
1229
|
-
16,
|
|
1230
|
-
17,
|
|
1231
|
-
18,
|
|
1232
|
-
19,
|
|
1233
|
-
20,
|
|
1234
|
-
21,
|
|
1235
|
-
22,
|
|
1236
|
-
23,
|
|
1237
|
-
24,
|
|
1238
|
-
25,
|
|
1239
|
-
26,
|
|
1240
|
-
27,
|
|
1241
|
-
28,
|
|
1242
|
-
29,
|
|
1243
|
-
30
|
|
1244
|
-
];
|
|
1245
|
-
var GROK_IMAGINE_15_DURATIONS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
|
|
1246
|
-
var RUNWAY_45_DURATIONS = [2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
1247
1212
|
var ELEVENLABS_OUTPUT_FORMATS = [
|
|
1248
1213
|
"mp3_22050_32",
|
|
1249
1214
|
"mp3_44100_32",
|
|
@@ -1262,7 +1227,6 @@ var REPLICATE_VIDEO_MIMES = ["video/mp4", "video/webm", "video/quicktime"];
|
|
|
1262
1227
|
var DECONSTRUCT_VIDEO_MIMES = ["video/mp4", "video/webm", "video/quicktime"];
|
|
1263
1228
|
var REPLICATE_AUDIO_MIMES = ["audio/wav", "audio/mpeg", "audio/mp3"];
|
|
1264
1229
|
var IMAGE_GENERATE_MODELS = [
|
|
1265
|
-
"openai/gpt-image-2.5-sunburst",
|
|
1266
1230
|
"openai/gpt-image-2",
|
|
1267
1231
|
"openai/gpt-5.4-image-2",
|
|
1268
1232
|
"google/gemini-3.1-flash-image-preview",
|
|
@@ -1281,10 +1245,7 @@ var VIDEO_GENERATE_MODELS = [
|
|
|
1281
1245
|
"google/veo-3.1-lite",
|
|
1282
1246
|
"kwaivgi/kling-v3.0-pro",
|
|
1283
1247
|
"x-ai/grok-imagine-video",
|
|
1284
|
-
"x-ai/grok-imagine-video-1.5",
|
|
1285
1248
|
"alibaba/wan-2.7",
|
|
1286
|
-
"alibaba/wan-3.0",
|
|
1287
|
-
"runway/gen-4.5",
|
|
1288
1249
|
"alibaba/happyhorse-1.1"
|
|
1289
1250
|
];
|
|
1290
1251
|
var DEFAULT_VIDEO_GENERATE_MODEL = "google/gemini-omni-1.1-flash";
|
|
@@ -1411,20 +1372,6 @@ var MODEL_REGISTRY = {
|
|
|
1411
1372
|
// images as a side effect. Notable capability gaps vs the Gemini entries:
|
|
1412
1373
|
// no `image_size` (OpenAI picks the pixel dimensions from the ratio, and a
|
|
1413
1374
|
// `resolution` is silently ignored) and the narrower `..._AR_GPT` ratio set.
|
|
1414
|
-
"openai/gpt-image-2.5-sunburst": {
|
|
1415
|
-
// Identical to the gpt-image-2 entry below except for the quality ladder — the
|
|
1416
|
-
// ratio set, the 16-reference ceiling, the missing `image_size` and the reasons
|
|
1417
|
-
// `background` / `output_compression` stay undeclared all carry over unchanged.
|
|
1418
|
-
label: "OpenAI GPT Image 2.5",
|
|
1419
|
-
inputs: [],
|
|
1420
|
-
optional_inputs: [{ kind: "image", mimes: OPENROUTER_IMAGE_MIMES, max: GPT_IMAGE_MAX_REFERENCES }],
|
|
1421
|
-
required: ["prompt"],
|
|
1422
|
-
params: {
|
|
1423
|
-
prompt: { kind: "string", maxLength: GPT_IMAGE_PROMPT_MAX },
|
|
1424
|
-
aspect_ratio: { kind: "string", enum: OPENROUTER_IMAGE_AR_GPT },
|
|
1425
|
-
quality: { kind: "string", enum: OPENROUTER_IMAGE_QUALITY_25 }
|
|
1426
|
-
}
|
|
1427
|
-
},
|
|
1428
1375
|
"openai/gpt-image-2": {
|
|
1429
1376
|
// Its live endpoint also advertises `background` (auto|opaque) and
|
|
1430
1377
|
// `output_compression` (0-100), which we deliberately do not declare. Not an
|
|
@@ -1864,67 +1811,6 @@ var MODEL_REGISTRY = {
|
|
|
1864
1811
|
generate_audio: { kind: "boolean" }
|
|
1865
1812
|
}
|
|
1866
1813
|
},
|
|
1867
|
-
"x-ai/grok-imagine-video-1.5": {
|
|
1868
|
-
// NOT a replacement for the entry above, and deliberately absent from
|
|
1869
|
-
// `AVATAR_VIDEO_MODELS` and `REFERENCE_TO_VIDEO_MODELS`: OpenRouter describes the
|
|
1870
|
-
// older Grok as "text-, image-, and reference-conditioned" and 1.5 as taking "an
|
|
1871
|
-
// optional starting image". Reference conditioning is the whole reason Grok carries
|
|
1872
|
-
// avatars — it is what holds a face from the identity sheet — so swapping them
|
|
1873
|
-
// would quietly cost every avatar clip its likeness.
|
|
1874
|
-
//
|
|
1875
|
-
// What it adds over the older one: 1080p, and 15s rather than 10.
|
|
1876
|
-
label: "Grok Imagine Video 1.5",
|
|
1877
|
-
inputs: [],
|
|
1878
|
-
optional_inputs: [{ kind: "image", mimes: OPENROUTER_IMAGE_MIMES }],
|
|
1879
|
-
required: ["prompt"],
|
|
1880
|
-
params: {
|
|
1881
|
-
prompt: { kind: "string" },
|
|
1882
|
-
aspect_ratio: { kind: "string", enum: ["16:9", "9:16", "1:1", "4:3", "3:4", "3:2", "2:3"] },
|
|
1883
|
-
resolution: { kind: "string", enum: ["480p", "720p", "1080p"] },
|
|
1884
|
-
duration: { kind: "number", enum: GROK_IMAGINE_15_DURATIONS }
|
|
1885
|
-
}
|
|
1886
|
-
},
|
|
1887
|
-
"alibaba/wan-3.0": {
|
|
1888
|
-
// 2.7's successor on every axis that matters: 2–30s against 2–10, 1080p and 480p
|
|
1889
|
-
// as well as 720p, and it keeps the native audio that made 2.7 the landscape
|
|
1890
|
-
// talking-head option.
|
|
1891
|
-
//
|
|
1892
|
-
// The ratios are what it ADVERTISES. 2.7 advertised five and delivered 16:9 for
|
|
1893
|
-
// all of them — measured, twice, which is why its enum here is a single value. That
|
|
1894
|
-
// was a different model and this one is not gated on its predecessor's lie, but
|
|
1895
|
-
// the first vertical clip anyone renders here is worth looking at before a campaign
|
|
1896
|
-
// is built on it.
|
|
1897
|
-
//
|
|
1898
|
-
// No `last_frame`: 2.7 takes one and 3.0 does not, so a canvas that ends a shot on
|
|
1899
|
-
// a chosen frame stays on 2.7.
|
|
1900
|
-
label: "Alibaba Wan 3.0",
|
|
1901
|
-
inputs: [],
|
|
1902
|
-
optional_inputs: [{ kind: "image", mimes: OPENROUTER_IMAGE_MIMES }],
|
|
1903
|
-
required: ["prompt"],
|
|
1904
|
-
params: {
|
|
1905
|
-
prompt: { kind: "string" },
|
|
1906
|
-
aspect_ratio: { kind: "string", enum: ["16:9", "9:16", "1:1", "4:3", "3:4"] },
|
|
1907
|
-
resolution: { kind: "string", enum: ["480p", "720p", "1080p"] },
|
|
1908
|
-
duration: { kind: "number", enum: WAN_30_DURATIONS },
|
|
1909
|
-
seed: { kind: "number" },
|
|
1910
|
-
generate_audio: { kind: "boolean" }
|
|
1911
|
-
}
|
|
1912
|
-
},
|
|
1913
|
-
"runway/gen-4.5": {
|
|
1914
|
-
// One resolution, two shapes, and silent — `generate_audio: false` in its listing,
|
|
1915
|
-
// so a talking head belongs on Wan or Omni. First frame only, no reference.
|
|
1916
|
-
label: "Runway Gen-4.5",
|
|
1917
|
-
inputs: [],
|
|
1918
|
-
optional_inputs: [{ kind: "image", mimes: OPENROUTER_IMAGE_MIMES }],
|
|
1919
|
-
required: ["prompt"],
|
|
1920
|
-
params: {
|
|
1921
|
-
prompt: { kind: "string" },
|
|
1922
|
-
aspect_ratio: { kind: "string", enum: ["16:9", "9:16"] },
|
|
1923
|
-
resolution: { kind: "string", enum: ["720p"] },
|
|
1924
|
-
duration: { kind: "number", enum: RUNWAY_45_DURATIONS },
|
|
1925
|
-
seed: { kind: "number" }
|
|
1926
|
-
}
|
|
1927
|
-
},
|
|
1928
1814
|
"alibaba/happyhorse-1.1": {
|
|
1929
1815
|
// Third reference-capable option: strong identity, correct ratios, no audio.
|
|
1930
1816
|
// First-frame only (no `last_frame`), so it cannot close a beat on a
|
|
@@ -2140,34 +2026,12 @@ function supportsParam(kind, model, param) {
|
|
|
2140
2026
|
return MODEL_REGISTRY[kind]?.[model]?.params[param] !== void 0;
|
|
2141
2027
|
}
|
|
2142
2028
|
var DEFAULT_CLIP_DURATION_S = 5;
|
|
2143
|
-
var SPOKEN_WORDS_PER_SECOND = 2.5;
|
|
2144
|
-
function spokenWordsIn(prompt) {
|
|
2145
|
-
if (!prompt) return 0;
|
|
2146
|
-
const quoted = [
|
|
2147
|
-
...prompt.matchAll(/['"\u201c\u201d\u2018\u2019]([^'"\u201c\u201d\u2018\u2019]{8,})['"\u201c\u201d\u2018\u2019]/g)
|
|
2148
|
-
].map((m) => m[1] ?? "");
|
|
2149
|
-
const text = quoted.join(" ").trim();
|
|
2150
|
-
return text ? text.split(/\s+/).filter(Boolean).length : 0;
|
|
2151
|
-
}
|
|
2152
2029
|
function nearestClipDuration(model, preferred) {
|
|
2153
2030
|
const schema = MODEL_REGISTRY.video_generate[model]?.params.duration;
|
|
2154
2031
|
const allowed = schema?.kind === "number" ? schema.enum : void 0;
|
|
2155
2032
|
if (!allowed?.length || allowed.includes(preferred)) return preferred;
|
|
2156
2033
|
return [...allowed].sort((a, b) => Math.abs(a - preferred) - Math.abs(b - preferred) || a - b)[0];
|
|
2157
2034
|
}
|
|
2158
|
-
function maxClipSeconds(model) {
|
|
2159
|
-
const schema = MODEL_REGISTRY.video_generate[model]?.params.duration;
|
|
2160
|
-
const allowed = schema?.kind === "number" ? schema.enum : void 0;
|
|
2161
|
-
return allowed?.length ? Math.max(...allowed) : void 0;
|
|
2162
|
-
}
|
|
2163
|
-
function splitScriptFor(words, model) {
|
|
2164
|
-
const maxSeconds = maxClipSeconds(model);
|
|
2165
|
-
if (!maxSeconds) return null;
|
|
2166
|
-
const perClip = Math.floor(maxSeconds * SPOKEN_WORDS_PER_SECOND);
|
|
2167
|
-
if (words <= perClip) return null;
|
|
2168
|
-
const clips = Math.ceil(words / perClip);
|
|
2169
|
-
return { clips, wordsPerClip: Math.ceil(words / clips), maxSeconds };
|
|
2170
|
-
}
|
|
2171
2035
|
function promptMaxLength(kind, model) {
|
|
2172
2036
|
const schema = MODEL_REGISTRY[kind]?.[model]?.params.prompt;
|
|
2173
2037
|
return schema?.kind === "string" ? schema.maxLength : void 0;
|
|
@@ -2182,19 +2046,6 @@ function maxInputSlot(kind, model, inputKind) {
|
|
|
2182
2046
|
function maxInputReferences(kind, model) {
|
|
2183
2047
|
return maxInputSlot(kind, model, "image");
|
|
2184
2048
|
}
|
|
2185
|
-
function supportedQualities(kind, model) {
|
|
2186
|
-
const schema = MODEL_REGISTRY[kind]?.[model]?.params.quality;
|
|
2187
|
-
return schema?.kind === "string" ? schema.enum : void 0;
|
|
2188
|
-
}
|
|
2189
|
-
function bestSupportedQuality(kind, model, want) {
|
|
2190
|
-
const supported = supportedQualities(kind, model);
|
|
2191
|
-
if (!supported) return void 0;
|
|
2192
|
-
if (supported.includes(want)) return want;
|
|
2193
|
-
const ladder = supported.filter((step) => step !== "auto");
|
|
2194
|
-
const ceiling = ladder.indexOf(want);
|
|
2195
|
-
const reachable = ceiling === -1 ? ladder : ladder.slice(0, ceiling);
|
|
2196
|
-
return reachable[reachable.length - 1];
|
|
2197
|
-
}
|
|
2198
2049
|
function supportedAspectRatios(kind, model) {
|
|
2199
2050
|
const schema = MODEL_REGISTRY[kind]?.[model]?.params.aspect_ratio;
|
|
2200
2051
|
return schema?.kind === "string" ? schema.enum : void 0;
|
|
@@ -3279,16 +3130,6 @@ function dfsCycle(u, color, stack, reverseAdj) {
|
|
|
3279
3130
|
stack.pop();
|
|
3280
3131
|
return null;
|
|
3281
3132
|
}
|
|
3282
|
-
function nodesBeforeCheckpoint(graph, types, stopKinds) {
|
|
3283
|
-
const all = new Set(graph.keys());
|
|
3284
|
-
if (!stopKinds?.size) return all;
|
|
3285
|
-
const running = /* @__PURE__ */ new Set();
|
|
3286
|
-
for (const layer of topologicalLayers(graph)) {
|
|
3287
|
-
if (layer.some((id) => stopKinds.has(types.get(id) ?? ""))) return running;
|
|
3288
|
-
for (const id of layer) running.add(id);
|
|
3289
|
-
}
|
|
3290
|
-
return running;
|
|
3291
|
-
}
|
|
3292
3133
|
|
|
3293
3134
|
// src/engine/engine/validator.ts
|
|
3294
3135
|
import { readFile as readFile2 } from "fs/promises";
|
|
@@ -3307,7 +3148,7 @@ var VEO_DURATIONS = [4, 6, 8];
|
|
|
3307
3148
|
var KLING_DURATIONS2 = [5, 10];
|
|
3308
3149
|
var KLING_NEGATIVE_PROMPT = "warped face, distorted hands, extra fingers, morphing, flicker, on-screen text, watermark, low quality";
|
|
3309
3150
|
var KLING_CFG_SCALE = 0.7;
|
|
3310
|
-
var SPEAKS_PROSE = (line
|
|
3151
|
+
var SPEAKS_PROSE = (line) => `The person speaks to camera; lip-sync follows the dialogue verbatim, with delivery and emotion carried in the wording itself (no bracketed cues). Dialogue: "${line}"`;
|
|
3311
3152
|
var SEEDANCE_PROFILE = {
|
|
3312
3153
|
id: "seedance",
|
|
3313
3154
|
dialogueDirective: SPEAKS_PROSE,
|
|
@@ -3408,14 +3249,9 @@ var GPT_IMAGE_PROFILE = {
|
|
|
3408
3249
|
id: "gpt-image",
|
|
3409
3250
|
constraintPlacement: "last",
|
|
3410
3251
|
photorealCue: true,
|
|
3411
|
-
// OpenRouter forwards `quality`; gpt-image already processes inputs at high
|
|
3412
|
-
// automatically, so we deliberately do NOT send `input_fidelity`.
|
|
3413
|
-
|
|
3414
|
-
// No `quality` here any more: it is the one default that depends on WHICH gpt-image is
|
|
3415
|
-
// pinned. 2.5 added `xhigh` and `max` above `high`, and the older entries would refuse
|
|
3416
|
-
// them at validate — so callers ask for the step they want through
|
|
3417
|
-
// `bestSupportedQuality` and get the best the chosen model actually has.
|
|
3418
|
-
paramDefaults: {}
|
|
3252
|
+
// OpenRouter forwards `quality`; gpt-image-2 already processes inputs at high
|
|
3253
|
+
// fidelity automatically, so we deliberately do NOT send `input_fidelity`.
|
|
3254
|
+
paramDefaults: { quality: "high" }
|
|
3419
3255
|
};
|
|
3420
3256
|
var GEMINI_IMAGE_PROFILE = {
|
|
3421
3257
|
id: "gemini",
|
|
@@ -3926,19 +3762,6 @@ function checkSlotsForNode(ctx, n, _i) {
|
|
|
3926
3762
|
});
|
|
3927
3763
|
}
|
|
3928
3764
|
}
|
|
3929
|
-
function estimateCreditsFor(canvas, registry, ids) {
|
|
3930
|
-
let total = 0;
|
|
3931
|
-
for (const n of canvas.nodes) {
|
|
3932
|
-
if (!ids.has(n.id)) continue;
|
|
3933
|
-
const def = registry.get(n.type);
|
|
3934
|
-
if (!def?.cost) continue;
|
|
3935
|
-
try {
|
|
3936
|
-
total += def.cost({ params: def.params.parse(n.params ?? {}) }).credits;
|
|
3937
|
-
} catch {
|
|
3938
|
-
}
|
|
3939
|
-
}
|
|
3940
|
-
return total;
|
|
3941
|
-
}
|
|
3942
3765
|
function estimateCredits(ctx) {
|
|
3943
3766
|
let total = 0;
|
|
3944
3767
|
for (const n of ctx.canvas.nodes) {
|
|
@@ -4245,7 +4068,7 @@ function checkAudioTimelineTotals(ctx, audioMixNode, expected) {
|
|
|
4245
4068
|
if (audioMixNode) mustBeFullLength.add(audioMixNode);
|
|
4246
4069
|
ctx.canvas.nodes.forEach((n) => {
|
|
4247
4070
|
if (n.type !== "audio_voice_convert") return;
|
|
4248
|
-
const track = refNodeOf(ctx, n.inputs
|
|
4071
|
+
const track = refNodeOf(ctx, (n.inputs ?? {}).audio);
|
|
4249
4072
|
if (track?.type === "audio_timeline") mustBeFullLength.add(track.id);
|
|
4250
4073
|
});
|
|
4251
4074
|
for (const id of mustBeFullLength) {
|
|
@@ -4266,11 +4089,11 @@ function checkAudioTimelineTotals(ctx, audioMixNode, expected) {
|
|
|
4266
4089
|
function checkNativeSegOverlap(ctx) {
|
|
4267
4090
|
for (const conv of ctx.canvas.nodes) {
|
|
4268
4091
|
if (conv.type !== "audio_voice_convert") continue;
|
|
4269
|
-
const track = refNodeOf(ctx, conv.inputs
|
|
4092
|
+
const track = refNodeOf(ctx, (conv.inputs ?? {}).audio);
|
|
4270
4093
|
if (track?.type !== "audio_timeline") continue;
|
|
4271
4094
|
const params = track.params;
|
|
4272
4095
|
const windows = (params.tracks ?? []).map((t) => {
|
|
4273
|
-
const extract = refNodeOf(ctx, track.inputs
|
|
4096
|
+
const extract = refNodeOf(ctx, (track.inputs ?? {})[t.slot]);
|
|
4274
4097
|
const len = t.duration_s ?? (extract ? ffmpegTrimSeconds(extract) : null);
|
|
4275
4098
|
return len === null ? null : { slot: t.slot, start: t.start_s, end: t.start_s + len };
|
|
4276
4099
|
}).filter((w) => w !== null).sort((a, b) => a.start - b.start);
|
|
@@ -4763,13 +4586,6 @@ function unwrap(schema) {
|
|
|
4763
4586
|
}
|
|
4764
4587
|
|
|
4765
4588
|
// src/engine/engine/executor.ts
|
|
4766
|
-
function heldByCheckpoint(canvas, layer, kinds) {
|
|
4767
|
-
if (!kinds?.size) return [];
|
|
4768
|
-
return layer.filter((id) => {
|
|
4769
|
-
const type = canvas.nodes.find((n) => n.id === id)?.type;
|
|
4770
|
-
return type !== void 0 && kinds.has(type);
|
|
4771
|
-
});
|
|
4772
|
-
}
|
|
4773
4589
|
var Engine = class {
|
|
4774
4590
|
registry;
|
|
4775
4591
|
client;
|
|
@@ -4797,7 +4613,7 @@ var Engine = class {
|
|
|
4797
4613
|
async run(input, opts = {}) {
|
|
4798
4614
|
const validation = await this.validateDeep(input);
|
|
4799
4615
|
if (!validation.ok) throw new ValidationError(validation.issues);
|
|
4800
|
-
if (
|
|
4616
|
+
if (opts.max_credits !== void 0 && validation.estimatedCredits > opts.max_credits) {
|
|
4801
4617
|
throw new RunAbortedError(
|
|
4802
4618
|
"cost_cap",
|
|
4803
4619
|
`estimated ${validation.estimatedCredits} credits exceeds the ${opts.max_credits}-credit cap \u2014 nothing was billed; raise --max-credits or shrink the canvas`
|
|
@@ -4816,20 +4632,6 @@ var Engine = class {
|
|
|
4816
4632
|
const counters = { cachedNodes: 0, totalCredits: 0 };
|
|
4817
4633
|
const nodeRuns = [];
|
|
4818
4634
|
const graph = this.pruneToOutput(canvas, buildGraph(canvas));
|
|
4819
|
-
if (opts.max_credits !== void 0 && opts.stop_before_kinds?.size) {
|
|
4820
|
-
const willRun = nodesBeforeCheckpoint(
|
|
4821
|
-
graph,
|
|
4822
|
-
new Map(canvas.nodes.map((n) => [n.id, n.type])),
|
|
4823
|
-
opts.stop_before_kinds
|
|
4824
|
-
);
|
|
4825
|
-
const estimate = estimateCreditsFor(canvas, this.registry, willRun);
|
|
4826
|
-
if (estimate > opts.max_credits) {
|
|
4827
|
-
throw new RunAbortedError(
|
|
4828
|
-
"cost_cap",
|
|
4829
|
-
`estimated ${estimate} credits exceeds the ${opts.max_credits}-credit cap \u2014 nothing was billed; raise --max-credits or shrink the canvas`
|
|
4830
|
-
);
|
|
4831
|
-
}
|
|
4832
|
-
}
|
|
4833
4635
|
const needsBytes = computeNeedsLocalBytes(canvas, graph, this.registry);
|
|
4834
4636
|
this.emitProgress(opts, {
|
|
4835
4637
|
kind: "plan",
|
|
@@ -4874,13 +4676,6 @@ var Engine = class {
|
|
|
4874
4676
|
`spent ${counters.totalCredits} credits, over the ${opts.max_credits}-credit cap \u2014 completed nodes are cached; raise --max-credits to continue where this stopped`
|
|
4875
4677
|
);
|
|
4876
4678
|
}
|
|
4877
|
-
const held = heldByCheckpoint(canvas, layer, opts.stop_before_kinds);
|
|
4878
|
-
if (held.length > 0) {
|
|
4879
|
-
throw new RunAbortedError(
|
|
4880
|
-
"checkpoint",
|
|
4881
|
-
`stopped before ${held.join(", ")} \u2014 everything up to here is done and cached, so continuing this run id pays for none of it again`
|
|
4882
|
-
);
|
|
4883
|
-
}
|
|
4884
4679
|
const settled = await mapWithConcurrency(layer, limit, (nodeId) => {
|
|
4885
4680
|
if (opts.signal?.aborted) {
|
|
4886
4681
|
return Promise.reject(new RunAbortedError("signal", "run aborted before node dispatch"));
|
|
@@ -6154,7 +5949,7 @@ function resolveStrategy(params) {
|
|
|
6154
5949
|
case "text": {
|
|
6155
5950
|
const p = safePathname(params.url);
|
|
6156
5951
|
const isRawText = RAW_TEXT_EXTENSIONS.some((ext) => p.endsWith(ext));
|
|
6157
|
-
return isRawText ? "direct_fetch" : "
|
|
5952
|
+
return isRawText ? "direct_fetch" : "firecrawl";
|
|
6158
5953
|
}
|
|
6159
5954
|
default: {
|
|
6160
5955
|
params.expect;
|
|
@@ -6171,10 +5966,10 @@ function safePathname(rawUrl) {
|
|
|
6171
5966
|
}
|
|
6172
5967
|
var ingestNode = defineNode({
|
|
6173
5968
|
id: "ingest",
|
|
6174
|
-
version: "1.
|
|
5969
|
+
version: "1.3.0",
|
|
6175
5970
|
category: "io",
|
|
6176
5971
|
location: "local",
|
|
6177
|
-
summary: "Ingest an external URL or a local file into the asset store. Declare the kind you expect (image/video/audio/text/json/font); the node picks the strategy. For source=url: yt-dlp for video/audio (YouTube/TikTok/Vimeo/etc. and direct file URLs),
|
|
5972
|
+
summary: "Ingest an external URL or a local file into the asset store. Declare the kind you expect (image/video/audio/text/json/font); the node picks the strategy. For source=url: yt-dlp for video/audio (YouTube/TikTok/Vimeo/etc. and direct file URLs), Firecrawl for HTML/PDF pages \u2192 markdown, direct HTTP fetch for binary URLs (images, fonts) and raw .txt/.md. For source=path: read from the local filesystem and upload to R2.",
|
|
6178
5973
|
when_to_use: 'Use as the on-ramp for any external asset a canvas needs. `expect` makes the output port statically typed so downstream wiring is checked before any byte is fetched. Pick `source: "url"` for portable canvas; pick `source: "path"` for local files (canvas is not portable across machines without the file). Compose with other nodes for richer pipelines (e.g. `ingest expect=video` \u2192 `video_transcribe`).',
|
|
6179
5974
|
inputs: IngestInputs,
|
|
6180
5975
|
params: IngestParams,
|
|
@@ -6201,8 +5996,8 @@ function runStrategy(strategy, params, ctx) {
|
|
|
6201
5996
|
switch (strategy) {
|
|
6202
5997
|
case "direct_fetch":
|
|
6203
5998
|
return execDirectFetch(params, ctx);
|
|
6204
|
-
case "
|
|
6205
|
-
return
|
|
5999
|
+
case "firecrawl":
|
|
6000
|
+
return execFirecrawl(params, ctx);
|
|
6206
6001
|
case "yt_dlp":
|
|
6207
6002
|
return execYtDlp(params, ctx);
|
|
6208
6003
|
case "local_file":
|
|
@@ -6240,9 +6035,8 @@ async function ingestImageUrl(url, ctx) {
|
|
|
6240
6035
|
} catch (e) {
|
|
6241
6036
|
throw localExecError(ctx, `${url}: ${e.message}`);
|
|
6242
6037
|
}
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
}
|
|
6038
|
+
const note = normalizationNote(normalized);
|
|
6039
|
+
if (note) ctx.log(`ingest: ${url} ${note}`);
|
|
6246
6040
|
return uploadAndIngest({
|
|
6247
6041
|
bytes: normalized.bytes,
|
|
6248
6042
|
kind: "image",
|
|
@@ -6256,11 +6050,11 @@ async function ingestImageUrl(url, ctx) {
|
|
|
6256
6050
|
ctx
|
|
6257
6051
|
});
|
|
6258
6052
|
}
|
|
6259
|
-
async function
|
|
6053
|
+
async function execFirecrawl(params, ctx) {
|
|
6260
6054
|
const result = await callBackendExec({
|
|
6261
6055
|
nodeType: "ingest",
|
|
6262
6056
|
nodeVersion: ingestNode.version,
|
|
6263
|
-
params: { strategy: "
|
|
6057
|
+
params: { strategy: "firecrawl", url: params.url },
|
|
6264
6058
|
inputs: {},
|
|
6265
6059
|
outputKinds: { asset: "text" },
|
|
6266
6060
|
ctx
|
|
@@ -6352,23 +6146,69 @@ async function rasterizeSvgToPng(bytes) {
|
|
|
6352
6146
|
return await sharp(bytes, { density }).png({ force: true, palette: false }).toBuffer();
|
|
6353
6147
|
}
|
|
6354
6148
|
var MODEL_SAFE_IMAGE_MIMES = /* @__PURE__ */ new Set(["image/jpeg", "image/png", "image/gif", "image/webp"]);
|
|
6355
|
-
|
|
6149
|
+
var MAX_MODEL_REFERENCE_BYTES = 30 * 1024 * 1024;
|
|
6150
|
+
async function fitWithinReferenceLimit(bytes, mime, maxBytes) {
|
|
6151
|
+
if (bytes.byteLength <= maxBytes) return { bytes, mime };
|
|
6152
|
+
const { default: sharp } = await import("sharp");
|
|
6153
|
+
const read = () => sharp(bytes, { animated: true });
|
|
6154
|
+
const meta = await read().metadata();
|
|
6155
|
+
const animated = (meta.pages ?? 1) > 1;
|
|
6156
|
+
const keepAlpha = meta.hasAlpha === true;
|
|
6157
|
+
const width = meta.width ?? 0;
|
|
6158
|
+
const encode = async (scale2) => {
|
|
6159
|
+
let pipeline2 = read();
|
|
6160
|
+
if (scale2 < 1 && width > 0) pipeline2 = pipeline2.resize({ width: Math.max(256, Math.round(width * scale2)) });
|
|
6161
|
+
if (animated && mime === "image/gif") return { bytes: await pipeline2.gif().toBuffer(), mime: "image/gif" };
|
|
6162
|
+
if (animated || keepAlpha) return { bytes: await pipeline2.webp({ quality: 90 }).toBuffer(), mime: "image/webp" };
|
|
6163
|
+
return { bytes: await pipeline2.jpeg({ quality: 90, mozjpeg: true }).toBuffer(), mime: "image/jpeg" };
|
|
6164
|
+
};
|
|
6165
|
+
let out = await encode(1);
|
|
6166
|
+
let scale = 1;
|
|
6167
|
+
for (let pass = 0; pass < 4 && out.bytes.byteLength > maxBytes; pass++) {
|
|
6168
|
+
scale *= Math.min(0.9, Math.sqrt(maxBytes / out.bytes.byteLength));
|
|
6169
|
+
out = await encode(scale);
|
|
6170
|
+
}
|
|
6171
|
+
return out;
|
|
6172
|
+
}
|
|
6173
|
+
async function toModelSafeImage(bytes, opts) {
|
|
6174
|
+
const maxBytes = opts?.maxBytes ?? MAX_MODEL_REFERENCE_BYTES;
|
|
6175
|
+
const fit = async (candidate, mime, rasterizedFrom) => {
|
|
6176
|
+
const fitted = await fitWithinReferenceLimit(candidate, mime, maxBytes);
|
|
6177
|
+
return {
|
|
6178
|
+
bytes: fitted.bytes,
|
|
6179
|
+
mime: fitted.mime,
|
|
6180
|
+
...rasterizedFrom ? { rasterizedFrom } : {},
|
|
6181
|
+
...fitted.bytes === candidate ? {} : { shrunkFromBytes: candidate.byteLength }
|
|
6182
|
+
};
|
|
6183
|
+
};
|
|
6356
6184
|
const safe = sniffImageMime(bytes);
|
|
6357
6185
|
if (safe && MODEL_SAFE_IMAGE_MIMES.has(safe)) {
|
|
6358
|
-
return
|
|
6186
|
+
return await fit(bytes, safe);
|
|
6359
6187
|
}
|
|
6360
6188
|
if (sniffSvg(bytes)) {
|
|
6361
|
-
return
|
|
6189
|
+
return await fit(await rasterizeSvgToPng(bytes), "image/png", "svg");
|
|
6362
6190
|
}
|
|
6363
6191
|
const { default: sharp } = await import("sharp");
|
|
6192
|
+
let png;
|
|
6193
|
+
let format;
|
|
6364
6194
|
try {
|
|
6365
6195
|
const img = sharp(bytes);
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
return { bytes: png, mime: "image/png", rasterizedFrom: format ?? "unknown" };
|
|
6196
|
+
format = (await img.metadata()).format ?? "unknown";
|
|
6197
|
+
png = await img.png({ force: true }).toBuffer();
|
|
6369
6198
|
} catch (e) {
|
|
6370
6199
|
throw new Error(`bytes are not a decodable image (${e.message})`);
|
|
6371
6200
|
}
|
|
6201
|
+
return await fit(png, "image/png", format);
|
|
6202
|
+
}
|
|
6203
|
+
function normalizationNote(image) {
|
|
6204
|
+
const parts = [];
|
|
6205
|
+
if (image.rasterizedFrom) parts.push(`normalized ${image.rasterizedFrom} -> ${image.mime}`);
|
|
6206
|
+
if (image.shrunkFromBytes !== void 0) {
|
|
6207
|
+
parts.push(
|
|
6208
|
+
`fitted ${image.shrunkFromBytes}B -> ${image.bytes.length}B as ${image.mime} (providers refuse a reference over ${MAX_MODEL_REFERENCE_BYTES}B)`
|
|
6209
|
+
);
|
|
6210
|
+
}
|
|
6211
|
+
return parts.length > 0 ? parts.join("; ") : null;
|
|
6372
6212
|
}
|
|
6373
6213
|
function hasAscii(buf, offset, sig) {
|
|
6374
6214
|
return buf.length >= offset + sig.length && buf.toString("ascii", offset, offset + sig.length) === sig;
|
|
@@ -6498,7 +6338,8 @@ async function execLocalFile(params, ctx) {
|
|
|
6498
6338
|
outBytes = normalized.bytes;
|
|
6499
6339
|
outMime = normalized.mime;
|
|
6500
6340
|
rasterizedFrom = normalized.rasterizedFrom;
|
|
6501
|
-
|
|
6341
|
+
const note = normalizationNote(normalized);
|
|
6342
|
+
if (note) ctx.log(`ingest: ${note}`);
|
|
6502
6343
|
}
|
|
6503
6344
|
const durationMs = probeVideoDurationMs(params.expect, outBytes, ctx);
|
|
6504
6345
|
const ref = await uploadAndIngest({
|
|
@@ -7470,24 +7311,6 @@ import { execFile as execFile3 } from "child_process";
|
|
|
7470
7311
|
import { readFile as readFile9 } from "fs/promises";
|
|
7471
7312
|
import path12 from "path";
|
|
7472
7313
|
import { promisify as promisify3 } from "util";
|
|
7473
|
-
|
|
7474
|
-
// src/engine/nodes/local/lib/caption-style.ts
|
|
7475
|
-
var TERMINAL = /[.,;:!?…]$/;
|
|
7476
|
-
function captionTextsFrom(html) {
|
|
7477
|
-
const cards = html.match(/<div[^>]*class="[^"]*\bcaption\b[^"]*"[\s\S]*?<\/div>/g) ?? [];
|
|
7478
|
-
return cards.map(
|
|
7479
|
-
(card) => card.replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim()
|
|
7480
|
-
).filter((text) => text.length > 0);
|
|
7481
|
-
}
|
|
7482
|
-
function fragmentedCaptions(texts) {
|
|
7483
|
-
if (texts.length < 2) return null;
|
|
7484
|
-
const fragments = texts.slice(0, -1).filter((t) => !TERMINAL.test(t));
|
|
7485
|
-
if (fragments.length === 0) return null;
|
|
7486
|
-
const example = fragments[0];
|
|
7487
|
-
return `${fragments.length} of ${texts.length} caption cards end mid-clause, e.g. "${example}". Split captions where the script punctuates, not on a word count \u2014 a card that ends bare is a sentence cut in half by a timing decision, and it reads as one.`;
|
|
7488
|
-
}
|
|
7489
|
-
|
|
7490
|
-
// src/engine/nodes/local/lib/hyperframe-check.ts
|
|
7491
7314
|
var execFileAsync = promisify3(execFile3);
|
|
7492
7315
|
var NEVER_BLOCK = [
|
|
7493
7316
|
/contrast/i,
|
|
@@ -7556,74 +7379,6 @@ function classifyCheckOutput(lintRaw, inspectRaw) {
|
|
|
7556
7379
|
warnings: findings.filter((f) => f.severity === "warning")
|
|
7557
7380
|
};
|
|
7558
7381
|
}
|
|
7559
|
-
var GATE_SECTIONS = ["lint", "runtime", "layout", "motion", "contrast"];
|
|
7560
|
-
function buildCheckArgs(dir, samples) {
|
|
7561
|
-
return ["hyperframes", "check", dir, "--json", "--samples", String(samples), "--at-transitions"];
|
|
7562
|
-
}
|
|
7563
|
-
var PROMOTE_TO_BLOCKING = /GSAP target [^ ]+ not found|no deterministic font mapping/i;
|
|
7564
|
-
function toGateFinding(section, entry) {
|
|
7565
|
-
const f = entry;
|
|
7566
|
-
const code = typeof f.code === "string" ? f.code : "unknown";
|
|
7567
|
-
const message2 = typeof f.message === "string" ? f.message : "";
|
|
7568
|
-
const isError = f.severity === "error" || PROMOTE_TO_BLOCKING.test(message2);
|
|
7569
|
-
const fatal = PROMOTE_TO_BLOCKING.test(message2);
|
|
7570
|
-
return {
|
|
7571
|
-
source: section,
|
|
7572
|
-
code,
|
|
7573
|
-
message: message2,
|
|
7574
|
-
severity: fatal || isError && !isAdvisory(code, message2) ? "blocking" : "warning"
|
|
7575
|
-
};
|
|
7576
|
-
}
|
|
7577
|
-
function classifyGateOutput(raw) {
|
|
7578
|
-
const parsed = parseCheckJson(raw);
|
|
7579
|
-
const findings = GATE_SECTIONS.flatMap((section) => {
|
|
7580
|
-
const block = parsed?.[section];
|
|
7581
|
-
const entries = Array.isArray(block?.findings) ? block.findings : [];
|
|
7582
|
-
return entries.map((entry) => toGateFinding(section, entry));
|
|
7583
|
-
});
|
|
7584
|
-
return {
|
|
7585
|
-
blocking: findings.filter((f) => f.severity === "blocking"),
|
|
7586
|
-
warnings: findings.filter((f) => f.severity === "warning")
|
|
7587
|
-
};
|
|
7588
|
-
}
|
|
7589
|
-
function summariseWarnings(warnings) {
|
|
7590
|
-
const groups = /* @__PURE__ */ new Map();
|
|
7591
|
-
for (const w of warnings) {
|
|
7592
|
-
const key = `${w.source}/${w.code}`;
|
|
7593
|
-
const seen = groups.get(key);
|
|
7594
|
-
if (seen) seen.count += 1;
|
|
7595
|
-
else groups.set(key, { count: 1, message: w.message });
|
|
7596
|
-
}
|
|
7597
|
-
return [...groups.entries()].sort((a, b) => b[1].count - a[1].count).map(([key, g]) => `${g.count}\xD7 [${key}] ${g.message}`);
|
|
7598
|
-
}
|
|
7599
|
-
var FINDING_ADVICE = {
|
|
7600
|
-
sweep_static: "the timeline never advanced, so this renders as a STILL IMAGE while billing like a video \u2014 and every other green verdict in this run is unreliable. Check the composition registers a paused timeline on `window.__timelines[<composition-id>]` and that the tweens target elements that exist.",
|
|
7601
|
-
console_error: "the composition threw in the browser, so whatever that script was driving did not happen.",
|
|
7602
|
-
page_error: "a script failed to evaluate \u2014 animations and any JS-driven layout in this composition did not run.",
|
|
7603
|
-
http_error: "an asset 404ed at render time; it will be missing from the frame, not substituted.",
|
|
7604
|
-
console_warning: "if this names a GSAP target, the element it animates does not exist, so that tween is a no-op and whatever it was meant to reveal never appears. Fix the selector or add the element before rendering."
|
|
7605
|
-
};
|
|
7606
|
-
function explainGateFinding(finding) {
|
|
7607
|
-
const advice = FINDING_ADVICE[finding.code];
|
|
7608
|
-
return advice ? `${finding.message} \u2014 ${advice}` : finding.message;
|
|
7609
|
-
}
|
|
7610
|
-
function condenseFrameDescriptions(markdown) {
|
|
7611
|
-
const lines = [];
|
|
7612
|
-
let frame = null;
|
|
7613
|
-
for (const line of markdown.split("\n")) {
|
|
7614
|
-
const heading = line.match(/^## (.+)$/);
|
|
7615
|
-
if (heading) {
|
|
7616
|
-
frame = heading[1] ?? null;
|
|
7617
|
-
continue;
|
|
7618
|
-
}
|
|
7619
|
-
const text = line.trim();
|
|
7620
|
-
if (frame && text) {
|
|
7621
|
-
lines.push(`${frame}: ${text}`);
|
|
7622
|
-
frame = null;
|
|
7623
|
-
}
|
|
7624
|
-
}
|
|
7625
|
-
return lines;
|
|
7626
|
-
}
|
|
7627
7382
|
function buildLintArgs(dir) {
|
|
7628
7383
|
return ["hyperframes", "lint", dir, "--json"];
|
|
7629
7384
|
}
|
|
@@ -7655,13 +7410,8 @@ async function lintStructuralIssues(dir, timeoutMs) {
|
|
|
7655
7410
|
function buildInspectArgs(dir, samples) {
|
|
7656
7411
|
return ["hyperframes", "inspect", dir, "--json", "--samples", String(samples)];
|
|
7657
7412
|
}
|
|
7658
|
-
function buildSnapshotArgs(dir, frames
|
|
7659
|
-
|
|
7660
|
-
return vision ? [
|
|
7661
|
-
...base,
|
|
7662
|
-
"--describe",
|
|
7663
|
-
"Answer four things about this frame. (1) Is it empty or near-empty? Name every element actually visible. (2) Could what is shown physically happen \u2014 object sizes, human scale, and whether every object has its real-world form (a phone has ONE screen, on its front). Say so plainly if something is impossible. (3) Read out every number, currency symbol and word visible INSIDE the picture \u2014 phone screens, documents, signage \u2014 and say whether any of it contradicts the caption on this frame. (4) Is the caption legible against what is behind it?"
|
|
7664
|
-
] : [...base, "--describe", "false"];
|
|
7413
|
+
function buildSnapshotArgs(dir, frames) {
|
|
7414
|
+
return ["hyperframes", "snapshot", dir, "--frames", String(frames), "--describe", "false"];
|
|
7665
7415
|
}
|
|
7666
7416
|
function usesNestedCompositions(indexHtml) {
|
|
7667
7417
|
const withoutComments = indexHtml.replace(/<!--[\s\S]*?-->/g, "");
|
|
@@ -7690,66 +7440,44 @@ async function runSnapshotSmoke(args, timeoutMs) {
|
|
|
7690
7440
|
return { ok: false, unavailable: false, message: (err.stderr || err.message || "snapshot failed").slice(0, 800) };
|
|
7691
7441
|
}
|
|
7692
7442
|
}
|
|
7693
|
-
async function
|
|
7694
|
-
const { dir, nodeId, ctx, timeoutMs, samples } = opts;
|
|
7695
|
-
const indexHtml = await readFile9(path12.join(dir, "index.html"), "utf-8").catch(() => "");
|
|
7696
|
-
if (!indexHtml || !usesNestedCompositions(indexHtml)) return;
|
|
7697
|
-
const snap = await runSnapshotSmoke(
|
|
7698
|
-
buildSnapshotArgs(dir, Math.min(samples, 3), hasFrameVisionKey()),
|
|
7699
|
-
Math.max(timeoutMs, 12e4)
|
|
7700
|
-
);
|
|
7701
|
-
if (snap.unavailable) {
|
|
7702
|
-
ctx.log(`${nodeId}: hyperframes snapshot unavailable \u2014 skipping nested-composition smoke test`);
|
|
7703
|
-
return;
|
|
7704
|
-
}
|
|
7705
|
-
if (!snap.ok) {
|
|
7706
|
-
throw new Error(
|
|
7707
|
-
`${nodeId}: nested-composition smoke test failed \u2014 an embedded block did not render. Check the host\u2194block id match, that the block's <style>/<script> live inside its <template>, and that it styles #root (not a class).
|
|
7708
|
-
${snap.message}`
|
|
7709
|
-
);
|
|
7710
|
-
}
|
|
7711
|
-
const described = await readFile9(path12.join(dir, "snapshots", "descriptions.md"), "utf-8").catch(() => "");
|
|
7712
|
-
for (const line of condenseFrameDescriptions(described)) {
|
|
7713
|
-
ctx.log(`${nodeId}: frame check \u2014 ${line}`);
|
|
7714
|
-
}
|
|
7715
|
-
ctx.log(`${nodeId}: nested-composition smoke test passed`);
|
|
7716
|
-
}
|
|
7717
|
-
async function runGate(dir, timeoutMs, samples) {
|
|
7718
|
-
const checkRaw = await runOne(buildCheckArgs(dir, samples), timeoutMs);
|
|
7719
|
-
if (checkRaw !== null) return classifyGateOutput(checkRaw);
|
|
7443
|
+
async function runHyperframesCheck(opts) {
|
|
7444
|
+
const { dir, nodeId, ctx, timeoutMs, samples = 5 } = opts;
|
|
7720
7445
|
const [lintRaw, inspectRaw] = await Promise.all([
|
|
7721
7446
|
runOne(buildLintArgs(dir), timeoutMs),
|
|
7722
7447
|
runOne(buildInspectArgs(dir, samples), timeoutMs)
|
|
7723
7448
|
]);
|
|
7724
|
-
if (lintRaw === null && inspectRaw === null)
|
|
7725
|
-
|
|
7726
|
-
}
|
|
7727
|
-
async function runHyperframesCheck(opts) {
|
|
7728
|
-
const { dir, nodeId, ctx, timeoutMs, samples = 5 } = opts;
|
|
7729
|
-
const gate = await runGate(dir, timeoutMs, samples);
|
|
7730
|
-
if (gate === null) {
|
|
7731
|
-
ctx.log(`${nodeId}: hyperframes check/lint/inspect unavailable \u2014 skipping pre-render check`);
|
|
7449
|
+
if (lintRaw === null && inspectRaw === null) {
|
|
7450
|
+
ctx.log(`${nodeId}: hyperframes lint/inspect unavailable \u2014 skipping pre-render check`);
|
|
7732
7451
|
return;
|
|
7733
7452
|
}
|
|
7734
|
-
const { blocking, warnings } =
|
|
7735
|
-
for (const
|
|
7736
|
-
ctx.log(`${nodeId}: hyperframe check warning ${
|
|
7453
|
+
const { blocking, warnings } = classifyCheckOutput(lintRaw ?? "", inspectRaw ?? "");
|
|
7454
|
+
for (const w of warnings) {
|
|
7455
|
+
ctx.log(`${nodeId}: hyperframe check warning [${w.source}/${w.code}] ${w.message}`);
|
|
7737
7456
|
}
|
|
7738
7457
|
if (blocking.length > 0) {
|
|
7739
|
-
const
|
|
7740
|
-
|
|
7741
|
-
|
|
7742
|
-
throw new Error(
|
|
7743
|
-
`${nodeId}: pre-render check failed (${blocking.length} blocking, ${seen.size} distinct)
|
|
7744
|
-
${detail}`
|
|
7745
|
-
);
|
|
7458
|
+
const detail = blocking.map((b) => `\u2022 [${b.source}/${b.code}] ${b.message}`).join("\n");
|
|
7459
|
+
throw new Error(`${nodeId}: pre-render check failed (${blocking.length} blocking)
|
|
7460
|
+
${detail}`);
|
|
7746
7461
|
}
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
|
|
7462
|
+
let indexHtml = "";
|
|
7463
|
+
try {
|
|
7464
|
+
indexHtml = await readFile9(path12.join(dir, "index.html"), "utf-8");
|
|
7465
|
+
} catch {
|
|
7466
|
+
indexHtml = "";
|
|
7467
|
+
}
|
|
7468
|
+
if (indexHtml && usesNestedCompositions(indexHtml)) {
|
|
7469
|
+
const snap = await runSnapshotSmoke(buildSnapshotArgs(dir, Math.min(samples, 3)), Math.max(timeoutMs, 12e4));
|
|
7470
|
+
if (snap.unavailable) {
|
|
7471
|
+
ctx.log(`${nodeId}: hyperframes snapshot unavailable \u2014 skipping nested-composition smoke test`);
|
|
7472
|
+
} else if (!snap.ok) {
|
|
7473
|
+
throw new Error(
|
|
7474
|
+
`${nodeId}: nested-composition smoke test failed \u2014 an embedded block did not render. Check the host\u2194block id match, that the block's <style>/<script> live inside its <template>, and that it styles #root (not a class).
|
|
7475
|
+
${snap.message}`
|
|
7476
|
+
);
|
|
7477
|
+
} else {
|
|
7478
|
+
ctx.log(`${nodeId}: nested-composition smoke test passed`);
|
|
7479
|
+
}
|
|
7751
7480
|
}
|
|
7752
|
-
await runNestedCompositionSmoke({ dir, nodeId, ctx, timeoutMs, samples });
|
|
7753
7481
|
ctx.log(`${nodeId}: pre-render check passed (${warnings.length} warning${warnings.length === 1 ? "" : "s"})`);
|
|
7754
7482
|
}
|
|
7755
7483
|
|
|
@@ -9256,14 +8984,7 @@ async function tryExtractAudio(inputs, ctx) {
|
|
|
9256
8984
|
import { z as z34 } from "zod";
|
|
9257
8985
|
var voiceSelectNode = delegated({
|
|
9258
8986
|
id: "voice_select",
|
|
9259
|
-
|
|
9260
|
-
// and no mime, which every consumer taking the id as an INPUT rejected —
|
|
9261
|
-
// `audio_voice_convert` died on `voice_ref.sha256: expected string, received undefined`.
|
|
9262
|
-
// The backend now uploads it as a real asset like the ranked branch, and the version has
|
|
9263
|
-
// to move with it: the cache key is built from this string, so entries written under
|
|
9264
|
-
// 1.0.0 kept replaying the broken shape long after the fix was deployed. A node whose
|
|
9265
|
-
// OUTPUT shape changes has to bump, not only one whose behaviour does.
|
|
9266
|
-
version: "1.1.0",
|
|
8987
|
+
version: "1.0.0",
|
|
9267
8988
|
category: "audio",
|
|
9268
8989
|
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.',
|
|
9269
8990
|
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.',
|
|
@@ -9274,11 +8995,6 @@ var voiceSelectNode = delegated({
|
|
|
9274
8995
|
age: z34.string().optional(),
|
|
9275
8996
|
accent: z34.string().optional(),
|
|
9276
8997
|
language: z34.string().optional(),
|
|
9277
|
-
/**
|
|
9278
|
-
* A voice already chosen — an Avatar's pinned one. Given, nothing is cast: the
|
|
9279
|
-
* ranking is skipped and this id is emitted as-is.
|
|
9280
|
-
*/
|
|
9281
|
-
voice_id: z34.string().min(1).max(120).optional(),
|
|
9282
8998
|
limit: z34.number().int().min(1).max(20).optional()
|
|
9283
8999
|
}).strict(),
|
|
9284
9000
|
outputs: z34.object({ voice_id: TextRef, candidates: JsonRef }).strict(),
|
|
@@ -9441,7 +9157,6 @@ export {
|
|
|
9441
9157
|
describeFailureReason,
|
|
9442
9158
|
AD_FORMAT_PLATFORMS,
|
|
9443
9159
|
platformFormats,
|
|
9444
|
-
frameRealismDirection,
|
|
9445
9160
|
SEEDANCE_DURATIONS,
|
|
9446
9161
|
ELEVENLABS_MAX_MUSIC_LENGTH_MS,
|
|
9447
9162
|
IMAGE_GENERATE_MODELS,
|
|
@@ -9453,14 +9168,10 @@ export {
|
|
|
9453
9168
|
MODEL_REGISTRY,
|
|
9454
9169
|
supportsParam,
|
|
9455
9170
|
DEFAULT_CLIP_DURATION_S,
|
|
9456
|
-
SPOKEN_WORDS_PER_SECOND,
|
|
9457
|
-
spokenWordsIn,
|
|
9458
9171
|
nearestClipDuration,
|
|
9459
|
-
splitScriptFor,
|
|
9460
9172
|
promptMaxLength,
|
|
9461
9173
|
maxInputSlot,
|
|
9462
9174
|
maxInputReferences,
|
|
9463
|
-
bestSupportedQuality,
|
|
9464
9175
|
nearestSupportedAspectRatio,
|
|
9465
9176
|
nearestSupportedImageSize,
|
|
9466
9177
|
estimateVideoCredits,
|
|
@@ -9501,4 +9212,4 @@ export {
|
|
|
9501
9212
|
defaultRegistry,
|
|
9502
9213
|
createEngineFromEnv
|
|
9503
9214
|
};
|
|
9504
|
-
//# sourceMappingURL=chunk-
|
|
9215
|
+
//# sourceMappingURL=chunk-YFQWCJX4.js.map
|