@koda-sl/baker-cli 0.421.0 → 0.422.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/README.md +7 -1
  2. package/dist/{chunk-GLPQQP5C.js → chunk-2S6QIXLX.js} +80 -9
  3. package/dist/{chunk-GLPQQP5C.js.map → chunk-2S6QIXLX.js.map} +1 -1
  4. package/dist/{chunk-LRRWIOJG.js → chunk-3X2EMLBQ.js} +3 -3
  5. package/dist/{chunk-YZLLC7DL.js → chunk-5JVAP5DC.js} +12 -1
  6. package/dist/chunk-5JVAP5DC.js.map +1 -0
  7. package/dist/{chunk-ZADYWHL5.js → chunk-G5L5FX2B.js} +3 -3
  8. package/dist/{chunk-FKFJKE75.js → chunk-S36N4SOX.js} +2 -2
  9. package/dist/{chunk-EXWIIEZP.js → chunk-XNFV6BQU.js} +4 -4
  10. package/dist/cli.js +703 -607
  11. package/dist/cli.js.map +1 -1
  12. package/dist/{client-CUHMSNK5.js → client-PIKJZFRA.js} +4 -4
  13. package/dist/engine/index.js +3 -3
  14. package/dist/{env-PJTJTW3N.js → env-6UAIAY4M.js} +6 -2
  15. package/dist/{output-A2ASA36U.js → output-CFDBWWYJ.js} +5 -5
  16. package/dist/{shared-LV6PIKFE.js → shared-3O5RKQYZ.js} +6 -6
  17. package/package.json +1 -1
  18. package/dist/chunk-YZLLC7DL.js.map +0 -1
  19. /package/dist/{chunk-LRRWIOJG.js.map → chunk-3X2EMLBQ.js.map} +0 -0
  20. /package/dist/{chunk-ZADYWHL5.js.map → chunk-G5L5FX2B.js.map} +0 -0
  21. /package/dist/{chunk-FKFJKE75.js.map → chunk-S36N4SOX.js.map} +0 -0
  22. /package/dist/{chunk-EXWIIEZP.js.map → chunk-XNFV6BQU.js.map} +0 -0
  23. /package/dist/{client-CUHMSNK5.js.map → client-PIKJZFRA.js.map} +0 -0
  24. /package/dist/{env-PJTJTW3N.js.map → env-6UAIAY4M.js.map} +0 -0
  25. /package/dist/{output-A2ASA36U.js.map → output-CFDBWWYJ.js.map} +0 -0
  26. /package/dist/{shared-LV6PIKFE.js.map → shared-3O5RKQYZ.js.map} +0 -0
package/README.md CHANGED
@@ -5988,14 +5988,20 @@ baker canvas catalog --category audio --full
5988
5988
  // Path source — an absolute path is used as-is, and so resolves only on the machine that wrote it.
5989
5989
  // Keep canvases portable: creative canvases are validated for this (`absolute-reference`).
5990
5990
  { "id": "src", "type": "ingest", "params": { "source": "path", "path": "/abs/work/clip.mp4", "expect": "video" } }
