@koda-sl/baker-cli 0.25.1 → 0.27.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 (182) hide show
  1. package/README.md +218 -0
  2. package/dist/cli.js +1 -1
  3. package/dist/commands/ads/index.d.ts.map +1 -1
  4. package/dist/commands/ads/index.js +12 -3
  5. package/dist/commands/ads/index.js.map +1 -1
  6. package/dist/commands/ads/linkedin/account.d.ts +20 -0
  7. package/dist/commands/ads/linkedin/account.d.ts.map +1 -0
  8. package/dist/commands/ads/linkedin/account.js +39 -0
  9. package/dist/commands/ads/linkedin/account.js.map +1 -0
  10. package/dist/commands/ads/linkedin/accounts.d.ts +20 -0
  11. package/dist/commands/ads/linkedin/accounts.d.ts.map +1 -0
  12. package/dist/commands/ads/linkedin/accounts.js +56 -0
  13. package/dist/commands/ads/linkedin/accounts.js.map +1 -0
  14. package/dist/commands/ads/linkedin/analytics.d.ts +84 -0
  15. package/dist/commands/ads/linkedin/analytics.d.ts.map +1 -0
  16. package/dist/commands/ads/linkedin/analytics.js +249 -0
  17. package/dist/commands/ads/linkedin/analytics.js.map +1 -0
  18. package/dist/commands/ads/linkedin/audience-size.d.ts +28 -0
  19. package/dist/commands/ads/linkedin/audience-size.d.ts.map +1 -0
  20. package/dist/commands/ads/linkedin/audience-size.js +75 -0
  21. package/dist/commands/ads/linkedin/audience-size.js.map +1 -0
  22. package/dist/commands/ads/linkedin/audit.d.ts +35 -0
  23. package/dist/commands/ads/linkedin/audit.d.ts.map +1 -0
  24. package/dist/commands/ads/linkedin/audit.js +136 -0
  25. package/dist/commands/ads/linkedin/audit.js.map +1 -0
  26. package/dist/commands/ads/linkedin/bid-pricing.d.ts +38 -0
  27. package/dist/commands/ads/linkedin/bid-pricing.d.ts.map +1 -0
  28. package/dist/commands/ads/linkedin/bid-pricing.js +76 -0
  29. package/dist/commands/ads/linkedin/bid-pricing.js.map +1 -0
  30. package/dist/commands/ads/linkedin/campaign-groups.d.ts +32 -0
  31. package/dist/commands/ads/linkedin/campaign-groups.d.ts.map +1 -0
  32. package/dist/commands/ads/linkedin/campaign-groups.js +50 -0
  33. package/dist/commands/ads/linkedin/campaign-groups.js.map +1 -0
  34. package/dist/commands/ads/linkedin/campaigns.d.ts +36 -0
  35. package/dist/commands/ads/linkedin/campaigns.d.ts.map +1 -0
  36. package/dist/commands/ads/linkedin/campaigns.js +57 -0
  37. package/dist/commands/ads/linkedin/campaigns.js.map +1 -0
  38. package/dist/commands/ads/linkedin/conversation.d.ts +36 -0
  39. package/dist/commands/ads/linkedin/conversation.d.ts.map +1 -0
  40. package/dist/commands/ads/linkedin/conversation.js +77 -0
  41. package/dist/commands/ads/linkedin/conversation.js.map +1 -0
  42. package/dist/commands/ads/linkedin/conversions.d.ts +2 -0
  43. package/dist/commands/ads/linkedin/conversions.d.ts.map +1 -0
  44. package/dist/commands/ads/linkedin/conversions.js +102 -0
  45. package/dist/commands/ads/linkedin/conversions.js.map +1 -0
  46. package/dist/commands/ads/linkedin/creatives.d.ts +36 -0
  47. package/dist/commands/ads/linkedin/creatives.d.ts.map +1 -0
  48. package/dist/commands/ads/linkedin/creatives.js +57 -0
  49. package/dist/commands/ads/linkedin/creatives.js.map +1 -0
  50. package/dist/commands/ads/linkedin/demographics.d.ts +40 -0
  51. package/dist/commands/ads/linkedin/demographics.d.ts.map +1 -0
  52. package/dist/commands/ads/linkedin/demographics.js +117 -0
  53. package/dist/commands/ads/linkedin/demographics.js.map +1 -0
  54. package/dist/commands/ads/linkedin/facets.d.ts +2 -0
  55. package/dist/commands/ads/linkedin/facets.d.ts.map +1 -0
  56. package/dist/commands/ads/linkedin/facets.js +95 -0
  57. package/dist/commands/ads/linkedin/facets.js.map +1 -0
  58. package/dist/commands/ads/linkedin/forecast.d.ts +50 -0
  59. package/dist/commands/ads/linkedin/forecast.d.ts.map +1 -0
  60. package/dist/commands/ads/linkedin/forecast.js +83 -0
  61. package/dist/commands/ads/linkedin/forecast.js.map +1 -0
  62. package/dist/commands/ads/linkedin/index.d.ts +19 -0
  63. package/dist/commands/ads/linkedin/index.d.ts.map +1 -0
  64. package/dist/commands/ads/linkedin/index.js +83 -0
  65. package/dist/commands/ads/linkedin/index.js.map +1 -0
  66. package/dist/commands/ads/linkedin/leads.d.ts +40 -0
  67. package/dist/commands/ads/linkedin/leads.d.ts.map +1 -0
  68. package/dist/commands/ads/linkedin/leads.js +75 -0
  69. package/dist/commands/ads/linkedin/leads.js.map +1 -0
  70. package/dist/commands/ads/linkedin/presets.d.ts +40 -0
  71. package/dist/commands/ads/linkedin/presets.d.ts.map +1 -0
  72. package/dist/commands/ads/linkedin/presets.js +193 -0
  73. package/dist/commands/ads/linkedin/presets.js.map +1 -0
  74. package/dist/commands/ads/linkedin/presets.test.d.ts +2 -0
  75. package/dist/commands/ads/linkedin/presets.test.d.ts.map +1 -0
  76. package/dist/commands/ads/linkedin/presets.test.js +98 -0
  77. package/dist/commands/ads/linkedin/presets.test.js.map +1 -0
  78. package/dist/commands/ads/linkedin/schemas.d.ts +2 -0
  79. package/dist/commands/ads/linkedin/schemas.d.ts.map +1 -0
  80. package/dist/commands/ads/linkedin/schemas.js +300 -0
  81. package/dist/commands/ads/linkedin/schemas.js.map +1 -0
  82. package/dist/commands/ads/linkedin/shared.d.ts +17 -0
  83. package/dist/commands/ads/linkedin/shared.d.ts.map +1 -0
  84. package/dist/commands/ads/linkedin/shared.js +116 -0
  85. package/dist/commands/ads/linkedin/shared.js.map +1 -0
  86. package/dist/commands/ads/linkedin/top-companies.d.ts +44 -0
  87. package/dist/commands/ads/linkedin/top-companies.d.ts.map +1 -0
  88. package/dist/commands/ads/linkedin/top-companies.js +86 -0
  89. package/dist/commands/ads/linkedin/top-companies.js.map +1 -0
  90. package/dist/commands/ads/x/accounts.d.ts +14 -0
  91. package/dist/commands/ads/x/accounts.d.ts.map +1 -0
  92. package/dist/commands/ads/x/accounts.js +73 -0
  93. package/dist/commands/ads/x/accounts.js.map +1 -0
  94. package/dist/commands/ads/x/active-entities.d.ts +43 -0
  95. package/dist/commands/ads/x/active-entities.d.ts.map +1 -0
  96. package/dist/commands/ads/x/active-entities.js +88 -0
  97. package/dist/commands/ads/x/active-entities.js.map +1 -0
  98. package/dist/commands/ads/x/audiences.d.ts +19 -0
  99. package/dist/commands/ads/x/audiences.d.ts.map +1 -0
  100. package/dist/commands/ads/x/audiences.js +65 -0
  101. package/dist/commands/ads/x/audiences.js.map +1 -0
  102. package/dist/commands/ads/x/campaigns.d.ts +34 -0
  103. package/dist/commands/ads/x/campaigns.d.ts.map +1 -0
  104. package/dist/commands/ads/x/campaigns.js +56 -0
  105. package/dist/commands/ads/x/campaigns.js.map +1 -0
  106. package/dist/commands/ads/x/cards.d.ts +19 -0
  107. package/dist/commands/ads/x/cards.d.ts.map +1 -0
  108. package/dist/commands/ads/x/cards.js +65 -0
  109. package/dist/commands/ads/x/cards.js.map +1 -0
  110. package/dist/commands/ads/x/error-parser.d.ts +3 -0
  111. package/dist/commands/ads/x/error-parser.d.ts.map +1 -0
  112. package/dist/commands/ads/x/error-parser.js +80 -0
  113. package/dist/commands/ads/x/error-parser.js.map +1 -0
  114. package/dist/commands/ads/x/funding.d.ts +19 -0
  115. package/dist/commands/ads/x/funding.d.ts.map +1 -0
  116. package/dist/commands/ads/x/funding.js +65 -0
  117. package/dist/commands/ads/x/funding.js.map +1 -0
  118. package/dist/commands/ads/x/index.d.ts +2 -0
  119. package/dist/commands/ads/x/index.d.ts.map +1 -0
  120. package/dist/commands/ads/x/index.js +50 -0
  121. package/dist/commands/ads/x/index.js.map +1 -0
  122. package/dist/commands/ads/x/line-items.d.ts +34 -0
  123. package/dist/commands/ads/x/line-items.d.ts.map +1 -0
  124. package/dist/commands/ads/x/line-items.js +55 -0
  125. package/dist/commands/ads/x/line-items.js.map +1 -0
  126. package/dist/commands/ads/x/media.d.ts +24 -0
  127. package/dist/commands/ads/x/media.d.ts.map +1 -0
  128. package/dist/commands/ads/x/media.js +70 -0
  129. package/dist/commands/ads/x/media.js.map +1 -0
  130. package/dist/commands/ads/x/output.d.ts +13 -0
  131. package/dist/commands/ads/x/output.d.ts.map +1 -0
  132. package/dist/commands/ads/x/output.js +75 -0
  133. package/dist/commands/ads/x/output.js.map +1 -0
  134. package/dist/commands/ads/x/presets.d.ts +15 -0
  135. package/dist/commands/ads/x/presets.d.ts.map +1 -0
  136. package/dist/commands/ads/x/presets.js +60 -0
  137. package/dist/commands/ads/x/presets.js.map +1 -0
  138. package/dist/commands/ads/x/promoted-tweets.d.ts +29 -0
  139. package/dist/commands/ads/x/promoted-tweets.d.ts.map +1 -0
  140. package/dist/commands/ads/x/promoted-tweets.js +74 -0
  141. package/dist/commands/ads/x/promoted-tweets.js.map +1 -0
  142. package/dist/commands/ads/x/run-list.d.ts +17 -0
  143. package/dist/commands/ads/x/run-list.d.ts.map +1 -0
  144. package/dist/commands/ads/x/run-list.js +60 -0
  145. package/dist/commands/ads/x/run-list.js.map +1 -0
  146. package/dist/commands/ads/x/stats/index.d.ts +2 -0
  147. package/dist/commands/ads/x/stats/index.d.ts.map +1 -0
  148. package/dist/commands/ads/x/stats/index.js +32 -0
  149. package/dist/commands/ads/x/stats/index.js.map +1 -0
  150. package/dist/commands/ads/x/stats/job-create.d.ts +58 -0
  151. package/dist/commands/ads/x/stats/job-create.d.ts.map +1 -0
  152. package/dist/commands/ads/x/stats/job-create.js +95 -0
  153. package/dist/commands/ads/x/stats/job-create.js.map +1 -0
  154. package/dist/commands/ads/x/stats/job-status.d.ts +18 -0
  155. package/dist/commands/ads/x/stats/job-status.d.ts.map +1 -0
  156. package/dist/commands/ads/x/stats/job-status.js +58 -0
  157. package/dist/commands/ads/x/stats/job-status.js.map +1 -0
  158. package/dist/commands/ads/x/stats/job.d.ts +63 -0
  159. package/dist/commands/ads/x/stats/job.d.ts.map +1 -0
  160. package/dist/commands/ads/x/stats/job.js +183 -0
  161. package/dist/commands/ads/x/stats/job.js.map +1 -0
  162. package/dist/commands/ads/x/stats/sync.d.ts +73 -0
  163. package/dist/commands/ads/x/stats/sync.d.ts.map +1 -0
  164. package/dist/commands/ads/x/stats/sync.js +151 -0
  165. package/dist/commands/ads/x/stats/sync.js.map +1 -0
  166. package/dist/commands/ads/x/targeting-constants.d.ts +34 -0
  167. package/dist/commands/ads/x/targeting-constants.d.ts.map +1 -0
  168. package/dist/commands/ads/x/targeting-constants.js +80 -0
  169. package/dist/commands/ads/x/targeting-constants.js.map +1 -0
  170. package/dist/commands/ads/x/targeting-criteria.d.ts +24 -0
  171. package/dist/commands/ads/x/targeting-criteria.d.ts.map +1 -0
  172. package/dist/commands/ads/x/targeting-criteria.js +69 -0
  173. package/dist/commands/ads/x/targeting-criteria.js.map +1 -0
  174. package/dist/env.d.ts +2 -0
  175. package/dist/env.d.ts.map +1 -1
  176. package/dist/env.js +8 -0
  177. package/dist/env.js.map +1 -1
  178. package/dist/error-handler.d.ts +1 -1
  179. package/dist/error-handler.d.ts.map +1 -1
  180. package/dist/error-handler.js +6 -0
  181. package/dist/error-handler.js.map +1 -1
  182. package/package.json +1 -1
