@koda-sl/baker-cli 0.294.0 → 0.295.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.
package/README.md CHANGED
@@ -4018,7 +4018,7 @@ Pick a `source` discriminator and declare the kind you expect. See [Ingestion](#
4018
4018
 
4019
4019
  **Params** — discriminated on `source`:
4020
4020
 
4021
- `source: "url"` (yt-dlp / direct fetch / Handinger):
4021
+ `source: "url"` (yt-dlp / direct fetch / Firecrawl):
4022
4022
 
4023
4023
  | Name | Type | Required | Constraint |
4024
4024
  |---|---|---|---|
@@ -4038,7 +4038,7 @@ Pick a `source` discriminator and declare the kind you expect. See [Ingestion](#
4038
4038
 
4039
4039
  **Path-source notes:** the canvas is **not portable** to another machine without the file. Cache key folds the file's `mtime:size`, so editing the file invalidates the cache automatically. Supported extensions: `png`, `jpg`/`jpeg`, `webp`, `gif`, `avif`, `svg`, `mp4`, `webm`, `mov`, `m4v`, `mp3`, `wav`, `m4a`, `ogg`, `flac`, `json`, `txt`, `md`, `markdown`, `html`/`htm`, `csv`, `ttf`, `otf`, `woff`, `woff2`. Unknown extensions fall back to magic-byte sniffing for common image formats (and an SVG content sniff), else `kind_mismatch`. **Any `expect: "image"` in a format image-generation models can't read (SVG, AVIF, HEIC, TIFF, BMP) is normalized to PNG on ingest** — model-safe rasters (`jpeg`/`png`/`gif`/`webp`) pass through untouched, everything else is transcoded so a reference can never 400 a generation. This applies to **both `source: "path"` and `source: "url"`** (URL images are fetched and normalized locally, since the backend can't run the rasterizer). SVG gets density-aware upscaling (longest edge near 2048px, transparency preserved). The normalized asset carries `metadata.rasterized_from` set to the source format (e.g. `"svg"`, `"avif"`). **Video (`expect: "video"`) duration is probed from the file's ISO-BMFF (`mp4`/`mov`/`m4v`) header** and stamped as the canonical `duration_ms` (and `metadata.duration_ms`); other containers (e.g. `webm`) leave it unset. Downstream `video_deconstruct` uses this declared duration to size its ingest-poll timeout and preflight — without it those fall back to worst-case budgets and a single deconstruct step can hit the action time limit.
4040
4040
 
4041
- **Cost:** 0 engine credits for direct fetch + yt-dlp + local file. Handinger charges per scrape.
4041
+ **Cost:** 0 engine credits for direct fetch + yt-dlp + local file. Firecrawl charges per scrape.
4042
4042
 
4043
4043
  ---
4044
4044
 
@@ -5352,10 +5352,10 @@ baker canvas catalog | jq '.compositions[].id'
5352
5352
  // URL source — YouTube audio-only (yt-dlp -x)
5353
5353
  { "id": "src", "type": "ingest", "params": { "source": "url", "url": "https://youtu.be/dQw4w9WgXcQ", "expect": "audio" } }
5354
5354
 
5355
- // URL source — blog post → clean markdown (Handinger)
5355
+ // URL source — blog post → clean markdown (Firecrawl)
5356
5356
  { "id": "src", "type": "ingest", "params": { "source": "url", "url": "https://example.com/blog/post", "expect": "text" } }
5357
5357
 
5358
- // URL source — PDF → markdown (Handinger handles PDFs)
5358
+ // URL source — PDF → markdown (Firecrawl handles PDFs)
5359
5359
  { "id": "src", "type": "ingest", "params": { "source": "url", "url": "https://example.com/whitepaper.pdf", "expect": "text" } }
5360
5360
 
5361
5361
  // URL source — raw markdown file (direct fetch)
@@ -5383,7 +5383,7 @@ baker canvas catalog | jq '.compositions[].id'
5383
5383
  | url | video / audio | any | `yt_dlp` (works on platform URLs *and* direct file URLs via the generic extractor) |
5384
5384
  | url | image / json / font | any | `direct_fetch` (HTTP GET) |
5385
5385
  | url | text | ends in `.txt` or `.md` | `direct_fetch` |
5386
- | url | text | anything else (HTML, PDF, …) | `handinger` `/markdown` (extracted markdown — HTML/PDF made readable) |
5386
+ | url | text | anything else (HTML, PDF, …) | `firecrawl` `/v2/scrape` (extracted markdown — HTML/PDF made readable) |
5387
5387
 
5388
5388
  If the bytes' mime contradicts `expect` (e.g. `expect: "image"` but the URL serves `text/html`, or a `.pdf` file is passed as `expect: "image"`), the node fails with a clear `kind_mismatch` error instead of forwarding bad bytes downstream.
5389
5389
 
@@ -5426,7 +5426,7 @@ If the bytes' mime contradicts `expect` (e.g. `expect: "image"` but the URL serv
5426
5426
  file_size?: number, // bytes
5427
5427
  original_filename?: string, // basename of the source path
5428
5428
 
5429
- strategy: "direct_fetch" | "handinger" | "yt_dlp" | "local_file",
5429
+ strategy: "direct_fetch" | "firecrawl" | "yt_dlp" | "local_file",
5430
5430
  ingested_at: string, // ISO 8601 timestamp
5431
5431
 
5432
5432
  // yt-dlp adds (when available from --dump-json):
@@ -5437,7 +5437,7 @@ If the bytes' mime contradicts `expect` (e.g. `expect: "image"` but the URL serv
5437
5437
  width?: number,
5438
5438
  height?: number,
5439
5439
 
5440
- // Handinger adds:
5440
+ // Firecrawl adds:
5441
5441
  word_count?: number,
5442
5442
  },
5443
5443
 
@@ -5456,8 +5456,8 @@ The `url` is a stable R2 URL — remote downstream nodes (e.g. `video_lipsync`)
5456
5456
  |-----------------------|----------------|------------------------------------------------------------------------------|
5457
5457
  | `INVALID_PARAMS` | engine validator | Missing/malformed fields, mixing `url` + `path`, unknown `source`, unknown `expect`. |
5458
5458
  | `kind_mismatch` | url + direct_fetch | URL returned bytes whose mime doesn't match `expect`. Message: `ingest expect=<X> but <url> returned <mime> (kind=<Y>)`. |
5459
- | `kind_mismatch` | path + local_file | File mime (from extension or magic-byte sniff) doesn't match `expect`, *or* mime doesn't map to any of our kinds (e.g. local PDFs aren't supported as `text` — host them via URL for Handinger to extract). |
5460
- | `provider_error` | url + handinger / direct_fetch | Handinger `/markdown` non-2xx, or upstream `fetch` non-ok. |
5459
+ | `kind_mismatch` | path + local_file | File mime (from extension or magic-byte sniff) doesn't match `expect`, *or* mime doesn't map to any of our kinds (e.g. local PDFs aren't supported as `text` — host them via URL for Firecrawl to extract). |
5460
+ | `provider_error` | url + firecrawl / direct_fetch | Firecrawl `/v2/scrape` non-2xx or no markdown in the response, **or the target site answering 4xx/5xx** (Firecrawl reports that inside a 200 carrying the error page, so it is refused rather than stored as the page), or upstream `fetch` non-ok. Terminal: a bad host, a 404 and a block page fail identically on a retry and each retry is billed. Retried: a 429 from either side, a Firecrawl-side 5xx, a dropped connection. |
5461
5461
  | `file_not_found` | path + local_file | `stat()` returned `ENOENT`. Error names the absolute resolved path. |
5462
5462
  | `not_a_file` | path + local_file | Path exists but is a directory / block device / socket. |
5463
5463
  | `permission_denied` | path + local_file | `stat()` or `readFile()` returned `EACCES`. |
@@ -5474,7 +5474,7 @@ All execution-time failures flow through `NodeExecutionError` so they appear in
5474
5474
  - **Engine-level cache** (`canvas/.cache/index/`) keys by node params + node version + node-supplied cache extras. Re-running the same canvas with the same params is a cache hit and skips the network/yt-dlp/local-read entirely.
5475
5475
  - **yt-dlp version is folded into the cache key** for `source: "url"` with `expect: video | audio`. Upgrading yt-dlp busts the cache automatically, since new versions can produce different bitstreams.
5476
5476
  - **Local file `mtime + size` is folded into the cache key** for `source: "path"`. Editing the file (any save bumps mtime) invalidates the cache so you get fresh ingestion. mtime+size is cheap to read at validate time; for cases where mtime is unreliable (`cp -p`, content-only changes), force-bypass with `--cache-policy bypass` or change the canvas's `cache_salt`.
5477
- - **No TTL.** Handinger and direct fetch don't cache-bust on page changes — if the source page updates, use `--cache-policy bypass` or change `cache_salt`.
5477
+ - **No TTL.** Firecrawl and direct fetch don't cache-bust on page changes — if the source page updates, use `--cache-policy bypass` or change `cache_salt`.
5478
5478
 
5479
5479
  **Local files (`source: "path"`):**
5480
5480
 
@@ -5497,10 +5497,10 @@ ingest { source: "path", expect: "video" } → video_transcribe # Local
5497
5497
  **Requirements:**
5498
5498
 
5499
5499
  - `yt-dlp` and `ffmpeg` on PATH for `source: "url"` with `expect: "video" | "audio"` (already in the E2B sandbox image; install locally with `pip install yt-dlp` and `brew install ffmpeg` / equivalent).
5500
- - `HANDINGER_API_KEY` on the Convex backend for `source: "url"` with `expect: "text"` on non-`.md`/`.txt` URLs.
5500
+ - `FIRECRAWL_API_KEY` on the Convex backend for `source: "url"` with `expect: "text"` on non-`.md`/`.txt` URLs.
5501
5501
  - No additional dependencies for `source: "path"` — just filesystem access.
5502
5502
 
5503
- **Cost:** zero engine credits for direct_fetch + yt_dlp. Handinger charges per scrape on your Handinger account (configured outside Baker).
5503
+ **Cost:** zero engine credits for direct_fetch + yt_dlp. Firecrawl charges per scrape on your Firecrawl account (configured outside Baker).
5504
5504
 
5505
5505
  ---
5506
5506
 
@@ -757,6 +757,19 @@ async function parseErrorBody(res) {
757
757
  }
758
758
  return classifyHttpError(res.status, errObj, message2);
759
759
  }
760
+ function providerVerdict(status, errObj, message2) {
761
+ if (errObj.provider === void 0 && errObj.code !== "provider_error") return null;
762
+ return {
763
+ kind: "provider",
764
+ status,
765
+ provider: errObj.provider,
766
+ code: errObj.code ?? "provider_error",
767
+ message: message2,
768
+ // A 4xx is terminal unless the backend says otherwise — the opposite
769
+ // default from the 502 branch, where the status itself means "try again".
770
+ retryable: errObj.retryable ?? false
771
+ };
772
+ }
760
773
  function classifyHttpError(status, errObj, message2) {
761
774
  if (errObj.code === CONTENT_POLICY_CODE) {
762
775
  return { kind: "content_policy", status, provider: errObj.provider, message: message2 };
@@ -765,7 +778,7 @@ function classifyHttpError(status, errObj, message2) {
765
778
  return { kind: "unauthorized", status, message: message2 };
766
779
  }
767
780
  if (status === 400 || status === 422) {
768
- return { kind: "validation", status, message: message2, details: errObj.details };
781
+ return providerVerdict(status, errObj, message2) ?? { kind: "validation", status, message: message2, details: errObj.details };
769
782
  }
770
783
  if (status === 502 || status === 504) {
771
784
  if (errObj.code === "provider_timeout" || status === 504) {
@@ -5936,7 +5949,7 @@ function resolveStrategy(params) {
5936
5949
  case "text": {
5937
5950
  const p = safePathname(params.url);
5938
5951
  const isRawText = RAW_TEXT_EXTENSIONS.some((ext) => p.endsWith(ext));
5939
- return isRawText ? "direct_fetch" : "handinger";
5952
+ return isRawText ? "direct_fetch" : "firecrawl";
5940
5953
  }
5941
5954
  default: {
5942
5955
  params.expect;
@@ -5953,10 +5966,10 @@ function safePathname(rawUrl) {
5953
5966
  }
5954
5967
  var ingestNode = defineNode({
5955
5968
  id: "ingest",
5956
- version: "1.2.0",
5969
+ version: "1.3.0",
5957
5970
  category: "io",
5958
5971
  location: "local",
5959
- 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.",
5972
+ 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.",
5960
5973
  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`).',
5961
5974
  inputs: IngestInputs,
5962
5975
  params: IngestParams,
@@ -5983,8 +5996,8 @@ function runStrategy(strategy, params, ctx) {
5983
5996
  switch (strategy) {
5984
5997
  case "direct_fetch":
5985
5998
  return execDirectFetch(params, ctx);
5986
- case "handinger":
5987
- return execHandinger(params, ctx);
5999
+ case "firecrawl":
6000
+ return execFirecrawl(params, ctx);
5988
6001
  case "yt_dlp":
5989
6002
  return execYtDlp(params, ctx);
5990
6003
  case "local_file":
@@ -6037,11 +6050,11 @@ async function ingestImageUrl(url, ctx) {
6037
6050
  ctx
6038
6051
  });
6039
6052
  }
6040
- async function execHandinger(params, ctx) {
6053
+ async function execFirecrawl(params, ctx) {
6041
6054
  const result = await callBackendExec({
6042
6055
  nodeType: "ingest",
6043
6056
  nodeVersion: ingestNode.version,
6044
- params: { strategy: "handinger", url: params.url },
6057
+ params: { strategy: "firecrawl", url: params.url },
6045
6058
  inputs: {},
6046
6059
  outputKinds: { asset: "text" },
6047
6060
  ctx
@@ -9199,4 +9212,4 @@ export {
9199
9212
  defaultRegistry,
9200
9213
  createEngineFromEnv
9201
9214
  };
9202
- //# sourceMappingURL=chunk-NNQDWCFG.js.map
9215
+ //# sourceMappingURL=chunk-YFQWCJX4.js.map