@koda-sl/baker-cli 0.121.0-dev.775026ddd → 0.121.0-dev.a2cdb40bc

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.
@@ -1,28 +1,7 @@
1
- var __create = Object.create;
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __commonJS = (cb, mod) => function __require() {
8
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
19
- // If the importer is in node compatibility mode or this is not an ESM
20
- // file that has been converted to a CommonJS file using a Babel-
21
- // compatible transform (i.e. "__esModule" has not been set), then set
22
- // "default" to the CommonJS "module.exports" for node compatibility.
23
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
24
- mod
25
- ));
1
+ import {
2
+ __commonJS,
3
+ __toESM
4
+ } from "./chunk-5WRI5ZAA.js";
26
5
 
27
6
  // ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/node_modules/safe-stable-stringify/index.js
28
7
  var require_safe_stable_stringify = __commonJS({
@@ -159,9 +138,9 @@ var require_safe_stable_stringify = __commonJS({
159
138
  }
160
139
  if (value) {
161
140
  return (value2) => {
162
- let message2 = `Object can not safely be stringified. Received type ${typeof value2}`;
163
- if (typeof value2 !== "function") message2 += ` (${value2.toString()})`;
164
- throw new Error(message2);
141
+ let message = `Object can not safely be stringified. Received type ${typeof value2}`;
142
+ if (typeof value2 !== "function") message += ` (${value2.toString()})`;
143
+ throw new Error(message);
165
144
  };
166
145
  }
167
146
  }
@@ -673,9 +652,6 @@ var HttpClient = class {
673
652
  async postJson(path16, body, signal) {
674
653
  return await this.requestJson("POST", path16, body, signal);
675
654
  }
676
- async putJson(path16, body, signal) {
677
- return await this.requestJson("PUT", path16, body, signal);
678
- }
679
655
  async getJson(path16, signal) {
680
656
  return await this.requestJson("GET", path16, void 0, signal);
681
657
  }
@@ -703,8 +679,8 @@ var HttpClient = class {
703
679
  try {
704
680
  const res = await this.fetchFn(url, {
705
681
  method,
706
- headers: method === "GET" ? { Authorization: `Bearer ${this.apiKey}` } : { "Content-Type": "application/json", Authorization: `Bearer ${this.apiKey}` },
707
- body: method === "GET" ? void 0 : JSON.stringify(body),
682
+ headers: method === "POST" ? { "Content-Type": "application/json", Authorization: `Bearer ${this.apiKey}` } : { Authorization: `Bearer ${this.apiKey}` },
683
+ body: method === "POST" ? JSON.stringify(body) : void 0,
708
684
  signal: controller.signal
709
685
  });
710
686
  if (res.ok) return { kind: "value", value: await res.json() };
@@ -741,33 +717,33 @@ async function parseErrorBody(res) {
741
717
  const errObj = body.error ?? {};
742
718
  return classifyHttpError(res.status, errObj, errObj.message ?? `HTTP ${res.status}`);
743
719
  }
744
- function classifyHttpError(status, errObj, message2) {
720
+ function classifyHttpError(status, errObj, message) {
745
721
  if (errObj.code === CONTENT_POLICY_CODE) {
746
- return { kind: "content_policy", status, provider: errObj.provider, message: message2 };
722
+ return { kind: "content_policy", status, provider: errObj.provider, message };
747
723
  }
748
724
  if (status === 401 || status === 403) {
749
- return { kind: "unauthorized", status, message: message2 };
725
+ return { kind: "unauthorized", status, message };
750
726
  }
751
727
  if (status === 400 || status === 422) {
752
- return { kind: "validation", status, message: message2, details: errObj.details };
728
+ return { kind: "validation", status, message, details: errObj.details };
753
729
  }
754
730
  if (status === 502 || status === 504) {
755
731
  if (errObj.code === "provider_timeout" || status === 504) {
756
- return { kind: "timeout", provider: errObj.provider, message: message2 };
732
+ return { kind: "timeout", provider: errObj.provider, message };
757
733
  }
758
734
  return {
759
735
  kind: "provider",
760
736
  status,
761
737
  provider: errObj.provider,
762
738
  code: errObj.code ?? "provider_error",
763
- message: message2,
739
+ message,
764
740
  retryable: errObj.retryable ?? true
765
741
  };
766
742
  }
767
743
  if (status >= 500 || status === 429) {
768
- return { kind: "server", status, message: message2 };
744
+ return { kind: "server", status, message };
769
745
  }
770
- return { kind: "validation", status, message: message2, details: errObj.details };
746
+ return { kind: "validation", status, message, details: errObj.details };
771
747
  }
772
748
  function backoffMs(attempt) {
773
749
  return 1e3 * 2 ** attempt;
@@ -838,27 +814,6 @@ var BackendClient = class {
838
814
  signal
839
815
  );
840
816
  }
841
- /** Remote cache lookup. A miss (404) — or an old backend without the route — returns null. */
842
- async getCacheEntry(cacheKey, signal) {
843
- try {
844
- const res = await this.http.getJson(`/api/canvas/cache/${encodeURIComponent(cacheKey)}`, signal);
845
- return res.entry;
846
- } catch (e) {
847
- if (e instanceof BackendHttpError && "status" in e.detail && e.detail.status === 404) return null;
848
- throw e;
849
- }
850
- }
851
- async putCacheEntry(entry, signal) {
852
- await this.http.putJson(
853
- `/api/canvas/cache/${encodeURIComponent(entry.cacheKey)}`,
854
- entry,
855
- signal
856
- );
857
- }
858
- /** Durable run-history record — POST /api/canvas/runs (idempotent server-side on runId). */
859
- async recordRun(payload, signal) {
860
- await this.http.postJson("/api/canvas/runs", payload, signal);
861
- }
862
817
  getArtifact(kind, name, version, signal) {
863
818
  const path16 = version ? `/api/canvas/artifacts/${encodeURIComponent(kind)}/${encodeURIComponent(name)}/${encodeURIComponent(version)}` : `/api/canvas/artifacts/${encodeURIComponent(kind)}/${encodeURIComponent(name)}`;
864
819
  return this.http.getJson(path16, signal);
@@ -882,17 +837,14 @@ function requireCredentialsFromEnv(env = process.env) {
882
837
  }
883
838
  return c;
884
839
  }
885
- function remoteCacheEnabledFromEnv(env = process.env) {
886
- return env.BAKER_CANVAS_REMOTE_CACHE !== "off";
887
- }
888
840
 
889
841
  // src/engine/engine/errors.ts
890
842
  function isBlocking(issue) {
891
843
  return issue.severity !== "warning";
892
844
  }
893
845
  var CanvasError = class extends Error {
894
- constructor(message2) {
895
- super(message2);
846
+ constructor(message) {
847
+ super(message);
896
848
  this.name = "CanvasError";
897
849
  }
898
850
  };
@@ -1610,160 +1562,6 @@ function encodeRandom() {
1610
1562
  return out;
1611
1563
  }
1612
1564
 
1613
- // src/engine/storage/remote-cache-store.ts
1614
- var CANVAS_ASSETS_URL_SEGMENT = "/canvas-assets/";
1615
- function isPersistedAssetRef(ref) {
1616
- const { url, sha256 } = ref;
1617
- if (typeof url !== "string" || typeof sha256 !== "string") return false;
1618
- return url.includes(`${CANVAS_ASSETS_URL_SEGMENT}${sha256.slice(0, 2)}/${sha256}`) || url.includes(`${CANVAS_ASSETS_URL_SEGMENT}${sha256}`);
1619
- }
1620
- function isAssetRefLike(value) {
1621
- return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.sha256 === "string" && typeof value.mime === "string";
1622
- }
1623
- function collectAssetRefLikes(value, out = []) {
1624
- if (Array.isArray(value)) {
1625
- for (const item of value) collectAssetRefLikes(item, out);
1626
- return out;
1627
- }
1628
- if (typeof value !== "object" || value === null) return out;
1629
- if (isAssetRefLike(value)) {
1630
- out.push(value);
1631
- }
1632
- for (const item of Object.values(value)) collectAssetRefLikes(item, out);
1633
- return out;
1634
- }
1635
- function entryFullyPersisted(entry) {
1636
- return collectAssetRefLikes(entry.outputs).every((ref) => isPersistedAssetRef(ref));
1637
- }
1638
- function stripLocalFields(entry) {
1639
- const clone = JSON.parse(JSON.stringify(entry));
1640
- for (const ref of collectAssetRefLikes(clone.outputs)) {
1641
- delete ref.path;
1642
- delete ref.bytes;
1643
- }
1644
- return clone;
1645
- }
1646
- var RemoteCacheStore = class {
1647
- client;
1648
- log;
1649
- constructor(client, log) {
1650
- this.client = client;
1651
- this.log = log ?? (() => void 0);
1652
- }
1653
- async get(cacheKey) {
1654
- return await this.client.getCacheEntry(cacheKey);
1655
- }
1656
- async put(entry) {
1657
- if (!entryFullyPersisted(entry)) {
1658
- this.log(`[cache ] ${entry.cacheKey.slice(0, 12)}\u2026 has local-only assets, kept local`);
1659
- return;
1660
- }
1661
- const stripped = stripLocalFields(entry);
1662
- if (stripped.refs.length > MAX_REMOTE_REFS) {
1663
- stripped.refs = stripped.refs.slice(0, MAX_REMOTE_REFS);
1664
- }
1665
- await this.client.putCacheEntry(stripped);
1666
- }
1667
- };
1668
- var MAX_REMOTE_REFS = 512;
1669
- var LayeredCacheStore = class {
1670
- rootDir;
1671
- local;
1672
- remote;
1673
- assets;
1674
- log;
1675
- constructor(opts) {
1676
- this.local = opts.local;
1677
- this.remote = opts.remote;
1678
- this.assets = opts.assets;
1679
- this.rootDir = opts.local.rootDir;
1680
- this.log = opts.log ?? (() => void 0);
1681
- }
1682
- async get(cacheKey) {
1683
- const localHit = await this.local.get(cacheKey);
1684
- if (localHit) return localHit;
1685
- let remoteEntry;
1686
- try {
1687
- remoteEntry = await this.remote.get(cacheKey);
1688
- } catch (e) {
1689
- this.log(`[cache ] remote lookup failed (${message(e)}) \u2014 treating as miss`);
1690
- return null;
1691
- }
1692
- if (!remoteEntry) return null;
1693
- let rehydrated;
1694
- try {
1695
- rehydrated = await this.rehydrate(remoteEntry);
1696
- } catch (e) {
1697
- this.log(`[cache ] ${cacheKey.slice(0, 12)}\u2026 rehydration failed (${message(e)}) \u2014 treating as miss`);
1698
- return null;
1699
- }
1700
- await this.local.put(rehydrated);
1701
- return rehydrated;
1702
- }
1703
- async put(entry) {
1704
- await this.local.put(entry);
1705
- try {
1706
- await this.remote.put(entry);
1707
- } catch (e) {
1708
- this.log(`[cache ] remote write failed (${message(e)}) \u2014 entry kept local`);
1709
- }
1710
- }
1711
- /**
1712
- * Download every referenced asset into the local content-addressed store
1713
- * (sha-verified) and stamp fresh local paths. Any ref that cannot be
1714
- * rehydrated fails the WHOLE entry — a partially-hydrated cache hit would
1715
- * crash materialization later with a far less actionable error.
1716
- */
1717
- async rehydrate(entry) {
1718
- const clone = JSON.parse(JSON.stringify(entry));
1719
- for (const ref of collectAssetRefLikes(clone.outputs)) {
1720
- if (!isPersistedAssetRef(ref)) {
1721
- throw new Error(`ref ${ref.sha256.slice(0, 12)}\u2026 has no persisted url`);
1722
- }
1723
- const ingested = await this.assets.ingestRemote({
1724
- kind: typeof ref.kind === "string" ? ref.kind : "json",
1725
- url: ref.url,
1726
- sha256: ref.sha256,
1727
- mime: ref.mime,
1728
- metadata: ref.metadata ?? void 0
1729
- });
1730
- ref.path = ingested.path;
1731
- }
1732
- return clone;
1733
- }
1734
- };
1735
- function message(e) {
1736
- return e instanceof Error ? e.message : String(e);
1737
- }
1738
-
1739
- // src/engine/nodes/remote/upload.ts
1740
- async function presignAndPut(args) {
1741
- const { putUrl, publicUrl } = await args.ctx.client.presignAssetUpload(args.sha256, args.mime, args.ctx.signal);
1742
- const putRes = await fetch(putUrl, {
1743
- method: "PUT",
1744
- body: new Uint8Array(args.bytes),
1745
- headers: { "Content-Type": args.mime },
1746
- signal: args.ctx.signal
1747
- });
1748
- if (!putRes.ok) {
1749
- throw new Error(`upload: presigned PUT failed ${putRes.status} ${putRes.statusText}`);
1750
- }
1751
- return publicUrl;
1752
- }
1753
- async function ensureUploaded(ref, ctx) {
1754
- if (ref.url) return ref;
1755
- const bytes = await ctx.assets.readBytes(ref.sha256, ref.mime);
1756
- const url = await presignAndPut({ bytes, sha256: ref.sha256, mime: ref.mime, ctx });
1757
- return { ...ref, url };
1758
- }
1759
- async function persistOutputAssetUrls(outputs, ctx) {
1760
- for (const ref of collectAssetRefLikes(outputs)) {
1761
- if (isPersistedAssetRef(ref)) continue;
1762
- const bytes = await ctx.assets.readBytes(ref.sha256, ref.mime);
1763
- ref.url = await presignAndPut({ bytes, sha256: ref.sha256, mime: ref.mime, ctx });
1764
- }
1765
- }
1766
-
1767
1565
  // src/engine/schema/canvas.ts
1768
1566
  import { z } from "zod";
1769
1567
  var REF_PREFIX = "$ref:";
@@ -1825,7 +1623,11 @@ var VideoMeta = z.object({
1825
1623
  // Advisory: the scene's visual length vs the estimated spoken length, so
1826
1624
  // a reviewer can see a native line that may run past its cut. Not gated.
1827
1625
  scene_s: z.number().optional(),
1828
- est_speech_s: z.number().optional()
1626
+ est_speech_s: z.number().optional(),
1627
+ // Word count of the line est_speech_s was measured for. Together they carry
1628
+ // the speaker's OBSERVED pace (from the deconstruct's word timings), so the
1629
+ // overrun check budgets re-authored lines at the real rate, not a wps guess.
1630
+ speech_words: z.number().optional()
1829
1631
  }),
1830
1632
  z.object({ scene: z.number(), lipsync_node: z.string() })
1831
1633
  ])
@@ -2355,7 +2157,9 @@ var STAGE_CODES = {
2355
2157
  SPEECH_OVERRUN: "VIDEO_SPEECH_OVERRUN",
2356
2158
  ASPECT_MISMATCH: "VIDEO_ASPECT_MISMATCH",
2357
2159
  REFERENCE_MISSING: "VIDEO_REFERENCE_MISSING",
2358
- SPAN_EXCEEDS_MODEL: "VIDEO_SPAN_EXCEEDS_MODEL"
2160
+ SPAN_EXCEEDS_MODEL: "VIDEO_SPAN_EXCEEDS_MODEL",
2161
+ UI_IN_PROMPT: "VIDEO_UI_IN_PROMPT",
2162
+ BRANDMARK_IN_PROMPT: "VIDEO_BRANDMARK_IN_PROMPT"
2359
2163
  };
2360
2164
  var SPAN_MODEL_SLACK_S = 0.25;
2361
2165
  var VIDEO_TIME_SLACK_S = 0.75;
@@ -2734,6 +2538,8 @@ function checkVideoInvariants(ctx) {
2734
2538
  }
2735
2539
  checkSpeechOverrun(ctx, meta.talking_scenes);
2736
2540
  checkAspectConsistency(ctx);
2541
+ checkUiInPrompt(ctx);
2542
+ checkBrandmarkInPrompt(ctx);
2737
2543
  checkReferenceCompleteness(ctx, meta);
2738
2544
  checkClipSpanFitsModel(ctx, meta);
2739
2545
  }
@@ -2763,18 +2569,30 @@ function keywordTokens(text) {
2763
2569
  if (!text) return [];
2764
2570
  return text.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 3 && !KEYWORD_STOPWORDS.has(t));
2765
2571
  }
2766
- function keywordsForElement(el) {
2767
- const type = el.type.toLowerCase();
2768
- const typeWords = ELEMENT_TYPE_KEYWORDS[type] ?? [];
2572
+ function elementMentionKeywords(el) {
2573
+ const typeWords = ELEMENT_TYPE_KEYWORDS[el.type.toLowerCase()] ?? [];
2769
2574
  return [.../* @__PURE__ */ new Set([...typeWords, ...keywordTokens(el.label), ...keywordTokens(el.description)])];
2770
2575
  }
2576
+ function keywordsForElement(el) {
2577
+ return elementMentionKeywords(el);
2578
+ }
2771
2579
  function containsWord(text, word) {
2772
2580
  const esc = word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2773
2581
  return new RegExp(`\\b${esc}\\b`, "i").test(text);
2774
2582
  }
2583
+ var FRAME_DESCRIPTION_START = "FRAME DESCRIPTION (this frame's editable prompt):";
2584
+ var FRAME_DESCRIPTION_END = "Render exactly what the FRAME DESCRIPTION";
2585
+ function frameDescriptionOf(prompt) {
2586
+ const i = prompt.indexOf(FRAME_DESCRIPTION_START);
2587
+ if (i < 0) return prompt;
2588
+ const rest = prompt.slice(i + FRAME_DESCRIPTION_START.length);
2589
+ const j = rest.indexOf(FRAME_DESCRIPTION_END);
2590
+ return j < 0 ? rest : rest.slice(0, j);
2591
+ }
2775
2592
  function checkFrameReferences(ctx, node, index, keyworded) {
2776
- const prompt = node.params?.prompt;
2777
- if (typeof prompt !== "string" || prompt.length === 0) return;
2593
+ const rawPrompt = node.params?.prompt;
2594
+ if (typeof rawPrompt !== "string" || rawPrompt.length === 0) return;
2595
+ const prompt = frameDescriptionOf(rawPrompt);
2778
2596
  const inputsBlob = JSON.stringify(node.inputs ?? {});
2779
2597
  for (const { el, keywords } of keyworded) {
2780
2598
  if (inputsBlob.includes(el.ref)) continue;
@@ -2824,13 +2642,24 @@ function checkClipSpanFitsModel(ctx, meta) {
2824
2642
  });
2825
2643
  }
2826
2644
  }
2827
- function speechOverrunOf(node) {
2645
+ var OBSERVED_WPS_MIN = 1;
2646
+ var OBSERVED_WPS_MAX = 6;
2647
+ function secondsPerWord(stamped) {
2648
+ const est = stamped?.est_speech_s;
2649
+ const words = stamped?.speech_words;
2650
+ if (est && words && est > 0 && words > 0) {
2651
+ const wps = words / est;
2652
+ if (wps >= OBSERVED_WPS_MIN && wps <= OBSERVED_WPS_MAX) return est / words;
2653
+ }
2654
+ return 1 / SPEECH_WORDS_PER_SECOND;
2655
+ }
2656
+ function speechOverrunOf(node, secPerWord) {
2828
2657
  const params = node.params;
2829
2658
  if (params?.generate_audio !== true) return null;
2830
2659
  const line = nativeDialogueOf(params.prompt);
2831
2660
  const duration = typeof params.duration === "number" ? params.duration : void 0;
2832
2661
  if (!line || !duration) return null;
2833
- const estSpeechS = line.split(/\s+/).filter(Boolean).length / SPEECH_WORDS_PER_SECOND;
2662
+ const estSpeechS = line.split(/\s+/).filter(Boolean).length * secPerWord;
2834
2663
  return estSpeechS > duration * SPEECH_OVERRUN_RATIO ? { estSpeechS, duration } : null;
2835
2664
  }
2836
2665
  function checkSpeechOverrun(ctx, talkingScenes) {
@@ -2839,7 +2668,7 @@ function checkSpeechOverrun(ctx, talkingScenes) {
2839
2668
  const nativeClipRe = new RegExp(`^s${entry.scene}(_r\\d+)?_clip$`);
2840
2669
  for (const n of ctx.canvas.nodes) {
2841
2670
  if (!nativeClipRe.test(n.id) || n.type !== "video_generate") continue;
2842
- const overrun = speechOverrunOf(n);
2671
+ const overrun = speechOverrunOf(n, secondsPerWord(entry));
2843
2672
  if (!overrun) continue;
2844
2673
  ctx.issues.push({
2845
2674
  path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].params.prompt`,
@@ -2849,6 +2678,38 @@ function checkSpeechOverrun(ctx, talkingScenes) {
2849
2678
  }
2850
2679
  }
2851
2680
  }
2681
+ var UI_IN_PROMPT_RE = /\bscreen[- ]?(?:recording|capture|grab|share)\b|\bapp (?:interface|screen)\b|\bphone screen overlay\b/i;
2682
+ function checkUiInPrompt(ctx) {
2683
+ for (const n of ctx.canvas.nodes) {
2684
+ if (n.type !== "video_generate") continue;
2685
+ const prompt = n.params?.prompt;
2686
+ if (typeof prompt !== "string" || !UI_IN_PROMPT_RE.test(prompt)) continue;
2687
+ ctx.issues.push({
2688
+ path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].params.prompt`,
2689
+ code: STAGE_CODES.UI_IN_PROMPT,
2690
+ severity: "warning",
2691
+ message: `"${n.id}" asks the video model to render a screen/UI surface \u2014 generative video garbles UI text and chrome. Composite the real screen on the overlay layer (screenshot / brand HTML) and keep this prompt to the background plate`,
2692
+ node_id: n.id,
2693
+ node_type: "video_generate"
2694
+ });
2695
+ }
2696
+ }
2697
+ var BRANDMARK_IN_PROMPT_RE = /\b(?:logo|wordmark) (?:overlay|animation|sting|card|reveal)\b|\b(?:google|facebook|instagram|tiktok|youtube|amazon|apple|microsoft|whatsapp|netflix|spotify|excel|trustpilot) (?:logo|wordmark|branding)\b/i;
2698
+ function checkBrandmarkInPrompt(ctx) {
2699
+ for (const n of ctx.canvas.nodes) {
2700
+ if (n.type !== "video_generate" && n.type !== "image_generate") continue;
2701
+ const prompt = n.params?.prompt;
2702
+ if (typeof prompt !== "string" || !BRANDMARK_IN_PROMPT_RE.test(prompt)) continue;
2703
+ ctx.issues.push({
2704
+ path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].params.prompt`,
2705
+ code: STAGE_CODES.BRANDMARK_IN_PROMPT,
2706
+ severity: "warning",
2707
+ message: `"${n.id}" asks the model to render a brand logo/wordmark \u2014 generation garbles marks and third-party logos carry IP exposure. Source the real mark (baker images logo <domain>) and composite it on the overlay layer`,
2708
+ node_id: n.id,
2709
+ node_type: n.type
2710
+ });
2711
+ }
2712
+ }
2852
2713
  function checkAspectConsistency(ctx) {
2853
2714
  const clips = ctx.canvas.nodes.filter((n) => n.type === "video_generate");
2854
2715
  if (clips.length < 2) return;
@@ -2980,7 +2841,6 @@ var Engine = class {
2980
2841
  cache;
2981
2842
  outputsDir;
2982
2843
  log;
2983
- persistAssets;
2984
2844
  constructor(opts) {
2985
2845
  this.registry = opts.registry;
2986
2846
  this.client = opts.client;
@@ -2988,7 +2848,6 @@ var Engine = class {
2988
2848
  this.cache = opts.cache;
2989
2849
  this.outputsDir = opts.outputsDir;
2990
2850
  this.log = opts.log ?? (() => void 0);
2991
- this.persistAssets = opts.persistAssets ?? false;
2992
2851
  }
2993
2852
  validate(canvas) {
2994
2853
  return validateCanvas(canvas, this.registry);
@@ -3035,7 +2894,7 @@ var Engine = class {
3035
2894
  `[done ] ${stats.cached_nodes}/${stats.total_nodes} cached, ${stats.total_credits} credits, ${stats.duration_ms}ms`
3036
2895
  );
3037
2896
  this.log(`outputs in: ${writer.runDir}`);
3038
- return { run_id: runId, output, outputs_by_node: outputs, stats, outputs_dir: writer.runDir, node_runs: nodeRuns };
2897
+ return { run_id: runId, output, outputs_by_node: outputs, stats, outputs_dir: writer.runDir };
3039
2898
  }
3040
2899
  async runLayers(canvas, outputs, runId, writer, opts, counters, nodeRuns) {
3041
2900
  const layers = topologicalLayers(this.pruneToOutput(canvas, buildGraph(canvas)));
@@ -3132,14 +2991,6 @@ var Engine = class {
3132
2991
  const credits = def.cost ? def.cost({ params: parsedParams }).credits : 0;
3133
2992
  const outputsObj = result;
3134
2993
  outputs[node.id] = outputsObj;
3135
- if (this.persistAssets) {
3136
- try {
3137
- await persistOutputAssetUrls(outputsObj, ctx);
3138
- } catch (e) {
3139
- const msg = e instanceof Error ? e.message : String(e);
3140
- this.log(`[warn ] ${node.id}: asset persistence failed (${msg}) \u2014 outputs stay local-only`);
3141
- }
3142
- }
3143
2994
  if (policy === "read_write") {
3144
2995
  await this.cache.put({
3145
2996
  cacheKey: prepared.cacheKey,
@@ -3458,6 +3309,27 @@ var FontRef = BaseAssetRef.extend({
3458
3309
  });
3459
3310
  var AssetRef = z4.discriminatedUnion("kind", [ImageRef, VideoRef, AudioRef, JsonRef, TextRef, FontRef]);
3460
3311
 
3312
+ // src/engine/nodes/remote/upload.ts
3313
+ async function presignAndPut(args) {
3314
+ const { putUrl, publicUrl } = await args.ctx.client.presignAssetUpload(args.sha256, args.mime, args.ctx.signal);
3315
+ const putRes = await fetch(putUrl, {
3316
+ method: "PUT",
3317
+ body: new Uint8Array(args.bytes),
3318
+ headers: { "Content-Type": args.mime },
3319
+ signal: args.ctx.signal
3320
+ });
3321
+ if (!putRes.ok) {
3322
+ throw new Error(`upload: presigned PUT failed ${putRes.status} ${putRes.statusText}`);
3323
+ }
3324
+ return publicUrl;
3325
+ }
3326
+ async function ensureUploaded(ref, ctx) {
3327
+ if (ref.url) return ref;
3328
+ const bytes = await ctx.assets.readBytes(ref.sha256, ref.mime);
3329
+ const url = await presignAndPut({ bytes, sha256: ref.sha256, mime: ref.mime, ctx });
3330
+ return { ...ref, url };
3331
+ }
3332
+
3461
3333
  // src/engine/nodes/remote/delegate.ts
3462
3334
  function delegated(spec) {
3463
3335
  return {
@@ -3852,10 +3724,10 @@ function inferKindFromMime(mime) {
3852
3724
  if (mime.startsWith("font/")) return "font";
3853
3725
  return null;
3854
3726
  }
3855
- function localExecError(ctx, message2) {
3727
+ function localExecError(ctx, message) {
3856
3728
  return new NodeExecutionError(ctx.nodeId, ctx.nodeType, {
3857
3729
  kind: "local",
3858
- cause: new Error(`ingest: ${message2}`)
3730
+ cause: new Error(`ingest: ${message}`)
3859
3731
  });
3860
3732
  }
3861
3733
  async function execLocalFile(params, ctx) {
@@ -4522,7 +4394,7 @@ async function refToUrl(ref) {
4522
4394
  return `data:${ref.mime};base64,${bytes.toString("base64")}`;
4523
4395
  }
4524
4396
  var ASSET_KINDS = /* @__PURE__ */ new Set(["image", "video", "audio", "json", "text", "font"]);
4525
- function isAssetRefLike2(value) {
4397
+ function isAssetRefLike(value) {
4526
4398
  if (!value || typeof value !== "object") return false;
4527
4399
  const v = value;
4528
4400
  return typeof v.kind === "string" && ASSET_KINDS.has(v.kind) && typeof v.mime === "string" && typeof v.sha256 === "string" && (typeof v.url === "string" || typeof v.path === "string");
@@ -4894,8 +4766,8 @@ var NEVER_BLOCK = [
4894
4766
  /text[_-]?occluded/i
4895
4767
  ];
4896
4768
  var UNAVAILABLE = /unknown command|command not found|not found|Did you mean|Unknown argument|ENOENT/i;
4897
- function isAdvisory(code, message2) {
4898
- const hay = `${code} ${message2}`;
4769
+ function isAdvisory(code, message) {
4770
+ const hay = `${code} ${message}`;
4899
4771
  return NEVER_BLOCK.some((re) => re.test(hay));
4900
4772
  }
4901
4773
  function parseCheckJson(raw) {
@@ -4923,10 +4795,10 @@ function classifyLint(json) {
4923
4795
  for (const f of findings) {
4924
4796
  const rec = f;
4925
4797
  const code = String(rec?.code ?? "");
4926
- const message2 = String(rec?.message ?? "");
4798
+ const message = String(rec?.message ?? "");
4927
4799
  const severity = String(rec?.severity ?? "info");
4928
- const blocking = severity === "error" && !isAdvisory(code, message2);
4929
- out.push({ source: "lint", code, message: message2, severity: blocking ? "blocking" : "warning" });
4800
+ const blocking = severity === "error" && !isAdvisory(code, message);
4801
+ out.push({ source: "lint", code, message, severity: blocking ? "blocking" : "warning" });
4930
4802
  }
4931
4803
  return out;
4932
4804
  }
@@ -4938,9 +4810,9 @@ function classifyInspect(json) {
4938
4810
  for (const iss of issues) {
4939
4811
  const rec = iss;
4940
4812
  const code = String(rec?.code ?? rec?.type ?? "overflow");
4941
- const message2 = String(rec?.message ?? rec?.detail ?? JSON.stringify(iss));
4813
+ const message = String(rec?.message ?? rec?.detail ?? JSON.stringify(iss));
4942
4814
  const severity = rec?.severity ? String(rec.severity) : obj?.ok === false ? "error" : "warning";
4943
- out.push({ source: "inspect", code, message: message2, severity: severity === "error" ? "blocking" : "warning" });
4815
+ out.push({ source: "inspect", code, message, severity: severity === "error" ? "blocking" : "warning" });
4944
4816
  }
4945
4817
  return out;
4946
4818
  }
@@ -5385,7 +5257,7 @@ async function buildSubstitutionValues(compositionParams, meta, duration) {
5385
5257
  }
5386
5258
  function coerceImageParam(value) {
5387
5259
  if (typeof value === "string") return Promise.resolve(value);
5388
- if (isAssetRefLike2(value)) return refToUrl(value);
5260
+ if (isAssetRefLike(value)) return refToUrl(value);
5389
5261
  throw new Error("hyperframe_render: image param must be a URL string or AssetRef");
5390
5262
  }
5391
5263
  async function substituteCompositionFiles(tmp, values) {
@@ -5615,7 +5487,7 @@ async function buildSubstitutionValues2(compositionParams, meta) {
5615
5487
  }
5616
5488
  function coerceImageParam2(value) {
5617
5489
  if (typeof value === "string") return Promise.resolve(value);
5618
- if (isAssetRefLike2(value)) return refToUrl(value);
5490
+ if (isAssetRefLike(value)) return refToUrl(value);
5619
5491
  throw new Error("hyperframe_snapshot: image param must be a URL string or AssetRef");
5620
5492
  }
5621
5493
  async function substituteCompositionFiles2(tmp, values) {
@@ -6641,29 +6513,17 @@ function createEngineFromEnv(opts = {}) {
6641
6513
  const cacheDir = opts.cacheDir ?? path15.join(cwd, "canvas", ".cache");
6642
6514
  const outputsDir = opts.outputsDir ?? path15.join(cwd, "canvas");
6643
6515
  const creds = requireCredentialsFromEnv();
6644
- const client = new BackendClient({ baseUrl: creds.url, apiKey: creds.apiKey });
6645
- const assets = new LocalAssetStore(path15.join(cacheDir, "assets"));
6646
- const localCache = new LocalCacheStore(path15.join(cacheDir, "index"));
6647
- const remoteCacheEnabled = opts.remoteCache ?? remoteCacheEnabledFromEnv();
6648
- const cache = remoteCacheEnabled ? new LayeredCacheStore({
6649
- local: localCache,
6650
- remote: new RemoteCacheStore(client, opts.log),
6651
- assets,
6652
- log: opts.log
6653
- }) : localCache;
6654
6516
  return new Engine({
6655
6517
  registry: defaultRegistry(),
6656
- client,
6657
- assets,
6658
- cache,
6518
+ client: new BackendClient({ baseUrl: creds.url, apiKey: creds.apiKey }),
6519
+ assets: new LocalAssetStore(path15.join(cacheDir, "assets")),
6520
+ cache: new LocalCacheStore(path15.join(cacheDir, "index")),
6659
6521
  outputsDir,
6660
- log: opts.log,
6661
- persistAssets: remoteCacheEnabled
6522
+ log: opts.log
6662
6523
  });
6663
6524
  }
6664
6525
 
6665
6526
  export {
6666
- requireCredentialsFromEnv,
6667
6527
  LayerExecutionError,
6668
6528
  describeFailureReason,
6669
6529
  SEEDANCE_DURATIONS,
@@ -6671,10 +6531,7 @@ export {
6671
6531
  IMAGE_GENERATE_MODELS,
6672
6532
  MODEL_REGISTRY,
6673
6533
  resolveConcurrency,
6674
- ulid,
6675
- isPersistedAssetRef,
6676
- collectAssetRefLikes,
6677
- sha256Hex,
6534
+ elementMentionKeywords,
6678
6535
  BackendClient2 as BackendClient,
6679
6536
  Engine2 as Engine,
6680
6537
  LocalAssetStore2 as LocalAssetStore,
@@ -6685,4 +6542,4 @@ export {
6685
6542
  defaultRegistry,
6686
6543
  createEngineFromEnv
6687
6544
  };
6688
- //# sourceMappingURL=chunk-GQIOFHVI.js.map
6545
+ //# sourceMappingURL=chunk-MWFJ5NOP.js.map