package/README.md CHANGED
@@ -27,6 +27,9 @@ export BAKER_GA4_PROPERTY_ID="properties/123456789"
27
27
  # Optional: default GSC site URL
28
28
  export BAKER_GSC_SITE_URL="https://example.com/"
29
29
 
30
+ # Optional: default X Ads account ID (base36)
31
+ export BAKER_X_ADS_ACCOUNT_ID="18ce53xyz"
32
+
30
33
  # Required for `baker actions ...` commands that stage against a chat
31
34
  export BAKER_CHAT_ID="<chat-id>"
32
35
  ```
@@ -36,6 +39,7 @@ export BAKER_CHAT_ID="<chat-id>"
36
39
  - `BAKER_GOOGLE_ADS_CUSTOMER_ID` — default Google Ads customer ID (10 digits). Used when `--customer-id` is not passed. If neither is set and the account has exactly one Google Ads customer, it is auto-selected.
37
40
  - `BAKER_GA4_PROPERTY_ID` — default GA4 property ID. Used when `--property-id` is not passed. If neither is set and exactly one property is connected, it is auto-selected.
38
41
  - `BAKER_GSC_SITE_URL` — default GSC site URL. Used when `--site-url` is not passed. If neither is set and exactly one site is verified, it is auto-selected.
42
+ - `BAKER_X_ADS_ACCOUNT_ID` — default X Ads account ID (base36). Used when `--account-id` is not passed. If neither is set and exactly one X Ads account is connected, it is auto-selected.
39
43
 
40
44
  ## Output Format
41
45
 
@@ -688,6 +692,75 @@ All errors include a `fix` object with `action`, `correctedCommand` (when applic
688
692
 
689
693
  ---
690
694
 
695
+ ### X (Twitter) Ads (`baker ads x`)
696
+
697
+ Read X Ads campaigns, line items, promoted tweets, creatives, audiences, and analytics. Powered by the X Ads API v12.
698
+
699
+ **Environment:**
700
+ - `BAKER_X_ADS_ACCOUNT_ID` — default account ID (base36). Used when `--account-id` is not passed. If neither is set and exactly one X Ads account is connected, it's auto-selected.
701
+
702
+ **Subcommands:**
703
+
704
+ | Subcommand | What it returns |
705
+ |---|---|
706
+ | `accounts` | All accessible X Ads accounts |
707
+ | `funding` | Funding instruments for an account |
708
+ | `campaigns` | Campaigns (filter by `--funding-instrument-ids`, `--campaign-ids`) |
709
+ | `line-items` | Line items / ad groups (filter by `--campaign-ids`, `--line-item-ids`) |
710
+ | `promoted-tweets` | Promoted tweets (filter by `--line-item-ids`) |
711
+ | `cards` | Website cards, video cards, carousels |
712
+ | `media` | Media library (images / GIFs / videos) — `--media-type IMAGE\|GIF\|VIDEO` |
713
+ | `audiences` | Custom audiences (size, targetable status) |
714
+ | `targeting-criteria` | Targeting attached to line items |
715
+ | `targeting-constants` | Lookup locations / interests / events / devices etc. — `--constant <name> --q "Madrid"` |
716
+ | `active-entities` | Entities with metric activity in a time range |
717
+ | `stats sync` | Synchronous analytics (≤7 days, no segmentation) |
718
+ | `stats job` | Async stats end-to-end (creates + polls + downloads + decompresses). Must run in the background (the harness enforces this). Use for ranges >7 days, segmented stats, or when sync limits are hit. |
719
+ | `stats job-create` | Low-level: create async stats job, return ID immediately |
720
+ | `stats job-status` | Low-level: poll job status / get download URL |
721
+
722
+ **Examples:**
723
+
724
+ ```bash
725
+ baker ads x accounts
726
+ baker ads x campaigns --account-id 18ce53xyz
727
+ baker ads x line-items --account-id 18ce53xyz --campaign-ids abc
728
+ baker ads x promoted-tweets --account-id 18ce53xyz
729
+
730
+ # Sync analytics with a preset (saves tokens)
731
+ baker ads x stats sync --preset campaign-engagement-7d --entity-ids abc,def
732
+
733
+ # Free-form sync stats
734
+ baker ads x stats sync --account-id 18ce53xyz --entity LINE_ITEM \
735
+ --entity-ids abc,def --start-time 2026-05-01T00:00:00Z --end-time 2026-05-07T00:00:00Z \
736
+ --metric-groups ENGAGEMENT,BILLING --granularity DAY
737
+
738
+ # Async job, sync from the CLI's perspective (creates → polls → downloads → returns).
739
+ # Must be invoked with run_in_background: true. The harness will block otherwise.
740
+ baker ads x stats job --account-id 18ce53xyz --entity CAMPAIGN \
741
+ --entity-ids abc --start-time 2026-04-01T00:00:00Z --end-time 2026-05-01T00:00:00Z \
742
+ --metric-groups ENGAGEMENT,BILLING --segmentation-type LOCATIONS
743
+
744
+ # Low-level (don't wait, manage polling yourself):
745
+ baker ads x stats job-create --account-id 18ce53xyz --entity CAMPAIGN \
746
+ --entity-ids abc --start-time 2026-04-01T00:00:00Z --end-time 2026-05-01T00:00:00Z \
747
+ --metric-groups ENGAGEMENT,BILLING --segmentation-type LOCATIONS
748
+ baker ads x stats job-status --account-id 18ce53xyz --job-id <jobId>
749
+
750
+ # List sync presets
751
+ baker ads x stats sync --list-presets
752
+
753
+ # Targeting lookups
754
+ baker ads x targeting-constants --constant locations --q "Madrid" --country-code ES
755
+ baker ads x targeting-constants --constant interests
756
+ ```
757
+
758
+ **Caching:** account list 1h · campaigns/line items/promoted tweets 1h (date-keyed) · cards/media/audiences 6h · stats sync 1h · targeting constants 7 days. Pass `--no-cache` to bypass.
759
+
760
+ **Rate limits:** server-side buckets (`xAds:read`, `xAds:write`, `xAds:analyticsSync`, `xAds:analyticsAsync`, `xAds:audiences`) sit well under X's published quotas; 429s honor `x-account-rate-limit-reset` / `x-rate-limit-reset` headers.
761
+
762
+ ---
763
+
691
764
  ### Meta Ads (`baker ads meta`)
692
765
 
693
766
  Meta Marketing API (Facebook + Instagram). Connect via OAuth in the dashboard, pick which ad accounts to scope to, then call from the CLI. Account ID via `--account-id act_123` or `BAKER_META_AD_ACCOUNT_ID` env var.
@@ -784,6 +857,151 @@ The HTTP backend exposes more endpoints (catalogs, ad-studies, ad-images, labels
784
857
 
785
858
  ---
786
859
 
860
+ ### LinkedIn Ads (`baker ads linkedin`)
861
+
862
+ LinkedIn Marketing API for B2B ad insights. Connect via OAuth in the dashboard, pick which ad accounts to scope to, then call from the CLI. Account ID via `--account-id 503001492` (numeric) or `--account-urn urn:li:sponsoredAccount:503001492`, or set `BAKER_LINKEDIN_AD_ACCOUNT_ID` env.
863
+
864
+ **LinkedIn's superpower over Meta/Google:** firmographic pivots on every analytics query — `pivot=job-title|company|industry|seniority|function|company-size`. Meta has nothing like `MEMBER_COMPANY` returning company URNs of every account whose employees saw the ad. AI agents should reach for `linkedin demographics` and `linkedin top-companies` first when answering ABM/persona questions.
865
+
866
+ **Read-only v1.** No campaign/creative writes. The surface is curated for AI agents per the LinkedIn Ads playbook (B2B operating manual covering audits, ABM, attribution, scaling).
867
+
868
+ #### Common AI questions, mapped to commands
869
+
870
+ ```bash
871
+ # "How is account X doing this week?"
872
+ baker ads linkedin analytics
873
+
874
+ # "Who are we reaching, by job title?" (the LinkedIn killer)
875
+ baker ads linkedin analytics --level campaign --campaign-id 1234 \
876
+ --pivot job-title --intent baseline --last-days 30
877
+
878
+ # "Top companies seeing this campaign" (ABM feedback loop)
879
+ baker ads linkedin top-companies --campaign-id 1234 --last-days 30
880
+
881
+ # "Who is in our audience by industry/seniority/function/title — all at once?"
882
+ baker ads linkedin demographics --campaign-id 1234 --last-days 30
883
+
884
+ # "Revenue by campaign over Q1"
885
+ baker ads linkedin analytics --level campaign --campaign-id 1,2,3 \
886
+ --intent revenue --start 2026-01-01 --end 2026-03-31 --granularity MONTHLY
887
+
888
+ # "How is the lead form converting?"
889
+ baker ads linkedin analytics --level campaign --campaign-id 1234 --intent lead-gen
890
+
891
+ # "Which creatives are fatigued?"
892
+ baker ads linkedin analytics --level creative --creative-id 1,2,3 --intent ranking
893
+
894
+ # "Run the playbook audit"
895
+ baker ads linkedin audit --account-id 503001492
896
+ baker ads linkedin audit --account-id 503001492 --format md # deliverable-ready table
897
+
898
+ # "Pull the leads from the last 7 days"
899
+ baker ads linkedin leads --account-id 503001492 --since-days 7
900
+
901
+ # "Is the Insight Tag healthy?"
902
+ baker ads linkedin conversions health --account-id 503001492
903
+
904
+ # "Pre-launch sanity check: is this audience too narrow?"
905
+ baker ads linkedin audience-size --account-id 503001492 --targeting-file targeting.json
906
+
907
+ # "What bid should I start at?"
908
+ baker ads linkedin bid-pricing --account-id 503001492 \
909
+ --objective LEAD_GENERATION --cost-type CPC --targeting-file targeting.json
910
+ ```
911
+
912
+ #### Smart defaults
913
+
914
+ **`analytics`** — the 3-axis workhorse:
915
+ - `--level account` (override: `campaign-group|campaign|creative`)
916
+ - `--intent baseline` field bundle. Intents: `baseline | revenue | funnel | engagement | video | lead-gen | inmail | document | ranking | identity`. List with `--list-intents`.
917
+ - `--pivot none`. Pivots: `none | campaign | campaign-group | creative | company | account | conversion | job-title | job-function | seniority | industry | company-size | country | region | device | placement | serving-location | card-index | objective | conversation-node | conversation-node-button`. List with `--list-pivots`.
918
+ - `--granularity DAILY`. Auto-forced to `ALL` when pivoting on a `MEMBER_*` dim (LinkedIn rejects DAILY + demographic) — surfaced in stderr + `warnings`.
919
+ - `--last-days 7` unless you pass `--start`/`--end`.
920
+ - Sort: `costInUsd` descending. `--no-sort` to disable.
921
+ - Derived metrics injected client-side: `ctr`, `cpc`, `cpm`, `frequency`, `leadCompletionRate` — LinkedIn doesn't return these.
922
+ - URN normalization: every URN field becomes `{type, id, urn}` so rows are self-describing.
923
+ - Wide windows (>180d) auto-segment into 30-day chunks and combine, since `q=analytics` caps at 15,000 rows with no pagination.
924
+
925
+ **Listings** (`campaign-groups`/`campaigns`/`creatives`) default to `ACTIVE` only — pass `--all-statuses` to widen, or `--statuses ACTIVE,PAUSED` for a custom set.
926
+
927
+ **Pagination** is auto-drained (start/count loop hidden).
928
+
929
+ **Demographic pivots** (`MEMBER_*`) come back delayed 12-24h with a ≥3-event privacy floor — small buckets are dropped. The CLI surfaces `DELAYED_DEMOGRAPHIC` and (when results are unexpectedly empty) `BELOW_PRIVACY_FLOOR` warnings.
930
+
931
+ #### Full command surface
932
+
933
+ ```
934
+ accounts # accounts in the connected picker scope
935
+ accounts --include-all # every account the token can see
936
+ account # single-account detail (currency, type, status)
937
+
938
+ campaign-groups # default ACTIVE-only
939
+ campaigns # default ACTIVE-only; reveals audit-relevant settings
940
+ creatives # default ACTIVE intended-status
941
+
942
+ analytics # see "Smart defaults" above (3-axis: level × intent × pivot)
943
+ demographics # sweep all firmographic pivots in one call
944
+ top-companies # pivot=MEMBER_COMPANY shortcut (ABM)
945
+ conversation # pivot=CONVERSATION_NODE_BUTTON shortcut (Sponsored Messaging)
946
+
947
+ facets list # all targeting facets (industries, seniorities, titles, employers, …)
948
+ facets values --facet titles --query "..." # typeahead → URNs
949
+ audience-size # estimate reach for a targetingCriteria payload + sweet-spot warnings
950
+ bid-pricing # min/suggested/max bid + playbook §06 floor (2/3 of suggested)
951
+ forecast # adSupplyForecasts wrapper for greenfield planning
952
+
953
+ leads # Lead Gen Form responses (90d retention — sync to your CRM)
954
+ conversions list # conversion rules (Insight Tag + CAPI)
955
+ conversions health # 5-point Insight Tag / CAPI health check (playbook §07)
956
+
957
+ audit # 30+ playbook checks → severity-tagged findings
958
+ audit --format md # deliverable-ready markdown table
959
+ ```
960
+
961
+ #### `audit` — playbook diagnostic
962
+
963
+ Runs in parallel: account detail + every campaign + every creative + conversion rules + account-level analytics + per-campaign frequency. Synthesizes findings across:
964
+
965
+ - **Settings** — Audience Expansion disabled, LinkedIn Audience Network disabled, Manual CPC bidding (not Maximum Delivery), daily budget ≥ $50, lifetime budget cap set, budget-fragmentation guard
966
+ - **Tracking** — at least one enabled rule, lead/purchase event configured, CAPI events recent (≤7d), `ONE_TIME_EACH_MEMBER` for leads, view-through window ≤7d, click attribution 7-90d
967
+ - **Creative** — ≥3 ad formats per campaign, TLA presence, no creative active >90d, frequency <7, REJECTED creatives flagged
968
+ - **Performance** — account CTR ≥ 0.4%, LP CVR ≥ 2%, lead-form completion ≥ 10%, delivered impressions in window, spend > $0
969
+ - **Hygiene** — no abandoned DRAFT campaigns, every active campaign has at least one creative
970
+
971
+ Each finding: `{id, area, check, status, severity, evidence, fix: {explanation, playbookRef}}`. Filter with `--severity critical,high` or `--area Settings,Tracking`. The JSON `summary` counts pass/critical/high/medium/low/n_a.
972
+
973
+ #### Auth + caching
974
+
975
+ - OAuth tokens auto-refresh server-side. On 401, reconnect LinkedIn from dashboard → integrations.
976
+ - Account scoping: backend rejects calls against an account not in the picker selection.
977
+ - Pinned API version: `Linkedin-Version: 202604` (declared in `oauth/constants.ts`). Bump as a coordinated change — LinkedIn deprecates monthly with a ~12-month support window.
978
+ - 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.
979
+ - `--skip-cache` (server-side) and `--no-cache` (client-side, where supported) on any command.
980
+
981
+ #### LinkedIn-specific gotchas
982
+
983
+ - **Demographic + DAILY = error.** CLI auto-forces granularity=ALL when pivoting on `MEMBER_*` and warns.
984
+ - **3-event privacy floor.** Small demographic buckets are dropped silently — widen the date range if results are sparse.
985
+ - **15,000-row cap on `q=analytics`** (no pagination). CLI auto-segments by month for wide windows.
986
+ - **Lead Gen Form 90-day retention.** Sync via `baker ads linkedin leads` regularly or lose them.
987
+ - **LinkedIn can overspend daily budget by 50%** — only `totalBudget` lifetime cap stops runaway spend (audit check).
988
+ - **CPC can look high** ($8–14 NA baseline) but CPL competitive thanks to firmographic targeting. Use playbook §00 dynamic benchmarks (geo + industry multipliers) before judging.
989
+ - **No native change-history API.** Activity logs are not surfaced — playbook §24 calls this out as a gap.
990
+
991
+ #### Error codes
992
+
993
+ | HTTP | LinkedIn `serviceErrorCode` | CLI maps to | Action |
994
+ |------|---|---|---|
995
+ | 401 | 100 | `UNAUTHORIZED` | Reconnect LinkedIn in dashboard |
996
+ | 403 | — | `FORBIDDEN` | Token lacks scope or account role |
997
+ | 404 | 65604 | `NOT_FOUND` | Entity removed or not in scope |
998
+ | 410 | — | `GONE` | Entity deleted; URN no longer queryable |
999
+ | 426 | — | `INTERNAL_ERROR` | LinkedIn-Version deprecated; backend bump required |
1000
+ | 429 | 101 | `RATE_LIMITED` | Auto-backoff with Retry-After; if you see this, retry budget exhausted |
1001
+ | 400 | 65601 / 65603 | `VALIDATION_ERROR` | Fix request — see `error.fix.explanation` |
1002
+
1003
+ ---
1004
+
787
1005
  ### Google Analytics 4 (`baker ga4`)
788
1006
 
789
1007
  GA4 commands for multi-channel audits. Playbook-aligned presets, property health audits, and free-form Data API queries.
package/dist/cli.js CHANGED
@@ -12,7 +12,7 @@ import { videosCommand } from "./commands/videos/index.js";
12
12
  const main = defineCommand({
13
13
  meta: {
14
14
  name: "baker",
15
- version: "0.25.1",
15
+ version: "0.27.0",
16
16
  description: `AI-agent CLI for finding and managing images, videos, testimonials, action items, and ad platform data in Baker.
