@koda-sl/baker-cli 0.294.0-dev.97421c92b → 0.294.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.
Files changed (28) hide show
  1. package/README.md +19 -75
  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-A2VZOTCV.js → chunk-EX2OIOVL.js} +3 -3
  8. package/dist/{chunk-KDTHRRAC.js → chunk-GU7IWEBC.js} +3 -3
  9. package/dist/{chunk-LTXNRRXW.js → chunk-NNQDWCFG.js} +102 -282
  10. package/dist/chunk-NNQDWCFG.js.map +1 -0
  11. package/dist/cli.js +1264 -2930
  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 +1 -1
  20. package/dist/chunk-LTXNRRXW.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
+ // ../../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
- "../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/88b6d0c76c4dff408de4044be85791896193f2435af7c280bc59312c3fb9649c/node_modules/safe-stable-stringify/index.js"(exports, module) {
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();
@@ -1034,17 +1033,12 @@ function failureDetail(reason) {
1034
1033
  }
1035
1034
  return { message: describeFailureReason(reason) };
1036
1035
  }
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
1036
  function describeCause(c) {
1043
1037
  switch (c.kind) {
1044
1038
  case "local":
1045
1039
  return c.cause instanceof Error ? c.cause.message : String(c.cause);
1046
1040
  case "remote":
1047
- return `[${c.code}] ${c.message}${c.provider ? ` (${c.provider})` : ""} \u2014 ${retryVerdict(c.retryable)}`;
1041
+ return `[${c.code}] ${c.message}${c.provider ? ` (${c.provider})` : ""}`;
1048
1042
  case "timeout":
1049
1043
  return `timeout${c.provider ? ` (${c.provider})` : ""}`;
1050
1044
  case "network":
@@ -1098,7 +1092,7 @@ function resolveAdaptFormats(params) {
1098
1092
  return params.formats ?? [];
1099
1093
  }
1100
1094
 
1101
- // ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/88b6d0c76c4dff408de4044be85791896193f2435af7c280bc59312c3fb9649c/node_modules/safe-stable-stringify/esm/wrapper.js
1095
+ // ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/esm/wrapper.js
1102
1096
  var import__ = __toESM(require_safe_stable_stringify(), 1);
1103
1097
  var configure = import__.default.configure;
1104
1098
  var wrapper_default = import__.default;
@@ -1139,14 +1133,6 @@ function normalizeForCanonical(value) {
1139
1133
  return void 0;
1140
1134
  }
1141
1135
 
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
1136
  // ../canvas-contract/src/registry.ts
1151
1137
  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
1138
  var OPENROUTER_IMAGE_AR_EXTREME = [...OPENROUTER_IMAGE_AR, "1:4", "4:1", "1:8", "8:1"];
@@ -1166,7 +1152,6 @@ var OPENROUTER_IMAGE_SIZES = ["0.5K", ...OPENROUTER_IMAGE_SIZE];
1166
1152
  var OPENROUTER_IMAGE_SIZE_EXTENDED = OPENROUTER_IMAGE_SIZES;
1167
1153
  var GEMINI_LITE_IMAGE_SIZE = ["1K"];
1168
1154
  var OPENROUTER_IMAGE_QUALITY = ["auto", "low", "medium", "high"];
1169
- var OPENROUTER_IMAGE_QUALITY_25 = ["auto", "low", "medium", "high", "xhigh", "max"];
1170
1155
  var RECRAFT_IMAGE_AR = ["1:1", "4:3", "3:4", "16:9", "9:16"];
1171
1156
  var SEEDANCE_DURATIONS = [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1172
1157
  var SEEDANCE_25_DURATIONS = [
@@ -1229,7 +1214,6 @@ var REPLICATE_VIDEO_MIMES = ["video/mp4", "video/webm", "video/quicktime"];
1229
1214
  var DECONSTRUCT_VIDEO_MIMES = ["video/mp4", "video/webm", "video/quicktime"];
1230
1215
  var REPLICATE_AUDIO_MIMES = ["audio/wav", "audio/mpeg", "audio/mp3"];
1231
1216
  var IMAGE_GENERATE_MODELS = [
1232
- "openai/gpt-image-2.5-sunburst",
1233
1217
  "openai/gpt-image-2",
1234
1218
  "openai/gpt-5.4-image-2",
1235
1219
  "google/gemini-3.1-flash-image-preview",
@@ -1375,20 +1359,6 @@ var MODEL_REGISTRY = {
1375
1359
  // images as a side effect. Notable capability gaps vs the Gemini entries:
1376
1360
  // no `image_size` (OpenAI picks the pixel dimensions from the ratio, and a
1377
1361
  // `resolution` is silently ignored) and the narrower `..._AR_GPT` ratio set.
1378
- "openai/gpt-image-2.5-sunburst": {
1379
- // Identical to the gpt-image-2 entry below except for the quality ladder — the
1380
- // ratio set, the 16-reference ceiling, the missing `image_size` and the reasons
1381
- // `background` / `output_compression` stay undeclared all carry over unchanged.
1382
- label: "OpenAI GPT Image 2.5",
1383
- inputs: [],
1384
- optional_inputs: [{ kind: "image", mimes: OPENROUTER_IMAGE_MIMES, max: GPT_IMAGE_MAX_REFERENCES }],
1385
- required: ["prompt"],
1386
- params: {
1387
- prompt: { kind: "string", maxLength: GPT_IMAGE_PROMPT_MAX },
1388
- aspect_ratio: { kind: "string", enum: OPENROUTER_IMAGE_AR_GPT },
1389
- quality: { kind: "string", enum: OPENROUTER_IMAGE_QUALITY_25 }
1390
- }
1391
- },
1392
1362
  "openai/gpt-image-2": {
1393
1363
  // Its live endpoint also advertises `background` (auto|opaque) and
1394
1364
  // `output_compression` (0-100), which we deliberately do not declare. Not an
@@ -2063,19 +2033,6 @@ function maxInputSlot(kind, model, inputKind) {
2063
2033
  function maxInputReferences(kind, model) {
2064
2034
  return maxInputSlot(kind, model, "image");
2065
2035
  }
2066
- function supportedQualities(kind, model) {
2067
- const schema = MODEL_REGISTRY[kind]?.[model]?.params.quality;
2068
- return schema?.kind === "string" ? schema.enum : void 0;
2069
- }
2070
- function bestSupportedQuality(kind, model, want) {
2071
- const supported = supportedQualities(kind, model);
2072
- if (!supported) return void 0;
2073
- if (supported.includes(want)) return want;
2074
- const ladder = supported.filter((step) => step !== "auto");
2075
- const ceiling = ladder.indexOf(want);
2076
- const reachable = ceiling === -1 ? ladder : ladder.slice(0, ceiling);
2077
- return reachable[reachable.length - 1];
2078
- }
2079
2036
  function supportedAspectRatios(kind, model) {
2080
2037
  const schema = MODEL_REGISTRY[kind]?.[model]?.params.aspect_ratio;
2081
2038
  return schema?.kind === "string" ? schema.enum : void 0;
@@ -3160,16 +3117,6 @@ function dfsCycle(u, color, stack, reverseAdj) {
3160
3117
  stack.pop();
3161
3118
  return null;
3162
3119
  }
3163
- function nodesBeforeCheckpoint(graph, types, stopKinds) {
3164
- const all = new Set(graph.keys());
3165
- if (!stopKinds?.size) return all;
3166
- const running = /* @__PURE__ */ new Set();
3167
- for (const layer of topologicalLayers(graph)) {
3168
- if (layer.some((id) => stopKinds.has(types.get(id) ?? ""))) return running;
3169
- for (const id of layer) running.add(id);
3170
- }
3171
- return running;
3172
- }
3173
3120
 
3174
3121
  // src/engine/engine/validator.ts
3175
3122
  import { readFile as readFile2 } from "fs/promises";
@@ -3188,7 +3135,7 @@ var VEO_DURATIONS = [4, 6, 8];
3188
3135
  var KLING_DURATIONS2 = [5, 10];
3189
3136
  var KLING_NEGATIVE_PROMPT = "warped face, distorted hands, extra fingers, morphing, flicker, on-screen text, watermark, low quality";
3190
3137
  var KLING_CFG_SCALE = 0.7;
3191
- 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}"`;
3138
+ 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}"`;
3192
3139
  var SEEDANCE_PROFILE = {
3193
3140
  id: "seedance",
3194
3141
  dialogueDirective: SPEAKS_PROSE,
@@ -3289,14 +3236,9 @@ var GPT_IMAGE_PROFILE = {
3289
3236
  id: "gpt-image",
3290
3237
  constraintPlacement: "last",
3291
3238
  photorealCue: true,
3292
- // OpenRouter forwards `quality`; gpt-image already processes inputs at high fidelity
3293
- // automatically, so we deliberately do NOT send `input_fidelity`.
3294
- //
3295
- // No `quality` here any more: it is the one default that depends on WHICH gpt-image is
3296
- // pinned. 2.5 added `xhigh` and `max` above `high`, and the older entries would refuse
3297
- // them at validate — so callers ask for the step they want through
3298
- // `bestSupportedQuality` and get the best the chosen model actually has.
3299
- paramDefaults: {}
3239
+ // OpenRouter forwards `quality`; gpt-image-2 already processes inputs at high
3240
+ // fidelity automatically, so we deliberately do NOT send `input_fidelity`.
3241
+ paramDefaults: { quality: "high" }
3300
3242
  };
3301
3243
  var GEMINI_IMAGE_PROFILE = {
3302
3244
  id: "gemini",
@@ -3807,19 +3749,6 @@ function checkSlotsForNode(ctx, n, _i) {
3807
3749
  });
3808
3750
  }
3809
3751
  }
3810
- function estimateCreditsFor(canvas, registry, ids) {
3811
- let total = 0;
3812
- for (const n of canvas.nodes) {
3813
- if (!ids.has(n.id)) continue;
3814
- const def = registry.get(n.type);
3815
- if (!def?.cost) continue;
3816
- try {
3817
- total += def.cost({ params: def.params.parse(n.params ?? {}) }).credits;
3818
- } catch {
3819
- }
3820
- }
3821
- return total;
3822
- }
3823
3752
  function estimateCredits(ctx) {
3824
3753
  let total = 0;
3825
3754
  for (const n of ctx.canvas.nodes) {
@@ -4126,7 +4055,7 @@ function checkAudioTimelineTotals(ctx, audioMixNode, expected) {
4126
4055
  if (audioMixNode) mustBeFullLength.add(audioMixNode);
4127
4056
  ctx.canvas.nodes.forEach((n) => {
4128
4057
  if (n.type !== "audio_voice_convert") return;
4129
- const track = refNodeOf(ctx, n.inputs?.audio);
4058
+ const track = refNodeOf(ctx, (n.inputs ?? {}).audio);
4130
4059
  if (track?.type === "audio_timeline") mustBeFullLength.add(track.id);
4131
4060
  });
4132
4061
  for (const id of mustBeFullLength) {
@@ -4147,11 +4076,11 @@ function checkAudioTimelineTotals(ctx, audioMixNode, expected) {
4147
4076
  function checkNativeSegOverlap(ctx) {
4148
4077
  for (const conv of ctx.canvas.nodes) {
4149
4078
  if (conv.type !== "audio_voice_convert") continue;
4150
- const track = refNodeOf(ctx, conv.inputs?.audio);
4079
+ const track = refNodeOf(ctx, (conv.inputs ?? {}).audio);
4151
4080
  if (track?.type !== "audio_timeline") continue;
4152
4081
  const params = track.params;
4153
4082
  const windows = (params.tracks ?? []).map((t) => {
4154
- const extract = refNodeOf(ctx, track.inputs?.[t.slot]);
4083
+ const extract = refNodeOf(ctx, (track.inputs ?? {})[t.slot]);
4155
4084
  const len = t.duration_s ?? (extract ? ffmpegTrimSeconds(extract) : null);
4156
4085
  return len === null ? null : { slot: t.slot, start: t.start_s, end: t.start_s + len };
4157
4086
  }).filter((w) => w !== null).sort((a, b) => a.start - b.start);
@@ -4644,13 +4573,6 @@ function unwrap(schema) {
4644
4573
  }
4645
4574
 
4646
4575
  // src/engine/engine/executor.ts
4647
- function heldByCheckpoint(canvas, layer, kinds) {
4648
- if (!kinds?.size) return [];
4649
- return layer.filter((id) => {
4650
- const type = canvas.nodes.find((n) => n.id === id)?.type;
4651
- return type !== void 0 && kinds.has(type);
4652
- });
4653
- }
4654
4576
  var Engine = class {
4655
4577
  registry;
4656
4578
  client;
@@ -4678,7 +4600,7 @@ var Engine = class {
4678
4600
  async run(input, opts = {}) {
4679
4601
  const validation = await this.validateDeep(input);
4680
4602
  if (!validation.ok) throw new ValidationError(validation.issues);
4681
- if (!opts.stop_before_kinds?.size && opts.max_credits !== void 0 && validation.estimatedCredits > opts.max_credits) {
4603
+ if (opts.max_credits !== void 0 && validation.estimatedCredits > opts.max_credits) {
4682
4604
  throw new RunAbortedError(
4683
4605
  "cost_cap",
4684
4606
  `estimated ${validation.estimatedCredits} credits exceeds the ${opts.max_credits}-credit cap \u2014 nothing was billed; raise --max-credits or shrink the canvas`
@@ -4697,20 +4619,6 @@ var Engine = class {
4697
4619
  const counters = { cachedNodes: 0, totalCredits: 0 };
4698
4620
  const nodeRuns = [];
4699
4621
  const graph = this.pruneToOutput(canvas, buildGraph(canvas));
4700
- if (opts.max_credits !== void 0 && opts.stop_before_kinds?.size) {
4701
- const willRun = nodesBeforeCheckpoint(
4702
- graph,
4703
- new Map(canvas.nodes.map((n) => [n.id, n.type])),
4704
- opts.stop_before_kinds
4705
- );
4706
- const estimate = estimateCreditsFor(canvas, this.registry, willRun);
4707
- if (estimate > opts.max_credits) {
4708
- throw new RunAbortedError(
4709
- "cost_cap",
4710
- `estimated ${estimate} credits exceeds the ${opts.max_credits}-credit cap \u2014 nothing was billed; raise --max-credits or shrink the canvas`
4711
- );
4712
- }
4713
- }
4714
4622
  const needsBytes = computeNeedsLocalBytes(canvas, graph, this.registry);
4715
4623
  this.emitProgress(opts, {
4716
4624
  kind: "plan",
@@ -4755,13 +4663,6 @@ var Engine = class {
4755
4663
  `spent ${counters.totalCredits} credits, over the ${opts.max_credits}-credit cap \u2014 completed nodes are cached; raise --max-credits to continue where this stopped`
4756
4664
  );
4757
4665
  }
4758
- const held = heldByCheckpoint(canvas, layer, opts.stop_before_kinds);
4759
- if (held.length > 0) {
4760
- throw new RunAbortedError(
4761
- "checkpoint",
4762
- `stopped before ${held.join(", ")} \u2014 everything up to here is done and cached, so continuing this run id pays for none of it again`
4763
- );
4764
- }
4765
4666
  const settled = await mapWithConcurrency(layer, limit, (nodeId) => {
4766
4667
  if (opts.signal?.aborted) {
4767
4668
  return Promise.reject(new RunAbortedError("signal", "run aborted before node dispatch"));
@@ -6121,9 +6022,8 @@ async function ingestImageUrl(url, ctx) {
6121
6022
  } catch (e) {
6122
6023
  throw localExecError(ctx, `${url}: ${e.message}`);
6123
6024
  }
6124
- if (normalized.rasterizedFrom) {
6125
- ctx.log(`ingest: normalized ${normalized.rasterizedFrom} URL -> PNG (${normalized.bytes.length}B)`);
6126
- }
6025
+ const note = normalizationNote(normalized);
6026
+ if (note) ctx.log(`ingest: ${url} ${note}`);
6127
6027
  return uploadAndIngest({
6128
6028
  bytes: normalized.bytes,
6129
6029
  kind: "image",
@@ -6233,23 +6133,69 @@ async function rasterizeSvgToPng(bytes) {
6233
6133
  return await sharp(bytes, { density }).png({ force: true, palette: false }).toBuffer();
6234
6134
  }
6235
6135
  var MODEL_SAFE_IMAGE_MIMES = /* @__PURE__ */ new Set(["image/jpeg", "image/png", "image/gif", "image/webp"]);
6236
- async function toModelSafeImage(bytes) {
6136
+ var MAX_MODEL_REFERENCE_BYTES = 30 * 1024 * 1024;
6137
+ async function fitWithinReferenceLimit(bytes, mime, maxBytes) {
6138
+ if (bytes.byteLength <= maxBytes) return { bytes, mime };
6139
+ const { default: sharp } = await import("sharp");
6140
+ const read = () => sharp(bytes, { animated: true });
6141
+ const meta = await read().metadata();
6142
+ const animated = (meta.pages ?? 1) > 1;
6143
+ const keepAlpha = meta.hasAlpha === true;
6144
+ const width = meta.width ?? 0;
6145
+ const encode = async (scale2) => {
6146
+ let pipeline2 = read();
6147
+ if (scale2 < 1 && width > 0) pipeline2 = pipeline2.resize({ width: Math.max(256, Math.round(width * scale2)) });
6148
+ if (animated && mime === "image/gif") return { bytes: await pipeline2.gif().toBuffer(), mime: "image/gif" };
6149
+ if (animated || keepAlpha) return { bytes: await pipeline2.webp({ quality: 90 }).toBuffer(), mime: "image/webp" };
6150
+ return { bytes: await pipeline2.jpeg({ quality: 90, mozjpeg: true }).toBuffer(), mime: "image/jpeg" };
6151
+ };
6152
+ let out = await encode(1);
6153
+ let scale = 1;
6154
+ for (let pass = 0; pass < 4 && out.bytes.byteLength > maxBytes; pass++) {
6155
+ scale *= Math.min(0.9, Math.sqrt(maxBytes / out.bytes.byteLength));
6156
+ out = await encode(scale);
6157
+ }
6158
+ return out;
6159
+ }
6160
+ async function toModelSafeImage(bytes, opts) {
6161
+ const maxBytes = opts?.maxBytes ?? MAX_MODEL_REFERENCE_BYTES;
6162
+ const fit = async (candidate, mime, rasterizedFrom) => {
6163
+ const fitted = await fitWithinReferenceLimit(candidate, mime, maxBytes);
6164
+ return {
6165
+ bytes: fitted.bytes,
6166
+ mime: fitted.mime,
6167
+ ...rasterizedFrom ? { rasterizedFrom } : {},
6168
+ ...fitted.bytes === candidate ? {} : { shrunkFromBytes: candidate.byteLength }
6169
+ };
6170
+ };
6237
6171
  const safe = sniffImageMime(bytes);
6238
6172
  if (safe && MODEL_SAFE_IMAGE_MIMES.has(safe)) {
6239
- return { bytes, mime: safe };
6173
+ return await fit(bytes, safe);
6240
6174
  }
6241
6175
  if (sniffSvg(bytes)) {
6242
- return { bytes: await rasterizeSvgToPng(bytes), mime: "image/png", rasterizedFrom: "svg" };
6176
+ return await fit(await rasterizeSvgToPng(bytes), "image/png", "svg");
6243
6177
  }
6244
6178
  const { default: sharp } = await import("sharp");
6179
+ let png;
6180
+ let format;
6245
6181
  try {
6246
6182
  const img = sharp(bytes);
6247
- const format = (await img.metadata()).format;
6248
- const png = await img.png({ force: true }).toBuffer();
6249
- return { bytes: png, mime: "image/png", rasterizedFrom: format ?? "unknown" };
6183
+ format = (await img.metadata()).format ?? "unknown";
6184
+ png = await img.png({ force: true }).toBuffer();
6250
6185
  } catch (e) {
6251
6186
  throw new Error(`bytes are not a decodable image (${e.message})`);
6252
6187
  }
6188
+ return await fit(png, "image/png", format);
6189
+ }
6190
+ function normalizationNote(image) {
6191
+ const parts = [];
6192
+ if (image.rasterizedFrom) parts.push(`normalized ${image.rasterizedFrom} -> ${image.mime}`);
6193
+ if (image.shrunkFromBytes !== void 0) {
6194
+ parts.push(
6195
+ `fitted ${image.shrunkFromBytes}B -> ${image.bytes.length}B as ${image.mime} (providers refuse a reference over ${MAX_MODEL_REFERENCE_BYTES}B)`
6196
+ );
6197
+ }
6198
+ return parts.length > 0 ? parts.join("; ") : null;
6253
6199
  }
6254
6200
  function hasAscii(buf, offset, sig) {
6255
6201
  return buf.length >= offset + sig.length && buf.toString("ascii", offset, offset + sig.length) === sig;
@@ -6379,7 +6325,8 @@ async function execLocalFile(params, ctx) {
6379
6325
  outBytes = normalized.bytes;
6380
6326
  outMime = normalized.mime;
6381
6327
  rasterizedFrom = normalized.rasterizedFrom;
6382
- if (rasterizedFrom) ctx.log(`ingest: normalized ${rasterizedFrom} -> PNG (${outBytes.length}B)`);
6328
+ const note = normalizationNote(normalized);
6329
+ if (note) ctx.log(`ingest: ${note}`);
6383
6330
  }
6384
6331
  const durationMs = probeVideoDurationMs(params.expect, outBytes, ctx);
6385
6332
  const ref = await uploadAndIngest({
@@ -7351,24 +7298,6 @@ import { execFile as execFile3 } from "child_process";
7351
7298
  import { readFile as readFile9 } from "fs/promises";
7352
7299
  import path12 from "path";
7353
7300
  import { promisify as promisify3 } from "util";
7354
-
7355
- // src/engine/nodes/local/lib/caption-style.ts
7356
- var TERMINAL = /[.,;:!?…]$/;
7357
- function captionTextsFrom(html) {
7358
- const cards = html.match(/<div[^>]*class="[^"]*\bcaption\b[^"]*"[\s\S]*?<\/div>/g) ?? [];
7359
- return cards.map(
7360
- (card) => card.replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim()
7361
- ).filter((text) => text.length > 0);
7362
- }
7363
- function fragmentedCaptions(texts) {
7364
- if (texts.length < 2) return null;
7365
- const fragments = texts.slice(0, -1).filter((t) => !TERMINAL.test(t));
7366
- if (fragments.length === 0) return null;
7367
- const example = fragments[0];
7368
- 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.`;
7369
- }
7370
-
7371
- // src/engine/nodes/local/lib/hyperframe-check.ts
7372
7301
  var execFileAsync = promisify3(execFile3);
7373
7302
  var NEVER_BLOCK = [
7374
7303
  /contrast/i,
@@ -7437,74 +7366,6 @@ function classifyCheckOutput(lintRaw, inspectRaw) {
7437
7366
  warnings: findings.filter((f) => f.severity === "warning")
7438
7367
  };
7439
7368
  }
7440
- var GATE_SECTIONS = ["lint", "runtime", "layout", "motion", "contrast"];
7441
- function buildCheckArgs(dir, samples) {
7442
- return ["hyperframes", "check", dir, "--json", "--samples", String(samples), "--at-transitions"];
7443
- }
7444
- var PROMOTE_TO_BLOCKING = /GSAP target [^ ]+ not found|no deterministic font mapping/i;
7445
- function toGateFinding(section, entry) {
7446
- const f = entry;
7447
- const code = typeof f.code === "string" ? f.code : "unknown";
7448
- const message2 = typeof f.message === "string" ? f.message : "";
7449
- const isError = f.severity === "error" || PROMOTE_TO_BLOCKING.test(message2);
7450
- const fatal = PROMOTE_TO_BLOCKING.test(message2);
7451
- return {
7452
- source: section,
7453
- code,
7454
- message: message2,
7455
- severity: fatal || isError && !isAdvisory(code, message2) ? "blocking" : "warning"
7456
- };
7457
- }
7458
- function classifyGateOutput(raw) {
7459
- const parsed = parseCheckJson(raw);
7460
- const findings = GATE_SECTIONS.flatMap((section) => {
7461
- const block = parsed?.[section];
7462
- const entries = Array.isArray(block?.findings) ? block.findings : [];
7463
- return entries.map((entry) => toGateFinding(section, entry));
7464
- });
7465
- return {
7466
- blocking: findings.filter((f) => f.severity === "blocking"),
7467
- warnings: findings.filter((f) => f.severity === "warning")
7468
- };
7469
- }
7470
- function summariseWarnings(warnings) {
7471
- const groups = /* @__PURE__ */ new Map();
7472
- for (const w of warnings) {
7473
- const key = `${w.source}/${w.code}`;
7474
- const seen = groups.get(key);
7475
- if (seen) seen.count += 1;
7476
- else groups.set(key, { count: 1, message: w.message });
7477
- }
7478
- return [...groups.entries()].sort((a, b) => b[1].count - a[1].count).map(([key, g]) => `${g.count}\xD7 [${key}] ${g.message}`);
7479
- }
7480
- var FINDING_ADVICE = {
7481
- 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.",
7482
- console_error: "the composition threw in the browser, so whatever that script was driving did not happen.",
7483
- page_error: "a script failed to evaluate \u2014 animations and any JS-driven layout in this composition did not run.",
7484
- http_error: "an asset 404ed at render time; it will be missing from the frame, not substituted.",
7485
- 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."
7486
- };
7487
- function explainGateFinding(finding) {
7488
- const advice = FINDING_ADVICE[finding.code];
7489
- return advice ? `${finding.message} \u2014 ${advice}` : finding.message;
7490
- }
7491
- function condenseFrameDescriptions(markdown) {
7492
- const lines = [];
7493
- let frame = null;
7494
- for (const line of markdown.split("\n")) {
7495
- const heading = line.match(/^## (.+)$/);
7496
- if (heading) {
7497
- frame = heading[1] ?? null;
7498
- continue;
7499
- }
7500
- const text = line.trim();
7501
- if (frame && text) {
7502
- lines.push(`${frame}: ${text}`);
7503
- frame = null;
7504
- }
7505
- }
7506
- return lines;
7507
- }
7508
7369
  function buildLintArgs(dir) {
7509
7370
  return ["hyperframes", "lint", dir, "--json"];
7510
7371
  }
@@ -7536,13 +7397,8 @@ async function lintStructuralIssues(dir, timeoutMs) {
7536
7397
  function buildInspectArgs(dir, samples) {
7537
7398
  return ["hyperframes", "inspect", dir, "--json", "--samples", String(samples)];
7538
7399
  }
7539
- function buildSnapshotArgs(dir, frames, vision) {
7540
- const base = ["hyperframes", "snapshot", dir, "--frames", String(frames)];
7541
- return vision ? [
7542
- ...base,
7543
- "--describe",
7544
- "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?"
7545
- ] : [...base, "--describe", "false"];
7400
+ function buildSnapshotArgs(dir, frames) {
7401
+ return ["hyperframes", "snapshot", dir, "--frames", String(frames), "--describe", "false"];
7546
7402
  }
7547
7403
  function usesNestedCompositions(indexHtml) {
7548
7404
  const withoutComments = indexHtml.replace(/<!--[\s\S]*?-->/g, "");
@@ -7571,66 +7427,44 @@ async function runSnapshotSmoke(args, timeoutMs) {
7571
7427
  return { ok: false, unavailable: false, message: (err.stderr || err.message || "snapshot failed").slice(0, 800) };
7572
7428
  }
7573
7429
  }
7574
- async function runNestedCompositionSmoke(opts) {
7575
- const { dir, nodeId, ctx, timeoutMs, samples } = opts;
7576
- const indexHtml = await readFile9(path12.join(dir, "index.html"), "utf-8").catch(() => "");
7577
- if (!indexHtml || !usesNestedCompositions(indexHtml)) return;
7578
- const snap = await runSnapshotSmoke(
7579
- buildSnapshotArgs(dir, Math.min(samples, 3), hasFrameVisionKey()),
7580
- Math.max(timeoutMs, 12e4)
7581
- );
7582
- if (snap.unavailable) {
7583
- ctx.log(`${nodeId}: hyperframes snapshot unavailable \u2014 skipping nested-composition smoke test`);
7584
- return;
7585
- }
7586
- if (!snap.ok) {
7587
- throw new Error(
7588
- `${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).
7589
- ${snap.message}`
7590
- );
7591
- }
7592
- const described = await readFile9(path12.join(dir, "snapshots", "descriptions.md"), "utf-8").catch(() => "");
7593
- for (const line of condenseFrameDescriptions(described)) {
7594
- ctx.log(`${nodeId}: frame check \u2014 ${line}`);
7595
- }
7596
- ctx.log(`${nodeId}: nested-composition smoke test passed`);
7597
- }
7598
- async function runGate(dir, timeoutMs, samples) {
7599
- const checkRaw = await runOne(buildCheckArgs(dir, samples), timeoutMs);
7600
- if (checkRaw !== null) return classifyGateOutput(checkRaw);
7430
+ async function runHyperframesCheck(opts) {
7431
+ const { dir, nodeId, ctx, timeoutMs, samples = 5 } = opts;
7601
7432
  const [lintRaw, inspectRaw] = await Promise.all([
7602
7433
  runOne(buildLintArgs(dir), timeoutMs),
7603
7434
  runOne(buildInspectArgs(dir, samples), timeoutMs)
7604
7435
  ]);
7605
- if (lintRaw === null && inspectRaw === null) return null;
7606
- return classifyCheckOutput(lintRaw ?? "", inspectRaw ?? "");
7607
- }
7608
- async function runHyperframesCheck(opts) {
7609
- const { dir, nodeId, ctx, timeoutMs, samples = 5 } = opts;
7610
- const gate = await runGate(dir, timeoutMs, samples);
7611
- if (gate === null) {
7612
- ctx.log(`${nodeId}: hyperframes check/lint/inspect unavailable \u2014 skipping pre-render check`);
7436
+ if (lintRaw === null && inspectRaw === null) {
7437
+ ctx.log(`${nodeId}: hyperframes lint/inspect unavailable \u2014 skipping pre-render check`);
7613
7438
  return;
7614
7439
  }
7615
- const { blocking, warnings } = gate;
7616
- for (const line of summariseWarnings(warnings)) {
7617
- ctx.log(`${nodeId}: hyperframe check warning ${line}`);
7440
+ const { blocking, warnings } = classifyCheckOutput(lintRaw ?? "", inspectRaw ?? "");
7441
+ for (const w of warnings) {
7442
+ ctx.log(`${nodeId}: hyperframe check warning [${w.source}/${w.code}] ${w.message}`);
7618
7443
  }
7619
7444
  if (blocking.length > 0) {
7620
- const seen = /* @__PURE__ */ new Map();
7621
- for (const b of blocking) seen.set(`${b.source}/${b.code}/${b.message}`, b);
7622
- const detail = [...seen.values()].map((b) => `\u2022 [${b.source}/${b.code}] ${explainGateFinding(b)}`).join("\n");
7623
- throw new Error(
7624
- `${nodeId}: pre-render check failed (${blocking.length} blocking, ${seen.size} distinct)
7625
- ${detail}`
7626
- );
7445
+ const detail = blocking.map((b) => `\u2022 [${b.source}/${b.code}] ${b.message}`).join("\n");
7446
+ throw new Error(`${nodeId}: pre-render check failed (${blocking.length} blocking)
7447
+ ${detail}`);
7627
7448
  }
7628
- const composition = await readFile9(path12.join(dir, "index.html"), "utf-8").catch(() => "");
7629
- const captionComplaint = fragmentedCaptions(captionTextsFrom(composition));
7630
- if (captionComplaint) {
7631
- ctx.log(`${nodeId}: caption style \u2014 ${captionComplaint}`);
7449
+ let indexHtml = "";
7450
+ try {
7451
+ indexHtml = await readFile9(path12.join(dir, "index.html"), "utf-8");
7452
+ } catch {
7453
+ indexHtml = "";
7454
+ }
7455
+ if (indexHtml && usesNestedCompositions(indexHtml)) {
7456
+ const snap = await runSnapshotSmoke(buildSnapshotArgs(dir, Math.min(samples, 3)), Math.max(timeoutMs, 12e4));
7457
+ if (snap.unavailable) {
7458
+ ctx.log(`${nodeId}: hyperframes snapshot unavailable \u2014 skipping nested-composition smoke test`);
7459
+ } else if (!snap.ok) {
7460
+ throw new Error(
7461
+ `${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).
7462
+ ${snap.message}`
7463
+ );
7464
+ } else {
7465
+ ctx.log(`${nodeId}: nested-composition smoke test passed`);
7466
+ }
7632
7467
  }
7633
- await runNestedCompositionSmoke({ dir, nodeId, ctx, timeoutMs, samples });
7634
7468
  ctx.log(`${nodeId}: pre-render check passed (${warnings.length} warning${warnings.length === 1 ? "" : "s"})`);
7635
7469
  }
7636
7470
 
@@ -9137,14 +8971,7 @@ async function tryExtractAudio(inputs, ctx) {
9137
8971
  import { z as z34 } from "zod";
9138
8972
  var voiceSelectNode = delegated({
9139
8973
  id: "voice_select",
9140
- // 1.1.0: a pinned voice used to come back as an inline `{ kind, text }` with no sha256
9141
- // and no mime, which every consumer taking the id as an INPUT rejected —
9142
- // `audio_voice_convert` died on `voice_ref.sha256: expected string, received undefined`.
9143
- // The backend now uploads it as a real asset like the ranked branch, and the version has
9144
- // to move with it: the cache key is built from this string, so entries written under
9145
- // 1.0.0 kept replaying the broken shape long after the fix was deployed. A node whose
9146
- // OUTPUT shape changes has to bump, not only one whose behaviour does.
9147
- version: "1.1.0",
8974
+ version: "1.0.0",
9148
8975
  category: "audio",
9149
8976
  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.',
9150
8977
  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.',
@@ -9155,11 +8982,6 @@ var voiceSelectNode = delegated({
9155
8982
  age: z34.string().optional(),
9156
8983
  accent: z34.string().optional(),
9157
8984
  language: z34.string().optional(),
9158
- /**
9159
- * A voice already chosen — an Avatar's pinned one. Given, nothing is cast: the
9160
- * ranking is skipped and this id is emitted as-is.
9161
- */
9162
- voice_id: z34.string().min(1).max(120).optional(),
9163
8985
  limit: z34.number().int().min(1).max(20).optional()
9164
8986
  }).strict(),
9165
8987
  outputs: z34.object({ voice_id: TextRef, candidates: JsonRef }).strict(),
@@ -9322,7 +9144,6 @@ export {
9322
9144
  describeFailureReason,
9323
9145
  AD_FORMAT_PLATFORMS,
9324
9146
  platformFormats,
9325
- frameRealismDirection,
9326
9147
  SEEDANCE_DURATIONS,
9327
9148
  ELEVENLABS_MAX_MUSIC_LENGTH_MS,
9328
9149
  IMAGE_GENERATE_MODELS,
@@ -9338,7 +9159,6 @@ export {
9338
9159
  promptMaxLength,
9339
9160
  maxInputSlot,
9340
9161
  maxInputReferences,
9341
- bestSupportedQuality,
9342
9162
  nearestSupportedAspectRatio,
9343
9163
  nearestSupportedImageSize,
9344
9164
  estimateVideoCredits,
@@ -9379,4 +9199,4 @@ export {
9379
9199
  defaultRegistry,
9380
9200
  createEngineFromEnv
9381
9201
  };
9382
- //# sourceMappingURL=chunk-LTXNRRXW.js.map
9202
+ //# sourceMappingURL=chunk-NNQDWCFG.js.map