@koda-sl/baker-cli 0.295.0-dev.f941e60fb → 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.
Files changed (28) hide show
  1. package/README.md +31 -87
  2. package/canvas/tiktok-captions-composition/index.html +2 -23
  3. package/dist/{chunk-2IUN3S3E.js → chunk-2IB3IL4K.js} +2 -2
  4. package/dist/{chunk-4YF56EGK.js → chunk-D3HHUBIL.js} +4 -4
  5. package/dist/{chunk-WFWU3CHS.js → chunk-DZUVUGEP.js} +1 -9
  6. package/dist/{chunk-WFWU3CHS.js.map → chunk-DZUVUGEP.js.map} +1 -1
  7. package/dist/{chunk-A2VZOTCV.js → chunk-EX2OIOVL.js} +3 -3
  8. package/dist/{chunk-KDTHRRAC.js → chunk-GU7IWEBC.js} +3 -3
  9. package/dist/{chunk-ER3KCAW3.js → chunk-YFQWCJX4.js} +123 -301
  10. package/dist/chunk-YFQWCJX4.js.map +1 -0
  11. package/dist/cli.js +1269 -2954
  12. package/dist/cli.js.map +1 -1
  13. package/dist/{client-6KQHCXS2.js → client-VDCCDEHE.js} +4 -4
  14. package/dist/engine/index.d.ts +0 -13
  15. package/dist/engine/index.js +3 -3
  16. package/dist/{env-C7YXL6C6.js → env-FWMZXMQK.js} +2 -6
  17. package/dist/{output-RBM32FKJ.js → output-4H2RNBXL.js} +5 -5
  18. package/dist/{shared-WJIJTWST.js → shared-UKL36AXT.js} +6 -6
  19. package/package.json +1 -1
  20. package/dist/chunk-ER3KCAW3.js.map +0 -1
  21. /package/dist/{chunk-2IUN3S3E.js.map → chunk-2IB3IL4K.js.map} +0 -0
  22. /package/dist/{chunk-4YF56EGK.js.map → chunk-D3HHUBIL.js.map} +0 -0
  23. /package/dist/{chunk-A2VZOTCV.js.map → chunk-EX2OIOVL.js.map} +0 -0
  24. /package/dist/{chunk-KDTHRRAC.js.map → chunk-GU7IWEBC.js.map} +0 -0
  25. /package/dist/{client-6KQHCXS2.js.map → client-VDCCDEHE.js.map} +0 -0
  26. /package/dist/{env-C7YXL6C6.js.map → env-FWMZXMQK.js.map} +0 -0
  27. /package/dist/{output-RBM32FKJ.js.map → output-4H2RNBXL.js.map} +0 -0
  28. /package/dist/{shared-WJIJTWST.js.map → shared-UKL36AXT.js.map} +0 -0
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.
@@ -669,7 +672,7 @@ Command groups: `budgets`, `campaigns`, `ad-groups`, `keywords` (add/update/remo
669
672
 
670
673
  **A device criterion can be added, but never removed** — `campaign-criteria add` with a device criterion works, but Google then rejects `campaign-criteria remove` on it forever, including on a criterion you added yourself (and Baker refuses the removal before publish). Adjust it instead with `campaign-criteria update customers/<cid>/campaignCriteria/<campaignId>~<criterionId> --bid-modifier <n>`: `1` for no adjustment, `1.25` to bid 25% more, `0.9` to bid 10% less, `0` to stop serving on that device. Locations, languages and ad schedules remove normally.
671
674
 
672
- **Images on a Search campaign** — `assets create` with an image publishes it into the advertiser's Google Ads asset library, but Google refuses the asset link that puts an image extension on a Search campaign (`AD_IMAGE` and every marketing-image field type, at campaign, ad group and customer level alike), so `assets attach` rejects it at stage time and names what to do instead. Staging an image therefore also stages a **Task** — "Add N images to <campaign> in Google Ads" — which applies with the rest of the chat, so the one step Google keeps inside its own interface is tracked rather than mentioned. Attach the image to a Performance Max asset group (`asset-groups attach`) and the Task withdraws itself. The one image a Search campaign does take through a link is the advertiser's logo: `BUSINESS_LOGO`, at campaign or customer level, on a verified account.
675
+ **Images on a Search campaign** — `assets create` with an image publishes it into the advertiser's Google Ads asset library, but Google refuses the asset link that puts an image extension on a Search campaign (`AD_IMAGE` and every marketing-image field type, at campaign, ad group and customer level alike), so `assets attach` **skips** it at stage time and names what to do instead — the op is never stored, the rest of a batch stages normally, and the skipped link is what records which images belong on which campaign. Staging an image, or asking to link one the account already holds, therefore also stages a **Task** — "Add N images to <campaign> in Google Ads" — which applies with the rest of the chat, so the one step Google keeps inside its own interface is tracked rather than mentioned. Attach the image to a Performance Max asset group (`asset-groups attach`) and the Task withdraws itself. The one image a Search campaign does take through a link is the advertiser's logo: `BUSINESS_LOGO`, at campaign or customer level, on a verified account.
673
676
 
674
677
  **Primary vs secondary conversion actions** — `conversions update <id> --primary` makes an action a primary action (automated bidding optimizes toward it); `--no-primary` (or `--primary=false`) demotes it to secondary (reported only) — the spaced form `--primary false` is rejected, since a boolean flag written with a space is set to true and the word dropped. `conversions create` stages a new action as **secondary** unless its `--file` payload sets `"primaryForGoal": true`, so a new action never silently joins the bidding target of a Maximize Conversions or Target CPA campaign. The staged card shows an "Action optimization" row with the before/after role.
675
678
 
@@ -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.
@@ -2935,7 +2940,7 @@ baker avatars create --name "Marta" --subject "…" \
2935
2940
  --source-image "<imageId1>,<imageId2>" --likeness-confirmed
2936
2941
  ```
2937
2942
 
2938
- Optional: `--handle`, `--speech`, `--motion`, `--setting`, `--accent`. Returns `{ avatarId, handle, likeness, status }`.
2943
+ Optional: `--handle`, `--speech`, `--motion`, `--setting`, `--voice-id`, `--voice-description`. Returns `{ avatarId, handle, likeness, status }`.
2939
2944
 
2940
2945
  #### `baker avatars update <handle>`
2941
2946
 
@@ -2944,7 +2949,7 @@ Edit an avatar. Profile flags are merged over the stored profile, so `--persona`
2944
2949
  ```bash
2945
2950
  baker avatars update marta --wardrobe "charcoal blazer over a white tee"
2946
2951
  baker avatars update marta --subject "woman in her early 50s, silver bob, …" --regenerate-sheet
2947
- baker avatars update marta --new-handle marta-founder --accent "Spanish from Spain, Madrid accent"
2952
+ baker avatars update marta --new-handle marta-founder --voice-id <voiceId>
2948
2953
  ```
2949
2954
 
2950
2955
  #### `baker avatars delete <handle>`
@@ -3636,7 +3641,6 @@ Reach for it when you are relaying the **client's** own words. When you are writ
3636
3641
 
3637
3642
  ```bash
3638
3643
  baker studio animate "slow push in, model turns to camera and smiles" --image j57abc123def456ghi789
3639
- baker studio animate "she looks to camera and says: \"Hola, soy Elena\"" --avatar elena --quality 720p --aspect-ratio 9:16
3640
3644
  baker studio animate "handheld drift right, steam rising from the cup" \
3641
3645
  --image ./out/hero.png --duration 6 --quality 1080p
3642
3646
  baker studio animate "product rotates once on a turntable" --image j57abc…,j57def… --from references
@@ -3786,65 +3790,6 @@ baker canvas set-prompt my-canvas.json s0_start --text "Close-up, flushed and br
3786
3790
  baker canvas set-prompt my-canvas.json s0_start --text-file ./hook-frame.txt
3787
3791
  ```
3788
3792
 
3789
- #### The brand, the ending and the cast are drawn, not generated
3790
-
3791
- `baker canvas scaffold-ad` renders everything with a known, fixed appearance locally,
3792
- because a video model garbles a wordmark every time:
3793
-
3794
- - **`brand.logo`** — the repo path to the mark. It is copied into the overlay composition
3795
- and drawn as a real `<img>`: small top-left for the whole ad, large on the closing card.
3796
- - **`end_card`** — on by default whenever the spec carries a brand. The last beat's PICTURE
3797
- becomes a flat brand colour plate (rendered by ffmpeg, not generated), with the mark and
3798
- the call to action over it. `end_card: false` keeps the footage; `end_card: { cta }` sets
3799
- the words on the button, which otherwise default to the last line of the script.
3800
- - **The brand fills itself.** When the spec leaves `brand.logo` or `brand.palette` empty,
3801
- they are read from the workspace — `src/brand/BRAND.md` for the colours (in document
3802
- order, page-furniture whites and blacks dropped) and `src/brand/logos/` for the mark
3803
- (vector first, plain mark over a mono/white/icon variant). Pass them yourself only when
3804
- the ad needs something other than the client's default.
3805
- - **The spoken language is read off the script** when the spec does not declare it, so a
3806
- Spanish ad is cast from Spanish voices without anyone having to say so.
3807
- - **`sound`** (per beat) — one sound effect for that shot, rendered by ElevenLabs
3808
- alongside the music bed. Most beats should not have one: it belongs where the picture
3809
- shows a specific physical event (a drill, a door, rain on glass), not on a talking head
3810
- or a calm lifestyle shot. A bed plus two well-placed effects beats eight.
3811
- - **A real brand mark is required.** `scaffold-ad` refuses to build without one in
3812
- `src/brand/logos/` (SVG or PNG): the mark is drawn on every frame and on the closing
3813
- card, so a stand-in ships an ad wearing the wrong brand.
3814
- - **An avatar with no pinned voice cannot speak on camera.** Their lines would be read by
3815
- a separately cast voice, which is dubbing and looks like it.
3816
- - **`voiceover: false`** — a music-led ad. No voice, no transcription: the `say` lines
3817
- become on-screen text and are captioned straight from the script, so the words are
3818
- exact. Give it `music` too.
3819
- - **`--avatar <handle>`** — the same flag as on `studio generate` / `studio animate`, and the way to cast a person into an ad. Overrides `cast.avatar` in the spec.
3820
- - **`cast.avatar`** — the handle of a cast Avatar (`baker avatars list`). The ad grounds
3821
- every beat they appear in on that avatar's identity sheet and copies its subject
3822
- description verbatim, so the ad shows the same face as the rest of the company's work.
3823
- This is the whole journey: `baker avatars create` → `cast.avatar` in the spec → render.
3824
- - **`cast.description`** — a person described in words, when there is no avatar.
3825
-
3826
- Each of these also removes a generation: the closing beat costs no image and no clip.
3827
-
3828
- #### The finished video is reviewed
3829
-
3830
- When a run's output is a video, `baker canvas run` samples twelve frames across it and
3831
- looks at them before reporting success. It reports, never blocks — the clips are already
3832
- paid for by the time it runs — and the findings come back as `review` and `hints`:
3833
-
3834
- - **Per frame:** anything that could not physically happen, or shows a job being done
3835
- incorrectly, plus objects floating unattached in shot. Malformed hands and limbs are
3836
- rolled up into one finding across the whole render rather than repeated per frame.
3837
- - **Across the reel:** whether the cast stays the same people, whether a brand mark is
3838
- visible anywhere, and whether the last frames close the ad or simply stop.
3839
-
3840
- Each per-frame finding names the **scene file** that produced that second, so a defect is
3841
- an edit rather than a hunt — and re-running re-renders only the scenes you changed.
3842
-
3843
- It needs `GEMINI_API_KEY` (the Runtime receives it automatically). Without one the run
3844
- says the video was **not** reviewed rather than reporting it clean, and a frame that
3845
- could not be read is reported as unchecked for the same reason.
3846
-
3847
-
3848
3793
  > Scaffolded canvases use **workspace-relative** composition/asset paths and are validated
3849
3794
  > against the engine's hyperframe pre-render gate at `validate` time, so a broken overlay
3850
3795
  > (or a moved/renamed workspace) is caught before any billed clip. The scaffold also adds
@@ -4073,7 +4018,7 @@ Pick a `source` discriminator and declare the kind you expect. See [Ingestion](#
4073
4018
 
4074
4019
  **Params** — discriminated on `source`:
4075
4020
 
4076
- `source: "url"` (yt-dlp / direct fetch / Handinger):
4021
+ `source: "url"` (yt-dlp / direct fetch / Firecrawl):
4077
4022
 
4078
4023
  | Name | Type | Required | Constraint |
4079
4024
  |---|---|---|---|
@@ -4093,7 +4038,7 @@ Pick a `source` discriminator and declare the kind you expect. See [Ingestion](#
4093
4038
 
4094
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.
4095
4040
 
4096
- **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.
4097
4042
 
4098
4043
  ---
4099
4044
 
@@ -5407,10 +5352,10 @@ baker canvas catalog | jq '.compositions[].id'
5407
5352
  // URL source — YouTube audio-only (yt-dlp -x)
5408
5353
  { "id": "src", "type": "ingest", "params": { "source": "url", "url": "https://youtu.be/dQw4w9WgXcQ", "expect": "audio" } }
5409
5354
 
5410
- // URL source — blog post → clean markdown (Handinger)
5355
+ // URL source — blog post → clean markdown (Firecrawl)
5411
5356
  { "id": "src", "type": "ingest", "params": { "source": "url", "url": "https://example.com/blog/post", "expect": "text" } }
5412
5357
 
5413
- // URL source — PDF → markdown (Handinger handles PDFs)
5358
+ // URL source — PDF → markdown (Firecrawl handles PDFs)
5414
5359
  { "id": "src", "type": "ingest", "params": { "source": "url", "url": "https://example.com/whitepaper.pdf", "expect": "text" } }
5415
5360
 
5416
5361
  // URL source — raw markdown file (direct fetch)
@@ -5438,7 +5383,7 @@ baker canvas catalog | jq '.compositions[].id'
5438
5383
  | url | video / audio | any | `yt_dlp` (works on platform URLs *and* direct file URLs via the generic extractor) |
5439
5384
  | url | image / json / font | any | `direct_fetch` (HTTP GET) |
5440
5385
  | url | text | ends in `.txt` or `.md` | `direct_fetch` |
5441
- | 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) |
5442
5387
 
5443
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.
5444
5389
 
@@ -5481,7 +5426,7 @@ If the bytes' mime contradicts `expect` (e.g. `expect: "image"` but the URL serv
5481
5426
  file_size?: number, // bytes
5482
5427
  original_filename?: string, // basename of the source path
5483
5428
 
5484
- strategy: "direct_fetch" | "handinger" | "yt_dlp" | "local_file",
5429
+ strategy: "direct_fetch" | "firecrawl" | "yt_dlp" | "local_file",
5485
5430
  ingested_at: string, // ISO 8601 timestamp
5486
5431
 
5487
5432
  // yt-dlp adds (when available from --dump-json):
@@ -5492,7 +5437,7 @@ If the bytes' mime contradicts `expect` (e.g. `expect: "image"` but the URL serv
5492
5437
  width?: number,
5493
5438
  height?: number,
5494
5439
 
5495
- // Handinger adds:
5440
+ // Firecrawl adds:
5496
5441
  word_count?: number,
5497
5442
  },
5498
5443
 
@@ -5511,8 +5456,8 @@ The `url` is a stable R2 URL — remote downstream nodes (e.g. `video_lipsync`)
5511
5456
  |-----------------------|----------------|------------------------------------------------------------------------------|
5512
5457
  | `INVALID_PARAMS` | engine validator | Missing/malformed fields, mixing `url` + `path`, unknown `source`, unknown `expect`. |
5513
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>)`. |
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 Handinger to extract). |
5515
- | `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. |
5516
5461
  | `file_not_found` | path + local_file | `stat()` returned `ENOENT`. Error names the absolute resolved path. |
5517
5462
  | `not_a_file` | path + local_file | Path exists but is a directory / block device / socket. |
5518
5463
  | `permission_denied` | path + local_file | `stat()` or `readFile()` returned `EACCES`. |
@@ -5529,7 +5474,7 @@ All execution-time failures flow through `NodeExecutionError` so they appear in
5529
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.
5530
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.
5531
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`.
5532
- - **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`.
5533
5478
 
5534
5479
  **Local files (`source: "path"`):**
5535
5480
 
@@ -5552,10 +5497,10 @@ ingest { source: "path", expect: "video" } → video_transcribe # Local
5552
5497
  **Requirements:**
5553
5498
 
5554
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).
5555
- - `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.
5556
5501
  - No additional dependencies for `source: "path"` — just filesystem access.