17
17
 
18
18
  Auth: Set BAKER_API_KEY (starts with bk_) and BAKER_API_URL environment variables.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/ads/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,UAAU,qDAoBrB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/ads/index.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,UAAU,qDA2BrB,CAAC"}
@@ -1,25 +1,34 @@
1
1
  import { defineCommand } from "citty";
2
2
  import { googleCommand } from "./google/index.js";
3
+ import { linkedinCommand } from "./linkedin/index.js";
3
4
  import { metaCommand } from "./meta/index.js";
5
+ import { xCommand } from "./x/index.js";
4
6
  export const adsCommand = defineCommand({
5
7
  meta: {
6
8
  name: "ads",
7
9
  description: `Ad platform commands. Each platform exposes its own native command surface — no forced parity.
8
10
 
9
11
  Platforms:
10
- google — Google Ads (GAQL queries, keywords, accounts)
11
- meta — Meta Ads (campaigns, adsets, ads, insights, previews)
12
+ google — Google Ads (GAQL queries, keywords, accounts, ad library)
13
+ meta — Meta Ads (campaigns, adsets, ads, insights, previews)
14
+ linkedin — LinkedIn Ads (firmographic analytics — job-title × company × industry × seniority pivots)
15
+ x — X (Twitter) Ads (campaigns, line items, promoted tweets, analytics)
12
16
 
13
17
  Examples:
14
18
  baker ads google accounts
15
19
  baker ads google query --preset campaign-performance --customer-id 1234567890
16
20
  baker ads meta accounts
17
21
  baker ads meta campaigns --account-id act_123 --effective-status ACTIVE
18
- baker ads meta insights --object act_123 --level ad --date-preset last_7d --fields impressions,spend,ctr`,
22
+ baker ads meta insights --object act_123 --level ad --date-preset last_7d --fields impressions,spend,ctr
23
+ baker ads linkedin accounts
24
+ baker ads x accounts
25
+ baker ads x stats sync --preset campaign-engagement-7d --entity-ids abc,def`,
19
26
  },
20
27
  subCommands: {
21
28
  google: googleCommand,
22
29
  meta: metaCommand,
30
+ linkedin: linkedinCommand,
31
+ x: xCommand,
23
32
  },
24
33
  });
25
34
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/ads/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,MAAM,CAAC,MAAM,UAAU,GAAG,aAAa,CAAC;IACtC,IAAI,EAAE;QACJ,IAAI,EAAE,KAAK;QACX,WAAW,EAAE;;;;;;;;;;;2GAW0F;KACxG;IACD,WAAW,EAAE;QACX,MAAM,EAAE,aAAa;QACrB,IAAI,EAAE,WAAW;KAClB;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/ads/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,MAAM,CAAC,MAAM,UAAU,GAAG,aAAa,CAAC;IACtC,IAAI,EAAE;QACJ,IAAI,EAAE,KAAK;QACX,WAAW,EAAE;;;;;;;;;;;;;;;;8EAgB6D;KAC3E;IACD,WAAW,EAAE;QACX,MAAM,EAAE,aAAa;QACrB,IAAI,EAAE,WAAW;QACjB,QAAQ,EAAE,eAAe;QACzB,CAAC,EAAE,QAAQ;KACZ;CACF,CAAC,CAAC"}
@@ -0,0 +1,20 @@
1
+ export declare const accountCommand: import("citty").CommandDef<{
2
+ readonly "account-id": {
3
+ readonly type: "string";
4
+ readonly description: "Numeric account ID or urn:li:sponsoredAccount:N";
5
+ };
6
+ readonly "account-urn": {
7
+ readonly type: "string";
8
+ readonly description: "Alias for --account-id";
9
+ };
10
+ readonly "skip-cache": {
11
+ readonly type: "boolean";
12
+ readonly description: "Bypass server-side cache";
13
+ };
14
+ readonly output: {
15
+ readonly type: "string";
16
+ readonly description: "json|csv|jsonl|md";
17
+ readonly default: "json";
18
+ };
19
+ }>;
20
+ //# sourceMappingURL=account.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/account.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;EA+BzB,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { defineCommand } from "citty";
2
+ import { apiGet } from "../../../client.js";
3
+ import { writeAdsJson, writeAdsOutput } from "../output.js";
4
+ import { csvOrJson, handleLinkedinError, resolveAccountIdArg } from "./shared.js";
5
+ export const accountCommand = defineCommand({
6
+ meta: {
7
+ name: "account",
8
+ description: `Single LinkedIn ad account detail (currency, status, type).
9
+
10
+ Examples:
11
+ baker ads linkedin account --account-id 503001492
12
+ baker ads linkedin account --account-urn urn:li:sponsoredAccount:503001492 --output md`,
13
+ },
14
+ args: {
15
+ "account-id": { type: "string", description: "Numeric account ID or urn:li:sponsoredAccount:N" },
16
+ "account-urn": { type: "string", description: "Alias for --account-id" },
17
+ "skip-cache": { type: "boolean", description: "Bypass server-side cache" },
18
+ output: { type: "string", description: "json|csv|jsonl|md", default: "json" },
19
+ },
20
+ run: async ({ args }) => {
21
+ const accountId = resolveAccountIdArg(args);
22
+ try {
23
+ const params = { "account-id": accountId };
24
+ if (args["skip-cache"])
25
+ params["skip-cache"] = "true";
26
+ const data = await apiGet("/api/ads/linkedin/account", params);
27
+ const fmt = csvOrJson(args);
28
+ if (fmt !== "json") {
29
+ writeAdsOutput([data], fmt);
30
+ return;
31
+ }
32
+ writeAdsJson({ ok: true, data });
33
+ }
34
+ catch (err) {
35
+ handleLinkedinError(err);
36
+ }
37
+ },
38
+ });
39
+ //# sourceMappingURL=account.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account.js","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/account.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAYlF,MAAM,CAAC,MAAM,cAAc,GAAG,aAAa,CAAC;IAC1C,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,WAAW,EAAE;;;;yFAIwE;KACtF;IACD,IAAI,EAAE;QACJ,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,iDAAiD,EAAE;QAChG,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,wBAAwB,EAAE;QACxE,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,0BAA0B,EAAE;QAC1E,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,EAAE;KAC9E;IACD,GAAG,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACtB,MAAM,SAAS,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC;YACH,MAAM,MAAM,GAA2B,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;YACnE,IAAI,IAAI,CAAC,YAAY,CAAC;gBAAE,MAAM,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC;YACtD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAsB,2BAA2B,EAAE,MAAM,CAAC,CAAC;YACpF,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;YAC5B,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;gBACnB,cAAc,CAAC,CAAC,IAAI,CAA8C,EAAE,GAAG,CAAC,CAAC;gBACzE,OAAO;YACT,CAAC;YACD,YAAY,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACnC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,mBAAmB,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,20 @@
1
+ export declare const accountsCommand: import("citty").CommandDef<{
2
+ readonly "include-all": {
3
+ readonly type: "boolean";
4
+ readonly description: "List all accessible accounts, ignoring picker scope";
5
+ };
6
+ readonly "no-cache": {
7
+ readonly type: "boolean";
8
+ readonly description: "Skip CLI-side cache";
9
+ };
10
+ readonly "skip-cache": {
11
+ readonly type: "boolean";
12
+ readonly description: "Bypass server-side cache (force re-fetch)";
13
+ };
14
+ readonly output: {
15
+ readonly type: "string";
16
+ readonly description: "Output format: json|csv|jsonl|md";
17
+ readonly default: "json";
18
+ };
19
+ }>;
20
+ //# sourceMappingURL=accounts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounts.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/accounts.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;EA6C1B,CAAC"}
@@ -0,0 +1,56 @@
1
+ import { defineCommand } from "citty";
2
+ import { apiGet } from "../../../client.js";
3
+ import { cacheGet, cacheSet } from "../cache.js";
4
+ import { writeAdsJson, writeAdsOutput } from "../output.js";
5
+ import { csvOrJson, handleLinkedinError } from "./shared.js";
6
+ const ACCOUNTS_TTL_MS = 60 * 60 * 1000;
7
+ export const accountsCommand = defineCommand({
8
+ meta: {
9
+ name: "accounts",
10
+ description: `List LinkedIn ad accounts in this company's connected scope.
11
+
12
+ Examples:
13
+ baker ads linkedin accounts
14
+ baker ads linkedin accounts --include-all # ignore picker scope, list every account the token can see
15
+ baker ads linkedin accounts --output csv`,
16
+ },
17
+ args: {
18
+ "include-all": { type: "boolean", description: "List all accessible accounts, ignoring picker scope" },
19
+ "no-cache": { type: "boolean", description: "Skip CLI-side cache" },
20
+ "skip-cache": { type: "boolean", description: "Bypass server-side cache (force re-fetch)" },
21
+ output: { type: "string", description: "Output format: json|csv|jsonl|md", default: "json" },
22
+ },
23
+ run: async ({ args }) => {
24
+ const includeAll = args["include-all"] === true;
25
+ const useCache = !args["no-cache"];
26
+ const cacheKey = `accounts:${includeAll ? "all" : "scoped"}`;
27
+ if (useCache) {
28
+ const cached = cacheGet("linkedin-accounts", cacheKey);
29
+ if (cached) {
30
+ writeAdsJson({ ok: true, data: cached.data, cached: true });
31
+ return;
32
+ }
33
+ }
34
+ try {
35
+ const params = {};
36
+ if (includeAll)
37
+ params["include-all"] = "true";
38
+ if (args["skip-cache"])
39
+ params["skip-cache"] = "true";
40
+ const data = await apiGet("/api/ads/linkedin/accounts", params);
41
+ if (useCache) {
42
+ cacheSet("linkedin-accounts", cacheKey, data, ACCOUNTS_TTL_MS);
43
+ }
44
+ const fmt = csvOrJson(args);
45
+ if (fmt !== "json") {
46
+ writeAdsOutput(data, fmt);
47
+ return;
48
+ }
49
+ writeAdsJson({ ok: true, data });
50
+ }
51
+ catch (err) {
52
+ handleLinkedinError(err);
53
+ }
54
+ },
55
+ });
56
+ //# sourceMappingURL=accounts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/accounts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAY7D,MAAM,eAAe,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEvC,MAAM,CAAC,MAAM,eAAe,GAAG,aAAa,CAAC;IAC3C,IAAI,EAAE;QACJ,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE;;;;;2CAK0B;KACxC;IACD,IAAI,EAAE;QACJ,aAAa,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,qDAAqD,EAAE;QACtG,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,qBAAqB,EAAE;QACnE,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,2CAA2C,EAAE;QAC3F,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,kCAAkC,EAAE,OAAO,EAAE,MAAM,EAAE;KAC7F;IACD,GAAG,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACtB,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,IAAI,CAAC;QAChD,MAAM,QAAQ,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACnC,MAAM,QAAQ,GAAG,YAAY,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC7D,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,MAAM,GAAG,QAAQ,CAAoB,mBAAmB,EAAE,QAAQ,CAAC,CAAC;YAC1E,IAAI,MAAM,EAAE,CAAC;gBACX,YAAY,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC5D,OAAO;YACT,CAAC;QACH,CAAC;QACD,IAAI,CAAC;YACH,MAAM,MAAM,GAA2B,EAAE,CAAC;YAC1C,IAAI,UAAU;gBAAE,MAAM,CAAC,aAAa,CAAC,GAAG,MAAM,CAAC;YAC/C,IAAI,IAAI,CAAC,YAAY,CAAC;gBAAE,MAAM,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC;YACtD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAoB,4BAA4B,EAAE,MAAM,CAAC,CAAC;YACnF,IAAI,QAAQ,EAAE,CAAC;gBACb,QAAQ,CAAC,mBAAmB,EAAE,QAAQ,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC;YACjE,CAAC;YACD,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;YAC5B,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;gBACnB,cAAc,CAAC,IAAiD,EAAE,GAAG,CAAC,CAAC;gBACvE,OAAO;YACT,CAAC;YACD,YAAY,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACnC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,mBAAmB,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,84 @@
1
+ export declare const analyticsCommand: import("citty").CommandDef<{
2
+ readonly level: {
3
+ readonly type: "string";
4
+ readonly description: "Object scope (default: account)";
5
+ };
6
+ readonly "account-id": {
7
+ readonly type: "string";
8
+ readonly description: "Account ID — numeric or urn:li:sponsoredAccount:N (level=account)";
9
+ };
10
+ readonly "account-urn": {
11
+ readonly type: "string";
12
+ readonly description: "Alias for --account-id (URN form)";
13
+ };
14
+ readonly "campaign-group-id": {
15
+ readonly type: "string";
16
+ readonly description: "Comma-separated IDs (level=campaign-group)";
17
+ };
18
+ readonly "campaign-id": {
19
+ readonly type: "string";
20
+ readonly description: "Comma-separated IDs (level=campaign)";
21
+ };
22
+ readonly "creative-id": {
23
+ readonly type: "string";
24
+ readonly description: "Comma-separated IDs (level=creative)";
25
+ };
26
+ readonly ids: {
27
+ readonly type: "string";
28
+ readonly description: "Generic CSV of IDs at the chosen level (alternative to per-level flags)";
29
+ };
30
+ readonly intent: {
31
+ readonly type: "string";
32
+ readonly description: "baseline|revenue|funnel|engagement|video|lead-gen|inmail|document|ranking|identity";
33
+ };
34
+ readonly pivot: {
35
+ readonly type: "string";
36
+ readonly description: "Pivot dim. Default: none. See `--list-pivots`.";
37
+ };
38
+ readonly metrics: {
39
+ readonly type: "string";
40
+ readonly description: "CSV metric override — bypass --intent's bundle";
41
+ };
42
+ readonly "list-intents": {
43
+ readonly type: "boolean";
44
+ readonly description: "Print intent definitions and exit";
45
+ };
46
+ readonly "list-pivots": {
47
+ readonly type: "boolean";
48
+ readonly description: "Print pivot slugs and exit";
49
+ };
50
+ readonly start: {
51
+ readonly type: "string";
52
+ readonly description: "Start date YYYY-MM-DD (overrides --last-days)";
53
+ };
54
+ readonly end: {
55
+ readonly type: "string";
56
+ readonly description: "End date YYYY-MM-DD";
57
+ };
58
+ readonly "last-days": {
59
+ readonly type: "string";
60
+ readonly description: "Window relative to today (default: 7)";
61
+ };
62
+ readonly granularity: {
63
+ readonly type: "string";
64
+ readonly description: "DAILY|MONTHLY|YEARLY|ALL (default: DAILY)";
65
+ };
66
+ readonly limit: {
67
+ readonly type: "string";
68
+ readonly description: "Max rows (default: 1000)";
69
+ };
70
+ readonly "no-sort": {
71
+ readonly type: "boolean";
72
+ readonly description: "Skip default sort by costInUsd desc";
73
+ };
74
+ readonly "skip-cache": {
75
+ readonly type: "boolean";
76
+ readonly description: "Bypass server-side cache";
77
+ };
78
+ readonly output: {
79
+ readonly type: "string";
80
+ readonly description: "Output format json|csv|jsonl|md";
81
+ readonly default: "json";
82
+ };
83
+ }>;
84
+ //# sourceMappingURL=analytics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/analytics.ts"],"names":[],"mappings":"AAgKA,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmI3B,CAAC"}