@adkit/cli 1.13.4 → 1.13.6

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 (2) hide show
  1. package/dist/cli.js +87 -25
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1253,7 +1253,7 @@ async function listMetaCampaigns(client, _args, flags) {
1253
1253
  }
1254
1254
  var CAMPAIGN_FLAGS = ["name", "objective", "status", "budget-daily", "budget-total", "abo", "bid-strategy"];
1255
1255
  var ADSET_FLAGS = ["campaign", "name", "status", "optimization", "budget-daily", "budget-total", "bid-strategy", "bid-amount", "countries", "genders", "targeting", "pixel", "event-type", "interest"];
1256
- var AD_FLAGS = ["creative", "adset", "name", "status", "media", "media-format", "primary-text", "headline", "description", "cta", "url", "lead-form", "page", "post"];
1256
+ var AD_FLAGS = ["creative", "adset", "name", "status", "media", "media-format", "primary-text", "headline", "link-description", "cta", "url", "lead-form", "page", "post"];
1257
1257
  var CREATIVE_FLAGS = ["page-id", "headline", "primary-text", "link-description", "link-url", "cta", "name", "image-hash", "image-url", "video-id", "force"];
1258
1258
  var MEDIA_FLAGS = ["file", "url", "account"];
1259
1259
  var ADSET_BID_STRATEGIES_REQUIRING_AMOUNT = /* @__PURE__ */ new Set(["cost_cap", "bid_cap"]);
@@ -1430,10 +1430,10 @@ async function createAd(client, _args, flags) {
1430
1430
  }
1431
1431
  validateFlags(flags, AD_FLAGS, "manage meta ads create");
1432
1432
  const creativeId = typeof flags.creative === "string" ? flags.creative : void 0;
1433
- const mediaFlags = ["media", "media-format", "primary-text", "headline", "description", "cta", "lead-form"];
1433
+ const mediaFlags = ["media", "media-format", "primary-text", "headline", "link-description", "cta", "lead-form"];
1434
1434
  const hasMediaFlags = mediaFlags.some((f) => flags[f] !== void 0);
1435
1435
  const postId = typeof flags.post === "string" ? flags.post : void 0;
1436
- if (creativeId && hasMediaFlags) throw new CliError("INVALID_VALUE", "`--creative` cannot be combined with --media, --primary-text, --headline, --description, or --cta", "Use --creative to reference an existing creative, OR use --media/--primary-text/etc. to create a new one");
1436
+ if (creativeId && hasMediaFlags) throw new CliError("INVALID_VALUE", "`--creative` cannot be combined with --media, --primary-text, --headline, --link-description, or --cta", "Use --creative to reference an existing creative, OR use --media/--primary-text/etc. to create a new one");
1437
1437
  if (postId && (creativeId || hasMediaFlags)) throw new CliError("INVALID_VALUE", "`--post` cannot be combined with --creative, --media, or text flags", "Use --post alone to create an ad from an existing post");
1438
1438
  if (flags["lead-form"] !== void 0 && (typeof flags["lead-form"] !== "string" || !flags["lead-form"].trim())) throw new CliError("MISSING_FLAG", "Lead Form ads require an Instant Form ID. Pass --lead-form <id>.", "Copy the form ID from Meta Instant Forms and pass it as --lead-form <id>.");