5557
5502
 
5558
- **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).
5559
5504
 
5560
5505
  ---
5561
5506
 
@@ -5930,6 +5875,8 @@ baker landing critique spring-offer summer-offer claude # score three landings
5930
5875
 
5931
5876
  - **Positioning integrity is scored too, and it is the one copy rule that blocks on a single sentence.** `competitor-concession` fires when the page ranks somebody else above the client, or says the client does not lead at what the page sells ("su constructor es mejor que el nuestro", "we're not the best"). Every other prose rule waits for a cluster because one AI tell proves nothing; this one is the whole defect on its own — the page is paid traffic, and it is arguing the visitor should leave. Two shapes that read identically are exempt by design: a concession about **price**, in either direction ("we are not the cheapest / the most expensive, and here is why" is premium positioning), and a superlative flipped by a negation ("ningún equipo trabaja más rápido que el nuestro"), which is the strongest claim *for* the client. Its warn-tier sibling `competitor-named-in-comparison` fires when a name from `src/content/competitors/` appears in a comparative sentence outside a `<table>` — a comparison table is a section the landing skill teaches, but naming a rival anywhere else on paid traffic is the client's call, not the build's. Spanish, Portuguese and English.
5932
5877
 
5878
+ - **Comments are not markup, and no rule reads one.** Astro expression comments (`{/* … */}`) and HTML comments (`<!-- … -->`) are blanked before detection, offsets preserved so `file:line` stays exact. A shared scaffold component explaining its `preconnect` in prose that spelled `<img>` twice was read as two broken images — block-tier — so every landing rendering a video failed the publish gate on a file the client cannot edit. Comment prose is also where a component's reasoning lives, em-dashes and buzzwords included, so reading it inflated the count-threshold tells too.
5879
+
5933
5880
  - **Copy held in component frontmatter is read like markup copy.** Astro landings keep FAQ pairs, testimonial quotes, feature cards and pricing rows as a `const items = [{ q, a }]` array above the `---` fence and render them in a `.map()`, so a critic that blanked the whole block was blind to a large share of every page's actual words. Prose string literals in that block now feed every copy rule; imports, class strings, URLs and config do not, and the bar is deliberately high in that direction — a Tailwind class list in front of the copy rules is worse than one missed tell.
