@koda-sl/baker-cli 0.301.0-dev.50ab1c583 → 0.301.0-dev.c73cac21a

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.
Files changed (28) hide show
  1. package/README.md +128 -88
  2. package/canvas/tiktok-captions-composition/index.html +2 -23
  3. package/dist/{chunk-2IUN3S3E.js → chunk-2IB3IL4K.js} +2 -2
  4. package/dist/{chunk-4YF56EGK.js → chunk-D3HHUBIL.js} +4 -4
  5. package/dist/{chunk-WFWU3CHS.js → chunk-DZUVUGEP.js} +1 -9
  6. package/dist/{chunk-WFWU3CHS.js.map → chunk-DZUVUGEP.js.map} +1 -1
  7. package/dist/{chunk-SSXER76B.js → chunk-EFETQHTX.js} +123 -438
  8. package/dist/chunk-EFETQHTX.js.map +1 -0
  9. package/dist/{chunk-A2VZOTCV.js → chunk-EX2OIOVL.js} +3 -3
  10. package/dist/{chunk-KDTHRRAC.js → chunk-GU7IWEBC.js} +3 -3
  11. package/dist/cli.js +7754 -6708
  12. package/dist/cli.js.map +1 -1
  13. package/dist/{client-6KQHCXS2.js → client-VDCCDEHE.js} +4 -4
  14. package/dist/engine/index.d.ts +0 -13
  15. package/dist/engine/index.js +3 -3
  16. package/dist/{env-C7YXL6C6.js → env-FWMZXMQK.js} +2 -6
  17. package/dist/{output-RBM32FKJ.js → output-4H2RNBXL.js} +5 -5
  18. package/dist/{shared-WJIJTWST.js → shared-UKL36AXT.js} +6 -6
  19. package/package.json +2 -5
  20. package/dist/chunk-SSXER76B.js.map +0 -1
  21. /package/dist/{chunk-2IUN3S3E.js.map → chunk-2IB3IL4K.js.map} +0 -0
  22. /package/dist/{chunk-4YF56EGK.js.map → chunk-D3HHUBIL.js.map} +0 -0
  23. /package/dist/{chunk-A2VZOTCV.js.map → chunk-EX2OIOVL.js.map} +0 -0
  24. /package/dist/{chunk-KDTHRRAC.js.map → chunk-GU7IWEBC.js.map} +0 -0
  25. /package/dist/{client-6KQHCXS2.js.map → client-VDCCDEHE.js.map} +0 -0
  26. /package/dist/{env-C7YXL6C6.js.map → env-FWMZXMQK.js.map} +0 -0
  27. /package/dist/{output-RBM32FKJ.js.map → output-4H2RNBXL.js.map} +0 -0
  28. /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-2IUN3S3E.js";
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-WFWU3CHS.js";
17
+ } from "./chunk-DZUVUGEP.js";
19
18
 
