@koda-sl/baker-cli 0.430.0-dev.8c14009f3 → 0.430.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 +56 -31
- package/dist/{chunk-Q6F5UNOU.js → chunk-6Z4ETVEZ.js} +3 -3
- package/dist/{chunk-GTHLEQHU.js → chunk-DL7VXQEI.js} +6 -27
- package/dist/chunk-DL7VXQEI.js.map +1 -0
- package/dist/{chunk-PHLFNYGQ.js → chunk-RM2TTRYQ.js} +4 -4
- package/dist/{chunk-RFZYRG2Q.js → chunk-UKGTWBKW.js} +11 -3
- package/dist/chunk-UKGTWBKW.js.map +1 -0
- package/dist/{chunk-FQOKJYKN.js → chunk-WJIYNKLI.js} +12 -4
- package/dist/chunk-WJIYNKLI.js.map +1 -0
- package/dist/{chunk-GCTR4MDE.js → chunk-Y77DA7EO.js} +2 -2
- package/dist/cli.js +44137 -43816
- package/dist/cli.js.map +1 -1
- package/dist/{client-GS7T7ETG.js → client-UUKOCY3Q.js} +4 -4
- package/dist/engine/index.js +3 -3
- package/dist/{env-SL22S6CM.js → env-Q3KQLCU5.js} +4 -2
- package/dist/output-V44OD5O3.js +22 -0
- package/dist/{shared-XR54KFMH.js → shared-3THDUK2O.js} +6 -6
- package/package.json +1 -1
- package/dist/chunk-FQOKJYKN.js.map +0 -1
- package/dist/chunk-GTHLEQHU.js.map +0 -1
- package/dist/chunk-RFZYRG2Q.js.map +0 -1
- package/dist/output-OFDKEPPJ.js +0 -18
- /package/dist/{chunk-Q6F5UNOU.js.map → chunk-6Z4ETVEZ.js.map} +0 -0
- /package/dist/{chunk-PHLFNYGQ.js.map → chunk-RM2TTRYQ.js.map} +0 -0
- /package/dist/{chunk-GCTR4MDE.js.map → chunk-Y77DA7EO.js.map} +0 -0
- /package/dist/{client-GS7T7ETG.js.map → client-UUKOCY3Q.js.map} +0 -0
- /package/dist/{env-SL22S6CM.js.map → env-Q3KQLCU5.js.map} +0 -0
- /package/dist/{output-OFDKEPPJ.js.map → output-V44OD5O3.js.map} +0 -0
- /package/dist/{shared-XR54KFMH.js.map → shared-3THDUK2O.js.map} +0 -0
package/README.md
CHANGED
|
@@ -1116,10 +1116,23 @@ baker ads linkedin analytics --level campaign --campaign-id 1234 \
|
|
|
1116
1116
|
# "Top companies seeing this campaign" (ABM feedback loop) — org names auto-resolved
|
|
1117
1117
|
baker ads linkedin top-companies --campaign-id 1234 --last-days 30
|
|
1118
1118
|
|
|
1119
|
+
# "Which companies are our ads reaching?" — one row per company over the whole period,
|
|
1120
|
+
# most engaged first ({org, name, impressions, clicks, engagements, costLocal}).
|
|
1121
|
+
# Defaults: last 12 calendar months, the only selected ad account, top 100 (--full for all).
|
|
1122
|
+
baker ads linkedin companies --account-id 1234
|
|
1123
|
+
baker ads linkedin companies --by month --min-impressions 50 # months[] per company
|
|
1124
|
+
baker ads linkedin companies --campaign-id 1,2 --by campaign # campaigns[] per company
|
|
1125
|
+
baker ads linkedin companies --with-domains --stats --output csv # + domain/domainSource; read-quality stats
|
|
1126
|
+
|
|
1119
1127
|
# "Who are these org URNs?" — resolve org URNs to company names (probe leakage)
|
|
1120
1128
|
baker ads linkedin resolve --urns urn:li:organization:17719,urn:li:organization:19022
|
|
1121
1129
|
baker ads linkedin resolve --ids 17719,19022 --output csv
|
|
1122
1130
|
|
|
1131
|
+
# "What are these companies' website domains?" — for matching against CRM companies.
|
|
1132
|
+
# Per org: vanityName (LinkedIn page), website, domain, domainSource (website | subdomain | none);
|
|
1133
|
+
# ids LinkedIn doesn't know land in `errors`. Shortlinks are never followed.
|
|
1134
|
+
baker ads linkedin resolve --ids 1035,1337 --with-domains
|
|
1135
|
+
|
|
1123
1136
|
# "Who is in our audience by industry/seniority/function/title — all at once?"
|
|
1124
1137
|
baker ads linkedin demographics --campaign-id 1234 --last-days 30
|
|
1125
1138
|
|
|
@@ -1188,6 +1201,7 @@ creatives # default ACTIVE intended-status
|
|
|
1188
1201
|
|
|
1189
1202
|
analytics # see "Smart defaults" above (3-axis: level × intent × pivot)
|
|
1190
1203
|
demographics # sweep all firmographic pivots in one call
|
|
1204
|
+
companies # which companies the ads reached, one row per company (--by month|campaign, --with-domains)
|
|
1191
1205
|
top-companies # pivot=MEMBER_COMPANY shortcut (ABM)
|
|
1192
1206
|
conversation # pivot=CONVERSATION_NODE_BUTTON shortcut (Sponsored Messaging)
|
|
1193
1207
|
|
|
@@ -1207,6 +1221,7 @@ audit --format md # deliverable-ready markdown table
|
|
|
1207
1221
|
campaign-groups create|update|pause|resume|duplicate # staged writes (see below)
|
|
1208
1222
|
campaigns create|update|pause|resume|archive|duplicate
|
|
1209
1223
|
campaigns url-params <ad-set-id> # UTMs for every ad in the ad set
|
|
1224
|
+
campaigns exclude-companies <ad-set-id> --org <ids> # stop an ad set reaching these companies
|
|
1210
1225
|
account url-params # UTMs for every ad in the whole account
|
|
1211
1226
|
url-params [--ad-sets] # read what is tagged now, and what lands twice
|
|
1212
1227
|
creatives create|update|pause|resume|duplicate
|
|
@@ -1247,9 +1262,16 @@ baker ads linkedin account url-params --param "utm_source=linkedin&utm_medium=pa
|
|
|
1247
1262
|
baker ads linkedin campaigns update 123456 --audience-expansion off --lan off
|
|
1248
1263
|
baker ads linkedin campaigns update 123456 --bid 7.50 --daily-budget 100 --currency USD
|
|
1249
1264
|
|
|
1250
|
-
# Matched audiences (CSV with header; email columns SHA-256 hashed locally)
|
|
1265
|
+
# Matched audiences (CSV with header; email columns SHA-256 hashed locally).
|
|
1266
|
+
# Company lists take companyName,domain,organizationUrn — organizationUrn matches the company page exactly
|
|
1251
1267
|
baker ads linkedin audiences create --name "ABM Tier-1" --type company-list --list-file accounts.csv
|
|
1252
1268
|
|
|
1269
|
+
# Stop an ad set reaching customers / open deals — add-only; at publish Baker re-reads the ad set
|
|
1270
|
+
# and merges into its targeting as it stands then. --org takes company ids, ≤200 per call;
|
|
1271
|
+
# a longer list goes into a company-list audience passed as --segment
|
|
1272
|
+
baker ads linkedin campaigns exclude-companies 123456 --org 1035,1441
|
|
1273
|
+
baker ads linkedin campaigns exclude-companies 123456 --segment urn:li:dmpSegment:42
|
|
1274
|
+
|
|
1253
1275
|
# Conversions with playbook-default windows
|
|
1254
1276
|
baker ads linkedin conversions create --name "Demo booked" --type LEAD --method INSIGHT_TAG \
|
|
1255
1277
|
--post-click-window 30 --view-window 7
|
|
@@ -1311,7 +1333,7 @@ Each finding: `{id, area, check, status, severity, evidence, fix: {explanation,
|
|
|
1311
1333
|
- OAuth tokens auto-refresh server-side. On 401, reconnect LinkedIn from dashboard → integrations.
|
|
1312
1334
|
- Account scoping: backend rejects calls against an account not in the picker selection.
|
|
1313
1335
|
- Pinned API version: `Linkedin-Version: 202606` (declared in `oauth/constants.ts`). Bump as a coordinated change — LinkedIn deprecates monthly with a ~12-month support window. The floor is `TRACKING_PARAM_DYNAMIC_MIN_API_VERSION` (`CREATIVE_NAME` needs 202606), asserted in `ads/linkedin/utils.test.ts`.
|
|
1314
|
-
- Cache TTLs: accounts/account-detail 1h, listings 30m, analytics 15m–6h (depends on date range), facets 7d, urn-resolve 1h, bid-pricing/forecast 6h, audience-size 1h.
|
|
1336
|
+
- Cache TTLs: accounts/account-detail 1h, listings 30m, analytics 15m–6h (depends on date range), facets 7d, urn-resolve 1h, org domains 7d, bid-pricing/forecast 6h, audience-size 1h.
|
|
1315
1337
|
- `--no-cache` on any command skips both cache layers (the CLI's own and the server's).
|
|
1316
1338
|
|
|
1317
1339
|
#### LinkedIn-specific gotchas
|
|
@@ -1803,22 +1825,6 @@ baker gsc sitemaps --site-url "https://example.com/"
|
|
|
1803
1825
|
|
|
1804
1826
|
---
|
|
1805
1827
|
|
|
1806
|
-
### Making ads (`baker creatives brief` / `ideas` / `make`)
|
|
1807
|
-
|
|
1808
|
-
Ads start from what customers said. Three commands carry the process; the agent writes the ideas and the copy.
|
|
1809
|
-
|
|
1810
|
-
```bash
|
|
1811
|
-
baker creatives brief [--adset <id>] [--account-id act_<id>] # one read: viewer, account bottleneck, what works, taste, customer quotes, ideas on file
|
|
1812
|
-
baker creatives ideas [--full] # check and rank src/creatives/_ideas/*.md; build_next
|
|
1813
|
-
baker creatives make <idea> [--slug <s>] [--adset <id>] [--reference <adId>] [--spec <beats.json>] [--from-idea]
|
|
1814
|
-
baker creatives learn [--days <n>] [--dry-run] # what each idea's ads did, written back onto the idea
|
|
1815
|
-
```
|
|
1816
|
-
|
|
1817
|
-
- `brief` returns a verdict, not raw numbers: `account.bottleneck` is `hook`, `after_click`, `none` or `too_little_data`. A part that cannot be read says `unavailable`. `market` says what competitors' ads already say (the followed brands, else the library), how sophisticated the market is, and which awareness stages nobody speaks to.
|
|
1818
|
-
- `ideas` is free and local. An idea's `evidence` resolves to `src/content/insights/` entries, or it is an `explore` bet. Numbers must be in the Knowledge or our own customers' quotes (a Reddit, forum or web quote never sources a claim), no two ideas may share an angle and format, and a review or testimonial format needs a quote from our own customer.
|
|
1819
|
-
- `learn` pools each built idea's creatives, judges them against the rest of the account's cost per result and writes `status` (`winner`, `failed`, `inconclusive`, `testing`) and a frozen `result` onto the idea. `ideas` then warns when a new idea repeats a failed one.
|
|
1820
|
-
- `make` builds one idea. A static searches the idea's container for ads made for our viewer, checks the closest three with `winning-ads fit` and scaffolds from the first that fits. A video idea takes its beats spec. Either way it records the idea on `_definition.md`, sets `prompt.json`'s `message` and syncs the definition. Drafted copy lines claiming a figure the company never wrote come back first in `hints`. When no proven ad fits (or with `--from-idea`), a static starts from the idea itself: `prompt.json` holds the message and placeholders, and `canvas run` refuses to render until the scene and copy are written.
|
|
1821
|
-
|
|
1822
1828
|
### Competitive Intelligence (`baker research`)
|
|
1823
1829
|
|
|
1824
1830
|
Market and competitor research powered by DataForSEO and AI-powered Google Search grounding. Shows who's competing for keywords, search intent classification, competitor keyword strategies, keyword gaps, landing page performance, and open-ended AI research.
|
|
@@ -1950,16 +1956,6 @@ Every search or thread item carries verbatim `text`, `author`, `score`, `created
|
|
|
1950
1956
|
|
|
1951
1957
|
---
|
|
1952
1958
|
|
|
1953
|
-
### `baker research customers`
|
|
1954
|
-
|
|
1955
|
-
Finds what real customers say and writes each quote as an insight in `src/content/insights/` (type, quote, source, date, `status: observed`). Sources: the company's reviews library, Reddit, and with `--web` pages from web research. A quote is kept only when it appears word for word in the text that was fetched; the model classifies quotes and never writes them.
|
|
1956
|
-
|
|
1957
|
-
```bash
|
|
1958
|
-
baker research customers --focus "solar panels" # reviews + Reddit
|
|
1959
|
-
baker research customers --query "payroll mistakes" --web --limit 15
|
|
1960
|
-
baker research customers --dry-run # print, write nothing
|
|
1961
|
-
```
|
|
1962
|
-
|
|
1963
1959
|
### `baker research advertisers "keyword"`
|
|
1964
1960
|
|
|
1965
1961
|
Returns a ranked list of **domains** competing for one keyword in Google, with average SERP position, relevance rating, estimated traffic value and visibility — domain economics, **NOT ad copy**. Despite the name it returns no headlines, descriptions or creative of any kind; for a competitor's actual ad copy use `baker winning-ads content <adId>` (Meta/LinkedIn only — Google SERP ad copy is not available anywhere in Baker).
|
|
@@ -3573,6 +3569,7 @@ baker scheduled-agents create --name "Celebrate the first sale" --description "O
|
|
|
3573
3569
|
baker scheduled-agents update <id-or-temp_agent_id> --enabled false
|
|
3574
3570
|
baker scheduled-agents update <id-or-temp_agent_id> --run-at "2026-09-02T07:00:00Z" --prompt "Use the launch checklist"
|
|
3575
3571
|
baker scheduled-agents update <id-or-temp_agent_id> --trigger experiment.ended
|
|
3572
|
+
baker scheduled-agents update <id-or-temp_agent_id> --slack "#marketing" # each run opens a thread there; also here | me | off
|
|
3576
3573
|
baker scheduled-agents delete <id-or-temp_agent_id>
|
|
3577
3574
|
baker scheduled-agents unstage <id-or-temp_agent_id> # everything this chat staged on it
|
|
3578
3575
|
baker scheduled-agents unstage <id-or-temp_agent_id> --index 2 # one staged change only
|
|
@@ -3950,7 +3947,7 @@ baker setup-links list # active lin
|
|
|
3950
3947
|
|
|
3951
3948
|
---
|
|
3952
3949
|
|
|
3953
|
-
### `baker hubspot forms list | view | submissions` · `baker hubspot workflows list | view` · `baker hubspot pipelines list` · `baker hubspot contacts summary | lookup | properties | fields` · `baker hubspot deals properties | fields` · `baker hubspot meetings list | view`
|
|
3950
|
+
### `baker hubspot forms list | view | submissions` · `baker hubspot workflows list | view` · `baker hubspot pipelines list` · `baker hubspot contacts summary | lookup | properties | fields` · `baker hubspot companies search` · `baker hubspot deals list | properties | fields` · `baker hubspot meetings list | view`
|
|
3954
3951
|
|
|
3955
3952
|
Read-only view of the company's **connected HubSpot account** — the forms and meeting links (calendars) that live there, the leads those forms received, and the workflows, deal pipelines, and contact records a lead runs through afterwards. Fetched from HubSpot live on every call; nothing is cached and nothing is ever written back.
|
|
3956
3953
|
|
|
@@ -3972,6 +3969,8 @@ baker hubspot pipelines list # where a lead lands, and the st
|
|
|
3972
3969
|
baker hubspot contacts summary --days 30 # are recent leads being worked? (counts only)
|
|
3973
3970
|
baker hubspot contacts lookup lead@example.com # was this one lead picked up?
|
|
3974
3971
|
baker hubspot contacts properties # what a HubSpot-read source can watch, with each value
|
|
3972
|
+
baker hubspot companies search --domains acme.com,globex.io --with-deals # CRM accounts by domain, with their deals
|
|
3973
|
+
baker hubspot deals list --pipeline default --modified-since 2026-01-01 # deals: amount, stage, closed won
|
|
3975
3974
|
baker hubspot meetings list # every calendar + whether booking redirects
|
|
3976
3975
|
baker hubspot meetings view <slug> # booking-form fields for one calendar
|
|
3977
3976
|
baker hubspot meetings view <slug> --as-node # the form.external blob for a flow node
|
|
@@ -3982,7 +3981,9 @@ baker hubspot meetings view <slug> --as-node # the form.external blob for a f
|
|
|
3982
3981
|
**`forms submissions`:** positional `<formId>`; `--days <1-365>` (default 30) sets the window; `--full` adds each submission's field values. The default response is counts and dates only — `total`, `inWindow`, `lastSubmittedAt`, and per-day counts — because submissions carry the lead's own contact details. `truncated: true` means the page budget ran out before the window was covered, so `inWindow` is a floor rather than an exact count.
|
|
3983
3982
|
**`workflows list` flags:** `--search <text>`, `--enabled-only`. Disabled workflows are listed too — a form wired to one that is off looks connected and does nothing.
|
|
3984
3983
|
**`workflows view`:** positional `<workflowId>` (the id from `workflows list`, and the one in HubSpot's own URL — the command translates it to HubSpot's separate internal flow id for you). Returns the enrolment criteria and whether it re-enrols (`null` = HubSpot does not report it for that criteria type, so it is unknown rather than off), plus every step with the property it writes. On a `LIST_BRANCH` step, branches come back in **evaluation order** (`order: 1` wins over `order: 2`) — a broad branch above a narrower one silently swallows the records the narrow one was written for, and HubSpot reports nothing. A `STATIC_BRANCH` matches on a value rather than top down, so its `order` is display only. `steps` is **not** in execution order; follow `nextActionId`. `--full` adds the raw payload, the only place the branch filter criteria (which property is compared to which value) can be read.
|
|
3985
|
-
**`pipelines list`:** no flags; stages come back in the order they run in HubSpot.
|
|
3984
|
+
**`pipelines list`:** no flags; stages come back in the order they run in HubSpot, each with `isClosed` and `isWon` (closed with probability 1).
|
|
3985
|
+
**`companies search` flags:** `--domains <a.com,b.com>` (up to 100; a URL or `www.` is reduced to the bare domain), `--names <"Acme, Inc.;Globex">` (semicolon-separated), `--with-deals`, `--limit <1-500>` (default 100), `--output json|csv|jsonl|md`. Rows: `{ id, name, domain, linkedinPage, deals? }`; `notFound` lists the searched domains no CRM company carries; `truncated: true` means more matched than `--limit`.
|
|
3986
|
+
**`deals list` flags:** `--pipeline <id,id>`, `--modified-since <YYYY-MM-DD>`, `--limit <1-1000>` (default 200), `--after <cursor>`, `--output json|csv|jsonl|md`. Rows, oldest change first: `{ id, name, amount, currency, pipelineId, stageId, stageLabel, isClosed, isWon, createdAt, closedAt, modifiedAt, companyIds }` — `amount: null` is none entered. `next` is the `--after` for the following page; `restartFrom` appears when HubSpot's 10,000-result search ceiling is reached (read on with `--modified-since <restartFrom>`); `companiesReadable: false` means the connection cannot read companies, so no deal carries `companyIds`.
|
|
3986
3987
|
**`contacts summary` flags:** `--days <1-365>` (default 30) over contact creation date. Returns counts only — total contacts, how many have an owner, and the lifecycle-stage split — never a name or an email. `truncated: true` means the page budget ran out, so the counts are floors.
|
|
3987
3988
|
**`contacts lookup`:** positional `<email>`; returns that contact's lifecycle stage, owner, the account (company) it was filed under, and associated deals with their stage. `dealsReadable: false` / `companyReadable: false` mean the connection does not cover deals or accounts, so an empty `deals` or a null `company` is unknown rather than absent. `found: false` means no contact with that email — the lead never reached the CRM, which is a finding rather than an error.
|
|
3988
3989
|
**`contacts properties`:** no flags; returns every contact property with a closed list of values — `{ name, label, groupName, options: [{ value, label }] }` — the ones a HubSpot-read source can watch. Names are HubSpot's internal ones; hand them to `baker analytics sources --create-hubspot --property <name> --value <value>` as printed. No personal data.
|
|
@@ -4014,6 +4015,30 @@ No write surface against HubSpot itself — a form's fields, redirect, or consen
|
|
|
4014
4015
|
|
|
4015
4016
|
---
|
|
4016
4017
|
|
|
4018
|
+
### `baker accounts influenced | engagement | warm | company | campaigns | crm`
|
|
4019
|
+
|
|
4020
|
+
Account insights: which companies the LinkedIn ads are warming, and which CRM deals they influenced. Reads Baker's nightly copy of LinkedIn and the CRM, so it is fast and covers up to 24 months; nothing is changed anywhere. Only for companies with Account insights switched on — otherwise every command exits 1 with `ACCOUNTS_NOT_ENABLED` and an `error.fix` pointing at the live read (`baker ads linkedin companies`, plus `baker hubspot companies search`). `--start`/`--end` must be real calendar days within 24 calendar months; a bad flag or an unsupported `--output` exits 1 with `INVALID_ARGS`.
|
|
4021
|
+
|
|
4022
|
+
```bash
|
|
4023
|
+
baker accounts influenced # influenced pipeline, last 12 calendar months
|
|
4024
|
+
baker accounts influenced --basis revenue --start 2026-01-01 --end 2026-06-30
|
|
4025
|
+
baker accounts influenced --full # every deal in the period, influenced or not
|
|
4026
|
+
baker accounts engagement --last-months 3 --limit 50 # companies reached, New/Rising, CRM match and deals
|
|
4027
|
+
baker accounts engagement --campaign-id 123456 --output csv
|
|
4028
|
+
baker accounts warm --output csv > warm.csv # companyName,domain,organizationUrn
|
|
4029
|
+
baker accounts company acme.com # or a LinkedIn company id / urn:li:organization:N
|
|
4030
|
+
baker accounts campaigns --last-months 6 --output md # per campaign: reached, engaged, influenced pipeline (--basis revenue)
|
|
4031
|
+
baker accounts influenced --model awareness --threshold 10 --output csv # another rule, this call only
|
|
4032
|
+
baker accounts warm --min 0 # every New/Rising company, below the threshold too
|
|
4033
|
+
baker accounts crm status # deals from HubSpot or sent by the client; what arrived, linked Google Sheets
|
|
4034
|
+
baker accounts crm check --file deals.csv # judge a deals or company CSV offline, nothing sent
|
|
4035
|
+
baker accounts crm contract # the send-your-CRM contract, as Markdown for a developer
|
|
4036
|
+
```
|
|
4037
|
+
|
|
4038
|
+
A deal is **influenced** when its company reached the company's influence rule — by default 3+ LinkedIn ad engagements (Awareness: 7+ impressions; Intent: 3+ clicks) — in the 12 months up to and including the month the deal was created (`--basis pipeline`, deals not won) or closed won (`--basis revenue`). It is a labelled rule, not credit: campaign rows overlap, and money is reported per currency, never summed across currencies. Deals whose company match is only a guess come back under `lowConfidence` and stay out of `summary` until the match is confirmed in Measure → Accounts. Without the deals set up there — HubSpot's pipelines and won/lost stages, or Another CRM — `influenced` and `campaigns` exit 1 with `ACCOUNTS_NOT_SET_UP`. A CRM Baker cannot read sends its companies and deals to `POST /v1/accounts/crm` (a `crm_ingest` key, `X-Baker-Mode: test` to rehearse, up to 500 records per request, records naming a person refused), is uploaded as a CSV in Measure → Accounts, or lives in a Google Sheet linked there that Baker reads every night (a row deleted from the sheet leaves Baker). A file is a deals export or a company list — a company needs only its website or LinkedIn page; `crm check` runs the same rules offline and `crm contract` prints the whole contract. The CLI never sends deals or links a sheet. `--model engagement|awareness|intent` and `--threshold N` on `influenced`, `campaigns` and `warm` read with another rule for that call only; the saved setting is unchanged. A **warm** account is New or Rising, reached the company's influence threshold this period on the rule's metric (`--min N` overrides, `--min 0` drops it), and has no open deal, no won deal and no deal lost in the last 6 months; `engagement` and `warm` rows carry `dealStatus` (`won` > `open` > `lost` > `none`), `engagement --output csv` adds `lastMonth` and `previousImpressions/Clicks/Engagements`, and `campaigns` / `company` carry each campaign's `campaignName` (null when LinkedIn could not name it). `company` returns `months` (the company's totals), `byCampaign` (the same numbers per campaign — overlapping, never add them) and its `deals` with `touchValue` and `influenced`. `warm --output csv` is the file `baker ads linkedin audiences create --type company-list --list-file` takes.
|
|
4039
|
+
|
|
4040
|
+
---
|
|
4041
|
+
|
|
4017
4042
|
### `baker chats list | view | transcript | diff`
|
|
4018
4043
|
|
|
4019
4044
|
Read-only inspection of **other Sessions (chats) on the same account** — so an agent can reuse prior work ("like we did in that other chat, but for X"). Same-account only; never crosses a company boundary and has no write surface.
|
|
@@ -5992,7 +6017,7 @@ The two scaffold passes are billed (the full `video_deconstruct` is the heavy on
|
|
|
5992
6017
|
|
|
5993
6018
|
Turn a source/inspiration image into a **runnable, self-validated static-ad canvas** — the static counterpart of `scaffold-video`. It runs **billed Gemini passes** up front:
|
|
5994
6019
|
|
|
5995
|
-
1. **`image_describe`** (`~google/gemini-pro-latest`) — reverse-engineers the image (layout by what fills each region, copy verbatim, typefaces, capture in words, the `mechanism` and the `audience` it is built for) and writes it next to the canvas as **`prompt.json`**, an **ad spec** (`"schema": "baker-static-ad/3"`; `/2` still reads) you rewrite into the ad you want: `viewer` (who sees it and what they already know), `message` (the one thing it tells them, never printed), `copy` (each line with `lettering`: `in_scene`, written on something in the picture (`on`, `look`), or `designed`, set in the brand's face), `scene` (`description`, `look`: photo | designed | illustration, `capture` in words, `mood`, `layout`), `brand`, `colors` (each tied to an object, from the brand palette), `references` (one picture each, labelled by role: `PERSON_1`, `ANIMAL_1`, `PRODUCT_1`, `BADGE_1`, `PROP_1`), `logo` (and `logo_position`), `third_party_brands` (other brands the offer is about, a car brand on a car loan: `name`, `what`, and `logo` naming the `BADGE_n` reference of its logo; their logos may appear), an optional `direction`, the `mechanism` to rebuild for our brand, and in `/3`: `viewer.market` (`country`, `language`, `currency`), `brand_presence` (`standard` | `native`: a native disguise carries the logo as its sender and no big button), `recognition` (the kind of product a stranger must take it for at a glance, never the format), `native` (the ad is a screen people know, drawn exactly from HTML at every size by the free `static_native` node instead of painted: `x_post` (a post or a thread, with `reply_to`), `whatsapp`, `imessage`, `trustpilot`, `google_reviews`, `notes`, `slack`; on a 9:16 the app is drawn full screen as a phone shows it: status bar and app header pinned at the top, the reply bar and tab bar (or a keyboard under a chat or note) pinned at the bottom, the posts or messages as large as the clear band holds, and the frame's headline, caption and logo a card in the brand's colours under the app's header; a wall of reviews (`trustpilot`, `google_reviews`) takes up to 16 real reviews, the first two (`FEATURED_REVIEWS`) the ad's copy set whole in the safe area and the rest filling the whole frame behind them as large, sharp cards, turned and cut by its edges, because a wall wins on how many there are; a review longer than 160 characters is shown cut at a word with "…", and that cut text is the copy the review reads back; a chat (`whatsapp`, `imessage`) on a 9:16 never shrinks below about 62% of the phone scale: its earliest messages scroll up under the header, as the app does, the node's optional `scrolled` output lists the copy lines scrolled off that size, and the run review expects those lines nowhere on it; a logo file whose drawing runs off its own edge is set aside for a whole one, and a square mark where a wordmark goes is drawn larger; the run review never flags the app's own interface words (status bar, keyboard) as under Reels; the image model renders only the screen's `photos`, each a plain phone photo of ordinary people of the market; adding the block switches an existing ad on its next run, `--fix` does not apply, an invented brand handle or a rating the Knowledge does not hold (either decimal mark) is refused as `native_screen_invalid`, and a photo of ours goes in as its `image`: a native photo takes no `avatar`, since nothing would cast it), a role on each third-party brand (`partner` | `proof` | `category_signal`), a reference's `image` (wired as a picture of ours) and a person's `avatar` (the company avatar cast), and `concept` (`why_it_wins` in five layers, `load_bearing` elements with `have`/`adapt`/`missing` and `ours`, `verdict` `use`|`adapt`|`reject`, `transfer_note`; seeded from `baker winning-ads fit`). Every render keeps the load-bearing elements; `validate`/`run` refuse a `reject` or a `missing` element with nothing named (`concept_does_not_fit`) and a rating
|
|
6020
|
+
1. **`image_describe`** (`~google/gemini-pro-latest`) — reverse-engineers the image (layout by what fills each region, copy verbatim, typefaces, capture in words, the `mechanism` and the `audience` it is built for) and writes it next to the canvas as **`prompt.json`**, an **ad spec** (`"schema": "baker-static-ad/3"`; `/2` still reads) you rewrite into the ad you want: `viewer` (who sees it and what they already know), `message` (the one thing it tells them, never printed), `copy` (each line with `lettering`: `in_scene`, written on something in the picture (`on`, `look`), or `designed`, set in the brand's face), `scene` (`description`, `look`: photo | designed | illustration, `capture` in words, `mood`, `layout`), `brand`, `colors` (each tied to an object, from the brand palette), `references` (one picture each, labelled by role: `PERSON_1`, `ANIMAL_1`, `PRODUCT_1`, `BADGE_1`, `PROP_1`), `logo` (and `logo_position`), `third_party_brands` (other brands the offer is about, a car brand on a car loan: `name`, `what`, and `logo` naming the `BADGE_n` reference of its logo; their logos may appear), an optional `direction`, the `mechanism` to rebuild for our brand, and in `/3`: `viewer.market` (`country`, `language`, `currency`), `brand_presence` (`standard` | `native`: a native disguise carries the logo as its sender and no big button), `recognition` (the kind of product a stranger must take it for at a glance, never the format), `native` (the ad is a screen people know, drawn exactly from HTML at every size by the free `static_native` node instead of painted: `x_post` (a post or a thread, with `reply_to`), `whatsapp`, `imessage`, `trustpilot`, `google_reviews`, `notes`, `slack`; on a 9:16 the app is drawn full screen as a phone shows it: status bar and app header pinned at the top, the reply bar and tab bar (or a keyboard under a chat or note) pinned at the bottom, the posts or messages as large as the clear band holds, and the frame's headline, caption and logo a card in the brand's colours under the app's header; a wall of reviews (`trustpilot`, `google_reviews`) takes up to 16 real reviews, the first two (`FEATURED_REVIEWS`) the ad's copy set whole in the safe area and the rest filling the whole frame behind them as large, sharp cards, turned and cut by its edges, because a wall wins on how many there are; a review longer than 160 characters is shown cut at a word with "…", and that cut text is the copy the review reads back; a chat (`whatsapp`, `imessage`) on a 9:16 never shrinks below about 62% of the phone scale: its earliest messages scroll up under the header, as the app does, the node's optional `scrolled` output lists the copy lines scrolled off that size, and the run review expects those lines nowhere on it; a logo file whose drawing runs off its own edge is set aside for a whole one, and a square mark where a wordmark goes is drawn larger; the run review never flags the app's own interface words (status bar, keyboard) as under Reels; the image model renders only the screen's `photos`, each a plain phone photo of ordinary people of the market; adding the block switches an existing ad on its next run, `--fix` does not apply, an invented brand handle or a rating the Knowledge does not hold (either decimal mark) is refused as `native_screen_invalid`, and a photo of ours goes in as its `image`: a native photo takes no `avatar`, since nothing would cast it), a role on each third-party brand (`partner` | `proof` | `category_signal`), a reference's `image` (wired as a picture of ours) and a person's `avatar` (the company avatar cast), and `concept` (`why_it_wins` in five layers, `load_bearing` elements with `have`/`adapt`/`missing` and `ours`, `verdict` `use`|`adapt`|`reject`, `transfer_note`; seeded from `baker winning-ads fit`). Every render keeps the load-bearing elements; `validate`/`run` refuse a `reject` or a `missing` element with nothing named (`concept_does_not_fit`) and a rating the Knowledge does not hold (`proof_without_source`); a line citing a review platform gets its real badge wired. **You never write a model prompt**: every render prompt is compiled from this file for the model it runs on (short labelled sections for gpt-image, sentences with reasons for Gemini; each line quoted once; pictures numbered with one role each; colours from the brand; realism as capture cues; the safe area from its one owner; constraints last), on every `validate` and `run`, and shown on its node. A `prompt.json` written before the ad spec (the describe blueprint) is read through an adapter and keeps working. The source's own read (`source`, and the library's `reference`) stays as bookkeeping and is never sent to a model.
|
|
5996
6021
|
2. **element selection** (`~google/gemini-flash-latest`) — picks the **main, identity-critical** elements (the brand logo, a showcased product, a trust badge) **plus any foreground/hero person or animal** — the emotional focal point — even a generic one, because a free-generated face/muzzle reads as AI and grows artifacts; the emotional hero always gets a real-reference slot. When the advertiser's logo appears in **more than one lockup** (a square/icon **mark** and a horizontal **wordmark**), each is emitted as its **own** element (e.g. `LOGO_MARK`, `LOGO_WORDMARK`). The describe pass also records the ad's **typography** (each typeface's classification, a best-guess family, and its weight/case), which seeds the spec's `brand.typeface`. Background extras are dropped. Each element becomes a `references` entry in the spec, named by its role and naming the canvas slot it fills, so the file says which slot grounds which subject.
|
|
5997
6022
|
|
|
5998
6023
|
It then scaffolds a canvas on which **the image model designs the finished ad**, holding only what `prompt.json` holds; no element is mandatory:
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
debugLogHttp,
|
|
3
3
|
readBodyForLog
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Y77DA7EO.js";
|
|
5
5
|
import {
|
|
6
6
|
getEnv
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-WJIYNKLI.js";
|
|
8
8
|
|
|
9
9
|
// src/client.ts
|
|
10
10
|
var MAX_RATE_LIMIT_RETRIES = 3;
|
|
@@ -208,4 +208,4 @@ export {
|
|
|
208
208
|
apiGet,
|
|
209
209
|
apiPost
|
|
210
210
|
};
|
|
211
|
-
//# sourceMappingURL=chunk-
|
|
211
|
+
//# sourceMappingURL=chunk-6Z4ETVEZ.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
debugLogHttp,
|
|
3
3
|
readBodyForLog
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Y77DA7EO.js";
|
|
5
5
|
import {
|
|
6
6
|
API_KEY_FORMAT_MESSAGE,
|
|
7
7
|
DIRECT_ROUTE,
|
|
@@ -22,11 +22,11 @@ import {
|
|
|
22
22
|
refuseNonPublicUrl,
|
|
23
23
|
requireConnection,
|
|
24
24
|
shouldEscalate
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-WJIYNKLI.js";
|
|
26
26
|
|
|
27
|
-
//
|
|
27
|
+
// ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/index.js
|
|
28
28
|
var require_safe_stable_stringify = __commonJS({
|
|
29
|
-
"
|
|
29
|
+
"../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/index.js"(exports, module) {
|
|
30
30
|
"use strict";
|
|
31
31
|
var { hasOwnProperty } = Object.prototype;
|
|
32
32
|
var stringify = configure2();
|
|
@@ -1408,7 +1408,7 @@ function resolveAdaptFormats(params) {
|
|
|
1408
1408
|
return params.formats ?? [];
|
|
1409
1409
|
}
|
|
1410
1410
|
|
|
1411
|
-
//
|
|
1411
|
+
// ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/esm/wrapper.js
|
|
1412
1412
|
var import__ = __toESM(require_safe_stable_stringify(), 1);
|
|
1413
1413
|
var configure = import__.default.configure;
|
|
1414
1414
|
var wrapper_default = import__.default;
|
|
@@ -2826,12 +2826,6 @@ var MODEL_ROLES = {
|
|
|
2826
2826
|
* only and asked once per ad and viewer, so Flash; the calibration set (E8) decides.
|
|
2827
2827
|
*/
|
|
2828
2828
|
viewerCheck: { kind: "text_generate", model: "~google/gemini-flash-latest" },
|
|
2829
|
-
/**
|
|
2830
|
-
* Picks the customer truths out of reviews, testimonials and threads (`baker research
|
|
2831
|
-
* customers`): returns exact spans the code then checks against the source. Text only and
|
|
2832
|
-
* one call per batch, so Flash.
|
|
2833
|
-
*/
|
|
2834
|
-
customerInsights: { kind: "text_generate", model: "~google/gemini-flash-latest" },
|
|
2835
2829
|
/**
|
|
2836
2830
|
* The same viewer shown the FINISHED static ad at phone size for about three seconds, with no
|
|
2837
2831
|
* brand and no brief: what is sold, whether it is for them, what to do next, what stopped them
|
|
@@ -2873,7 +2867,6 @@ var PLATE_JUDGE_MODEL = MODEL_ROLES.plateJudge.model;
|
|
|
2873
2867
|
var AD_DESCRIBE_MODEL = MODEL_ROLES.adDescribe.model;
|
|
2874
2868
|
var BLUEPRINT_ANALYSIS_MODEL = MODEL_ROLES.blueprintAnalysis.model;
|
|
2875
2869
|
var VIEWER_CHECK_MODEL = MODEL_ROLES.viewerCheck.model;
|
|
2876
|
-
var CUSTOMER_INSIGHTS_MODEL = MODEL_ROLES.customerInsights.model;
|
|
2877
2870
|
var GLANCE_CHECK_MODEL = MODEL_ROLES.glanceCheck.model;
|
|
2878
2871
|
var TEXT_BOX_READER_MODEL = MODEL_ROLES.textBoxReader.model;
|
|
2879
2872
|
var VIDEO_DECONSTRUCT_MODEL = MODEL_ROLES.videoDeconstruct.model;
|
|
@@ -6919,18 +6912,6 @@ function conceptProblems(spec) {
|
|
|
6919
6912
|
}
|
|
6920
6913
|
return problems;
|
|
6921
6914
|
}
|
|
6922
|
-
var PLACEHOLDER = "[TODO";
|
|
6923
|
-
function unwrittenProblems(spec) {
|
|
6924
|
-
const problems = [];
|
|
6925
|
-
if (spec.scene.description.includes(PLACEHOLDER)) {
|
|
6926
|
-
problems.push("The scene is still a placeholder: write what the viewer sees in `scene.description`.");
|
|
6927
|
-
}
|
|
6928
|
-
const lines = spec.copy.filter((line) => line.text.includes(PLACEHOLDER));
|
|
6929
|
-
if (lines.length > 0) {
|
|
6930
|
-
problems.push(`${lines.length} line(s) of copy are still placeholders: write each as it prints, or drop it.`);
|
|
6931
|
-
}
|
|
6932
|
-
return problems;
|
|
6933
|
-
}
|
|
6934
6915
|
function readAdSpec(raw) {
|
|
6935
6916
|
if (isAdSpec(raw)) return AdSpecSchema.parse(raw);
|
|
6936
6917
|
const advertiser = raw?.reference?.advertiser;
|
|
@@ -18404,7 +18385,6 @@ export {
|
|
|
18404
18385
|
AD_DESCRIBE_MODEL,
|
|
18405
18386
|
BLUEPRINT_ANALYSIS_MODEL,
|
|
18406
18387
|
VIEWER_CHECK_MODEL,
|
|
18407
|
-
CUSTOMER_INSIGHTS_MODEL,
|
|
18408
18388
|
GLANCE_CHECK_MODEL,
|
|
18409
18389
|
VIDEO_DECONSTRUCT_MODEL,
|
|
18410
18390
|
VOICEOVER_MODEL,
|
|
@@ -18476,7 +18456,6 @@ export {
|
|
|
18476
18456
|
isAdSpec,
|
|
18477
18457
|
conceptSummary,
|
|
18478
18458
|
conceptProblems,
|
|
18479
|
-
unwrittenProblems,
|
|
18480
18459
|
readAdSpec,
|
|
18481
18460
|
withRenderedLettering,
|
|
18482
18461
|
conceptClassOf,
|
|
@@ -18564,4 +18543,4 @@ export {
|
|
|
18564
18543
|
defaultRegistry,
|
|
18565
18544
|
createEngineFromEnv
|
|
18566
18545
|
};
|
|
18567
|
-
//# sourceMappingURL=chunk-
|
|
18546
|
+
//# sourceMappingURL=chunk-DL7VXQEI.js.map
|