@adkit/cli 1.13.1 → 1.13.3

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 +51 -18
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1834,7 +1834,7 @@ var CAMPAIGN_CREATE_FLAGS = ["name", "status", "budget-daily", "campaign-type",
1834
1834
  var CAMPAIGN_UPDATE_FLAGS = ["name", "status", "budget-daily", "bid-strategy", "target-cpa", "target-roas", "search-partners", "display-network", "tracking-url-suffix", "countries"];
1835
1835
  var AD_GROUP_LIST_FLAGS = ["campaign", "status", "limit", "offset"];
1836
1836
  var AD_GROUP_FLAGS = ["campaign", "name", "status", "cpc-bid"];
1837
- var AD_LIST_FLAGS = ["ad-group", "status", "policy-status", "limit", "offset"];
1837
+ var AD_LIST_FLAGS = ["ad-group", "campaign", "status", "policy-status", "limit", "offset"];
1838
1838
  var AD_FLAGS2 = ["headline", "headline-1", "headline-2", "headline-3", "description", "description-1", "description-2", "callout", "sitelink", "final-url", "path", "ad-group", "status"];
1839
1839
  var KEYWORD_LIST_FLAGS = ["ad-group", "campaign", "status", "limit", "offset"];
1840
1840
  var KEYWORD_FLAGS = ["ad-group", "text", "match-type", "cpc-bid", "status"];
@@ -2484,8 +2484,9 @@ async function deleteAdGroup(client, args, flags) {
2484
2484
  }
2485
2485
  async function listGoogleAds(client, _args, flags) {
2486
2486
  validateFlags(flags, AD_LIST_FLAGS, "manage google ads list");
2487
- const adGroupId = typeof flags["ad-group"] === "string" ? flags["ad-group"] : void 0;
2488
- const qs = buildGoogleListQuery(flags, { adGroupId });
2487
+ const adGroupIds = typeof flags["ad-group"] === "string" ? flags["ad-group"] : void 0;
2488
+ const campaignIds = typeof flags.campaign === "string" ? flags.campaign : void 0;
2489
+ const qs = buildGoogleListQuery(flags, { adGroupIds, campaignIds });
2489
2490
  const path3 = `/manage/google/ads${qs}`;
2490
2491
  return client.get(path3);
2491
2492
  }
@@ -2782,17 +2783,25 @@ async function listGoogleKeywordResults(client, _args, flags) {
2782
2783
  }
2783
2784
  async function researchKeywords(client, _args, flags) {
2784
2785
  validateFlags(flags, KEYWORD_RESEARCH_FLAGS, "manage google research keywords");
2785
- const keywords = _args.join(",");
2786
- if (!keywords) throw new CliError("MISSING_ARGUMENT", "At least one seed keyword is required", 'Run: adkit manage google research keywords "saas ads" --account 1234567890');
2786
+ const keywords = _args.map((keyword) => keyword.trim());
2787
+ const hasBlankKeyword = keywords.some((keyword) => keyword.length === 0);
2788
+ if (keywords.length === 0) throw new CliError("MISSING_ARGUMENT", "At least one seed keyword is required", 'Run: adkit manage google research keywords "saas ads" --account 1234567890');
2789
+ if (hasBlankKeyword) throw new CliError("INVALID_VALUE", "Seed keywords must be non-empty", 'Run: adkit manage google research keywords "saas ads" --account 1234567890');
2787
2790
  const url = typeof flags.url === "string" ? flags.url : void 0;
2788
2791
  const location = typeof flags.location === "string" ? flags.location : void 0;
2789
2792
  const language = typeof flags.language === "string" ? flags.language : void 0;
2790
2793
  const limit = typeof flags.limit === "string" ? flags.limit : void 0;
2791
2794
  const minVolume = typeof flags["min-volume"] === "string" ? flags["min-volume"] : void 0;
2792
2795
  const accountId = typeof flags.account === "string" ? flags.account : void 0;
2793
- const qs = queryString({ accountId, keywords, url, location, language, limit, minVolume });
2794
- const path3 = `/manage/google/research/keywords${qs}`;
2795
- return client.get(path3);
2796
+ return client.post("/manage/google/research/keywords", {
2797
+ accountId,
2798
+ keywords,
2799
+ url,
2800
+ location,
2801
+ language,
2802
+ limit,
2803
+ minVolume
2804
+ });
2796
2805
  }
2797
2806
  async function researchTopics(client, args, flags) {
2798
2807
  validateFlags(flags, TARGETING_RESEARCH_FLAGS, "manage google research topics");
@@ -3504,6 +3513,7 @@ function isStatusResponse(value) {
3504
3513
  if (!value || typeof value !== "object") return false;
3505
3514
  if (!("connected" in value) || typeof value.connected !== "boolean") return false;
3506
3515
  if (!("project" in value) || !value.project || typeof value.project !== "object") return false;
3516
+ if (!("instructions" in value) || typeof value.instructions !== "string") return false;
3507
3517
  if (!("platforms" in value) || !value.platforms || typeof value.platforms !== "object") return false;
3508
3518
  return true;
3509
3519
  }
@@ -3531,6 +3541,8 @@ async function status(client, json) {
3531
3541
  lines.push("Google: connected");
3532
3542
  for (const a of platforms.google.accounts) lines.push(` ${a.id} \u2014 ${a.name} (${a.currency})`);
3533
3543
  } else lines.push("Google: not connected");
3544
+ lines.push("");
3545
+ lines.push(data.instructions);
3534
3546
  console.log(lines.join("\n"));
3535
3547
  }
3536
3548
 
@@ -4150,7 +4162,10 @@ function wrapToLines(value, max) {
4150
4162
  if (word.length > max) {
4151
4163
  if (current) lines.push(current);
4152
4164
  current = "";
4153
- for (let i = 0; i < word.length; i += max) lines.push(word.slice(i, i + max));
4165
+ for (let i = 0; i < word.length; i += max) {
4166
+ const chunk = word.slice(i, i + max);
4167
+ lines.push(chunk);
4168
+ }
4154
4169
  continue;
4155
4170
  }
4156
4171
  const next = current ? current + " " + word : word;
@@ -4164,14 +4179,19 @@ function wrapToLines(value, max) {
4164
4179
  if (current) lines.push(current);
4165
4180
  return lines.length > 0 ? lines : [""];
4166
4181
  }
4167
- function formatSearchTermCost(cost) {
4168
- return `$${cost.toFixed(2)}`;
4182
+ function formatSearchTermMoney(value) {
4183
+ return `$${value.toFixed(2)}`;
4169
4184
  }
4170
4185
  function formatSearchTermConversions(conversions) {
4171
4186
  if (conversions === 0) return "0";
4172
4187
  const fixed = conversions.toFixed(2);
4173
4188
  return fixed.replace(/\.?0+$/, "");
4174
4189
  }
4190
+ function formatSearchTermRoas(roas) {
4191
+ if (roas === 0) return "0x";
4192
+ const fixed = roas.toFixed(2);
4193
+ return fixed.replace(/\.?0+$/, "") + "x";
4194
+ }
4175
4195
  function hasSearchTermStringFields(value) {
4176
4196
  if (value === null || typeof value !== "object") return false;
4177
4197
  if (!("searchTerm" in value) || typeof value.searchTerm !== "string") return false;
@@ -4182,7 +4202,11 @@ function hasSearchTermStringFields(value) {
4182
4202
  function hasSearchTermNumberFields(value) {
4183
4203
  if (value === null || typeof value !== "object") return false;
4184
4204
  if (!("impressions" in value) || typeof value.impressions !== "number") return false;
4205
+ if (!("clicks" in value) || typeof value.clicks !== "number") return false;
4185
4206
  if (!("cost" in value) || typeof value.cost !== "number") return false;
4207
+ if (!("conversions" in value) || typeof value.conversions !== "number") return false;
4208
+ if (!("conversionsValue" in value) || typeof value.conversionsValue !== "number") return false;
4209
+ if (!("roas" in value) || typeof value.roas !== "number") return false;
4186
4210
  return true;
4187
4211
  }
4188
4212
  function isGoogleSearchTermRecord(value) {
@@ -4205,8 +4229,10 @@ function toGoogleSearchTermDisplayRows(row) {
4205
4229
  MATCH: isFirst ? row.matchType : "",
4206
4230
  IMPR: isFirst ? row.impressions : "",
4207
4231
  CLICKS: isFirst ? row.clicks : "",
4208
- COST: isFirst ? formatSearchTermCost(row.cost) : "",
4209
- CONV: isFirst ? formatSearchTermConversions(row.conversions) : ""
4232
+ COST: isFirst ? formatSearchTermMoney(row.cost) : "",
4233
+ CONV: isFirst ? formatSearchTermConversions(row.conversions) : "",
4234
+ VALUE: isFirst ? formatSearchTermMoney(row.conversionsValue) : "",
4235
+ ROAS: isFirst ? formatSearchTermRoas(row.roas) : ""
4210
4236
  });
4211
4237
  }
4212
4238
  return result;
@@ -5242,7 +5268,7 @@ Notes:
5242
5268
  Display targeting is passed with --data on create or update:
5243
5269
  targeting.audience.interests, targeting.audience.customAudiences, targeting.content.topics, targeting.content.websites.
5244
5270
  Topic and interest search return IDs used in targeting: research topics -> targeting.content.topics; research interests -> targeting.audience.interests.
5245
- Websites use direct URLs in targeting.content.websites.include/exclude. Custom audiences are Google user list IDs.
5271
+ 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.
5246
5272
  WARNING: On update, targeting replaces the full Display targeting set. If you send only one new website, old websites/topics/audiences can be removed.
5247
5273
  list returns ad group configuration only (name, status, bid).
5248
5274
  For spend/clicks/conversions: adkit manage google results
@@ -5274,7 +5300,8 @@ Flags (create/update):
5274
5300
  --status <s> enabled, paused, removed
5275
5301
 
5276
5302
  Flags (list):
5277
- --ad-group <id> Filter by Google ad group ID (platformId)
5303
+ --campaign <ids> Filter by Google campaign IDs (platformId, comma-separated)
5304
+ --ad-group <ids> Filter by Google ad group IDs (platformId, comma-separated)
5278
5305
  --status <s> Filter by ad status (default: enabled,paused; use all for every status)
5279
5306
  --policy-status <s> Filter by policy status: approved, limited, disapproved, problem, pending_review, under_appeal
5280
5307
  --limit <n> Max results
@@ -5293,6 +5320,7 @@ Notes:
5293
5320
  That means sibling ads in the same ad group may also use those assets. Use google assets if you need explicit scope control or richer sitelink payloads.
5294
5321
 
5295
5322
  Examples:
5323
+ adkit manage google ads list --account 1234567890 --campaign 987654321,987654322
5296
5324
  adkit manage google ads list --account 1234567890 --ad-group 555666777
5297
5325
  adkit manage google ads create --data '{"ads":[{"type":"responsive_display","adGroupId":"555666777","headlines":[{"text":"Display headline"}],"longHeadline":{"text":"A longer Display headline"},"descriptions":[{"text":"Display description"}],"businessName":"AdKit","finalUrls":["https://example.com"],"media":[{"role":"marketing_image","id":"customers/123/assets/111"},{"role":"square_marketing_image","id":"customers/123/assets/222"},{"role":"logo","id":"customers/123/assets/333"},{"role":"square_logo","id":"customers/123/assets/444"}]}]}' --account 1234567890
5298
5326
  adkit manage google ads create --ad-group 555666777 \\
@@ -5328,7 +5356,8 @@ ${FLAG.publish}
5328
5356
  ${FLAG.data}
5329
5357
 
5330
5358
  Flags (list):
5331
- --ad-group <id> Filter by Google ad group ID (platformId)
5359
+ --campaign <ids> Filter by Google campaign IDs (platformId, comma-separated)
5360
+ --ad-group <ids> Filter by Google ad group IDs (platformId, comma-separated)
5332
5361
  --status <s> Filter by ad status (default: enabled,paused; use all for every status)
5333
5362
  --policy-status <s> Filter by policy status: approved, limited, disapproved, problem, pending_review, under_appeal
5334
5363
  --limit <n> Max results
@@ -5349,6 +5378,7 @@ Notes:
5349
5378
  Path format: pass one string like "crm" or "crm/demo". AdKit normalizes extra slashes, and each segment must stay within Google's 15-character limit.
5350
5379
 
5351
5380
  Examples:
5381
+ adkit manage google ads list --account 1234567890 --campaign 987654321,987654322
5352
5382
  adkit manage google ads list --account 1234567890 --ad-group 555666777
5353
5383
  adkit manage google ads create --data '{"ads":[{"type":"responsive_display","adGroupId":"555666777","headlines":[{"text":"Display headline"}],"longHeadline":{"text":"A longer Display headline"},"descriptions":[{"text":"Display description"}],"businessName":"AdKit","finalUrls":["https://example.com"],"media":[{"role":"marketing_image","id":"customers/123/assets/111"},{"role":"square_marketing_image","id":"customers/123/assets/222"},{"role":"logo","id":"customers/123/assets/333"},{"role":"square_logo","id":"customers/123/assets/444"}]}]}' --account 1234567890
5354
5384
  adkit manage google ads create --ad-group 555666777 \\
@@ -5623,6 +5653,8 @@ Flags:
5623
5653
  --limit <n> Max results
5624
5654
  --raw Return raw Google API response
5625
5655
 
5656
+ Output includes conversion value and ROAS so tROAS accounts can be judged from the same rows.
5657
+
5626
5658
  Examples:
5627
5659
  adkit manage google results search-terms --account 1234567890
5628
5660
  adkit manage google results search-terms --account 1234567890 --campaign 456 --period 30d --sort clicks
@@ -5665,7 +5697,7 @@ Examples:
5665
5697
  adkit manage google results keywords --account 1234567890 --period 30d --fields all`;
5666
5698
  var GOOGLE_RESEARCH_HELP = `adkit manage google research \u2014 Google Ads research tools
5667
5699
 
5668
- keywords <query> Get keyword ideas from Google Keyword Planner
5700
+ keywords <keyword...> Get keyword ideas from Google Keyword Planner
5669
5701
  topics <query> Find Display topic IDs for targeting.content.topics
5670
5702
  interests <query> Find Display interest IDs for targeting.audience.interests
5671
5703
 
@@ -5687,9 +5719,10 @@ Examples:
5687
5719
  adkit manage google research interests software --account 1234567890
5688
5720
 
5689
5721
  Notes:
5722
+ Keyword research: pass each seed keyword as a separate argument. Do not comma-separate keywords.
5690
5723
  Topic search returns IDs for targeting.content.topics.include/exclude.
5691
5724
  Interest search returns IDs for targeting.audience.interests.include/exclude.
5692
- Websites use direct URLs in targeting.content.websites.include/exclude. Custom audiences are Google user list IDs.`;
5725
+ 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.`;
5693
5726
  var GOOGLE_MEDIA_HELP = `adkit manage google media \u2014 Upload creative media to Google Ads
5694
5727
 
5695
5728
  upload Upload an image and return reusable Google asset IDs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adkit/cli",
3
- "version": "1.13.1",
3
+ "version": "1.13.3",
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",