1439
1439
  if (postId) {
@@ -1466,7 +1466,7 @@ async function createAd(client, _args, flags) {
1466
1466
  const mediaFormatInputs = collectFlagValues(flags, "media-format");
1467
1467
  const primaryTexts = collectFlagValues(flags, "primary-text");
1468
1468
  const headlines = collectFlagValues(flags, "headline");
1469
- const descriptions = collectFlagValues(flags, "description");
1469
+ const linkDescriptions = collectFlagValues(flags, "link-description");
1470
1470
  if (mediaInputs.length === 0) throw new CliError("MISSING_FLAG", "Missing required flag: `--media` (or use `--creative` to reference an existing creative)", 'Run: adkit manage meta ads create --media ./hero.mp4 --primary-text "Hello" --adset as_xyz --publish');
1471
1471
  if (mediaFormatInputs.length > 0 && mediaFormatInputs.length !== mediaInputs.length) throw new CliError("INVALID_VALUE", "Pass one --media-format value for each --media value.", "Use --media-format default for the base media, then square, vertical, or horizontal for replacements.");
1472
1472
  const mediaFormats = mediaFormatInputs.map(readMediaFormatFlag);
@@ -1509,7 +1509,7 @@ async function createAd(client, _args, flags) {
1509
1509
  const ad = { adsetId, media };
1510
1510
  if (primaryTexts.length) ad.primaryTexts = primaryTexts;
1511
1511
  if (headlines.length) ad.headlines = headlines;
1512
- if (descriptions.length) ad.descriptions = descriptions;
1512
+ if (linkDescriptions.length) ad.linkDescriptions = linkDescriptions;
1513
1513
  if (typeof flags.cta === "string") ad.cta = flags.cta;
1514
1514
  if (typeof flags.name === "string") ad.name = flags.name;
1515
1515
  if (typeof flags.url === "string") ad.url = flags.url;
@@ -1852,6 +1852,7 @@ var CONVERSION_UPLOAD_FLAGS = ["conversion", "gclid", "gbraid", "wbraid", "occur
1852
1852
  var CHANGE_HISTORY_FLAGS = ["account", "period", "from", "to", "limit", "resource", "campaign", "ad-group"];
1853
1853
  var KEYWORD_RESEARCH_FLAGS = ["url", "location", "language", "limit", "min-volume"];
1854
1854
  var TARGETING_RESEARCH_FLAGS = ["limit"];
1855
+ var GEO_LOCATION_SEARCH_FLAGS = ["country-code", "locale", "type", "limit"];
1855
1856
  var NUMBER_FLAG_PATTERN = /^(?:\d+|\d+\.\d+|\.\d+)(?:e[+-]?\d+)?$/iu;
1856
1857
  function generateCampaignName2() {
1857
1858
  return `campaign ${getDateStamp()}`;
@@ -2815,6 +2816,18 @@ async function researchInterests(client, args, flags) {
2815
2816
  const path3 = `/manage/google/research/interests${qs}`;
2816
2817
  return client.get(path3);
2817
2818
  }
2819
+ async function searchGoogleGeoLocations(client, args, flags) {
2820
+ validateFlags(flags, GEO_LOCATION_SEARCH_FLAGS, "manage google geo-locations search");
2821
+ const query = args.join(" ").trim();
2822
+ if (!query) throw new CliError("MISSING_ARGUMENT", "Missing location query", 'Run: adkit manage google geo-locations search "Austin, Texas" --country-code US --type city');
2823
+ const accountId = typeof flags.account === "string" ? flags.account : void 0;
2824
+ const countryCode = typeof flags["country-code"] === "string" ? flags["country-code"] : void 0;
2825
+ const locale = typeof flags.locale === "string" ? flags.locale : void 0;
2826
+ const type = typeof flags.type === "string" ? flags.type : void 0;
2827
+ const limit = typeof flags.limit === "string" ? flags.limit : void 0;
2828
+ const qs = queryString({ accountId, query, countryCode, locale, type, limit });
2829
+ return client.get(`/manage/google/geo-locations${qs}`);
2830
+ }
2818
2831
  function buildGoogleTargetingResearchQuery(args, flags, missingQueryHint) {
2819
2832
  const q = args.join(" ").trim();
2820
2833
  if (!q) throw new CliError("MISSING_ARGUMENT", "Missing query", missingQueryHint);
@@ -3211,10 +3224,10 @@ var CAMPAIGN_LIST_FLAGS3 = ["campaign-ids", "limit", "offset"];
3211
3224
  var AD_GROUP_LIST_FLAGS3 = ["campaign-ids", "ad-group-ids", "limit", "offset"];
3212
3225
  var AD_LIST_FLAGS3 = ["campaign-ids", "limit", "offset"];
3213
3226
  var CAMPAIGN_CREATE_FLAGS3 = ["name", "status", "budget-daily", "budget-lifetime", "end-date"];
3214
- var AD_GROUP_CREATE_FLAGS2 = ["campaign", "name", "objective", "status", "budget-daily", "budget-lifetime"];
3227
+ var AD_GROUP_CREATE_FLAGS2 = ["campaign", "name", "objective", "status", "budget-daily", "budget-lifetime", "bid-strategy", "optimization", "bid-amount"];
3215
3228
  var AD_CREATE_FLAGS2 = ["ad-group", "existing-post", "name", "status"];
3216
3229
  var CAMPAIGN_UPDATE_FLAGS3 = ["name", "status", "budget-lifetime", "end-date"];
3217
- var AD_GROUP_UPDATE_FLAGS2 = ["name", "status", "budget-daily", "budget-lifetime", "start-date", "end-date"];
3230
+ var AD_GROUP_UPDATE_FLAGS2 = ["name", "status", "budget-daily", "budget-lifetime", "start-date", "end-date", "bid-strategy", "optimization", "bid-amount"];
3218
3231
  var AD_UPDATE_FLAGS2 = ["name", "status"];
3219
3232
  var TARGETING_SEARCH_FLAGS = ["account", "facet", "query", "limit"];
3220
3233
  var RESULTS_FLAGS2 = ["level", "period", "fields", "breakdowns", "from", "to", "sort", "limit", "offset"];
@@ -3386,6 +3399,10 @@ function buildLinkedInAdGroupPayload(flags) {
3386
3399
  const budget = buildLinkedInBudget(flags);
3387
3400
  if (budget) payload.budget = budget;
3388
3401
  if (typeof flags.objective === "string") payload.objective = flags.objective;
3402
+ if (typeof flags["bid-strategy"] === "string") payload.bidStrategy = flags["bid-strategy"];
3403
+ if (typeof flags.optimization === "string") payload.optimization = flags.optimization;
3404
+ const bidAmount = readNumberFlag3(flags, "bid-amount");
3405
+ if (bidAmount !== void 0) payload.bidAmount = bidAmount;
3389
3406
  if (typeof flags.status === "string") payload.status = flags.status;
3390
3407
  return payload;
3391
3408
  }
@@ -3415,6 +3432,10 @@ function buildLinkedInAdGroupUpdatePayload(flags) {
3415
3432
  if (typeof flags["end-date"] === "string") payload.endDate = flags["end-date"];
3416
3433
  const budget = buildLinkedInBudget(flags);
3417
3434
  if (budget) payload.budget = budget;
3435
+ if (typeof flags["bid-strategy"] === "string") payload.bidStrategy = flags["bid-strategy"];
3436
+ if (typeof flags.optimization === "string") payload.optimization = flags.optimization;
3437
+ const bidAmount = readNumberFlag3(flags, "bid-amount");
3438
+ if (bidAmount !== void 0) payload.bidAmount = bidAmount;
3418
3439
  return payload;
3419
3440
  }
3420
3441
  function buildLinkedInAdUpdatePayload(flags) {
@@ -4737,7 +4758,7 @@ Flags (create \u2014 new creative from media):
4737
4758
  --media <path|url> Media file or URL (repeatable). Handles upload + processing automatically for single-image/video ads
4738
4759
  --primary-text <t> Ad body text (repeatable)
4739
4760
  --headline <text> Headline text (repeatable)
4740
- --description <t> Description text (repeatable, optional)
4761
+ --link-description <t> Link preview subtitle (repeatable, optional)
4741
4762
  ${FLAG.cta}
4742
4763
  --name <name> Ad name
4743
4764
  --url <url> Landing page URL
@@ -4799,7 +4820,7 @@ Flags (create \u2014 new creative from media):
4799
4820
  --media-format <f> Optional format for each --media: default, square, vertical, horizontal
4800
4821
  --primary-text <t> Ad body text (repeatable)
4801
4822
  --headline <text> Headline text (repeatable)
4802
- --description <t> Description text (repeatable, optional)
4823
+ --link-description <t> Link preview subtitle (repeatable, optional)
4803
4824
  ${FLAG.cta}
4804
4825
  --name <name> Ad name
4805
4826
  --url <url> Landing page URL
@@ -4835,6 +4856,7 @@ Note:
4835
4856
  media [{ id: "imageHashOrVideoId" }, { id: "...", format: "vertical" }]
4836
4857
  Normal media works on all placements. Add format only to replace a specific placement group with different media: square (Feed), vertical (Stories/Reels), horizontal (Right Column).
4837
4858
  Do not use top-level media for carousel ads.
4859
+ linkDescriptions Link-preview subtitle copy for feed ads.
4838
4860
  carouselCards
4839
4861
  Card list for carousel ads. Each card needs headline and media.
4840
4862
  carouselCards[].headline
@@ -5136,7 +5158,8 @@ Flags (list):
5136
5158
 
5137
5159
  Note:
5138
5160
  list returns campaign configuration only (name, status, budget).
5139
- Geo targeting lives in targeting.geoLocations. Use type=country for country targeting.
5161
+ Geo targeting lives in targeting.geoLocations. Use type=country for countries.
5162
+ For cities, regions, postal codes, and metros, run google geo-locations search and use the returned bare code.
5140
5163
  Advanced geo examples: adkit manage google campaigns --help full
5141
5164
  Display creation starts here: campaigns create --campaign-type display, then create Display ad groups and responsive_display ads.
5142
5165
  Performance Max: campaignType "performance_max" with asset groups \u2014 build it with --data. See --help full.
@@ -5187,9 +5210,9 @@ Flags (list):
5187
5210
  Notes:
5188
5211
  Use google assets for explicit account/campaign/ad-group attachment, reusable libraries, or richer sitelinks via --data.
5189
5212
  list returns campaign configuration only (name, status, budget).
5190
- Geo targeting lives in targeting.geoLocations. Use type=country for simple countries, or type=platformLocation for raw Google location/proximity geo.
5191
- With platformLocation, pass the exact Google geo object to send; AdKit does not look up or convert city names or addresses.
5192
- Google excludes support location geo target constants, not proximity radius exclusions.
5213
+ Geo targeting lives in targeting.geoLocations. Use type=country for countries and copy named locations from google geo-locations search.
5214
+ platformLocation accepts exact Google geo objects for advanced criteria and existing raw values.
5215
+ Google exclusions support named locations. Proximity circles belong in include.
5193
5216
  Display campaign creation uses campaignType:"display" or --campaign-type display. Videos are not supported for Display media in v1.
5194
5217
  Performance Max: campaignType "performance_max" with exactly one assetGroups[] entry. Text assets inline ({role,text}); image assets are uploaded via google media first, then referenced by id/resourceName. Needs an enabled conversion (publish is blocked without one). Non-retail only. Build it with --data.
5195
5218
  View a Performance Max campaign by ID to read back assetGroups from Google. There is no separate google asset-groups command.
@@ -5199,7 +5222,7 @@ Examples:
5199
5222
  adkit manage google campaigns list --account 1234567890
5200
5223
  adkit manage google campaigns create --name "Display Prospecting" --campaign-type display --budget-daily 50 --account 1234567890
5201
5224
  adkit manage google campaigns create --data '{"campaigns":[{"name":"Display US","campaignType":"display","budget":{"daily":50},"targeting":{"geoLocations":{"include":[{"type":"country","country":"US"}]}}}]}' --account 1234567890
5202
- adkit manage google campaigns create --data '{"campaigns":[{"name":"Search US Native","campaignType":"search","budget":{"daily":5},"targeting":{"geoLocations":{"include":[{"type":"platformLocation","platform":"google","value":{"location":{"geo_target_constant":"geoTargetConstants/2840"}}}]}}}]}' --account 1234567890
5225
+ adkit manage google campaigns create --data '{"campaigns":[{"name":"Austin Search","campaignType":"search","budget":{"daily":5},"targeting":{"geoLocations":{"include":[{"type":"city","code":"1026201"}]}}}]}' --account 1234567890
5203
5226
  adkit manage google campaigns create --data '{"campaigns":[{"name":"SF Radius","campaignType":"search","budget":{"daily":5},"targeting":{"geoLocations":{"include":[{"type":"platformLocation","platform":"google","value":{"proximity":{"geo_point":{"latitude_in_micro_degrees":37774900,"longitude_in_micro_degrees":-122419400},"radius":5,"radius_units":"MILES"}}}]}}}]}' --account 1234567890
5204
5227
  adkit manage google campaigns create --data '{"campaigns":[{"name":"PMAX Launch","campaignType":"performance_max","budget":{"daily":50},"bidStrategy":"maximize_conversions","assetGroups":[{"name":"Asset Group 1","finalUrls":["https://example.com"],"assets":[{"role":"headline","text":"Ship faster"},{"role":"headline","text":"Save hours every week"},{"role":"headline","text":"Start free today"},{"role":"long_headline","text":"Automate your ad workflow and launch in minutes"},{"role":"description","text":"Automate your workflow in minutes"},{"role":"description","text":"No credit card required"},{"role":"business_name","text":"Acme"},{"role":"logo","id":"<uploaded-asset-id>"},{"role":"marketing_image","id":"<uploaded-asset-id>"},{"role":"square_marketing_image","id":"<uploaded-asset-id>"}],"signals":[{"type":"search_theme","text":"project management software"}]}]}]}' --account 1234567890
5205
5228
  adkit manage google campaigns update 987654321 --status paused --account 1234567890
@@ -5723,6 +5746,24 @@ Notes:
5723
5746
  Topic search returns IDs for targeting.content.topics.include/exclude.
5724
5747
  Interest search returns IDs for targeting.audience.interests.include/exclude.
5725
5748
  Websites use direct URLs in targeting.content.websites.include/exclude. Bare numeric customAudiences IDs are treated as Google user lists (remarketing/Customer Match); for custom segments, pass a customAudiences/{id} or customers/{accountId}/customAudiences/{id} resource path.`;
5749
+ var GOOGLE_GEO_LOCATIONS_HELP = `adkit manage google geo-locations \u2014 Find locations for campaign targeting
5750
+
5751
+ search <place...> Find enabled Google locations and return bare codes and AdKit types
5752
+
5753
+ Flags:
5754
+ --account <id> Optional if one Google Ads account is connected
5755
+ --country-code <c> ISO country code such as US
5756
+ --locale <locale> Google result locale such as en
5757
+ --type <type> country, region, city, postal_code, or metro
5758
+ --limit <n> Max results (1-50, default: 10)
5759
+
5760
+ Examples:
5761
+ adkit manage google geo-locations search "Austin, Texas" --country-code US --type city
5762
+ adkit manage google geo-locations search Paris --country-code FR --locale fr
5763
+
5764
+ Supported results contain type; unmapped Google categories contain platformType.
5765
+ Pass type and the bare code into campaign targeting. For country, use {"type":"country","country":"US"}.
5766
+ AdKit builds the Google resource name.`;
5726
5767
  var GOOGLE_MEDIA_HELP = `adkit manage google media \u2014 Upload creative media to Google Ads
5727
5768
 
5728
5769
  upload Upload an image and return reusable Google asset IDs
@@ -5833,6 +5874,7 @@ Examples:
5833
5874
  "google results keywords": GOOGLE_RESULTS_KEYWORDS_HELP,
5834
5875
  "google change-history": GOOGLE_CHANGE_HISTORY_HELP,
5835
5876
  "google research": GOOGLE_RESEARCH_HELP,
5877
+ "google geo-locations": GOOGLE_GEO_LOCATIONS_HELP,
5836
5878
  "google media": GOOGLE_MEDIA_HELP,
5837
5879
  "google conversions": GOOGLE_CONVERSIONS_HELP,
5838
5880
  "tiktok accounts": `adkit manage tiktok accounts \u2014 TikTok Ads accounts
@@ -6020,7 +6062,7 @@ Examples:
6020
6062
 
6021
6063
  list List ad groups
6022
6064
  create Create an ad group draft
6023
- update <id> Update an ad group (rename, pause/resume, budget, dates, targeting)
6065
+ update <id> Update an ad group (rename, status, budget, bidding, dates, targeting)
6024
6066
  <id> View ad group details
6025
6067
 
6026
6068
  Naming note:
@@ -6037,10 +6079,13 @@ ${FLAG.data}
6037
6079
  --ad-group-ids <ids> Comma-separated ad group IDs for filtered reads
6038
6080
  --campaign <id> Parent campaign ID (create only)
6039
6081
  --name <name> Ad group name
6040
- --objective <type> brand_awareness, engagement, website_visits, video_views, website_conversions
6082
+ --objective <type> brand_awareness, engagement, website_visits, video_views, website_conversions, lead_generation
6041
6083
  --status <s> create: active or draft (defaults to draft). update: active, paused, or archived
6042
6084
  --budget-daily <n> Daily budget in account currency
6043
6085
  --budget-lifetime <n> Lifetime budget in account currency
6086
+ --bid-strategy <s> maximum_delivery, manual, target_cost, cost_cap
6087
+ --optimization <s> clicks, impressions, conversions, video_views, leads, qualified_leads, reach
6088
+ --bid-amount <n> Bid/target/cap in account currency; required except for maximum_delivery
6044
6089
  --start-date <date> YYYY-MM-DD (update only)
6045
6090
  --end-date <date> YYYY-MM-DD (update only)
6046
6091
 
@@ -6053,9 +6098,12 @@ Targeting (--data only):
6053
6098
 
6054
6099
  Notes:
6055
6100
  A live ad group can never go back to draft. targeting on update is a full replace (send the whole targeting object via --data).
6101
+ Explicit bidding on create requires a compatible --objective. Bidding changes are atomic: send --bid-strategy and --optimization together. manual, target_cost, and cost_cap also require --bid-amount; maximum_delivery rejects it.
6102
+ LinkedIn further constrains bidding by objective and creative format.
6056
6103
 
6057
6104
  Examples:
6058
6105
  adkit manage linkedin ad-groups create --campaign 628940516 --name "US Broad" --budget-daily 50 --objective website_visits
6106
+ adkit manage linkedin ad-groups create --campaign 628940516 --name "US Manual CPC" --objective website_visits --bid-strategy manual --optimization clicks --bid-amount 8
6059
6107
  adkit manage linkedin ad-groups update 361387516 --account 512345678 --status paused
6060
6108
  adkit manage linkedin ad-groups create --data '{"adGroups":[{"campaignId":"628940516","name":"US Senior Eng","budget":{"daily":50},"targeting":{"geoLocations":{"include":[{"type":"country","code":"103644278"}]},"audience":{"seniorities":{"include":["6"]},"customAudiences":{"include":["123456"]}}}}]}'`.trim(),
6061
6109
  "linkedin ads": `adkit manage linkedin ads \u2014 LinkedIn ads
@@ -6260,6 +6308,7 @@ Entity groups:
6260
6308
  results Performance metrics (campaigns, ad-groups, ads, keywords, search-terms)
6261
6309
  change-history Recent Google Ads account changes from the last 30 days
6262
6310
  research Keyword Planner plus Display topic/interest IDs
6311
+ geo-locations Find Google location codes for campaign targeting
6263
6312
  assets Manage reusable assets (sitelinks, callouts, structured snippets, images)
6264
6313
  media Upload images for Display creative media
6265
6314
 
@@ -6601,6 +6650,7 @@ var HELP_FULL = {
6601
6650
  "google results keywords": GOOGLE_RESULTS_KEYWORDS_HELP,
6602
6651
  "google change-history": GOOGLE_CHANGE_HISTORY_HELP,
6603
6652
  "google research": GOOGLE_RESEARCH_HELP,
6653
+ "google geo-locations": GOOGLE_GEO_LOCATIONS_HELP,
6604
6654
  "google media": GOOGLE_MEDIA_HELP,
6605
6655
  "google conversions": GOOGLE_CONVERSIONS_HELP,
6606
6656
  "meta research": META_RESEARCH_HELP,
@@ -6937,15 +6987,17 @@ function printSingleEntity(e, entity) {
6937
6987
  const creative = ad.creative;
6938
6988
  if (creative) {
6939
6989
  lines.push("", " Creative", "");
6940
- if (creative.id) lines.push(` ID: ${formatForTTY(creative.id)}`);
6941
- if (creative.headline) lines.push(` Headline: ${formatForTTY(creative.headline)}`);
6942
- if (creative.body) lines.push(` Body: ${formatForTTY(creative.body)}`);
6943
- if (creative.description) lines.push(` Description: ${formatForTTY(creative.description)}`);
6990
+ const headlines = creative.headlines?.join(", ");
6991
+ const primaryTexts = creative.primaryTexts?.join(", ");
6992
+ const linkDescriptions = creative.linkDescriptions?.join(", ");
6993
+ const mediaItems = creative.media?.map((item) => `${item.role}:${item.id}`) ?? [];
6994
+ const media = mediaItems.join(", ");
6995
+ if (headlines) lines.push(` Headline: ${formatForTTY(headlines)}`);
6996
+ if (primaryTexts) lines.push(` Body: ${formatForTTY(primaryTexts)}`);
6997
+ if (linkDescriptions) lines.push(` Description: ${formatForTTY(linkDescriptions)}`);
6944
6998
  if (creative.cta) lines.push(` CTA: ${formatForTTY(creative.cta)}`);
6945
- if (creative.linkUrl) lines.push(` Link: ${formatForTTY(creative.linkUrl)}`);
6946
- if (creative.imageUrl) lines.push(` Image: ${formatForTTY(creative.imageUrl)}`);
6947
- if (creative.thumbnailUrl) lines.push(` Thumbnail: ${formatForTTY(creative.thumbnailUrl)}`);
6948
- if (creative.videoId) lines.push(` Video ID: ${formatForTTY(creative.videoId)}`);
6999
+ if (creative.url) lines.push(` Link: ${formatForTTY(creative.url)}`);
7000
+ if (media) lines.push(` Media: ${formatForTTY(media)}`);
6949
7001
  }
6950
7002
  if (createdAt) lines.push(` Created: ${createdAt}`);
6951
7003
  }
@@ -7964,8 +8016,18 @@ async function main() {
7964
8016
  }
7965
8017
  break;
7966
8018
  }
8019
+ case "geo-locations": {
8020
+ if (!action) {
8021
+ showHelp("google geo-locations", flags.help === "full");
8022
+ return;
8023
+ }
8024
+ if (action !== "search") throw new CliError("UNKNOWN_COMMAND", `Unknown action: google geo-locations ${action}`, "Available: search");
8025
+ data = await searchGoogleGeoLocations(client, restArgs, flags);
8026
+ emptyHint = "No matching Google locations found. Try a broader place name or country filter.";
8027
+ break;
8028
+ }
7967
8029
  default:
7968
- throw new CliError("UNKNOWN_COMMAND", `Unknown entity: google ${entity}`, "Available: accounts, assets, media, campaigns, ad-groups, ads, keywords, conversions, results, change-history, research");
8030
+ throw new CliError("UNKNOWN_COMMAND", `Unknown entity: google ${entity}`, "Available: accounts, assets, media, campaigns, ad-groups, ads, keywords, conversions, results, change-history, research, geo-locations");
7969
8031
  }
7970
8032
  } else if (platform2 === "tiktok") {
7971
8033
  switch (entity) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adkit/cli",
3
- "version": "1.13.4",
3
+ "version": "1.13.6",
4
4
  "description": "The Ads CLI for AI agents — manage Meta & Google ad campaigns, browse the ad library, and generate creatives from your terminal.",
5
5
  "keywords": [
6
6
  "ads cli",