20
- // ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/88b6d0c76c4dff408de4044be85791896193f2435af7c280bc59312c3fb9649c/node_modules/safe-stable-stringify/index.js
19
+ // ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/node_modules/safe-stable-stringify/index.js
21
20
  var require_safe_stable_stringify = __commonJS({
22
- "../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/88b6d0c76c4dff408de4044be85791896193f2435af7c280bc59312c3fb9649c/node_modules/safe-stable-stringify/index.js"(exports, module) {
21
+ "../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/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})` : ""} \u2014 ${retryVerdict(c.retryable)}`;
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
- // ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/88b6d0c76c4dff408de4044be85791896193f2435af7c280bc59312c3fb9649c/node_modules/safe-stable-stringify/esm/wrapper.js
1108
+ // ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/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;
@@ -2414,32 +2265,6 @@ var USD_PER_SECOND = {
2414
2265
  },
2415
2266
  // Flat $0.10/s across resolutions and modes, audio included.
2416
2267
  "alibaba/wan-2.7": { silent: 0.1, audio: 0.1 },
2417
- // 3.0 prices per resolution where 2.7 was flat, and its rate still includes audio —
2418
- // there is no separate audio SKU, so a talking clip costs what a silent one costs.
2419
- // `duration_seconds_480p|720p|1080p` on GET /api/v1/videos/models.
2420
- "alibaba/wan-3.0": {
2421
- silent: 0.1,
2422
- audio: 0.1,
2423
- byResolution: {
2424
- "480p": { silent: 0.05, audio: 0.05 },
2425
- "720p": { silent: 0.1, audio: 0.1 },
2426
- "1080p": { silent: 0.2, audio: 0.2 }
2427
- }
2428
- },
2429
- // `cents_per_video_output_second_*` — 8c, 14c and 25c. Dearer than the older Grok at
2430
- // every tier, and it reaches 1080p, which that one does not. No audio, so both columns
2431
- // are the same rate rather than one being wrong.
2432
- "x-ai/grok-imagine-video-1.5": {
2433
- silent: 0.14,
2434
- audio: 0.14,
2435
- byResolution: {
2436
- "480p": { silent: 0.08, audio: 0.08 },
2437
- "720p": { silent: 0.14, audio: 0.14 },
2438
- "1080p": { silent: 0.25, audio: 0.25 }
2439
- }
2440
- },
2441
- // One flat rate, one resolution: `cents_per_second_output: 12`. Silent.
2442
- "runway/gen-4.5": { silent: 0.12, audio: 0.12 },
2443
2268
  "alibaba/happyhorse-1.1": {
2444
2269
  silent: 0.1278,
2445
2270
  audio: 0.1278,
@@ -3305,16 +3130,6 @@ function dfsCycle(u, color, stack, reverseAdj) {
3305
3130
  stack.pop();
3306
3131
  return null;
3307
3132
  }
3308
- function nodesBeforeCheckpoint(graph, types, stopKinds) {
3309
- const all = new Set(graph.keys());
3310
- if (!stopKinds?.size) return all;
3311
- const running = /* @__PURE__ */ new Set();
3312
- for (const layer of topologicalLayers(graph)) {
3313
- if (layer.some((id) => stopKinds.has(types.get(id) ?? ""))) return running;
3314
- for (const id of layer) running.add(id);
3315
- }
3316
- return running;
3317
- }
3318
3133
 
3319
3134
  // src/engine/engine/validator.ts
3320
3135
  import { readFile as readFile2 } from "fs/promises";
@@ -3333,7 +3148,7 @@ var VEO_DURATIONS = [4, 6, 8];
3333
3148
  var KLING_DURATIONS2 = [5, 10];
3334
3149
  var KLING_NEGATIVE_PROMPT = "warped face, distorted hands, extra fingers, morphing, flicker, on-screen text, watermark, low quality";
3335
3150
  var KLING_CFG_SCALE = 0.7;
3336
- var SPEAKS_PROSE = (line, accent) => `The person speaks to camera; lip-sync follows the dialogue verbatim, with delivery and emotion carried in the wording itself (no bracketed cues).${accent ? ` The speech is in ${accent}.` : ""} Dialogue: "${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}"`;
3337
3152
  var SEEDANCE_PROFILE = {
3338
3153
  id: "seedance",
3339
3154
  dialogueDirective: SPEAKS_PROSE,
@@ -3434,14 +3249,9 @@ var GPT_IMAGE_PROFILE = {
3434
3249
  id: "gpt-image",
3435
3250
  constraintPlacement: "last",
3436
3251
  photorealCue: true,
3437
- // OpenRouter forwards `quality`; gpt-image already processes inputs at high fidelity
3438
- // automatically, so we deliberately do NOT send `input_fidelity`.
3439
- //
3440
- // No `quality` here any more: it is the one default that depends on WHICH gpt-image is
3441
- // pinned. 2.5 added `xhigh` and `max` above `high`, and the older entries would refuse
3442
- // them at validate — so callers ask for the step they want through
3443
- // `bestSupportedQuality` and get the best the chosen model actually has.
3444
- 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" }
3445
3255
  };
3446
3256
  var GEMINI_IMAGE_PROFILE = {
3447
3257
  id: "gemini",
@@ -3952,19 +3762,6 @@ function checkSlotsForNode(ctx, n, _i) {
3952
3762
  });
3953
3763
  }
3954
3764
  }
3955
- function estimateCreditsFor(canvas, registry, ids) {
3956
- let total = 0;
3957
- for (const n of canvas.nodes) {
3958
- if (!ids.has(n.id)) continue;
3959
- const def = registry.get(n.type);
3960
- if (!def?.cost) continue;
3961
- try {
3962
- total += def.cost({ params: def.params.parse(n.params ?? {}) }).credits;
3963
- } catch {
3964
- }
3965
- }
3966
- return total;
3967
- }
3968
3765
  function estimateCredits(ctx) {
3969
3766
  let total = 0;
3970
3767
  for (const n of ctx.canvas.nodes) {
@@ -4271,7 +4068,7 @@ function checkAudioTimelineTotals(ctx, audioMixNode, expected) {
4271
4068
  if (audioMixNode) mustBeFullLength.add(audioMixNode);
4272
4069
  ctx.canvas.nodes.forEach((n) => {
4273
4070
  if (n.type !== "audio_voice_convert") return;
4274
- const track = refNodeOf(ctx, n.inputs?.audio);
4071
+ const track = refNodeOf(ctx, (n.inputs ?? {}).audio);
4275
4072
  if (track?.type === "audio_timeline") mustBeFullLength.add(track.id);
4276
4073
  });
4277
4074
  for (const id of mustBeFullLength) {
@@ -4292,11 +4089,11 @@ function checkAudioTimelineTotals(ctx, audioMixNode, expected) {
4292
4089
  function checkNativeSegOverlap(ctx) {
4293
4090
  for (const conv of ctx.canvas.nodes) {
4294
4091
  if (conv.type !== "audio_voice_convert") continue;
4295
- const track = refNodeOf(ctx, conv.inputs?.audio);
4092
+ const track = refNodeOf(ctx, (conv.inputs ?? {}).audio);
4296
4093
  if (track?.type !== "audio_timeline") continue;
4297
4094
  const params = track.params;
4298
4095
  const windows = (params.tracks ?? []).map((t) => {
4299
- const extract = refNodeOf(ctx, track.inputs?.[t.slot]);
4096
+ const extract = refNodeOf(ctx, (track.inputs ?? {})[t.slot]);
4300
4097
  const len = t.duration_s ?? (extract ? ffmpegTrimSeconds(extract) : null);
4301
4098
  return len === null ? null : { slot: t.slot, start: t.start_s, end: t.start_s + len };
4302
4099
  }).filter((w) => w !== null).sort((a, b) => a.start - b.start);
@@ -4789,13 +4586,6 @@ function unwrap(schema) {
4789
4586
  }
4790
4587
 
4791
4588
  // src/engine/engine/executor.ts
4792
- function heldByCheckpoint(canvas, layer, kinds) {
4793
- if (!kinds?.size) return [];
4794
- return layer.filter((id) => {
4795
- const type = canvas.nodes.find((n) => n.id === id)?.type;
4796
- return type !== void 0 && kinds.has(type);
4797
- });
4798
- }
4799
4589
  var Engine = class {
4800
4590
  registry;
4801
4591
  client;
@@ -4823,7 +4613,7 @@ var Engine = class {
4823
4613
  async run(input, opts = {}) {
4824
4614
  const validation = await this.validateDeep(input);
4825
4615
  if (!validation.ok) throw new ValidationError(validation.issues);
4826
- if (!opts.stop_before_kinds?.size && opts.max_credits !== void 0 && validation.estimatedCredits > opts.max_credits) {
4616
+ if (opts.max_credits !== void 0 && validation.estimatedCredits > opts.max_credits) {
4827
4617
  throw new RunAbortedError(
4828
4618
  "cost_cap",
4829
4619
  `estimated ${validation.estimatedCredits} credits exceeds the ${opts.max_credits}-credit cap \u2014 nothing was billed; raise --max-credits or shrink the canvas`
@@ -4842,20 +4632,6 @@ var Engine = class {
4842
4632
  const counters = { cachedNodes: 0, totalCredits: 0 };
4843
4633
  const nodeRuns = [];
4844
4634
  const graph = this.pruneToOutput(canvas, buildGraph(canvas));
4845
- if (opts.max_credits !== void 0 && opts.stop_before_kinds?.size) {
4846
- const willRun = nodesBeforeCheckpoint(
4847
- graph,
4848
- new Map(canvas.nodes.map((n) => [n.id, n.type])),
4849
- opts.stop_before_kinds
4850
- );
4851
- const estimate = estimateCreditsFor(canvas, this.registry, willRun);
4852
- if (estimate > opts.max_credits) {
4853
- throw new RunAbortedError(
4854
- "cost_cap",
4855
- `estimated ${estimate} credits exceeds the ${opts.max_credits}-credit cap \u2014 nothing was billed; raise --max-credits or shrink the canvas`
4856
- );
4857
- }
4858
- }
4859
4635
  const needsBytes = computeNeedsLocalBytes(canvas, graph, this.registry);
4860
4636
  this.emitProgress(opts, {
4861
4637
  kind: "plan",
@@ -4900,13 +4676,6 @@ var Engine = class {
4900
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`
4901
4677
  );
4902
4678
  }
4903
- const held = heldByCheckpoint(canvas, layer, opts.stop_before_kinds);
4904
- if (held.length > 0) {
4905
- throw new RunAbortedError(
4906
- "checkpoint",
4907
- `stopped before ${held.join(", ")} \u2014 everything up to here is done and cached, so continuing this run id pays for none of it again`
4908
- );
4909
- }
4910
4679
  const settled = await mapWithConcurrency(layer, limit, (nodeId) => {
4911
4680
  if (opts.signal?.aborted) {
4912
4681
  return Promise.reject(new RunAbortedError("signal", "run aborted before node dispatch"));
@@ -6180,7 +5949,7 @@ function resolveStrategy(params) {
6180
5949
  case "text": {
6181
5950
  const p = safePathname(params.url);
6182
5951
  const isRawText = RAW_TEXT_EXTENSIONS.some((ext) => p.endsWith(ext));
6183
- return isRawText ? "direct_fetch" : "handinger";
5952
+ return isRawText ? "direct_fetch" : "firecrawl";
6184
5953
  }
6185
5954
  default: {
6186
5955
  params.expect;
@@ -6197,10 +5966,10 @@ function safePathname(rawUrl) {
6197
5966
  }
6198
5967
  var ingestNode = defineNode({
6199
5968
  id: "ingest",
6200
- version: "1.2.0",
5969
+ version: "1.3.0",
6201
5970
  category: "io",
6202
5971
  location: "local",
6203
- 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), Handinger 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.",
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.",
6204
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`).',
6205
5974
  inputs: IngestInputs,
6206
5975
  params: IngestParams,
@@ -6227,8 +5996,8 @@ function runStrategy(strategy, params, ctx) {
6227
5996
  switch (strategy) {
6228
5997
  case "direct_fetch":
6229
5998
  return execDirectFetch(params, ctx);
6230
- case "handinger":
6231
- return execHandinger(params, ctx);
5999
+ case "firecrawl":
6000
+ return execFirecrawl(params, ctx);
6232
6001
  case "yt_dlp":
6233
6002
  return execYtDlp(params, ctx);
6234
6003
  case "local_file":
@@ -6266,9 +6035,8 @@ async function ingestImageUrl(url, ctx) {
6266
6035
  } catch (e) {
6267
6036
  throw localExecError(ctx, `${url}: ${e.message}`);
6268
6037
  }
6269
- if (normalized.rasterizedFrom) {
6270
- ctx.log(`ingest: normalized ${normalized.rasterizedFrom} URL -> PNG (${normalized.bytes.length}B)`);
6271
- }
6038
+ const note = normalizationNote(normalized);
6039
+ if (note) ctx.log(`ingest: ${url} ${note}`);
6272
6040
  return uploadAndIngest({
6273
6041
  bytes: normalized.bytes,
6274
6042
  kind: "image",
@@ -6282,11 +6050,11 @@ async function ingestImageUrl(url, ctx) {
6282
6050
  ctx
6283
6051
  });
6284
6052
  }
6285
- async function execHandinger(params, ctx) {
6053
+ async function execFirecrawl(params, ctx) {
6286
6054
  const result = await callBackendExec({
6287
6055
  nodeType: "ingest",
6288
6056
  nodeVersion: ingestNode.version,
6289
- params: { strategy: "handinger", url: params.url },
6057
+ params: { strategy: "firecrawl", url: params.url },
6290
6058
  inputs: {},
6291
6059
  outputKinds: { asset: "text" },
6292
6060
  ctx
@@ -6378,23 +6146,69 @@ async function rasterizeSvgToPng(bytes) {
6378
6146
  return await sharp(bytes, { density }).png({ force: true, palette: false }).toBuffer();
6379
6147
  }
6380
6148
  var MODEL_SAFE_IMAGE_MIMES = /* @__PURE__ */ new Set(["image/jpeg", "image/png", "image/gif", "image/webp"]);
6381
- async function toModelSafeImage(bytes) {
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
+ };
6382
6184
  const safe = sniffImageMime(bytes);
6383
6185
  if (safe && MODEL_SAFE_IMAGE_MIMES.has(safe)) {
6384
- return { bytes, mime: safe };
6186
+ return await fit(bytes, safe);
6385
6187
  }
6386
6188
  if (sniffSvg(bytes)) {
6387
- return { bytes: await rasterizeSvgToPng(bytes), mime: "image/png", rasterizedFrom: "svg" };
6189
+ return await fit(await rasterizeSvgToPng(bytes), "image/png", "svg");
6388
6190
  }
6389
6191
  const { default: sharp } = await import("sharp");
6192
+ let png;
6193
+ let format;
6390
6194
  try {
6391
6195
  const img = sharp(bytes);
6392
- const format = (await img.metadata()).format;
6393
- const png = await img.png({ force: true }).toBuffer();
6394
- return { bytes: png, mime: "image/png", rasterizedFrom: format ?? "unknown" };
6196
+ format = (await img.metadata()).format ?? "unknown";
6197
+ png = await img.png({ force: true }).toBuffer();
6395
6198
  } catch (e) {
6396
6199
  throw new Error(`bytes are not a decodable image (${e.message})`);
6397
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;
6398
6212
  }
6399
6213
  function hasAscii(buf, offset, sig) {
6400
6214
  return buf.length >= offset + sig.length && buf.toString("ascii", offset, offset + sig.length) === sig;
@@ -6524,7 +6338,8 @@ async function execLocalFile(params, ctx) {
6524
6338
  outBytes = normalized.bytes;
6525
6339
  outMime = normalized.mime;
6526
6340
  rasterizedFrom = normalized.rasterizedFrom;
6527
- if (rasterizedFrom) ctx.log(`ingest: normalized ${rasterizedFrom} -> PNG (${outBytes.length}B)`);
6341
+ const note = normalizationNote(normalized);
6342
+ if (note) ctx.log(`ingest: ${note}`);
6528
6343
  }
6529
6344
  const durationMs = probeVideoDurationMs(params.expect, outBytes, ctx);
6530
6345
  const ref = await uploadAndIngest({
@@ -7496,24 +7311,6 @@ import { execFile as execFile3 } from "child_process";
7496
7311
  import { readFile as readFile9 } from "fs/promises";
7497
7312
  import path12 from "path";
7498
7313
  import { promisify as promisify3 } from "util";
7499
-
7500
- // src/engine/nodes/local/lib/caption-style.ts
7501
- var TERMINAL = /[.,;:!?…]$/;
7502
- function captionTextsFrom(html) {
7503
- const cards = html.match(/<div[^>]*class="[^"]*\bcaption\b[^"]*"[\s\S]*?<\/div>/g) ?? [];
7504
- return cards.map(
7505
- (card) => card.replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim()
7506
- ).filter((text) => text.length > 0);
7507
- }
7508
- function fragmentedCaptions(texts) {
7509
- if (texts.length < 2) return null;
7510
- const fragments = texts.slice(0, -1).filter((t) => !TERMINAL.test(t));
7511
- if (fragments.length === 0) return null;
7512
- const example = fragments[0];
7513
- 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.`;
7514
- }
7515
-
7516
- // src/engine/nodes/local/lib/hyperframe-check.ts
7517
7314
  var execFileAsync = promisify3(execFile3);
7518
7315
  var NEVER_BLOCK = [
7519
7316
  /contrast/i,
@@ -7582,74 +7379,6 @@ function classifyCheckOutput(lintRaw, inspectRaw) {
7582
7379
  warnings: findings.filter((f) => f.severity === "warning")
7583
7380
  };
7584
7381
  }
7585
- var GATE_SECTIONS = ["lint", "runtime", "layout", "motion", "contrast"];
7586
- function buildCheckArgs(dir, samples) {
7587
- return ["hyperframes", "check", dir, "--json", "--samples", String(samples), "--at-transitions"];
7588
- }
7589
- var PROMOTE_TO_BLOCKING = /GSAP target [^ ]+ not found|no deterministic font mapping/i;
7590
- function toGateFinding(section, entry) {
7591
- const f = entry;
7592
- const code = typeof f.code === "string" ? f.code : "unknown";
7593
- const message2 = typeof f.message === "string" ? f.message : "";
7594
- const isError = f.severity === "error" || PROMOTE_TO_BLOCKING.test(message2);
7595
- const fatal = PROMOTE_TO_BLOCKING.test(message2);
7596
- return {
7597
- source: section,
7598
- code,
7599
- message: message2,
7600
- severity: fatal || isError && !isAdvisory(code, message2) ? "blocking" : "warning"
7601
- };
7602
- }
7603
- function classifyGateOutput(raw) {
7604
- const parsed = parseCheckJson(raw);
7605
- const findings = GATE_SECTIONS.flatMap((section) => {
7606
- const block = parsed?.[section];
7607
- const entries = Array.isArray(block?.findings) ? block.findings : [];
7608
- return entries.map((entry) => toGateFinding(section, entry));
7609
- });
7610
- return {
7611
- blocking: findings.filter((f) => f.severity === "blocking"),
7612
- warnings: findings.filter((f) => f.severity === "warning")
7613
- };
7614
- }
7615
- function summariseWarnings(warnings) {
7616
- const groups = /* @__PURE__ */ new Map();
7617
- for (const w of warnings) {
7618
- const key = `${w.source}/${w.code}`;
7619
- const seen = groups.get(key);
7620
- if (seen) seen.count += 1;
7621
- else groups.set(key, { count: 1, message: w.message });
7622
- }
7623
- return [...groups.entries()].sort((a, b) => b[1].count - a[1].count).map(([key, g]) => `${g.count}\xD7 [${key}] ${g.message}`);
7624
- }
7625
- var FINDING_ADVICE = {
7626
- 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.",
7627
- console_error: "the composition threw in the browser, so whatever that script was driving did not happen.",
7628
- page_error: "a script failed to evaluate \u2014 animations and any JS-driven layout in this composition did not run.",
7629
- http_error: "an asset 404ed at render time; it will be missing from the frame, not substituted.",
7630
- 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."
7631
- };
7632
- function explainGateFinding(finding) {
7633
- const advice = FINDING_ADVICE[finding.code];
7634
- return advice ? `${finding.message} \u2014 ${advice}` : finding.message;
7635
- }
7636
- function condenseFrameDescriptions(markdown) {
7637
- const lines = [];
7638
- let frame = null;
7639
- for (const line of markdown.split("\n")) {
7640
- const heading = line.match(/^## (.+)$/);
7641
- if (heading) {
7642
- frame = heading[1] ?? null;
7643
- continue;
7644
- }
7645
- const text = line.trim();
7646
- if (frame && text) {
7647
- lines.push(`${frame}: ${text}`);
7648
- frame = null;
7649
- }
7650
- }
7651
- return lines;
7652
- }
7653
7382
  function buildLintArgs(dir) {
7654
7383
  return ["hyperframes", "lint", dir, "--json"];
7655
7384
  }
@@ -7681,13 +7410,8 @@ async function lintStructuralIssues(dir, timeoutMs) {
7681
7410
  function buildInspectArgs(dir, samples) {
7682
7411
  return ["hyperframes", "inspect", dir, "--json", "--samples", String(samples)];
7683
7412
  }
7684
- function buildSnapshotArgs(dir, frames, vision) {
7685
- const base = ["hyperframes", "snapshot", dir, "--frames", String(frames)];
7686
- return vision ? [
7687
- ...base,
7688
- "--describe",
7689
- "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?"
7690
- ] : [...base, "--describe", "false"];
7413
+ function buildSnapshotArgs(dir, frames) {
7414
+ return ["hyperframes", "snapshot", dir, "--frames", String(frames), "--describe", "false"];
7691
7415
  }
7692
7416
  function usesNestedCompositions(indexHtml) {
7693
7417
  const withoutComments = indexHtml.replace(/<!--[\s\S]*?-->/g, "");
@@ -7716,66 +7440,44 @@ async function runSnapshotSmoke(args, timeoutMs) {
7716
7440
  return { ok: false, unavailable: false, message: (err.stderr || err.message || "snapshot failed").slice(0, 800) };
7717
7441
  }
7718
7442
  }
7719
- async function runNestedCompositionSmoke(opts) {
7720
- const { dir, nodeId, ctx, timeoutMs, samples } = opts;
7721
- const indexHtml = await readFile9(path12.join(dir, "index.html"), "utf-8").catch(() => "");
7722
- if (!indexHtml || !usesNestedCompositions(indexHtml)) return;
7723
- const snap = await runSnapshotSmoke(
7724
- buildSnapshotArgs(dir, Math.min(samples, 3), hasFrameVisionKey()),
7725
- Math.max(timeoutMs, 12e4)
7726
- );
7727
- if (snap.unavailable) {
7728
- ctx.log(`${nodeId}: hyperframes snapshot unavailable \u2014 skipping nested-composition smoke test`);
7729
- return;
7730
- }
7731
- if (!snap.ok) {
7732
- throw new Error(
7733
- `${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).
7734
- ${snap.message}`
7735
- );
7736
- }
7737
- const described = await readFile9(path12.join(dir, "snapshots", "descriptions.md"), "utf-8").catch(() => "");
7738
- for (const line of condenseFrameDescriptions(described)) {
7739
- ctx.log(`${nodeId}: frame check \u2014 ${line}`);
7740
- }
7741
- ctx.log(`${nodeId}: nested-composition smoke test passed`);
7742
- }
7743
- async function runGate(dir, timeoutMs, samples) {
7744
- const checkRaw = await runOne(buildCheckArgs(dir, samples), timeoutMs);
7745
- if (checkRaw !== null) return classifyGateOutput(checkRaw);
7443
+ async function runHyperframesCheck(opts) {
7444
+ const { dir, nodeId, ctx, timeoutMs, samples = 5 } = opts;
7746
7445
  const [lintRaw, inspectRaw] = await Promise.all([
7747
7446
  runOne(buildLintArgs(dir), timeoutMs),
7748
7447
  runOne(buildInspectArgs(dir, samples), timeoutMs)
7749
7448
  ]);
7750
- if (lintRaw === null && inspectRaw === null) return null;
7751
- return classifyCheckOutput(lintRaw ?? "", inspectRaw ?? "");
7752
- }
7753
- async function runHyperframesCheck(opts) {
7754
- const { dir, nodeId, ctx, timeoutMs, samples = 5 } = opts;
7755
- const gate = await runGate(dir, timeoutMs, samples);
7756
- if (gate === null) {
7757
- 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`);
7758
7451
  return;
7759
7452
  }
7760
- const { blocking, warnings } = gate;
7761
- for (const line of summariseWarnings(warnings)) {
7762
- ctx.log(`${nodeId}: hyperframe check warning ${line}`);
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}`);
7763
7456
  }
7764
7457
  if (blocking.length > 0) {
7765
- const seen = /* @__PURE__ */ new Map();
7766
- for (const b of blocking) seen.set(`${b.source}/${b.code}/${b.message}`, b);
7767
- const detail = [...seen.values()].map((b) => `\u2022 [${b.source}/${b.code}] ${explainGateFinding(b)}`).join("\n");
7768
- throw new Error(
7769
- `${nodeId}: pre-render check failed (${blocking.length} blocking, ${seen.size} distinct)
7770
- ${detail}`
7771
- );
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}`);
7461
+ }
7462
+ let indexHtml = "";
7463
+ try {
7464
+ indexHtml = await readFile9(path12.join(dir, "index.html"), "utf-8");
7465
+ } catch {
7466
+ indexHtml = "";
7772
7467
  }
7773
- const composition = await readFile9(path12.join(dir, "index.html"), "utf-8").catch(() => "");
7774
- const captionComplaint = fragmentedCaptions(captionTextsFrom(composition));
7775
- if (captionComplaint) {
7776
- ctx.log(`${nodeId}: caption style \u2014 ${captionComplaint}`);
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
+ }
7777
7480
  }
7778
- await runNestedCompositionSmoke({ dir, nodeId, ctx, timeoutMs, samples });
7779
7481
  ctx.log(`${nodeId}: pre-render check passed (${warnings.length} warning${warnings.length === 1 ? "" : "s"})`);
7780
7482
  }
7781
7483
 
@@ -9282,14 +8984,7 @@ async function tryExtractAudio(inputs, ctx) {
9282
8984
  import { z as z34 } from "zod";
9283
8985
  var voiceSelectNode = delegated({
9284
8986
  id: "voice_select",
9285
- // 1.1.0: a pinned voice used to come back as an inline `{ kind, text }` with no sha256
9286
- // and no mime, which every consumer taking the id as an INPUT rejected —
9287
- // `audio_voice_convert` died on `voice_ref.sha256: expected string, received undefined`.
9288
- // The backend now uploads it as a real asset like the ranked branch, and the version has
9289
- // to move with it: the cache key is built from this string, so entries written under
9290
- // 1.0.0 kept replaying the broken shape long after the fix was deployed. A node whose
9291
- // OUTPUT shape changes has to bump, not only one whose behaviour does.
9292
- version: "1.1.0",
8987
+ version: "1.0.0",
9293
8988
  category: "audio",
9294
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.',
9295
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.',
@@ -9300,11 +8995,6 @@ var voiceSelectNode = delegated({
9300
8995
  age: z34.string().optional(),
9301
8996
  accent: z34.string().optional(),
9302
8997
  language: z34.string().optional(),
9303
- /**
9304
- * A voice already chosen — an Avatar's pinned one. Given, nothing is cast: the
9305
- * ranking is skipped and this id is emitted as-is.
9306
- */
9307
- voice_id: z34.string().min(1).max(120).optional(),
9308
8998
  limit: z34.number().int().min(1).max(20).optional()
9309
8999
  }).strict(),
9310
9000
  outputs: z34.object({ voice_id: TextRef, candidates: JsonRef }).strict(),
@@ -9467,7 +9157,6 @@ export {
9467
9157
  describeFailureReason,
9468
9158
  AD_FORMAT_PLATFORMS,
9469
9159
  platformFormats,
9470
- frameRealismDirection,
9471
9160
  SEEDANCE_DURATIONS,
9472
9161
  ELEVENLABS_MAX_MUSIC_LENGTH_MS,
9473
9162
  IMAGE_GENERATE_MODELS,
@@ -9479,14 +9168,10 @@ export {
9479
9168
  MODEL_REGISTRY,
9480
9169
  supportsParam,
9481
9170
  DEFAULT_CLIP_DURATION_S,
9482
- SPOKEN_WORDS_PER_SECOND,
9483
- spokenWordsIn,
9484
9171
  nearestClipDuration,
9485
- splitScriptFor,
9486
9172
  promptMaxLength,
9487
9173
  maxInputSlot,
9488
9174
  maxInputReferences,
9489
- bestSupportedQuality,
9490
9175
  nearestSupportedAspectRatio,
9491
9176
  nearestSupportedImageSize,
9492
9177
  estimateVideoCredits,
@@ -9527,4 +9212,4 @@ export {
9527
9212
  defaultRegistry,
9528
9213
  createEngineFromEnv
9529
9214
  };
9530
- //# sourceMappingURL=chunk-SSXER76B.js.map
9215
+ //# sourceMappingURL=chunk-EFETQHTX.js.map