@koda-sl/baker-cli 0.115.0 → 0.116.0-dev.3bcc79f9c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -0
- package/dist/{chunk-ZWMMCLJ4.js → chunk-OCMOQOIJ.js} +241 -57
- package/dist/chunk-OCMOQOIJ.js.map +1 -0
- package/dist/cli.js +287 -86
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.d.ts +30 -0
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-ZWMMCLJ4.js.map +0 -1
package/README.md
CHANGED
|
@@ -2599,6 +2599,17 @@ baker canvas run my-canvas.json
|
|
|
2599
2599
|
# single node — that old serial workaround is obsolete.
|
|
2600
2600
|
baker canvas run my-canvas.json --parallel 8
|
|
2601
2601
|
|
|
2602
|
+
# 2c. Runs persist across sandboxes/sessions by default: node results sync to a
|
|
2603
|
+
# company-scoped remote cache (small JSON pointers; bytes stay in R2), so a FRESH
|
|
2604
|
+
# sandbox re-runs an already-computed canvas at zero credits — assets rehydrate
|
|
2605
|
+
# from R2, sha-verified. Every run also posts a durable history record (per-node
|
|
2606
|
+
# outputs, credits, cached/fresh) that powers the dashboard's Creatives
|
|
2607
|
+
# generations timeline. Opt out with --remote-cache off (env
|
|
2608
|
+
# BAKER_CANVAS_REMOTE_CACHE=off) and --no-record. With --remote-cache off,
|
|
2609
|
+
# assets are not uploaded, so a recorded run keeps its stats but has no
|
|
2610
|
+
# browsable outputs — pass --no-record too if you want nothing persisted.
|
|
2611
|
+
baker canvas run my-canvas.json --remote-cache off --no-record
|
|
2612
|
+
|
|
2602
2613
|
# 3. Inspect a finished run (per-node timing, file list, optional video thumbs)
|
|
2603
2614
|
baker canvas inspect <run_id>
|
|
2604
2615
|
|
|
@@ -3902,6 +3913,7 @@ baker canvas run ./reference-ad.video.canvas.json
|
|
|
3902
3913
|
| Flag | Default | Effect |
|
|
3903
3914
|
|---|---|---|
|
|
3904
3915
|
| `--out <path>` | `<video-dir>/<name>.video.canvas.json` | Where to write the canvas (composition is copied alongside). |
|
|
3916
|
+
| `--slug <slug>` | — | Creative slug (lowercase kebab): writes the canvas to `src/creatives/<slug>/<slug>.canvas.json` — the repo convention that attaches every run to the creative's dashboard generation history. `--out` wins over `--slug`. |
|
|
3905
3917
|
| `--frames <mode>` | `generate` | `generate` emits ONE recast keyframe per scene (the original frame is dropped so the dropped `el_*` assets drive identity); `reuse` wires the real extracted first+last frames straight into the clips (faithful, cheaper, no recast). |
|
|
3906
3918
|
| `--ambient` | off | Give silent **b-roll** scenes native diegetic ambient (Seedance `generate_audio`), mixed deep under the music bed. Talking scenes already carry voice; check levels don't muddy the mix before keeping it. |
|
|
3907
3919
|
| `--max-scenes <n>` | all source scenes | **Cost lever that reduces fidelity** — caps the deconstruct, MERGING away every scene beyond the cap (fewer cuts, lost beats). Prints a warning when set; omit it to reproduce every scene. |
|
|
@@ -3943,6 +3955,7 @@ baker canvas run ./static-ad.canvas.json
|
|
|
3943
3955
|
|---|---|---|
|
|
3944
3956
|
| `--context <text>` | — | Known provenance (advertiser, category, market) to ground the describe. |
|
|
3945
3957
|
| `--out <path>` | `<image-dir>/static-ad.canvas.json` (cwd when `<image>` is a URL) | Where to write the canvas (`prompt.json` is written alongside). |
|
|
3958
|
+
| `--slug <slug>` | — | Creative slug (lowercase kebab): writes the canvas to `src/creatives/<slug>/<slug>.canvas.json` — the repo convention that attaches every run to the creative's dashboard generation history. `--out` wins over `--slug`. |
|
|
3946
3959
|
| `--describe-model <id>` | registry default (`~google/gemini-pro-latest`) | Override the `image_describe` model. |
|
|
3947
3960
|
| `--select-model <id>` | registry default (`~google/gemini-flash-latest`) | Override the element-selection `text_generate` model. |
|
|
3948
3961
|
| `--layout-model <id>` | registry default (`~google/gemini-flash-latest`) | Override the global-layout `text_generate` model. |
|
|
@@ -4414,6 +4427,26 @@ import {
|
|
|
4414
4427
|
} from "@koda-sl/baker-cli/engine";
|
|
4415
4428
|
```
|
|
4416
4429
|
|
|
4430
|
+
## Creatives
|
|
4431
|
+
|
|
4432
|
+
Publish an approved canvas render as a first-class Baker creative. The image uploads to the Baker image library (tagged `creative`), a creative record is created/updated, and the command prints the creative reference JSON the dashboard renders in chat.
|
|
4433
|
+
|
|
4434
|
+
```bash
|
|
4435
|
+
baker creatives publish ./canvas/<run_id>/<final>.png --title "Spring Offer 4x5" \
|
|
4436
|
+
--slug spring-offer-4x5 --run-id r_01JXYZ... \
|
|
4437
|
+
--source-reference-url "https://www.facebook.com/ads/library/?id=..."
|
|
4438
|
+
```
|
|
4439
|
+
|
|
4440
|
+
| Flag | Effect |
|
|
4441
|
+
|---|---|
|
|
4442
|
+
| `--title <text>` | Required. Human title for the creative. |
|
|
4443
|
+
| `--slug <slug>` | Creative slug (`src/creatives/<slug>/`) — attaches the image to that creative's row, marks it `published`. |
|
|
4444
|
+
| `--run-id <r_…>` | Pins the approved generation from the creative's run history as the published one. |
|
|
4445
|
+
| `--source-reference-url <url>` | Original reference ad URL, recorded on the creative. |
|
|
4446
|
+
| `--context <text>` | Optional describe context for the uploaded image asset. |
|
|
4447
|
+
|
|
4448
|
+
Without `--slug` the command behaves as before (one creative record per published image). With `--slug` it upserts the repo-convention row — the same one the dashboard's Creatives tab and the `src/creatives/{slug}/` folder describe — so publish, repo sync, and run history all land on a single record regardless of order.
|
|
4449
|
+
|
|
4417
4450
|
## Help & Discovery
|
|
4418
4451
|
|
|
4419
4452
|
Every command supports `--help` for usage info:
|
|
@@ -24,9 +24,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
mod
|
|
25
25
|
));
|
|
26
26
|
|
|
27
|
-
//
|
|
27
|
+
// ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/node_modules/safe-stable-stringify/index.js
|
|
28
28
|
var require_safe_stable_stringify = __commonJS({
|
|
29
|
-
"
|
|
29
|
+
"../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/node_modules/safe-stable-stringify/index.js"(exports, module) {
|
|
30
30
|
"use strict";
|
|
31
31
|
var { hasOwnProperty } = Object.prototype;
|
|
32
32
|
var stringify = configure2();
|
|
@@ -159,9 +159,9 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
159
159
|
}
|
|
160
160
|
if (value) {
|
|
161
161
|
return (value2) => {
|
|
162
|
-
let
|
|
163
|
-
if (typeof value2 !== "function")
|
|
164
|
-
throw new Error(
|
|
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);
|
|
165
165
|
};
|
|
166
166
|
}
|
|
167
167
|
}
|
|
@@ -673,6 +673,9 @@ var HttpClient = class {
|
|
|
673
673
|
async postJson(path16, body, signal) {
|
|
674
674
|
return await this.requestJson("POST", path16, body, signal);
|
|
675
675
|
}
|
|
676
|
+
async putJson(path16, body, signal) {
|
|
677
|
+
return await this.requestJson("PUT", path16, body, signal);
|
|
678
|
+
}
|
|
676
679
|
async getJson(path16, signal) {
|
|
677
680
|
return await this.requestJson("GET", path16, void 0, signal);
|
|
678
681
|
}
|
|
@@ -700,8 +703,8 @@ var HttpClient = class {
|
|
|
700
703
|
try {
|
|
701
704
|
const res = await this.fetchFn(url, {
|
|
702
705
|
method,
|
|
703
|
-
headers: method === "
|
|
704
|
-
body: 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),
|
|
705
708
|
signal: controller.signal
|
|
706
709
|
});
|
|
707
710
|
if (res.ok) return { kind: "value", value: await res.json() };
|
|
@@ -738,33 +741,33 @@ async function parseErrorBody(res) {
|
|
|
738
741
|
const errObj = body.error ?? {};
|
|
739
742
|
return classifyHttpError(res.status, errObj, errObj.message ?? `HTTP ${res.status}`);
|
|
740
743
|
}
|
|
741
|
-
function classifyHttpError(status, errObj,
|
|
744
|
+
function classifyHttpError(status, errObj, message2) {
|
|
742
745
|
if (errObj.code === CONTENT_POLICY_CODE) {
|
|
743
|
-
return { kind: "content_policy", status, provider: errObj.provider, message };
|
|
746
|
+
return { kind: "content_policy", status, provider: errObj.provider, message: message2 };
|
|
744
747
|
}
|
|
745
748
|
if (status === 401 || status === 403) {
|
|
746
|
-
return { kind: "unauthorized", status, message };
|
|
749
|
+
return { kind: "unauthorized", status, message: message2 };
|
|
747
750
|
}
|
|
748
751
|
if (status === 400 || status === 422) {
|
|
749
|
-
return { kind: "validation", status, message, details: errObj.details };
|
|
752
|
+
return { kind: "validation", status, message: message2, details: errObj.details };
|
|
750
753
|
}
|
|
751
754
|
if (status === 502 || status === 504) {
|
|
752
755
|
if (errObj.code === "provider_timeout" || status === 504) {
|
|
753
|
-
return { kind: "timeout", provider: errObj.provider, message };
|
|
756
|
+
return { kind: "timeout", provider: errObj.provider, message: message2 };
|
|
754
757
|
}
|
|
755
758
|
return {
|
|
756
759
|
kind: "provider",
|
|
757
760
|
status,
|
|
758
761
|
provider: errObj.provider,
|
|
759
762
|
code: errObj.code ?? "provider_error",
|
|
760
|
-
message,
|
|
763
|
+
message: message2,
|
|
761
764
|
retryable: errObj.retryable ?? true
|
|
762
765
|
};
|
|
763
766
|
}
|
|
764
767
|
if (status >= 500 || status === 429) {
|
|
765
|
-
return { kind: "server", status, message };
|
|
768
|
+
return { kind: "server", status, message: message2 };
|
|
766
769
|
}
|
|
767
|
-
return { kind: "validation", status, message, details: errObj.details };
|
|
770
|
+
return { kind: "validation", status, message: message2, details: errObj.details };
|
|
768
771
|
}
|
|
769
772
|
function backoffMs(attempt) {
|
|
770
773
|
return 1e3 * 2 ** attempt;
|
|
@@ -835,6 +838,27 @@ var BackendClient = class {
|
|
|
835
838
|
signal
|
|
836
839
|
);
|
|
837
840
|
}
|
|
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
|
+
}
|
|
838
862
|
getArtifact(kind, name, version, signal) {
|
|
839
863
|
const path16 = version ? `/api/canvas/artifacts/${encodeURIComponent(kind)}/${encodeURIComponent(name)}/${encodeURIComponent(version)}` : `/api/canvas/artifacts/${encodeURIComponent(kind)}/${encodeURIComponent(name)}`;
|
|
840
864
|
return this.http.getJson(path16, signal);
|
|
@@ -858,14 +882,17 @@ function requireCredentialsFromEnv(env = process.env) {
|
|
|
858
882
|
}
|
|
859
883
|
return c;
|
|
860
884
|
}
|
|
885
|
+
function remoteCacheEnabledFromEnv(env = process.env) {
|
|
886
|
+
return env.BAKER_CANVAS_REMOTE_CACHE !== "off";
|
|
887
|
+
}
|
|
861
888
|
|
|
862
889
|
// src/engine/engine/errors.ts
|
|
863
890
|
function isBlocking(issue) {
|
|
864
891
|
return issue.severity !== "warning";
|
|
865
892
|
}
|
|
866
893
|
var CanvasError = class extends Error {
|
|
867
|
-
constructor(
|
|
868
|
-
super(
|
|
894
|
+
constructor(message2) {
|
|
895
|
+
super(message2);
|
|
869
896
|
this.name = "CanvasError";
|
|
870
897
|
}
|
|
871
898
|
};
|
|
@@ -922,7 +949,7 @@ function describeCause(c) {
|
|
|
922
949
|
}
|
|
923
950
|
}
|
|
924
951
|
|
|
925
|
-
//
|
|
952
|
+
// ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/node_modules/safe-stable-stringify/esm/wrapper.js
|
|
926
953
|
var import__ = __toESM(require_safe_stable_stringify(), 1);
|
|
927
954
|
var configure = import__.default.configure;
|
|
928
955
|
var wrapper_default = import__.default;
|
|
@@ -1583,6 +1610,158 @@ function encodeRandom() {
|
|
|
1583
1610
|
return out;
|
|
1584
1611
|
}
|
|
1585
1612
|
|
|
1613
|
+
// src/engine/storage/remote-cache-store.ts
|
|
1614
|
+
var CANVAS_ASSETS_URL_SEGMENT = "/canvas-assets/";
|
|
1615
|
+
function isPersistedAssetRef(ref) {
|
|
1616
|
+
return typeof ref.url === "string" && ref.url.includes(`${CANVAS_ASSETS_URL_SEGMENT}${ref.sha256}`);
|
|
1617
|
+
}
|
|
1618
|
+
function isAssetRefLike(value) {
|
|
1619
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.sha256 === "string" && typeof value.mime === "string";
|
|
1620
|
+
}
|
|
1621
|
+
function collectAssetRefLikes(value, out = []) {
|
|
1622
|
+
if (Array.isArray(value)) {
|
|
1623
|
+
for (const item of value) collectAssetRefLikes(item, out);
|
|
1624
|
+
return out;
|
|
1625
|
+
}
|
|
1626
|
+
if (typeof value !== "object" || value === null) return out;
|
|
1627
|
+
if (isAssetRefLike(value)) {
|
|
1628
|
+
out.push(value);
|
|
1629
|
+
}
|
|
1630
|
+
for (const item of Object.values(value)) collectAssetRefLikes(item, out);
|
|
1631
|
+
return out;
|
|
1632
|
+
}
|
|
1633
|
+
function entryFullyPersisted(entry) {
|
|
1634
|
+
return collectAssetRefLikes(entry.outputs).every((ref) => isPersistedAssetRef(ref));
|
|
1635
|
+
}
|
|
1636
|
+
function stripLocalFields(entry) {
|
|
1637
|
+
const clone = JSON.parse(JSON.stringify(entry));
|
|
1638
|
+
for (const ref of collectAssetRefLikes(clone.outputs)) {
|
|
1639
|
+
delete ref.path;
|
|
1640
|
+
delete ref.bytes;
|
|
1641
|
+
}
|
|
1642
|
+
return clone;
|
|
1643
|
+
}
|
|
1644
|
+
var RemoteCacheStore = class {
|
|
1645
|
+
client;
|
|
1646
|
+
log;
|
|
1647
|
+
constructor(client, log) {
|
|
1648
|
+
this.client = client;
|
|
1649
|
+
this.log = log ?? (() => void 0);
|
|
1650
|
+
}
|
|
1651
|
+
async get(cacheKey) {
|
|
1652
|
+
return await this.client.getCacheEntry(cacheKey);
|
|
1653
|
+
}
|
|
1654
|
+
async put(entry) {
|
|
1655
|
+
if (!entryFullyPersisted(entry)) {
|
|
1656
|
+
this.log(`[cache ] ${entry.cacheKey.slice(0, 12)}\u2026 has local-only assets, kept local`);
|
|
1657
|
+
return;
|
|
1658
|
+
}
|
|
1659
|
+
const stripped = stripLocalFields(entry);
|
|
1660
|
+
if (stripped.refs.length > MAX_REMOTE_REFS) {
|
|
1661
|
+
stripped.refs = stripped.refs.slice(0, MAX_REMOTE_REFS);
|
|
1662
|
+
}
|
|
1663
|
+
await this.client.putCacheEntry(stripped);
|
|
1664
|
+
}
|
|
1665
|
+
};
|
|
1666
|
+
var MAX_REMOTE_REFS = 512;
|
|
1667
|
+
var LayeredCacheStore = class {
|
|
1668
|
+
rootDir;
|
|
1669
|
+
local;
|
|
1670
|
+
remote;
|
|
1671
|
+
assets;
|
|
1672
|
+
log;
|
|
1673
|
+
constructor(opts) {
|
|
1674
|
+
this.local = opts.local;
|
|
1675
|
+
this.remote = opts.remote;
|
|
1676
|
+
this.assets = opts.assets;
|
|
1677
|
+
this.rootDir = opts.local.rootDir;
|
|
1678
|
+
this.log = opts.log ?? (() => void 0);
|
|
1679
|
+
}
|
|
1680
|
+
async get(cacheKey) {
|
|
1681
|
+
const localHit = await this.local.get(cacheKey);
|
|
1682
|
+
if (localHit) return localHit;
|
|
1683
|
+
let remoteEntry;
|
|
1684
|
+
try {
|
|
1685
|
+
remoteEntry = await this.remote.get(cacheKey);
|
|
1686
|
+
} catch (e) {
|
|
1687
|
+
this.log(`[cache ] remote lookup failed (${message(e)}) \u2014 treating as miss`);
|
|
1688
|
+
return null;
|
|
1689
|
+
}
|
|
1690
|
+
if (!remoteEntry) return null;
|
|
1691
|
+
let rehydrated;
|
|
1692
|
+
try {
|
|
1693
|
+
rehydrated = await this.rehydrate(remoteEntry);
|
|
1694
|
+
} catch (e) {
|
|
1695
|
+
this.log(`[cache ] ${cacheKey.slice(0, 12)}\u2026 rehydration failed (${message(e)}) \u2014 treating as miss`);
|
|
1696
|
+
return null;
|
|
1697
|
+
}
|
|
1698
|
+
await this.local.put(rehydrated);
|
|
1699
|
+
return rehydrated;
|
|
1700
|
+
}
|
|
1701
|
+
async put(entry) {
|
|
1702
|
+
await this.local.put(entry);
|
|
1703
|
+
try {
|
|
1704
|
+
await this.remote.put(entry);
|
|
1705
|
+
} catch (e) {
|
|
1706
|
+
this.log(`[cache ] remote write failed (${message(e)}) \u2014 entry kept local`);
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
/**
|
|
1710
|
+
* Download every referenced asset into the local content-addressed store
|
|
1711
|
+
* (sha-verified) and stamp fresh local paths. Any ref that cannot be
|
|
1712
|
+
* rehydrated fails the WHOLE entry — a partially-hydrated cache hit would
|
|
1713
|
+
* crash materialization later with a far less actionable error.
|
|
1714
|
+
*/
|
|
1715
|
+
async rehydrate(entry) {
|
|
1716
|
+
const clone = JSON.parse(JSON.stringify(entry));
|
|
1717
|
+
for (const ref of collectAssetRefLikes(clone.outputs)) {
|
|
1718
|
+
if (!isPersistedAssetRef(ref)) {
|
|
1719
|
+
throw new Error(`ref ${ref.sha256.slice(0, 12)}\u2026 has no persisted url`);
|
|
1720
|
+
}
|
|
1721
|
+
const ingested = await this.assets.ingestRemote({
|
|
1722
|
+
kind: typeof ref.kind === "string" ? ref.kind : "json",
|
|
1723
|
+
url: ref.url,
|
|
1724
|
+
sha256: ref.sha256,
|
|
1725
|
+
mime: ref.mime,
|
|
1726
|
+
metadata: ref.metadata ?? void 0
|
|
1727
|
+
});
|
|
1728
|
+
ref.path = ingested.path;
|
|
1729
|
+
}
|
|
1730
|
+
return clone;
|
|
1731
|
+
}
|
|
1732
|
+
};
|
|
1733
|
+
function message(e) {
|
|
1734
|
+
return e instanceof Error ? e.message : String(e);
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
// src/engine/nodes/remote/upload.ts
|
|
1738
|
+
async function presignAndPut(args) {
|
|
1739
|
+
const { putUrl, publicUrl } = await args.ctx.client.presignAssetUpload(args.sha256, args.mime, args.ctx.signal);
|
|
1740
|
+
const putRes = await fetch(putUrl, {
|
|
1741
|
+
method: "PUT",
|
|
1742
|
+
body: new Uint8Array(args.bytes),
|
|
1743
|
+
headers: { "Content-Type": args.mime },
|
|
1744
|
+
signal: args.ctx.signal
|
|
1745
|
+
});
|
|
1746
|
+
if (!putRes.ok) {
|
|
1747
|
+
throw new Error(`upload: presigned PUT failed ${putRes.status} ${putRes.statusText}`);
|
|
1748
|
+
}
|
|
1749
|
+
return publicUrl;
|
|
1750
|
+
}
|
|
1751
|
+
async function ensureUploaded(ref, ctx) {
|
|
1752
|
+
if (ref.url) return ref;
|
|
1753
|
+
const bytes = await ctx.assets.readBytes(ref.sha256, ref.mime);
|
|
1754
|
+
const url = await presignAndPut({ bytes, sha256: ref.sha256, mime: ref.mime, ctx });
|
|
1755
|
+
return { ...ref, url };
|
|
1756
|
+
}
|
|
1757
|
+
async function persistOutputAssetUrls(outputs, ctx) {
|
|
1758
|
+
for (const ref of collectAssetRefLikes(outputs)) {
|
|
1759
|
+
if (isPersistedAssetRef(ref)) continue;
|
|
1760
|
+
const bytes = await ctx.assets.readBytes(ref.sha256, ref.mime);
|
|
1761
|
+
ref.url = await presignAndPut({ bytes, sha256: ref.sha256, mime: ref.mime, ctx });
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1586
1765
|
// src/engine/schema/canvas.ts
|
|
1587
1766
|
import { z } from "zod";
|
|
1588
1767
|
var REF_PREFIX = "$ref:";
|
|
@@ -2799,6 +2978,7 @@ var Engine = class {
|
|
|
2799
2978
|
cache;
|
|
2800
2979
|
outputsDir;
|
|
2801
2980
|
log;
|
|
2981
|
+
persistAssets;
|
|
2802
2982
|
constructor(opts) {
|
|
2803
2983
|
this.registry = opts.registry;
|
|
2804
2984
|
this.client = opts.client;
|
|
@@ -2806,6 +2986,7 @@ var Engine = class {
|
|
|
2806
2986
|
this.cache = opts.cache;
|
|
2807
2987
|
this.outputsDir = opts.outputsDir;
|
|
2808
2988
|
this.log = opts.log ?? (() => void 0);
|
|
2989
|
+
this.persistAssets = opts.persistAssets ?? false;
|
|
2809
2990
|
}
|
|
2810
2991
|
validate(canvas) {
|
|
2811
2992
|
return validateCanvas(canvas, this.registry);
|
|
@@ -2852,7 +3033,7 @@ var Engine = class {
|
|
|
2852
3033
|
`[done ] ${stats.cached_nodes}/${stats.total_nodes} cached, ${stats.total_credits} credits, ${stats.duration_ms}ms`
|
|
2853
3034
|
);
|
|
2854
3035
|
this.log(`outputs in: ${writer.runDir}`);
|
|
2855
|
-
return { run_id: runId, output, outputs_by_node: outputs, stats, outputs_dir: writer.runDir };
|
|
3036
|
+
return { run_id: runId, output, outputs_by_node: outputs, stats, outputs_dir: writer.runDir, node_runs: nodeRuns };
|
|
2856
3037
|
}
|
|
2857
3038
|
async runLayers(canvas, outputs, runId, writer, opts, counters, nodeRuns) {
|
|
2858
3039
|
const layers = topologicalLayers(this.pruneToOutput(canvas, buildGraph(canvas)));
|
|
@@ -2949,6 +3130,14 @@ var Engine = class {
|
|
|
2949
3130
|
const credits = def.cost ? def.cost({ params: parsedParams }).credits : 0;
|
|
2950
3131
|
const outputsObj = result;
|
|
2951
3132
|
outputs[node.id] = outputsObj;
|
|
3133
|
+
if (this.persistAssets) {
|
|
3134
|
+
try {
|
|
3135
|
+
await persistOutputAssetUrls(outputsObj, ctx);
|
|
3136
|
+
} catch (e) {
|
|
3137
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
3138
|
+
this.log(`[warn ] ${node.id}: asset persistence failed (${msg}) \u2014 outputs stay local-only`);
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
2952
3141
|
if (policy === "read_write") {
|
|
2953
3142
|
await this.cache.put({
|
|
2954
3143
|
cacheKey: prepared.cacheKey,
|
|
@@ -3267,27 +3456,6 @@ var FontRef = BaseAssetRef.extend({
|
|
|
3267
3456
|
});
|
|
3268
3457
|
var AssetRef = z4.discriminatedUnion("kind", [ImageRef, VideoRef, AudioRef, JsonRef, TextRef, FontRef]);
|
|
3269
3458
|
|
|
3270
|
-
// src/engine/nodes/remote/upload.ts
|
|
3271
|
-
async function presignAndPut(args) {
|
|
3272
|
-
const { putUrl, publicUrl } = await args.ctx.client.presignAssetUpload(args.sha256, args.mime, args.ctx.signal);
|
|
3273
|
-
const putRes = await fetch(putUrl, {
|
|
3274
|
-
method: "PUT",
|
|
3275
|
-
body: new Uint8Array(args.bytes),
|
|
3276
|
-
headers: { "Content-Type": args.mime },
|
|
3277
|
-
signal: args.ctx.signal
|
|
3278
|
-
});
|
|
3279
|
-
if (!putRes.ok) {
|
|
3280
|
-
throw new Error(`upload: presigned PUT failed ${putRes.status} ${putRes.statusText}`);
|
|
3281
|
-
}
|
|
3282
|
-
return publicUrl;
|
|
3283
|
-
}
|
|
3284
|
-
async function ensureUploaded(ref, ctx) {
|
|
3285
|
-
if (ref.url) return ref;
|
|
3286
|
-
const bytes = await ctx.assets.readBytes(ref.sha256, ref.mime);
|
|
3287
|
-
const url = await presignAndPut({ bytes, sha256: ref.sha256, mime: ref.mime, ctx });
|
|
3288
|
-
return { ...ref, url };
|
|
3289
|
-
}
|
|
3290
|
-
|
|
3291
3459
|
// src/engine/nodes/remote/delegate.ts
|
|
3292
3460
|
function delegated(spec) {
|
|
3293
3461
|
return {
|
|
@@ -3682,10 +3850,10 @@ function inferKindFromMime(mime) {
|
|
|
3682
3850
|
if (mime.startsWith("font/")) return "font";
|
|
3683
3851
|
return null;
|
|
3684
3852
|
}
|
|
3685
|
-
function localExecError(ctx,
|
|
3853
|
+
function localExecError(ctx, message2) {
|
|
3686
3854
|
return new NodeExecutionError(ctx.nodeId, ctx.nodeType, {
|
|
3687
3855
|
kind: "local",
|
|
3688
|
-
cause: new Error(`ingest: ${
|
|
3856
|
+
cause: new Error(`ingest: ${message2}`)
|
|
3689
3857
|
});
|
|
3690
3858
|
}
|
|
3691
3859
|
async function execLocalFile(params, ctx) {
|
|
@@ -4352,7 +4520,7 @@ async function refToUrl(ref) {
|
|
|
4352
4520
|
return `data:${ref.mime};base64,${bytes.toString("base64")}`;
|
|
4353
4521
|
}
|
|
4354
4522
|
var ASSET_KINDS = /* @__PURE__ */ new Set(["image", "video", "audio", "json", "text", "font"]);
|
|
4355
|
-
function
|
|
4523
|
+
function isAssetRefLike2(value) {
|
|
4356
4524
|
if (!value || typeof value !== "object") return false;
|
|
4357
4525
|
const v = value;
|
|
4358
4526
|
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");
|
|
@@ -4724,8 +4892,8 @@ var NEVER_BLOCK = [
|
|
|
4724
4892
|
/text[_-]?occluded/i
|
|
4725
4893
|
];
|
|
4726
4894
|
var UNAVAILABLE = /unknown command|command not found|not found|Did you mean|Unknown argument|ENOENT/i;
|
|
4727
|
-
function isAdvisory(code,
|
|
4728
|
-
const hay = `${code} ${
|
|
4895
|
+
function isAdvisory(code, message2) {
|
|
4896
|
+
const hay = `${code} ${message2}`;
|
|
4729
4897
|
return NEVER_BLOCK.some((re) => re.test(hay));
|
|
4730
4898
|
}
|
|
4731
4899
|
function parseCheckJson(raw) {
|
|
@@ -4753,10 +4921,10 @@ function classifyLint(json) {
|
|
|
4753
4921
|
for (const f of findings) {
|
|
4754
4922
|
const rec = f;
|
|
4755
4923
|
const code = String(rec?.code ?? "");
|
|
4756
|
-
const
|
|
4924
|
+
const message2 = String(rec?.message ?? "");
|
|
4757
4925
|
const severity = String(rec?.severity ?? "info");
|
|
4758
|
-
const blocking = severity === "error" && !isAdvisory(code,
|
|
4759
|
-
out.push({ source: "lint", code, message, severity: blocking ? "blocking" : "warning" });
|
|
4926
|
+
const blocking = severity === "error" && !isAdvisory(code, message2);
|
|
4927
|
+
out.push({ source: "lint", code, message: message2, severity: blocking ? "blocking" : "warning" });
|
|
4760
4928
|
}
|
|
4761
4929
|
return out;
|
|
4762
4930
|
}
|
|
@@ -4768,9 +4936,9 @@ function classifyInspect(json) {
|
|
|
4768
4936
|
for (const iss of issues) {
|
|
4769
4937
|
const rec = iss;
|
|
4770
4938
|
const code = String(rec?.code ?? rec?.type ?? "overflow");
|
|
4771
|
-
const
|
|
4939
|
+
const message2 = String(rec?.message ?? rec?.detail ?? JSON.stringify(iss));
|
|
4772
4940
|
const severity = rec?.severity ? String(rec.severity) : obj?.ok === false ? "error" : "warning";
|
|
4773
|
-
out.push({ source: "inspect", code, message, severity: severity === "error" ? "blocking" : "warning" });
|
|
4941
|
+
out.push({ source: "inspect", code, message: message2, severity: severity === "error" ? "blocking" : "warning" });
|
|
4774
4942
|
}
|
|
4775
4943
|
return out;
|
|
4776
4944
|
}
|
|
@@ -5215,7 +5383,7 @@ async function buildSubstitutionValues(compositionParams, meta, duration) {
|
|
|
5215
5383
|
}
|
|
5216
5384
|
function coerceImageParam(value) {
|
|
5217
5385
|
if (typeof value === "string") return Promise.resolve(value);
|
|
5218
|
-
if (
|
|
5386
|
+
if (isAssetRefLike2(value)) return refToUrl(value);
|
|
5219
5387
|
throw new Error("hyperframe_render: image param must be a URL string or AssetRef");
|
|
5220
5388
|
}
|
|
5221
5389
|
async function substituteCompositionFiles(tmp, values) {
|
|
@@ -5445,7 +5613,7 @@ async function buildSubstitutionValues2(compositionParams, meta) {
|
|
|
5445
5613
|
}
|
|
5446
5614
|
function coerceImageParam2(value) {
|
|
5447
5615
|
if (typeof value === "string") return Promise.resolve(value);
|
|
5448
|
-
if (
|
|
5616
|
+
if (isAssetRefLike2(value)) return refToUrl(value);
|
|
5449
5617
|
throw new Error("hyperframe_snapshot: image param must be a URL string or AssetRef");
|
|
5450
5618
|
}
|
|
5451
5619
|
async function substituteCompositionFiles2(tmp, values) {
|
|
@@ -6471,17 +6639,29 @@ function createEngineFromEnv(opts = {}) {
|
|
|
6471
6639
|
const cacheDir = opts.cacheDir ?? path15.join(cwd, "canvas", ".cache");
|
|
6472
6640
|
const outputsDir = opts.outputsDir ?? path15.join(cwd, "canvas");
|
|
6473
6641
|
const creds = requireCredentialsFromEnv();
|
|
6642
|
+
const client = new BackendClient({ baseUrl: creds.url, apiKey: creds.apiKey });
|
|
6643
|
+
const assets = new LocalAssetStore(path15.join(cacheDir, "assets"));
|
|
6644
|
+
const localCache = new LocalCacheStore(path15.join(cacheDir, "index"));
|
|
6645
|
+
const remoteCacheEnabled = opts.remoteCache ?? remoteCacheEnabledFromEnv();
|
|
6646
|
+
const cache = remoteCacheEnabled ? new LayeredCacheStore({
|
|
6647
|
+
local: localCache,
|
|
6648
|
+
remote: new RemoteCacheStore(client, opts.log),
|
|
6649
|
+
assets,
|
|
6650
|
+
log: opts.log
|
|
6651
|
+
}) : localCache;
|
|
6474
6652
|
return new Engine({
|
|
6475
6653
|
registry: defaultRegistry(),
|
|
6476
|
-
client
|
|
6477
|
-
assets
|
|
6478
|
-
cache
|
|
6654
|
+
client,
|
|
6655
|
+
assets,
|
|
6656
|
+
cache,
|
|
6479
6657
|
outputsDir,
|
|
6480
|
-
log: opts.log
|
|
6658
|
+
log: opts.log,
|
|
6659
|
+
persistAssets: remoteCacheEnabled
|
|
6481
6660
|
});
|
|
6482
6661
|
}
|
|
6483
6662
|
|
|
6484
6663
|
export {
|
|
6664
|
+
requireCredentialsFromEnv,
|
|
6485
6665
|
LayerExecutionError,
|
|
6486
6666
|
describeFailureReason,
|
|
6487
6667
|
SEEDANCE_DURATIONS,
|
|
@@ -6489,6 +6669,10 @@ export {
|
|
|
6489
6669
|
IMAGE_GENERATE_MODELS,
|
|
6490
6670
|
MODEL_REGISTRY,
|
|
6491
6671
|
resolveConcurrency,
|
|
6672
|
+
ulid,
|
|
6673
|
+
isPersistedAssetRef,
|
|
6674
|
+
collectAssetRefLikes,
|
|
6675
|
+
sha256Hex,
|
|
6492
6676
|
BackendClient2 as BackendClient,
|
|
6493
6677
|
Engine2 as Engine,
|
|
6494
6678
|
LocalAssetStore2 as LocalAssetStore,
|
|
@@ -6499,4 +6683,4 @@ export {
|
|
|
6499
6683
|
defaultRegistry,
|
|
6500
6684
|
createEngineFromEnv
|
|
6501
6685
|
};
|
|
6502
|
-
//# sourceMappingURL=chunk-
|
|
6686
|
+
//# sourceMappingURL=chunk-OCMOQOIJ.js.map
|