5934
5881
 
5935
5882
  Output is the standard envelope `{ ok, data, hints }` with `data = { advisory, slug, overall, counts, dimensions, findings }`. `dimensions` scores seven design families (typography, color, borders_depth, motion, spacing, copy, integrity) 0–1 (higher is better); `counts` is the block/warn/advisory tally.
@@ -6030,18 +5977,7 @@ This CLI is designed for AI agent consumption. Key patterns:
6030
5977
 
6031
5978
  - **0.217.0**: new family `baker avatars list|get|create|update|delete`, plus `--avatar <handle>` on `baker studio generate` and `baker studio animate` — company-scoped reusable presenters, typed from the shared `@baker/api` avatars contract. `--avatar` is the way to cast one: it grounds the render on the identity sheet, reuses the subject description verbatim, and records the avatar on the batch. Passing the sheet through `--reference` instead does none of those and leaves the batch unattributed, so the avatar can never show what it has appeared in; the route refuses a handle that is not `ready`, because generating a stranger is worse than an error. `get` returns the sheet URL and that description; every command's `hints` carry the next move (still building → re-check with `get`; changed `--subject` without `--regenerate-sheet` → the sheet is stale). `create` refuses `--source-image` without `--likeness-confirmed` and returns an `error.fix` telling the agent to ask the user for that permission, since only they can give it; an unknown handle returns an `error.fix` pointing at `baker avatars list`. Profile flags on `update` merge over the stored profile instead of replacing it.
