@koda-sl/baker-cli 0.380.0-dev.824ccf426 → 0.381.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
@@ -942,6 +942,8 @@ baker ads meta custom-conversions --account-id act_123 --full # raw rule JSON,
942
942
 
943
943
  Meta writes mirror the Google/LinkedIn model: they **never hit the Graph API when you run them**. Each op is validated at stage time (field shapes, budget rules, page/Instagram identity ownership, CBO/ABO exclusivity, status-transition legality), shows in the dashboard chat as a pending Meta change, and applies only when the chat is published. Meta has no atomic batch mutate, so ops apply **sequentially** on publish — one failure skips its dependents but the publish completes.
944
944
 
945
+ Staging also refuses what LinkedIn would reject at publish: a campaign group created `PAUSED` (only `DRAFT`/`ACTIVE`), a group `--total-budget` without `--end`, a `PAUSED` campaign in a `DRAFT` group, archiving a `DRAFT` ad, and a library image that is not JPG, PNG or GIF. Every campaign carries `--political-intent` (`NOT_DECLARED` by default; `NOT_POLITICAL` or `POLITICAL` are the advertiser's declaration). A write to an ad account LinkedIn has not enabled for Baker fails with a message saying so.
946
+
945
947
  **That publish reaches the real ad account for every company** — Meta writes are generally available, with no per-company flag. Discarding the chat before it completes is what undoes a staged change.
946
948
 
947
949
  Chain a full ad from scratch with `meta_temp_*` refs (each create returns one):
@@ -1107,7 +1109,7 @@ baker ads linkedin bid-pricing --account-id 503001492 \
1107
1109
  - URN normalization: every URN field becomes `{type, id, urn}` so rows are self-describing.
1108
1110
  - Wide windows (>180d) auto-segment into 30-day chunks and combine, since `q=analytics` caps at 15,000 rows with no pagination.
1109
1111
 
1110
- **Listings** (`campaign-groups`/`campaigns`/`creatives`) default to `ACTIVE` only — pass `--all-statuses` to widen, or `--statuses ACTIVE,PAUSED` for a custom set.
1112
+ **Listings** (`campaign-groups`/`campaigns`/`creatives`) default to `ACTIVE` only — pass `--all-statuses` to widen, or `--statuses ACTIVE,PAUSED` for a custom set. Only campaigns can be `COMPLETED`; each listing's `--help` names the statuses it accepts, and any other value is refused with `INVALID_STATUS` and that list before LinkedIn is called.
1111
1113
 
1112
1114
  **`creatives` is compact by default** — `id`, `urn`, `campaign`, `intendedStatus`, `isServing`, `reviewStatus`, `format`, and the resolved destination. The destination is read out of the format-specific `content` block for you: direct-content ads (text, spotlight) report a `landingUrl`; post-based ads (image, video, carousel, document, article, event, TLA) report a `postUrn`, because their destination lives in the sponsored post and is not reachable through the ads API. Pass `--fields id,content.textAd.landingPage` for specific dotted paths, or `--full` for the complete raw records.
1113
1115
 
@@ -1168,7 +1170,7 @@ Write commands never call the LinkedIn API at stage time. Each stages an op on t
1168
1170
 
1169
1171
  ```bash
1170
1172
  # Chain creates via li_temp_* refs — group → campaign → creative in one chat
1171
- baker ads linkedin campaign-groups create --name "Q3 ABM" --total-budget 5000 --currency EUR # → li_temp_x1
1173
+ baker ads linkedin campaign-groups create --name "Q3 ABM" --start 2026-08-01 --end 2026-10-31 --total-budget 5000 --currency EUR # → li_temp_x1
1172
1174
  baker ads linkedin campaigns create --name "ABM Tier-1" --group li_temp_x1 \
1173
1175
  --objective WEBSITE_VISIT --type SPONSORED_UPDATES --cost-type CPC \
1174
1176
  --bid 8.50 --daily-budget 75 --currency EUR --locale en_US --targeting-file targeting.json # → li_temp_x2
@@ -2621,7 +2623,7 @@ baker images ingest https://images.pexels.com/photos/13219418/pexels-photo-13219
2621
2623
  baker images ingest https://acme.com/hero.png --source firecrawl --external-url https://acme.com/pricing --context "competitor pricing hero"
2622
2624
  ```
2623
2625
 
2624
- `--source` is a closed set, validated before the request so a wrong value costs nothing: `uploaded`, `website`, `google_testimonial`, `trustpilot_testimonial`, `instagram`, `tiktok`, `linkedin`, `facebook`, `pexels`, `pixabay`, `magnific` (retired provider, kept for existing rows), `brandfetch`, `google_images`, `firecrawl`, `screenshotone`, `iconify`, `giphy`, `pinterest`, `ai_generated`, `layer_edit`. Use `website` for an asset pulled off a company's own site.
2626
+ `--source` is a closed set, validated before the request so a wrong value costs nothing: `uploaded`, `website`, `google_testimonial`, `trustpilot_testimonial`, `instagram`, `pexels`, `pixabay`, `magnific` (retired provider, kept for existing rows), `brandfetch`, `google_images`, `firecrawl`, `screenshotone`, `iconify`, `giphy`, `pinterest`, `ai_generated`, `layer_edit`. Use `website` for an asset pulled off a company's own site.
2625
2627
 
2626
2628
  Returns `{ imageId, deduped, contentHash }`. When `deduped: true`, an existing library row is returned — no new bytes are stored. Max ingest size 25MB.
2627
2629
 
@@ -2955,7 +2957,7 @@ Re-ingesting the same link returns the existing row with `deduped: true` instead
2955
2957
 
2956
2958
  | Flag | Purpose |
2957
2959
  |---|---|
2958
- | `--source` | Provenance. Inferred from the link's host when omitted, and that inference covers 21 video platforms — `youtube`, `instagram`, `tiktok`, `twitter`, `facebook`, `linkedin`, `reddit`, `vimeo`, `dailymotion`, `twitch`, `bluesky`, `bilibili`, `vk`, `ok`, `pinterest`, `tumblr`, `snapchat`, `loom`, `streamable`, `newgrounds`, `rutube` — plus `uploaded` and `url`. A link from anywhere else still ingests, recorded as `url`. |
2960
+ | `--source` | Provenance. Inferred from the link's host when omitted, and that inference covers 20 video platforms — `youtube`, `instagram`, `tiktok`, `twitter`, `facebook`, `reddit`, `vimeo`, `dailymotion`, `twitch`, `bluesky`, `bilibili`, `vk`, `ok`, `pinterest`, `tumblr`, `snapchat`, `loom`, `streamable`, `newgrounds`, `rutube` — plus `uploaded` and `url`. A link from anywhere else still ingests, recorded as `url`. |
2959
2961
  | `--external-id` | Dedupe key — re-running the same link returns the existing video. Derived from the link automatically on the download route. |
2960
2962
  | `--external-url` | Canonical page URL. Defaults to the input for a page URL. |
2961
2963
  | `--download` | Skip the direct attempt and always download. |
@@ -2987,16 +2989,6 @@ Requires `yt-dlp` on `PATH` (present in the Baker Runtime). Private, age-restric
2987
2989
 
2988
2990
  **yt-dlp goes stale, and that looks like a broken video.** Large platforms change their players every few weeks and an out-of-date `yt-dlp` then resolves *no* formats at all, failing with "Requested format is not available" or "Only images are available" rather than anything that names the real cause. The error classifies this case separately and says the link is probably fine. The Runtime image installs `yt-dlp` unpinned at build time (`packages/e2b-template/src/template.ts`), so it ages with the image — if platform ingests start failing across the board, rebuild the template before investigating anything else. Direct file URLs don't depend on `yt-dlp` at all and keep working regardless.
2989
2991
 
2990
- ### `baker videos fetch <url> --out <dir>`
2991
-
2992
- Download a video page (YouTube, TikTok, Vimeo, …) to a local `.mp4` without adding it to the library — the download half of `ingest`, with the same proxy fallback and duration check. `--max-seconds` lowers the length limit (default 3 hours); `--timeout-ms` bounds the download. By default it keeps the best quality the site offers; `--max-height 1080` keeps a version no taller than that when there is one (a 4K master can be five times the bytes). Returns `{ file, durationSeconds, egress }`.
2993
-
2994
- ```bash
2995
- baker videos fetch https://www.youtube.com/watch?v=abc123 --out ./clips
2996
- ```
2997
-
2998
- Baker's YouTube source sync runs this same command on a disposable machine, with `--max-height 1080`, because a YouTube file URL only works from the IP that requested it.
2999
-
3000
2992
  ### `baker videos delete <id>`
3001
2993
 
3002
2994
  Delete a video by ID.
@@ -3150,32 +3142,6 @@ baker avatars delete marta --archive
3150
3142
 
3151
3143
  ---
3152
3144
 
3153
- ### Media Sources (`baker media-sources`)
3154
-
3155
- Connect a public social account so its posts fill the image and video libraries: **Instagram**, **TikTok**, **YouTube** (videos and Shorts only), **LinkedIn** company pages and **Facebook** pages — or a **Google Drive folder** shared with Baker. Each social sync reaches back up to a year, stops paging at the first page it already has, imports only what the library doesn't have yet, keeps carousels/albums/documents together as a set (`images group` / `videos group`), and passes the post caption to the analysis. A source is named by its key, `<platform>:<handle>`.
3156
-
3157
- Connecting and removing apply immediately and show on the chat as an already-applied change.
3158
-
3159
- | Command | What it does |
3160
- |---|---|
3161
- | `baker media-sources add <profile URL \| Drive folder link \| @handle> [--platform <p>] [--every never\|daily\|weekly\|monthly]` | Look the account up (must exist and be public) or check the folder is shared with Baker, and connect it; the import starts in the background. `--platform` only for a bare handle. Idempotent. |
3162
- | `baker media-sources list` | Every source: key, platform, label, auto-sync, and per library `{ status, stored, pending?, failed?, error? }`. |
3163
- | `baker media-sources sync <key>` | Import new posts now. |
3164
- | `baker media-sources schedule <key> --every <interval>` | Set the auto-sync cadence. |
3165
- | `baker media-sources remove <key>` | Disconnect; imported media stays in the library. |
3166
-
3167
- ```bash
3168
- baker media-sources add https://www.tiktok.com/@acme
3169
- baker media-sources add @acme --platform instagram --every daily
3170
- baker media-sources list
3171
- ```
3172
-
3173
- YouTube downloads are the expensive import (a disposable machine per batch), so a YouTube sync takes the newest 25 videos it hasn't stored, skips videos over 20 minutes, and continues with older ones on the next sync.
3174
-
3175
- A Google Drive folder is usually connected by the user in the dashboard (Add source → Google Drive → Choose a folder, which shares it with Baker in one step); `add` takes a folder that is already shared with Baker's service account (the error names the address) or open to anyone with the link, and shared by a member of the company. Everything in it and its subfolders is imported in batches of 150 images and 20 videos (60 minutes / 5 GB at most each), new files arrive within minutes through Drive change notifications, and images are stored resized to 2560 px. Imported images from every source are resized the same way.
3176
-
3177
- ---
3178
-
3179
3145
  ### Winning Ads (`baker winning-ads`)
3180
3146
 
3181
3147
  Search the **ad-dna** corpus of scored "winning" competitor ads for reference creatives to reproduce (e.g. with `baker canvas`), and manage the brands your library tracks (`follow` / `following` / `feed` / `unfollow`). Each result carries a presigned media URL (~1h TTL), the ad's DNA summary, and scores. The CLI authenticates with the normal `BAKER_API_KEY`; the Baker backend proxies the request to the ad-dna service with a server-held token — no extra credential in the sandbox.
@@ -20,9 +20,9 @@ import {
20
20
  shouldEscalate
21
21
  } from "./chunk-HJEMOSPH.js";
22
22
 
23
- // ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/766ad107c42df28eeadaf0ac2f9089c8a9e200f6ba0b2b1a817b8d536cbdcc49/node_modules/safe-stable-stringify/index.js
23
+ // ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/index.js
24
24
  var require_safe_stable_stringify = __commonJS({
25
- "../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/766ad107c42df28eeadaf0ac2f9089c8a9e200f6ba0b2b1a817b8d536cbdcc49/node_modules/safe-stable-stringify/index.js"(exports, module) {
25
+ "../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/index.js"(exports, module) {
26
26
  "use strict";
27
27
  var { hasOwnProperty } = Object.prototype;
28
28
  var stringify = configure2();
@@ -1123,7 +1123,7 @@ function resolveAdaptFormats(params) {
1123
1123
  return params.formats ?? [];
1124
1124
  }
1125
1125
 
1126
- // ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/766ad107c42df28eeadaf0ac2f9089c8a9e200f6ba0b2b1a817b8d536cbdcc49/node_modules/safe-stable-stringify/esm/wrapper.js
1126
+ // ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/esm/wrapper.js
1127
1127
  var import__ = __toESM(require_safe_stable_stringify(), 1);
1128
1128
  var configure = import__.default.configure;
1129
1129
  var wrapper_default = import__.default;
@@ -5999,12 +5999,6 @@ function isVimeoAuthFailure(detail) {
5999
5999
  return VIMEO_AUTH_SIGNATURES.some((signature) => haystack.includes(signature));
6000
6000
  }
6001
6001
 
6002
- // src/lib/ytDlpFormat.ts
6003
- function videoFormatArgs(maxHeight) {
6004
- const format = maxHeight ? `bv*[height<=${maxHeight}]+ba/b[height<=${maxHeight}]/bv*+ba/b` : "bv*+ba/b";
6005
- return ["--format", format, "--merge-output-format", "mp4"];
6006
- }
6007
-
6008
6002
  // src/lib/ytDlpPlayerClient.ts
6009
6003
  var YOUTUBE_FALLBACK_CLIENT_ARGS = ["--extractor-args", "youtube:player_client=android"];
6010
6004
  var MEDIA_FETCH_REFUSED = "unable to download video data";
@@ -6073,7 +6067,7 @@ async function runYtDlp(args) {
6073
6067
  if (args.audioOnly) {
6074
6068
  argv.push("-x", "--audio-format", "m4a");
6075
6069
  } else {
6076
- argv.push(...videoFormatArgs(args.maxHeight));
6070
+ argv.push("--format", "bv*+ba/b", "--merge-output-format", "mp4");
6077
6071
  }
6078
6072
  const cookieFile = await writeVimeoCookieFile(args.vimeoCookie, args.workDir);
6079
6073
  if (cookieFile) argv.push("--cookies", cookieFile);
@@ -9992,4 +9986,4 @@ export {
9992
9986
  defaultRegistry,
9993
9987
  createEngineFromEnv
9994
9988
  };
9995
- //# sourceMappingURL=chunk-HOMVJ2D3.js.map
9989
+ //# sourceMappingURL=chunk-FGHMWBUD.js.map