@adkit/cli 1.12.28 → 1.12.30
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 +587 -14
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -744,14 +744,14 @@ var baseOpen = async (options) => {
|
|
|
744
744
|
}
|
|
745
745
|
const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
|
|
746
746
|
if (options.wait) {
|
|
747
|
-
return new Promise((
|
|
747
|
+
return new Promise((resolve4, reject) => {
|
|
748
748
|
subprocess.once("error", reject);
|
|
749
749
|
subprocess.once("close", (exitCode) => {
|
|
750
750
|
if (!options.allowNonzeroExitCode && exitCode > 0) {
|
|
751
751
|
reject(new Error(`Exited with code ${exitCode}`));
|
|
752
752
|
return;
|
|
753
753
|
}
|
|
754
|
-
|
|
754
|
+
resolve4(subprocess);
|
|
755
755
|
});
|
|
756
756
|
});
|
|
757
757
|
}
|
|
@@ -3195,10 +3195,303 @@ async function disconnectRedditAccount(client, args, flags) {
|
|
|
3195
3195
|
}
|
|
3196
3196
|
|
|
3197
3197
|
// src/commands/linkedin.ts
|
|
3198
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
3199
|
+
import { basename as basename3, extname as extname3, resolve as resolve3 } from "node:path";
|
|
3200
|
+
var MEDIA_UPLOAD_FLAGS3 = ["file", "url", "base64", "adkit-media", "temporary-upload", "filename", "content-type", "name"];
|
|
3201
|
+
var CAMPAIGN_LIST_FLAGS3 = ["campaign-ids", "limit", "offset"];
|
|
3202
|
+
var AD_GROUP_LIST_FLAGS3 = ["campaign-ids", "ad-group-ids", "limit", "offset"];
|
|
3203
|
+
var AD_LIST_FLAGS3 = ["campaign-ids", "limit", "offset"];
|
|
3204
|
+
var CAMPAIGN_CREATE_FLAGS3 = ["name", "status", "budget-daily", "budget-lifetime", "end-date"];
|
|
3205
|
+
var AD_GROUP_CREATE_FLAGS2 = ["campaign", "name", "objective", "status", "budget-daily", "budget-lifetime"];
|
|
3206
|
+
var AD_CREATE_FLAGS2 = ["ad-group", "existing-post", "name", "status"];
|
|
3207
|
+
var CAMPAIGN_UPDATE_FLAGS3 = ["name", "status", "budget-lifetime", "end-date"];
|
|
3208
|
+
var AD_GROUP_UPDATE_FLAGS2 = ["name", "status", "budget-daily", "budget-lifetime", "start-date", "end-date"];
|
|
3209
|
+
var AD_UPDATE_FLAGS2 = ["name", "status"];
|
|
3210
|
+
var TARGETING_SEARCH_FLAGS = ["account", "facet", "query", "limit"];
|
|
3211
|
+
var RESULTS_FLAGS2 = ["level", "period", "fields", "breakdowns", "from", "to", "sort", "limit", "offset"];
|
|
3198
3212
|
async function listLinkedInAvailableAccounts(client, _args, flags) {
|
|
3199
3213
|
validateFlags(flags, [], "manage linkedin accounts available");
|
|
3200
3214
|
return client.get("/manage/linkedin/accounts/available");
|
|
3201
3215
|
}
|
|
3216
|
+
async function uploadLinkedInMedia(client, _args, flags) {
|
|
3217
|
+
validateFlags(flags, MEDIA_UPLOAD_FLAGS3, "manage linkedin media upload");
|
|
3218
|
+
if (typeof flags.data === "string") {
|
|
3219
|
+
const body2 = parseDataFlag(flags, 'Use { "assets": [...] } for LinkedIn media upload');
|
|
3220
|
+
const qs2 = buildLinkedInMutationQuery(flags);
|
|
3221
|
+
return client.post(`/manage/linkedin/media${qs2}`, body2);
|
|
3222
|
+
}
|
|
3223
|
+
const source = buildLinkedInMediaUploadSource(flags);
|
|
3224
|
+
const body = { assets: [source] };
|
|
3225
|
+
const qs = buildLinkedInMutationQuery(flags);
|
|
3226
|
+
return client.post(`/manage/linkedin/media${qs}`, body);
|
|
3227
|
+
}
|
|
3228
|
+
async function getLinkedInMedia(client, args, flags) {
|
|
3229
|
+
validateFlags(flags, [], "manage linkedin media <urn>");
|
|
3230
|
+
const id = requireArg(args, 0, "media-urn", "Run: adkit manage linkedin media <media-urn>");
|
|
3231
|
+
const qs = buildLinkedInReadQuery(flags);
|
|
3232
|
+
return client.get(`/manage/linkedin/media/${id}${qs}`);
|
|
3233
|
+
}
|
|
3234
|
+
async function listLinkedInCampaigns(client, _args, flags) {
|
|
3235
|
+
validateFlags(flags, CAMPAIGN_LIST_FLAGS3, "manage linkedin campaigns list");
|
|
3236
|
+
const qs = buildLinkedInListQuery(flags, ["campaign-ids"]);
|
|
3237
|
+
return client.get(`/manage/linkedin/campaigns${qs}`);
|
|
3238
|
+
}
|
|
3239
|
+
async function getLinkedInCampaign(client, args, flags) {
|
|
3240
|
+
validateFlags(flags, [], "manage linkedin campaigns <id>");
|
|
3241
|
+
const id = requireArg(args, 0, "campaign-id", "Run: adkit manage linkedin campaigns <campaign-id>");
|
|
3242
|
+
const qs = buildLinkedInReadQuery(flags);
|
|
3243
|
+
return client.get(`/manage/linkedin/campaigns/${id}${qs}`);
|
|
3244
|
+
}
|
|
3245
|
+
async function createLinkedInCampaign(client, _args, flags) {
|
|
3246
|
+
validateFlags(flags, CAMPAIGN_CREATE_FLAGS3, "manage linkedin campaigns create");
|
|
3247
|
+
const qs = buildLinkedInMutationQuery(flags);
|
|
3248
|
+
if (typeof flags.data === "string") {
|
|
3249
|
+
const body2 = parseDataFlag(flags, 'Use { "campaigns": [...] } for LinkedIn campaign create');
|
|
3250
|
+
return client.post(`/manage/linkedin/campaigns${qs}`, body2);
|
|
3251
|
+
}
|
|
3252
|
+
const body = { campaigns: [buildLinkedInCampaignPayload(flags)] };
|
|
3253
|
+
return client.post(`/manage/linkedin/campaigns${qs}`, body);
|
|
3254
|
+
}
|
|
3255
|
+
async function updateLinkedInCampaign(client, args, flags) {
|
|
3256
|
+
validateFlags(flags, CAMPAIGN_UPDATE_FLAGS3, "manage linkedin campaigns update");
|
|
3257
|
+
const id = requireArg(args, 0, "campaign-id", "Run: adkit manage linkedin campaigns update <campaign-id> --status paused");
|
|
3258
|
+
const qs = buildLinkedInMutationQuery(flags);
|
|
3259
|
+
const body = typeof flags.data === "string" ? parseDataFlag(flags, "Use a flat LinkedIn campaign changes object") : buildLinkedInCampaignUpdatePayload(flags);
|
|
3260
|
+
return client.patch(`/manage/linkedin/campaigns/${id}${qs}`, body);
|
|
3261
|
+
}
|
|
3262
|
+
async function listLinkedInAdGroups(client, _args, flags) {
|
|
3263
|
+
validateFlags(flags, AD_GROUP_LIST_FLAGS3, "manage linkedin ad-groups list");
|
|
3264
|
+
const qs = buildLinkedInListQuery(flags, ["campaign-ids", "ad-group-ids"]);
|
|
3265
|
+
return client.get(`/manage/linkedin/ad-groups${qs}`);
|
|
3266
|
+
}
|
|
3267
|
+
async function getLinkedInAdGroup(client, args, flags) {
|
|
3268
|
+
validateFlags(flags, [], "manage linkedin ad-groups <id>");
|
|
3269
|
+
const id = requireArg(args, 0, "ad-group-id", "Run: adkit manage linkedin ad-groups <ad-group-id>");
|
|
3270
|
+
const qs = buildLinkedInReadQuery(flags);
|
|
3271
|
+
return client.get(`/manage/linkedin/ad-groups/${id}${qs}`);
|
|
3272
|
+
}
|
|
3273
|
+
async function createLinkedInAdGroup(client, _args, flags) {
|
|
3274
|
+
validateFlags(flags, AD_GROUP_CREATE_FLAGS2, "manage linkedin ad-groups create");
|
|
3275
|
+
const qs = buildLinkedInMutationQuery(flags);
|
|
3276
|
+
if (typeof flags.data === "string") {
|
|
3277
|
+
const body2 = parseDataFlag(flags, 'Use { "adGroups": [...] } for LinkedIn ad group create');
|
|
3278
|
+
return client.post(`/manage/linkedin/ad-groups${qs}`, body2);
|
|
3279
|
+
}
|
|
3280
|
+
const body = { adGroups: [buildLinkedInAdGroupPayload(flags)] };
|
|
3281
|
+
return client.post(`/manage/linkedin/ad-groups${qs}`, body);
|
|
3282
|
+
}
|
|
3283
|
+
async function updateLinkedInAdGroup(client, args, flags) {
|
|
3284
|
+
validateFlags(flags, AD_GROUP_UPDATE_FLAGS2, "manage linkedin ad-groups update");
|
|
3285
|
+
const id = requireArg(args, 0, "ad-group-id", "Run: adkit manage linkedin ad-groups update <ad-group-id> --status paused");
|
|
3286
|
+
const qs = buildLinkedInMutationQuery(flags);
|
|
3287
|
+
const body = typeof flags.data === "string" ? parseDataFlag(flags, "Use a flat LinkedIn ad group changes object") : buildLinkedInAdGroupUpdatePayload(flags);
|
|
3288
|
+
return client.patch(`/manage/linkedin/ad-groups/${id}${qs}`, body);
|
|
3289
|
+
}
|
|
3290
|
+
async function listLinkedInAds(client, _args, flags) {
|
|
3291
|
+
validateFlags(flags, AD_LIST_FLAGS3, "manage linkedin ads list");
|
|
3292
|
+
const qs = buildLinkedInListQuery(flags, ["campaign-ids"]);
|
|
3293
|
+
return client.get(`/manage/linkedin/ads${qs}`);
|
|
3294
|
+
}
|
|
3295
|
+
async function getLinkedInAd(client, args, flags) {
|
|
3296
|
+
validateFlags(flags, [], "manage linkedin ads <id>");
|
|
3297
|
+
const id = requireArg(args, 0, "ad-id", "Run: adkit manage linkedin ads <ad-id>");
|
|
3298
|
+
const qs = buildLinkedInReadQuery(flags);
|
|
3299
|
+
return client.get(`/manage/linkedin/ads/${id}${qs}`);
|
|
3300
|
+
}
|
|
3301
|
+
async function createLinkedInAd(client, _args, flags) {
|
|
3302
|
+
validateFlags(flags, AD_CREATE_FLAGS2, "manage linkedin ads create");
|
|
3303
|
+
const qs = buildLinkedInMutationQuery(flags);
|
|
3304
|
+
if (typeof flags.data === "string") {
|
|
3305
|
+
const body2 = parseDataFlag(flags, 'Use { "ads": [...] } for LinkedIn ad create');
|
|
3306
|
+
return client.post(`/manage/linkedin/ads${qs}`, body2);
|
|
3307
|
+
}
|
|
3308
|
+
if (typeof flags["existing-post"] !== "string") throw new CliError("MISSING_FLAG", "A LinkedIn ad needs a creative or an existing post", `Inline ad: --data '{"ads":[{"adGroupId":"...","creative":{"media":[{"role":"image","id":"urn:li:image:..."}]}}]}'. Reference ad: --existing-post <share/ugcPost URN>`);
|
|
3309
|
+
const body = { ads: [buildLinkedInAdPayload(flags)] };
|
|
3310
|
+
return client.post(`/manage/linkedin/ads${qs}`, body);
|
|
3311
|
+
}
|
|
3312
|
+
async function updateLinkedInAd(client, args, flags) {
|
|
3313
|
+
validateFlags(flags, AD_UPDATE_FLAGS2, "manage linkedin ads update");
|
|
3314
|
+
const id = requireArg(args, 0, "ad-id", "Run: adkit manage linkedin ads update <ad-id> --status paused");
|
|
3315
|
+
const qs = buildLinkedInMutationQuery(flags);
|
|
3316
|
+
const body = typeof flags.data === "string" ? parseDataFlag(flags, "Use a flat LinkedIn ad changes object (name/status only)") : buildLinkedInAdUpdatePayload(flags);
|
|
3317
|
+
return client.patch(`/manage/linkedin/ads/${id}${qs}`, body);
|
|
3318
|
+
}
|
|
3319
|
+
async function searchLinkedInTargeting(client, _args, flags) {
|
|
3320
|
+
validateFlags(flags, TARGETING_SEARCH_FLAGS, "manage linkedin targeting-search");
|
|
3321
|
+
const facet = requireFlag(flags, "facet", "Use jobTitles, seniorities, jobFunctions, industries, companySizes, companies, skills, interests, geoLocations, ageRanges, genders, or customAudiences");
|
|
3322
|
+
const accountId = typeof flags.account === "string" ? flags.account : void 0;
|
|
3323
|
+
const query = typeof flags.query === "string" ? flags.query : void 0;
|
|
3324
|
+
const limit = typeof flags.limit === "string" ? flags.limit : void 0;
|
|
3325
|
+
const qs = queryString({ accountId, facet, query, limit });
|
|
3326
|
+
return client.get(`/manage/linkedin/targeting-search${qs}`);
|
|
3327
|
+
}
|
|
3328
|
+
async function listLinkedInResults(client, _args, flags) {
|
|
3329
|
+
validateFlags(flags, RESULTS_FLAGS2, "manage linkedin results");
|
|
3330
|
+
const accountId = typeof flags.account === "string" ? flags.account : void 0;
|
|
3331
|
+
const level = requireFlag(flags, "level", "Use --level account, campaigns, ad-groups, or ads");
|
|
3332
|
+
const query = queryString({
|
|
3333
|
+
accountId,
|
|
3334
|
+
level,
|
|
3335
|
+
period: typeof flags.period === "string" ? flags.period : void 0,
|
|
3336
|
+
fields: typeof flags.fields === "string" ? flags.fields : void 0,
|
|
3337
|
+
from: typeof flags.from === "string" ? flags.from : void 0,
|
|
3338
|
+
to: typeof flags.to === "string" ? flags.to : void 0,
|
|
3339
|
+
breakdowns: typeof flags.breakdowns === "string" ? flags.breakdowns : void 0,
|
|
3340
|
+
sort: typeof flags.sort === "string" ? flags.sort : void 0,
|
|
3341
|
+
limit: typeof flags.limit === "string" ? flags.limit : void 0,
|
|
3342
|
+
offset: typeof flags.offset === "string" ? flags.offset : void 0
|
|
3343
|
+
});
|
|
3344
|
+
return client.get(`/manage/linkedin/results${query}`);
|
|
3345
|
+
}
|
|
3346
|
+
function buildLinkedInListQuery(flags, filterKeys) {
|
|
3347
|
+
const accountId = typeof flags.account === "string" ? flags.account : void 0;
|
|
3348
|
+
const campaignIds = filterKeys.includes("campaign-ids") && typeof flags["campaign-ids"] === "string" ? flags["campaign-ids"] : void 0;
|
|
3349
|
+
const adGroupIds = filterKeys.includes("ad-group-ids") && typeof flags["ad-group-ids"] === "string" ? flags["ad-group-ids"] : void 0;
|
|
3350
|
+
const limit = typeof flags.limit === "string" ? flags.limit : void 0;
|
|
3351
|
+
const offset = typeof flags.offset === "string" ? flags.offset : void 0;
|
|
3352
|
+
return queryString({ accountId, campaignIds, adGroupIds, limit, offset });
|
|
3353
|
+
}
|
|
3354
|
+
function buildLinkedInReadQuery(flags) {
|
|
3355
|
+
const accountId = typeof flags.account === "string" ? flags.account : void 0;
|
|
3356
|
+
return queryString({ accountId });
|
|
3357
|
+
}
|
|
3358
|
+
function buildLinkedInMutationQuery(flags) {
|
|
3359
|
+
const accountId = typeof flags.account === "string" ? flags.account : void 0;
|
|
3360
|
+
const publish = flags.publish === true ? "true" : void 0;
|
|
3361
|
+
return queryString({ accountId, publish });
|
|
3362
|
+
}
|
|
3363
|
+
function buildLinkedInCampaignPayload(flags) {
|
|
3364
|
+
const payload = {};
|
|
3365
|
+
payload.name = requireFlag(flags, "name", 'Run: adkit manage linkedin campaigns create --name "Spring Sale"');
|
|
3366
|
+
if (typeof flags.status === "string") payload.status = flags.status;
|
|
3367
|
+
if (typeof flags["end-date"] === "string") payload.endDate = flags["end-date"];
|
|
3368
|
+
const budget = buildLinkedInBudget(flags);
|
|
3369
|
+
if (budget) payload.budget = budget;
|
|
3370
|
+
if (budget?.lifetime !== void 0 && payload.endDate === void 0) throw new CliError("MISSING_FLAG", "A lifetime budget needs an end date", "Pass --end-date YYYY-MM-DD alongside --budget-lifetime");
|
|
3371
|
+
return payload;
|
|
3372
|
+
}
|
|
3373
|
+
function buildLinkedInAdGroupPayload(flags) {
|
|
3374
|
+
const payload = {};
|
|
3375
|
+
payload.campaignId = requireFlag(flags, "campaign", 'Run: adkit manage linkedin ad-groups create --campaign <campaign-id> --name "US Broad"');
|
|
3376
|
+
payload.name = requireFlag(flags, "name", 'Run: adkit manage linkedin ad-groups create --name "US Broad"');
|
|
3377
|
+
const budget = buildLinkedInBudget(flags);
|
|
3378
|
+
if (budget) payload.budget = budget;
|
|
3379
|
+
if (typeof flags.objective === "string") payload.objective = flags.objective;
|
|
3380
|
+
if (typeof flags.status === "string") payload.status = flags.status;
|
|
3381
|
+
return payload;
|
|
3382
|
+
}
|
|
3383
|
+
function buildLinkedInAdPayload(flags) {
|
|
3384
|
+
const payload = {};
|
|
3385
|
+
payload.adGroupId = requireFlag(flags, "ad-group", "Run: adkit manage linkedin ads create --ad-group <ad-group-id> --existing-post <share/ugcPost URN>");
|
|
3386
|
+
payload.existingPostId = requireFlag(flags, "existing-post", "Run: adkit manage linkedin ads create --ad-group <ad-group-id> --existing-post <share/ugcPost URN>");
|
|
3387
|
+
if (typeof flags.name === "string") payload.name = flags.name;
|
|
3388
|
+
if (typeof flags.status === "string") payload.status = flags.status;
|
|
3389
|
+
return payload;
|
|
3390
|
+
}
|
|
3391
|
+
function buildLinkedInCampaignUpdatePayload(flags) {
|
|
3392
|
+
const payload = {};
|
|
3393
|
+
if (typeof flags.name === "string") payload.name = flags.name;
|
|
3394
|
+
if (typeof flags.status === "string") payload.status = flags.status;
|
|
3395
|
+
if (typeof flags["end-date"] === "string") payload.endDate = flags["end-date"];
|
|
3396
|
+
const budget = buildLinkedInBudget(flags);
|
|
3397
|
+
if (budget) payload.budget = budget;
|
|
3398
|
+
if (budget?.lifetime !== void 0 && payload.endDate === void 0) throw new CliError("MISSING_FLAG", "A lifetime budget needs an end date", "Pass --end-date YYYY-MM-DD alongside --budget-lifetime");
|
|
3399
|
+
return payload;
|
|
3400
|
+
}
|
|
3401
|
+
function buildLinkedInAdGroupUpdatePayload(flags) {
|
|
3402
|
+
const payload = {};
|
|
3403
|
+
if (typeof flags.name === "string") payload.name = flags.name;
|
|
3404
|
+
if (typeof flags.status === "string") payload.status = flags.status;
|
|
3405
|
+
if (typeof flags["start-date"] === "string") payload.startDate = flags["start-date"];
|
|
3406
|
+
if (typeof flags["end-date"] === "string") payload.endDate = flags["end-date"];
|
|
3407
|
+
const budget = buildLinkedInBudget(flags);
|
|
3408
|
+
if (budget) payload.budget = budget;
|
|
3409
|
+
return payload;
|
|
3410
|
+
}
|
|
3411
|
+
function buildLinkedInAdUpdatePayload(flags) {
|
|
3412
|
+
const payload = {};
|
|
3413
|
+
if (typeof flags.name === "string") payload.name = flags.name;
|
|
3414
|
+
if (typeof flags.status === "string") payload.status = flags.status;
|
|
3415
|
+
return payload;
|
|
3416
|
+
}
|
|
3417
|
+
function buildLinkedInBudget(flags) {
|
|
3418
|
+
const daily = readNumberFlag3(flags, "budget-daily");
|
|
3419
|
+
const lifetime = readNumberFlag3(flags, "budget-lifetime");
|
|
3420
|
+
if (daily === void 0 && lifetime === void 0) return void 0;
|
|
3421
|
+
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");
|
|
3422
|
+
return daily !== void 0 ? { daily } : { lifetime };
|
|
3423
|
+
}
|
|
3424
|
+
function readNumberFlag3(flags, key) {
|
|
3425
|
+
const value = flags[key];
|
|
3426
|
+
if (value === void 0) return void 0;
|
|
3427
|
+
if (typeof value !== "string") throw new CliError("INVALID_VALUE", `Use one --${key} value`, `Run: adkit manage linkedin --help`);
|
|
3428
|
+
const parsed = Number(value);
|
|
3429
|
+
if (!Number.isFinite(parsed)) throw new CliError("INVALID_VALUE", `Invalid number for --${key}: ${value}`, `Use a numeric value for --${key}`);
|
|
3430
|
+
return parsed;
|
|
3431
|
+
}
|
|
3432
|
+
function readSingleFlagValue3(flags, key, duplicateHint) {
|
|
3433
|
+
const value = flags[key];
|
|
3434
|
+
if (Array.isArray(value)) {
|
|
3435
|
+
if (value.length > 1) throw new CliError("INVALID_VALUE", `Use one --${key} value`, duplicateHint);
|
|
3436
|
+
return typeof value[0] === "string" ? value[0] : void 0;
|
|
3437
|
+
}
|
|
3438
|
+
return typeof value === "string" ? value : void 0;
|
|
3439
|
+
}
|
|
3440
|
+
function inferLinkedInMediaContentType(filename) {
|
|
3441
|
+
const extension = extname3(filename).toLowerCase();
|
|
3442
|
+
if (extension === ".jpg" || extension === ".jpeg") return "image/jpeg";
|
|
3443
|
+
if (extension === ".png") return "image/png";
|
|
3444
|
+
if (extension === ".gif") return "image/gif";
|
|
3445
|
+
if (extension === ".mp4") return "video/mp4";
|
|
3446
|
+
if (extension === ".mov") return "video/quicktime";
|
|
3447
|
+
return void 0;
|
|
3448
|
+
}
|
|
3449
|
+
function buildLinkedInMediaUploadSource(flags) {
|
|
3450
|
+
const filePath = readSingleFlagValue3(flags, "file", "Use one media source per command, or switch to --data for batch upload");
|
|
3451
|
+
const url = readSingleFlagValue3(flags, "url", "Use one media source per command, or switch to --data for batch upload");
|
|
3452
|
+
const base64 = readSingleFlagValue3(flags, "base64", "Use one media source per command, or switch to --data for batch upload");
|
|
3453
|
+
const adkitMediaId = readSingleFlagValue3(flags, "adkit-media", "Use one media source per command, or switch to --data for batch upload");
|
|
3454
|
+
const temporaryUploadId = readSingleFlagValue3(flags, "temporary-upload", "Use one media source per command, or switch to --data for batch upload");
|
|
3455
|
+
const sourceCount = [filePath, url, base64, adkitMediaId, temporaryUploadId].filter(Boolean).length;
|
|
3456
|
+
if (sourceCount === 0) throw new CliError("MISSING_FLAG", "Missing required media source flag", "Use --file, --url, --base64, --adkit-media, or --temporary-upload");
|
|
3457
|
+
if (sourceCount > 1) throw new CliError("INVALID_VALUE", "Use exactly one media source flag", "Use one of --file, --url, --base64, --adkit-media, or --temporary-upload");
|
|
3458
|
+
const filename = readSingleFlagValue3(flags, "filename", "Use one --filename value");
|
|
3459
|
+
const contentType = readSingleFlagValue3(flags, "content-type", "Use one --content-type value");
|
|
3460
|
+
const name = readSingleFlagValue3(flags, "name", "Use one --name value");
|
|
3461
|
+
if (filePath) {
|
|
3462
|
+
const resolvedPath = resolve3(filePath);
|
|
3463
|
+
const resolvedFilename = filename ?? basename3(resolvedPath);
|
|
3464
|
+
const resolvedContentType = contentType ?? inferLinkedInMediaContentType(resolvedFilename);
|
|
3465
|
+
const data = readFileSync4(resolvedPath).toString("base64");
|
|
3466
|
+
const source2 = { source: "base64", data, filename: resolvedFilename };
|
|
3467
|
+
if (resolvedContentType) source2.contentType = resolvedContentType;
|
|
3468
|
+
if (name) source2.name = name;
|
|
3469
|
+
return source2;
|
|
3470
|
+
}
|
|
3471
|
+
if (url) {
|
|
3472
|
+
const source2 = { source: "url", url };
|
|
3473
|
+
if (filename) source2.filename = filename;
|
|
3474
|
+
if (contentType) source2.contentType = contentType;
|
|
3475
|
+
if (name) source2.name = name;
|
|
3476
|
+
return source2;
|
|
3477
|
+
}
|
|
3478
|
+
if (base64) {
|
|
3479
|
+
if (!filename) throw new CliError("MISSING_FLAG", "Missing required flag: `--filename`", "Base64 uploads need a filename so LinkedIn can infer media type");
|
|
3480
|
+
const source2 = { source: "base64", data: base64, filename };
|
|
3481
|
+
if (contentType) source2.contentType = contentType;
|
|
3482
|
+
if (name) source2.name = name;
|
|
3483
|
+
return source2;
|
|
3484
|
+
}
|
|
3485
|
+
if (adkitMediaId) {
|
|
3486
|
+
const source2 = { source: "adkit_media", mediaId: adkitMediaId };
|
|
3487
|
+
if (name) source2.name = name;
|
|
3488
|
+
return source2;
|
|
3489
|
+
}
|
|
3490
|
+
if (!temporaryUploadId) throw new CliError("MISSING_FLAG", "Missing required flag: `--temporary-upload`", "Use --temporary-upload <upload-id>");
|
|
3491
|
+
const source = { source: "temporary_upload", uploadId: temporaryUploadId };
|
|
3492
|
+
if (name) source.name = name;
|
|
3493
|
+
return source;
|
|
3494
|
+
}
|
|
3202
3495
|
|
|
3203
3496
|
// src/commands/status.ts
|
|
3204
3497
|
function isStatusResponse(value) {
|
|
@@ -3659,8 +3952,8 @@ async function pollMedia(client, mediaIds, fields) {
|
|
|
3659
3952
|
if (!currentMedia || typeof currentMedia.status !== "string") throw new CliError("SERVER_ERROR", `Invalid media response while polling ${id}`);
|
|
3660
3953
|
media = currentMedia;
|
|
3661
3954
|
if (currentMedia.status === "completed" || currentMedia.status === "failed") break;
|
|
3662
|
-
await new Promise((
|
|
3663
|
-
setTimeout(
|
|
3955
|
+
await new Promise((resolve4) => {
|
|
3956
|
+
setTimeout(resolve4, 2e3);
|
|
3664
3957
|
});
|
|
3665
3958
|
}
|
|
3666
3959
|
results.push(media);
|
|
@@ -3917,14 +4210,14 @@ function toGoogleSearchTermDisplayRows(row) {
|
|
|
3917
4210
|
import { createRequire } from "node:module";
|
|
3918
4211
|
|
|
3919
4212
|
// src/update-check.ts
|
|
3920
|
-
import { readFileSync as
|
|
4213
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
|
|
3921
4214
|
import { join as join2 } from "node:path";
|
|
3922
4215
|
var CACHE_PATH = join2(CONFIG_DIR, "update-check.json");
|
|
3923
4216
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
3924
4217
|
var REGISTRY_URL = "https://registry.npmjs.org/@adkit/cli/latest";
|
|
3925
4218
|
function readCache() {
|
|
3926
4219
|
try {
|
|
3927
|
-
const raw =
|
|
4220
|
+
const raw = readFileSync5(CACHE_PATH, "utf-8");
|
|
3928
4221
|
const parsed = JSON.parse(raw);
|
|
3929
4222
|
if (!parsed || typeof parsed !== "object") return void 0;
|
|
3930
4223
|
const cache = parsed;
|
|
@@ -4123,6 +4416,7 @@ var DEFAULT_BASE_URL = "https://app.adkit.so/api/v1";
|
|
|
4123
4416
|
var GOOGLE_NUMERIC_ID_PATTERN = /^\d+$/;
|
|
4124
4417
|
var GOOGLE_RESULTS_LEVEL_ACTIONS = /* @__PURE__ */ new Set(["campaigns", "ad-groups", "ads"]);
|
|
4125
4418
|
var TIKTOK_NUMERIC_ID_PATTERN = /^\d+$/;
|
|
4419
|
+
var LINKEDIN_ID_PATTERN = /[\d:_]/;
|
|
4126
4420
|
var USAGE = `
|
|
4127
4421
|
Usage: adkit <command> [options]
|
|
4128
4422
|
|
|
@@ -4573,7 +4867,7 @@ Flags:
|
|
|
4573
4867
|
--from <date> Start date, YYYY-MM-DD
|
|
4574
4868
|
--to <date> End date, YYYY-MM-DD
|
|
4575
4869
|
--fields <list> Normalized fields, e.g. spend, impressions, clicks, conversionEvents, etc
|
|
4576
|
-
--conversion-event-keys <list> purchase,lead or all
|
|
4870
|
+
--conversion-event-keys <list> purchase,lead,messaging_conversation_started or all
|
|
4577
4871
|
--attribution-window <w> 1d_click, 7d_click, 1d_view, 7d_click_1d_view, 28d_click_1d_view
|
|
4578
4872
|
--breakdowns <list> day, week, month, age, gender, country, region, device_platform, publisher_platform, platform_position, impression_device
|
|
4579
4873
|
--confirm Required with --period maximum plus any breakdown
|
|
@@ -4596,6 +4890,8 @@ Examples:
|
|
|
4596
4890
|
adkit manage meta results ads --period maximum --breakdowns country --confirm
|
|
4597
4891
|
# Conversion data for a campaign
|
|
4598
4892
|
adkit manage meta results --campaign-ids 123 --conversion-event-keys purchase,lead
|
|
4893
|
+
# Messenger conversations for click-to-Messenger campaigns
|
|
4894
|
+
adkit manage meta results --campaign-ids 123 --conversion-event-keys messaging_conversation_started
|
|
4599
4895
|
# Raw API response with custom metrics
|
|
4600
4896
|
adkit manage meta results --campaign-ids 123 --raw --fields spend,impressions,conversionEvents`.trim();
|
|
4601
4897
|
var CREATIVE_HELP = `adkit manage meta creatives \u2014 Meta ad creatives
|
|
@@ -5649,6 +5945,177 @@ Note:
|
|
|
5649
5945
|
|
|
5650
5946
|
Example:
|
|
5651
5947
|
adkit manage tiktok results --level campaigns --fields spend,impressions,clicks --breakdowns day --from 2026-05-01 --to 2026-05-15`.trim(),
|
|
5948
|
+
"linkedin campaigns": `adkit manage linkedin campaigns \u2014 LinkedIn campaigns (LinkedIn "Campaign Groups")
|
|
5949
|
+
|
|
5950
|
+
list List campaigns
|
|
5951
|
+
create Create a campaign draft
|
|
5952
|
+
update <id> Update a campaign (rename, pause/resume, budget)
|
|
5953
|
+
<id> View campaign details
|
|
5954
|
+
|
|
5955
|
+
Naming note:
|
|
5956
|
+
AdKit "campaigns" are LinkedIn "Campaign Groups". Objective, daily budgets, and targeting live on ad-groups; this tier takes lifetime budgets only.
|
|
5957
|
+
IDs copied from Campaign Manager's "Campaigns" tab are AdKit ad-group IDs, not campaign IDs.
|
|
5958
|
+
|
|
5959
|
+
Flags:
|
|
5960
|
+
${FLAG.account}
|
|
5961
|
+
${FLAG.publish}
|
|
5962
|
+
${FLAG.data}
|
|
5963
|
+
--limit <n> Max rows for list
|
|
5964
|
+
--offset <n> Row offset for list
|
|
5965
|
+
--campaign-ids <ids> Comma-separated campaign IDs for filtered reads
|
|
5966
|
+
--name <name> Campaign name
|
|
5967
|
+
--status <s> create: active or draft (defaults to draft). update: active, paused, or archived
|
|
5968
|
+
--budget-lifetime <n> Lifetime budget in account currency
|
|
5969
|
+
--end-date <date> YYYY-MM-DD; required with --budget-lifetime
|
|
5970
|
+
|
|
5971
|
+
Notes:
|
|
5972
|
+
This tier takes lifetime budgets only; a daily budget here returns a 500. Set daily budgets on ad-groups.
|
|
5973
|
+
A live campaign can never go back to draft.
|
|
5974
|
+
|
|
5975
|
+
Examples:
|
|
5976
|
+
adkit manage linkedin campaigns list --account 512345678
|
|
5977
|
+
adkit manage linkedin campaigns create --name "Spring Sale" --budget-lifetime 500 --end-date 2026-08-31
|
|
5978
|
+
adkit manage linkedin campaigns update 628940516 --account 512345678 --status paused
|
|
5979
|
+
adkit manage linkedin campaigns 628940516 --account 512345678`.trim(),
|
|
5980
|
+
"linkedin ad-groups": `adkit manage linkedin ad-groups \u2014 LinkedIn ad groups (LinkedIn "Campaigns"/"Ad sets")
|
|
5981
|
+
|
|
5982
|
+
list List ad groups
|
|
5983
|
+
create Create an ad group draft
|
|
5984
|
+
update <id> Update an ad group (rename, pause/resume, budget, dates, targeting)
|
|
5985
|
+
<id> View ad group details
|
|
5986
|
+
|
|
5987
|
+
Naming note:
|
|
5988
|
+
AdKit "ad-groups" are what Campaign Manager now calls "Campaigns" (formerly "Ad sets"). Objective, budget, and targeting live here.
|
|
5989
|
+
IDs copied from Campaign Manager's "Campaigns" tab are AdKit ad-group IDs.
|
|
5990
|
+
|
|
5991
|
+
Flags:
|
|
5992
|
+
${FLAG.account}
|
|
5993
|
+
${FLAG.publish}
|
|
5994
|
+
${FLAG.data}
|
|
5995
|
+
--limit <n> Max rows for list
|
|
5996
|
+
--offset <n> Row offset for list
|
|
5997
|
+
--campaign-ids <ids> Comma-separated parent campaign IDs for filtered reads
|
|
5998
|
+
--ad-group-ids <ids> Comma-separated ad group IDs for filtered reads
|
|
5999
|
+
--campaign <id> Parent campaign ID (create only)
|
|
6000
|
+
--name <name> Ad group name
|
|
6001
|
+
--objective <type> brand_awareness, engagement, website_visits, video_views, website_conversions
|
|
6002
|
+
--status <s> create: active or draft (defaults to draft). update: active, paused, or archived
|
|
6003
|
+
--budget-daily <n> Daily budget in account currency
|
|
6004
|
+
--budget-lifetime <n> Lifetime budget in account currency
|
|
6005
|
+
--start-date <date> YYYY-MM-DD (update only)
|
|
6006
|
+
--end-date <date> YYYY-MM-DD (update only)
|
|
6007
|
+
|
|
6008
|
+
Targeting (--data only):
|
|
6009
|
+
targeting is nested: { geoLocations: { include: [{ type: "country", code }] }, audience: { <facet>: { include?: [ids], exclude?: [ids] } } }.
|
|
6010
|
+
Values are bare IDs \u2014 use "adkit manage linkedin targeting-search" to find them.
|
|
6011
|
+
Facets: jobTitles, seniorities, jobFunctions, industries, companySizes, companies, skills, interests, customAudiences, ageRanges, genders.
|
|
6012
|
+
ageRanges/genders are include-only (LinkedIn rejects them in exclude): ageRanges take tuples like (25,34), genders take FEMALE/MALE.
|
|
6013
|
+
customAudiences goes inside targeting.audience (never top-level): pass the segment ID/URN. Matched Audiences are created in Campaign Manager, but you list them with "adkit manage linkedin targeting-search --facet customAudiences". Choose a language via platformOverrides.locale.
|
|
6014
|
+
|
|
6015
|
+
Notes:
|
|
6016
|
+
A live ad group can never go back to draft. targeting on update is a full replace (send the whole targeting object via --data).
|
|
6017
|
+
|
|
6018
|
+
Examples:
|
|
6019
|
+
adkit manage linkedin ad-groups create --campaign 628940516 --name "US Broad" --budget-daily 50 --objective website_visits
|
|
6020
|
+
adkit manage linkedin ad-groups update 361387516 --account 512345678 --status paused
|
|
6021
|
+
adkit manage linkedin ad-groups create --data '{"adGroups":[{"campaignId":"628940516","name":"US Senior Eng","budget":{"daily":50},"targeting":{"geoLocations":{"include":[{"type":"country","code":"103644278"}]},"audience":{"seniorities":{"include":["6"]},"customAudiences":{"include":["123456"]}}}}]}'`.trim(),
|
|
6022
|
+
"linkedin ads": `adkit manage linkedin ads \u2014 LinkedIn ads
|
|
6023
|
+
|
|
6024
|
+
list List ads
|
|
6025
|
+
create Create an ad draft
|
|
6026
|
+
update <id> Update an ad (rename, pause/resume) \u2014 name and status only
|
|
6027
|
+
<id> View ad details
|
|
6028
|
+
|
|
6029
|
+
Flags:
|
|
6030
|
+
${FLAG.account}
|
|
6031
|
+
${FLAG.publish}
|
|
6032
|
+
${FLAG.data}
|
|
6033
|
+
--limit <n> Max rows for list
|
|
6034
|
+
--offset <n> Row offset for list
|
|
6035
|
+
--campaign-ids <ids> Comma-separated parent ad-group IDs for filtered reads
|
|
6036
|
+
--ad-group <id> Parent ad group ID (create only)
|
|
6037
|
+
--existing-post <urn> Reference an existing share/ugcPost URN (create only)
|
|
6038
|
+
--name <name> Ad name
|
|
6039
|
+
--status <s> create: active or draft (defaults to draft). update: active, paused, or archived
|
|
6040
|
+
|
|
6041
|
+
Create:
|
|
6042
|
+
Inline ad: pass --data with a creative \u2014 keys are primaryTexts, headlines, media, url, cta. creative.media needs at least one { role: "image"|"video", id: "urn:li:image:..." } entry.
|
|
6043
|
+
Reference ad: pass --existing-post <share/ugcPost URN>; omit creative entirely (its text/url/media are ignored).
|
|
6044
|
+
creative.cta is the button label \u2014 one of: apply, download, view_quote, learn_more, sign_up, subscribe, register, join, attend, request_demo, see_more, buy_now, shop_now. It needs creative.url and cannot be combined with existing-post.
|
|
6045
|
+
Media IDs are the opaque URNs returned by "adkit manage linkedin media upload".
|
|
6046
|
+
|
|
6047
|
+
Update:
|
|
6048
|
+
Rename + pause/resume only \u2014 creative content and the parent ad-group are locked after create.
|
|
6049
|
+
Pausing an ACTIVE ad only works once LinkedIn review has APPROVED the creative; a still-in-review ad rejects the pause. A live ad can never go back to draft.
|
|
6050
|
+
|
|
6051
|
+
Examples:
|
|
6052
|
+
adkit manage linkedin ads list --account 512345678
|
|
6053
|
+
adkit manage linkedin ads create --data '{"ads":[{"adGroupId":"361387516","creative":{"media":[{"role":"image","id":"urn:li:image:C123"}]}}]}'
|
|
6054
|
+
adkit manage linkedin ads create --ad-group 361387516 --existing-post urn:li:share:123456
|
|
6055
|
+
adkit manage linkedin ads update 361387516 --account 512345678 --status paused`.trim(),
|
|
6056
|
+
"linkedin media": `adkit manage linkedin media \u2014 Upload creative media and fetch by URN
|
|
6057
|
+
|
|
6058
|
+
upload Upload an image or video and return a LinkedIn media URN
|
|
6059
|
+
<urn> Fetch a single media asset by URN
|
|
6060
|
+
|
|
6061
|
+
Flags:
|
|
6062
|
+
--file <path> Local image/video file
|
|
6063
|
+
--url <url> Public image/video URL
|
|
6064
|
+
--base64 <data> Base64 image/video data
|
|
6065
|
+
--adkit-media <id> Existing AdKit Studio media ID
|
|
6066
|
+
--temporary-upload <id> Temporary upload ID from the upload URL helper
|
|
6067
|
+
--filename <name> Required with --base64, optional with --url
|
|
6068
|
+
--content-type <t> image/jpeg, image/png, image/gif, video/mp4, or video/quicktime
|
|
6069
|
+
--name <name> Media asset name
|
|
6070
|
+
${FLAG.account}
|
|
6071
|
+
--data <json> Batch body: {"assets":[...]}
|
|
6072
|
+
|
|
6073
|
+
Note:
|
|
6074
|
+
Media IDs are opaque URNs (e.g. urn:li:image:C123) returned by upload. Pass them into ad creatives via --data.
|
|
6075
|
+
|
|
6076
|
+
Examples:
|
|
6077
|
+
adkit manage linkedin media upload --url https://example.com/hero.png --filename hero.png --account 512345678
|
|
6078
|
+
adkit manage linkedin media upload --adkit-media media_123 --account 512345678
|
|
6079
|
+
adkit manage linkedin media urn:li:image:C123 --account 512345678`.trim(),
|
|
6080
|
+
"linkedin targeting-search": `adkit manage linkedin targeting-search \u2014 Look up bare targeting IDs
|
|
6081
|
+
|
|
6082
|
+
search Search a facet for the bare IDs used in ad group targeting
|
|
6083
|
+
|
|
6084
|
+
Flags:
|
|
6085
|
+
--facet <facet> jobTitles, seniorities, jobFunctions, industries, companySizes, companies, skills, interests, geoLocations, ageRanges, genders, customAudiences
|
|
6086
|
+
--query <text> Search text; optional for the enum facets (seniorities, jobFunctions, companySizes, ageRanges, genders, customAudiences)
|
|
6087
|
+
--limit <n> Max results
|
|
6088
|
+
--account <id> LinkedIn ad account; required when several accounts are connected
|
|
6089
|
+
|
|
6090
|
+
Note:
|
|
6091
|
+
Returned IDs are bare \u2014 feed them into ad-group targeting via --data.
|
|
6092
|
+
Matched Audiences are created in Campaign Manager, but you list them here with --facet customAudiences.
|
|
6093
|
+
|
|
6094
|
+
Examples:
|
|
6095
|
+
adkit manage linkedin targeting-search --facet jobTitles --query engineer
|
|
6096
|
+
adkit manage linkedin targeting-search --facet seniorities
|
|
6097
|
+
adkit manage linkedin targeting-search --facet customAudiences`.trim(),
|
|
6098
|
+
"linkedin results": `adkit manage linkedin results \u2014 LinkedIn performance reporting
|
|
6099
|
+
|
|
6100
|
+
list List reporting rows
|
|
6101
|
+
|
|
6102
|
+
Flags:
|
|
6103
|
+
${FLAG.account}
|
|
6104
|
+
--level <level> Required: account, campaigns, ad-groups, ads
|
|
6105
|
+
--period <p> Rolling window, e.g. 14d (default: 14d); or use --from/--to
|
|
6106
|
+
--fields <csv> Normalized fields, e.g. cost,impressions,clicks
|
|
6107
|
+
--breakdowns <csv> Row breakdowns, e.g. day
|
|
6108
|
+
--from <date> YYYY-MM-DD
|
|
6109
|
+
--to <date> YYYY-MM-DD
|
|
6110
|
+
--sort <field> Optional normalized sort field, e.g. cost (default: cost). Rows always sort descending.
|
|
6111
|
+
--limit <n> Max rows
|
|
6112
|
+
--offset <n> Row offset
|
|
6113
|
+
|
|
6114
|
+
Note:
|
|
6115
|
+
--level is required. Results come back as a flat { results, currency, timezone, period, ... } envelope. Spend is reported as the canonical "cost" field. Sort is descending-only.
|
|
6116
|
+
|
|
6117
|
+
Example:
|
|
6118
|
+
adkit manage linkedin results --level campaigns --fields cost,impressions,clicks --breakdowns day --from 2026-05-01 --to 2026-05-15`.trim(),
|
|
5652
6119
|
"reddit accounts": `adkit manage reddit accounts \u2014 Reddit Ads accounts
|
|
5653
6120
|
|
|
5654
6121
|
list List connected Reddit ad accounts
|
|
@@ -5710,7 +6177,7 @@ Platforms:
|
|
|
5710
6177
|
tiktok TikTok Ads
|
|
5711
6178
|
reddit Reddit Ads
|
|
5712
6179
|
x X Ads raw API access
|
|
5713
|
-
linkedin LinkedIn Ads (
|
|
6180
|
+
linkedin LinkedIn Ads (campaigns, ad groups, ads, targeting, results)
|
|
5714
6181
|
drafts Manage drafts across platforms
|
|
5715
6182
|
platform-api-requests Send raw API requests (escape hatch)
|
|
5716
6183
|
|
|
@@ -5806,16 +6273,26 @@ Examples:
|
|
|
5806
6273
|
linkedin: `adkit manage linkedin \u2014 LinkedIn Ads management
|
|
5807
6274
|
|
|
5808
6275
|
Entity groups:
|
|
5809
|
-
accounts
|
|
6276
|
+
accounts Discover LinkedIn ad accounts (available)
|
|
6277
|
+
media Upload creative and fetch media by URN
|
|
6278
|
+
campaigns Manage campaigns (LinkedIn "Campaign Groups")
|
|
6279
|
+
ad-groups Manage ad groups (LinkedIn "Campaigns"/"Ad sets" \u2014 objective, budget, targeting)
|
|
6280
|
+
ads Manage ads
|
|
6281
|
+
targeting-search Look up bare targeting IDs by facet
|
|
6282
|
+
results Performance metrics
|
|
5810
6283
|
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
6284
|
+
Naming note (Campaign Manager renamed things in Oct 2025):
|
|
6285
|
+
AdKit "campaigns" = LinkedIn "Campaign Groups"
|
|
6286
|
+
AdKit "ad-groups" = LinkedIn "Campaigns" / "Ad sets" \u2014 this is where objective, budget, and targeting live.
|
|
6287
|
+
IDs copied from Campaign Manager's "Campaigns" tab are AdKit ad-group IDs.
|
|
5814
6288
|
|
|
5815
6289
|
General flags:
|
|
5816
6290
|
${FLAG.account}
|
|
6291
|
+
${FLAG.publish}
|
|
6292
|
+
${FLAG.data}
|
|
5817
6293
|
|
|
5818
|
-
|
|
6294
|
+
${rawPlatformCommandLine("linkedin")}
|
|
6295
|
+
Run adkit manage linkedin <group> --help for details.`.trim(),
|
|
5819
6296
|
drafts: `adkit manage drafts \u2014 Manage drafts
|
|
5820
6297
|
|
|
5821
6298
|
list List all drafts
|
|
@@ -7588,8 +8065,104 @@ async function main() {
|
|
|
7588
8065
|
throw new CliError("UNKNOWN_COMMAND", `Unknown action: linkedin accounts ${action}`, "Available: available.");
|
|
7589
8066
|
}
|
|
7590
8067
|
break;
|
|
8068
|
+
case "media": {
|
|
8069
|
+
if (!action) {
|
|
8070
|
+
showHelp("linkedin media", flags.help === "full");
|
|
8071
|
+
return;
|
|
8072
|
+
}
|
|
8073
|
+
switch (action) {
|
|
8074
|
+
case "upload":
|
|
8075
|
+
data = await uploadLinkedInMedia(client, restArgs, flags);
|
|
8076
|
+
break;
|
|
8077
|
+
case "get":
|
|
8078
|
+
data = await getLinkedInMedia(client, restArgs, flags);
|
|
8079
|
+
break;
|
|
8080
|
+
default:
|
|
8081
|
+
if (LINKEDIN_ID_PATTERN.test(action)) data = await getLinkedInMedia(client, [action], flags);
|
|
8082
|
+
else throw new CliError("UNKNOWN_COMMAND", `Unknown action: linkedin media ${action}`, "Available: upload, get");
|
|
8083
|
+
}
|
|
8084
|
+
break;
|
|
8085
|
+
}
|
|
8086
|
+
case "campaigns": {
|
|
8087
|
+
if (!action) {
|
|
8088
|
+
showHelp("linkedin campaigns", flags.help === "full");
|
|
8089
|
+
return;
|
|
8090
|
+
}
|
|
8091
|
+
switch (action) {
|
|
8092
|
+
case "list":
|
|
8093
|
+
data = await listLinkedInCampaigns(client, restArgs, flags);
|
|
8094
|
+
emptyHint = "No LinkedIn campaigns found for this account.";
|
|
8095
|
+
break;
|
|
8096
|
+
case "create":
|
|
8097
|
+
data = await createLinkedInCampaign(client, restArgs, flags);
|
|
8098
|
+
break;
|
|
8099
|
+
case "update":
|
|
8100
|
+
data = await updateLinkedInCampaign(client, restArgs, flags);
|
|
8101
|
+
break;
|
|
8102
|
+
default:
|
|
8103
|
+
if (LINKEDIN_ID_PATTERN.test(action)) data = await getLinkedInCampaign(client, [action], flags);
|
|
8104
|
+
else throw new CliError("UNKNOWN_COMMAND", `Unknown action: linkedin campaigns ${action}`, "Available: list, create, update");
|
|
8105
|
+
}
|
|
8106
|
+
break;
|
|
8107
|
+
}
|
|
8108
|
+
case "ad-groups": {
|
|
8109
|
+
if (!action) {
|
|
8110
|
+
showHelp("linkedin ad-groups", flags.help === "full");
|
|
8111
|
+
return;
|
|
8112
|
+
}
|
|
8113
|
+
switch (action) {
|
|
8114
|
+
case "list":
|
|
8115
|
+
data = await listLinkedInAdGroups(client, restArgs, flags);
|
|
8116
|
+
emptyHint = "No LinkedIn ad groups found for this account.";
|
|
8117
|
+
break;
|
|
8118
|
+
case "create":
|
|
8119
|
+
data = await createLinkedInAdGroup(client, restArgs, flags);
|
|
8120
|
+
break;
|
|
8121
|
+
case "update":
|
|
8122
|
+
data = await updateLinkedInAdGroup(client, restArgs, flags);
|
|
8123
|
+
break;
|
|
8124
|
+
default:
|
|
8125
|
+
if (LINKEDIN_ID_PATTERN.test(action)) data = await getLinkedInAdGroup(client, [action], flags);
|
|
8126
|
+
else throw new CliError("UNKNOWN_COMMAND", `Unknown action: linkedin ad-groups ${action}`, "Available: list, create, update");
|
|
8127
|
+
}
|
|
8128
|
+
break;
|
|
8129
|
+
}
|
|
8130
|
+
case "ads": {
|
|
8131
|
+
if (!action) {
|
|
8132
|
+
showHelp("linkedin ads", flags.help === "full");
|
|
8133
|
+
return;
|
|
8134
|
+
}
|
|
8135
|
+
switch (action) {
|
|
8136
|
+
case "list":
|
|
8137
|
+
data = await listLinkedInAds(client, restArgs, flags);
|
|
8138
|
+
emptyHint = "No LinkedIn ads found for this account.";
|
|
8139
|
+
break;
|
|
8140
|
+
case "create":
|
|
8141
|
+
data = await createLinkedInAd(client, restArgs, flags);
|
|
8142
|
+
break;
|
|
8143
|
+
case "update":
|
|
8144
|
+
data = await updateLinkedInAd(client, restArgs, flags);
|
|
8145
|
+
break;
|
|
8146
|
+
default:
|
|
8147
|
+
if (LINKEDIN_ID_PATTERN.test(action)) data = await getLinkedInAd(client, [action], flags);
|
|
8148
|
+
else throw new CliError("UNKNOWN_COMMAND", `Unknown action: linkedin ads ${action}`, "Available: list, create, update");
|
|
8149
|
+
}
|
|
8150
|
+
break;
|
|
8151
|
+
}
|
|
8152
|
+
case "targeting-search": {
|
|
8153
|
+
if (action && action !== "search") throw new CliError("UNKNOWN_COMMAND", `Unknown action: linkedin targeting-search ${action}`, "Available: search");
|
|
8154
|
+
data = await searchLinkedInTargeting(client, restArgs, flags);
|
|
8155
|
+
emptyHint = "No LinkedIn targeting entities matched this facet/query.";
|
|
8156
|
+
break;
|
|
8157
|
+
}
|
|
8158
|
+
case "results": {
|
|
8159
|
+
if (action && action !== "list") throw new CliError("UNKNOWN_COMMAND", `Unknown action: linkedin results ${action}`, "Available: list");
|
|
8160
|
+
data = await listLinkedInResults(client, restArgs, flags);
|
|
8161
|
+
emptyHint = "No LinkedIn result rows found for the selected date range.";
|
|
8162
|
+
break;
|
|
8163
|
+
}
|
|
7591
8164
|
default:
|
|
7592
|
-
throw new CliError("UNKNOWN_COMMAND", `Unknown entity: linkedin ${entity}`, "Available: accounts, platform-api-requests");
|
|
8165
|
+
throw new CliError("UNKNOWN_COMMAND", `Unknown entity: linkedin ${entity}`, "Available: accounts, media, campaigns, ad-groups, ads, targeting-search, results, platform-api-requests");
|
|
7593
8166
|
}
|
|
7594
8167
|
} else if (platform2 === "x") throw new CliError("UNKNOWN_COMMAND", `Unknown entity: x ${entity}`, "Available: platform-api-requests");
|
|
7595
8168
|
else throw new CliError("UNKNOWN_COMMAND", "Unknown platform", "Available: meta, google, tiktok, reddit, x, linkedin, drafts, platform-api-requests");
|
package/package.json
CHANGED