@adkit/cli 1.13.20 → 1.13.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +344 -123
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -88,6 +88,108 @@ var CliError = class extends Error {
|
|
|
88
88
|
}
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
+
// src/cli-utils.ts
|
|
92
|
+
function parseArgs(argv, options) {
|
|
93
|
+
const args = [];
|
|
94
|
+
const flags = {};
|
|
95
|
+
const multiSet = new Set(options?.multi);
|
|
96
|
+
for (let i = 0; i < argv.length; i++) {
|
|
97
|
+
const arg = argv[i];
|
|
98
|
+
if (!arg.startsWith("--")) {
|
|
99
|
+
args.push(arg);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const eqIdx = arg.indexOf("=");
|
|
103
|
+
let key;
|
|
104
|
+
let value;
|
|
105
|
+
if (eqIdx !== -1) {
|
|
106
|
+
key = arg.slice(2, eqIdx);
|
|
107
|
+
value = arg.slice(eqIdx + 1);
|
|
108
|
+
} else {
|
|
109
|
+
key = arg.slice(2);
|
|
110
|
+
const next = argv[i + 1];
|
|
111
|
+
if (next && !next.startsWith("--")) {
|
|
112
|
+
value = next;
|
|
113
|
+
i++;
|
|
114
|
+
} else value = true;
|
|
115
|
+
}
|
|
116
|
+
if (multiSet.has(key) && typeof value === "string") {
|
|
117
|
+
const existing = flags[key];
|
|
118
|
+
if (Array.isArray(existing)) existing.push(value);
|
|
119
|
+
else flags[key] = [value];
|
|
120
|
+
} else flags[key] = value;
|
|
121
|
+
}
|
|
122
|
+
return { args, flags };
|
|
123
|
+
}
|
|
124
|
+
var GLOBAL_FLAGS = ["account", "json", "fields", "publish", "data", "platform-overrides", "force", "project"];
|
|
125
|
+
function validateFlags(flags, allowed, command) {
|
|
126
|
+
const allAllowed = /* @__PURE__ */ new Set([...GLOBAL_FLAGS, ...allowed]);
|
|
127
|
+
const unknown = Object.keys(flags).filter((k) => !allAllowed.has(k));
|
|
128
|
+
if (unknown.length) throw new CliError("UNKNOWN_FLAG", `Unknown flag${unknown.length > 1 ? "s" : ""}: ${unknown.map((f) => `--${f}`).join(", ")}`, `Run: adkit ${command} --help`);
|
|
129
|
+
}
|
|
130
|
+
function requireArg(args, index, label, hint) {
|
|
131
|
+
const val = args[index];
|
|
132
|
+
if (!val) throw new CliError("MISSING_ARGUMENT", `Missing required argument: \`<${label}>\``, hint);
|
|
133
|
+
return val;
|
|
134
|
+
}
|
|
135
|
+
function requireFlag(flags, key, hint) {
|
|
136
|
+
const val = flags[key];
|
|
137
|
+
if (typeof val !== "string") throw new CliError("MISSING_FLAG", `Missing required flag: \`--${key}\``, hint);
|
|
138
|
+
return val;
|
|
139
|
+
}
|
|
140
|
+
function parseDataFlag(flags, hint) {
|
|
141
|
+
const raw = requireFlag(flags, "data", hint);
|
|
142
|
+
try {
|
|
143
|
+
return JSON.parse(raw);
|
|
144
|
+
} catch {
|
|
145
|
+
const truncated = raw.length > 80 ? raw.slice(0, 80) + "..." : raw;
|
|
146
|
+
throw new CliError("INVALID_VALUE", `Invalid JSON in \`--data\` flag: ${truncated}`, "Check JSON syntax in --data");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function mergeAccountId(body, flags) {
|
|
150
|
+
if (typeof body !== "object" || body === null || Array.isArray(body)) return body;
|
|
151
|
+
const obj = body;
|
|
152
|
+
if (!obj.accountId && typeof flags.account === "string") obj.accountId = flags.account;
|
|
153
|
+
return obj;
|
|
154
|
+
}
|
|
155
|
+
function queryString(params) {
|
|
156
|
+
const entries = Object.entries(params).filter((entry) => entry[1] !== void 0);
|
|
157
|
+
if (entries.length === 0) return "";
|
|
158
|
+
return "?" + entries.map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join("&");
|
|
159
|
+
}
|
|
160
|
+
function isDisplayRow(value) {
|
|
161
|
+
return typeof value === "object" && value !== null;
|
|
162
|
+
}
|
|
163
|
+
function isDisplayRowEntry(value) {
|
|
164
|
+
return isDisplayRow(value);
|
|
165
|
+
}
|
|
166
|
+
function isDisplayRowArray(value) {
|
|
167
|
+
if (!Array.isArray(value)) return false;
|
|
168
|
+
const everyItemIsDisplayRow = value.every(isDisplayRowEntry);
|
|
169
|
+
return everyItemIsDisplayRow;
|
|
170
|
+
}
|
|
171
|
+
function isWrappedRowArray(value) {
|
|
172
|
+
return isDisplayRowArray(value);
|
|
173
|
+
}
|
|
174
|
+
function getDateStamp() {
|
|
175
|
+
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
176
|
+
}
|
|
177
|
+
function collectFlagValues(flags, key) {
|
|
178
|
+
const value = flags[key];
|
|
179
|
+
if (Array.isArray(value)) return value;
|
|
180
|
+
if (typeof value === "string") return [value];
|
|
181
|
+
return [];
|
|
182
|
+
}
|
|
183
|
+
function unwrapList(data) {
|
|
184
|
+
if (isDisplayRowArray(data)) return data;
|
|
185
|
+
if (data && typeof data === "object") {
|
|
186
|
+
const values = Object.values(data);
|
|
187
|
+
const rowArray = values.find(isWrappedRowArray);
|
|
188
|
+
if (rowArray) return rowArray;
|
|
189
|
+
}
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
|
|
91
193
|
// src/client.ts
|
|
92
194
|
var noticePrinted = false;
|
|
93
195
|
function printServerNotice(response) {
|
|
@@ -166,22 +268,53 @@ function buildAuthSuggestion(code) {
|
|
|
166
268
|
if (!AUTH_CODES.has(normalizedCode)) return void 0;
|
|
167
269
|
return "Run: adkit setup manage";
|
|
168
270
|
}
|
|
271
|
+
function appendReadQueryFallback(path3, rawData) {
|
|
272
|
+
const parsedData = parseDataFlag({ data: rawData }, "Use a flat JSON object for read query parameters");
|
|
273
|
+
if (typeof parsedData !== "object" || parsedData === null || Array.isArray(parsedData)) throw new CliError("INVALID_VALUE", "Read --data must be a flat JSON object", 'Use an object such as {"adGroupIds":["123"]}');
|
|
274
|
+
const queryStart = path3.indexOf("?");
|
|
275
|
+
const existingQuery = queryStart === -1 ? "" : path3.slice(queryStart + 1);
|
|
276
|
+
const existingParams = new URLSearchParams(existingQuery);
|
|
277
|
+
const extraParams = [];
|
|
278
|
+
for (const [key, value] of Object.entries(parsedData)) {
|
|
279
|
+
const values = Array.isArray(value) ? value : [value];
|
|
280
|
+
if (values.length === 0) throw new CliError("INVALID_VALUE", `Cannot use --data field \`${key}\` as a read query parameter`, "Use a string, finite number, boolean, or non-empty array of those values");
|
|
281
|
+
const queryItems = [];
|
|
282
|
+
for (const item of values) {
|
|
283
|
+
const isScalar = typeof item === "string" || typeof item === "boolean" || typeof item === "number" && Number.isFinite(item);
|
|
284
|
+
if (!isScalar) throw new CliError("INVALID_VALUE", `Cannot use --data field \`${key}\` as a read query parameter`, "Use a string, finite number, boolean, or non-empty array of those values");
|
|
285
|
+
const queryItem = String(item);
|
|
286
|
+
queryItems.push(queryItem);
|
|
287
|
+
}
|
|
288
|
+
const queryValue = queryItems.join(",");
|
|
289
|
+
if (existingParams.has(key)) continue;
|
|
290
|
+
const encodedKey = encodeURIComponent(key);
|
|
291
|
+
const encodedValue = encodeURIComponent(queryValue);
|
|
292
|
+
extraParams.push(`${encodedKey}=${encodedValue}`);
|
|
293
|
+
}
|
|
294
|
+
if (extraParams.length === 0) return path3;
|
|
295
|
+
const separator = queryStart === -1 ? "?" : "&";
|
|
296
|
+
return `${path3}${separator}${extraParams.join("&")}`;
|
|
297
|
+
}
|
|
169
298
|
var AdkitClient = class {
|
|
170
299
|
apiKey;
|
|
171
300
|
baseUrl;
|
|
172
301
|
clientVersion;
|
|
173
302
|
projectId;
|
|
174
|
-
|
|
303
|
+
readQueryFallback;
|
|
304
|
+
constructor({ apiKey, baseUrl, clientVersion, projectId, readQueryFallback }) {
|
|
175
305
|
const isLocalhost = baseUrl.startsWith("http://localhost") || baseUrl.startsWith("http://127.0.0.1");
|
|
176
306
|
if (baseUrl.startsWith("http://") && !isLocalhost) throw new Error("HTTPS is required \u2014 insecure HTTP base URLs are not allowed");
|
|
177
307
|
this.apiKey = apiKey;
|
|
178
308
|
this.baseUrl = baseUrl;
|
|
179
309
|
this.clientVersion = clientVersion;
|
|
180
310
|
this.projectId = projectId;
|
|
311
|
+
this.readQueryFallback = readQueryFallback;
|
|
181
312
|
}
|
|
182
313
|
// eslint-disable-next-line max-lines-per-function -- shared client error mapping is intentionally centralized here.
|
|
183
314
|
async request(method, path3, body) {
|
|
184
|
-
|
|
315
|
+
if (method !== "GET") this.readQueryFallback = void 0;
|
|
316
|
+
const requestPath = method === "GET" && this.readQueryFallback !== void 0 ? appendReadQueryFallback(path3, this.readQueryFallback) : path3;
|
|
317
|
+
const url = `${this.baseUrl}${requestPath}`;
|
|
185
318
|
const headers = {
|
|
186
319
|
Authorization: `Bearer ${this.apiKey}`,
|
|
187
320
|
"Content-Type": "application/json",
|
|
@@ -249,7 +382,7 @@ var AdkitClient = class {
|
|
|
249
382
|
throw error2;
|
|
250
383
|
}
|
|
251
384
|
if (response.status === 404) {
|
|
252
|
-
const fallback = `Not found: ${method} ${
|
|
385
|
+
const fallback = `Not found: ${method} ${requestPath} \u2014 the resource may have been deleted or the ID is wrong`;
|
|
253
386
|
const error2 = new CliError("NOT_FOUND", appendErrorDetail(serverMessage || fallback, batchSummary), "Check the resource ID");
|
|
254
387
|
error2.notice = serverNotice;
|
|
255
388
|
throw error2;
|
|
@@ -395,10 +528,15 @@ var isWsl = () => {
|
|
|
395
528
|
return true;
|
|
396
529
|
}
|
|
397
530
|
try {
|
|
398
|
-
|
|
531
|
+
if (fs3.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft")) {
|
|
532
|
+
return !isInsideContainer();
|
|
533
|
+
}
|
|
399
534
|
} catch {
|
|
400
|
-
return false;
|
|
401
535
|
}
|
|
536
|
+
if (fs3.existsSync("/proc/sys/fs/binfmt_misc/WSLInterop") || fs3.existsSync("/run/WSL")) {
|
|
537
|
+
return !isInsideContainer();
|
|
538
|
+
}
|
|
539
|
+
return false;
|
|
402
540
|
};
|
|
403
541
|
var is_wsl_default = process2.env.__IS_WSL_TEST__ ? isWsl : isWsl();
|
|
404
542
|
|
|
@@ -1022,108 +1160,6 @@ function logout() {
|
|
|
1022
1160
|
console.log("Logged out \u2014 API key removed.");
|
|
1023
1161
|
}
|
|
1024
1162
|
|
|
1025
|
-
// src/cli-utils.ts
|
|
1026
|
-
function parseArgs(argv, options) {
|
|
1027
|
-
const args = [];
|
|
1028
|
-
const flags = {};
|
|
1029
|
-
const multiSet = new Set(options?.multi);
|
|
1030
|
-
for (let i = 0; i < argv.length; i++) {
|
|
1031
|
-
const arg = argv[i];
|
|
1032
|
-
if (!arg.startsWith("--")) {
|
|
1033
|
-
args.push(arg);
|
|
1034
|
-
continue;
|
|
1035
|
-
}
|
|
1036
|
-
const eqIdx = arg.indexOf("=");
|
|
1037
|
-
let key;
|
|
1038
|
-
let value;
|
|
1039
|
-
if (eqIdx !== -1) {
|
|
1040
|
-
key = arg.slice(2, eqIdx);
|
|
1041
|
-
value = arg.slice(eqIdx + 1);
|
|
1042
|
-
} else {
|
|
1043
|
-
key = arg.slice(2);
|
|
1044
|
-
const next = argv[i + 1];
|
|
1045
|
-
if (next && !next.startsWith("--")) {
|
|
1046
|
-
value = next;
|
|
1047
|
-
i++;
|
|
1048
|
-
} else value = true;
|
|
1049
|
-
}
|
|
1050
|
-
if (multiSet.has(key) && typeof value === "string") {
|
|
1051
|
-
const existing = flags[key];
|
|
1052
|
-
if (Array.isArray(existing)) existing.push(value);
|
|
1053
|
-
else flags[key] = [value];
|
|
1054
|
-
} else flags[key] = value;
|
|
1055
|
-
}
|
|
1056
|
-
return { args, flags };
|
|
1057
|
-
}
|
|
1058
|
-
var GLOBAL_FLAGS = ["account", "json", "fields", "publish", "data", "platform-overrides", "force", "project"];
|
|
1059
|
-
function validateFlags(flags, allowed, command) {
|
|
1060
|
-
const allAllowed = /* @__PURE__ */ new Set([...GLOBAL_FLAGS, ...allowed]);
|
|
1061
|
-
const unknown = Object.keys(flags).filter((k) => !allAllowed.has(k));
|
|
1062
|
-
if (unknown.length) throw new CliError("UNKNOWN_FLAG", `Unknown flag${unknown.length > 1 ? "s" : ""}: ${unknown.map((f) => `--${f}`).join(", ")}`, `Run: adkit ${command} --help`);
|
|
1063
|
-
}
|
|
1064
|
-
function requireArg(args, index, label, hint) {
|
|
1065
|
-
const val = args[index];
|
|
1066
|
-
if (!val) throw new CliError("MISSING_ARGUMENT", `Missing required argument: \`<${label}>\``, hint);
|
|
1067
|
-
return val;
|
|
1068
|
-
}
|
|
1069
|
-
function requireFlag(flags, key, hint) {
|
|
1070
|
-
const val = flags[key];
|
|
1071
|
-
if (typeof val !== "string") throw new CliError("MISSING_FLAG", `Missing required flag: \`--${key}\``, hint);
|
|
1072
|
-
return val;
|
|
1073
|
-
}
|
|
1074
|
-
function parseDataFlag(flags, hint) {
|
|
1075
|
-
const raw = requireFlag(flags, "data", hint);
|
|
1076
|
-
try {
|
|
1077
|
-
return JSON.parse(raw);
|
|
1078
|
-
} catch {
|
|
1079
|
-
const truncated = raw.length > 80 ? raw.slice(0, 80) + "..." : raw;
|
|
1080
|
-
throw new CliError("INVALID_VALUE", `Invalid JSON in \`--data\` flag: ${truncated}`, "Check JSON syntax in --data");
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
function mergeAccountId(body, flags) {
|
|
1084
|
-
if (typeof body !== "object" || body === null || Array.isArray(body)) return body;
|
|
1085
|
-
const obj = body;
|
|
1086
|
-
if (!obj.accountId && typeof flags.account === "string") obj.accountId = flags.account;
|
|
1087
|
-
return obj;
|
|
1088
|
-
}
|
|
1089
|
-
function queryString(params) {
|
|
1090
|
-
const entries = Object.entries(params).filter((entry) => entry[1] !== void 0);
|
|
1091
|
-
if (entries.length === 0) return "";
|
|
1092
|
-
return "?" + entries.map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join("&");
|
|
1093
|
-
}
|
|
1094
|
-
function isDisplayRow(value) {
|
|
1095
|
-
return typeof value === "object" && value !== null;
|
|
1096
|
-
}
|
|
1097
|
-
function isDisplayRowEntry(value) {
|
|
1098
|
-
return isDisplayRow(value);
|
|
1099
|
-
}
|
|
1100
|
-
function isDisplayRowArray(value) {
|
|
1101
|
-
if (!Array.isArray(value)) return false;
|
|
1102
|
-
const everyItemIsDisplayRow = value.every(isDisplayRowEntry);
|
|
1103
|
-
return everyItemIsDisplayRow;
|
|
1104
|
-
}
|
|
1105
|
-
function isWrappedRowArray(value) {
|
|
1106
|
-
return isDisplayRowArray(value);
|
|
1107
|
-
}
|
|
1108
|
-
function getDateStamp() {
|
|
1109
|
-
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1110
|
-
}
|
|
1111
|
-
function collectFlagValues(flags, key) {
|
|
1112
|
-
const value = flags[key];
|
|
1113
|
-
if (Array.isArray(value)) return value;
|
|
1114
|
-
if (typeof value === "string") return [value];
|
|
1115
|
-
return [];
|
|
1116
|
-
}
|
|
1117
|
-
function unwrapList(data) {
|
|
1118
|
-
if (isDisplayRowArray(data)) return data;
|
|
1119
|
-
if (data && typeof data === "object") {
|
|
1120
|
-
const values = Object.values(data);
|
|
1121
|
-
const rowArray = values.find(isWrappedRowArray);
|
|
1122
|
-
if (rowArray) return rowArray;
|
|
1123
|
-
}
|
|
1124
|
-
return [];
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
1163
|
// src/commands/projects.ts
|
|
1128
1164
|
async function createProject(client, options) {
|
|
1129
1165
|
const response = await client.post("/manage/projects", options);
|
|
@@ -1888,6 +1924,18 @@ function parseGoogleSitelinkShorthand(value) {
|
|
|
1888
1924
|
return normalizeSitelinkAsset({ type: "sitelink", linkText, finalUrls: [finalUrl] });
|
|
1889
1925
|
}
|
|
1890
1926
|
|
|
1927
|
+
// ../shared/dist/utils/type-guards.js
|
|
1928
|
+
function isPlainObject(value) {
|
|
1929
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1930
|
+
}
|
|
1931
|
+
function readProperty(value, key) {
|
|
1932
|
+
return value[key];
|
|
1933
|
+
}
|
|
1934
|
+
function readStringProperty(value, key) {
|
|
1935
|
+
const raw = value[key];
|
|
1936
|
+
return typeof raw === "string" ? raw : void 0;
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1891
1939
|
// src/commands/google.ts
|
|
1892
1940
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
1893
1941
|
import { basename, extname, resolve } from "node:path";
|
|
@@ -1899,6 +1947,9 @@ var MEDIA_UPLOAD_FLAGS = ["file", "url", "base64", "adkit-media", "video-id", "f
|
|
|
1899
1947
|
var CAMPAIGN_LIST_FLAGS = ["status", "limit", "offset"];
|
|
1900
1948
|
var CAMPAIGN_CREATE_FLAGS = ["name", "status", "budget-daily", "campaign-type", "bid-strategy", "target-cpa", "target-roas", "search-partners", "display-network", "tracking-url-suffix", "countries"];
|
|
1901
1949
|
var CAMPAIGN_UPDATE_FLAGS = ["name", "status", "budget-daily", "bid-strategy", "target-cpa", "target-roas", "search-partners", "display-network", "tracking-url-suffix", "countries"];
|
|
1950
|
+
var ASSET_GROUP_LIST_FLAGS = ["campaign", "limit", "offset"];
|
|
1951
|
+
var ASSET_GROUP_CREATE_FLAGS = ["campaign", "name", "status", "final-url"];
|
|
1952
|
+
var ASSET_GROUP_UPDATE_FLAGS = ["name", "status", "final-url"];
|
|
1902
1953
|
var AD_GROUP_LIST_FLAGS = ["campaign", "status", "limit", "offset"];
|
|
1903
1954
|
var AD_GROUP_FLAGS = ["campaign", "name", "status", "cpc-bid"];
|
|
1904
1955
|
var AD_LIST_FLAGS = ["ad-group", "campaign", "status", "policy-status", "limit", "offset"];
|
|
@@ -1921,6 +1972,7 @@ var KEYWORD_RESEARCH_FLAGS = ["url", "location", "language", "limit", "min-volum
|
|
|
1921
1972
|
var TARGETING_RESEARCH_FLAGS = ["limit"];
|
|
1922
1973
|
var GEO_LOCATION_SEARCH_FLAGS = ["country-code", "locale", "type", "limit"];
|
|
1923
1974
|
var NUMBER_FLAG_PATTERN = /^(?:\d+|\d+\.\d+|\.\d+)(?:e[+-]?\d+)?$/iu;
|
|
1975
|
+
var GOOGLE_NUMERIC_ID_PATTERN = /^\d+$/u;
|
|
1924
1976
|
function generateCampaignName2() {
|
|
1925
1977
|
return `campaign ${getDateStamp()}`;
|
|
1926
1978
|
}
|
|
@@ -1995,6 +2047,52 @@ function requireAssetReference(args, hint) {
|
|
|
1995
2047
|
throw new CliError("INVALID_VALUE", `Invalid asset reference: ${value}`, "Use asset:<numeric-id>, e.g. asset:123456789");
|
|
1996
2048
|
}
|
|
1997
2049
|
}
|
|
2050
|
+
function requireGoogleNumericId(args, label, hint) {
|
|
2051
|
+
const value = requireArg(args, 0, label, hint);
|
|
2052
|
+
if (!GOOGLE_NUMERIC_ID_PATTERN.test(value)) throw new CliError("INVALID_VALUE", `Invalid ${label}: ${value}`, "Use the numeric Google Ads platformId returned by list/get");
|
|
2053
|
+
return value;
|
|
2054
|
+
}
|
|
2055
|
+
function mergeGoogleAssetGroupAccountId(body, flags) {
|
|
2056
|
+
const bodyAccountId = isPlainObject(body) ? readStringProperty(body, "accountId") : void 0;
|
|
2057
|
+
const flagAccountId = typeof flags.account === "string" ? flags.account : void 0;
|
|
2058
|
+
if (bodyAccountId && flagAccountId && bodyAccountId !== flagAccountId) throw new CliError("INVALID_VALUE", `Conflicting accountId: --account ${flagAccountId} vs data.accountId ${bodyAccountId}`, "Pass the same account once, either with --account or inside --data");
|
|
2059
|
+
return mergeAccountId(body, flags);
|
|
2060
|
+
}
|
|
2061
|
+
function buildGoogleAssetGroupCreateBody(flags) {
|
|
2062
|
+
if (typeof flags.data !== "string") throw new CliError("MISSING_FLAG", "Asset-group create requires `--data` for its assets", `Pass the full {"assetGroups":[...]} body, or combine --campaign, --name, and --final-url with --data '{"assets":[...]}'`);
|
|
2063
|
+
const parsedData = parseDataFlag(flags, "Check JSON syntax in --data");
|
|
2064
|
+
if (!isPlainObject(parsedData)) throw new CliError("INVALID_VALUE", "`--data` must be a JSON object", 'Use {"assetGroups":[...]} or a single asset-group object');
|
|
2065
|
+
const explicitAssetGroups = readProperty(parsedData, "assetGroups");
|
|
2066
|
+
if (Array.isArray(explicitAssetGroups)) return mergeGoogleAssetGroupAccountId(parsedData, flags);
|
|
2067
|
+
const dataCampaignId = readStringProperty(parsedData, "campaignId");
|
|
2068
|
+
const dataName = readStringProperty(parsedData, "name");
|
|
2069
|
+
const dataFinalUrls = readProperty(parsedData, "finalUrls");
|
|
2070
|
+
const campaignId = dataCampaignId ?? requireFlag(flags, "campaign", "Pass --campaign <id> or include campaignId in --data");
|
|
2071
|
+
const name = dataName ?? requireFlag(flags, "name", "Pass --name or include name in --data");
|
|
2072
|
+
const finalUrls = Array.isArray(dataFinalUrls) ? dataFinalUrls : [requireFlag(flags, "final-url", "Pass --final-url or include finalUrls in --data")];
|
|
2073
|
+
const dataStatus = readProperty(parsedData, "status");
|
|
2074
|
+
const status2 = dataStatus ?? (typeof flags.status === "string" ? flags.status : void 0);
|
|
2075
|
+
const fragment = removeQueryOnlyBodyKeys(parsedData, ["accountId", "assetGroups"]);
|
|
2076
|
+
const assetGroup = {
|
|
2077
|
+
...isPlainObject(fragment) ? fragment : {},
|
|
2078
|
+
campaignId,
|
|
2079
|
+
name,
|
|
2080
|
+
finalUrls,
|
|
2081
|
+
...status2 === void 0 ? {} : { status: status2 }
|
|
2082
|
+
};
|
|
2083
|
+
const dataAccountId = readStringProperty(parsedData, "accountId");
|
|
2084
|
+
const body = { ...dataAccountId ? { accountId: dataAccountId } : {}, assetGroups: [assetGroup] };
|
|
2085
|
+
return mergeGoogleAssetGroupAccountId(body, flags);
|
|
2086
|
+
}
|
|
2087
|
+
function buildGoogleAssetGroupUpdateBody(flags) {
|
|
2088
|
+
const body = {};
|
|
2089
|
+
if (typeof flags.name === "string") body.name = flags.name;
|
|
2090
|
+
if (flags.status === "enabled" || flags.status === "paused") body.status = flags.status;
|
|
2091
|
+
else if (typeof flags.status === "string") throw new CliError("INVALID_VALUE", `Invalid asset group status: ${flags.status}`, "Use enabled or paused; use delete to remove the group");
|
|
2092
|
+
if (typeof flags["final-url"] === "string") body.finalUrls = [flags["final-url"]];
|
|
2093
|
+
if (Object.keys(body).length === 0) throw new CliError("MISSING_FLAG", "Asset-group update requires a change", "Pass --name, --status, --final-url, or --data with assets.add/assets.remove");
|
|
2094
|
+
return body;
|
|
2095
|
+
}
|
|
1998
2096
|
function inferGoogleMediaContentType(filename) {
|
|
1999
2097
|
const extension = extname(filename).toLowerCase();
|
|
2000
2098
|
if (extension === ".jpg" || extension === ".jpeg") return "image/jpeg";
|
|
@@ -2503,6 +2601,45 @@ async function deleteCampaign2(client, args, flags) {
|
|
|
2503
2601
|
const path3 = `/manage/google/campaigns/${id}${qs}`;
|
|
2504
2602
|
return client.delete(path3);
|
|
2505
2603
|
}
|
|
2604
|
+
async function listGoogleAssetGroups(client, _args, flags) {
|
|
2605
|
+
validateFlags(flags, ASSET_GROUP_LIST_FLAGS, "manage google asset-groups list");
|
|
2606
|
+
const campaignId = requireFlag(flags, "campaign", "Run: adkit manage google asset-groups list --campaign <campaign-id>");
|
|
2607
|
+
const qs = buildGoogleListQuery(flags, { campaignId });
|
|
2608
|
+
return client.get(`/manage/google/asset-groups${qs}`);
|
|
2609
|
+
}
|
|
2610
|
+
async function getGoogleAssetGroup(client, args, flags) {
|
|
2611
|
+
validateFlags(flags, ["raw"], "manage google asset-groups <id>");
|
|
2612
|
+
const id = requireGoogleNumericId(args, "asset-group-id", "Run: adkit manage google asset-groups <asset-group-id>");
|
|
2613
|
+
const qs = buildGoogleListQuery(flags);
|
|
2614
|
+
return client.get(`/manage/google/asset-groups/${id}${qs}`);
|
|
2615
|
+
}
|
|
2616
|
+
async function createGoogleAssetGroup(client, _args, flags) {
|
|
2617
|
+
validateFlags(flags, ASSET_GROUP_CREATE_FLAGS, "manage google asset-groups create");
|
|
2618
|
+
const body = buildGoogleAssetGroupCreateBody(flags);
|
|
2619
|
+
const qs = buildGoogleMutationQuery(flags, body);
|
|
2620
|
+
return client.post(`/manage/google/asset-groups${qs}`, body);
|
|
2621
|
+
}
|
|
2622
|
+
async function updateGoogleAssetGroup(client, args, flags) {
|
|
2623
|
+
validateFlags(flags, ASSET_GROUP_UPDATE_FLAGS, "manage google asset-groups update");
|
|
2624
|
+
const id = requireGoogleNumericId(args, "asset-group-id", "Run: adkit manage google asset-groups update <asset-group-id> --data <json>");
|
|
2625
|
+
if (typeof flags.data === "string") {
|
|
2626
|
+
const parsedBody = parseDataFlag(flags, "Use assets.add and assets.remove for asset mutations");
|
|
2627
|
+
if (!isPlainObject(parsedBody)) throw new CliError("INVALID_VALUE", "`--data` must be a JSON object", "Use an asset-group update object");
|
|
2628
|
+
const scopedBody = mergeGoogleAssetGroupAccountId(parsedBody, flags);
|
|
2629
|
+
const qs2 = buildGoogleMutationQuery(flags, scopedBody);
|
|
2630
|
+
const body2 = removeQueryOnlyBodyKeys(scopedBody, ["accountId"]);
|
|
2631
|
+
return client.patch(`/manage/google/asset-groups/${id}${qs2}`, body2);
|
|
2632
|
+
}
|
|
2633
|
+
const body = buildGoogleAssetGroupUpdateBody(flags);
|
|
2634
|
+
const qs = buildGoogleMutationQuery(flags, body);
|
|
2635
|
+
return client.patch(`/manage/google/asset-groups/${id}${qs}`, body);
|
|
2636
|
+
}
|
|
2637
|
+
async function deleteGoogleAssetGroup(client, args, flags) {
|
|
2638
|
+
validateFlags(flags, [], "manage google asset-groups delete");
|
|
2639
|
+
const id = requireGoogleNumericId(args, "asset-group-id", "Run: adkit manage google asset-groups delete <asset-group-id>");
|
|
2640
|
+
const qs = buildGoogleMutationQuery(flags, {});
|
|
2641
|
+
return client.delete(`/manage/google/asset-groups/${id}${qs}`);
|
|
2642
|
+
}
|
|
2506
2643
|
async function listGoogleAdGroups(client, _args, flags) {
|
|
2507
2644
|
validateFlags(flags, AD_GROUP_LIST_FLAGS, "manage google ad-groups list");
|
|
2508
2645
|
const campaignId = typeof flags.campaign === "string" ? flags.campaign : void 0;
|
|
@@ -4622,7 +4759,7 @@ function isHelpSearchResponse(value) {
|
|
|
4622
4759
|
var require2 = createRequire(import.meta.url);
|
|
4623
4760
|
var CLI_VERSION = require2("../package.json").version;
|
|
4624
4761
|
var DEFAULT_BASE_URL = "https://app.adkit.so/api/v1";
|
|
4625
|
-
var
|
|
4762
|
+
var GOOGLE_NUMERIC_ID_PATTERN2 = /^\d+$/;
|
|
4626
4763
|
var GOOGLE_RESULTS_LEVEL_ACTIONS = /* @__PURE__ */ new Set(["campaigns", "ad-groups", "ads"]);
|
|
4627
4764
|
var TIKTOK_NUMERIC_ID_PATTERN = /^\d+$/;
|
|
4628
4765
|
var LINKEDIN_ID_PATTERN = /[\d:_]/;
|
|
@@ -5326,7 +5463,7 @@ Note:
|
|
|
5326
5463
|
Advanced geo examples: adkit manage google campaigns --help full
|
|
5327
5464
|
Display creation starts here: campaigns create --campaign-type display, then create Display ad groups and responsive_display ads.
|
|
5328
5465
|
Performance Max: campaignType "performance_max" with asset groups \u2014 build it with --data. See --help full.
|
|
5329
|
-
View a Performance Max campaign by ID to read back its assetGroups
|
|
5466
|
+
View a Performance Max campaign by ID to read back its assetGroups; manage existing groups with google asset-groups.
|
|
5330
5467
|
For spend/clicks/conversions: adkit manage google results
|
|
5331
5468
|
|
|
5332
5469
|
Examples:
|
|
@@ -5379,8 +5516,8 @@ Notes:
|
|
|
5379
5516
|
platformLocation accepts exact Google geo objects for advanced criteria and existing raw values.
|
|
5380
5517
|
Google exclusions support named locations. Proximity circles belong in include.
|
|
5381
5518
|
Display campaign creation uses campaignType:"display" or --campaign-type display. Videos are not supported for Display media in v1.
|
|
5382
|
-
Performance Max: campaignType "performance_max"
|
|
5383
|
-
View a Performance Max campaign by ID to read back assetGroups
|
|
5519
|
+
Performance Max: campaignType "performance_max" requires at least one assetGroups[] entry and supports multiple groups. Text assets are inline ({role,text}); upload images with google media first, then reference their id/resourceName. Needs an enabled conversion (publish is blocked without one). Non-retail only. Build it with --data.
|
|
5520
|
+
View a Performance Max campaign by ID to read back its assetGroups. After creation, manage groups with google asset-groups.
|
|
5384
5521
|
For spend/clicks/conversions: adkit manage google results
|
|
5385
5522
|
|
|
5386
5523
|
Examples:
|
|
@@ -5393,6 +5530,58 @@ Examples:
|
|
|
5393
5530
|
adkit manage google campaigns update 987654321 --status paused --account 1234567890
|
|
5394
5531
|
adkit manage google campaigns update 987654321 --data '{"targeting":{"geoLocations":{"match":"presence_or_interest"}}}' --account 1234567890
|
|
5395
5532
|
adkit manage google campaigns delete 987654321 --account 1234567890 --publish`;
|
|
5533
|
+
var GOOGLE_ASSET_GROUP_HELP = `adkit manage google asset-groups \u2014 Performance Max asset groups
|
|
5534
|
+
|
|
5535
|
+
list List groups in one campaign (requires --campaign)
|
|
5536
|
+
create Create one group or a batch
|
|
5537
|
+
update <id> Update by numeric Google asset group ID (platformId)
|
|
5538
|
+
delete <id> Delete by numeric Google asset group ID (platformId)
|
|
5539
|
+
<id> View one group by numeric Google asset group ID (platformId)
|
|
5540
|
+
|
|
5541
|
+
Flags (list):
|
|
5542
|
+
--campaign <id> Parent Performance Max campaign ID (required)
|
|
5543
|
+
--limit <n> Max results
|
|
5544
|
+
--offset <n> Pagination offset
|
|
5545
|
+
${FLAG.account}
|
|
5546
|
+
|
|
5547
|
+
Flags (create):
|
|
5548
|
+
--campaign <id> Parent campaign ID for a single-group JSON fragment
|
|
5549
|
+
--name <name> Group name for a single-group JSON fragment
|
|
5550
|
+
--status <s> enabled or paused
|
|
5551
|
+
--final-url <url> Landing page for a single-group JSON fragment
|
|
5552
|
+
--data <json> Required assets. Pass a full {"assetGroups":[...]} body for batches, or one group fragment with assets when using the named flags
|
|
5553
|
+
${FLAG.account}
|
|
5554
|
+
${FLAG.publish}
|
|
5555
|
+
|
|
5556
|
+
Flags (update):
|
|
5557
|
+
--name <name> New group name
|
|
5558
|
+
--status <s> enabled or paused; use delete to remove the group
|
|
5559
|
+
--final-url <url> Replace landing pages with one URL
|
|
5560
|
+
--data <json> Full update object. Use assets.add and assets.remove for asset changes
|
|
5561
|
+
${FLAG.account}
|
|
5562
|
+
${FLAG.publish}
|
|
5563
|
+
|
|
5564
|
+
Asset JSON:
|
|
5565
|
+
Text asset {"role":"headline|long_headline|description|business_name","text":"..."}
|
|
5566
|
+
Image asset {"role":"marketing_image|square_marketing_image|logo|landscape_logo","id":"<uploaded-google-asset-id>"}
|
|
5567
|
+
Remove asset {"linkResourceName":"customers/.../assetGroupAssets/..."} \u2014 copy the exact value returned by list/get
|
|
5568
|
+
Search theme {"type":"search_theme","text":"..."} \u2014 create/read only; signals cannot be edited
|
|
5569
|
+
|
|
5570
|
+
Notes:
|
|
5571
|
+
Mutations create drafts by default. Add --publish to apply immediately.
|
|
5572
|
+
Minimum per group: 3 headlines, 1 long_headline, 2 descriptions (at least one 60 characters or fewer), 1 business_name, 1 logo, 1 marketing_image, and 1 square_marketing_image.
|
|
5573
|
+
list/get return assets grouped by role; every linked asset includes linkResourceName for later removal.
|
|
5574
|
+
Upload images with google media before referencing their id or resourceName.
|
|
5575
|
+
Creating groups for campaigns with Google brand guidelines enabled is not supported.
|
|
5576
|
+
|
|
5577
|
+
Examples:
|
|
5578
|
+
adkit manage google asset-groups list --campaign 987654321 --account 1234567890
|
|
5579
|
+
adkit manage google asset-groups 555666777 --account 1234567890
|
|
5580
|
+
adkit manage google asset-groups create --campaign 987654321 --name "US Prospects" --final-url https://example.com --data '{"assets":[{"role":"headline","text":"Ship faster"},{"role":"headline","text":"Save hours every week"},{"role":"headline","text":"Start free today"},{"role":"long_headline","text":"Automate your ad workflow"},{"role":"description","text":"Launch campaigns in minutes"},{"role":"description","text":"No credit card required"},{"role":"business_name","text":"Acme"},{"role":"logo","id":"111"},{"role":"marketing_image","id":"222"},{"role":"square_marketing_image","id":"333"}]}' --account 1234567890
|
|
5581
|
+
adkit manage google asset-groups update 555666777 --name "US \u2014 refreshed" --account 1234567890
|
|
5582
|
+
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
|
|
5583
|
+
adkit manage google asset-groups delete 555666777 --account 1234567890 --publish`;
|
|
5584
|
+
var GOOGLE_ASSET_GROUP_HELP_FULL = GOOGLE_ASSET_GROUP_HELP;
|
|
5396
5585
|
var GOOGLE_AD_GROUP_HELP = `adkit manage google ad-groups \u2014 Google Ads ad groups
|
|
5397
5586
|
|
|
5398
5587
|
list List ad groups
|
|
@@ -6038,6 +6227,7 @@ Examples:
|
|
|
6038
6227
|
adkit manage google accounts disconnect 1234567890`.trim(),
|
|
6039
6228
|
"google assets": GOOGLE_ASSET_HELP,
|
|
6040
6229
|
"google campaigns": GOOGLE_CAMPAIGN_HELP,
|
|
6230
|
+
"google asset-groups": GOOGLE_ASSET_GROUP_HELP,
|
|
6041
6231
|
"google ad-groups": GOOGLE_AD_GROUP_HELP,
|
|
6042
6232
|
"google ads": GOOGLE_AD_HELP,
|
|
6043
6233
|
"google keywords": GOOGLE_KEYWORD_HELP,
|
|
@@ -6232,11 +6422,12 @@ ${FLAG.data}
|
|
|
6232
6422
|
--offset <n> Row offset for list
|
|
6233
6423
|
--campaign-ids <ids> Comma-separated campaign IDs for filtered reads
|
|
6234
6424
|
--name <name> Campaign name
|
|
6235
|
-
--status <s> create: active or draft (defaults to
|
|
6425
|
+
--status <s> create: active or draft (defaults to active). update: active, paused, or archived
|
|
6236
6426
|
--budget-lifetime <n> Lifetime budget in account currency
|
|
6237
6427
|
--end-date <date> YYYY-MM-DD; required with --budget-lifetime
|
|
6238
6428
|
|
|
6239
6429
|
Notes:
|
|
6430
|
+
Creates are saved as AdKit drafts unless --publish is set. The create status controls the LinkedIn state after publishing.
|
|
6240
6431
|
This tier takes lifetime budgets only; a daily budget here returns a 500. Set daily budgets on ad-groups.
|
|
6241
6432
|
A live campaign can never go back to draft.
|
|
6242
6433
|
|
|
@@ -6267,7 +6458,7 @@ ${FLAG.data}
|
|
|
6267
6458
|
--campaign <id> Parent campaign ID (create only)
|
|
6268
6459
|
--name <name> Ad group name
|
|
6269
6460
|
--objective <type> brand_awareness, engagement, website_visits, video_views, website_conversions, lead_generation
|
|
6270
|
-
--status <s> create: active or draft (defaults to
|
|
6461
|
+
--status <s> create: active or draft (defaults to active). update: active, paused, or archived
|
|
6271
6462
|
--budget-daily <n> Daily budget in account currency
|
|
6272
6463
|
--budget-lifetime <n> Lifetime budget in account currency
|
|
6273
6464
|
--bid-strategy <s> maximum_delivery, manual, target_cost, cost_cap
|
|
@@ -6278,19 +6469,21 @@ ${FLAG.data}
|
|
|
6278
6469
|
|
|
6279
6470
|
Targeting (--data only):
|
|
6280
6471
|
targeting is nested: { geoLocations: { include: [{ type: "country", code }] }, audience: { <facet>: { include?: [ids], exclude?: [ids] } } }.
|
|
6472
|
+
Active (including omitted status) requires at least one geoLocations.include target. Use --data for active creation; named flags without targeting need --status draft.
|
|
6281
6473
|
Values are bare IDs \u2014 use "adkit manage linkedin targeting-search" to find them.
|
|
6282
6474
|
Facets: jobTitles, seniorities, jobFunctions, industries, companySizes, companies, skills, interests, customAudiences, ageRanges, genders.
|
|
6283
6475
|
ageRanges/genders are include-only (LinkedIn rejects them in exclude): ageRanges take tuples like (25,34), genders take FEMALE/MALE.
|
|
6284
6476
|
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.
|
|
6285
6477
|
|
|
6286
6478
|
Notes:
|
|
6479
|
+
Creates are saved as AdKit drafts unless --publish is set. The create status controls the LinkedIn state after publishing.
|
|
6287
6480
|
A live ad group can never go back to draft. targeting on update is a full replace (send the whole targeting object via --data).
|
|
6288
6481
|
Explicit bidding on create requires a compatible --objective. Bidding changes are atomic: send --bid-strategy and --optimization together. manual, target_cost, and cost_cap also require --bid-amount; maximum_delivery rejects it.
|
|
6289
6482
|
LinkedIn further constrains bidding by objective and creative format.
|
|
6290
6483
|
|
|
6291
6484
|
Examples:
|
|
6292
|
-
adkit manage linkedin ad-groups create --campaign 628940516 --name "
|
|
6293
|
-
adkit manage linkedin ad-groups create --
|
|
6485
|
+
adkit manage linkedin ad-groups create --campaign 628940516 --name "Incomplete setup" --budget-daily 50 --objective website_visits --status draft
|
|
6486
|
+
adkit manage linkedin ad-groups create --data '{"adGroups":[{"campaignId":"628940516","name":"US Manual CPC","objective":"website_visits","bidStrategy":"manual","optimization":"clicks","bidAmount":8,"targeting":{"geoLocations":{"include":[{"type":"country","code":"103644278"}]}}}]}'
|
|
6294
6487
|
adkit manage linkedin ad-groups update 361387516 --account 512345678 --status paused
|
|
6295
6488
|
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(),
|
|
6296
6489
|
"linkedin ads": `adkit manage linkedin ads \u2014 LinkedIn ads
|
|
@@ -6310,9 +6503,10 @@ ${FLAG.data}
|
|
|
6310
6503
|
--ad-group <id> Parent ad group ID (create only)
|
|
6311
6504
|
--existing-post <urn> Reference an existing share/ugcPost URN (create only)
|
|
6312
6505
|
--name <name> Ad name
|
|
6313
|
-
--status <s> create: active or draft (defaults to
|
|
6506
|
+
--status <s> create: active or draft (defaults to active). update: active, paused, or archived
|
|
6314
6507
|
|
|
6315
6508
|
Create:
|
|
6509
|
+
Creates are saved as AdKit drafts unless --publish is set. The create status controls the LinkedIn state after publishing.
|
|
6316
6510
|
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.
|
|
6317
6511
|
Reference ad: pass --existing-post <share/ugcPost URN>; omit creative entirely (its text/url/media are ignored).
|
|
6318
6512
|
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.
|
|
@@ -6487,6 +6681,7 @@ Run adkit manage meta <group> --help for details.`.trim(),
|
|
|
6487
6681
|
Entity groups:
|
|
6488
6682
|
accounts List and connect Google Ads accounts
|
|
6489
6683
|
campaigns Manage Search, Display, and Performance Max campaigns
|
|
6684
|
+
asset-groups Manage Performance Max asset groups
|
|
6490
6685
|
ad-groups Manage ad groups and Display targeting
|
|
6491
6686
|
ads Manage responsive search and Display ads
|
|
6492
6687
|
keywords Manage keywords and negatives \u2014 config only, not metrics
|
|
@@ -6839,6 +7034,7 @@ var HELP_FULL = {
|
|
|
6839
7034
|
"meta lead-forms": LEAD_FORM_HELP,
|
|
6840
7035
|
"google assets": GOOGLE_ASSET_HELP_FULL,
|
|
6841
7036
|
"google campaigns": GOOGLE_CAMPAIGN_HELP_FULL,
|
|
7037
|
+
"google asset-groups": GOOGLE_ASSET_GROUP_HELP_FULL,
|
|
6842
7038
|
"google ad-groups": GOOGLE_AD_GROUP_HELP_FULL,
|
|
6843
7039
|
"google ads": GOOGLE_AD_HELP_FULL,
|
|
6844
7040
|
"google keywords": GOOGLE_KEYWORD_HELP_FULL,
|
|
@@ -6909,7 +7105,7 @@ function requireClient(flags, options = {}) {
|
|
|
6909
7105
|
if (!apiKey) throw new CliError("NOT_AUTHENTICATED", "No API key found", "Run: adkit setup");
|
|
6910
7106
|
const baseUrl = getBaseUrl();
|
|
6911
7107
|
const projectId = options.projectOptional ? void 0 : selectedProject;
|
|
6912
|
-
return new AdkitClient({ apiKey, baseUrl, clientVersion: CLI_VERSION, projectId });
|
|
7108
|
+
return new AdkitClient({ apiKey, baseUrl, clientVersion: CLI_VERSION, projectId, readQueryFallback: flags.data });
|
|
6913
7109
|
}
|
|
6914
7110
|
function toPrintListDisplayRows(rows, isJson) {
|
|
6915
7111
|
if (isJson) return rows;
|
|
@@ -8002,7 +8198,7 @@ async function main() {
|
|
|
8002
8198
|
data = await detachAsset(client, restArgs, flags);
|
|
8003
8199
|
break;
|
|
8004
8200
|
default:
|
|
8005
|
-
if (
|
|
8201
|
+
if (GOOGLE_NUMERIC_ID_PATTERN2.test(action)) data = await getAsset(client, [action], flags);
|
|
8006
8202
|
else throw new CliError("UNKNOWN_COMMAND", `Unknown action: google assets ${action}`, "Run: adkit manage google assets --help");
|
|
8007
8203
|
}
|
|
8008
8204
|
break;
|
|
@@ -8041,11 +8237,36 @@ async function main() {
|
|
|
8041
8237
|
data = await deleteCampaign2(client, restArgs, flags);
|
|
8042
8238
|
break;
|
|
8043
8239
|
default:
|
|
8044
|
-
if (
|
|
8240
|
+
if (GOOGLE_NUMERIC_ID_PATTERN2.test(action)) data = await getCampaign2(client, [action], flags);
|
|
8045
8241
|
else throw new CliError("UNKNOWN_COMMAND", `Unknown action: google campaigns ${action}`, "Run: adkit manage google campaigns --help");
|
|
8046
8242
|
}
|
|
8047
8243
|
break;
|
|
8048
8244
|
}
|
|
8245
|
+
case "asset-groups": {
|
|
8246
|
+
if (!action) {
|
|
8247
|
+
showHelp("google asset-groups", flags.help === "full");
|
|
8248
|
+
return;
|
|
8249
|
+
}
|
|
8250
|
+
switch (action) {
|
|
8251
|
+
case "list":
|
|
8252
|
+
data = await listGoogleAssetGroups(client, restArgs, flags);
|
|
8253
|
+
emptyHint = "No asset groups found in this campaign. Create one with `adkit manage google asset-groups create --help`.";
|
|
8254
|
+
break;
|
|
8255
|
+
case "create":
|
|
8256
|
+
data = await createGoogleAssetGroup(client, restArgs, flags);
|
|
8257
|
+
break;
|
|
8258
|
+
case "update":
|
|
8259
|
+
data = await updateGoogleAssetGroup(client, restArgs, flags);
|
|
8260
|
+
break;
|
|
8261
|
+
case "delete":
|
|
8262
|
+
data = await deleteGoogleAssetGroup(client, restArgs, flags);
|
|
8263
|
+
break;
|
|
8264
|
+
default:
|
|
8265
|
+
if (GOOGLE_NUMERIC_ID_PATTERN2.test(action)) data = await getGoogleAssetGroup(client, [action], flags);
|
|
8266
|
+
else throw new CliError("UNKNOWN_COMMAND", `Unknown action: google asset-groups ${action}`, "Run: adkit manage google asset-groups --help");
|
|
8267
|
+
}
|
|
8268
|
+
break;
|
|
8269
|
+
}
|
|
8049
8270
|
case "ad-groups": {
|
|
8050
8271
|
if (!action) {
|
|
8051
8272
|
showHelp("google ad-groups", flags.help === "full");
|
|
@@ -8066,7 +8287,7 @@ async function main() {
|
|
|
8066
8287
|
data = await deleteAdGroup(client, restArgs, flags);
|
|
8067
8288
|
break;
|
|
8068
8289
|
default:
|
|
8069
|
-
if (
|
|
8290
|
+
if (GOOGLE_NUMERIC_ID_PATTERN2.test(action)) data = await getAdGroup(client, [action], flags);
|
|
8070
8291
|
else throw new CliError("UNKNOWN_COMMAND", `Unknown action: google ad-groups ${action}`, "Run: adkit manage google ad-groups --help");
|
|
8071
8292
|
}
|
|
8072
8293
|
break;
|
|
@@ -8091,7 +8312,7 @@ async function main() {
|
|
|
8091
8312
|
data = await deleteAd2(client, restArgs, flags);
|
|
8092
8313
|
break;
|
|
8093
8314
|
default:
|
|
8094
|
-
if (
|
|
8315
|
+
if (GOOGLE_NUMERIC_ID_PATTERN2.test(action)) {
|
|
8095
8316
|
data = await getAd2(client, [action], flags);
|
|
8096
8317
|
detailEntity = "google-ad";
|
|
8097
8318
|
} else throw new CliError("UNKNOWN_COMMAND", `Unknown action: google ads ${action}`, "Run: adkit manage google ads --help");
|
|
@@ -8172,7 +8393,7 @@ async function main() {
|
|
|
8172
8393
|
break;
|
|
8173
8394
|
}
|
|
8174
8395
|
default:
|
|
8175
|
-
if (
|
|
8396
|
+
if (GOOGLE_NUMERIC_ID_PATTERN2.test(action)) data = await getKeyword(client, [action], flags);
|
|
8176
8397
|
else throw new CliError("UNKNOWN_COMMAND", `Unknown action: google keywords ${action}`, "Run: adkit manage google keywords --help (includes negatives)");
|
|
8177
8398
|
}
|
|
8178
8399
|
break;
|
|
@@ -8244,7 +8465,7 @@ async function main() {
|
|
|
8244
8465
|
break;
|
|
8245
8466
|
}
|
|
8246
8467
|
default:
|
|
8247
|
-
throw new CliError("UNKNOWN_COMMAND", `Unknown entity: google ${entity}`, "Available: accounts, assets, media, campaigns, ad-groups, ads, keywords, conversions, results, change-history, research, geo-locations");
|
|
8468
|
+
throw new CliError("UNKNOWN_COMMAND", `Unknown entity: google ${entity}`, "Available: accounts, assets, media, campaigns, asset-groups, ad-groups, ads, keywords, conversions, results, change-history, research, geo-locations");
|
|
8248
8469
|
}
|
|
8249
8470
|
} else if (platform2 === "tiktok") {
|
|
8250
8471
|
switch (entity) {
|
package/package.json
CHANGED