6032
5979
 
6033
- - **0.250.1**: `--avatar <handle>` on `baker studio generate` and `baker studio animate` never worked. Both were declared in `registerSchema` and read at the call site, but neither was in the command's own citty `args` — and citty does not reject an undeclared flag, it keeps it and sets it to `true`. So `--avatar elena` reached the route as `avatar: true` with `elena` dropped as a stray positional, and the body schema (`z.string()`) refused it. Documented since 0.217.0, broken since 0.217.0: casting an avatar into a render was not possible from the CLI at all, which is why a clip of a named presenter came back with neither the identity sheet nor the voice. Both flags are now declared. A new test in `command-reference-integrity` compares every registered schema against the args of the command that parses it, so the two cannot drift again — it found a third case on the way, `actions status`, whose schema advertised a `--refs` flag for what the command actually takes as positionals.
6034
- - **0.251.0**: `--accent` on `baker avatars create|update` — the region an avatar's voice comes from, e.g. `"Spanish from Spain, Madrid accent"`. Its own field rather than a sentence inside `--speech`, because that is where it went to die: asked for "pace, register, accent", the agent wrote *"clear Spanish accent"* for a Madrid presenter, the video model read that as no accent, and the clip came out sounding like nowhere. A place can be checked; an adjective cannot. It reaches the render through `buildDeliveryDirection` as its own `ACCENT:` instruction, and `thinProfileHint` now names it first among the missing fields since it is the only one that fails silently — a clip with no accent still looks finished.
6035
- - **0.252.0**: avatar voices removed. `--voice-id` / `--voice-description` are gone from `baker avatars create|update`, and an avatar no longer holds a voice at all — every clip is voiced by the video model as it renders, directed by the profile's `--accent`. The picker they replaced offered a voice, played its preview, and then shipped a clip that did not sound like it: the re-voice was speech-to-speech, which replaces timbre and keeps pronunciation, so the accent the user picked could never survive. Three other routes were built and judged on video before removing it — TTS + lip-sync (mouth), audio-driven avatar models (picture), and Seedance 2.5 driven by audio (refuses AI faces). Reasoning and the re-open condition: `docs/adr/0005-an-avatar-is-a-face-not-a-voice.md`.
6036
- - **0.253.0**: the pre-render gate runs `hyperframes check` — lint, runtime, layout, motion and contrast in one browser session, sampled at transition seams — instead of the static `lint` + `inspect` pair, falling back to them when the installed binary predates `check`. The browser half is signal the parser could never produce: measured on HyperFrames' own `warm-grain` example, lint/runtime/layout/motion all reported `ok: true` while the composition rendered an almost entirely blank ten-second video, the only trace being six `GSAP target #a-roll not found` warnings — a tween pointed at an element that does not exist, so nothing it animates ever appears. Baker promotes that warning to blocking, alongside `sweep_static` ("the timeline never advanced", i.e. a still image billed as a video). Warnings are grouped by code with a count (one run returned 62, 53 of them identical) and the highest-value findings carry advice on what to do rather than only what was seen. Requires `hyperframes@0.8.17` in the sandbox, bumped from 0.7.5 in the same change. Reasoning: `docs/adr/0007-the-render-gate-runs-in-a-browser.md`.
6037
- - **0.254.0**: the nested-composition smoke test now looks at the frames instead of trusting the exit code — exiting 0 is what a blank render does too. `hyperframes snapshot --describe` sends the captured frames to Gemini and writes back what is actually in them, and the credential Studio already uses for Gemini and Omni (`GOOGLE_GENERATIVE_AI_API_KEY`) is the `GEMINI_API_KEY` it reads; the backend now ships it to the sandbox by omission, so a deployment without one keeps the plain smoke test. Verified against a real render: the pass reported "the image is completely empty" and named the single visible element without being told what to look for. Frame verdicts are condensed into the node log, one line per frame — a file the agent would have to know to open is a file it does not read.
6038
- - **0.263.0**: shot briefs refuse the frame that contradicts its own caption, and the vision pass reads the numbers back. Observed verbatim: a phone screen reading "$215.40 SAVED THIS MONTH" under a caption reading "Save over £900 a year" — wrong currency, and a figure that contradicts the claim it sits beneath, since 215.40 a month is 2,584 a year. The model invents a number whenever a screen or document is in shot, so the frames no longer carry legible text at all: the caption track is where an ad's numbers live. Same brief also pins each object's real-world FORM after a phone rendered with a second screen on its back, and names the market's currency for anything unavoidably visible. The frame-vision question now reads out every number and word inside the picture and says whether it contradicts the caption — the defect only exists between the two, so neither alone can see it.
6039
- - **0.262.0**: `scaffold-ad` carries the brand. The spec takes `brand.palette` (hex tokens) and `brand.logo` (repo path to the mark), and emits them as the `style.palette` and `branding` the engine already reads to colour its brand plate and CTA. The palette dresses the plate and the type and is deliberately kept OUT of the shot briefs — hex in a frame prompt makes the model paint the whole scene that colour, which reads as a filter. Also takes `market`, since an ad that does not say where it is set is an ad the model sets wherever it likes.
6040
- - **0.261.0**: four defects that reached a finished ad, fixed in `scaffold-ad` where they originate. Frames render on **`openai/gpt-image-2`**, pinned rather than inheriting the catalogue default (Gemini Flash — right for cheap high-volume work, wrong for the opening still a five-second clip is built from). Every shot brief now states the MARKET, inferred from the voice language when unstated: an ad for Spain came back full of British houses because nothing said where it was. Every shot brief also rules out the impossible — paper and screens are opaque, objects are at real-world scale — after a bill rendered with the text from its reverse showing through, and a solar panel five metres tall. And beat durations snap to lengths the video model can actually render: free-form durations were rounded down per clip, so 11 beats summed to 27.0s of picture against a 31.4s timeline and the 4.4s difference rendered as a black tail. The read rate was re-measured off a real Spanish ad (81 words in 26.48s = 3.06 w/s); the old 2.6 over-predicted every beat by 18%.
6041
- - **0.260.0**: burned-in captions break where the script punctuates instead of every N words. The karaoke composition sliced the transcript into fixed groups, and a word count knows nothing about the sentence it is cutting — an ad the engine rendered showed cards reading "OTRA VEZ, EN" and "TU CASA GENERA", half-thoughts ending on a preposition. A card now closes when the clause does; the word count stays as a ceiling so a long clause is still split, and a linking word ("de", "al", "y") never ends a card because it belongs to what follows. Same rule the pre-render gate already checked for, now enforced where the cards are actually built.
6042
- - **0.259.0**: `baker canvas scaffold-ad` is listed in `baker canvas --help` with a description, and the three video routes are separated in the skill. It had a usage line and no description row, so an agent reading help saw two scaffolders explained and a bare name — and three runs from the same prompt took three different wrong paths: hand-directing the flow, reproducing a competitor ad found via `winning-ads`, and hand-authoring a canvas. The routes now state their own boundary: `scaffold-ad` is the default for a new ad, `scaffold-video` only reproduces a video the USER supplied, and `video-flow.md` opens by saying so rather than calling itself the default eleven times.
6043
- - **0.257.0**: the frame-vision pass now asks whether what is on screen could physically happen, not just whether the frame is empty. The defect that motivated it was a generated shot of a solar panel roughly five metres tall being lifted onto a roof by one person — well lit, on brief, perfectly legible, and impossible. That is what reads loudest as "AI-generated", and the previous question could not see it: nothing was missing, something present was the wrong size. Asked the new question, the pass called the rejected shot *"physically implausible due to the immense weight and surface area"* and the replacement *"consistent with what two people could reasonably maneuver"* — the same verdict a person reached, unprompted.
6044
- - **0.256.0**: the pre-render gate reports caption cards that end mid-clause. The first cut of this check asked for consistency and got it the wrong way round — stripping terminal punctuation makes the cards agree and leaves them wrong, because "En solo nueve días tu casa" is not a line anyone wrote, it is a sentence halved by a word count. Captions split where the script punctuates; each card is then a clause carrying its own mark, grammatical, and consistent as a by-product. The last card is exempt, since a CTA legitimately ends bare. Reported, never rewritten — the copy belongs to whoever wrote it.
5980
+ ## Publishing
6045
5981
 
