@koda-sl/baker-cli 0.123.0-dev.8e4328629 → 0.123.0-dev.b74ab562

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.
@@ -138,9 +138,9 @@ var require_safe_stable_stringify = __commonJS({
138
138
  }
139
139
  if (value) {
140
140
  return (value2) => {
141
- let message2 = `Object can not safely be stringified. Received type ${typeof value2}`;
142
- if (typeof value2 !== "function") message2 += ` (${value2.toString()})`;
143
- 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);
144
144
  };
145
145
  }
146
146
  }
@@ -652,9 +652,6 @@ var HttpClient = class {
652
652
  async postJson(path16, body, signal) {
653
653
  return await this.requestJson("POST", path16, body, signal);
654
654
  }
655
- async putJson(path16, body, signal) {
656
- return await this.requestJson("PUT", path16, body, signal);
657
- }
658
655
  async getJson(path16, signal) {
659
656
  return await this.requestJson("GET", path16, void 0, signal);
660
657
  }
@@ -682,8 +679,8 @@ var HttpClient = class {
682
679
  try {
683
680
  const res = await this.fetchFn(url, {
684
681
  method,
685
- headers: method === "GET" ? { Authorization: `Bearer ${this.apiKey}` } : { "Content-Type": "application/json", Authorization: `Bearer ${this.apiKey}` },
686
- 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,
687
684
  signal: controller.signal
688
685
  });
689
686
  if (res.ok) return { kind: "value", value: await res.json() };
@@ -720,33 +717,33 @@ async function parseErrorBody(res) {
720
717
  const errObj = body.error ?? {};
721
718
  return classifyHttpError(res.status, errObj, errObj.message ?? `HTTP ${res.status}`);
722
719
  }
723
- function classifyHttpError(status, errObj, message2) {
720
+ function classifyHttpError(status, errObj, message) {
724
721
  if (errObj.code === CONTENT_POLICY_CODE) {
725
- return { kind: "content_policy", status, provider: errObj.provider, message: message2 };
722
+ return { kind: "content_policy", status, provider: errObj.provider, message };
726
723
  }
727
724
  if (status === 401 || status === 403) {
728
- return { kind: "unauthorized", status, message: message2 };
725
+ return { kind: "unauthorized", status, message };
729
726
  }
730
727
  if (status === 400 || status === 422) {
731
- return { kind: "validation", status, message: message2, details: errObj.details };
728
+ return { kind: "validation", status, message, details: errObj.details };
732
729
  }
733
730
  if (status === 502 || status === 504) {
734
731
  if (errObj.code === "provider_timeout" || status === 504) {
735
- return { kind: "timeout", provider: errObj.provider, message: message2 };
732
+ return { kind: "timeout", provider: errObj.provider, message };
736
733
  }
737
734
  return {
738
735
  kind: "provider",
739
736
  status,
740
737
  provider: errObj.provider,
741
738
  code: errObj.code ?? "provider_error",
742
- message: message2,
739
+ message,
743
740
  retryable: errObj.retryable ?? true
744
741
  };
745
742
  }
746
743
  if (status >= 500 || status === 429) {
747
- return { kind: "server", status, message: message2 };
744
+ return { kind: "server", status, message };
748
745
  }
749
- return { kind: "validation", status, message: message2, details: errObj.details };
746
+ return { kind: "validation", status, message, details: errObj.details };
750
747
  }
751
748
  function backoffMs(attempt) {
752
749
  return 1e3 * 2 ** attempt;
@@ -780,9 +777,7 @@ function failedJobError(error) {
780
777
  retryable: error.retryable ?? false
781
778
  });
782
779
  }
783
- function pollInterval(attempt) {
784
- return attempt < 15 ? 1e3 : 3e3;
785
- }
780
+ var JOB_POLL_INTERVAL_MS = 3e3;
786
781
  var JOB_POLL_MAX_MS = 20 * 60 * 1e3;
787
782
  var BackendClient = class {
788
783
  http;
@@ -799,7 +794,7 @@ var BackendClient = class {
799
794
  async pollJob(jobId, signal) {
800
795
  const deadline = Date.now() + JOB_POLL_MAX_MS;
801
796
  const path16 = `/api/canvas/jobs/${encodeURIComponent(jobId)}`;
802
- for (let attempt = 0; ; attempt++) {
797
+ while (true) {
803
798
  if (signal?.aborted) {
804
799
  throw new BackendHttpError({ kind: "network", cause: signal.reason ?? new Error("aborted") });
805
800
  }
@@ -809,7 +804,7 @@ var BackendClient = class {
809
804
  if (Date.now() > deadline) {
810
805
  throw new BackendHttpError({ kind: "timeout", message: `job ${jobId} did not finish in time` });
811
806
  }
812
- await sleep(pollInterval(attempt));
807
+ await sleep(JOB_POLL_INTERVAL_MS);
813
808
  }
814
809
  }
815
810
  presignAssetUpload(sha256, mime, signal) {
@@ -819,35 +814,6 @@ var BackendClient = class {
819
814
  signal
820
815
  );
821
816
  }
822
- /** Remote cache lookup. A miss (404) — or an old backend without the route — returns null. */
823
- async getCacheEntry(cacheKey, signal) {
824
- try {
825
- const res = await this.http.getJson(`/api/canvas/cache/${encodeURIComponent(cacheKey)}`, signal);
826
- return res.entry;
827
- } catch (e) {
828
- if (e instanceof BackendHttpError && "status" in e.detail && e.detail.status === 404) return null;
829
- throw e;
830
- }
831
- }
832
- async putCacheEntry(entry, signal) {
833
- await this.http.putJson(
834
- `/api/canvas/cache/${encodeURIComponent(entry.cacheKey)}`,
835
- entry,
836
- signal
837
- );
838
- }
839
- /** Durable run-history record — POST /api/canvas/runs (idempotent server-side on runId). */
840
- async recordRun(payload, signal) {
841
- await this.http.postJson("/api/canvas/runs", payload, signal);
842
- }
843
- /**
844
- * Chat-scoped blueprint sync — POST /api/creatives/definition. Lets the
845
- * dashboard draw a scaffolded creative's workflow graph BEFORE the first run.
846
- * Additive on the backend (never archives siblings, never sets definitionPath).
847
- */
848
- async syncCreativeDefinition(payload, signal) {
849
- await this.http.postJson("/api/creatives/definition", payload, signal);
850
- }
851
817
  getArtifact(kind, name, version, signal) {
852
818
  const path16 = version ? `/api/canvas/artifacts/${encodeURIComponent(kind)}/${encodeURIComponent(name)}/${encodeURIComponent(version)}` : `/api/canvas/artifacts/${encodeURIComponent(kind)}/${encodeURIComponent(name)}`;
853
819
  return this.http.getJson(path16, signal);
@@ -871,17 +837,14 @@ function requireCredentialsFromEnv(env = process.env) {
871
837
  }
872
838
  return c;
873
839
  }
874
- function remoteCacheEnabledFromEnv(env = process.env) {
875
- return env.BAKER_CANVAS_REMOTE_CACHE !== "off";
876
- }
877
840
 
878
841
  // src/engine/engine/errors.ts
879
842
  function isBlocking(issue) {
880
843
  return issue.severity !== "warning";
881
844
  }
882
845
  var CanvasError = class extends Error {
883
- constructor(message2) {
884
- super(message2);
846
+ constructor(message) {
847
+ super(message);
885
848
  this.name = "CanvasError";
886
849
  }
887
850
  };
@@ -996,10 +959,10 @@ var ELEVENLABS_OUTPUT_FORMATS = [
996
959
  var ELEVENLABS_MAX_TEXT_CHARS = 45454;
997
960
  var ELEVENLABS_MAX_MUSIC_LENGTH_MS = 454545;
998
961
  var OPENROUTER_IMAGE_MIMES = ["image/png", "image/jpeg", "image/webp", "image/gif"];
999
- var REPLICATE_IMAGE_MIMES = ["image/png", "image/jpeg", "image/webp"];
1000
- var REPLICATE_VIDEO_MIMES = ["video/mp4", "video/webm", "video/quicktime"];
962
+ var FAL_IMAGE_MIMES = ["image/png", "image/jpeg", "image/webp"];
963
+ var FAL_VIDEO_MIMES = ["video/mp4", "video/webm", "video/quicktime"];
1001
964
  var DECONSTRUCT_VIDEO_MIMES = ["video/mp4", "video/webm", "video/quicktime"];
1002
- var REPLICATE_AUDIO_MIMES = ["audio/wav", "audio/mpeg", "audio/mp3"];
965
+ var FAL_AUDIO_MIMES = ["audio/wav", "audio/mpeg", "audio/mp3"];
1003
966
  var IMAGE_GENERATE_MODELS = [
1004
967
  "openai/gpt-5.4-image-2",
1005
968
  "google/gemini-3.5-flash",
@@ -1217,23 +1180,20 @@ var MODEL_REGISTRY = {
1217
1180
  },
1218
1181
  video_generate: {
1219
1182
  "bytedance/seedance-2.0": {
1220
- // Routed via Replicate's official `bytedance/seedance-2.0` model. NOTE:
1221
- // ByteDance's upstream "real person" likeness filter still blocks photoreal
1222
- // human reference frames on ANY reseller — the escape is a synthetic/AI
1223
- // presenter face or routing real faces to Veo, not the provider choice.
1183
+ // Routed via fal.ai (not OpenRouter) because OpenRouter's Seedance
1184
+ // passthrough rejects photorealistic human reference frames via
1185
+ // ByteDance's "real person" safety filter.
1224
1186
  label: "ByteDance Seedance 2.0",
1225
1187
  inputs: [],
1226
- optional_inputs: [{ kind: "image", mimes: REPLICATE_IMAGE_MIMES }],
1188
+ optional_inputs: [{ kind: "image", mimes: FAL_IMAGE_MIMES }],
1227
1189
  required: ["prompt"],
1228
1190
  params: {
1229
- // Replicate's Seedance wrapper hard-caps the prompt at 4000 chars; gate
1230
- // it here so an over-length prompt fails validate (free) not the billed call.
1231
- prompt: { kind: "string", maxLength: 4e3 },
1191
+ prompt: { kind: "string" },
1232
1192
  aspect_ratio: {
1233
1193
  kind: "string",
1234
1194
  enum: ["1:1", "3:4", "9:16", "4:3", "16:9", "21:9", "9:21"]
1235
1195
  },
1236
- resolution: { kind: "string", enum: ["480p", "720p", "1080p", "4k"] },
1196
+ resolution: { kind: "string", enum: ["480p", "720p", "1080p"] },
1237
1197
  duration: { kind: "number", enum: SEEDANCE_DURATIONS },
1238
1198
  seed: { kind: "number" },
1239
1199
  generate_audio: { kind: "boolean" }
@@ -1254,10 +1214,7 @@ var MODEL_REGISTRY = {
1254
1214
  duration: { kind: "number", enum: [4, 6, 8] },
1255
1215
  seed: { kind: "number" },
1256
1216
  generate_audio: { kind: "boolean" },
1257
- // Image-to-video and EU/UK/CH/MENA regions cap this at `allow_adult`;
1258
- // `allow_all` is text-to-video only. Allow both so an image-conditioned
1259
- // Veo clip (the real-face fallback) validates.
1260
- person_generation: { kind: "string", enum: ["allow_all", "allow_adult"] },
1217
+ person_generation: { kind: "string", enum: ["allow_all"] },
1261
1218
  enhance_prompt: { kind: "boolean" },
1262
1219
  conditioning_scale: { kind: "number" }
1263
1220
  }
@@ -1308,8 +1265,8 @@ var MODEL_REGISTRY = {
1308
1265
  "fal/veed-lipsync": {
1309
1266
  label: "VEED Lipsync (fal.ai)",
1310
1267
  inputs: [
1311
- { kind: "video", mimes: REPLICATE_VIDEO_MIMES },
1312
- { kind: "audio", mimes: REPLICATE_AUDIO_MIMES }
1268
+ { kind: "video", mimes: FAL_VIDEO_MIMES },
1269
+ { kind: "audio", mimes: FAL_AUDIO_MIMES }
1313
1270
  ],
1314
1271
  required: [],
1315
1272
  params: {}
@@ -1345,7 +1302,7 @@ var MODEL_REGISTRY = {
1345
1302
  // TARGET voice, preserving timing/prosody. Used to normalize a talking-head
1346
1303
  // clip's native (generator-chosen) voice into ONE consistent brand voice.
1347
1304
  label: "ElevenLabs Voice Changer (multilingual STS v2)",
1348
- inputs: [{ kind: "audio", mimes: REPLICATE_AUDIO_MIMES }],
1305
+ inputs: [{ kind: "audio", mimes: FAL_AUDIO_MIMES }],
1349
1306
  required: ["voice"],
1350
1307
  params: {
1351
1308
  voice: { kind: "string" },
@@ -1372,7 +1329,7 @@ var MODEL_REGISTRY = {
1372
1329
  },
1373
1330
  "elevenlabs/video-background-music-v1": {
1374
1331
  label: "ElevenLabs Video Background Music v1",
1375
- inputs: [{ kind: "video", mimes: REPLICATE_VIDEO_MIMES }],
1332
+ inputs: [{ kind: "video", mimes: FAL_VIDEO_MIMES }],
1376
1333
  required: [],
1377
1334
  params: {
1378
1335
  description: { kind: "string" },
@@ -1543,7 +1500,7 @@ function validateValue(key, value, schema, model) {
1543
1500
  }
1544
1501
 
1545
1502
  // src/engine/lib/concurrency.ts
1546
- var DEFAULT_CONCURRENCY = 8;
1503
+ var DEFAULT_CONCURRENCY = 5;
1547
1504
  function resolveConcurrency(...candidates) {
1548
1505
  for (const candidate of candidates) {
1549
1506
  if (candidate === void 0 || candidate === "") continue;
@@ -1605,160 +1562,6 @@ function encodeRandom() {
1605
1562
  return out;
1606
1563
  }
1607
1564
 
1608
- // src/engine/storage/remote-cache-store.ts
1609
- var CANVAS_ASSETS_URL_SEGMENT = "/canvas-assets/";
1610
- function isPersistedAssetRef(ref) {
1611
- const { url, sha256 } = ref;
1612
- if (typeof url !== "string" || typeof sha256 !== "string") return false;
1613
- return url.includes(`${CANVAS_ASSETS_URL_SEGMENT}${sha256.slice(0, 2)}/${sha256}`) || url.includes(`${CANVAS_ASSETS_URL_SEGMENT}${sha256}`);
1614
- }
1615
- function isAssetRefLike(value) {
1616
- return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.sha256 === "string" && typeof value.mime === "string";
1617
- }
1618
- function collectAssetRefLikes(value, out = []) {
1619
- if (Array.isArray(value)) {
1620
- for (const item of value) collectAssetRefLikes(item, out);
1621
- return out;
1622
- }
1623
- if (typeof value !== "object" || value === null) return out;
1624
- if (isAssetRefLike(value)) {
1625
- out.push(value);
1626
- }
1627
- for (const item of Object.values(value)) collectAssetRefLikes(item, out);
1628
- return out;
1629
- }
1630
- function entryFullyPersisted(entry) {
1631
- return collectAssetRefLikes(entry.outputs).every((ref) => isPersistedAssetRef(ref));
1632
- }
1633
- function stripLocalFields(entry) {
1634
- const clone = JSON.parse(JSON.stringify(entry));
1635
- for (const ref of collectAssetRefLikes(clone.outputs)) {
1636
- delete ref.path;
1637
- delete ref.bytes;
1638
- }
1639
- return clone;
1640
- }
1641
- var RemoteCacheStore = class {
1642
- client;
1643
- log;
1644
- constructor(client, log) {
1645
- this.client = client;
1646
- this.log = log ?? (() => void 0);
1647
- }
1648
- async get(cacheKey) {
1649
- return await this.client.getCacheEntry(cacheKey);
1650
- }
1651
- async put(entry) {
1652
- if (!entryFullyPersisted(entry)) {
1653
- this.log(`[cache ] ${entry.cacheKey.slice(0, 12)}\u2026 has local-only assets, kept local`);
1654
- return;
1655
- }
1656
- const stripped = stripLocalFields(entry);
1657
- if (stripped.refs.length > MAX_REMOTE_REFS) {
1658
- stripped.refs = stripped.refs.slice(0, MAX_REMOTE_REFS);
1659
- }
1660
- await this.client.putCacheEntry(stripped);
1661
- }
1662
- };
1663
- var MAX_REMOTE_REFS = 512;
1664
- var LayeredCacheStore = class {
1665
- rootDir;
1666
- local;
1667
- remote;
1668
- assets;
1669
- log;
1670
- constructor(opts) {
1671
- this.local = opts.local;
1672
- this.remote = opts.remote;
1673
- this.assets = opts.assets;
1674
- this.rootDir = opts.local.rootDir;
1675
- this.log = opts.log ?? (() => void 0);
1676
- }
1677
- async get(cacheKey) {
1678
- const localHit = await this.local.get(cacheKey);
1679
- if (localHit) return localHit;
1680
- let remoteEntry;
1681
- try {
1682
- remoteEntry = await this.remote.get(cacheKey);
1683
- } catch (e) {
1684
- this.log(`[cache ] remote lookup failed (${message(e)}) \u2014 treating as miss`);
1685
- return null;
1686
- }
1687
- if (!remoteEntry) return null;
1688
- let rehydrated;
1689
- try {
1690
- rehydrated = await this.rehydrate(remoteEntry);
1691
- } catch (e) {
1692
- this.log(`[cache ] ${cacheKey.slice(0, 12)}\u2026 rehydration failed (${message(e)}) \u2014 treating as miss`);
1693
- return null;
1694
- }
1695
- await this.local.put(rehydrated);
1696
- return rehydrated;
1697
- }
1698
- async put(entry) {
1699
- await this.local.put(entry);
1700
- try {
1701
- await this.remote.put(entry);
1702
- } catch (e) {
1703
- this.log(`[cache ] remote write failed (${message(e)}) \u2014 entry kept local`);
1704
- }
1705
- }
1706
- /**
1707
- * Download every referenced asset into the local content-addressed store
1708
- * (sha-verified) and stamp fresh local paths. Any ref that cannot be
1709
- * rehydrated fails the WHOLE entry — a partially-hydrated cache hit would
1710
- * crash materialization later with a far less actionable error.
1711
- */
1712
- async rehydrate(entry) {
1713
- const clone = JSON.parse(JSON.stringify(entry));
1714
- for (const ref of collectAssetRefLikes(clone.outputs)) {
1715
- if (!isPersistedAssetRef(ref)) {
1716
- throw new Error(`ref ${ref.sha256.slice(0, 12)}\u2026 has no persisted url`);
1717
- }
1718
- const ingested = await this.assets.ingestRemote({
1719
- kind: typeof ref.kind === "string" ? ref.kind : "json",
1720
- url: ref.url,
1721
- sha256: ref.sha256,
1722
- mime: ref.mime,
1723
- metadata: ref.metadata ?? void 0
1724
- });
1725
- ref.path = ingested.path;
1726
- }
1727
- return clone;
1728
- }
1729
- };
1730
- function message(e) {
1731
- return e instanceof Error ? e.message : String(e);
1732
- }
1733
-
1734
- // src/engine/nodes/remote/upload.ts
1735
- async function presignAndPut(args) {
1736
- const { putUrl, publicUrl } = await args.ctx.client.presignAssetUpload(args.sha256, args.mime, args.ctx.signal);
1737
- const putRes = await fetch(putUrl, {
1738
- method: "PUT",
1739
- body: new Uint8Array(args.bytes),
1740
- headers: { "Content-Type": args.mime },
1741
- signal: args.ctx.signal
1742
- });
1743
- if (!putRes.ok) {
1744
- throw new Error(`upload: presigned PUT failed ${putRes.status} ${putRes.statusText}`);
1745
- }
1746
- return publicUrl;
1747
- }
1748
- async function ensureUploaded(ref, ctx) {
1749
- if (ref.url) return ref;
1750
- const bytes = await ctx.assets.readBytes(ref.sha256, ref.mime);
1751
- const url = await presignAndPut({ bytes, sha256: ref.sha256, mime: ref.mime, ctx });
1752
- return { ...ref, url };
1753
- }
1754
- async function persistOutputAssetUrls(outputs, ctx) {
1755
- for (const ref of collectAssetRefLikes(outputs)) {
1756
- if (isPersistedAssetRef(ref)) continue;
1757
- const bytes = await ctx.assets.readBytes(ref.sha256, ref.mime);
1758
- ref.url = await presignAndPut({ bytes, sha256: ref.sha256, mime: ref.mime, ctx });
1759
- }
1760
- }
1761
-
1762
1565
  // src/engine/schema/canvas.ts
1763
1566
  import { z } from "zod";
1764
1567
  var REF_PREFIX = "$ref:";
@@ -1788,14 +1591,7 @@ var NodeDecl = z.object({
1788
1591
  version: z.string().min(1).optional(),
1789
1592
  inputs: z.record(z.string(), z.unknown()).optional(),
1790
1593
  params: z.record(z.string(), z.unknown()).optional(),
1791
- when: z.unknown().optional(),
1792
- // Regenerate knob. The engine is content-addressed: identical params + inputs
1793
- // return the cached render, so re-running an unchanged node NEVER re-bills or
1794
- // produces a new result. Bump this token (any string/number — a `2`, a `"v3"`,
1795
- // a note) and re-run to force THIS node to render fresh; because its new output
1796
- // changes downstream input hashes, everything depending on it regenerates too.
1797
- // This is the declarative "change a value, re-run, get a new render" affordance.
1798
- regenerate: z.union([z.string(), z.number()]).optional()
1594
+ when: z.unknown().optional()
1799
1595
  }).strict();
1800
1596
  var OutputRef = z.object({
1801
1597
  node: z.string(),
@@ -3045,7 +2841,6 @@ var Engine = class {
3045
2841
  cache;
3046
2842
  outputsDir;
3047
2843
  log;
3048
- persistAssets;
3049
2844
  constructor(opts) {
3050
2845
  this.registry = opts.registry;
3051
2846
  this.client = opts.client;
@@ -3053,7 +2848,6 @@ var Engine = class {
3053
2848
  this.cache = opts.cache;
3054
2849
  this.outputsDir = opts.outputsDir;
3055
2850
  this.log = opts.log ?? (() => void 0);
3056
- this.persistAssets = opts.persistAssets ?? false;
3057
2851
  }
3058
2852
  validate(canvas) {
3059
2853
  return validateCanvas(canvas, this.registry);
@@ -3077,16 +2871,7 @@ var Engine = class {
3077
2871
  const outputs = {};
3078
2872
  const counters = { cachedNodes: 0, totalCredits: 0 };
3079
2873
  const nodeRuns = [];
3080
- const graph = this.pruneToOutput(canvas, buildGraph(canvas));
3081
- const needsBytes = computeNeedsLocalBytes(canvas, graph, this.registry);
3082
- this.emitProgress(opts, {
3083
- kind: "plan",
3084
- nodes: [...graph.entries()].map(([id, deps]) => {
3085
- const node = canvas.nodes.find((n) => n.id === id);
3086
- return { node_id: id, node_type: node?.type ?? "unknown", deps: [...deps], params: node?.params };
3087
- })
3088
- });
3089
- await this.runLayers(canvas, graph, outputs, runId, writer, opts, counters, nodeRuns, needsBytes);
2874
+ await this.runLayers(canvas, outputs, runId, writer, opts, counters, nodeRuns);
3090
2875
  const output = pickFinalOutput(canvas, outputs);
3091
2876
  const stats = {
3092
2877
  total_nodes: canvas.nodes.length,
@@ -3109,51 +2894,39 @@ var Engine = class {
3109
2894
  `[done ] ${stats.cached_nodes}/${stats.total_nodes} cached, ${stats.total_credits} credits, ${stats.duration_ms}ms`
3110
2895
  );
3111
2896
  this.log(`outputs in: ${writer.runDir}`);
3112
- 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 };
3113
2898
  }
3114
- async runLayers(canvas, graph, outputs, runId, writer, opts, counters, nodeRuns, needsBytes) {
3115
- const layers = topologicalLayers(graph);
2899
+ async runLayers(canvas, outputs, runId, writer, opts, counters, nodeRuns) {
2900
+ const layers = topologicalLayers(this.pruneToOutput(canvas, buildGraph(canvas)));
3116
2901
  const limit = resolveConcurrency(opts.concurrency);
3117
2902
  for (const layer of layers) {
3118
- const settled = await mapWithConcurrency(layer, limit, (nodeId) => {
3119
- this.emitProgress(opts, { kind: "node_start", node_id: nodeId });
3120
- return this.executeOne(canvas, nodeId, outputs, runId, writer, opts, needsBytes.has(nodeId)).then((r) => {
2903
+ const settled = await mapWithConcurrency(
2904
+ layer,
2905
+ limit,
2906
+ (nodeId) => this.executeOne(canvas, nodeId, outputs, runId, writer, opts).then((r) => {
3121
2907
  if (r.cached) counters.cachedNodes++;
3122
2908
  counters.totalCredits += r.credits;
3123
2909
  const node = canvas.nodes.find((n) => n.id === nodeId);
3124
2910
  if (node) {
3125
- const run = {
2911
+ nodeRuns.push({
3126
2912
  node_id: nodeId,
3127
2913
  node_type: node.type,
3128
2914
  cached: r.cached,
3129
2915
  duration_ms: r.durationMs,
3130
2916
  credits: r.credits
3131
- };
3132
- nodeRuns.push(run);
3133
- this.emitProgress(opts, { kind: "node_settled", run, outputs: outputs[nodeId] ?? {} });
2917
+ });
3134
2918
  }
3135
- });
3136
- });
2919
+ })
2920
+ );
3137
2921
  const failures = [];
3138
2922
  settled.forEach((result, i) => {
3139
2923
  const nodeId = layer[i];
3140
- if (result.status === "rejected" && nodeId) {
3141
- failures.push({ nodeId, reason: result.reason });
3142
- this.emitProgress(opts, { kind: "node_failed", node_id: nodeId });
3143
- }
2924
+ if (result.status === "rejected" && nodeId) failures.push({ nodeId, reason: result.reason });
3144
2925
  });
3145
2926
  if (failures.length === 1 && failures[0]) throw failures[0].reason;
3146
2927
  if (failures.length > 1) throw new LayerExecutionError(failures);
3147
2928
  }
3148
2929
  }
3149
- /** Progress consumers are observers only — an exception there must never fail the run. */
3150
- emitProgress(opts, event) {
3151
- if (!opts.onProgress) return;
3152
- try {
3153
- opts.onProgress(event);
3154
- } catch {
3155
- }
3156
- }
3157
2930
  /**
3158
2931
  * Dead-node elimination: when the canvas declares an `output`, execute only the
3159
2932
  * nodes that output transitively depends on. Orphaned nodes (left by an edit or
@@ -3184,13 +2957,12 @@ var Engine = class {
3184
2957
  }
3185
2958
  await writer.writeManifest("_final", output);
3186
2959
  }
3187
- async executeOne(canvas, nodeId, outputs, runId, writer, opts, downloadOutputs) {
2960
+ async executeOne(canvas, nodeId, outputs, runId, writer, opts) {
3188
2961
  const node = canvas.nodes.find((n) => n.id === nodeId);
3189
2962
  if (!node) throw new Error(`executor: missing node ${nodeId}`);
3190
2963
  const def = this.registry.get(node.type);
3191
2964
  if (!def) throw new Error(`executor: missing registry entry for type ${node.type}`);
3192
- const regenerateToken = resolveRegenerateToken(node, opts.regenerate, runId);
3193
- const prepared = await prepareForExecution(node, outputs, def, canvas.cache_salt, regenerateToken, this.assets);
2965
+ const prepared = await prepareForExecution(node, outputs, def, canvas.cache_salt, this.assets);
3194
2966
  const policy = opts.cache_policy ?? "read_write";
3195
2967
  if (policy !== "bypass") {
3196
2968
  const cacheT0 = Date.now();
@@ -3208,27 +2980,17 @@ var Engine = class {
3208
2980
  nodeId: node.id,
3209
2981
  nodeType: node.type,
3210
2982
  cacheKey: prepared.cacheKey,
3211
- downloadOutputs,
3212
2983
  client: this.client,
3213
2984
  assets: this.assets,
3214
2985
  log: this.log,
3215
2986
  signal: opts.signal
3216
2987
  };
3217
- const preparedForExec = def.location === "local" ? { ...prepared, resolvedInputs: await this.materializeLocalInputs(prepared.resolvedInputs) } : prepared;
3218
- const { parsedInputs, parsedParams } = parseNodeArgs(def, preparedForExec, node.id, node.type);
2988
+ const { parsedInputs, parsedParams } = parseNodeArgs(def, prepared, node.id, node.type);
3219
2989
  const result = await invokeExecute(def, parsedInputs, parsedParams, ctx, node.id, node.type);
3220
2990
  const elapsed = Date.now() - t0;
3221
2991
  const credits = def.cost ? def.cost({ params: parsedParams }).credits : 0;
3222
2992
  const outputsObj = result;
3223
2993
  outputs[node.id] = outputsObj;
3224
- if (this.persistAssets) {
3225
- try {
3226
- await persistOutputAssetUrls(outputsObj, ctx);
3227
- } catch (e) {
3228
- const msg = e instanceof Error ? e.message : String(e);
3229
- this.log(`[warn ] ${node.id}: asset persistence failed (${msg}) \u2014 outputs stay local-only`);
3230
- }
3231
- }
3232
2994
  if (policy === "read_write") {
3233
2995
  await this.cache.put({
3234
2996
  cacheKey: prepared.cacheKey,
@@ -3257,42 +3019,8 @@ var Engine = class {
3257
3019
  }
3258
3020
  }
3259
3021
  }
3260
- /**
3261
- * Download any URL-only asset ref reachable in a local node's inputs so the
3262
- * bytes are on disk before the local runner stages them. Returns a copy —
3263
- * refs are replaced, never mutated in place, so the producer's cached output
3264
- * (shared object) keeps its URL-only shape.
3265
- */
3266
- async materializeLocalInputs(inputs) {
3267
- const fix = async (value) => {
3268
- if (Array.isArray(value)) return Promise.all(value.map(fix));
3269
- if (value && typeof value === "object") {
3270
- const v = value;
3271
- if (typeof v.kind === "string" && typeof v.url === "string" && typeof v.sha256 === "string" && typeof v.mime === "string" && typeof v.path !== "string") {
3272
- this.log(`[warn ] materializing URL-only input on demand (${v.kind}/${v.mime}) \u2014 missed graph edge`);
3273
- return this.assets.ingestRemote({
3274
- kind: v.kind,
3275
- url: v.url,
3276
- sha256: v.sha256,
3277
- mime: v.mime,
3278
- metadata: v.metadata
3279
- });
3280
- }
3281
- const out = {};
3282
- for (const [k, val] of Object.entries(v)) out[k] = await fix(val);
3283
- return out;
3284
- }
3285
- return value;
3286
- };
3287
- return await fix(inputs);
3288
- }
3289
3022
  };
3290
- function resolveRegenerateToken(node, forced, runId) {
3291
- if (forced?.has(node.id)) return `run:${runId}`;
3292
- if (node.regenerate !== void 0) return `node:${String(node.regenerate)}`;
3293
- return void 0;
3294
- }
3295
- async function prepareForExecution(node, outputs, def, cacheSalt, regenerateToken, assets) {
3023
+ async function prepareForExecution(node, outputs, def, cacheSalt, assets) {
3296
3024
  const resolvedInputs = resolveRefs(node.inputs ?? {}, { outputs }) ?? {};
3297
3025
  const resolvedParams = resolveRefs(node.params ?? {}, { outputs }) ?? {};
3298
3026
  const slotValues = await hydrateTextSlots(resolvedInputs, assets, node.id, node.type);
@@ -3305,9 +3033,6 @@ async function prepareForExecution(node, outputs, def, cacheSalt, regenerateToke
3305
3033
  throw new NodeExecutionError(node.id, node.type, { kind: "local", cause: e });
3306
3034
  }
3307
3035
  }
3308
- if (regenerateToken !== void 0) {
3309
- extras = { ...extras ?? {}, __regenerate__: regenerateToken };
3310
- }
3311
3036
  const cacheKey = computeCacheKey({
3312
3037
  node_id: node.type,
3313
3038
  node_version: def.version,
@@ -3346,16 +3071,6 @@ function pickFinalOutput(canvas, outputs) {
3346
3071
  const lastOut = outputs[last.id];
3347
3072
  return lastOut ? Object.values(lastOut)[0] : void 0;
3348
3073
  }
3349
- function computeNeedsLocalBytes(canvas, graph, registry) {
3350
- const typeById = new Map(canvas.nodes.map((n) => [n.id, n.type]));
3351
- const needs = /* @__PURE__ */ new Set();
3352
- for (const [consumerId, deps] of graph) {
3353
- const def = registry.get(typeById.get(consumerId) ?? "");
3354
- if (def?.location !== "local") continue;
3355
- for (const dep of deps) needs.add(dep);
3356
- }
3357
- return needs;
3358
- }
3359
3074
  function buildGraph(canvas) {
3360
3075
  const graph = /* @__PURE__ */ new Map();
3361
3076
  for (const n of canvas.nodes) graph.set(n.id, /* @__PURE__ */ new Set());
@@ -3486,16 +3201,7 @@ async function hydrateSlotValue(value, assets, nodeId, nodeType) {
3486
3201
  try {
3487
3202
  bytes = await assets.readBytes(value.sha256, value.mime);
3488
3203
  } catch (e) {
3489
- if (value.url) {
3490
- try {
3491
- await assets.ingestRemote({ kind: value.kind, url: value.url, sha256: value.sha256, mime: value.mime });
3492
- bytes = await assets.readBytes(value.sha256, value.mime);
3493
- } catch (e2) {
3494
- throw new NodeExecutionError(nodeId, nodeType, { kind: "local", cause: e2 });
3495
- }
3496
- } else {
3497
- throw new NodeExecutionError(nodeId, nodeType, { kind: "local", cause: e });
3498
- }
3204
+ throw new NodeExecutionError(nodeId, nodeType, { kind: "local", cause: e });
3499
3205
  }
3500
3206
  if (bytes.length > MAX_INLINE_TEXT_BYTES) {
3501
3207
  throw new NodeExecutionError(nodeId, nodeType, {
@@ -3603,6 +3309,27 @@ var FontRef = BaseAssetRef.extend({
3603
3309
  });
3604
3310
  var AssetRef = z4.discriminatedUnion("kind", [ImageRef, VideoRef, AudioRef, JsonRef, TextRef, FontRef]);
3605
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
+
3606
3333
  // src/engine/nodes/remote/delegate.ts
3607
3334
  function delegated(spec) {
3608
3335
  return {
@@ -3630,9 +3357,7 @@ async function callBackendExec(args) {
3630
3357
  nodeVersion: args.nodeVersion,
3631
3358
  params: args.params,
3632
3359
  inputs: serialized,
3633
- idempotency_key: idempotencyKey,
3634
- canvas_run_id: args.ctx.canvasRunId,
3635
- node_id: args.ctx.nodeId
3360
+ idempotency_key: idempotencyKey
3636
3361
  },
3637
3362
  args.ctx.signal
3638
3363
  );
@@ -3684,9 +3409,6 @@ async function ingestValue(value, ctx, declaredKind) {
3684
3409
  }
3685
3410
  if (isRawAsset(value)) {
3686
3411
  const kind = value.kind ?? declaredKind ?? "json";
3687
- if (ctx.downloadOutputs === false) {
3688
- return buildRef({ kind, sha: value.sha256, mime: value.mime, url: value.url, metadata: value.metadata });
3689
- }
3690
3412
  return ctx.assets.ingestRemote({
3691
3413
  kind,
3692
3414
  url: value.url,
@@ -3801,7 +3523,7 @@ function safePathname(rawUrl) {
3801
3523
  }
3802
3524
  var ingestNode = defineNode({
3803
3525
  id: "ingest",
3804
- version: "1.2.0",
3526
+ version: "1.1.0",
3805
3527
  category: "io",
3806
3528
  location: "local",
3807
3529
  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.",
@@ -3844,9 +3566,6 @@ function runStrategy(strategy, params, ctx) {
3844
3566
  }
3845
3567
  }
3846
3568
  async function execDirectFetch(params, ctx) {
3847
- if (params.expect === "image") {
3848
- return ingestImageUrl(params.url, ctx);
3849
- }
3850
3569
  const result = await callBackendExec({
3851
3570
  nodeType: "ingest",
3852
3571
  nodeVersion: ingestNode.version,
@@ -3857,37 +3576,6 @@ async function execDirectFetch(params, ctx) {
3857
3576
  });
3858
3577
  return assertAssetOutput(result, params.expect);
3859
3578
  }
3860
- async function ingestImageUrl(url, ctx) {
3861
- const res = await fetch(url);
3862
- if (!res.ok) {
3863
- throw localExecError(ctx, `fetch ${url} \u2192 ${res.status}`);
3864
- }
3865
- const ab = await res.arrayBuffer();
3866
- if (ab.byteLength > MAX_ASSET_BYTES) {
3867
- throw localExecError(ctx, `file_too_large: ${url} is ${ab.byteLength} bytes (limit ${MAX_ASSET_BYTES})`);
3868
- }
3869
- let normalized;
3870
- try {
3871
- normalized = await toModelSafeImage(Buffer.from(ab));
3872
- } catch (e) {
3873
- throw localExecError(ctx, `${url}: ${e.message}`);
3874
- }
3875
- if (normalized.rasterizedFrom) {
3876
- ctx.log(`ingest: normalized ${normalized.rasterizedFrom} URL -> PNG (${normalized.bytes.length}B)`);
3877
- }
3878
- return uploadAndIngest({
3879
- bytes: normalized.bytes,
3880
- kind: "image",
3881
- mime: normalized.mime,
3882
- metadata: {
3883
- source_url: url,
3884
- strategy: "direct_fetch",
3885
- ingested_at: (/* @__PURE__ */ new Date()).toISOString(),
3886
- ...normalized.rasterizedFrom ? { rasterized_from: normalized.rasterizedFrom } : {}
3887
- },
3888
- ctx
3889
- });
3890
- }
3891
3579
  async function execHandinger(params, ctx) {
3892
3580
  const result = await callBackendExec({
3893
3581
  nodeType: "ingest",
@@ -3924,15 +3612,7 @@ var EXT_TO_MIME = {
3924
3612
  jpeg: "image/jpeg",
3925
3613
  webp: "image/webp",
3926
3614
  gif: "image/gif",
3927
- // Non-model-safe rasters `toModelSafeImage` transcodes to PNG at ingest — they
3928
- // must resolve to an image mime here or the kind-check rejects the local file
3929
- // before normalization ever runs.
3930
3615
  avif: "image/avif",
3931
- heic: "image/heic",
3932
- heif: "image/heif",
3933
- tif: "image/tiff",
3934
- tiff: "image/tiff",
3935
- bmp: "image/bmp",
3936
3616
  mp4: "video/mp4",
3937
3617
  webm: "video/webm",
3938
3618
  mov: "video/quicktime",
@@ -3983,45 +3663,15 @@ async function rasterizeSvgToPng(bytes) {
3983
3663
  }
3984
3664
  return await sharp(bytes, { density }).png({ force: true, palette: false }).toBuffer();
3985
3665
  }
3986
- var MODEL_SAFE_IMAGE_MIMES = /* @__PURE__ */ new Set(["image/jpeg", "image/png", "image/gif", "image/webp"]);
3987
- async function toModelSafeImage(bytes) {
3988
- const safe = sniffImageMime(bytes);
3989
- if (safe && MODEL_SAFE_IMAGE_MIMES.has(safe)) {
3990
- return { bytes, mime: safe };
3991
- }
3992
- if (sniffSvg(bytes)) {
3993
- return { bytes: await rasterizeSvgToPng(bytes), mime: "image/png", rasterizedFrom: "svg" };
3994
- }
3995
- const { default: sharp } = await import("sharp");
3996
- try {
3997
- const img = sharp(bytes);
3998
- const format = (await img.metadata()).format;
3999
- const png = await img.png({ force: true }).toBuffer();
4000
- return { bytes: png, mime: "image/png", rasterizedFrom: format ?? "unknown" };
4001
- } catch (e) {
4002
- throw new Error(`bytes are not a decodable image (${e.message})`);
4003
- }
4004
- }
4005
- function hasAscii(buf, offset, sig) {
4006
- return buf.length >= offset + sig.length && buf.toString("ascii", offset, offset + sig.length) === sig;
4007
- }
4008
- var HEIC_BRANDS = /* @__PURE__ */ new Set(["heic", "heix", "heim", "heis", "hevc", "hevx", "mif1", "msf1", "heif"]);
4009
- function sniffIsoBmff(buf) {
4010
- if (!hasAscii(buf, 4, "ftyp")) return null;
4011
- const brand = buf.subarray(8, 12).toString("ascii");
4012
- if (brand === "avif" || brand === "avis") return "image/avif";
4013
- if (HEIC_BRANDS.has(brand)) return "image/heic";
4014
- return null;
4015
- }
4016
3666
  function sniffImageMime(buf) {
4017
3667
  if (buf.length < 4) return null;
4018
- if (buf[0] === 137 && hasAscii(buf, 1, "PNG")) return "image/png";
3668
+ if (buf[0] === 137 && buf[1] === 80 && buf[2] === 78 && buf[3] === 71) return "image/png";
4019
3669
  if (buf[0] === 255 && buf[1] === 216 && buf[2] === 255) return "image/jpeg";
4020
- if (hasAscii(buf, 0, "GIF")) return "image/gif";
4021
- if (hasAscii(buf, 0, "RIFF") && hasAscii(buf, 8, "WEBP")) return "image/webp";
4022
- if (hasAscii(buf, 0, "II*\0") || hasAscii(buf, 0, "MM\0*")) return "image/tiff";
4023
- if (hasAscii(buf, 0, "BM")) return "image/bmp";
4024
- return sniffIsoBmff(buf);
3670
+ if (buf[0] === 71 && buf[1] === 73 && buf[2] === 70) return "image/gif";
3671
+ if (buf.length >= 12 && buf[0] === 82 && buf[1] === 73 && buf[2] === 70 && buf[3] === 70 && buf[8] === 87 && buf[9] === 69 && buf[10] === 66 && buf[11] === 80) {
3672
+ return "image/webp";
3673
+ }
3674
+ return null;
4025
3675
  }
4026
3676
  function findBoxPayload(buf, start, end, type) {
4027
3677
  let offset = start;
@@ -4074,10 +3724,10 @@ function inferKindFromMime(mime) {
4074
3724
  if (mime.startsWith("font/")) return "font";
4075
3725
  return null;
4076
3726
  }
4077
- function localExecError(ctx, message2) {
3727
+ function localExecError(ctx, message) {
4078
3728
  return new NodeExecutionError(ctx.nodeId, ctx.nodeType, {
4079
3729
  kind: "local",
4080
- cause: new Error(`ingest: ${message2}`)
3730
+ cause: new Error(`ingest: ${message}`)
4081
3731
  });
4082
3732
  }
4083
3733
  async function execLocalFile(params, ctx) {
@@ -4124,20 +3774,17 @@ async function execLocalFile(params, ctx) {
4124
3774
  ctx.log(`ingest: local file ${stats.size}B mime=${mime}`);
4125
3775
  let outBytes = bytes;
4126
3776
  let outMime = mime;
4127
- let rasterizedFrom;
4128
- if (kind === "image") {
4129
- const normalized = await toModelSafeImage(bytes);
4130
- outBytes = normalized.bytes;
4131
- outMime = normalized.mime;
4132
- rasterizedFrom = normalized.rasterizedFrom;
4133
- if (rasterizedFrom) ctx.log(`ingest: normalized ${rasterizedFrom} -> PNG (${outBytes.length}B)`);
3777
+ if (mime === SVG_MIME) {
3778
+ outBytes = await rasterizeSvgToPng(bytes);
3779
+ outMime = "image/png";
3780
+ ctx.log(`ingest: rasterized SVG -> PNG (${outBytes.length}B)`);
4134
3781
  }
4135
3782
  const durationMs = probeVideoDurationMs(params.expect, outBytes, ctx);
4136
3783
  const ref = await uploadAndIngest({
4137
3784
  bytes: outBytes,
4138
3785
  kind: params.expect,
4139
3786
  mime: outMime,
4140
- metadata: localFileMetadata({ absPath, fileSize: stats.size, mime, durationMs, rasterizedFrom }),
3787
+ metadata: localFileMetadata({ absPath, fileSize: stats.size, mime, durationMs }),
4141
3788
  ctx
4142
3789
  });
4143
3790
  return withProbedDuration(ref, durationMs);
@@ -4155,7 +3802,7 @@ function localFileMetadata(args) {
4155
3802
  ingested_at: (/* @__PURE__ */ new Date()).toISOString(),
4156
3803
  file_size: args.fileSize,
4157
3804
  original_filename: path3.basename(args.absPath),
4158
- ...args.rasterizedFrom ? { rasterized_from: args.rasterizedFrom } : {},
3805
+ ...args.mime === SVG_MIME ? { rasterized_from: "svg" } : {},
4159
3806
  ...args.durationMs !== void 0 ? { duration_ms: args.durationMs } : {}
4160
3807
  };
4161
3808
  }
@@ -4747,7 +4394,7 @@ async function refToUrl(ref) {
4747
4394
  return `data:${ref.mime};base64,${bytes.toString("base64")}`;
4748
4395
  }
4749
4396
  var ASSET_KINDS = /* @__PURE__ */ new Set(["image", "video", "audio", "json", "text", "font"]);
4750
- function isAssetRefLike2(value) {
4397
+ function isAssetRefLike(value) {
4751
4398
  if (!value || typeof value !== "object") return false;
4752
4399
  const v = value;
4753
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");
@@ -5119,8 +4766,8 @@ var NEVER_BLOCK = [
5119
4766
  /text[_-]?occluded/i
5120
4767
  ];
5121
4768
  var UNAVAILABLE = /unknown command|command not found|not found|Did you mean|Unknown argument|ENOENT/i;
5122
- function isAdvisory(code, message2) {
5123
- const hay = `${code} ${message2}`;
4769
+ function isAdvisory(code, message) {
4770
+ const hay = `${code} ${message}`;
5124
4771
  return NEVER_BLOCK.some((re) => re.test(hay));
5125
4772
  }
5126
4773
  function parseCheckJson(raw) {
@@ -5148,10 +4795,10 @@ function classifyLint(json) {
5148
4795
  for (const f of findings) {
5149
4796
  const rec = f;
5150
4797
  const code = String(rec?.code ?? "");
5151
- const message2 = String(rec?.message ?? "");
4798
+ const message = String(rec?.message ?? "");
5152
4799
  const severity = String(rec?.severity ?? "info");
5153
- const blocking = severity === "error" && !isAdvisory(code, message2);
5154
- 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" });
5155
4802
  }
5156
4803
  return out;
5157
4804
  }
@@ -5163,9 +4810,9 @@ function classifyInspect(json) {
5163
4810
  for (const iss of issues) {
5164
4811
  const rec = iss;
5165
4812
  const code = String(rec?.code ?? rec?.type ?? "overflow");
5166
- const message2 = String(rec?.message ?? rec?.detail ?? JSON.stringify(iss));
4813
+ const message = String(rec?.message ?? rec?.detail ?? JSON.stringify(iss));
5167
4814
  const severity = rec?.severity ? String(rec.severity) : obj?.ok === false ? "error" : "warning";
5168
- out.push({ source: "inspect", code, message: message2, severity: severity === "error" ? "blocking" : "warning" });
4815
+ out.push({ source: "inspect", code, message, severity: severity === "error" ? "blocking" : "warning" });
5169
4816
  }
5170
4817
  return out;
5171
4818
  }
@@ -5610,7 +5257,7 @@ async function buildSubstitutionValues(compositionParams, meta, duration) {
5610
5257
  }
5611
5258
  function coerceImageParam(value) {
5612
5259
  if (typeof value === "string") return Promise.resolve(value);
5613
- if (isAssetRefLike2(value)) return refToUrl(value);
5260
+ if (isAssetRefLike(value)) return refToUrl(value);
5614
5261
  throw new Error("hyperframe_render: image param must be a URL string or AssetRef");
5615
5262
  }
5616
5263
  async function substituteCompositionFiles(tmp, values) {
@@ -5840,7 +5487,7 @@ async function buildSubstitutionValues2(compositionParams, meta) {
5840
5487
  }
5841
5488
  function coerceImageParam2(value) {
5842
5489
  if (typeof value === "string") return Promise.resolve(value);
5843
- if (isAssetRefLike2(value)) return refToUrl(value);
5490
+ if (isAssetRefLike(value)) return refToUrl(value);
5844
5491
  throw new Error("hyperframe_snapshot: image param must be a URL string or AssetRef");
5845
5492
  }
5846
5493
  async function substituteCompositionFiles2(tmp, values) {
@@ -6866,29 +6513,17 @@ function createEngineFromEnv(opts = {}) {
6866
6513
  const cacheDir = opts.cacheDir ?? path15.join(cwd, "canvas", ".cache");
6867
6514
  const outputsDir = opts.outputsDir ?? path15.join(cwd, "canvas");
6868
6515
  const creds = requireCredentialsFromEnv();
6869
- const client = new BackendClient({ baseUrl: creds.url, apiKey: creds.apiKey });
6870
- const assets = new LocalAssetStore(path15.join(cacheDir, "assets"));
6871
- const localCache = new LocalCacheStore(path15.join(cacheDir, "index"));
6872
- const remoteCacheEnabled = opts.remoteCache ?? remoteCacheEnabledFromEnv();
6873
- const cache = remoteCacheEnabled ? new LayeredCacheStore({
6874
- local: localCache,
6875
- remote: new RemoteCacheStore(client, opts.log),
6876
- assets,
6877
- log: opts.log
6878
- }) : localCache;
6879
6516
  return new Engine({
6880
6517
  registry: defaultRegistry(),
6881
- client,
6882
- assets,
6883
- 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")),
6884
6521
  outputsDir,
6885
- log: opts.log,
6886
- persistAssets: remoteCacheEnabled
6522
+ log: opts.log
6887
6523
  });
6888
6524
  }
6889
6525
 
6890
6526
  export {
6891
- requireCredentialsFromEnv,
6892
6527
  LayerExecutionError,
6893
6528
  describeFailureReason,
6894
6529
  SEEDANCE_DURATIONS,
@@ -6896,14 +6531,7 @@ export {
6896
6531
  IMAGE_GENERATE_MODELS,
6897
6532
  MODEL_REGISTRY,
6898
6533
  resolveConcurrency,
6899
- ulid,
6900
- isPersistedAssetRef,
6901
- collectAssetRefLikes,
6902
- REF_PREFIX,
6903
- parseRefExpr,
6904
- sha256Hex,
6905
6534
  elementMentionKeywords,
6906
- toModelSafeImage,
6907
6535
  BackendClient2 as BackendClient,
6908
6536
  Engine2 as Engine,
6909
6537
  LocalAssetStore2 as LocalAssetStore,
@@ -6914,4 +6542,4 @@ export {
6914
6542
  defaultRegistry,
6915
6543
  createEngineFromEnv
6916
6544
  };
6917
- //# sourceMappingURL=chunk-43KBQLP5.js.map
6545
+ //# sourceMappingURL=chunk-MWFJ5NOP.js.map