5991
+
5992
+ // URL source — an earlier output, by its durable canvas-assets URL: the same bytes and sha back
5993
+ { "id": "src", "type": "ingest", "params": { "source": "url", "url": "https://media.withbaker.com/canvas-assets/5b/5b50…c672.mp3", "expect": "audio" } }
5991
5994
  ```
5992
5995
 
5996
+ **Never point `path` into `canvas/`.** `canvas/.cache/` and `canvas/r_<id>/` are the engine's own working folder and are never committed, so a canvas that reads from them renders only where it was written. `canvas validate` and `canvas run` rewrite such a path to the durable URL of the same bytes (reported under `normalized_paths`), keeping the file first if the store does not hold it yet.
5997
+
5993
5998
  **Strategy resolution** (deterministic from `source` + `expect` + URL extension for text):
5994
5999
 
5995
6000
  | `source` | `expect` | URL / path | Strategy |
5996
6001
  |----------|---------------|----------------------------------|-----------------|
5997
6002
  | path | any | any | `local_file` — read from disk, sniff mime, upload to R2 via presign |
5998
- | url | video / audio | any | `yt_dlp` (works on platform URLs *and* direct file URLs via the generic extractor) |
6003
+ | url | any | `…/canvas-assets/<xx>/<sha>.<ext>` | `canvas_asset` — the stored asset, verbatim, its sha checked |
6004
+ | url | video / audio | any other | `yt_dlp` (works on platform URLs *and* direct file URLs via the generic extractor; verifies TLS against the system bundle) |
5999
6005
  | url | image / json / font | any | `direct_fetch` (HTTP GET) |
6000
6006
  | url | text | ends in `.txt` or `.md` | `direct_fetch` |
6001
6007
  | url | text | anything else (HTML, PDF, …) | `firecrawl` `/v2/scrape` (extracted markdown — HTML/PDF made readable) |
@@ -1,12 +1,13 @@
1
1
  import {
2
2
  debugLogHttp,
3
3
  readBodyForLog
4
- } from "./chunk-FKFJKE75.js";
4
+ } from "./chunk-S36N4SOX.js";
5
5
  import {
6
6
  API_KEY_FORMAT_MESSAGE,
7
7
  DIRECT_ROUTE,
8
8
  __commonJS,
9
9
  __toESM,
10
+ canvasAssetOriginsSetting,
10
11
  captureProxyCredentials,
11
12
  childEnvWith,
12
13
  hasFrameVisionKey,
@@ -21,7 +22,7 @@ import {
21
22
  refuseNonPublicUrl,
22
23
  requireConnection,
23
24
  shouldEscalate
24
- } from "./chunk-YZLLC7DL.js";
25
+ } from "./chunk-5JVAP5DC.js";
25
26
 
26
27
  // ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/index.js
27
28
  var require_safe_stable_stringify = __commonJS({
@@ -12495,6 +12496,38 @@ function isAssetRefLike(v) {
12495
12496
 
12496
12497
  // src/engine/storage/remote-cache-store.ts
12497
12498
  var CANVAS_ASSETS_URL_SEGMENT = "/canvas-assets/";
12499
+ function canvasAssetOrigins(setting = canvasAssetOriginsSetting()) {
12500
+ return (setting ?? "https://media.withbaker.com,https://media.dev-baker.com").split(",").map((origin) => origin.trim().replace(/\/+$/, "")).filter(Boolean);
12501
+ }
12502
+ function isCanvasAssetOrigin(url) {
12503
+ try {
12504
+ const { origin } = new URL(url);
12505
+ return canvasAssetOrigins().some((allowed) => {
12506
+ try {
12507
+ return new URL(allowed).origin === origin;
12508
+ } catch {
12509
+ return false;
12510
+ }
12511
+ });
12512
+ } catch {
12513
+ return false;
12514
+ }
12515
+ }
12516
+ function parseCanvasAssetUrl(url) {
12517
+ let pathname;
12518
+ try {
12519
+ const parsed = new URL(url);
12520
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
12521
+ pathname = parsed.pathname;
12522
+ } catch {
12523
+ return null;
12524
+ }
12525
+ const match = /\/canvas-assets\/([0-9a-f]{2})\/([0-9a-f]{64})\.([a-z0-9]+)$/.exec(pathname);
12526
+ if (!match) return null;
12527
+ const [, shard, sha256, ext] = match;
12528
+ if (shard === void 0 || sha256 === void 0 || ext === void 0 || !sha256.startsWith(shard)) return null;
12529
+ return { sha256, ext };
12530
+ }
12498
12531
  function isPersistedAssetRef(ref) {
12499
12532
  const { url, sha256 } = ref;
12500
12533
  if (typeof url !== "string" || typeof sha256 !== "string") return false;
@@ -18202,6 +18235,7 @@ function ytDlpBlockSignal(stderr) {
18202
18235
  var YT_DLP_BIN = "yt-dlp";
18203
18236
  var YT_DLP_TIMEOUT_MS = 10 * 60 * 1e3;
18204
18237
  var YT_DLP_PROBE_TIMEOUT_MS = 60 * 1e3;
18238
+ var TRUST_SYSTEM_CERTS = ["--compat-options", "no-certifi"];
18205
18239
  var YtDlpError = class extends Error {
18206
18240
  constructor(message2, stderrTail) {
18207
18241
  super(message2);
@@ -18222,6 +18256,7 @@ async function runYtDlp(args) {
18222
18256
  "--no-warnings",
18223
18257
  "--quiet",
18224
18258
  "--write-info-json",
18259
+ ...TRUST_SYSTEM_CERTS,
18225
18260
  "-o",
18226
18261
  outTemplate
18227
18262
  ];
@@ -18270,7 +18305,8 @@ async function probeYtDlp(args) {
18270
18305
  // exits "Requested format is not available" and we learn nothing — measured
18271
18306
  // against a stale build, which returned full metadata the moment this flag
18272
18307
  // was added. Duration and id come from the page, not from a format.
18273
- "--ignore-no-formats-error"
18308
+ "--ignore-no-formats-error",
18309
+ ...TRUST_SYSTEM_CERTS
18274
18310
  ];
18275
18311
  if (args.cookieDir) {
18276
18312
  const cookieFile = await writeVimeoCookieFile(args.vimeoCookie, args.cookieDir);
@@ -18588,6 +18624,7 @@ var IngestOutputs = z44.object({ asset: AssetRef }).strict();
18588
18624
  var RAW_TEXT_EXTENSIONS = [".txt", ".md"];
18589
18625
  function resolveStrategy(params) {
18590
18626
  if (params.source === "path") return "local_file";
18627
+ if (takesCanvasAssetVerbatim(params.url, params.expect)) return "canvas_asset";
18591
18628
  switch (params.expect) {
18592
18629
  case "video":
18593
18630
  case "audio":
@@ -18619,8 +18656,8 @@ var ingestNode = defineNode({
18619
18656
  version: "1.3.0",
18620
18657
  category: "io",
18621
18658
  location: "local",
18622
- summary: "Ingest an external URL or a local file into the asset store. Declare the kind you expect (image/video/audio/text/json/font); the node picks the strategy. For source=url: yt-dlp for video/audio (YouTube/TikTok/Vimeo/etc. and direct file URLs), Firecrawl for HTML/PDF pages \u2192 markdown, direct HTTP fetch for binary URLs (images, fonts) and raw .txt/.md. For source=path: read from the local filesystem and upload to R2.",
18623
- when_to_use: 'Use as the on-ramp for any external asset a canvas needs. `expect` makes the output port statically typed so downstream wiring is checked before any byte is fetched. Pick `source: "url"` for portable canvas; pick `source: "path"` for local files (canvas is not portable across machines without the file). Compose with other nodes for richer pipelines (e.g. `ingest expect=video` \u2192 `video_transcribe`).',
18659
+ 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: a canvas-assets URL (`\u2026/canvas-assets/<xx>/<sha>.<ext>`, the durable copy of an earlier output) is taken back byte-for-byte with its sha unchanged; yt-dlp for other video/audio (YouTube/TikTok/Vimeo/etc. and direct file URLs), Firecrawl for HTML/PDF pages \u2192 markdown, direct HTTP fetch for binary URLs (images, fonts) and raw .txt/.md. For source=path: read a committed file from the workspace and upload to R2.",
18660
+ when_to_use: 'Use as the on-ramp for any external asset a canvas needs. `expect` makes the output port statically typed so downstream wiring is checked before any byte is fetched. Pick `source: "url"` for portable canvas; pick `source: "path"` only for a file committed to the workspace (`src/brand/\u2026`, `src/creatives/<slug>/assets/\u2026`) \u2014 the canvas is rendered again in other Sessions, which have only what is committed. To reuse an earlier output (a voice take, a clip, a frame), ingest its canvas-assets URL from the run outputs: it comes back with the same sha, so nothing downstream re-renders. Never point `path` into `canvas/` (`canvas/.cache/`, `canvas/r_*`): that folder is not committed, and validate rewrites a cache path to its canvas-assets URL. Compose with other nodes for richer pipelines (e.g. `ingest expect=video` \u2192 `video_transcribe`).',
18624
18661
  inputs: IngestInputs,
18625
18662
  params: IngestParams,
18626
18663
  outputs: IngestOutputs,
@@ -18644,6 +18681,8 @@ function runStrategy(strategy, params, ctx) {
18644
18681
  return execLocalFile(params, ctx);
18645
18682
  }
18646
18683
  switch (strategy) {
18684
+ case "canvas_asset":
18685
+ return execCanvasAsset(params, ctx);
18647
18686
  case "direct_fetch":
18648
18687
  return execDirectFetch(params, ctx);
18649
18688
  case "firecrawl":
@@ -18658,6 +18697,30 @@ function runStrategy(strategy, params, ctx) {
18658
18697
  }
18659
18698
  }
18660
18699
  }
18700
+ function canvasAssetMime(url, expect) {
18701
+ const address = parseCanvasAssetUrl(url);
18702
+ const mime = address ? EXT_TO_MIME[address.ext] : void 0;
18703
+ return mime !== void 0 && inferKindFromMime(mime) === expect ? mime : null;
18704
+ }
18705
+ function takesCanvasAssetVerbatim(url, expect) {
18706
+ return isCanvasAssetOrigin(url) && canvasAssetMime(url, expect) !== null;
18707
+ }
18708
+ async function execCanvasAsset(params, ctx) {
18709
+ const address = parseCanvasAssetUrl(params.url);
18710
+ const mime = canvasAssetMime(params.url, params.expect);
18711
+ if (!address || mime === null) throw new Error(`ingest: invariant \u2014 canvas_asset strategy for ${params.url}`);
18712
+ try {
18713
+ return await ctx.assets.ingestRemote({
18714
+ kind: params.expect,
18715
+ url: params.url,
18716
+ sha256: address.sha256,
18717
+ mime,
18718
+ metadata: { strategy: "canvas_asset", source_url: params.url, ingested_at: (/* @__PURE__ */ new Date()).toISOString() }
18719
+ });
18720
+ } catch (e) {
18721
+ throw localExecError(ctx, e.message);
18722
+ }
18723
+ }
18661
18724
  async function execDirectFetch(params, ctx) {
18662
18725
  if (params.expect === "image") {
18663
18726
  return ingestImageUrl(params.url, ctx);
@@ -19077,13 +19140,19 @@ async function runYtDlpForNode(args) {
19077
19140
  if (error instanceof YtDlpError) {
19078
19141
  throw new NodeExecutionError(args.ctx.nodeId, args.ctx.nodeType, {
19079
19142
  kind: "local",
19080
- cause: new Error(error.stderrTail ? `${error.message}
19081
- ${error.stderrTail}` : error.message)
19143
+ cause: new Error(describeYtDlpFailure(error.message, error.stderrTail))
19082
19144
  });
19083
19145
  }
19084
19146
  throw error;
19085
19147
  }
19086
19148
  }
19149
+ function describeYtDlpFailure(message2, stderrTail) {
19150
+ const base = stderrTail ? `${message2}
19151
+ ${stderrTail}` : message2;
19152
+ if (!/CERTIFICATE_VERIFY_FAILED|SSL certificate problem/i.test(stderrTail)) return base;
19153
+ return `${base}
19154
+ This machine could not verify the download's TLS certificate; the file itself is fine. Report it and leave this ingest as it is. Never copy the file out of canvas/ (the render cache or a run folder) to work around it: canvas/ is not committed, so the creative would fail every later publish. To reuse one of our own outputs, ingest its canvas-assets URL, which does not go through this downloader.`;
19155
+ }
19087
19156
  function buildYtDlpMetadata(sourceUrl, info) {
19088
19157
  const out = {
19089
19158
  source_url: sourceUrl,
@@ -19128,7 +19197,7 @@ ingestNode.cacheKeyExtras = async ({ params }) => {
19128
19197
  } catch {
19129
19198
  }
19130
19199
  }
19131
- if (p.source === "url" && (p.expect === "video" || p.expect === "audio")) {
19200
+ if (p.source === "url" && (p.expect === "video" || p.expect === "audio") && !(typeof p.url === "string" && takesCanvasAssetVerbatim(p.url, p.expect))) {
19132
19201
  extras.yt_dlp_version = await ytDlpVersion();
19133
19202
  }
19134
19203
  return extras;
@@ -24434,6 +24503,8 @@ export {
24434
24503
  durationSeconds,
24435
24504
  canonicalUrl,
24436
24505
  coercePathUrlToUrl,
24506
+ canvasAssetMime,
24507
+ inferMimeFromPath,
24437
24508
  toModelSafeImage,
24438
24509
  loadCompositionMeta,
24439
24510
  videoPathFromOutput,
@@ -24459,4 +24530,4 @@ export {
24459
24530
  defaultRegistry,
24460
24531
  createEngineFromEnv
24461
24532
  };
24462
- //# sourceMappingURL=chunk-GLPQQP5C.js.map
24533
+ //# sourceMappingURL=chunk-2S6QIXLX.js.map