6046
5982
  ### Auto-publish (CI)
6047
5983
 
@@ -6086,6 +6022,14 @@ Pushing to `main` with changes in `packages/cli/` triggers the GitHub Actions wo
6086
6022
 
6087
6023
  - **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.
6088
6024
 
6025
+ - **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.
6026
+
6027
+ 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.
6028
+
6029
+ 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.
6030
+
6031
+ - **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.
6032
+
6089
6033
  ### Testing a pre-release in sandboxes
6090
6034
 
6091
6035
  ```bash
@@ -35,10 +35,6 @@
35
35
 
36
36
  .caption-group {
37
37
  position: absolute;
38
- /* Absolutely positioned with no width, a card sizes to its content and
39
- runs off the 1080px frame rather than wrapping. Matches the container's
40
- 60px gutters, so a long clause becomes two lines instead of an overflow. */
41
- max-width: 960px;
42
38
  font-family: 'Arial Black', 'Helvetica Neue', sans-serif;
43
39
  font-size: {{font_size}}px;
44
40
  font-weight: 900;
@@ -84,27 +80,10 @@
84
80
  const container = document.getElementById('captions');
85
81
  const tl = gsap.timeline({ paused: true });
86
82
 
87
- // A card closes where the SCRIPT closes, not on a word count. Slicing every
88
- // N words gave cards reading "OTRA VEZ, EN" — half a thought ending on a
89
- // preposition. The count stays as a ceiling (a long clause still has to be
90
- // split), and a linking word never ends a card: it belongs to what follows.
91
- const CLOSES = /[.,;:!?\u2026]$/;
92
- const CARRIES_ON = new Set([
93
- 'de','del','al','a','en','y','e','o','u','que','la','el','los','las','un','una',
94
- 'con','por','para','su','tu','mi','más','mas','the','of','to','and','in','for','your',
95
- ]);
96
- const carriesOn = (w) => CARRIES_ON.has(String(w.text || '').replace(/[.,;:!?\u2026]$/, '').toLowerCase());
97
-
98
83
  const groups = [];
99
- let current = [];
100
- for (const word of transcript) {
101
- current.push(word);
102
- const last = current[current.length - 1];
103
- const closes = CLOSES.test(String(last.text || '')) ||
104
- (current.length >= WORDS_PER_GROUP && !carriesOn(last));
105
- if (closes) { groups.push(current); current = []; }
84
+ for (let i = 0; i < transcript.length; i += WORDS_PER_GROUP) {
85
+ groups.push(transcript.slice(i, i + WORDS_PER_GROUP));
106
86
  }
107
- if (current.length > 0) groups.push(current);
108
87
 
109
88
  groups.forEach((group, gi) => {
110
89
  const el = document.createElement('div');
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  debugLogSetting
3
- } from "./chunk-WFWU3CHS.js";
3
+ } from "./chunk-DZUVUGEP.js";
4
4
 
5
5
  // src/debugLog.ts
6
6
  import { appendFileSync, mkdirSync, renameSync, statSync } from "fs";
@@ -150,4 +150,4 @@ export {
150
150
  readBodyForLog,
151
151
  installStreamTaps
152
152
  };
153
- //# sourceMappingURL=chunk-2IUN3S3E.js.map
153
+ //# sourceMappingURL=chunk-2IB3IL4K.js.map
@@ -2,13 +2,13 @@ import {
2
2
  handleConnectionError,
3
3
  needsConnectionFix,
4
4
  writeAdsJson
5
- } from "./chunk-A2VZOTCV.js";
5
+ } from "./chunk-EX2OIOVL.js";
6
6
  import {
7
7
  ApiError
8
- } from "./chunk-KDTHRRAC.js";
8
+ } from "./chunk-GU7IWEBC.js";
9
9
  import {
10
10
  getEnv
11
- } from "./chunk-WFWU3CHS.js";
11
+ } from "./chunk-DZUVUGEP.js";
12
12
 
13
13
  // src/commands/ads/meta/shared.ts
14
14
  var DAY_MS = 864e5;
@@ -108,4 +108,4 @@ export {
108
108
  csvOrJson,
109
109
  resolveEffectiveStatus
110
110
  };
111
- //# sourceMappingURL=chunk-4YF56EGK.js.map
111
+ //# sourceMappingURL=chunk-D3HHUBIL.js.map
@@ -311,12 +311,6 @@ function requireChatId() {
311
311
  function resolveChatId(chat) {
312
312
  return typeof chat === "string" && chat.length > 0 ? chat : requireChatId();
313
313
  }
314
- function hasFrameVisionKey() {
315
- return Boolean(frameVisionKey());
316
- }
317
- function frameVisionKey() {
318
- return process.env.GEMINI_API_KEY;
319
- }
320
314
  function captureBudgetMs() {
321
315
  const raw = Number(process.env.BAKER_CAPTURE_BUDGET_MS);
322
316
  return Number.isFinite(raw) && raw > 0 ? raw : null;
@@ -346,11 +340,9 @@ export {
346
340
  debugLogSetting,
347
341
  requireChatId,
348
342
  resolveChatId,
349
- hasFrameVisionKey,
350
- frameVisionKey,
351
343
  captureBudgetMs,
352
344
  captureProxyCredentials,
353
345
  captureVimeoCookie,
354
346
  childEnvWith
355
347
  };
356
- //# sourceMappingURL=chunk-WFWU3CHS.js.map
348
+ //# sourceMappingURL=chunk-DZUVUGEP.js.map