@koda-sl/baker-cli 0.305.0-dev.a04fd407d → 0.306.0-dev.2b6124eb2
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 +29 -13
- package/dist/{chunk-ZNN6ZHDS.js → chunk-UBNLCVGN.js} +41 -12
- package/dist/chunk-UBNLCVGN.js.map +1 -0
- package/dist/cli.js +2804 -2549
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-ZNN6ZHDS.js.map +0 -1
package/README.md
CHANGED
|
@@ -352,6 +352,7 @@ The same lines also go to stderr. A read with nothing to report prints none of i
|
|
|
352
352
|
| Preset | Description | Default date range |
|
|
353
353
|
|------------------------|------------------------------------------|--------------------|
|
|
354
354
|
| `campaign-performance` | Campaign metrics overview | LAST_30_DAYS |
|
|
355
|
+
| `conversions-by-action` | Which conversion action each campaign's conversions actually are | LAST_30_DAYS |
|
|
355
356
|
| `keyword-analysis` | Keyword performance per ad group | LAST_30_DAYS |
|
|
356
357
|
| `keyword-serving` | Why a keyword is limited — status reasons, first-page bid estimate vs max CPC, quality score | ALL_TIME |
|
|
357
358
|
| `positive-keywords` | Positive (targeting) keywords only | ALL_TIME |
|
|
@@ -366,6 +367,8 @@ The same lines also go to stderr. A read with nothing to report prints none of i
|
|
|
366
367
|
|
|
367
368
|
Every preset except the negatives lists and `account-summary` filters to **actually-serving entities** by default — the full status chain (`campaign.status = 'ENABLED' AND ad_group.status = 'ENABLED' AND ad_group_ad.status = 'ENABLED'`, as applicable) — and selects the status columns so you can see them. Pass `--include-paused` to widen to `!= 'REMOVED'` (keeps paused, still drops removed). Raw GAQL (no `--preset`) has no such default: a query over a serving-hierarchy resource without a status filter emits a `SERVING_SCOPE` warning reminding you to add the chain, because Google Ads has no single serving flag.
|
|
368
369
|
|
|
370
|
+
**Conversion rates across campaigns are not comparable by default.** `metrics.conversions` is a sum over whatever conversion actions are primary for each campaign, and those differ campaign by campaign — a webinar registration in one, an info request in the next. A read that selects a conversion metric, is not segmented by conversion action, and is not pinned to one campaign (`WHERE campaign.id = …`) comes back with a `CONVERSION_MIX` warning: a total across those rows adds different events, and one campaign's rate is not comparable with another's. `--preset conversions-by-action` is the read that settles it. Like every other warning it travels in `--output csv|jsonl|md` and on the `--out` file summary.
|
|
371
|
+
|
|
369
372
|
**"Eligible (Limited)" keywords:** an enabled keyword that barely shows is invisible in the performance presets — near-zero impressions look the same whether it is outbid, low quality, or rarely searched. `--preset keyword-serving` reads the signals behind Google Ads' status column: `ad_group_criterion.primary_status` and `primary_status_reasons` (the status enum has no `LIMITED` value — "(Limited)" lives in the reasons while the status stays `ELIGIBLE`), `position_estimates.first_page_cpc_micros` (the number in "Below first page bid (€3.63)"), the max CPC actually in force and where it comes from, quality score split into ad relevance / landing page / expected CTR, approval status, and `system_serving_status`. Any read whose rows carry those fields — preset or raw GAQL — comes back with a `KEYWORD_SERVING_LIMITED` warning naming the affected keywords (each by its ad group, since one keyword's copies share a criterion id) and the bid each would need. Below-first-page-bid and low-quality want opposite fixes (raise the bid vs. improve relevance), so the warning reports them separately. On `--out` the verdict is folded across pages as they stream to disk and returned on the file summary; like every other warning it also travels in `--output csv|jsonl|md`.
|
|
370
373
|
|
|
371
374
|
The warning defers to Google: when the query selects `primary_status_reasons`, a keyword Google did not flag is not reported, even if its bid sits under the first-page estimate — the estimate is advisory and the bid may not be the criterion's to set under a portfolio strategy. Only an estimates-only read (no reasons selected) falls back to inferring the limit from the bid gap.
|
|
@@ -1342,6 +1345,8 @@ baker experiment fold # make the page
|
|
|
1342
1345
|
|
|
1343
1346
|
**The goal is one event, named by its key.** `--goal submit:book-a-call` — the same vocabulary the event stream uses (`form:<flow>:<node>:<trigger>`, `submit:<flow>`, `page:<name>`, `exit:<host>`), and `baker analytics conversions --candidates` lists the keys these pages produce. Not a conversion *name*, which groups several keys: a variant that swaps the page's Form for a shorter one converts on a different key, and a name would either count both endings as one outcome or count the new version as never converting at all — `--variant-goal` gives the new version its own event when it has one. The goal is fixed for the life of the test and the verdict is read on it alone. Every other conversion the company counts comes back under `secondary` on every result, with the same interval and a one-word reading, and decides nothing. A conversion counts for a visitor wherever it happens after they were shown a version — on the page, on another page, in a delivered lead, or in a deal the CRM posted a week later by email.
|
|
1344
1347
|
|
|
1348
|
+
**`start` checks that both versions still fire a conversion on the same action.** A conversion is a name matched against each page's own `data-baker-*` declarations, so the two versions agree about what an event means only because a person made them agree — and nothing downstream can tell when they stop. `start` compares the sections the two versions do not share and names any conversion whose markup differs, because a step moved from a pop-up to the open page keeps its name and starts counting a scroll instead of a click. A forked section is supposed to change, so most of these are fine; the one that is not is the one nobody looked at. Give the new version's step its own name when it is a different action.
|
|
1349
|
+
|
|
1345
1350
|
**Nothing ends a test on its own.** `status` returns a verdict; `finish --keep original|variant` is the decision, and it takes effect within seconds with nothing to publish. Ending a `keep_running` test records `inconclusive`. Going against the verdict is allowed and recorded. `pause` and `resume` switch the split off and on without losing the test; `cancel` is `finish --keep original` for a live test and takes a staged one back off the session. `update` changes what a test *says* — `--because`, `--change`, `--evidence` (repeatable, `source:what it showed`, optionally `|link`) — at any time, writes down what a finished one taught (`--learning`), and changes what it *measures* — `--goal`, `--lift`, `--expect` — only while it is still staged.
|
|
1346
1351
|
|
|
1347
1352
|
**A finished test is a record.** Its counts are frozen when it ends, so it reads the same next year as it did that day; its `learning` is what it taught, in a person's words. `history` returns every finished test — the claim, what was different, the numbers with their interval and confidence, what was kept and whether that went against the numbers, whether the belief held, and the learning — and `plan` returns the same entries as `pastTests` for the page it sizes, so a hypothesis is chosen against the page's record rather than in ignorance of it.
|
|
@@ -3796,7 +3801,10 @@ because a video model garbles a wordmark every time:
|
|
|
3796
3801
|
- **`end_card`** — on by default whenever the spec carries a brand. The last beat's PICTURE
|
|
3797
3802
|
becomes a flat brand colour plate (rendered by ffmpeg, not generated), with the mark and
|
|
3798
3803
|
the call to action over it. `end_card: false` keeps the footage; `end_card: { cta }` sets
|
|
3799
|
-
the words on the button
|
|
3804
|
+
the words on the button. There is **no default**: with no `cta` the card carries the mark
|
|
3805
|
+
alone. It used to fall back to the last line of the script, which printed that sentence
|
|
3806
|
+
twice on the closing frame — once as the button and once as the caption of the line being
|
|
3807
|
+
spoken over it. Repeat the closing line as the `cta` and it is dropped for the same reason.
|
|
3800
3808
|
- **The brand fills itself.** When the spec leaves `brand.logo` or `brand.palette` empty,
|
|
3801
3809
|
they are read from the workspace — `src/brand/BRAND.md` for the colours (in document
|
|
3802
3810
|
order, page-furniture whites and blacks dropped) and `src/brand/logos/` for the mark
|
|
@@ -4073,7 +4081,7 @@ Pick a `source` discriminator and declare the kind you expect. See [Ingestion](#
|
|
|
4073
4081
|
|
|
4074
4082
|
**Params** — discriminated on `source`:
|
|
4075
4083
|
|
|
4076
|
-
`source: "url"` (yt-dlp / direct fetch /
|
|
4084
|
+
`source: "url"` (yt-dlp / direct fetch / Firecrawl):
|
|
4077
4085
|
|
|
4078
4086
|
| Name | Type | Required | Constraint |
|
|
4079
4087
|
|---|---|---|---|
|
|
@@ -4093,7 +4101,7 @@ Pick a `source` discriminator and declare the kind you expect. See [Ingestion](#
|
|
|
4093
4101
|
|
|
4094
4102
|
**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.
|
|
4095
4103
|
|
|
4096
|
-
**Cost:** 0 engine credits for direct fetch + yt-dlp + local file.
|
|
4104
|
+
**Cost:** 0 engine credits for direct fetch + yt-dlp + local file. Firecrawl charges per scrape.
|
|
4097
4105
|
|
|
4098
4106
|
---
|
|
4099
4107
|
|
|
@@ -5407,10 +5415,10 @@ baker canvas catalog | jq '.compositions[].id'
|
|
|
5407
5415
|
// URL source — YouTube audio-only (yt-dlp -x)
|
|
5408
5416
|
{ "id": "src", "type": "ingest", "params": { "source": "url", "url": "https://youtu.be/dQw4w9WgXcQ", "expect": "audio" } }
|
|
5409
5417
|
|
|
5410
|
-
// URL source — blog post → clean markdown (
|
|
5418
|
+
// URL source — blog post → clean markdown (Firecrawl)
|
|
5411
5419
|
{ "id": "src", "type": "ingest", "params": { "source": "url", "url": "https://example.com/blog/post", "expect": "text" } }
|
|
5412
5420
|
|
|
5413
|
-
// URL source — PDF → markdown (
|
|
5421
|
+
// URL source — PDF → markdown (Firecrawl handles PDFs)
|
|
5414
5422
|
{ "id": "src", "type": "ingest", "params": { "source": "url", "url": "https://example.com/whitepaper.pdf", "expect": "text" } }
|
|
5415
5423
|
|
|
5416
5424
|
// URL source — raw markdown file (direct fetch)
|
|
@@ -5438,7 +5446,7 @@ baker canvas catalog | jq '.compositions[].id'
|
|
|
5438
5446
|
| url | video / audio | any | `yt_dlp` (works on platform URLs *and* direct file URLs via the generic extractor) |
|
|
5439
5447
|
| url | image / json / font | any | `direct_fetch` (HTTP GET) |
|
|
5440
5448
|
| url | text | ends in `.txt` or `.md` | `direct_fetch` |
|
|
5441
|
-
| url | text | anything else (HTML, PDF, …) | `
|
|
5449
|
+
| url | text | anything else (HTML, PDF, …) | `firecrawl` `/v2/scrape` (extracted markdown — HTML/PDF made readable) |
|
|
5442
5450
|
|
|
5443
5451
|
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.
|
|
5444
5452
|
|
|
@@ -5481,7 +5489,7 @@ If the bytes' mime contradicts `expect` (e.g. `expect: "image"` but the URL serv
|
|
|
5481
5489
|
file_size?: number, // bytes
|
|
5482
5490
|
original_filename?: string, // basename of the source path
|
|
5483
5491
|
|
|
5484
|
-
strategy: "direct_fetch" | "
|
|
5492
|
+
strategy: "direct_fetch" | "firecrawl" | "yt_dlp" | "local_file",
|
|
5485
5493
|
ingested_at: string, // ISO 8601 timestamp
|
|
5486
5494
|
|
|
5487
5495
|
// yt-dlp adds (when available from --dump-json):
|
|
@@ -5492,7 +5500,7 @@ If the bytes' mime contradicts `expect` (e.g. `expect: "image"` but the URL serv
|
|
|
5492
5500
|
width?: number,
|
|
5493
5501
|
height?: number,
|
|
5494
5502
|
|
|
5495
|
-
//
|
|
5503
|
+
// Firecrawl adds:
|
|
5496
5504
|
word_count?: number,
|
|
5497
5505
|
},
|
|
5498
5506
|
|
|
@@ -5511,8 +5519,8 @@ The `url` is a stable R2 URL — remote downstream nodes (e.g. `video_lipsync`)
|
|
|
5511
5519
|
|-----------------------|----------------|------------------------------------------------------------------------------|
|
|
5512
5520
|
| `INVALID_PARAMS` | engine validator | Missing/malformed fields, mixing `url` + `path`, unknown `source`, unknown `expect`. |
|
|
5513
5521
|
| `kind_mismatch` | url + direct_fetch | URL returned bytes whose mime doesn't match `expect`. Message: `ingest expect=<X> but <url> returned <mime> (kind=<Y>)`. |
|
|
5514
|
-
| `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
|
|
5515
|
-
| `provider_error` | url +
|
|
5522
|
+
| `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). |
|
|
5523
|
+
| `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. |
|
|
5516
5524
|
| `file_not_found` | path + local_file | `stat()` returned `ENOENT`. Error names the absolute resolved path. |
|
|
5517
5525
|
| `not_a_file` | path + local_file | Path exists but is a directory / block device / socket. |
|
|
5518
5526
|
| `permission_denied` | path + local_file | `stat()` or `readFile()` returned `EACCES`. |
|
|
@@ -5529,7 +5537,7 @@ All execution-time failures flow through `NodeExecutionError` so they appear in
|
|
|
5529
5537
|
- **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.
|
|
5530
5538
|
- **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.
|
|
5531
5539
|
- **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`.
|
|
5532
|
-
- **No TTL.**
|
|
5540
|
+
- **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`.
|
|
5533
5541
|
|
|
5534
5542
|
**Local files (`source: "path"`):**
|
|
5535
5543
|
|
|
@@ -5552,10 +5560,10 @@ ingest { source: "path", expect: "video" } → video_transcribe # Local
|
|
|
5552
5560
|
**Requirements:**
|
|
5553
5561
|
|
|
5554
5562
|
- `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).
|
|
5555
|
-
- `
|
|
5563
|
+
- `FIRECRAWL_API_KEY` on the Convex backend for `source: "url"` with `expect: "text"` on non-`.md`/`.txt` URLs.
|
|
5556
5564
|
- No additional dependencies for `source: "path"` — just filesystem access.
|
|
5557
5565
|
|
|
5558
|
-
**Cost:** zero engine credits for direct_fetch + yt_dlp.
|
|
5566
|
+
**Cost:** zero engine credits for direct_fetch + yt_dlp. Firecrawl charges per scrape on your Firecrawl account (configured outside Baker).
|
|
5559
5567
|
|
|
5560
5568
|
---
|
|
5561
5569
|
|
|
@@ -6088,8 +6096,16 @@ Pushing to `main` with changes in `packages/cli/` triggers the GitHub Actions wo
|
|
|
6088
6096
|
|
|
6089
6097
|
- **0.271.0**: **`--format demandGenVideo` — the Demand Gen ad most Demand Gen spend actually runs through can be edited.** A `DEMAND_GEN_VIDEO_RESPONSIVE_AD` keeps its copy in `demandGenVideoResponsiveAd`, which has a `longHeadlines` list no other ad format has and takes its business name as a text asset rather than a string. Baker modelled only the multi-asset Demand Gen ad and labelled all four Demand Gen types as one format, so every served video ad was uneditable: the strict content schema refused `longHeadlines` and named a field instead of a missing format, and the only route left was the Google Ads interface. On the account that surfaced it, Demand Gen was 76% of the spend. New flags on `ads create|update`: `--long-headlines` (up to 5 × 90 chars, this format only), `--video-assets`, `--logo-image-assets`, `--call-to-action-assets`; a create with no video is refused with the `assets create` call to make first, and an omitted media list on an update leaves that media on the ad. Two more gates ship with it. **A content flag a format does not read is now refused instead of dropped** — `--long-headlines` on `--format demandGen` staged the copy without them and reported success, the same silent-drop the backend's strict schemas exist to stop. And **the format is checked against the live `ad_group_ad.ad.type` when it stages**: a mismatch is refused with the right format named, and the two Demand Gen types Baker writes no format for (`DEMAND_GEN_CAROUSEL_AD`, `DEMAND_GEN_PRODUCT_AD`) are refused by name, so "Baker cannot edit this ad" arrives at stage time rather than as a publish failure a Session too late.
|
|
6090
6098
|
|
|
6099
|
+
- **0.293.0**: an `assets detach` whose asset link is not on the account is refused **at stage time**, and an asset-link read that leaves the link's own status unscoped is refused in preflight. Both come from one publish: 74 of 85 staged ops came back `RESOURCE_NOT_FOUND` — 67 ad-group sitelinks and 7 campaign ones — twelve hours after the agent reported them staged, against a Session that had already merged. Nothing checked them: `google.assetLink.detach` had no entry in the stage-time snapshot map, so its target read back as "read failed" (silent) instead of "not found" (refused), exactly the gap that was closed for `audienceCriterion`. The read that produced the list had the same hole from the other side — it scoped `campaign.status` and `ad_group.status` and said nothing about `campaign_asset.status` / `ad_group_asset.status`, and Google returns a removed link like any other row, so already-detached extensions read as live. The snapshot query excludes `REMOVED` for that reason: a retired link is zero rows, which is the refusal. A bare-id target still only warns — it names no level, so there is no row to read and a refusal would be a guess.
|
|
6100
|
+
|
|
6101
|
+
Two rules the new chains brought with them. **`campaign_asset` and `ad_group_asset` segment on their owners**, so a `campaign.*` / `ad_group.*` field used in WHERE must also be SELECTed or Google refuses the whole statement (`EXPECTED_REFERENCED_FIELD_IN_SELECT_CLAUSE`) — the rule that already applied to `campaign_budget`, now keyed off a table rather than one resource, and applied to the query `INCOMPLETE_STATUS_CHAIN` hands back so the corrected command is one you can actually run. Checked live on v23 against 3865083115: `ad_group_ad`, `ad_group_criterion` and `asset_group_asset` filter their ancestors freely, these two do not. And **`asset_group_asset` carries the same status** its campaign and asset group do not, so its chain gained `asset_group_asset.status` too — the Performance Max `asset-performance` preset scopes and shows it.
|
|
6102
|
+
|
|
6103
|
+
Underneath both: **`query` now returns a `.resource_name` you selected.** Google injects `resource_name` into every row whether or not it was asked for, and the backend stripped all of them as noise — including the one the SELECT clause named. For an asset link that column *is* the identity (`customers/{cid}/adGroupAssets/{adGroupId}~{assetId}~{FIELD_TYPE}`; there is no id to detach by), so the read the docs prescribe — `SELECT ad_group_asset.resource_name …` — came back without it and composing the string by hand was the only route left. Auto-injected resource names are still stripped; a selected one is kept. This also repairs the `asset-groups detach` instruction, which pointed at `asset_group_asset.resource_name` and could not have worked either.
|
|
6104
|
+
|
|
6091
6105
|
- **0.291.0**: **the image handover Task no longer depends on having uploaded the images.** It was built only from staged `asset.create` image ops, so a Session that audited a live account — found the brand campaign carrying 22 image extensions and the generic one carrying none, and reached for `assets attach` on assets that were *already in the library* — got Google's refusal and nothing else: no upload to hang a Task on, and a CLI hint telling the agent not to raise one by hand because "Baker stages it itself". The one step only the user can take reached them as a sentence in a chat that scrolls away. An image `assets attach` is now **skipped rather than refused**: it still never reaches Google (re-verified live with `validate_only` on v23 *and* v25 against 3865083115, against a control op that validates clean in the same request — `AD_IMAGE` → `UNSUPPORTED_FIELD_TYPE` at customer and campaign level, `MARKETING_IMAGE` → `FIELD_TYPE_INCOMPATIBLE_WITH_CAMPAIGN_TYPE`, `BUSINESS_LOGO` → `CUSTOMER_NOT_VERIFIED`, which is what proves the field type is the objection; **we are not on Google's allowlist for it**), but the skip carries the image and campaign names, recorded on the draft so the next staged op cannot recompute the Task away, and a batch that also carries sitelinks keeps them instead of failing whole. The schema stopped refusing the field type for the same reason: a boundary refusal has no chat to record a handover against.
|
|
6092
6106
|
|
|
6107
|
+
- **0.306.0**: review fixes across the avatar and ad paths. **A silent clip is 5 seconds again** — the 8s speaking floor was applied to every `studio animate` call, not just ones that cast an avatar, so plain b-roll was lengthened by three seconds and billed for them. **`scaffold-ad` validates the mark it will actually draw**: the logo gates read the path the spec named rather than the one `fetchClientLogo` had just put there, so a fetched mark either skipped both checks or was refused as unreadable. **The render review is told where the ad is set, who it is for and what the script says** — read off the blueprint beside the canvas; without them the watch pass judged an accent against nothing and still graded the verdict as blocking. **A beat is "too short" by its LINE, not by the clip floor** — the old test needed a 25-word clause, so it fired on all three beats of this command's own documented example. `--voice-id` / `--voice-description` are gone from `baker avatars create` (0.252.0 said so; the flags were still declared and still sent), `end_card.cta` is documented as having no default, and the dead Veo/Omni content-block fallbacks are removed.
|
|
6108
|
+
|
|
6093
6109
|
### Testing a pre-release in sandboxes
|
|
6094
6110
|
|
|
6095
6111
|
```bash
|
|
@@ -17,9 +17,9 @@ import {
|
|
|
17
17
|
shouldEscalate
|
|
18
18
|
} from "./chunk-WFWU3CHS.js";
|
|
19
19
|
|
|
20
|
-
// ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/
|
|
20
|
+
// ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/509a695c9237c6e3727f1f1f99169917f329e3ff3f7877be290b684e24bf153d/node_modules/safe-stable-stringify/index.js
|
|
21
21
|
var require_safe_stable_stringify = __commonJS({
|
|
22
|
-
"../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/
|
|
22
|
+
"../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/509a695c9237c6e3727f1f1f99169917f329e3ff3f7877be290b684e24bf153d/node_modules/safe-stable-stringify/index.js"(exports, module) {
|
|
23
23
|
"use strict";
|
|
24
24
|
var { hasOwnProperty } = Object.prototype;
|
|
25
25
|
var stringify = configure2();
|
|
@@ -758,6 +758,19 @@ async function parseErrorBody(res) {
|
|
|
758
758
|
}
|
|
759
759
|
return classifyHttpError(res.status, errObj, message2);
|
|
760
760
|
}
|
|
761
|
+
function providerVerdict(status, errObj, message2) {
|
|
762
|
+
if (errObj.provider === void 0 && errObj.code !== "provider_error") return null;
|
|
763
|
+
return {
|
|
764
|
+
kind: "provider",
|
|
765
|
+
status,
|
|
766
|
+
provider: errObj.provider,
|
|
767
|
+
code: errObj.code ?? "provider_error",
|
|
768
|
+
message: message2,
|
|
769
|
+
// A 4xx is terminal unless the backend says otherwise — the opposite
|
|
770
|
+
// default from the 502 branch, where the status itself means "try again".
|
|
771
|
+
retryable: errObj.retryable ?? false
|
|
772
|
+
};
|
|
773
|
+
}
|
|
761
774
|
function classifyHttpError(status, errObj, message2) {
|
|
762
775
|
if (errObj.code === CONTENT_POLICY_CODE) {
|
|
763
776
|
return { kind: "content_policy", status, provider: errObj.provider, message: message2 };
|
|
@@ -766,7 +779,7 @@ function classifyHttpError(status, errObj, message2) {
|
|
|
766
779
|
return { kind: "unauthorized", status, message: message2 };
|
|
767
780
|
}
|
|
768
781
|
if (status === 400 || status === 422) {
|
|
769
|
-
return { kind: "validation", status, message: message2, details: errObj.details };
|
|
782
|
+
return providerVerdict(status, errObj, message2) ?? { kind: "validation", status, message: message2, details: errObj.details };
|
|
770
783
|
}
|
|
771
784
|
if (status === 502 || status === 504) {
|
|
772
785
|
if (errObj.code === "provider_timeout" || status === 504) {
|
|
@@ -1098,7 +1111,7 @@ function resolveAdaptFormats(params) {
|
|
|
1098
1111
|
return params.formats ?? [];
|
|
1099
1112
|
}
|
|
1100
1113
|
|
|
1101
|
-
// ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/
|
|
1114
|
+
// ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/509a695c9237c6e3727f1f1f99169917f329e3ff3f7877be290b684e24bf153d/node_modules/safe-stable-stringify/esm/wrapper.js
|
|
1102
1115
|
var import__ = __toESM(require_safe_stable_stringify(), 1);
|
|
1103
1116
|
var configure = import__.default.configure;
|
|
1104
1117
|
var wrapper_default = import__.default;
|
|
@@ -1139,6 +1152,21 @@ function normalizeForCanonical(value) {
|
|
|
1139
1152
|
return void 0;
|
|
1140
1153
|
}
|
|
1141
1154
|
|
|
1155
|
+
// ../canvas-contract/src/castName.ts
|
|
1156
|
+
function withoutCastName(text, names) {
|
|
1157
|
+
let out = text;
|
|
1158
|
+
for (const name of names) {
|
|
1159
|
+
const token = name.trim();
|
|
1160
|
+
if (token.length < 3) continue;
|
|
1161
|
+
const pattern = new RegExp(`(?<![\\p{L}\\p{N}])${escapeForPattern(token)}(?![\\p{L}\\p{N}])`, "giu");
|
|
1162
|
+
out = out.replace(pattern, "the person in the reference");
|
|
1163
|
+
}
|
|
1164
|
+
return out;
|
|
1165
|
+
}
|
|
1166
|
+
function escapeForPattern(value) {
|
|
1167
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1142
1170
|
// ../canvas-contract/src/clipWeight.ts
|
|
1143
1171
|
var MB_PER_SECOND_720P = 2.13;
|
|
1144
1172
|
var RESOLUTION_WEIGHT = {
|
|
@@ -6237,7 +6265,7 @@ function resolveStrategy(params) {
|
|
|
6237
6265
|
case "text": {
|
|
6238
6266
|
const p = safePathname(params.url);
|
|
6239
6267
|
const isRawText = RAW_TEXT_EXTENSIONS.some((ext) => p.endsWith(ext));
|
|
6240
|
-
return isRawText ? "direct_fetch" : "
|
|
6268
|
+
return isRawText ? "direct_fetch" : "firecrawl";
|
|
6241
6269
|
}
|
|
6242
6270
|
default: {
|
|
6243
6271
|
params.expect;
|
|
@@ -6254,10 +6282,10 @@ function safePathname(rawUrl) {
|
|
|
6254
6282
|
}
|
|
6255
6283
|
var ingestNode = defineNode({
|
|
6256
6284
|
id: "ingest",
|
|
6257
|
-
version: "1.
|
|
6285
|
+
version: "1.3.0",
|
|
6258
6286
|
category: "io",
|
|
6259
6287
|
location: "local",
|
|
6260
|
-
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),
|
|
6288
|
+
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.",
|
|
6261
6289
|
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`).',
|
|
6262
6290
|
inputs: IngestInputs,
|
|
6263
6291
|
params: IngestParams,
|
|
@@ -6284,8 +6312,8 @@ function runStrategy(strategy, params, ctx) {
|
|
|
6284
6312
|
switch (strategy) {
|
|
6285
6313
|
case "direct_fetch":
|
|
6286
6314
|
return execDirectFetch(params, ctx);
|
|
6287
|
-
case "
|
|
6288
|
-
return
|
|
6315
|
+
case "firecrawl":
|
|
6316
|
+
return execFirecrawl(params, ctx);
|
|
6289
6317
|
case "yt_dlp":
|
|
6290
6318
|
return execYtDlp(params, ctx);
|
|
6291
6319
|
case "local_file":
|
|
@@ -6338,11 +6366,11 @@ async function ingestImageUrl(url, ctx) {
|
|
|
6338
6366
|
ctx
|
|
6339
6367
|
});
|
|
6340
6368
|
}
|
|
6341
|
-
async function
|
|
6369
|
+
async function execFirecrawl(params, ctx) {
|
|
6342
6370
|
const result = await callBackendExec({
|
|
6343
6371
|
nodeType: "ingest",
|
|
6344
6372
|
nodeVersion: ingestNode.version,
|
|
6345
|
-
params: { strategy: "
|
|
6373
|
+
params: { strategy: "firecrawl", url: params.url },
|
|
6346
6374
|
inputs: {},
|
|
6347
6375
|
outputKinds: { asset: "text" },
|
|
6348
6376
|
ctx
|
|
@@ -9570,6 +9598,7 @@ export {
|
|
|
9570
9598
|
describeFailureReason,
|
|
9571
9599
|
AD_FORMAT_PLATFORMS,
|
|
9572
9600
|
platformFormats,
|
|
9601
|
+
withoutCastName,
|
|
9573
9602
|
frameRealismDirection,
|
|
9574
9603
|
SEEDANCE_DURATIONS,
|
|
9575
9604
|
ELEVENLABS_MAX_MUSIC_LENGTH_MS,
|
|
@@ -9633,4 +9662,4 @@ export {
|
|
|
9633
9662
|
defaultRegistry,
|
|
9634
9663
|
createEngineFromEnv
|
|
9635
9664
|
};
|
|
9636
|
-
//# sourceMappingURL=chunk-
|
|
9665
|
+
//# sourceMappingURL=chunk-UBNLCVGN.js.map
|