@adkit/cli 1.13.30 → 1.13.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +912 -124
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1110,14 +1110,14 @@ var baseOpen = async (options) => {
|
|
|
1110
1110
|
}
|
|
1111
1111
|
const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
|
|
1112
1112
|
if (options.wait) {
|
|
1113
|
-
return new Promise((
|
|
1113
|
+
return new Promise((resolve5, reject) => {
|
|
1114
1114
|
subprocess.once("error", reject);
|
|
1115
1115
|
subprocess.once("close", (exitCode) => {
|
|
1116
1116
|
if (!options.allowNonzeroExitCode && exitCode > 0) {
|
|
1117
1117
|
reject(new Error(`Exited with code ${exitCode}`));
|
|
1118
1118
|
return;
|
|
1119
1119
|
}
|
|
1120
|
-
|
|
1120
|
+
resolve5(subprocess);
|
|
1121
1121
|
});
|
|
1122
1122
|
});
|
|
1123
1123
|
}
|
|
@@ -1576,25 +1576,26 @@ async function listMetaConversions(client, _args, flags) {
|
|
|
1576
1576
|
const qs = queryString({ accountId, limit, offset, raw });
|
|
1577
1577
|
return client.get(`/manage/meta/conversions${qs}`);
|
|
1578
1578
|
}
|
|
1579
|
-
function buildCampaignPayload(flags) {
|
|
1579
|
+
function buildCampaignPayload(flags, { defaultCbo }) {
|
|
1580
1580
|
const payload = {};
|
|
1581
1581
|
if (typeof flags.name === "string") payload.name = flags.name;
|
|
1582
1582
|
if (typeof flags.objective === "string") payload.objective = flags.objective;
|
|
1583
1583
|
if (typeof flags.status === "string") payload.status = flags.status;
|
|
1584
1584
|
if (typeof flags["bid-strategy"] === "string") payload.bidStrategy = flags["bid-strategy"];
|
|
1585
1585
|
if (flags.abo === true) payload.advantageCampaignBudget = false;
|
|
1586
|
-
else payload.advantageCampaignBudget = true;
|
|
1586
|
+
else if (defaultCbo) payload.advantageCampaignBudget = true;
|
|
1587
1587
|
if (typeof flags["budget-daily"] === "string") payload.budget = { daily: parseFloat(flags["budget-daily"]) };
|
|
1588
1588
|
if (typeof flags["budget-total"] === "string") payload.budget = { ...payload.budget, lifetime: parseFloat(flags["budget-total"]) };
|
|
1589
1589
|
if (typeof flags["platform-overrides"] === "string") payload.platformOverrides = parseJsonObject(flags["platform-overrides"], "platform-overrides");
|
|
1590
1590
|
return payload;
|
|
1591
1591
|
}
|
|
1592
1592
|
async function listMetaCampaigns(client, _args, flags) {
|
|
1593
|
-
validateFlags(flags, ["account", "status", "raw"], "manage meta campaigns list");
|
|
1593
|
+
validateFlags(flags, ["account", "status", "limit", "raw"], "manage meta campaigns list");
|
|
1594
1594
|
const accountId = typeof flags.account === "string" ? flags.account : void 0;
|
|
1595
1595
|
const status2 = typeof flags.status === "string" ? flags.status : void 0;
|
|
1596
|
+
const limit = typeof flags.limit === "string" ? flags.limit : void 0;
|
|
1596
1597
|
const raw = flags.raw === true ? "true" : void 0;
|
|
1597
|
-
const qs = queryString({ accountId, status: status2, raw });
|
|
1598
|
+
const qs = queryString({ accountId, status: status2, limit, raw });
|
|
1598
1599
|
return client.get(`/manage/meta/campaigns${qs}`);
|
|
1599
1600
|
}
|
|
1600
1601
|
var CAMPAIGN_FLAGS = ["name", "objective", "status", "budget-daily", "budget-total", "abo", "bid-strategy"];
|
|
@@ -1612,7 +1613,7 @@ async function createCampaign(client, _args, flags) {
|
|
|
1612
1613
|
return client.post(`/manage/meta/campaigns${publish2}`, body2);
|
|
1613
1614
|
}
|
|
1614
1615
|
validateFlags(flags, CAMPAIGN_FLAGS, "manage meta campaigns create");
|
|
1615
|
-
const payload = buildCampaignPayload(flags);
|
|
1616
|
+
const payload = buildCampaignPayload(flags, { defaultCbo: true });
|
|
1616
1617
|
if (!payload.name) payload.name = generateCampaignName();
|
|
1617
1618
|
const body = { campaigns: [payload] };
|
|
1618
1619
|
mergeAccountId(body, flags);
|
|
@@ -1621,15 +1622,16 @@ async function createCampaign(client, _args, flags) {
|
|
|
1621
1622
|
}
|
|
1622
1623
|
async function updateCampaign(client, args, flags) {
|
|
1623
1624
|
const id = requireArg(args, 0, "campaign-id", `Run: adkit manage meta campaigns update <campaign-id> --status paused`);
|
|
1625
|
+
const publish = flags.publish === true ? "?publish=true" : "";
|
|
1624
1626
|
if (typeof flags.data === "string") {
|
|
1625
1627
|
const body = parseDataFlag(flags, "Check JSON syntax in --data");
|
|
1626
1628
|
mergeAccountId(body, flags);
|
|
1627
|
-
return client.patch(`/manage/meta/campaigns/${id}`, body);
|
|
1629
|
+
return client.patch(`/manage/meta/campaigns/${id}${publish}`, body);
|
|
1628
1630
|
}
|
|
1629
1631
|
validateFlags(flags, CAMPAIGN_FLAGS, "manage meta campaigns update");
|
|
1630
|
-
const payload = buildCampaignPayload(flags);
|
|
1632
|
+
const payload = buildCampaignPayload(flags, { defaultCbo: false });
|
|
1631
1633
|
mergeAccountId(payload, flags);
|
|
1632
|
-
return client.patch(`/manage/meta/campaigns/${id}`, payload);
|
|
1634
|
+
return client.patch(`/manage/meta/campaigns/${id}${publish}`, payload);
|
|
1633
1635
|
}
|
|
1634
1636
|
async function deleteCampaign(client, args, flags) {
|
|
1635
1637
|
const id = requireArg(args, 0, "campaign-id", "Run: adkit manage meta campaigns delete <campaign-id>");
|
|
@@ -1702,15 +1704,16 @@ async function createAdSet(client, _args, flags) {
|
|
|
1702
1704
|
}
|
|
1703
1705
|
async function updateAdSet(client, args, flags) {
|
|
1704
1706
|
const id = requireArg(args, 0, "adset-id", `Run: adkit manage meta adsets update <adset-id> --budget-daily 50`);
|
|
1707
|
+
const publish = flags.publish === true ? "?publish=true" : "";
|
|
1705
1708
|
if (typeof flags.data === "string") {
|
|
1706
1709
|
const body = parseDataFlag(flags, "Check JSON syntax in --data");
|
|
1707
1710
|
mergeAccountId(body, flags);
|
|
1708
|
-
return client.patch(`/manage/meta/adsets/${id}`, body);
|
|
1711
|
+
return client.patch(`/manage/meta/adsets/${id}${publish}`, body);
|
|
1709
1712
|
}
|
|
1710
1713
|
validateFlags(flags, ADSET_FLAGS, "manage meta adsets update");
|
|
1711
1714
|
const payload = buildAdSetPayload(flags);
|
|
1712
1715
|
mergeAccountId(payload, flags);
|
|
1713
|
-
return client.patch(`/manage/meta/adsets/${id}`, payload);
|
|
1716
|
+
return client.patch(`/manage/meta/adsets/${id}${publish}`, payload);
|
|
1714
1717
|
}
|
|
1715
1718
|
async function deleteAdSet(client, args, flags) {
|
|
1716
1719
|
const id = requireArg(args, 0, "adset-id", "Run: adkit manage meta adsets delete <adset-id>");
|
|
@@ -1873,7 +1876,8 @@ async function updateAd(client, args, flags) {
|
|
|
1873
1876
|
const id = requireArg(args, 0, "ad-id", `Run: adkit manage meta ads update <ad-id> --data '{"status":"paused"}'`);
|
|
1874
1877
|
const body = parseDataFlag(flags, "Check JSON syntax in --data");
|
|
1875
1878
|
mergeAccountId(body, flags);
|
|
1876
|
-
|
|
1879
|
+
const publish = flags.publish === true ? "?publish=true" : "";
|
|
1880
|
+
return client.patch(`/manage/meta/ads/${id}${publish}`, body);
|
|
1877
1881
|
}
|
|
1878
1882
|
async function deleteAd(client, args, flags) {
|
|
1879
1883
|
const id = requireArg(args, 0, "ad-id", "Run: adkit manage meta ads delete <ad-id>");
|
|
@@ -1927,15 +1931,16 @@ async function createCreative(client, _args, flags) {
|
|
|
1927
1931
|
}
|
|
1928
1932
|
async function updateCreative(client, args, flags) {
|
|
1929
1933
|
const id = requireArg(args, 0, "creative-id", `Run: adkit manage meta creatives update <creative-id> --primary-text "Start free trial"`);
|
|
1934
|
+
const publish = flags.publish === true ? "?publish=true" : "";
|
|
1930
1935
|
if (typeof flags.data === "string") {
|
|
1931
1936
|
const body = parseDataFlag(flags, "Check JSON syntax in --data");
|
|
1932
1937
|
mergeAccountId(body, flags);
|
|
1933
|
-
return client.patch(`/manage/meta/creatives/${id}`, body);
|
|
1938
|
+
return client.patch(`/manage/meta/creatives/${id}${publish}`, body);
|
|
1934
1939
|
}
|
|
1935
1940
|
validateFlags(flags, CREATIVE_FLAGS, "manage meta creatives update");
|
|
1936
1941
|
const payload = buildCreativePayload(flags);
|
|
1937
1942
|
mergeAccountId(payload, flags);
|
|
1938
|
-
return client.patch(`/manage/meta/creatives/${id}`, payload);
|
|
1943
|
+
return client.patch(`/manage/meta/creatives/${id}${publish}`, payload);
|
|
1939
1944
|
}
|
|
1940
1945
|
async function deleteCreative(client, args, flags) {
|
|
1941
1946
|
const id = requireArg(args, 0, "creative-id", "Run: adkit manage meta creatives delete <creative-id>");
|
|
@@ -2018,7 +2023,7 @@ var ASSET_GROUP_LIST_FLAGS = ["campaign", "limit", "offset"];
|
|
|
2018
2023
|
var ASSET_GROUP_CREATE_FLAGS = ["campaign", "name", "status", "final-url"];
|
|
2019
2024
|
var ASSET_GROUP_UPDATE_FLAGS = ["name", "status", "final-url"];
|
|
2020
2025
|
var AD_GROUP_LIST_FLAGS = ["campaign", "status", "limit", "offset"];
|
|
2021
|
-
var AD_GROUP_FLAGS = ["campaign", "name", "status", "cpc-bid"];
|
|
2026
|
+
var AD_GROUP_FLAGS = ["campaign", "name", "status", "cpc-bid", "target-cpa", "target-roas"];
|
|
2022
2027
|
var AD_LIST_FLAGS = ["ad-group", "campaign", "status", "policy-status", "limit", "offset"];
|
|
2023
2028
|
var AD_FLAGS2 = ["headline", "headline-1", "headline-2", "headline-3", "description", "description-1", "description-2", "callout", "sitelink", "final-url", "path", "ad-group", "status"];
|
|
2024
2029
|
var KEYWORD_LIST_FLAGS = ["ad-group", "campaign", "status", "limit", "offset"];
|
|
@@ -2358,6 +2363,8 @@ function buildAdGroupPayload(flags) {
|
|
|
2358
2363
|
if (typeof flags.name === "string") payload.name = flags.name;
|
|
2359
2364
|
if (typeof flags.status === "string") payload.status = flags.status;
|
|
2360
2365
|
if (typeof flags["cpc-bid"] === "string") payload.cpcBid = Number.parseFloat(flags["cpc-bid"]);
|
|
2366
|
+
if (typeof flags["target-cpa"] === "string") payload.targetCpa = readNumberFlag(flags, "target-cpa", "Use a positive number for --target-cpa");
|
|
2367
|
+
if (typeof flags["target-roas"] === "string") payload.targetRoas = readNumberFlag(flags, "target-roas", "Use a number from 0.01 to 1000 for --target-roas");
|
|
2361
2368
|
return payload;
|
|
2362
2369
|
}
|
|
2363
2370
|
function buildAdPayload(flags) {
|
|
@@ -3582,6 +3589,29 @@ async function listTikTokResults(client, _args, flags) {
|
|
|
3582
3589
|
}
|
|
3583
3590
|
|
|
3584
3591
|
// src/commands/reddit.ts
|
|
3592
|
+
import { readFile } from "node:fs/promises";
|
|
3593
|
+
import { basename as basename3, extname as extname3, resolve as resolve3 } from "node:path";
|
|
3594
|
+
var ACCOUNT_RESOURCE_FLAGS = ["limit", "offset"];
|
|
3595
|
+
var CAMPAIGN_LIST_FLAGS3 = ["fields", "limit", "offset"];
|
|
3596
|
+
var CAMPAIGN_CREATE_FLAGS3 = ["name", "objective", "optimization", "status", "budget-lifetime", "bid-strategy", "start-date", "end-date", "funding-instrument", "pixel"];
|
|
3597
|
+
var CAMPAIGN_UPDATE_FLAGS3 = ["name", "status", "budget-lifetime", "bid-strategy", "start-date", "end-date", "funding-instrument", "pixel"];
|
|
3598
|
+
var AD_GROUP_LIST_FLAGS3 = ["campaign-ids", "fields", "limit", "offset"];
|
|
3599
|
+
var AD_GROUP_CREATE_FLAGS2 = ["campaign", "name", "status", "budget-daily", "bid-strategy", "bid-amount", "optimization", "start-date", "end-date", "pixel"];
|
|
3600
|
+
var AD_GROUP_UPDATE_FLAGS2 = ["name", "status", "budget-daily", "bid-strategy", "bid-amount", "start-date", "end-date", "pixel"];
|
|
3601
|
+
var AD_LIST_FLAGS3 = ["campaign-ids", "ad-group-ids", "fields", "limit", "offset"];
|
|
3602
|
+
var AD_CREATE_FLAGS2 = ["ad-group", "name", "status", "ad-type", "existing-post", "profile", "headline", "primary-text", "file", "media-id", "thumbnail-file", "thumbnail-id", "url", "cta"];
|
|
3603
|
+
var AD_UPDATE_FLAGS2 = ["ad-group", "name", "status", "ad-type", "profile", "headline", "primary-text", "file", "media-id", "thumbnail-file", "thumbnail-id", "url", "cta"];
|
|
3604
|
+
var RESULTS_FLAGS2 = ["level", "period", "from", "to", "fields", "breakdowns", "campaign-ids", "ad-group-ids", "ad-ids", "sort", "sort-direction", "limit", "offset", "raw"];
|
|
3605
|
+
var REDDIT_MEDIA_CONTENT_TYPES = {
|
|
3606
|
+
".gif": "image/gif",
|
|
3607
|
+
".jpeg": "image/jpeg",
|
|
3608
|
+
".jpg": "image/jpeg",
|
|
3609
|
+
".mov": "video/quicktime",
|
|
3610
|
+
".mp4": "video/mp4",
|
|
3611
|
+
".png": "image/png",
|
|
3612
|
+
".webm": "video/webm",
|
|
3613
|
+
".webp": "image/webp"
|
|
3614
|
+
};
|
|
3585
3615
|
async function listRedditAccounts(client, _args, flags) {
|
|
3586
3616
|
validateFlags(flags, [], "manage reddit accounts list");
|
|
3587
3617
|
return client.get("/manage/reddit/accounts");
|
|
@@ -3592,30 +3622,384 @@ async function listRedditAvailableAccounts(client, _args, flags) {
|
|
|
3592
3622
|
}
|
|
3593
3623
|
async function connectRedditAccount(client, args, flags) {
|
|
3594
3624
|
validateFlags(flags, [], "manage reddit accounts connect");
|
|
3595
|
-
const
|
|
3596
|
-
return client.post("/manage/reddit/accounts/connect", { adAccountId });
|
|
3625
|
+
const accountId = requireArg(args, 0, "account-id", "Run: adkit manage reddit accounts connect <account-id>");
|
|
3626
|
+
return client.post("/manage/reddit/accounts/connect", { adAccountId: accountId });
|
|
3597
3627
|
}
|
|
3598
3628
|
async function disconnectRedditAccount(client, args, flags) {
|
|
3599
3629
|
validateFlags(flags, [], "manage reddit accounts disconnect");
|
|
3600
|
-
const
|
|
3601
|
-
return client.delete(`/manage/reddit/accounts/${
|
|
3630
|
+
const accountId = requireArg(args, 0, "account-id", "Run: adkit manage reddit accounts disconnect <account-id>");
|
|
3631
|
+
return client.delete(`/manage/reddit/accounts/${accountId}`);
|
|
3632
|
+
}
|
|
3633
|
+
async function listRedditProfiles(client, args, flags) {
|
|
3634
|
+
return listRedditAccountResource(client, args, flags, "profiles");
|
|
3635
|
+
}
|
|
3636
|
+
async function listRedditPixels(client, args, flags) {
|
|
3637
|
+
return listRedditAccountResource(client, args, flags, "pixels");
|
|
3638
|
+
}
|
|
3639
|
+
async function listRedditFundingInstruments(client, args, flags) {
|
|
3640
|
+
return listRedditAccountResource(client, args, flags, "funding-instruments");
|
|
3641
|
+
}
|
|
3642
|
+
async function updateRedditAccount(client, args, flags) {
|
|
3643
|
+
validateFlags(flags, ["default-profile", "default-pixel", "default-funding-instrument"], "manage reddit accounts <account-id> update");
|
|
3644
|
+
const accountId = requireArg(args, 0, "account-id", "Run: adkit manage reddit accounts <account-id> update --default-profile <profile-id>");
|
|
3645
|
+
if (typeof flags.data === "string") {
|
|
3646
|
+
const body = parseDataFlag(flags, 'Use { "defaultProfileId": "...", "defaultPixelId": "...", "defaultFundingInstrumentId": "..." }');
|
|
3647
|
+
return client.patch(`/manage/reddit/accounts/${accountId}`, body);
|
|
3648
|
+
}
|
|
3649
|
+
const defaultProfileId = readStringFlag(flags, "default-profile");
|
|
3650
|
+
const defaultPixelId = readStringFlag(flags, "default-pixel");
|
|
3651
|
+
const defaultFundingInstrumentId = readStringFlag(flags, "default-funding-instrument");
|
|
3652
|
+
if (!defaultProfileId && !defaultPixelId && !defaultFundingInstrumentId) throw new CliError("MISSING_FLAG", "Nothing to update", "Pass --default-profile, --default-pixel, --default-funding-instrument, or --data");
|
|
3653
|
+
return client.patch(`/manage/reddit/accounts/${accountId}`, {
|
|
3654
|
+
...defaultProfileId ? { defaultProfileId } : {},
|
|
3655
|
+
...defaultPixelId ? { defaultPixelId } : {},
|
|
3656
|
+
...defaultFundingInstrumentId ? { defaultFundingInstrumentId } : {}
|
|
3657
|
+
});
|
|
3658
|
+
}
|
|
3659
|
+
async function listRedditCampaigns(client, _args, flags) {
|
|
3660
|
+
validateFlags(flags, CAMPAIGN_LIST_FLAGS3, "manage reddit campaigns list");
|
|
3661
|
+
const query = buildRedditListQuery(flags);
|
|
3662
|
+
return client.get(`/manage/reddit/campaigns${query}`);
|
|
3663
|
+
}
|
|
3664
|
+
async function getRedditCampaign(client, args, flags) {
|
|
3665
|
+
validateFlags(flags, ["fields"], "manage reddit campaigns <campaign-id>");
|
|
3666
|
+
const campaignId = requireArg(args, 0, "campaign-id", "Run: adkit manage reddit campaigns <campaign-id>");
|
|
3667
|
+
const query = buildRedditReadQuery(flags);
|
|
3668
|
+
return client.get(`/manage/reddit/campaigns/${campaignId}${query}`);
|
|
3669
|
+
}
|
|
3670
|
+
async function createRedditCampaign(client, _args, flags) {
|
|
3671
|
+
validateFlags(flags, CAMPAIGN_CREATE_FLAGS3, "manage reddit campaigns create");
|
|
3672
|
+
const query = buildRedditMutationQuery(flags);
|
|
3673
|
+
if (typeof flags.data === "string") {
|
|
3674
|
+
const body2 = parseDataFlag(flags, 'Use { "campaigns": [...] } for Reddit campaign create');
|
|
3675
|
+
return client.post(`/manage/reddit/campaigns${query}`, body2);
|
|
3676
|
+
}
|
|
3677
|
+
const campaign = buildRedditCampaignPayload(flags, "create");
|
|
3678
|
+
const body = { campaigns: [campaign] };
|
|
3679
|
+
return client.post(`/manage/reddit/campaigns${query}`, body);
|
|
3680
|
+
}
|
|
3681
|
+
async function updateRedditCampaign(client, args, flags) {
|
|
3682
|
+
validateFlags(flags, CAMPAIGN_UPDATE_FLAGS3, "manage reddit campaigns update");
|
|
3683
|
+
const campaignId = requireArg(args, 0, "campaign-id", "Run: adkit manage reddit campaigns update <campaign-id> --status paused");
|
|
3684
|
+
const body = typeof flags.data === "string" ? parseDataFlag(flags, "Use a flat Reddit campaign changes object") : buildRedditCampaignPayload(flags, "update");
|
|
3685
|
+
const query = buildRedditMutationQuery(flags);
|
|
3686
|
+
return client.patch(`/manage/reddit/campaigns/${campaignId}${query}`, body);
|
|
3687
|
+
}
|
|
3688
|
+
async function listRedditAdGroups(client, _args, flags) {
|
|
3689
|
+
validateFlags(flags, AD_GROUP_LIST_FLAGS3, "manage reddit ad-groups list");
|
|
3690
|
+
const campaignIds = readStringFlag(flags, "campaign-ids");
|
|
3691
|
+
const query = buildRedditListQuery(flags, { campaignIds });
|
|
3692
|
+
return client.get(`/manage/reddit/ad-groups${query}`);
|
|
3693
|
+
}
|
|
3694
|
+
async function getRedditAdGroup(client, args, flags) {
|
|
3695
|
+
validateFlags(flags, ["fields"], "manage reddit ad-groups <ad-group-id>");
|
|
3696
|
+
const adGroupId = requireArg(args, 0, "ad-group-id", "Run: adkit manage reddit ad-groups <ad-group-id>");
|
|
3697
|
+
const query = buildRedditReadQuery(flags);
|
|
3698
|
+
return client.get(`/manage/reddit/ad-groups/${adGroupId}${query}`);
|
|
3699
|
+
}
|
|
3700
|
+
async function createRedditAdGroup(client, _args, flags) {
|
|
3701
|
+
validateFlags(flags, AD_GROUP_CREATE_FLAGS2, "manage reddit ad-groups create");
|
|
3702
|
+
const query = buildRedditMutationQuery(flags);
|
|
3703
|
+
if (typeof flags.data === "string") {
|
|
3704
|
+
const body2 = parseDataFlag(flags, 'Use { "adGroups": [...] } for Reddit ad-group create');
|
|
3705
|
+
return client.post(`/manage/reddit/ad-groups${query}`, body2);
|
|
3706
|
+
}
|
|
3707
|
+
const adGroup = buildRedditAdGroupPayload(flags, "create");
|
|
3708
|
+
const body = { adGroups: [adGroup] };
|
|
3709
|
+
return client.post(`/manage/reddit/ad-groups${query}`, body);
|
|
3710
|
+
}
|
|
3711
|
+
async function updateRedditAdGroup(client, args, flags) {
|
|
3712
|
+
validateFlags(flags, AD_GROUP_UPDATE_FLAGS2, "manage reddit ad-groups update");
|
|
3713
|
+
const adGroupId = requireArg(args, 0, "ad-group-id", "Run: adkit manage reddit ad-groups update <ad-group-id> --status paused");
|
|
3714
|
+
const body = typeof flags.data === "string" ? parseDataFlag(flags, "Use a flat Reddit ad-group changes object") : buildRedditAdGroupPayload(flags, "update");
|
|
3715
|
+
const query = buildRedditMutationQuery(flags);
|
|
3716
|
+
return client.patch(`/manage/reddit/ad-groups/${adGroupId}${query}`, body);
|
|
3717
|
+
}
|
|
3718
|
+
async function listRedditAds(client, _args, flags) {
|
|
3719
|
+
validateFlags(flags, AD_LIST_FLAGS3, "manage reddit ads list");
|
|
3720
|
+
const campaignIds = readStringFlag(flags, "campaign-ids");
|
|
3721
|
+
const adGroupIds = readStringFlag(flags, "ad-group-ids");
|
|
3722
|
+
const extra = { campaignIds, adGroupIds };
|
|
3723
|
+
const query = buildRedditListQuery(flags, extra);
|
|
3724
|
+
return client.get(`/manage/reddit/ads${query}`);
|
|
3725
|
+
}
|
|
3726
|
+
async function getRedditAd(client, args, flags) {
|
|
3727
|
+
validateFlags(flags, ["fields"], "manage reddit ads <ad-id>");
|
|
3728
|
+
const adId = requireArg(args, 0, "ad-id", "Run: adkit manage reddit ads <ad-id>");
|
|
3729
|
+
const query = buildRedditReadQuery(flags);
|
|
3730
|
+
return client.get(`/manage/reddit/ads/${adId}${query}`);
|
|
3731
|
+
}
|
|
3732
|
+
async function createRedditAd(client, _args, flags) {
|
|
3733
|
+
validateFlags(flags, AD_CREATE_FLAGS2, "manage reddit ads create");
|
|
3734
|
+
const query = buildRedditMutationQuery(flags);
|
|
3735
|
+
if (typeof flags.data === "string") {
|
|
3736
|
+
const body2 = parseDataFlag(flags, 'Use { "ads": [...] } for Reddit ad create');
|
|
3737
|
+
return client.post(`/manage/reddit/ads${query}`, body2);
|
|
3738
|
+
}
|
|
3739
|
+
const resolvedFlags = await resolveRedditAdFiles(client, flags, "create");
|
|
3740
|
+
const ad = buildRedditAdPayload(resolvedFlags, "create");
|
|
3741
|
+
const body = { ads: [ad] };
|
|
3742
|
+
return client.post(`/manage/reddit/ads${query}`, body);
|
|
3743
|
+
}
|
|
3744
|
+
async function updateRedditAd(client, args, flags) {
|
|
3745
|
+
validateFlags(flags, AD_UPDATE_FLAGS2, "manage reddit ads update");
|
|
3746
|
+
const adId = requireArg(args, 0, "ad-id", "Run: adkit manage reddit ads update <ad-id> --status paused");
|
|
3747
|
+
const resolvedFlags = typeof flags.data === "string" ? flags : await resolveRedditAdFiles(client, flags, "update");
|
|
3748
|
+
const body = typeof flags.data === "string" ? parseDataFlag(flags, "Use a flat Reddit ad changes object") : buildRedditAdPayload(resolvedFlags, "update");
|
|
3749
|
+
const query = buildRedditMutationQuery(flags);
|
|
3750
|
+
return client.patch(`/manage/reddit/ads/${adId}${query}`, body);
|
|
3751
|
+
}
|
|
3752
|
+
async function listRedditResults(client, _args, flags) {
|
|
3753
|
+
validateFlags(flags, RESULTS_FLAGS2, "manage reddit results");
|
|
3754
|
+
const accountId = readStringFlag(flags, "account");
|
|
3755
|
+
const level = requireFlag(flags, "level", "Use --level account, campaigns, ad-groups, or ads");
|
|
3756
|
+
const period = readStringFlag(flags, "period");
|
|
3757
|
+
const from = readStringFlag(flags, "from");
|
|
3758
|
+
const to = readStringFlag(flags, "to");
|
|
3759
|
+
const fields = readStringFlag(flags, "fields");
|
|
3760
|
+
const breakdowns = readStringFlag(flags, "breakdowns");
|
|
3761
|
+
const campaignIds = readStringFlag(flags, "campaign-ids");
|
|
3762
|
+
const adGroupIds = readStringFlag(flags, "ad-group-ids");
|
|
3763
|
+
const adIds = readStringFlag(flags, "ad-ids");
|
|
3764
|
+
const sort = readStringFlag(flags, "sort");
|
|
3765
|
+
const sortDirection = readStringFlag(flags, "sort-direction");
|
|
3766
|
+
const limit = readStringFlag(flags, "limit");
|
|
3767
|
+
const offset = readStringFlag(flags, "offset");
|
|
3768
|
+
const raw = flags.raw === true ? "true" : readStringFlag(flags, "raw");
|
|
3769
|
+
const query = queryString({
|
|
3770
|
+
accountId,
|
|
3771
|
+
level,
|
|
3772
|
+
period,
|
|
3773
|
+
from,
|
|
3774
|
+
to,
|
|
3775
|
+
fields,
|
|
3776
|
+
breakdowns,
|
|
3777
|
+
campaignIds,
|
|
3778
|
+
adGroupIds,
|
|
3779
|
+
adIds,
|
|
3780
|
+
sort,
|
|
3781
|
+
sortDirection,
|
|
3782
|
+
limit,
|
|
3783
|
+
offset,
|
|
3784
|
+
raw
|
|
3785
|
+
});
|
|
3786
|
+
return client.get(`/manage/reddit/results${query}`);
|
|
3787
|
+
}
|
|
3788
|
+
async function listRedditAccountResource(client, args, flags, resource) {
|
|
3789
|
+
validateFlags(flags, ACCOUNT_RESOURCE_FLAGS, `manage reddit accounts <account-id> ${resource}`);
|
|
3790
|
+
const accountId = requireArg(args, 0, "account-id", `Run: adkit manage reddit accounts <account-id> ${resource}`);
|
|
3791
|
+
const limit = readStringFlag(flags, "limit");
|
|
3792
|
+
const offset = readStringFlag(flags, "offset");
|
|
3793
|
+
const query = queryString({ limit, offset });
|
|
3794
|
+
return client.get(`/manage/reddit/accounts/${accountId}/${resource}${query}`);
|
|
3795
|
+
}
|
|
3796
|
+
function buildRedditListQuery(flags, extra = {}) {
|
|
3797
|
+
const accountId = readStringFlag(flags, "account");
|
|
3798
|
+
const fields = readStringFlag(flags, "fields");
|
|
3799
|
+
const limit = readStringFlag(flags, "limit");
|
|
3800
|
+
const offset = readStringFlag(flags, "offset");
|
|
3801
|
+
return queryString({ accountId, ...extra, fields, limit, offset });
|
|
3802
|
+
}
|
|
3803
|
+
function buildRedditReadQuery(flags) {
|
|
3804
|
+
const accountId = readStringFlag(flags, "account");
|
|
3805
|
+
const fields = readStringFlag(flags, "fields");
|
|
3806
|
+
return queryString({ accountId, fields });
|
|
3807
|
+
}
|
|
3808
|
+
function buildRedditMutationQuery(flags) {
|
|
3809
|
+
const accountId = readStringFlag(flags, "account");
|
|
3810
|
+
const publish = flags.publish === true ? "true" : void 0;
|
|
3811
|
+
return queryString({ accountId, publish });
|
|
3812
|
+
}
|
|
3813
|
+
function buildRedditCampaignPayload(flags, mode) {
|
|
3814
|
+
const payload = {};
|
|
3815
|
+
if (mode === "create") {
|
|
3816
|
+
payload.name = requireFlag(flags, "name", 'Run: adkit manage reddit campaigns create --name "Traffic" --objective clicks');
|
|
3817
|
+
payload.objective = readRedditCampaignObjective(flags);
|
|
3818
|
+
if (typeof flags.optimization === "string") payload.optimization = validateRedditCampaignOptimization(flags.optimization);
|
|
3819
|
+
} else if (typeof flags.name === "string") payload.name = flags.name;
|
|
3820
|
+
if (typeof flags.status === "string") payload.status = readRedditStatus(flags.status);
|
|
3821
|
+
const lifetime = readNumberFlag3(flags, "budget-lifetime");
|
|
3822
|
+
if (lifetime !== void 0) payload.budget = { lifetime };
|
|
3823
|
+
if (typeof flags["bid-strategy"] === "string") payload.bidStrategy = flags["bid-strategy"];
|
|
3824
|
+
if (typeof flags["start-date"] === "string") payload.startDate = flags["start-date"];
|
|
3825
|
+
if (typeof flags["end-date"] === "string") payload.endDate = flags["end-date"];
|
|
3826
|
+
if (typeof flags["funding-instrument"] === "string") payload.fundingInstrumentId = flags["funding-instrument"];
|
|
3827
|
+
if (typeof flags.pixel === "string") payload.pixelId = flags.pixel;
|
|
3828
|
+
return payload;
|
|
3829
|
+
}
|
|
3830
|
+
function buildRedditAdGroupPayload(flags, mode) {
|
|
3831
|
+
const payload = {};
|
|
3832
|
+
if (mode === "create") {
|
|
3833
|
+
payload.campaignId = requireFlag(flags, "campaign", 'Run: adkit manage reddit ad-groups create --campaign <campaign-id> --name "Poland"');
|
|
3834
|
+
payload.name = requireFlag(flags, "name", 'Run: adkit manage reddit ad-groups create --campaign <campaign-id> --name "Poland"');
|
|
3835
|
+
} else if (typeof flags.name === "string") payload.name = flags.name;
|
|
3836
|
+
if (typeof flags.status === "string") payload.status = readRedditStatus(flags.status);
|
|
3837
|
+
const daily = readNumberFlag3(flags, "budget-daily");
|
|
3838
|
+
if (daily !== void 0) payload.budget = { daily };
|
|
3839
|
+
const bidAmount = readNumberFlag3(flags, "bid-amount");
|
|
3840
|
+
if (bidAmount !== void 0) payload.bidAmount = bidAmount;
|
|
3841
|
+
if (typeof flags["bid-strategy"] === "string") payload.bidStrategy = flags["bid-strategy"];
|
|
3842
|
+
if (mode === "create" && typeof flags.optimization === "string") payload.optimization = flags.optimization;
|
|
3843
|
+
if (typeof flags["start-date"] === "string") payload.startDate = flags["start-date"];
|
|
3844
|
+
if (typeof flags["end-date"] === "string") payload.endDate = flags["end-date"];
|
|
3845
|
+
if (typeof flags.pixel === "string") payload.pixelId = flags.pixel;
|
|
3846
|
+
return payload;
|
|
3847
|
+
}
|
|
3848
|
+
function buildRedditAdPayload(flags, mode) {
|
|
3849
|
+
const payload = {};
|
|
3850
|
+
if (mode === "create") payload.adGroupId = requireFlag(flags, "ad-group", "Run: adkit manage reddit ads create --ad-group <ad-group-id> --ad-type image");
|
|
3851
|
+
else if (typeof flags["ad-group"] === "string") payload.adGroupId = flags["ad-group"];
|
|
3852
|
+
if (typeof flags.name === "string") payload.name = flags.name;
|
|
3853
|
+
if (typeof flags.status === "string") payload.status = readRedditStatus(flags.status);
|
|
3854
|
+
if (typeof flags.profile === "string") payload.profileId = flags.profile;
|
|
3855
|
+
if (mode === "create" && typeof flags["existing-post"] === "string") {
|
|
3856
|
+
const incompatibleFlags = ["ad-type", "primary-text", "file", "media-id", "thumbnail-file", "thumbnail-id"].filter((key) => flags[key] !== void 0);
|
|
3857
|
+
if (incompatibleFlags.length > 0) throw new CliError("INVALID_VALUE", "--existing-post cannot be combined with new creative content", "Keep --headline and optional --url/--cta, and remove --ad-type/--primary-text/--media-id/--thumbnail-id");
|
|
3858
|
+
const headline = requireSingleTextFlag(flags, "headline", "Promoting an existing Reddit post still needs one ad headline");
|
|
3859
|
+
const creative = { headlines: [headline] };
|
|
3860
|
+
if (typeof flags.url === "string") creative.url = flags.url;
|
|
3861
|
+
if (typeof flags.cta === "string") creative.cta = flags.cta;
|
|
3862
|
+
return { ...payload, existingPostId: flags["existing-post"], creative };
|
|
3863
|
+
}
|
|
3864
|
+
const hasCreativeFlags = ["ad-type", "headline", "primary-text", "media-id", "thumbnail-id", "url", "cta"].some((key) => flags[key] !== void 0) || mode === "update" && flags.profile !== void 0;
|
|
3865
|
+
if (!hasCreativeFlags) {
|
|
3866
|
+
if (mode === "create") throw new CliError("MISSING_FLAG", "A Reddit ad needs creative content or an existing post", "Use --ad-type with its creative flags, --existing-post, or --data");
|
|
3867
|
+
return payload;
|
|
3868
|
+
}
|
|
3869
|
+
const adType = readRedditAdType(flags);
|
|
3870
|
+
if (adType === "carousel") throw new CliError("INVALID_VALUE", "Carousel ads need --data", "Pass canonical carouselCards and creative fields through --data");
|
|
3871
|
+
payload.adType = adType;
|
|
3872
|
+
payload.creative = buildRedditCreative(flags, adType);
|
|
3873
|
+
return payload;
|
|
3874
|
+
}
|
|
3875
|
+
async function resolveRedditAdFiles(client, flags, mode) {
|
|
3876
|
+
const file = readOptionalSingleFlag(flags, "file");
|
|
3877
|
+
const thumbnailFile = readOptionalSingleFlag(flags, "thumbnail-file");
|
|
3878
|
+
if (file && flags["media-id"] !== void 0) throw new CliError("INVALID_VALUE", "Use either --file or --media-id", "Pass one source for the Reddit image or video");
|
|
3879
|
+
if (thumbnailFile && flags["thumbnail-id"] !== void 0) throw new CliError("INVALID_VALUE", "Use either --thumbnail-file or --thumbnail-id", "Pass one source for the Reddit video thumbnail");
|
|
3880
|
+
if (!file && !thumbnailFile) return flags;
|
|
3881
|
+
const adType = readStringFlag(flags, "ad-type");
|
|
3882
|
+
if (adType !== "image" && adType !== "video") throw new CliError("INVALID_VALUE", "Local files require --ad-type image or video", "Use --data for carousel ads; freeform and existing-post ads do not accept media");
|
|
3883
|
+
if (adType === "image" && thumbnailFile) throw new CliError("INVALID_VALUE", "--thumbnail-file is only valid for video ads");
|
|
3884
|
+
const validationFlags = {
|
|
3885
|
+
...flags,
|
|
3886
|
+
...file ? { "media-id": "pending_upload" } : {},
|
|
3887
|
+
...thumbnailFile ? { "thumbnail-id": "pending_upload" } : {}
|
|
3888
|
+
};
|
|
3889
|
+
buildRedditAdPayload(validationFlags, mode);
|
|
3890
|
+
const mediaId = file ? await uploadRedditTemporaryFile(client, file) : void 0;
|
|
3891
|
+
const thumbnailId = thumbnailFile ? await uploadRedditTemporaryFile(client, thumbnailFile) : void 0;
|
|
3892
|
+
return {
|
|
3893
|
+
...flags,
|
|
3894
|
+
...mediaId ? { "media-id": mediaId } : {},
|
|
3895
|
+
...thumbnailId ? { "thumbnail-id": thumbnailId } : {}
|
|
3896
|
+
};
|
|
3897
|
+
}
|
|
3898
|
+
async function uploadRedditTemporaryFile(client, filePath) {
|
|
3899
|
+
const resolvedPath = resolve3(filePath);
|
|
3900
|
+
const filename = basename3(resolvedPath);
|
|
3901
|
+
const contentType = REDDIT_MEDIA_CONTENT_TYPES[extname3(filename).toLowerCase()];
|
|
3902
|
+
if (!contentType) throw new CliError("INVALID_VALUE", `Unsupported Reddit media file: ${filename}`, "Use .jpg, .jpeg, .png, .gif, .webp, .mp4, .mov, or .webm");
|
|
3903
|
+
let data;
|
|
3904
|
+
try {
|
|
3905
|
+
data = await readFile(resolvedPath);
|
|
3906
|
+
} catch (error) {
|
|
3907
|
+
const code = error instanceof Error && "code" in error ? error.code : void 0;
|
|
3908
|
+
if (code === "ENOENT") throw new CliError("INVALID_VALUE", `File not found: ${resolvedPath}`);
|
|
3909
|
+
throw error;
|
|
3910
|
+
}
|
|
3911
|
+
const response = await client.post("/manage/media/upload-url", { contentType, filename, sizeBytes: data.byteLength });
|
|
3912
|
+
const target = readTemporaryUploadTarget(response);
|
|
3913
|
+
const signal = AbortSignal.timeout(12e4);
|
|
3914
|
+
const body = new Uint8Array(data);
|
|
3915
|
+
let uploadResponse;
|
|
3916
|
+
try {
|
|
3917
|
+
uploadResponse = await fetch(target.uploadUrl, { method: "PUT", headers: target.headers, body, signal });
|
|
3918
|
+
} catch (error) {
|
|
3919
|
+
if (error instanceof DOMException && (error.name === "AbortError" || error.name === "TimeoutError")) throw new CliError("NETWORK_ERROR", `Upload timed out for ${filename}`, "Retry the command");
|
|
3920
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
3921
|
+
throw new CliError("NETWORK_ERROR", `Could not upload ${filename} (${detail})`, "Check your connection and retry the command");
|
|
3922
|
+
}
|
|
3923
|
+
await uploadResponse.arrayBuffer().catch(() => new ArrayBuffer(0));
|
|
3924
|
+
if (!uploadResponse.ok) throw new CliError("SERVER_ERROR", `Could not upload ${filename} (HTTP ${String(uploadResponse.status)})`, "Retry the command");
|
|
3925
|
+
return target.uploadId;
|
|
3926
|
+
}
|
|
3927
|
+
function readTemporaryUploadTarget(value) {
|
|
3928
|
+
if (!isObject(value) || !isObject(value.upload)) throw new CliError("SERVER_ERROR", "AdKit did not return a temporary upload target");
|
|
3929
|
+
const { upload } = value;
|
|
3930
|
+
if (typeof upload.uploadId !== "string" || typeof upload.uploadUrl !== "string" || !isStringRecord(upload.headers)) throw new CliError("SERVER_ERROR", "AdKit returned an invalid temporary upload target");
|
|
3931
|
+
return { headers: upload.headers, uploadId: upload.uploadId, uploadUrl: upload.uploadUrl };
|
|
3932
|
+
}
|
|
3933
|
+
function readOptionalSingleFlag(flags, key) {
|
|
3934
|
+
const values = collectFlagValues(flags, key);
|
|
3935
|
+
if (values.length > 1) throw new CliError("INVALID_VALUE", `Use one --${key} value`);
|
|
3936
|
+
return values[0];
|
|
3937
|
+
}
|
|
3938
|
+
function isObject(value) {
|
|
3939
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3940
|
+
}
|
|
3941
|
+
function isStringRecord(value) {
|
|
3942
|
+
return isObject(value) && Object.values(value).every((item) => typeof item === "string");
|
|
3943
|
+
}
|
|
3944
|
+
function buildRedditCreative(flags, adType) {
|
|
3945
|
+
const headline = requireSingleTextFlag(flags, "headline", "Reddit ads need exactly one --headline");
|
|
3946
|
+
const creative = { headlines: [headline] };
|
|
3947
|
+
if (adType === "freeform") creative.primaryTexts = [requireSingleTextFlag(flags, "primary-text", "Freeform Reddit ads need exactly one --primary-text")];
|
|
3948
|
+
if (adType === "image") creative.media = [{ id: requireFlag(flags, "media-id", "Image Reddit ads need --media-id <upload-id>"), role: "image" }];
|
|
3949
|
+
if (adType === "video") creative.media = [{ id: requireFlag(flags, "media-id", "Video Reddit ads need --media-id <upload-id>"), role: "video" }, { id: requireFlag(flags, "thumbnail-id", "Video Reddit ads need --thumbnail-id <upload-id>"), role: "thumbnail" }];
|
|
3950
|
+
if (adType === "image" || adType === "video") creative.url = requireFlag(flags, "url", `${adType === "image" ? "Image" : "Video"} Reddit ads need --url <destination>`);
|
|
3951
|
+
if (typeof flags.cta === "string") creative.cta = flags.cta;
|
|
3952
|
+
return creative;
|
|
3953
|
+
}
|
|
3954
|
+
function requireSingleTextFlag(flags, key, hint) {
|
|
3955
|
+
const values = collectFlagValues(flags, key);
|
|
3956
|
+
if (values.length !== 1) throw new CliError("INVALID_VALUE", `Reddit requires exactly one --${key}`, hint);
|
|
3957
|
+
return values[0];
|
|
3958
|
+
}
|
|
3959
|
+
function readNumberFlag3(flags, key) {
|
|
3960
|
+
const value = readStringFlag(flags, key);
|
|
3961
|
+
if (value === void 0) return void 0;
|
|
3962
|
+
const parsed = Number(value);
|
|
3963
|
+
if (!Number.isFinite(parsed)) throw new CliError("INVALID_VALUE", `Invalid number for --${key}: ${value}`, `Use a numeric value for --${key}`);
|
|
3964
|
+
return parsed;
|
|
3965
|
+
}
|
|
3966
|
+
function readRedditCampaignObjective(flags) {
|
|
3967
|
+
const objective = requireFlag(flags, "objective", "Use brand_awareness, clicks, lead_generation, or sales");
|
|
3968
|
+
if (objective === "brand_awareness" || objective === "clicks" || objective === "lead_generation" || objective === "sales") return objective;
|
|
3969
|
+
throw new CliError("INVALID_VALUE", `Unsupported Reddit objective: ${objective}`, "Use brand_awareness, clicks, lead_generation, or sales");
|
|
3970
|
+
}
|
|
3971
|
+
function validateRedditCampaignOptimization(value) {
|
|
3972
|
+
if (value === "clicks" || value === "video_view_6s") return value;
|
|
3973
|
+
throw new CliError("INVALID_VALUE", `Unsupported Reddit campaign optimization: ${value}`, "Use clicks or video_view_6s");
|
|
3974
|
+
}
|
|
3975
|
+
function readRedditStatus(status2) {
|
|
3976
|
+
if (status2 === "active" || status2 === "paused") return status2;
|
|
3977
|
+
throw new CliError("INVALID_VALUE", `Unsupported Reddit status: ${status2}`, "Use active or paused");
|
|
3978
|
+
}
|
|
3979
|
+
function readRedditAdType(flags) {
|
|
3980
|
+
const adType = requireFlag(flags, "ad-type", "Creative changes need --ad-type freeform, image, video, or carousel");
|
|
3981
|
+
if (adType === "freeform" || adType === "image" || adType === "video" || adType === "carousel") return adType;
|
|
3982
|
+
throw new CliError("INVALID_VALUE", `Unsupported Reddit ad type: ${adType}`, "Use freeform, image, video, or carousel");
|
|
3983
|
+
}
|
|
3984
|
+
function readStringFlag(flags, key) {
|
|
3985
|
+
return typeof flags[key] === "string" ? flags[key] : void 0;
|
|
3602
3986
|
}
|
|
3603
3987
|
|
|
3604
3988
|
// src/commands/linkedin.ts
|
|
3605
3989
|
import { readFileSync as readFileSync4 } from "node:fs";
|
|
3606
|
-
import { basename as
|
|
3990
|
+
import { basename as basename4, extname as extname4, resolve as resolve4 } from "node:path";
|
|
3607
3991
|
var MEDIA_UPLOAD_FLAGS3 = ["file", "url", "base64", "adkit-media", "temporary-upload", "filename", "content-type", "name"];
|
|
3608
|
-
var
|
|
3609
|
-
var
|
|
3610
|
-
var
|
|
3611
|
-
var
|
|
3612
|
-
var
|
|
3613
|
-
var
|
|
3614
|
-
var
|
|
3615
|
-
var
|
|
3616
|
-
var
|
|
3992
|
+
var CAMPAIGN_LIST_FLAGS4 = ["campaign-ids", "limit", "offset"];
|
|
3993
|
+
var AD_GROUP_LIST_FLAGS4 = ["campaign-ids", "ad-group-ids", "limit", "offset"];
|
|
3994
|
+
var AD_LIST_FLAGS4 = ["campaign-ids", "limit", "offset"];
|
|
3995
|
+
var CAMPAIGN_CREATE_FLAGS4 = ["name", "status", "budget-daily", "budget-lifetime", "end-date"];
|
|
3996
|
+
var AD_GROUP_CREATE_FLAGS3 = ["campaign", "name", "objective", "status", "budget-daily", "budget-lifetime", "bid-strategy", "optimization", "bid-amount"];
|
|
3997
|
+
var AD_CREATE_FLAGS3 = ["ad-group", "existing-post", "name", "status"];
|
|
3998
|
+
var CAMPAIGN_UPDATE_FLAGS4 = ["name", "status", "budget-lifetime", "end-date"];
|
|
3999
|
+
var AD_GROUP_UPDATE_FLAGS3 = ["name", "status", "budget-daily", "budget-lifetime", "start-date", "end-date", "bid-strategy", "optimization", "bid-amount"];
|
|
4000
|
+
var AD_UPDATE_FLAGS3 = ["name", "status"];
|
|
3617
4001
|
var TARGETING_SEARCH_FLAGS = ["account", "facet", "query", "limit"];
|
|
3618
|
-
var
|
|
4002
|
+
var RESULTS_FLAGS3 = ["level", "period", "fields", "breakdowns", "from", "to", "sort", "limit", "offset"];
|
|
3619
4003
|
async function listLinkedInAccounts(client, _args, flags) {
|
|
3620
4004
|
validateFlags(flags, [], "manage linkedin accounts list");
|
|
3621
4005
|
return client.get("/manage/linkedin/accounts");
|
|
@@ -3654,7 +4038,7 @@ async function getLinkedInMedia(client, args, flags) {
|
|
|
3654
4038
|
return client.get(`/manage/linkedin/media/${id}${qs}`);
|
|
3655
4039
|
}
|
|
3656
4040
|
async function listLinkedInCampaigns(client, _args, flags) {
|
|
3657
|
-
validateFlags(flags,
|
|
4041
|
+
validateFlags(flags, CAMPAIGN_LIST_FLAGS4, "manage linkedin campaigns list");
|
|
3658
4042
|
const qs = buildLinkedInListQuery(flags, ["campaign-ids"]);
|
|
3659
4043
|
return client.get(`/manage/linkedin/campaigns${qs}`);
|
|
3660
4044
|
}
|
|
@@ -3665,7 +4049,7 @@ async function getLinkedInCampaign(client, args, flags) {
|
|
|
3665
4049
|
return client.get(`/manage/linkedin/campaigns/${id}${qs}`);
|
|
3666
4050
|
}
|
|
3667
4051
|
async function createLinkedInCampaign(client, _args, flags) {
|
|
3668
|
-
validateFlags(flags,
|
|
4052
|
+
validateFlags(flags, CAMPAIGN_CREATE_FLAGS4, "manage linkedin campaigns create");
|
|
3669
4053
|
const qs = buildLinkedInMutationQuery(flags);
|
|
3670
4054
|
if (typeof flags.data === "string") {
|
|
3671
4055
|
const body2 = parseDataFlag(flags, 'Use { "campaigns": [...] } for LinkedIn campaign create');
|
|
@@ -3675,14 +4059,14 @@ async function createLinkedInCampaign(client, _args, flags) {
|
|
|
3675
4059
|
return client.post(`/manage/linkedin/campaigns${qs}`, body);
|
|
3676
4060
|
}
|
|
3677
4061
|
async function updateLinkedInCampaign(client, args, flags) {
|
|
3678
|
-
validateFlags(flags,
|
|
4062
|
+
validateFlags(flags, CAMPAIGN_UPDATE_FLAGS4, "manage linkedin campaigns update");
|
|
3679
4063
|
const id = requireArg(args, 0, "campaign-id", "Run: adkit manage linkedin campaigns update <campaign-id> --status paused");
|
|
3680
4064
|
const qs = buildLinkedInMutationQuery(flags);
|
|
3681
4065
|
const body = typeof flags.data === "string" ? parseDataFlag(flags, "Use a flat LinkedIn campaign changes object") : buildLinkedInCampaignUpdatePayload(flags);
|
|
3682
4066
|
return client.patch(`/manage/linkedin/campaigns/${id}${qs}`, body);
|
|
3683
4067
|
}
|
|
3684
4068
|
async function listLinkedInAdGroups(client, _args, flags) {
|
|
3685
|
-
validateFlags(flags,
|
|
4069
|
+
validateFlags(flags, AD_GROUP_LIST_FLAGS4, "manage linkedin ad-groups list");
|
|
3686
4070
|
const qs = buildLinkedInListQuery(flags, ["campaign-ids", "ad-group-ids"]);
|
|
3687
4071
|
return client.get(`/manage/linkedin/ad-groups${qs}`);
|
|
3688
4072
|
}
|
|
@@ -3693,7 +4077,7 @@ async function getLinkedInAdGroup(client, args, flags) {
|
|
|
3693
4077
|
return client.get(`/manage/linkedin/ad-groups/${id}${qs}`);
|
|
3694
4078
|
}
|
|
3695
4079
|
async function createLinkedInAdGroup(client, _args, flags) {
|
|
3696
|
-
validateFlags(flags,
|
|
4080
|
+
validateFlags(flags, AD_GROUP_CREATE_FLAGS3, "manage linkedin ad-groups create");
|
|
3697
4081
|
const qs = buildLinkedInMutationQuery(flags);
|
|
3698
4082
|
if (typeof flags.data === "string") {
|
|
3699
4083
|
const body2 = parseDataFlag(flags, 'Use { "adGroups": [...] } for LinkedIn ad group create');
|
|
@@ -3703,14 +4087,14 @@ async function createLinkedInAdGroup(client, _args, flags) {
|
|
|
3703
4087
|
return client.post(`/manage/linkedin/ad-groups${qs}`, body);
|
|
3704
4088
|
}
|
|
3705
4089
|
async function updateLinkedInAdGroup(client, args, flags) {
|
|
3706
|
-
validateFlags(flags,
|
|
4090
|
+
validateFlags(flags, AD_GROUP_UPDATE_FLAGS3, "manage linkedin ad-groups update");
|
|
3707
4091
|
const id = requireArg(args, 0, "ad-group-id", "Run: adkit manage linkedin ad-groups update <ad-group-id> --status paused");
|
|
3708
4092
|
const qs = buildLinkedInMutationQuery(flags);
|
|
3709
4093
|
const body = typeof flags.data === "string" ? parseDataFlag(flags, "Use a flat LinkedIn ad group changes object") : buildLinkedInAdGroupUpdatePayload(flags);
|
|
3710
4094
|
return client.patch(`/manage/linkedin/ad-groups/${id}${qs}`, body);
|
|
3711
4095
|
}
|
|
3712
4096
|
async function listLinkedInAds(client, _args, flags) {
|
|
3713
|
-
validateFlags(flags,
|
|
4097
|
+
validateFlags(flags, AD_LIST_FLAGS4, "manage linkedin ads list");
|
|
3714
4098
|
const qs = buildLinkedInListQuery(flags, ["campaign-ids"]);
|
|
3715
4099
|
return client.get(`/manage/linkedin/ads${qs}`);
|
|
3716
4100
|
}
|
|
@@ -3721,7 +4105,7 @@ async function getLinkedInAd(client, args, flags) {
|
|
|
3721
4105
|
return client.get(`/manage/linkedin/ads/${id}${qs}`);
|
|
3722
4106
|
}
|
|
3723
4107
|
async function createLinkedInAd(client, _args, flags) {
|
|
3724
|
-
validateFlags(flags,
|
|
4108
|
+
validateFlags(flags, AD_CREATE_FLAGS3, "manage linkedin ads create");
|
|
3725
4109
|
const qs = buildLinkedInMutationQuery(flags);
|
|
3726
4110
|
if (typeof flags.data === "string") {
|
|
3727
4111
|
const body2 = parseDataFlag(flags, 'Use { "ads": [...] } for LinkedIn ad create');
|
|
@@ -3732,7 +4116,7 @@ async function createLinkedInAd(client, _args, flags) {
|
|
|
3732
4116
|
return client.post(`/manage/linkedin/ads${qs}`, body);
|
|
3733
4117
|
}
|
|
3734
4118
|
async function updateLinkedInAd(client, args, flags) {
|
|
3735
|
-
validateFlags(flags,
|
|
4119
|
+
validateFlags(flags, AD_UPDATE_FLAGS3, "manage linkedin ads update");
|
|
3736
4120
|
const id = requireArg(args, 0, "ad-id", "Run: adkit manage linkedin ads update <ad-id> --status paused");
|
|
3737
4121
|
const qs = buildLinkedInMutationQuery(flags);
|
|
3738
4122
|
const body = typeof flags.data === "string" ? parseDataFlag(flags, "Use a flat LinkedIn ad changes object (name/status only)") : buildLinkedInAdUpdatePayload(flags);
|
|
@@ -3748,7 +4132,7 @@ async function searchLinkedInTargeting(client, _args, flags) {
|
|
|
3748
4132
|
return client.get(`/manage/linkedin/targeting-search${qs}`);
|
|
3749
4133
|
}
|
|
3750
4134
|
async function listLinkedInResults(client, _args, flags) {
|
|
3751
|
-
validateFlags(flags,
|
|
4135
|
+
validateFlags(flags, RESULTS_FLAGS3, "manage linkedin results");
|
|
3752
4136
|
const accountId = typeof flags.account === "string" ? flags.account : void 0;
|
|
3753
4137
|
const level = requireFlag(flags, "level", "Use --level account, campaigns, ad-groups, or ads");
|
|
3754
4138
|
const query = queryString({
|
|
@@ -3801,7 +4185,7 @@ function buildLinkedInAdGroupPayload(flags) {
|
|
|
3801
4185
|
if (typeof flags.objective === "string") payload.objective = flags.objective;
|
|
3802
4186
|
if (typeof flags["bid-strategy"] === "string") payload.bidStrategy = flags["bid-strategy"];
|
|
3803
4187
|
if (typeof flags.optimization === "string") payload.optimization = flags.optimization;
|
|
3804
|
-
const bidAmount =
|
|
4188
|
+
const bidAmount = readNumberFlag4(flags, "bid-amount");
|
|
3805
4189
|
if (bidAmount !== void 0) payload.bidAmount = bidAmount;
|
|
3806
4190
|
if (typeof flags.status === "string") payload.status = flags.status;
|
|
3807
4191
|
return payload;
|
|
@@ -3834,7 +4218,7 @@ function buildLinkedInAdGroupUpdatePayload(flags) {
|
|
|
3834
4218
|
if (budget) payload.budget = budget;
|
|
3835
4219
|
if (typeof flags["bid-strategy"] === "string") payload.bidStrategy = flags["bid-strategy"];
|
|
3836
4220
|
if (typeof flags.optimization === "string") payload.optimization = flags.optimization;
|
|
3837
|
-
const bidAmount =
|
|
4221
|
+
const bidAmount = readNumberFlag4(flags, "bid-amount");
|
|
3838
4222
|
if (bidAmount !== void 0) payload.bidAmount = bidAmount;
|
|
3839
4223
|
return payload;
|
|
3840
4224
|
}
|
|
@@ -3845,13 +4229,13 @@ function buildLinkedInAdUpdatePayload(flags) {
|
|
|
3845
4229
|
return payload;
|
|
3846
4230
|
}
|
|
3847
4231
|
function buildLinkedInBudget(flags) {
|
|
3848
|
-
const daily =
|
|
3849
|
-
const lifetime =
|
|
4232
|
+
const daily = readNumberFlag4(flags, "budget-daily");
|
|
4233
|
+
const lifetime = readNumberFlag4(flags, "budget-lifetime");
|
|
3850
4234
|
if (daily === void 0 && lifetime === void 0) return void 0;
|
|
3851
4235
|
if (daily !== void 0 && lifetime !== void 0) throw new CliError("INVALID_VALUE", "Use either --budget-daily or --budget-lifetime", "LinkedIn accepts one budget mode at a time");
|
|
3852
4236
|
return daily !== void 0 ? { daily } : { lifetime };
|
|
3853
4237
|
}
|
|
3854
|
-
function
|
|
4238
|
+
function readNumberFlag4(flags, key) {
|
|
3855
4239
|
const value = flags[key];
|
|
3856
4240
|
if (value === void 0) return void 0;
|
|
3857
4241
|
if (typeof value !== "string") throw new CliError("INVALID_VALUE", `Use one --${key} value`, `Run: adkit manage linkedin --help`);
|
|
@@ -3868,7 +4252,7 @@ function readSingleFlagValue3(flags, key, duplicateHint) {
|
|
|
3868
4252
|
return typeof value === "string" ? value : void 0;
|
|
3869
4253
|
}
|
|
3870
4254
|
function inferLinkedInMediaContentType(filename) {
|
|
3871
|
-
const extension =
|
|
4255
|
+
const extension = extname4(filename).toLowerCase();
|
|
3872
4256
|
if (extension === ".jpg" || extension === ".jpeg") return "image/jpeg";
|
|
3873
4257
|
if (extension === ".png") return "image/png";
|
|
3874
4258
|
if (extension === ".gif") return "image/gif";
|
|
@@ -3889,8 +4273,8 @@ function buildLinkedInMediaUploadSource(flags) {
|
|
|
3889
4273
|
const contentType = readSingleFlagValue3(flags, "content-type", "Use one --content-type value");
|
|
3890
4274
|
const name = readSingleFlagValue3(flags, "name", "Use one --name value");
|
|
3891
4275
|
if (filePath) {
|
|
3892
|
-
const resolvedPath =
|
|
3893
|
-
const resolvedFilename = filename ??
|
|
4276
|
+
const resolvedPath = resolve4(filePath);
|
|
4277
|
+
const resolvedFilename = filename ?? basename4(resolvedPath);
|
|
3894
4278
|
const resolvedContentType = contentType ?? inferLinkedInMediaContentType(resolvedFilename);
|
|
3895
4279
|
const data = readFileSync4(resolvedPath).toString("base64");
|
|
3896
4280
|
const source2 = { source: "base64", data, filename: resolvedFilename };
|
|
@@ -3924,10 +4308,15 @@ function buildLinkedInMediaUploadSource(flags) {
|
|
|
3924
4308
|
}
|
|
3925
4309
|
|
|
3926
4310
|
// src/commands/x.ts
|
|
3927
|
-
var
|
|
3928
|
-
var
|
|
3929
|
-
var
|
|
3930
|
-
var
|
|
4311
|
+
var CAMPAIGN_LIST_FLAGS5 = ["campaign-ids", "status", "limit", "offset"];
|
|
4312
|
+
var CAMPAIGN_CREATE_FLAGS5 = ["name", "status", "budget-daily", "budget-lifetime", "funding-instrument"];
|
|
4313
|
+
var CAMPAIGN_UPDATE_FLAGS5 = ["name", "status", "budget-daily", "budget-lifetime"];
|
|
4314
|
+
var AD_GROUP_LIST_FLAGS5 = ["campaign-ids", "ad-group-ids", "status", "limit", "offset"];
|
|
4315
|
+
var AD_GROUP_CREATE_FLAGS4 = ["campaign", "name", "objective", "status", "budget-daily", "budget-lifetime", "bid-amount", "bid-strategy", "optimization", "start-date", "end-date"];
|
|
4316
|
+
var AD_GROUP_UPDATE_FLAGS4 = ["name", "status", "budget-daily", "budget-lifetime", "bid-amount", "bid-strategy", "optimization", "start-date", "end-date"];
|
|
4317
|
+
var AD_LIST_FLAGS5 = ["campaign-ids", "ad-group-ids", "ad-ids", "status", "limit", "offset"];
|
|
4318
|
+
var AD_CREATE_FLAGS4 = ["ad-group", "existing-post", "status"];
|
|
4319
|
+
var RESULTS_FLAGS4 = ["level", "from", "to", "fields", "breakdowns", "campaign-ids", "ad-group-ids", "ad-ids", "sort", "sort-direction", "limit", "offset", "raw"];
|
|
3931
4320
|
async function listXAccounts(client, _args, flags) {
|
|
3932
4321
|
validateFlags(flags, [], "manage x accounts list");
|
|
3933
4322
|
return client.get("/manage/x/accounts");
|
|
@@ -3957,23 +4346,30 @@ async function listXPixels(client, args, flags) {
|
|
|
3957
4346
|
const accountId = requireArg(args, 0, "account-id", "Run: adkit manage x accounts <account-id> pixels");
|
|
3958
4347
|
return client.get(`/manage/x/accounts/${accountId}/pixels`);
|
|
3959
4348
|
}
|
|
4349
|
+
async function listXFundingInstruments(client, args, flags) {
|
|
4350
|
+
validateFlags(flags, [], "manage x accounts <account-id> funding-instruments");
|
|
4351
|
+
const accountId = requireArg(args, 0, "account-id", "Run: adkit manage x accounts <account-id> funding-instruments");
|
|
4352
|
+
return client.get(`/manage/x/accounts/${accountId}/funding-instruments`);
|
|
4353
|
+
}
|
|
3960
4354
|
async function updateXAccount(client, args, flags) {
|
|
3961
|
-
validateFlags(flags, ["default-profile", "default-pixel"], "manage x accounts <account-id> update");
|
|
4355
|
+
validateFlags(flags, ["default-profile", "default-pixel", "default-funding-instrument"], "manage x accounts <account-id> update");
|
|
3962
4356
|
const accountId = requireArg(args, 0, "account-id", "Run: adkit manage x accounts <account-id> update --default-profile <profile-id>");
|
|
3963
4357
|
if (typeof flags.data === "string") {
|
|
3964
|
-
const body = parseDataFlag(flags, 'Use { "defaultProfileId": "...", "defaultPixelId": "..." }');
|
|
4358
|
+
const body = parseDataFlag(flags, 'Use { "defaultProfileId": "...", "defaultPixelId": "...", "defaultFundingInstrumentId": "..." }');
|
|
3965
4359
|
return client.patch(`/manage/x/accounts/${accountId}`, body);
|
|
3966
4360
|
}
|
|
3967
4361
|
const defaultProfileId = typeof flags["default-profile"] === "string" ? flags["default-profile"] : void 0;
|
|
3968
4362
|
const defaultPixelId = typeof flags["default-pixel"] === "string" ? flags["default-pixel"] : void 0;
|
|
3969
|
-
|
|
4363
|
+
const defaultFundingInstrumentId = typeof flags["default-funding-instrument"] === "string" ? flags["default-funding-instrument"] : void 0;
|
|
4364
|
+
if (!defaultProfileId && !defaultPixelId && !defaultFundingInstrumentId) throw new CliError("MISSING_FLAG", "Nothing to update", "Pass --default-profile, --default-pixel, --default-funding-instrument, or --data");
|
|
3970
4365
|
return client.patch(`/manage/x/accounts/${accountId}`, {
|
|
3971
4366
|
...defaultProfileId ? { defaultProfileId } : {},
|
|
3972
|
-
...defaultPixelId ? { defaultPixelId } : {}
|
|
4367
|
+
...defaultPixelId ? { defaultPixelId } : {},
|
|
4368
|
+
...defaultFundingInstrumentId ? { defaultFundingInstrumentId } : {}
|
|
3973
4369
|
});
|
|
3974
4370
|
}
|
|
3975
4371
|
async function listXCampaigns(client, _args, flags) {
|
|
3976
|
-
validateXReadFlags(flags,
|
|
4372
|
+
validateXReadFlags(flags, CAMPAIGN_LIST_FLAGS5, "manage x campaigns list");
|
|
3977
4373
|
const query = buildXListQuery(flags, ["campaign-ids"]);
|
|
3978
4374
|
return client.get(`/manage/x/campaigns${query}`);
|
|
3979
4375
|
}
|
|
@@ -3983,8 +4379,25 @@ async function getXCampaign(client, args, flags) {
|
|
|
3983
4379
|
const query = queryString({ accountId: typeof flags.account === "string" ? flags.account : void 0 });
|
|
3984
4380
|
return client.get(`/manage/x/campaigns/${campaignId}${query}`);
|
|
3985
4381
|
}
|
|
4382
|
+
async function createXCampaign(client, _args, flags) {
|
|
4383
|
+
validateFlags(flags, CAMPAIGN_CREATE_FLAGS5, "manage x campaigns create");
|
|
4384
|
+
const query = buildXMutationQuery(flags);
|
|
4385
|
+
if (typeof flags.data === "string") {
|
|
4386
|
+
const body = parseDataFlag(flags, 'Use { "campaigns": [...] } for X campaign create');
|
|
4387
|
+
return client.post(`/manage/x/campaigns${query}`, body);
|
|
4388
|
+
}
|
|
4389
|
+
const campaign = buildXCampaignCreatePayload(flags);
|
|
4390
|
+
return client.post(`/manage/x/campaigns${query}`, { campaigns: [campaign] });
|
|
4391
|
+
}
|
|
4392
|
+
async function updateXCampaign(client, args, flags) {
|
|
4393
|
+
validateFlags(flags, CAMPAIGN_UPDATE_FLAGS5, "manage x campaigns update");
|
|
4394
|
+
const campaignId = requireArg(args, 0, "campaign-id", "Run: adkit manage x campaigns update <campaign-id> --status paused");
|
|
4395
|
+
const query = buildXMutationQuery(flags);
|
|
4396
|
+
const body = typeof flags.data === "string" ? parseDataFlag(flags, "Use a flat X campaign changes object") : buildXCampaignUpdatePayload(flags);
|
|
4397
|
+
return client.patch(`/manage/x/campaigns/${campaignId}${query}`, body);
|
|
4398
|
+
}
|
|
3986
4399
|
async function listXAdGroups(client, _args, flags) {
|
|
3987
|
-
validateXReadFlags(flags,
|
|
4400
|
+
validateXReadFlags(flags, AD_GROUP_LIST_FLAGS5, "manage x ad-groups list");
|
|
3988
4401
|
const query = buildXListQuery(flags, ["campaign-ids", "ad-group-ids"]);
|
|
3989
4402
|
return client.get(`/manage/x/ad-groups${query}`);
|
|
3990
4403
|
}
|
|
@@ -3994,8 +4407,25 @@ async function getXAdGroup(client, args, flags) {
|
|
|
3994
4407
|
const query = queryString({ accountId: typeof flags.account === "string" ? flags.account : void 0 });
|
|
3995
4408
|
return client.get(`/manage/x/ad-groups/${adGroupId}${query}`);
|
|
3996
4409
|
}
|
|
4410
|
+
async function createXAdGroup(client, _args, flags) {
|
|
4411
|
+
validateFlags(flags, AD_GROUP_CREATE_FLAGS4, "manage x ad-groups create");
|
|
4412
|
+
const query = buildXMutationQuery(flags);
|
|
4413
|
+
if (typeof flags.data === "string") {
|
|
4414
|
+
const body = parseDataFlag(flags, 'Use { "adGroups": [...] } for X ad-group create');
|
|
4415
|
+
return client.post(`/manage/x/ad-groups${query}`, body);
|
|
4416
|
+
}
|
|
4417
|
+
const adGroup = buildXAdGroupCreatePayload(flags);
|
|
4418
|
+
return client.post(`/manage/x/ad-groups${query}`, { adGroups: [adGroup] });
|
|
4419
|
+
}
|
|
4420
|
+
async function updateXAdGroup(client, args, flags) {
|
|
4421
|
+
validateFlags(flags, AD_GROUP_UPDATE_FLAGS4, "manage x ad-groups update");
|
|
4422
|
+
const adGroupId = requireArg(args, 0, "ad-group-id", "Run: adkit manage x ad-groups update <ad-group-id> --status paused");
|
|
4423
|
+
const query = buildXMutationQuery(flags);
|
|
4424
|
+
const body = typeof flags.data === "string" ? parseDataFlag(flags, "Use a flat X ad-group changes object") : buildXAdGroupUpdatePayload(flags);
|
|
4425
|
+
return client.patch(`/manage/x/ad-groups/${adGroupId}${query}`, body);
|
|
4426
|
+
}
|
|
3997
4427
|
async function listXAds(client, _args, flags) {
|
|
3998
|
-
validateXReadFlags(flags,
|
|
4428
|
+
validateXReadFlags(flags, AD_LIST_FLAGS5, "manage x ads list");
|
|
3999
4429
|
const query = buildXListQuery(flags, ["campaign-ids", "ad-group-ids", "ad-ids"]);
|
|
4000
4430
|
return client.get(`/manage/x/ads${query}`);
|
|
4001
4431
|
}
|
|
@@ -4005,8 +4435,26 @@ async function getXAd(client, args, flags) {
|
|
|
4005
4435
|
const query = queryString({ accountId: typeof flags.account === "string" ? flags.account : void 0 });
|
|
4006
4436
|
return client.get(`/manage/x/ads/${adId}${query}`);
|
|
4007
4437
|
}
|
|
4438
|
+
async function createXAd(client, _args, flags) {
|
|
4439
|
+
validateFlags(flags, AD_CREATE_FLAGS4, "manage x ads create");
|
|
4440
|
+
const query = buildXMutationQuery(flags);
|
|
4441
|
+
if (typeof flags.data === "string") {
|
|
4442
|
+
const body = parseDataFlag(flags, 'Use { "ads": [...] } for X ad create');
|
|
4443
|
+
return client.post(`/manage/x/ads${query}`, body);
|
|
4444
|
+
}
|
|
4445
|
+
if (typeof flags["existing-post"] !== "string") throw new CliError("MISSING_FLAG", "An X ad needs a creative or an existing Post", "Inline ad: use --data with ads[].creative. Existing Post: pass --existing-post <Post ID>.");
|
|
4446
|
+
const ad = buildXAdCreatePayload(flags);
|
|
4447
|
+
return client.post(`/manage/x/ads${query}`, { ads: [ad] });
|
|
4448
|
+
}
|
|
4449
|
+
async function deleteXAd(client, args, flags) {
|
|
4450
|
+
validateFlags(flags, [], "manage x ads delete");
|
|
4451
|
+
const adId = requireArg(args, 0, "ad-id", "Run: adkit manage x ads delete <promoted-association-id>");
|
|
4452
|
+
const query = buildXMutationQuery(flags);
|
|
4453
|
+
const encodedAdId = encodeURIComponent(adId);
|
|
4454
|
+
return client.delete(`/manage/x/ads/${encodedAdId}${query}`);
|
|
4455
|
+
}
|
|
4008
4456
|
async function listXResults(client, _args, flags) {
|
|
4009
|
-
validateXReadFlags(flags,
|
|
4457
|
+
validateXReadFlags(flags, RESULTS_FLAGS4, "manage x results");
|
|
4010
4458
|
const level = requireFlag(flags, "level", "Use --level campaigns, ad-groups, or ads");
|
|
4011
4459
|
const from = requireFlag(flags, "from", "Use --from YYYY-MM-DD with --to");
|
|
4012
4460
|
const to = requireFlag(flags, "to", "Use --to YYYY-MM-DD with --from");
|
|
@@ -4040,6 +4488,76 @@ function buildXListQuery(flags, filterKeys) {
|
|
|
4040
4488
|
offset: typeof flags.offset === "string" ? flags.offset : void 0
|
|
4041
4489
|
});
|
|
4042
4490
|
}
|
|
4491
|
+
function buildXMutationQuery(flags) {
|
|
4492
|
+
return queryString({ accountId: typeof flags.account === "string" ? flags.account : void 0, publish: flags.publish === true ? "true" : void 0 });
|
|
4493
|
+
}
|
|
4494
|
+
function buildXCampaignCreatePayload(flags) {
|
|
4495
|
+
const name = requireFlag(flags, "name", 'Run: adkit manage x campaigns create --name "Launch" --budget-daily 25');
|
|
4496
|
+
const budget = buildXBudgetFromFlags(flags, { required: true });
|
|
4497
|
+
const payload = {
|
|
4498
|
+
name,
|
|
4499
|
+
budget
|
|
4500
|
+
};
|
|
4501
|
+
if (typeof flags.status === "string") payload.status = flags.status;
|
|
4502
|
+
if (typeof flags["funding-instrument"] === "string") payload.fundingInstrumentId = flags["funding-instrument"];
|
|
4503
|
+
return payload;
|
|
4504
|
+
}
|
|
4505
|
+
function buildXCampaignUpdatePayload(flags) {
|
|
4506
|
+
const payload = {};
|
|
4507
|
+
if (typeof flags.name === "string") payload.name = flags.name;
|
|
4508
|
+
if (typeof flags.status === "string") payload.status = flags.status;
|
|
4509
|
+
const budget = buildXBudgetFromFlags(flags, { required: false });
|
|
4510
|
+
if (budget) payload.budget = budget;
|
|
4511
|
+
return payload;
|
|
4512
|
+
}
|
|
4513
|
+
function buildXAdGroupCreatePayload(flags) {
|
|
4514
|
+
return {
|
|
4515
|
+
...buildXAdGroupUpdatePayload(flags),
|
|
4516
|
+
campaignId: requireFlag(flags, "campaign", "Use --campaign <campaign-id>"),
|
|
4517
|
+
name: requireFlag(flags, "name", 'Use --name "Ad group name"'),
|
|
4518
|
+
objective: requireFlag(flags, "objective", "Use --objective engagements, reach, video_views, or website_clicks"),
|
|
4519
|
+
startDate: requireFlag(flags, "start-date", "Use --start-date YYYY-MM-DD")
|
|
4520
|
+
};
|
|
4521
|
+
}
|
|
4522
|
+
function buildXAdGroupUpdatePayload(flags) {
|
|
4523
|
+
const payload = {};
|
|
4524
|
+
if (typeof flags.name === "string") payload.name = flags.name;
|
|
4525
|
+
if (typeof flags.status === "string") payload.status = flags.status;
|
|
4526
|
+
if (typeof flags["bid-strategy"] === "string") payload.bidStrategy = flags["bid-strategy"];
|
|
4527
|
+
if (typeof flags.optimization === "string") payload.optimization = flags.optimization;
|
|
4528
|
+
if (typeof flags["start-date"] === "string") payload.startDate = flags["start-date"];
|
|
4529
|
+
if (typeof flags["end-date"] === "string") payload.endDate = flags["end-date"];
|
|
4530
|
+
const budget = buildXBudgetFromFlags(flags, { required: false });
|
|
4531
|
+
if (budget) payload.budget = budget;
|
|
4532
|
+
const bidAmount = parseXNumberFlag(flags, "bid-amount");
|
|
4533
|
+
if (bidAmount !== void 0) payload.bidAmount = bidAmount;
|
|
4534
|
+
return payload;
|
|
4535
|
+
}
|
|
4536
|
+
function buildXAdCreatePayload(flags) {
|
|
4537
|
+
const ad = {
|
|
4538
|
+
adGroupId: requireFlag(flags, "ad-group", "Run: adkit manage x ads create --ad-group <line-item-id> --existing-post <Post ID>"),
|
|
4539
|
+
existingPostId: requireFlag(flags, "existing-post", "Run: adkit manage x ads create --existing-post <Post ID>")
|
|
4540
|
+
};
|
|
4541
|
+
if (typeof flags.status === "string") ad.status = flags.status;
|
|
4542
|
+
return ad;
|
|
4543
|
+
}
|
|
4544
|
+
function buildXBudgetFromFlags(flags, options) {
|
|
4545
|
+
const daily = parseXNumberFlag(flags, "budget-daily");
|
|
4546
|
+
const lifetime = parseXNumberFlag(flags, "budget-lifetime");
|
|
4547
|
+
if (daily === void 0 && lifetime === void 0) {
|
|
4548
|
+
if (options.required) throw new CliError("MISSING_FLAG", "Missing required campaign budget", "Use --budget-daily <amount>, --budget-lifetime <amount>, or --data");
|
|
4549
|
+
return void 0;
|
|
4550
|
+
}
|
|
4551
|
+
return { ...daily === void 0 ? {} : { daily }, ...lifetime === void 0 ? {} : { lifetime } };
|
|
4552
|
+
}
|
|
4553
|
+
function parseXNumberFlag(flags, key) {
|
|
4554
|
+
const value = flags[key];
|
|
4555
|
+
if (value === void 0) return void 0;
|
|
4556
|
+
if (typeof value !== "string") throw new CliError("INVALID_VALUE", `Use one --${key} value`, "Pass one numeric amount");
|
|
4557
|
+
const parsed = Number(value);
|
|
4558
|
+
if (!Number.isFinite(parsed)) throw new CliError("INVALID_VALUE", `Invalid number for --${key}: ${value}`, `Use a numeric value for --${key}`);
|
|
4559
|
+
return parsed;
|
|
4560
|
+
}
|
|
4043
4561
|
function validateXReadFlags(flags, allowed, command) {
|
|
4044
4562
|
const permitted = /* @__PURE__ */ new Set(["account", "json", "project", ...allowed]);
|
|
4045
4563
|
const unsupported = Object.keys(flags).filter((flag) => !permitted.has(flag));
|
|
@@ -4080,6 +4598,7 @@ var STATUS_PLATFORMS = [
|
|
|
4080
4598
|
["microsoft", "Microsoft"]
|
|
4081
4599
|
];
|
|
4082
4600
|
var DEFAULT_LABELS = {
|
|
4601
|
+
fundingInstrumentId: "Funding instrument",
|
|
4083
4602
|
identityId: "Identity",
|
|
4084
4603
|
identityType: "Identity type",
|
|
4085
4604
|
instagramUserId: "Instagram user",
|
|
@@ -4204,7 +4723,7 @@ function queryString3(params) {
|
|
|
4204
4723
|
if (entries.length === 0) return "";
|
|
4205
4724
|
return "?" + entries.map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join("&");
|
|
4206
4725
|
}
|
|
4207
|
-
function
|
|
4726
|
+
function readStringFlag2(flags, key) {
|
|
4208
4727
|
const value = flags[key];
|
|
4209
4728
|
return typeof value === "string" ? value : void 0;
|
|
4210
4729
|
}
|
|
@@ -4260,20 +4779,20 @@ function buildAdvertiserQueryString(flags, query) {
|
|
|
4260
4779
|
return queryString3({ industry, category, platform: platform2, query, sort, limit, page });
|
|
4261
4780
|
}
|
|
4262
4781
|
async function listLibraryAds(client, _args, flags) {
|
|
4263
|
-
const platform2 =
|
|
4264
|
-
const status2 =
|
|
4265
|
-
const format =
|
|
4266
|
-
const minDays =
|
|
4267
|
-
const maxDays =
|
|
4268
|
-
const language =
|
|
4269
|
-
const minScore =
|
|
4270
|
-
const minSpend =
|
|
4271
|
-
const query =
|
|
4272
|
-
const advertiser =
|
|
4273
|
-
const ids =
|
|
4274
|
-
const sort =
|
|
4275
|
-
const limit =
|
|
4276
|
-
const page =
|
|
4782
|
+
const platform2 = readStringFlag2(flags, "platform");
|
|
4783
|
+
const status2 = readStringFlag2(flags, "status");
|
|
4784
|
+
const format = readStringFlag2(flags, "format");
|
|
4785
|
+
const minDays = readStringFlag2(flags, "min-days");
|
|
4786
|
+
const maxDays = readStringFlag2(flags, "max-days");
|
|
4787
|
+
const language = readStringFlag2(flags, "language");
|
|
4788
|
+
const minScore = readStringFlag2(flags, "min-score");
|
|
4789
|
+
const minSpend = readStringFlag2(flags, "min-spend");
|
|
4790
|
+
const query = readStringFlag2(flags, "query") ?? readStringFlag2(flags, "search");
|
|
4791
|
+
const advertiser = readStringFlag2(flags, "advertiser");
|
|
4792
|
+
const ids = readStringFlag2(flags, "ids");
|
|
4793
|
+
const sort = readStringFlag2(flags, "sort");
|
|
4794
|
+
const limit = readStringFlag2(flags, "limit");
|
|
4795
|
+
const page = readStringFlag2(flags, "page");
|
|
4277
4796
|
const qs = queryString3({ platform: platform2, status: status2, format, "min-days": minDays, "max-days": maxDays, language, "min-score": minScore, "min-spend": minSpend, query, advertiser, ids, sort, limit, page });
|
|
4278
4797
|
return client.get(`/library/ads${qs}`);
|
|
4279
4798
|
}
|
|
@@ -4284,7 +4803,7 @@ async function getLibraryAd(client, args, _flags) {
|
|
|
4284
4803
|
}
|
|
4285
4804
|
|
|
4286
4805
|
// src/commands/studio.ts
|
|
4287
|
-
import { readFile } from "node:fs/promises";
|
|
4806
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
4288
4807
|
import path2 from "node:path";
|
|
4289
4808
|
var GENERATE_FLAGS = ["ad", "mode", "aspect", "model", "quality", "quantity", "instructions", "audience", "colors", "ref", "title", "fields", "wait"];
|
|
4290
4809
|
var ADS_LIST_FLAGS = ["fields", "status", "format", "search", "limit", "offset"];
|
|
@@ -4494,7 +5013,7 @@ async function uploadStudioFile(client, adId, filePath) {
|
|
|
4494
5013
|
const filename = path2.basename(resolved);
|
|
4495
5014
|
let data;
|
|
4496
5015
|
try {
|
|
4497
|
-
data = await
|
|
5016
|
+
data = await readFile2(resolved);
|
|
4498
5017
|
} catch (err) {
|
|
4499
5018
|
const code = err instanceof Error && "code" in err ? err.code : void 0;
|
|
4500
5019
|
if (code === "ENOENT") throw new CliError("INVALID_VALUE", `File not found: ${resolved}`);
|
|
@@ -4563,8 +5082,8 @@ async function pollMedia(client, mediaIds, fields) {
|
|
|
4563
5082
|
if (!currentMedia || typeof currentMedia.status !== "string") throw new CliError("SERVER_ERROR", `Invalid media response while polling ${id}`);
|
|
4564
5083
|
media = currentMedia;
|
|
4565
5084
|
if (currentMedia.status === "completed" || currentMedia.status === "failed") break;
|
|
4566
|
-
await new Promise((
|
|
4567
|
-
setTimeout(
|
|
5085
|
+
await new Promise((resolve5) => {
|
|
5086
|
+
setTimeout(resolve5, 2e3);
|
|
4568
5087
|
});
|
|
4569
5088
|
}
|
|
4570
5089
|
results.push(media);
|
|
@@ -4969,8 +5488,8 @@ async function submitFeedback({ args, client, flags, json }) {
|
|
|
4969
5488
|
const payload = readFeedbackPayload(flags);
|
|
4970
5489
|
const body = {
|
|
4971
5490
|
context: parseJsonFlag(flags, "context"),
|
|
4972
|
-
error:
|
|
4973
|
-
intent:
|
|
5491
|
+
error: readStringFlag3(flags, "error"),
|
|
5492
|
+
intent: readStringFlag3(flags, "intent"),
|
|
4974
5493
|
message,
|
|
4975
5494
|
payload,
|
|
4976
5495
|
severity: readEnumFlag({ allowed: FEEDBACK_SEVERITIES, fallback: "medium", flags, key: "severity" }),
|
|
@@ -5024,7 +5543,7 @@ function parseNamedJsonFlag(flags, key) {
|
|
|
5024
5543
|
throw new CliError("INVALID_VALUE", `Invalid JSON in \`--${key}\` flag`, `Example: --${key} '{"tool":"adkit_manage"}'`);
|
|
5025
5544
|
}
|
|
5026
5545
|
}
|
|
5027
|
-
function
|
|
5546
|
+
function readStringFlag3(flags, key) {
|
|
5028
5547
|
const value = flags[key];
|
|
5029
5548
|
if (typeof value !== "string") return void 0;
|
|
5030
5549
|
const trimmed = value.trim();
|
|
@@ -5116,6 +5635,7 @@ var GOOGLE_NUMERIC_ID_PATTERN2 = /^\d+$/;
|
|
|
5116
5635
|
var GOOGLE_CAMPAIGN_CONVERSION_GOAL_CATEGORY_LIST = GOOGLE_CAMPAIGN_CONVERSION_GOAL_CATEGORIES.join(", ");
|
|
5117
5636
|
var GOOGLE_RESULTS_LEVEL_ACTIONS = /* @__PURE__ */ new Set(["campaigns", "ad-groups", "ads"]);
|
|
5118
5637
|
var TIKTOK_NUMERIC_ID_PATTERN = /^\d+$/;
|
|
5638
|
+
var REDDIT_ID_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
5119
5639
|
var LINKEDIN_ID_PATTERN = /[\d:_]/;
|
|
5120
5640
|
var USAGE = `
|
|
5121
5641
|
Usage: adkit <command> [options]
|
|
@@ -5180,6 +5700,7 @@ ${FLAG.budgetDaily}
|
|
|
5180
5700
|
Flags (list):
|
|
5181
5701
|
${FLAG.account}
|
|
5182
5702
|
--status <s> Filter by effective status (default: active,paused,in_process,with_issues; use "all" for every status)
|
|
5703
|
+
--limit <n> Maximum campaigns to return
|
|
5183
5704
|
|
|
5184
5705
|
Flags (list/get):
|
|
5185
5706
|
--raw Return raw Meta API response (no conversion)
|
|
@@ -5187,6 +5708,7 @@ Flags (list/get):
|
|
|
5187
5708
|
Note:
|
|
5188
5709
|
list returns campaign configuration only (name, status, budget).
|
|
5189
5710
|
AdKit hides deleted and archived campaigns by default. Use --status to include them.
|
|
5711
|
+
App campaigns use promotedApp in --data. Run --help full for the exact shape.
|
|
5190
5712
|
For spend/clicks/conversions: adkit manage meta results
|
|
5191
5713
|
|
|
5192
5714
|
Examples:
|
|
@@ -5215,6 +5737,7 @@ ${FLAG.budgetDaily}
|
|
|
5215
5737
|
Flags (list):
|
|
5216
5738
|
${FLAG.account}
|
|
5217
5739
|
--status <s> Filter by effective status (default: active,paused,in_process,with_issues; use "all" for every status)
|
|
5740
|
+
--limit <n> Maximum campaigns to return
|
|
5218
5741
|
|
|
5219
5742
|
Flags (list/get):
|
|
5220
5743
|
--raw Return raw Meta API response (no conversion)
|
|
@@ -5229,10 +5752,15 @@ Advanced flags:
|
|
|
5229
5752
|
specialAdCategories (string[]) credit, housing, employment, social_issues
|
|
5230
5753
|
startDate (string) ISO date, e.g. "2026-04-01T00:00:00+0000"
|
|
5231
5754
|
endDate (string|null) ISO date or null for no end
|
|
5755
|
+
promotedApp.store (string) apple_app_store or google_play
|
|
5756
|
+
promotedApp.storeAppId (string) Apple numeric app ID or Android package name
|
|
5757
|
+
promotedApp.providerAppId (string) Meta App ID (required)
|
|
5758
|
+
promotedApp.storeUrl (string) Optional store URL; AdKit builds the default when omitted
|
|
5232
5759
|
|
|
5233
5760
|
Note:
|
|
5234
5761
|
list returns campaign configuration only (name, status, budget).
|
|
5235
5762
|
AdKit hides deleted and archived campaigns by default. Use --status to include them.
|
|
5763
|
+
promotedApp is create-only. To change the app, create a replacement campaign; AdKit never creates one automatically.
|
|
5236
5764
|
For spend/clicks/conversions: adkit manage meta results
|
|
5237
5765
|
|
|
5238
5766
|
Examples:
|
|
@@ -5251,6 +5779,11 @@ Examples:
|
|
|
5251
5779
|
"name":"Housing Leads","objective":"leads",
|
|
5252
5780
|
"budget":{"lifetime":500},"specialAdCategories":["housing"],
|
|
5253
5781
|
"startDate":"2026-04-01T00:00:00+0000","endDate":"2026-04-30T23:59:59+0000"
|
|
5782
|
+
}' --publish
|
|
5783
|
+
# Create an Apple app campaign. Repeat the same promotedApp on its app ad sets.
|
|
5784
|
+
adkit manage meta campaigns create --data '{
|
|
5785
|
+
"name":"App Installs","objective":"app_promotion",
|
|
5786
|
+
"promotedApp":{"store":"apple_app_store","storeAppId":"123456789","providerAppId":"987654321"}
|
|
5254
5787
|
}' --publish`;
|
|
5255
5788
|
var ADSET_HELP = `adkit manage meta adsets \u2014 Meta ad sets
|
|
5256
5789
|
|
|
@@ -5286,6 +5819,7 @@ Rules:
|
|
|
5286
5819
|
Note:
|
|
5287
5820
|
list returns ad set configuration only (targeting, budget, optimization).
|
|
5288
5821
|
AdKit hides deleted and archived ad sets by default. Use --status to include them.
|
|
5822
|
+
App ad sets use promotedApp in --data. Run --help full for the exact shape.
|
|
5289
5823
|
Geo targeting lives in targeting.geoLocations. Use type=country or type=radius with latitude/longitude coordinates.
|
|
5290
5824
|
Native city/place/zip examples: adkit manage meta adsets --help full
|
|
5291
5825
|
For spend/clicks/conversions: adkit manage meta results
|
|
@@ -5353,6 +5887,10 @@ Advanced flags:
|
|
|
5353
5887
|
promotedObject.pixelId (string) Meta Pixel ID
|
|
5354
5888
|
promotedObject.customEventType (string) Custom conversion event
|
|
5355
5889
|
promotedObject.pageId (string) Facebook Page ID
|
|
5890
|
+
promotedApp.store (string) apple_app_store or google_play
|
|
5891
|
+
promotedApp.storeAppId (string) Apple numeric app ID or Android package name
|
|
5892
|
+
promotedApp.providerAppId (string) Meta App ID (required)
|
|
5893
|
+
promotedApp.storeUrl (string) Optional store URL; AdKit builds the default when omitted
|
|
5356
5894
|
targeting.age (object) {"min":25,"max":55}
|
|
5357
5895
|
targeting.geoLocations (object) {"include":[{"type":"country","country":"US"}]} or {"include":[{"type":"radius","latitude":51.745,"longitude":4.55,"radius":30,"radiusUnit":"km"}]}
|
|
5358
5896
|
targeting.publisherPlatforms (string[]) facebook, instagram, audience_network, messenger
|
|
@@ -5374,6 +5912,7 @@ Note:
|
|
|
5374
5912
|
Geo targeting lives in targeting.geoLocations. Use type=country for simple countries or type=radius for coordinate-radius targeting.
|
|
5375
5913
|
Radius targeting requires latitude/longitude coordinates; AdKit does not geocode addresses yet.
|
|
5376
5914
|
With platformLocation, pass the exact Meta geo object to send for native city/place/zip IDs and unsupported geo fields.
|
|
5915
|
+
Repeat the campaign promotedApp on app ad sets. It is create-only; changing it requires a replacement.
|
|
5377
5916
|
For spend/clicks/conversions: adkit manage meta results
|
|
5378
5917
|
|
|
5379
5918
|
Examples:
|
|
@@ -5569,7 +6108,9 @@ Flags:
|
|
|
5569
6108
|
--adset-ids <ids> Comma-separated ad set IDs
|
|
5570
6109
|
--ad-ids <ids> Comma-separated ad IDs
|
|
5571
6110
|
--statuses <s> active, paused (filter by entity status)
|
|
5572
|
-
--period <range>
|
|
6111
|
+
--period <range> 3d, 7d, 14d, 28d, 30d, 90d, or a named preset (default: 30d)
|
|
6112
|
+
Named presets: today, yesterday, this_month, last_month, this_quarter, last_quarter, this_year, last_year, maximum
|
|
6113
|
+
Use --from/--to for any other date range.
|
|
5573
6114
|
--from <date> Start date, YYYY-MM-DD
|
|
5574
6115
|
--to <date> End date, YYYY-MM-DD
|
|
5575
6116
|
--fields <list> Normalized fields, e.g. spend, impressions, clicks, conversionEvents, etc
|
|
@@ -5960,6 +6501,7 @@ Examples:
|
|
|
5960
6501
|
adkit manage google asset-groups update 555666777 --data '{"assets":{"add":[{"role":"headline","text":"New headline"}],"remove":[{"linkResourceName":"customers/1234567890/assetGroupAssets/555666777~444~HEADLINE"}]}}' --account 1234567890
|
|
5961
6502
|
adkit manage google asset-groups delete 555666777 --account 1234567890 --publish`;
|
|
5962
6503
|
var GOOGLE_ASSET_GROUP_HELP_FULL = GOOGLE_ASSET_GROUP_HELP;
|
|
6504
|
+
var GOOGLE_AD_GROUP_BIDDING_NOTE = " Set target CPA or ROAS, not both. Google may ignore an incompatible target; get after publish confirms the effective value. Use --data with null to remove an override.";
|
|
5963
6505
|
var GOOGLE_AD_GROUP_HELP = `adkit manage google ad-groups \u2014 Google Ads ad groups
|
|
5964
6506
|
|
|
5965
6507
|
list List ad groups
|
|
@@ -5973,6 +6515,8 @@ Flags (create/update):
|
|
|
5973
6515
|
--name <name> Ad group name
|
|
5974
6516
|
--status <s> enabled, paused, removed
|
|
5975
6517
|
--cpc-bid <n> CPC bid in account currency
|
|
6518
|
+
--target-cpa <n> Target CPA override in account currency
|
|
6519
|
+
--target-roas <n> Target ROAS override as a ratio (3.5 = 350%)
|
|
5976
6520
|
|
|
5977
6521
|
Flags (list):
|
|
5978
6522
|
--campaign <id> Filter by Google campaign ID (platformId)
|
|
@@ -5985,12 +6529,15 @@ Note:
|
|
|
5985
6529
|
Demand Gen creation uses --data with targeting.geoLocations and targeting.inventory.networks (youtube_in_feed, youtube_in_stream, youtube_shorts). These fields are create-only.
|
|
5986
6530
|
Topic and interest search return IDs used in Display targeting: adkit manage google research topics <query>, adkit manage google research interests <query>.
|
|
5987
6531
|
WARNING: On update, targeting replaces the full Display targeting set. If you send only one new website, old websites/topics/audiences can be removed.
|
|
6532
|
+
${GOOGLE_AD_GROUP_BIDDING_NOTE}
|
|
5988
6533
|
list returns ad group configuration only (name, status, bid).
|
|
5989
6534
|
For spend/clicks/conversions: adkit manage google results
|
|
5990
6535
|
|
|
5991
6536
|
Examples:
|
|
5992
6537
|
adkit manage google ad-groups list --account 1234567890 --campaign 987654321
|
|
5993
6538
|
adkit manage google ad-groups create --campaign 987654321 --name "Brand Terms" --cpc-bid 2 --account 1234567890
|
|
6539
|
+
adkit manage google ad-groups update 555666777 --target-cpa 25 --account 1234567890
|
|
6540
|
+
adkit manage google ad-groups update 555666777 --data '{"targetCpa":null}' --account 1234567890
|
|
5994
6541
|
adkit manage google ad-groups create --data '{"adGroups":[{"campaignId":"987654321","name":"Display SaaS","targeting":{"audience":{"interests":{"include":["804"]},"customAudiences":{"include":["123456789"]}},"content":{"topics":{"include":["3"]},"websites":{"include":["https://example.com"],"exclude":["https://bad.example.com"]}}}}]}' --account 1234567890
|
|
5995
6542
|
adkit manage google ad-groups create --data '{"adGroups":[{"campaignId":"987654321","name":"YouTube US","targeting":{"geoLocations":{"include":[{"type":"country","country":"US"}]},"inventory":{"networks":["youtube_in_feed","youtube_in_stream","youtube_shorts"]}}}]}' --account 1234567890
|
|
5996
6543
|
adkit manage google ad-groups update 555666777 --data '{"targeting":{"content":{"websites":{"include":["https://example.com","https://nytimes.com"]}}}}' --account 1234567890
|
|
@@ -6011,6 +6558,8 @@ Flags (create/update):
|
|
|
6011
6558
|
--name <name> Ad group name
|
|
6012
6559
|
--status <s> enabled, paused, removed
|
|
6013
6560
|
--cpc-bid <n> CPC bid in account currency
|
|
6561
|
+
--target-cpa <n> Target CPA override in account currency
|
|
6562
|
+
--target-roas <n> Target ROAS override as a ratio (3.5 = 350%)
|
|
6014
6563
|
${FLAG.account}
|
|
6015
6564
|
${FLAG.publish}
|
|
6016
6565
|
${FLAG.data}
|
|
@@ -6028,6 +6577,7 @@ Notes:
|
|
|
6028
6577
|
Topic and interest search return IDs used in targeting: research topics -> targeting.content.topics; research interests -> targeting.audience.interests.
|
|
6029
6578
|
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.
|
|
6030
6579
|
WARNING: On update, targeting replaces the full Display targeting set. If you send only one new website, old websites/topics/audiences can be removed.
|
|
6580
|
+
${GOOGLE_AD_GROUP_BIDDING_NOTE}
|
|
6031
6581
|
Demand Gen creation uses targeting.geoLocations plus targeting.inventory.networks (youtube_in_feed, youtube_in_stream, youtube_shorts). Omit targeting.inventory for Google's default channels. Demand Gen geo/inventory updates are not supported yet.
|
|
6032
6582
|
list returns ad group configuration only (name, status, bid).
|
|
6033
6583
|
For spend/clicks/conversions: adkit manage google results
|
|
@@ -6035,6 +6585,8 @@ Notes:
|
|
|
6035
6585
|
Examples:
|
|
6036
6586
|
adkit manage google ad-groups list --account 1234567890 --campaign 987654321
|
|
6037
6587
|
adkit manage google ad-groups create --campaign 987654321 --name "Brand Terms" --cpc-bid 2 --account 1234567890
|
|
6588
|
+
adkit manage google ad-groups update 555666777 --target-roas 3.5 --account 1234567890 --publish
|
|
6589
|
+
adkit manage google ad-groups update 555666777 --data '{"targetRoas":null}' --account 1234567890 --publish
|
|
6038
6590
|
adkit manage google ad-groups create --data '{"adGroups":[{"campaignId":"987654321","name":"Brand Terms","cpcBid":2}]}' --account 1234567890
|
|
6039
6591
|
adkit manage google ad-groups create --data '{"adGroups":[{"campaignId":"987654321","name":"Display SaaS","targeting":{"audience":{"interests":{"include":["804"]},"customAudiences":{"include":["123456789"]}},"content":{"topics":{"include":["3"]},"websites":{"include":["https://example.com"],"exclude":["https://bad.example.com"]}}}}]}' --account 1234567890
|
|
6040
6592
|
adkit manage google ad-groups create --data '{"adGroups":[{"campaignId":"987654321","name":"YouTube US","targeting":{"geoLocations":{"include":[{"type":"country","country":"US"}]},"inventory":{"networks":["youtube_in_feed","youtube_in_stream","youtube_shorts"]}}}]}' --account 1234567890
|
|
@@ -6775,8 +7327,8 @@ ${FLAG.data}
|
|
|
6775
7327
|
--campaign <id> Parent campaign ID (create only)
|
|
6776
7328
|
--name <name> Ad group name
|
|
6777
7329
|
--status <s> active, paused (create default: paused)
|
|
6778
|
-
--budget-daily <n> Daily budget
|
|
6779
|
-
--budget-lifetime <n> Lifetime budget
|
|
7330
|
+
--budget-daily <n> Daily budget
|
|
7331
|
+
--budget-lifetime <n> Lifetime budget
|
|
6780
7332
|
--start-date <value> Optional TikTok schedule datetime; omit to start now
|
|
6781
7333
|
--end-date <value> Optional TikTok schedule datetime; required with lifetime budget
|
|
6782
7334
|
--billing-event <s> cpc, cpm, ocpm
|
|
@@ -7039,12 +7591,123 @@ Example:
|
|
|
7039
7591
|
available List accounts available from OAuth
|
|
7040
7592
|
connect <id> Connect an account to the project
|
|
7041
7593
|
disconnect <id> Disconnect an account
|
|
7594
|
+
<id> profiles List profiles available to the account
|
|
7595
|
+
<id> pixels List pixels available to the account
|
|
7596
|
+
<id> funding-instruments List funding instruments available to the account
|
|
7597
|
+
<id> update Save default profile, pixel, or funding instrument
|
|
7598
|
+
|
|
7599
|
+
Default flags:
|
|
7600
|
+
--default-profile <id>
|
|
7601
|
+
--default-pixel <id>
|
|
7602
|
+
--default-funding-instrument <id>
|
|
7042
7603
|
|
|
7043
7604
|
Examples:
|
|
7044
7605
|
adkit manage reddit accounts list
|
|
7045
7606
|
adkit manage reddit accounts available
|
|
7046
7607
|
adkit manage reddit accounts connect a2_example
|
|
7608
|
+
adkit manage reddit accounts a2_example profiles
|
|
7609
|
+
adkit manage reddit accounts a2_example update --default-profile profile_1 --default-pixel pixel_1 --default-funding-instrument funding_1
|
|
7047
7610
|
adkit manage reddit accounts disconnect a2_example`.trim(),
|
|
7611
|
+
"reddit campaigns": `adkit manage reddit campaigns \u2014 Reddit campaigns
|
|
7612
|
+
|
|
7613
|
+
list List campaign configuration
|
|
7614
|
+
<id> Get one campaign by platform ID
|
|
7615
|
+
create Create a draft, or publish with --publish
|
|
7616
|
+
update <id> Create an update draft, or publish with --publish
|
|
7617
|
+
|
|
7618
|
+
Create flags:
|
|
7619
|
+
${FLAG.account}
|
|
7620
|
+
--name <name> Required
|
|
7621
|
+
--objective <value> brand_awareness, clicks, lead_generation, sales
|
|
7622
|
+
--optimization <v> CBO only: clicks or video_view_6s
|
|
7623
|
+
--status <status> active or paused (default: paused)
|
|
7624
|
+
--budget-lifetime <amount> Optional CBO lifetime budget; requires --end-date
|
|
7625
|
+
--bid-strategy <value> CBO supports bidless
|
|
7626
|
+
--start-date <date> --end-date <date>
|
|
7627
|
+
--funding-instrument <id> Uses the account default when omitted
|
|
7628
|
+
--pixel <id> Uses the account default when required and omitted
|
|
7629
|
+
${FLAG.publish}
|
|
7630
|
+
${FLAG.data}
|
|
7631
|
+
|
|
7632
|
+
Examples:
|
|
7633
|
+
adkit manage reddit campaigns create --name "Traffic" --objective clicks
|
|
7634
|
+
adkit manage reddit campaigns update campaign_1 --status paused --publish`.trim(),
|
|
7635
|
+
"reddit ad-groups": `adkit manage reddit ad-groups \u2014 Reddit ad groups
|
|
7636
|
+
|
|
7637
|
+
list List ad-group configuration
|
|
7638
|
+
<id> Get one ad group by platform ID
|
|
7639
|
+
create Create a draft, or publish with --publish
|
|
7640
|
+
update <id> Create an update draft, or publish with --publish
|
|
7641
|
+
|
|
7642
|
+
Create flags:
|
|
7643
|
+
${FLAG.account}
|
|
7644
|
+
--campaign <id> Required parent campaign ID
|
|
7645
|
+
--name <name> Required
|
|
7646
|
+
--status <status> active or paused (default: active)
|
|
7647
|
+
--budget-daily <amount> ABO daily budget
|
|
7648
|
+
--bid-strategy <value> bidless, manual_bidding, maximize_volume, or target_cpx
|
|
7649
|
+
--bid-amount <amount>
|
|
7650
|
+
--optimization <value>
|
|
7651
|
+
--start-date <date> --end-date <date>
|
|
7652
|
+
--pixel <id> Uses the account default when required and omitted
|
|
7653
|
+
${FLAG.publish}
|
|
7654
|
+
${FLAG.data}
|
|
7655
|
+
|
|
7656
|
+
Targeting:
|
|
7657
|
+
Pass the canonical targeting object through --data. Supported branches are geoLocations, demographics, audience (communities/interests/customAudiences), content.keywords, and inventory.
|
|
7658
|
+
|
|
7659
|
+
Example:
|
|
7660
|
+
adkit manage reddit ad-groups create --data '{"adGroups":[{"campaignId":"campaign_1","name":"Poland","budget":{"daily":20},"targeting":{"audience":{"communities":{"include":["advertising"]}}}}]}'`.trim(),
|
|
7661
|
+
"reddit ads": `adkit manage reddit ads \u2014 Reddit ads
|
|
7662
|
+
|
|
7663
|
+
list List ads and visible creative content
|
|
7664
|
+
<id> Get one ad by platform ID
|
|
7665
|
+
create Create a draft, or publish with --publish
|
|
7666
|
+
update <id> Create an update draft, or publish with --publish
|
|
7667
|
+
|
|
7668
|
+
Simple create flags:
|
|
7669
|
+
${FLAG.account}
|
|
7670
|
+
--ad-group <id> Required parent ad-group ID
|
|
7671
|
+
--ad-type <type> freeform, image, or video; carousel uses --data
|
|
7672
|
+
--headline <text> Exactly one
|
|
7673
|
+
--primary-text <text> Required for freeform
|
|
7674
|
+
--file <path> Local image/video (uploaded internally)
|
|
7675
|
+
--media-id <id> Existing temporary upload ID instead of --file
|
|
7676
|
+
--thumbnail-file <path> Local video thumbnail (uploaded internally)
|
|
7677
|
+
--thumbnail-id <id> Existing thumbnail upload ID instead of --thumbnail-file
|
|
7678
|
+
--url <url> Required for image/video
|
|
7679
|
+
--cta <value>
|
|
7680
|
+
--existing-post <t3_id> Promote an eligible community post instead of creating content
|
|
7681
|
+
--profile <id> Uses the account default when omitted
|
|
7682
|
+
--status <status> active or paused (default: active)
|
|
7683
|
+
${FLAG.publish}
|
|
7684
|
+
${FLAG.data}
|
|
7685
|
+
|
|
7686
|
+
Notes:
|
|
7687
|
+
AdKit creates and waits for Reddit's post internally. Agents only create an ad and receive the verified ad result.
|
|
7688
|
+
Local --file/--thumbnail-file uploads are handled internally. Canonical --data bodies use temporary upload IDs in creative.media[].id.
|
|
7689
|
+
|
|
7690
|
+
Examples:
|
|
7691
|
+
adkit manage reddit ads create --ad-group ad_group_1 --ad-type image --headline "Try it" --file ./creative.jpg --url https://example.com
|
|
7692
|
+
adkit manage reddit ads create --ad-group ad_group_1 --existing-post t3_abc123 --headline "Try it"`.trim(),
|
|
7693
|
+
"reddit results": `adkit manage reddit results \u2014 Reddit performance reporting
|
|
7694
|
+
|
|
7695
|
+
list List normalized account, campaign, ad-group, or ad rows
|
|
7696
|
+
|
|
7697
|
+
Flags:
|
|
7698
|
+
${FLAG.account}
|
|
7699
|
+
--level <level> Required: account, campaigns, ad-groups, ads
|
|
7700
|
+
--period <period> 3d, 7d, 14d, 28d, 30d, or 90d; or use --from/--to
|
|
7701
|
+
--from <date> YYYY-MM-DD
|
|
7702
|
+
--to <date> YYYY-MM-DD
|
|
7703
|
+
--fields <csv> Normalized result fields
|
|
7704
|
+
--breakdowns <csv> Currently day
|
|
7705
|
+
--campaign-ids <csv> --ad-group-ids <csv> --ad-ids <csv>
|
|
7706
|
+
--sort <field> --sort-direction <asc|desc>
|
|
7707
|
+
--limit <n> --offset <n>
|
|
7708
|
+
|
|
7709
|
+
Example:
|
|
7710
|
+
adkit manage reddit results --level campaigns --period 7d --fields spend,impressions,clicks`.trim(),
|
|
7048
7711
|
"meta media": `adkit manage meta media \u2014 Upload media to Meta
|
|
7049
7712
|
|
|
7050
7713
|
upload Upload an image or video
|
|
@@ -7064,6 +7727,7 @@ Examples:
|
|
|
7064
7727
|
Must be enabled in Agent Permissions first.
|
|
7065
7728
|
Use this for unsupported native platform resources/workflows. Use platformOverrides instead when you only need raw fields on a supported normalized resource.
|
|
7066
7729
|
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.
|
|
7730
|
+
A successful raw request means the platform returned a successful HTTP response. It does not prove an asynchronous platform job or every downstream entity finished creating.
|
|
7067
7731
|
|
|
7068
7732
|
Flags:
|
|
7069
7733
|
--data <json> Full request object (preferred for agents)
|
|
@@ -7083,7 +7747,7 @@ Examples:
|
|
|
7083
7747
|
adkit manage platform-api-requests --platform meta --endpoint "act_123/campaigns" --payload '{"name":"Test","objective":"OUTCOME_TRAFFIC","status":"PAUSED","special_ad_categories":[]}' --request-description "Create campaign" --account act_123 --publish
|
|
7084
7748
|
adkit manage platform-api-requests --platform google --endpoint "customers/123/googleAds:mutate" --payload '{"mutateOperations":[...]}' --request-description "Pause ad" --account 123
|
|
7085
7749
|
adkit manage platform-api-requests --platform tiktok --endpoint "campaign/get/" --method GET --payload '{"advertiser_id":"123"}' --request-description "List TikTok campaigns" --account 123
|
|
7086
|
-
adkit manage platform-api-requests --platform reddit --endpoint "ad_accounts/a2_example/campaigns" --payload '{"name":"Test","objective":"
|
|
7750
|
+
adkit manage platform-api-requests --platform reddit --endpoint "ad_accounts/a2_example/campaigns" --payload '{"name":"Test","objective":"SALES","configured_status":"PAUSED","funding_instrument_id":"fi_example","is_campaign_budget_optimization":false}' --request-description "Create paused Reddit campaign" --account a2_example
|
|
7087
7751
|
adkit manage platform-api-requests --platform x --endpoint "accounts/18ce54d4x5t/campaigns" --method GET --request-description "List X campaigns" --account 18ce54d4x5t
|
|
7088
7752
|
adkit manage platform-api-requests --platform linkedin --endpoint "adCampaignGroups/(account:urn:li:sponsoredAccount:512345678)" --method GET --request-description "List LinkedIn campaign groups" --account 512345678
|
|
7089
7753
|
adkit manage platform-api-requests --platform microsoft --endpoint "https://campaign.api.bingads.microsoft.com/CampaignManagement/v13/Campaigns/QueryByAccountId" --payload '{"AccountId":187276743,"CampaignType":"Search"}' --request-description "List Microsoft campaigns" --account 187276743`.trim(),
|
|
@@ -7094,7 +7758,7 @@ Platforms:
|
|
|
7094
7758
|
google Google Ads
|
|
7095
7759
|
tiktok TikTok Ads
|
|
7096
7760
|
reddit Reddit Ads
|
|
7097
|
-
x X
|
|
7761
|
+
x X accounts plus campaign/ad-group reads and writes
|
|
7098
7762
|
linkedin LinkedIn Ads (campaigns, ad groups, ads, targeting, results)
|
|
7099
7763
|
microsoft Microsoft Ads raw API access + account setup
|
|
7100
7764
|
drafts Manage drafts across platforms
|
|
@@ -7175,19 +7839,27 @@ Run adkit manage tiktok <group> --help for details.`.trim(),
|
|
|
7175
7839
|
reddit: `adkit manage reddit \u2014 Reddit Ads management
|
|
7176
7840
|
|
|
7177
7841
|
Entity groups:
|
|
7178
|
-
accounts
|
|
7842
|
+
accounts Connect accounts and configure profile/pixel/funding defaults
|
|
7843
|
+
campaigns List, get, create, and update campaigns
|
|
7844
|
+
ad-groups List, get, create, and update ad groups with targeting
|
|
7845
|
+
ads List, get, create, and update ads; Reddit post jobs stay internal
|
|
7846
|
+
results Normalized performance reporting
|
|
7179
7847
|
|
|
7180
7848
|
${rawPlatformCommandLine("reddit")}
|
|
7181
7849
|
|
|
7182
7850
|
General flags:
|
|
7183
7851
|
${FLAG.account}
|
|
7852
|
+
${FLAG.publish}
|
|
7853
|
+
${FLAG.data}
|
|
7184
7854
|
|
|
7185
|
-
|
|
7855
|
+
Mutations are draft-first by default. Use --publish only when the user wants immediate platform execution.
|
|
7856
|
+
Recommended order: configure account defaults, create campaign, create ad group, create ad, then read results.
|
|
7857
|
+
Run adkit manage reddit <group> --help for details.`.trim(),
|
|
7186
7858
|
x: `adkit manage x \u2014 X Ads management
|
|
7187
7859
|
|
|
7188
7860
|
Entity groups:
|
|
7189
|
-
accounts Connect accounts and configure profile/pixel defaults
|
|
7190
|
-
campaigns List/get campaigns
|
|
7861
|
+
accounts Connect accounts and configure profile/pixel/funding defaults
|
|
7862
|
+
campaigns List/get/create/update campaigns
|
|
7191
7863
|
ad-groups List/get line items as ad groups
|
|
7192
7864
|
ads List/get promoted-Post associations
|
|
7193
7865
|
results Campaign, ad-group, and ad performance
|
|
@@ -7206,12 +7878,16 @@ Examples:
|
|
|
7206
7878
|
disconnect <id> Disconnect an X ad account
|
|
7207
7879
|
<id> profiles List X identities available to an account
|
|
7208
7880
|
<id> pixels List X conversion pixels available to an account
|
|
7209
|
-
<id>
|
|
7881
|
+
<id> funding-instruments
|
|
7882
|
+
List X funding sources available to an account
|
|
7883
|
+
<id> update Save default identity, pixel, and funding IDs
|
|
7210
7884
|
|
|
7211
7885
|
Flags:
|
|
7212
7886
|
--integration <id> Connect using a specific workspace login returned by available
|
|
7213
7887
|
--default-profile <id> Default X identity for ad creation
|
|
7214
7888
|
--default-pixel <id> Default X conversion pixel
|
|
7889
|
+
--default-funding-instrument <id>
|
|
7890
|
+
Default X funding source
|
|
7215
7891
|
--data <json> Full update body; also supports null to clear a default
|
|
7216
7892
|
|
|
7217
7893
|
Examples:
|
|
@@ -7220,15 +7896,23 @@ Examples:
|
|
|
7220
7896
|
adkit manage x accounts connect 18ce54d4x5t
|
|
7221
7897
|
adkit manage x accounts 18ce54d4x5t profiles
|
|
7222
7898
|
adkit manage x accounts 18ce54d4x5t pixels
|
|
7223
|
-
adkit manage x accounts 18ce54d4x5t
|
|
7899
|
+
adkit manage x accounts 18ce54d4x5t funding-instruments
|
|
7900
|
+
adkit manage x accounts 18ce54d4x5t update --default-profile 12abc --default-pixel p123 --default-funding-instrument f123
|
|
7224
7901
|
adkit manage x accounts disconnect 18ce54d4x5t`.trim(),
|
|
7225
|
-
"x campaigns": `adkit manage x campaigns \u2014 X Ads campaigns
|
|
7902
|
+
"x campaigns": `adkit manage x campaigns \u2014 X Ads campaigns
|
|
7226
7903
|
|
|
7227
7904
|
list List campaigns
|
|
7228
7905
|
get <id> Get one campaign
|
|
7906
|
+
create Create a draft; add --publish to send it to X paused by default
|
|
7907
|
+
update <id> Create an update draft; add --publish to apply it
|
|
7229
7908
|
|
|
7230
7909
|
Flags:
|
|
7231
7910
|
${FLAG.account}
|
|
7911
|
+
${FLAG.publish}
|
|
7912
|
+
--name <name> Campaign name
|
|
7913
|
+
--budget-daily <n> Daily budget in account currency
|
|
7914
|
+
--budget-lifetime <n> Lifetime budget in account currency
|
|
7915
|
+
--funding-instrument <id> Override the account funding default (create only)
|
|
7232
7916
|
--campaign-ids <ids> Comma-separated campaign platform IDs
|
|
7233
7917
|
--status <s> active, paused, or removed
|
|
7234
7918
|
--limit <n> Max rows
|
|
@@ -7236,26 +7920,46 @@ ${FLAG.account}
|
|
|
7236
7920
|
|
|
7237
7921
|
Example:
|
|
7238
7922
|
adkit manage x campaigns list --status active
|
|
7239
|
-
adkit manage x campaigns get 8v7jo
|
|
7240
|
-
|
|
7923
|
+
adkit manage x campaigns get 8v7jo
|
|
7924
|
+
adkit manage x campaigns create --name "Launch" --budget-daily 25 --publish
|
|
7925
|
+
adkit manage x campaigns update 8v7jo --status paused --publish`.trim(),
|
|
7926
|
+
"x ad-groups": `adkit manage x ad-groups \u2014 X line items as AdKit ad groups
|
|
7241
7927
|
|
|
7242
7928
|
list List ad groups
|
|
7243
7929
|
get <id> Get one ad group
|
|
7930
|
+
create Create a draft; add --publish to send it to X paused by default
|
|
7931
|
+
update <id> Create an update draft; add --publish to apply it
|
|
7244
7932
|
|
|
7245
7933
|
Flags:
|
|
7246
7934
|
${FLAG.account}
|
|
7935
|
+
${FLAG.publish}
|
|
7936
|
+
--campaign <id> Parent campaign platform ID (create only)
|
|
7937
|
+
--name <name> Ad-group name
|
|
7938
|
+
--objective <value> engagements, reach, video_views, or website_clicks (create only)
|
|
7939
|
+
--budget-daily <n> Daily budget; parent campaign must use X LINE_ITEM budgeting
|
|
7940
|
+
--budget-lifetime <n> Lifetime budget; parent campaign must use X LINE_ITEM budgeting
|
|
7941
|
+
--bid-amount <n> Bid amount in account currency
|
|
7942
|
+
--bid-strategy <s> auto, max, or target
|
|
7943
|
+
--optimization <s> X optimization goal
|
|
7944
|
+
--start-date <date> ISO 8601 start date (required for create)
|
|
7945
|
+
--end-date <date> ISO 8601 end date
|
|
7247
7946
|
--campaign-ids <ids> Comma-separated parent campaign IDs
|
|
7248
7947
|
--ad-group-ids <ids> Comma-separated line-item IDs
|
|
7249
7948
|
--status <s> active, paused, or removed
|
|
7250
7949
|
--limit <n> Max rows
|
|
7251
7950
|
--offset <n> Row offset
|
|
7951
|
+
--data <json> Full request body; use for targeting
|
|
7252
7952
|
|
|
7253
|
-
|
|
7254
|
-
adkit manage x ad-groups list --campaign-ids 8v7jo
|
|
7255
|
-
|
|
7953
|
+
Examples:
|
|
7954
|
+
adkit manage x ad-groups list --campaign-ids 8v7jo
|
|
7955
|
+
adkit manage x ad-groups create --campaign 8v7jo --name "Poland launch" --objective website_clicks --start-date 2026-09-10 --budget-daily 25
|
|
7956
|
+
adkit manage x ad-groups update b9x4k --status paused --publish`.trim(),
|
|
7957
|
+
"x ads": `adkit manage x ads \u2014 X promoted-Post ads
|
|
7256
7958
|
|
|
7257
7959
|
list List promoted-Post ads
|
|
7258
7960
|
get <id> Get one ad by promoted association ID
|
|
7961
|
+
create Create an ad draft; add --publish to send it to X
|
|
7962
|
+
delete <id> Remove a promoted association as a draft; add --publish to send
|
|
7259
7963
|
|
|
7260
7964
|
Identity:
|
|
7261
7965
|
platformId is the promoted association ID used by X Ads and analytics. postId is the underlying Post ID.
|
|
@@ -7268,9 +7972,18 @@ ${FLAG.account}
|
|
|
7268
7972
|
--status <s> active, paused, or removed
|
|
7269
7973
|
--limit <n> Max rows
|
|
7270
7974
|
--offset <n> Row offset
|
|
7975
|
+
--ad-group <id> Parent X line-item ID (create)
|
|
7976
|
+
--existing-post <id> Promote an existing X Post (create)
|
|
7977
|
+
--status active Only active is valid; pause the ad group or delete the ad
|
|
7978
|
+
${FLAG.publish}
|
|
7979
|
+
--data <json> Full body for inline creative: { "ads": [...] }
|
|
7271
7980
|
|
|
7272
|
-
|
|
7273
|
-
|
|
7981
|
+
Create one X ad per request.
|
|
7982
|
+
|
|
7983
|
+
Examples:
|
|
7984
|
+
adkit manage x ads create --ad-group b9x4k --existing-post 880290790664060928 --publish
|
|
7985
|
+
adkit manage x ads create --data '{"ads":[{"adGroupId":"b9x4k","creative":{"primaryTexts":["Hello"]}}]}'
|
|
7986
|
+
adkit manage x ads delete 1efwlo --publish`.trim(),
|
|
7274
7987
|
"x results": `adkit manage x results \u2014 X Ads performance reporting (read-only)
|
|
7275
7988
|
|
|
7276
7989
|
list List reporting rows (list is optional)
|
|
@@ -7715,7 +8428,7 @@ function printList(data, flags, emptyHint = "No results.") {
|
|
|
7715
8428
|
if (output) console.log(output);
|
|
7716
8429
|
else console.log(emptyHint);
|
|
7717
8430
|
}
|
|
7718
|
-
function
|
|
8431
|
+
function isObject2(value) {
|
|
7719
8432
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
7720
8433
|
}
|
|
7721
8434
|
function fmtNum(n) {
|
|
@@ -7761,7 +8474,7 @@ function num(obj, key) {
|
|
|
7761
8474
|
}
|
|
7762
8475
|
function nested(obj, key) {
|
|
7763
8476
|
const v = obj[key];
|
|
7764
|
-
return
|
|
8477
|
+
return isObject2(v) ? v : void 0;
|
|
7765
8478
|
}
|
|
7766
8479
|
function printClickLine(label, obj, currency) {
|
|
7767
8480
|
const parts = [];
|
|
@@ -7774,7 +8487,7 @@ function printClickLine(label, obj, currency) {
|
|
|
7774
8487
|
if (parts.length > 0) console.log(` ${label.padEnd(14)}${parts.join(" \xB7 ")}`);
|
|
7775
8488
|
}
|
|
7776
8489
|
function printResultsCard(data) {
|
|
7777
|
-
if (!
|
|
8490
|
+
if (!isObject2(data)) {
|
|
7778
8491
|
console.log(JSON.stringify(data, null, 2));
|
|
7779
8492
|
return;
|
|
7780
8493
|
}
|
|
@@ -7803,7 +8516,7 @@ function printResultsCard(data) {
|
|
|
7803
8516
|
return;
|
|
7804
8517
|
}
|
|
7805
8518
|
for (const row of results) {
|
|
7806
|
-
if (!
|
|
8519
|
+
if (!isObject2(row)) continue;
|
|
7807
8520
|
const entity = nested(row, "entity");
|
|
7808
8521
|
const metrics = nested(row, "metrics");
|
|
7809
8522
|
const breakdown = nested(row, "breakdown");
|
|
@@ -7857,7 +8570,7 @@ ${titleParts.join(" \xB7 ")}`);
|
|
|
7857
8570
|
const actions = nested(row, "conversionEvents") ?? nested(row, "actions");
|
|
7858
8571
|
if (actions) {
|
|
7859
8572
|
for (const [key, val] of Object.entries(actions)) {
|
|
7860
|
-
if (!
|
|
8573
|
+
if (!isObject2(val)) continue;
|
|
7861
8574
|
const parts = [];
|
|
7862
8575
|
const count = num(val, "count");
|
|
7863
8576
|
const cost = num(val, "costPerResult") ?? num(val, "cost");
|
|
@@ -7894,7 +8607,7 @@ function printWarnings(warnings) {
|
|
|
7894
8607
|
if (!Array.isArray(warnings) || warnings.length === 0) return;
|
|
7895
8608
|
console.log("");
|
|
7896
8609
|
for (const w of warnings) {
|
|
7897
|
-
if (!
|
|
8610
|
+
if (!isObject2(w)) continue;
|
|
7898
8611
|
const msg = str(w, "message") ?? JSON.stringify(w);
|
|
7899
8612
|
console.log(`Warning: ${msg}`);
|
|
7900
8613
|
}
|
|
@@ -7913,28 +8626,28 @@ function shortDate(v) {
|
|
|
7913
8626
|
return v.slice(0, 10);
|
|
7914
8627
|
}
|
|
7915
8628
|
function isCampaignDetail(value) {
|
|
7916
|
-
return
|
|
8629
|
+
return isObject2(value);
|
|
7917
8630
|
}
|
|
7918
8631
|
function isAdSetDetail(value) {
|
|
7919
|
-
return
|
|
8632
|
+
return isObject2(value);
|
|
7920
8633
|
}
|
|
7921
8634
|
function isAdDetail(value) {
|
|
7922
|
-
return
|
|
8635
|
+
return isObject2(value);
|
|
7923
8636
|
}
|
|
7924
8637
|
function isCampaignDetailData(value) {
|
|
7925
|
-
if (!
|
|
8638
|
+
if (!isObject2(value)) return false;
|
|
7926
8639
|
if ("campaigns" in value) return Array.isArray(value.campaigns) && value.campaigns.every(isCampaignDetail);
|
|
7927
8640
|
if ("campaign" in value) return isCampaignDetail(value.campaign);
|
|
7928
8641
|
return true;
|
|
7929
8642
|
}
|
|
7930
8643
|
function isAdSetDetailData(value) {
|
|
7931
|
-
if (!
|
|
8644
|
+
if (!isObject2(value)) return false;
|
|
7932
8645
|
if ("adsets" in value) return Array.isArray(value.adsets) && value.adsets.every(isAdSetDetail);
|
|
7933
8646
|
if ("adset" in value) return isAdSetDetail(value.adset);
|
|
7934
8647
|
return true;
|
|
7935
8648
|
}
|
|
7936
8649
|
function isAdDetailData(value) {
|
|
7937
|
-
if (!
|
|
8650
|
+
if (!isObject2(value)) return false;
|
|
7938
8651
|
if ("ads" in value) return Array.isArray(value.ads) && value.ads.every(isAdDetail);
|
|
7939
8652
|
if ("ad" in value) return isAdDetail(value.ad);
|
|
7940
8653
|
return true;
|
|
@@ -7949,6 +8662,7 @@ function printSingleEntity(e, entity) {
|
|
|
7949
8662
|
if (campaign.lifetimeBudget != null) lines.push(` Lifetime: ${formatForTTY(campaign.lifetimeBudget)}`);
|
|
7950
8663
|
if (campaign.bidStrategy) lines.push(` Bid Strategy: ${formatForTTY(campaign.bidStrategy)}`);
|
|
7951
8664
|
if (campaign.spendCap != null) lines.push(` Spend Cap: ${formatForTTY(campaign.spendCap)}`);
|
|
8665
|
+
if (campaign.promotedApp) lines.push(` App: ${JSON.stringify(campaign.promotedApp)}`);
|
|
7952
8666
|
if (campaign.startDate) lines.push(` Start: ${formatForTTY(campaign.startDate)}`);
|
|
7953
8667
|
if (campaign.endDate) lines.push(` End: ${formatForTTY(campaign.endDate)}`);
|
|
7954
8668
|
if (campaign.specialAdCategories) lines.push(` Categories: ${JSON.stringify(campaign.specialAdCategories)}`);
|
|
@@ -7962,6 +8676,7 @@ function printSingleEntity(e, entity) {
|
|
|
7962
8676
|
if (adset.billingEvent) lines.push(` Billing: ${formatForTTY(adset.billingEvent)}`);
|
|
7963
8677
|
if (adset.targeting) lines.push(` Targeting: ${JSON.stringify(adset.targeting)}`);
|
|
7964
8678
|
if (adset.promotedObject) lines.push(` Promoted: ${JSON.stringify(adset.promotedObject)}`);
|
|
8679
|
+
if (adset.promotedApp) lines.push(` App: ${JSON.stringify(adset.promotedApp)}`);
|
|
7965
8680
|
if (adset.startDate) lines.push(` Start: ${formatForTTY(adset.startDate)}`);
|
|
7966
8681
|
if (adset.endDate) lines.push(` End: ${formatForTTY(adset.endDate)}`);
|
|
7967
8682
|
if (createdAt) lines.push(` Created: ${createdAt}`);
|
|
@@ -8011,14 +8726,14 @@ function printAdDetail(data) {
|
|
|
8011
8726
|
printSingleEntity("ad" in data ? data.ad : data, "ad");
|
|
8012
8727
|
}
|
|
8013
8728
|
function printGoogleAdDetail(data) {
|
|
8014
|
-
if (
|
|
8729
|
+
if (isObject2(data) && "ads" in data && Array.isArray(data.ads)) {
|
|
8015
8730
|
for (const item of data.ads) {
|
|
8016
8731
|
if (!isGoogleCliAdRecord(item)) continue;
|
|
8017
8732
|
console.log(toGoogleAdDetailLines(item).join("\n"));
|
|
8018
8733
|
}
|
|
8019
8734
|
return;
|
|
8020
8735
|
}
|
|
8021
|
-
const ad =
|
|
8736
|
+
const ad = isObject2(data) && "ad" in data ? data.ad : data;
|
|
8022
8737
|
if (!isGoogleCliAdRecord(ad)) {
|
|
8023
8738
|
console.log(JSON.stringify(data, null, 2));
|
|
8024
8739
|
return;
|
|
@@ -8403,11 +9118,11 @@ async function selfUpdate(currentVersion) {
|
|
|
8403
9118
|
const installed = execSync("npm list -g @adkit/cli --depth=0 --json", { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
|
|
8404
9119
|
const parsed = JSON.parse(installed);
|
|
8405
9120
|
let newVersion = "unknown";
|
|
8406
|
-
if (
|
|
9121
|
+
if (isObject2(parsed) && "dependencies" in parsed) {
|
|
8407
9122
|
const deps = parsed.dependencies;
|
|
8408
|
-
if (
|
|
9123
|
+
if (isObject2(deps) && "@adkit/cli" in deps) {
|
|
8409
9124
|
const pkg = deps["@adkit/cli"];
|
|
8410
|
-
if (
|
|
9125
|
+
if (isObject2(pkg) && "version" in pkg) {
|
|
8411
9126
|
const v = pkg.version;
|
|
8412
9127
|
if (typeof v === "string") newVersion = v;
|
|
8413
9128
|
}
|
|
@@ -9202,6 +9917,22 @@ async function main() {
|
|
|
9202
9917
|
} else if (platform2 === "reddit") {
|
|
9203
9918
|
switch (entity) {
|
|
9204
9919
|
case "accounts": {
|
|
9920
|
+
if (action && action !== "list" && action !== "available" && action !== "connect" && action !== "disconnect") {
|
|
9921
|
+
const accountId = action;
|
|
9922
|
+
const subcommand = args[4];
|
|
9923
|
+
if (subcommand === "profiles") {
|
|
9924
|
+
data = await listRedditProfiles(client, [accountId], flags);
|
|
9925
|
+
emptyHint = "No Reddit profiles found for this account.";
|
|
9926
|
+
} else if (subcommand === "pixels") {
|
|
9927
|
+
data = await listRedditPixels(client, [accountId], flags);
|
|
9928
|
+
emptyHint = "No Reddit pixels found for this account.";
|
|
9929
|
+
} else if (subcommand === "funding-instruments") {
|
|
9930
|
+
data = await listRedditFundingInstruments(client, [accountId], flags);
|
|
9931
|
+
emptyHint = "No Reddit funding instruments found for this account.";
|
|
9932
|
+
} else if (subcommand === "update") data = await updateRedditAccount(client, [accountId], flags);
|
|
9933
|
+
else throw new CliError("UNKNOWN_COMMAND", `Unknown subcommand: reddit accounts ${accountId} ${subcommand ?? ""}`, "Expected: profiles, pixels, funding-instruments, or update");
|
|
9934
|
+
break;
|
|
9935
|
+
}
|
|
9205
9936
|
switch (action) {
|
|
9206
9937
|
case "list":
|
|
9207
9938
|
case void 0:
|
|
@@ -9223,8 +9954,56 @@ async function main() {
|
|
|
9223
9954
|
}
|
|
9224
9955
|
break;
|
|
9225
9956
|
}
|
|
9957
|
+
case "campaigns": {
|
|
9958
|
+
if (!action) {
|
|
9959
|
+
showHelp("reddit campaigns", flags.help === "full");
|
|
9960
|
+
return;
|
|
9961
|
+
}
|
|
9962
|
+
if (action === "list") {
|
|
9963
|
+
data = await listRedditCampaigns(client, restArgs, flags);
|
|
9964
|
+
emptyHint = "No Reddit campaigns found for this account.";
|
|
9965
|
+
} else if (action === "create") data = await createRedditCampaign(client, restArgs, flags);
|
|
9966
|
+
else if (action === "update") data = await updateRedditCampaign(client, restArgs, flags);
|
|
9967
|
+
else if (REDDIT_ID_PATTERN.test(action)) data = await getRedditCampaign(client, [action], flags);
|
|
9968
|
+
else throw new CliError("UNKNOWN_COMMAND", `Unknown action: reddit campaigns ${action}`, "Available: list, create, update, or a campaign ID");
|
|
9969
|
+
break;
|
|
9970
|
+
}
|
|
9971
|
+
case "ad-groups": {
|
|
9972
|
+
if (!action) {
|
|
9973
|
+
showHelp("reddit ad-groups", flags.help === "full");
|
|
9974
|
+
return;
|
|
9975
|
+
}
|
|
9976
|
+
if (action === "list") {
|
|
9977
|
+
data = await listRedditAdGroups(client, restArgs, flags);
|
|
9978
|
+
emptyHint = "No Reddit ad groups found for this account.";
|
|
9979
|
+
} else if (action === "create") data = await createRedditAdGroup(client, restArgs, flags);
|
|
9980
|
+
else if (action === "update") data = await updateRedditAdGroup(client, restArgs, flags);
|
|
9981
|
+
else if (REDDIT_ID_PATTERN.test(action)) data = await getRedditAdGroup(client, [action], flags);
|
|
9982
|
+
else throw new CliError("UNKNOWN_COMMAND", `Unknown action: reddit ad-groups ${action}`, "Available: list, create, update, or an ad-group ID");
|
|
9983
|
+
break;
|
|
9984
|
+
}
|
|
9985
|
+
case "ads": {
|
|
9986
|
+
if (!action) {
|
|
9987
|
+
showHelp("reddit ads", flags.help === "full");
|
|
9988
|
+
return;
|
|
9989
|
+
}
|
|
9990
|
+
if (action === "list") {
|
|
9991
|
+
data = await listRedditAds(client, restArgs, flags);
|
|
9992
|
+
emptyHint = "No Reddit ads found for this account.";
|
|
9993
|
+
} else if (action === "create") data = await createRedditAd(client, restArgs, flags);
|
|
9994
|
+
else if (action === "update") data = await updateRedditAd(client, restArgs, flags);
|
|
9995
|
+
else if (REDDIT_ID_PATTERN.test(action)) data = await getRedditAd(client, [action], flags);
|
|
9996
|
+
else throw new CliError("UNKNOWN_COMMAND", `Unknown action: reddit ads ${action}`, "Available: list, create, update, or an ad ID");
|
|
9997
|
+
break;
|
|
9998
|
+
}
|
|
9999
|
+
case "results": {
|
|
10000
|
+
if (action && action !== "list") throw new CliError("UNKNOWN_COMMAND", `Unknown action: reddit results ${action}`, "Available: list");
|
|
10001
|
+
data = await listRedditResults(client, restArgs, flags);
|
|
10002
|
+
emptyHint = "No Reddit result rows found for the selected date range.";
|
|
10003
|
+
break;
|
|
10004
|
+
}
|
|
9226
10005
|
default:
|
|
9227
|
-
throw new CliError("UNKNOWN_COMMAND", `Unknown entity: reddit ${entity}`, "Available: accounts, platform-api-requests");
|
|
10006
|
+
throw new CliError("UNKNOWN_COMMAND", `Unknown entity: reddit ${entity}`, "Available: accounts, campaigns, ad-groups, ads, results, platform-api-requests");
|
|
9228
10007
|
}
|
|
9229
10008
|
} else if (platform2 === "linkedin") {
|
|
9230
10009
|
switch (entity) {
|
|
@@ -9388,8 +10167,11 @@ async function main() {
|
|
|
9388
10167
|
} else if (subcommand === "pixels") {
|
|
9389
10168
|
data = await listXPixels(client, [accountId], flags);
|
|
9390
10169
|
emptyHint = "No X conversion pixels found for this account.";
|
|
10170
|
+
} else if (subcommand === "funding-instruments") {
|
|
10171
|
+
data = await listXFundingInstruments(client, [accountId], flags);
|
|
10172
|
+
emptyHint = "No X funding sources found for this account.";
|
|
9391
10173
|
} else if (subcommand === "update") data = await updateXAccount(client, [accountId], flags);
|
|
9392
|
-
else throw new CliError("UNKNOWN_COMMAND", `Unknown subcommand: x accounts ${accountId} ${subcommand ?? ""}`, "Expected: profiles, pixels, or update");
|
|
10174
|
+
else throw new CliError("UNKNOWN_COMMAND", `Unknown subcommand: x accounts ${accountId} ${subcommand ?? ""}`, "Expected: profiles, pixels, funding-instruments, or update");
|
|
9393
10175
|
break;
|
|
9394
10176
|
}
|
|
9395
10177
|
switch (action) {
|
|
@@ -9418,21 +10200,27 @@ async function main() {
|
|
|
9418
10200
|
data = await listXCampaigns(client, restArgs, flags);
|
|
9419
10201
|
emptyHint = "No X campaigns matched the selected filters.";
|
|
9420
10202
|
} else if (action === "get") data = await getXCampaign(client, restArgs, flags);
|
|
9421
|
-
else
|
|
10203
|
+
else if (action === "create") data = await createXCampaign(client, restArgs, flags);
|
|
10204
|
+
else if (action === "update") data = await updateXCampaign(client, restArgs, flags);
|
|
10205
|
+
else throw new CliError("UNKNOWN_COMMAND", `Unknown action: x campaigns ${action}`, "Available: list, get, create, update");
|
|
9422
10206
|
break;
|
|
9423
10207
|
case "ad-groups":
|
|
9424
10208
|
if (action === "list") {
|
|
9425
10209
|
data = await listXAdGroups(client, restArgs, flags);
|
|
9426
10210
|
emptyHint = "No X ad groups matched the selected filters.";
|
|
9427
10211
|
} else if (action === "get") data = await getXAdGroup(client, restArgs, flags);
|
|
9428
|
-
else
|
|
10212
|
+
else if (action === "create") data = await createXAdGroup(client, restArgs, flags);
|
|
10213
|
+
else if (action === "update") data = await updateXAdGroup(client, restArgs, flags);
|
|
10214
|
+
else throw new CliError("UNKNOWN_COMMAND", `Unknown action: x ad-groups ${action}`, "Available: list, get, create, update");
|
|
9429
10215
|
break;
|
|
9430
10216
|
case "ads":
|
|
9431
10217
|
if (action === "list") {
|
|
9432
10218
|
data = await listXAds(client, restArgs, flags);
|
|
9433
10219
|
emptyHint = "No X promoted-Post ads matched the selected filters.";
|
|
9434
10220
|
} else if (action === "get") data = await getXAd(client, restArgs, flags);
|
|
9435
|
-
else
|
|
10221
|
+
else if (action === "create") data = await createXAd(client, restArgs, flags);
|
|
10222
|
+
else if (action === "delete") data = await deleteXAd(client, restArgs, flags);
|
|
10223
|
+
else throw new CliError("UNKNOWN_COMMAND", `Unknown action: x ads ${action}`, "Available: list, get, create, delete");
|
|
9436
10224
|
break;
|
|
9437
10225
|
case "results":
|
|
9438
10226
|
if (action && action !== "list") throw new CliError("UNKNOWN_COMMAND", `Unknown action: x results ${action}`, "Available: list");
|