@adkit/cli 1.12.20 → 1.12.22

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 +122 -12
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1833,9 +1833,11 @@ var SEARCH_TERMS_FLAGS = ["campaign", "ad-group", "keyword", "keyword-match-type
1833
1833
  var PLACEMENT_REPORT_FLAGS = ["campaign", "ad-group", "period", "from", "to", "sort", "limit", "raw"];
1834
1834
  var GOOGLE_RESULTS_FLAGS = ["campaign", "ad-group", "period", "from", "to", "sort", "limit", "status", "breakdown", "fields", "conversion-actions", "raw"];
1835
1835
  var GOOGLE_KEYWORD_RESULTS_FLAGS = ["campaign", "ad-group", "period", "from", "to", "sort", "limit", "status", "fields", "raw"];
1836
+ var CONVERSION_UPLOAD_FLAGS = ["conversion", "gclid", "gbraid", "wbraid", "occurred-at", "value", "currency", "order-id", "conversion-environment", "validate-only", "job-id"];
1836
1837
  var CHANGE_HISTORY_FLAGS = ["account", "period", "from", "to", "limit", "resource", "campaign", "ad-group"];
1837
1838
  var KEYWORD_RESEARCH_FLAGS = ["url", "location", "language", "limit", "min-volume"];
1838
1839
  var TARGETING_RESEARCH_FLAGS = ["limit"];
1840
+ var NUMBER_FLAG_PATTERN = /^(?:\d+|\d+\.\d+|\.\d+)(?:e[+-]?\d+)?$/iu;
1839
1841
  function generateCampaignName2() {
1840
1842
  return `campaign ${getDateStamp()}`;
1841
1843
  }
@@ -1961,6 +1963,51 @@ function buildGoogleMediaUploadSource(flags) {
1961
1963
  if (name) source.name = name;
1962
1964
  return source;
1963
1965
  }
1966
+ function buildGoogleClickConversionUploadInput(flags) {
1967
+ const conversionId = requireSingleFlagValue(flags, "conversion", {
1968
+ missingHint: 'Run: adkit manage google conversions upload --conversion 123 --gclid <gclid> --occurred-at "2026-06-24 13:45:00+00:00" --value 120 --currency USD --validate-only'
1969
+ });
1970
+ const occurredAt = requireSingleFlagValue(flags, "occurred-at", {
1971
+ missingHint: 'Google offline uploads require a timestamp with timezone, e.g. "2026-06-24 13:45:00+00:00"'
1972
+ });
1973
+ const value = readNumberFlag(flags, "value", "Google offline uploads require --value <number>");
1974
+ const currency = requireSingleFlagValue(flags, "currency", {
1975
+ missingHint: "Google offline uploads require --currency <ISO code>, e.g. USD"
1976
+ });
1977
+ const conversion = {
1978
+ conversionId,
1979
+ occurredAt,
1980
+ value,
1981
+ currency
1982
+ };
1983
+ const gclid = readSingleFlagValue(flags, "gclid", "Use one --gclid value");
1984
+ const gbraid = readSingleFlagValue(flags, "gbraid", "Use one --gbraid value");
1985
+ const wbraid = readSingleFlagValue(flags, "wbraid", "Use one --wbraid value");
1986
+ const orderId = readSingleFlagValue(flags, "order-id", "Use one --order-id value");
1987
+ const conversionEnvironment = readSingleFlagValue(flags, "conversion-environment", "Use one --conversion-environment value");
1988
+ if (gclid) conversion.gclid = gclid;
1989
+ if (gbraid) conversion.gbraid = gbraid;
1990
+ if (wbraid) conversion.wbraid = wbraid;
1991
+ if (orderId) conversion.orderId = orderId;
1992
+ if (conversionEnvironment) conversion.conversionEnvironment = conversionEnvironment;
1993
+ return conversion;
1994
+ }
1995
+ function readNumberFlag(flags, key, hint) {
1996
+ const rawValue = requireSingleFlagValue(flags, key, { missingHint: hint });
1997
+ const trimmedValue = rawValue.trim();
1998
+ const value = NUMBER_FLAG_PATTERN.test(trimmedValue) ? Number(trimmedValue) : Number.NaN;
1999
+ if (!Number.isFinite(value) || value < 0) throw new CliError("INVALID_VALUE", `Invalid number for --${key}: ${rawValue}`, hint);
2000
+ return value;
2001
+ }
2002
+ function applyGoogleClickConversionUploadOptions(body, flags) {
2003
+ if (flags["validate-only"] === true) body.validateOnly = true;
2004
+ const rawJobId = readSingleFlagValue(flags, "job-id", "Use one --job-id value");
2005
+ if (!rawJobId) return;
2006
+ const trimmedJobId = rawJobId.trim();
2007
+ const jobId = /^\d+$/u.test(trimmedJobId) ? Number(trimmedJobId) : Number.NaN;
2008
+ if (!Number.isInteger(jobId) || jobId >= 2147483648) throw new CliError("INVALID_VALUE", `Invalid integer for --job-id: ${rawJobId}`, "Use a non-negative integer below 2147483648");
2009
+ body.jobId = jobId;
2010
+ }
1964
2011
  function resolveAssetScope(flags) {
1965
2012
  const scopeType = typeof flags.scope === "string" ? flags.scope : void 0;
1966
2013
  if (scopeType !== "account" && scopeType !== "campaign" && scopeType !== "ad-group") throw new CliError("MISSING_FLAG", "Missing required flag: `--scope`", "Use --scope account|campaign|ad-group");
@@ -2301,6 +2348,24 @@ async function uploadMedia2(client, _args, flags) {
2301
2348
  const path3 = `/manage/google/media${query}`;
2302
2349
  return client.post(path3, body);
2303
2350
  }
2351
+ async function uploadGoogleConversions(client, _args, flags) {
2352
+ validateFlags(flags, CONVERSION_UPLOAD_FLAGS, "manage google conversions upload");
2353
+ if (flags.publish === true) throw new CliError("INVALID_VALUE", "`--publish` is not used for conversion uploads", "Use --validate-only for a dry run; omit it to upload directly.");
2354
+ if (typeof flags.data === "string") {
2355
+ const rawBody = parseDataFlag(flags, 'Use { "conversions": [...] } for Google conversion upload');
2356
+ const accountId2 = resolveGoogleAccountId(rawBody, flags);
2357
+ const body2 = removeQueryOnlyBodyKeys(rawBody, ["accountId"]);
2358
+ const query2 = queryString({ accountId: accountId2 });
2359
+ const path4 = `/manage/google/conversions/upload${query2}`;
2360
+ return client.post(path4, body2);
2361
+ }
2362
+ const body = { conversions: [buildGoogleClickConversionUploadInput(flags)] };
2363
+ applyGoogleClickConversionUploadOptions(body, flags);
2364
+ const accountId = typeof flags.account === "string" ? flags.account : void 0;
2365
+ const query = queryString({ accountId });
2366
+ const path3 = `/manage/google/conversions/upload${query}`;
2367
+ return client.post(path3, body);
2368
+ }
2304
2369
  async function listGoogleCampaigns(client, _args, flags) {
2305
2370
  validateFlags(flags, CAMPAIGN_LIST_FLAGS, "manage google campaigns list");
2306
2371
  const qs = buildGoogleListQuery(flags);
@@ -2843,7 +2908,7 @@ function readTikTokPlatformOverridesFlag(flags) {
2843
2908
  function isCliTikTokPlatformOverrides(value) {
2844
2909
  return !!value && typeof value === "object" && !Array.isArray(value);
2845
2910
  }
2846
- function readNumberFlag(flags, key) {
2911
+ function readNumberFlag2(flags, key) {
2847
2912
  const value = flags[key];
2848
2913
  if (value === void 0) return void 0;
2849
2914
  if (typeof value !== "string") throw new CliError("INVALID_VALUE", `Use one --${key} value`, `Run: adkit manage tiktok --help`);
@@ -2852,8 +2917,8 @@ function readNumberFlag(flags, key) {
2852
2917
  return parsed;
2853
2918
  }
2854
2919
  function buildTikTokBudget(flags) {
2855
- const daily = readNumberFlag(flags, "budget-daily");
2856
- const lifetime = readNumberFlag(flags, "budget-lifetime");
2920
+ const daily = readNumberFlag2(flags, "budget-daily");
2921
+ const lifetime = readNumberFlag2(flags, "budget-lifetime");
2857
2922
  if (daily === void 0 && lifetime === void 0) return void 0;
2858
2923
  if (daily !== void 0 && lifetime !== void 0) throw new CliError("INVALID_VALUE", "Use either --budget-daily or --budget-lifetime", "TikTok accepts one budget mode per campaign/ad group");
2859
2924
  return daily !== void 0 ? { daily } : { lifetime };
@@ -2898,7 +2963,7 @@ function buildTikTokAdGroupPayload(flags, mode) {
2898
2963
  if (typeof flags["start-date"] === "string") payload.startDate = flags["start-date"];
2899
2964
  if (typeof flags["end-date"] === "string") payload.endDate = flags["end-date"];
2900
2965
  if (typeof flags.pacing === "string") payload.pacing = flags.pacing;
2901
- const bidAmount = readNumberFlag(flags, "bid-amount");
2966
+ const bidAmount = readNumberFlag2(flags, "bid-amount");
2902
2967
  if (bidAmount !== void 0) payload.bidAmount = bidAmount;
2903
2968
  if (typeof flags["event-type"] === "string") {
2904
2969
  payload.conversion = {
@@ -4753,7 +4818,7 @@ Notes:
4753
4818
  With platformLocation, pass the exact Google geo object to send; AdKit does not look up or convert city names or addresses.
4754
4819
  Google excludes support location geo target constants, not proximity radius exclusions.
4755
4820
  Display campaign creation uses campaignType:"display" or --campaign-type display. Videos are not supported for Display media in v1.
4756
- 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 action (publish is blocked without one). Non-retail only. Build it with --data.
4821
+ 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.
4757
4822
  View a Performance Max campaign by ID to read back assetGroups from Google. There is no separate google asset-groups command.
4758
4823
  For spend/clicks/conversions: adkit manage google results
4759
4824
 
@@ -5302,6 +5367,33 @@ Notes:
5302
5367
  Upload returns assets[].id and assets[].resourceName. Use either value in Display ad media[].id.
5303
5368
  Display media roles: marketing_image, square_marketing_image, logo, square_logo.
5304
5369
  Videos are not supported in Google Display v1; upload images only.`;
5370
+ var GOOGLE_CONVERSIONS_HELP = `adkit manage google conversions \u2014 Google Ads conversion tracking
5371
+
5372
+ upload Upload offline click conversion events
5373
+
5374
+ Flags (upload):
5375
+ --conversion <id> Google conversion ID (platformId), must be type upload_clicks
5376
+ --gclid <id> Google click ID
5377
+ --gbraid <id> GBRAID click parameter
5378
+ --wbraid <id> WBRAID click parameter
5379
+ --occurred-at <ts> Conversion timestamp with timezone, e.g. "2026-06-24 13:45:00+00:00"
5380
+ --value <n> Conversion value
5381
+ --currency <code> ISO currency code, e.g. USD
5382
+ --order-id <id> Optional order ID for dedupe/adjustments
5383
+ --conversion-environment <web|app>
5384
+ --validate-only Validate without importing conversions
5385
+ --job-id <n> Optional Google diagnostics job id
5386
+ --account <id> Optional if one Google Ads account is connected
5387
+ --data <json> Batch body: {"conversions":[...],"validateOnly":true}
5388
+
5389
+ Examples:
5390
+ adkit manage google conversions upload --conversion 123 --gclid EAIaIQob... --occurred-at "2026-06-24 13:45:00+00:00" --value 120 --currency USD --validate-only
5391
+ adkit manage google conversions upload --data '{"conversions":[{"conversionId":"123","gclid":"EAIaIQob...","occurredAt":"2026-06-24 13:45:00+00:00","value":120,"currency":"USD"}],"validateOnly":true}' --account 1234567890
5392
+
5393
+ Notes:
5394
+ Upload sends events directly to Google; it is not a draft flow.
5395
+ Use --validate-only before a real upload when testing a new CRM/backend export.
5396
+ Real uploads require publish access.`;
5305
5397
  var META_RESEARCH_HELP = `adkit manage meta research \u2014 Meta Ads research tools
5306
5398
 
5307
5399
  interests <query> Search targeting interests (returns IDs for ad set targeting)
@@ -5362,6 +5454,7 @@ Examples:
5362
5454
  "google change-history": GOOGLE_CHANGE_HISTORY_HELP,
5363
5455
  "google research": GOOGLE_RESEARCH_HELP,
5364
5456
  "google media": GOOGLE_MEDIA_HELP,
5457
+ "google conversions": GOOGLE_CONVERSIONS_HELP,
5365
5458
  "tiktok accounts": `adkit manage tiktok accounts \u2014 TikTok Ads accounts
5366
5459
 
5367
5460
  list List connected TikTok advertisers
@@ -5537,7 +5630,7 @@ Examples:
5537
5630
 
5538
5631
  Bypasses normalized AdKit entity validation, but still runs account and safety checks.
5539
5632
  GET requests execute immediately. POST/PATCH/DELETE create drafts by default; add --publish to execute mutating requests immediately.
5540
- Must be enabled in project settings first.
5633
+ Must be enabled in Agent Permissions first.
5541
5634
  Use this for unsupported native platform resources/workflows. Use platformOverrides instead when you only need raw fields on a supported normalized resource.
5542
5635
  Use the selected platform's official endpoint paths, field names, enum values, and resource shapes. Do not translate normalized AdKit field names into raw API payloads by guessing.
5543
5636
 
@@ -5604,6 +5697,7 @@ Entity groups:
5604
5697
  ads Manage responsive search and Display ads
5605
5698
  keywords Manage keywords and negatives \u2014 config only, not metrics
5606
5699
  keywords negative-lists Shared negative keyword lists (create, attach to campaigns)
5700
+ conversions Manage conversion tracking and upload offline conversions
5607
5701
  results Performance metrics (campaigns, ad-groups, ads, keywords, search-terms)
5608
5702
  change-history Recent Google Ads account changes from the last 30 days
5609
5703
  research Keyword Planner plus Display topic/interest IDs
@@ -5615,7 +5709,8 @@ ${FLAG.account}
5615
5709
  ${FLAG.publish}
5616
5710
  ${FLAG.data}
5617
5711
 
5618
- Mutations are draft-first by default. Use --publish to publish immediately.
5712
+ Config mutations are draft-first by default. Use --publish to publish immediately.
5713
+ Conversion uploads send events directly; use --validate-only for a dry run.
5619
5714
  ${rawPlatformCommandLine("google")}
5620
5715
  Display path: research topics/interests, upload images with google media, create campaigns --campaign-type display, create ad-groups with targeting, then create ads with type:"responsive_display".
5621
5716
  Google Display Ads is in beta \u2014 some first-class normalized Display features may not be available yet.
@@ -5901,6 +5996,7 @@ var HELP_FULL = {
5901
5996
  "google change-history": GOOGLE_CHANGE_HISTORY_HELP,
5902
5997
  "google research": GOOGLE_RESEARCH_HELP,
5903
5998
  "google media": GOOGLE_MEDIA_HELP,
5999
+ "google conversions": GOOGLE_CONVERSIONS_HELP,
5904
6000
  "meta research": META_RESEARCH_HELP,
5905
6001
  "studio generate": STUDIO_GENERATE_HELP_FULL,
5906
6002
  "library advertisers": `adkit library advertisers \u2014 Browse, inspect, and discover advertisers
@@ -6817,7 +6913,7 @@ async function main() {
6817
6913
  case "list":
6818
6914
  case void 0:
6819
6915
  data = await listAccounts(client, restArgs, flags);
6820
- emptyHint = "No Meta ad accounts connected. Run `adkit setup manage` or connect via https://app.adkit.so/settings/integrations";
6916
+ emptyHint = "No Meta ad accounts connected. Run `adkit setup manage` or connect a workspace login via https://app.adkit.so/dashboard/settings/workspace-integrations";
6821
6917
  break;
6822
6918
  case "connect":
6823
6919
  data = await connectAccount(client, restArgs, flags);
@@ -6988,7 +7084,7 @@ async function main() {
6988
7084
  case "list":
6989
7085
  case void 0:
6990
7086
  data = await listAccounts2(client, restArgs, flags);
6991
- emptyHint = "No Google Ads accounts connected. Run `adkit setup manage` or connect via https://app.adkit.so/settings/integrations";
7087
+ emptyHint = "No Google Ads accounts connected. Run `adkit setup manage` or connect a workspace login via https://app.adkit.so/dashboard/settings/workspace-integrations";
6992
7088
  break;
6993
7089
  case "available":
6994
7090
  data = await listAvailable(client, restArgs, flags);
@@ -7220,6 +7316,20 @@ async function main() {
7220
7316
  else data = await listGoogleResults(client, restArgs, flags);
7221
7317
  break;
7222
7318
  }
7319
+ case "conversions": {
7320
+ if (!action || flags.help) {
7321
+ showHelp("google conversions", flags.help === "full");
7322
+ process.exit(0);
7323
+ }
7324
+ switch (action) {
7325
+ case "upload":
7326
+ data = await uploadGoogleConversions(client, restArgs, flags);
7327
+ break;
7328
+ default:
7329
+ throw new CliError("UNKNOWN_COMMAND", `Unknown action: google conversions ${action}`, "Available: upload");
7330
+ }
7331
+ break;
7332
+ }
7223
7333
  case "change-history": {
7224
7334
  if (action && action !== "list") throw new CliError("UNKNOWN_COMMAND", `Unknown action: google change-history ${action}`, "Available: list");
7225
7335
  data = await listGoogleChangeHistory(client, restArgs, flags);
@@ -7246,7 +7356,7 @@ async function main() {
7246
7356
  break;
7247
7357
  }
7248
7358
  default:
7249
- throw new CliError("UNKNOWN_COMMAND", `Unknown entity: google ${entity}`, "Available: accounts, assets, media, campaigns, ad-groups, ads, keywords, results, change-history, research");
7359
+ throw new CliError("UNKNOWN_COMMAND", `Unknown entity: google ${entity}`, "Available: accounts, assets, media, campaigns, ad-groups, ads, keywords, conversions, results, change-history, research");
7250
7360
  }
7251
7361
  } else if (platform2 === "tiktok") {
7252
7362
  switch (entity) {
@@ -7255,7 +7365,7 @@ async function main() {
7255
7365
  case "list":
7256
7366
  case void 0:
7257
7367
  data = await listTikTokAccounts(client, restArgs, flags);
7258
- emptyHint = "No TikTok Ads accounts connected. Run `adkit setup manage` or connect via https://app.adkit.so/settings/integrations";
7368
+ emptyHint = "No TikTok Ads accounts connected. Run `adkit setup manage` or connect a workspace login via https://app.adkit.so/dashboard/settings/workspace-integrations";
7259
7369
  break;
7260
7370
  case "available":
7261
7371
  data = await listTikTokAvailableAccounts(client, restArgs, flags);
@@ -7372,7 +7482,7 @@ async function main() {
7372
7482
  case "list":
7373
7483
  case void 0:
7374
7484
  data = await listRedditAccounts(client, restArgs, flags);
7375
- emptyHint = "No Reddit Ads accounts connected. Run `adkit setup manage` or connect via https://app.adkit.so/settings/integrations";
7485
+ emptyHint = "No Reddit Ads accounts connected. Run `adkit setup manage` or connect a workspace login via https://app.adkit.so/dashboard/settings/workspace-integrations";
7376
7486
  break;
7377
7487
  case "available":
7378
7488
  data = await listRedditAvailableAccounts(client, restArgs, flags);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adkit/cli",
3
- "version": "1.12.20",
3
+ "version": "1.12.22",
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",