mobile-growth-mcp 2.2.4 → 2.3.4
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/index.js +248 -29
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -258,6 +258,8 @@ var CAMPAIGN_DEFAULT_FIELDS = "id,name,status,effective_status,objective,bid_str
|
|
|
258
258
|
var ADSET_DEFAULT_FIELDS = "id,name,status,effective_status,campaign_id,optimization_goal,billing_event,bid_strategy,bid_amount,daily_budget,lifetime_budget,targeting,promoted_object";
|
|
259
259
|
var AD_DEFAULT_FIELDS = "id,name,status,effective_status,adset_id,campaign_id,creative{id,title,body,image_url,video_id,call_to_action_type}";
|
|
260
260
|
var INSIGHT_DEFAULT_FIELDS = "campaign_id,campaign_name,spend,impressions,clicks,ctr,cpm,cpc,actions,cost_per_action_type";
|
|
261
|
+
var INSIGHT_ADSET_FIELDS = "adset_id,adset_name";
|
|
262
|
+
var INSIGHT_AD_FIELDS = "adset_id,adset_name,ad_id,ad_name";
|
|
261
263
|
|
|
262
264
|
// src/tools/meta-campaigns.ts
|
|
263
265
|
function registerGetMetaCampaigns(server2) {
|
|
@@ -420,16 +422,17 @@ import { z as z4 } from "zod";
|
|
|
420
422
|
function registerGetMetaAds(server2) {
|
|
421
423
|
server2.tool(
|
|
422
424
|
"get_meta_ads",
|
|
423
|
-
"List ads from a Meta ad account, optionally scoped to
|
|
425
|
+
"List ads from a Meta ad account, optionally scoped to a campaign or ad set. Defaults to active ads with lean field set.",
|
|
424
426
|
{
|
|
425
427
|
ad_account_id: z4.string().describe("Meta ad account ID (e.g. act_123456789)"),
|
|
426
|
-
|
|
428
|
+
campaign_id: z4.string().optional().describe("Scope to a specific campaign ID (e.g. 23851234567890)"),
|
|
429
|
+
adset_id: z4.string().optional().describe("Scope to a specific ad set ID. Takes priority over campaign_id."),
|
|
427
430
|
fields: z4.string().optional().describe(`Comma-separated fields. Default: ${AD_DEFAULT_FIELDS}`),
|
|
428
431
|
effective_status: z4.array(z4.string()).optional().describe('Filter by status. Default: ["ACTIVE"]'),
|
|
429
432
|
limit: z4.number().min(1).max(100).optional().describe("Results per page (default 50, max 100)"),
|
|
430
433
|
after: z4.string().optional().describe("Pagination cursor from previous response")
|
|
431
434
|
},
|
|
432
|
-
async ({ ad_account_id, adset_id, fields, effective_status, limit, after }) => {
|
|
435
|
+
async ({ ad_account_id, campaign_id, adset_id, fields, effective_status, limit, after }) => {
|
|
433
436
|
try {
|
|
434
437
|
const params = {
|
|
435
438
|
fields: fields ?? AD_DEFAULT_FIELDS,
|
|
@@ -449,7 +452,7 @@ function registerGetMetaAds(server2) {
|
|
|
449
452
|
if (after) {
|
|
450
453
|
params.after = after;
|
|
451
454
|
}
|
|
452
|
-
const parentPath = adset_id ? `/${adset_id}/ads` : `/${ad_account_id}/ads`;
|
|
455
|
+
const parentPath = adset_id ? `/${adset_id}/ads` : campaign_id ? `/${campaign_id}/ads` : `/${ad_account_id}/ads`;
|
|
453
456
|
const result = await metaApiGet({
|
|
454
457
|
path: parentPath,
|
|
455
458
|
params
|
|
@@ -461,7 +464,7 @@ function registerGetMetaAds(server2) {
|
|
|
461
464
|
`;
|
|
462
465
|
for (const ad of ads) {
|
|
463
466
|
text += `- **${ad.name}** (${ad.id})
|
|
464
|
-
Ad Set: ${ad.adset_id} | Status: ${ad.effective_status}` + (ad.creative?.call_to_action_type ? ` | CTA: ${ad.creative.call_to_action_type}` : "") + (ad.creative?.video_id ? " | Format: Video" : "") + (ad.creative?.image_url ? " | Format: Image" : "") + "\n";
|
|
467
|
+
Campaign: ${ad.campaign_id} | Ad Set: ${ad.adset_id} | Status: ${ad.effective_status}` + (ad.creative?.call_to_action_type ? ` | CTA: ${ad.creative.call_to_action_type}` : "") + (ad.creative?.video_id ? " | Format: Video" : "") + (ad.creative?.image_url ? " | Format: Image" : "") + "\n";
|
|
465
468
|
}
|
|
466
469
|
if (nextCursor) {
|
|
467
470
|
text += `
|
|
@@ -493,12 +496,14 @@ import { z as z5 } from "zod";
|
|
|
493
496
|
function registerGetMetaInsights(server2) {
|
|
494
497
|
server2.tool(
|
|
495
498
|
"get_meta_insights",
|
|
496
|
-
"Pull performance insights from a Meta ad account with configurable level, breakdowns, and date range. Default: campaign-level, last 7 days, active only. Conversion event is configurable (default: mobile_app_install).",
|
|
499
|
+
"Pull performance insights from a Meta ad account with configurable level, breakdowns, and date range. Default: campaign-level, last 7 days, active only. Conversion event is configurable (default: mobile_app_install). Use campaign_id or adset_id to scope results.",
|
|
497
500
|
{
|
|
498
501
|
ad_account_id: z5.string().describe("Meta ad account ID (e.g. act_123456789)"),
|
|
502
|
+
campaign_id: z5.string().optional().describe("Scope to a specific campaign ID (e.g. 23851234567890)"),
|
|
503
|
+
adset_id: z5.string().optional().describe("Scope to a specific ad set ID (e.g. 23851234567891)"),
|
|
499
504
|
level: z5.enum(["account", "campaign", "adset", "ad"]).optional().describe("Aggregation level (default: campaign)"),
|
|
500
505
|
fields: z5.string().optional().describe(
|
|
501
|
-
`Comma-separated fields. Default
|
|
506
|
+
`Comma-separated fields. Default includes level-appropriate name fields + ${INSIGHT_DEFAULT_FIELDS}`
|
|
502
507
|
),
|
|
503
508
|
date_preset: z5.string().optional().describe(`Date preset (default: ${DEFAULT_DATE_PRESET})`),
|
|
504
509
|
time_range: z5.object({
|
|
@@ -525,6 +530,8 @@ function registerGetMetaInsights(server2) {
|
|
|
525
530
|
},
|
|
526
531
|
async ({
|
|
527
532
|
ad_account_id,
|
|
533
|
+
campaign_id,
|
|
534
|
+
adset_id,
|
|
528
535
|
level,
|
|
529
536
|
fields,
|
|
530
537
|
date_preset,
|
|
@@ -539,9 +546,20 @@ function registerGetMetaInsights(server2) {
|
|
|
539
546
|
}) => {
|
|
540
547
|
try {
|
|
541
548
|
const convEvent = conversion_event ?? "mobile_app_install";
|
|
549
|
+
const effectiveLevel = level ?? "campaign";
|
|
550
|
+
let effectiveFields = fields;
|
|
551
|
+
if (!effectiveFields) {
|
|
552
|
+
if (effectiveLevel === "ad") {
|
|
553
|
+
effectiveFields = `${INSIGHT_AD_FIELDS},${INSIGHT_DEFAULT_FIELDS}`;
|
|
554
|
+
} else if (effectiveLevel === "adset") {
|
|
555
|
+
effectiveFields = `${INSIGHT_ADSET_FIELDS},${INSIGHT_DEFAULT_FIELDS}`;
|
|
556
|
+
} else {
|
|
557
|
+
effectiveFields = INSIGHT_DEFAULT_FIELDS;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
542
560
|
const params = {
|
|
543
|
-
fields:
|
|
544
|
-
level:
|
|
561
|
+
fields: effectiveFields,
|
|
562
|
+
level: effectiveLevel,
|
|
545
563
|
limit: String(limit ?? 50)
|
|
546
564
|
};
|
|
547
565
|
if (time_range) {
|
|
@@ -555,11 +573,22 @@ function registerGetMetaInsights(server2) {
|
|
|
555
573
|
if (breakdowns) {
|
|
556
574
|
params.breakdowns = breakdowns;
|
|
557
575
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
576
|
+
const baseFilters = filtering !== void 0 ? filtering === "[]" ? [] : JSON.parse(filtering) : JSON.parse(activeFilter());
|
|
577
|
+
if (campaign_id) {
|
|
578
|
+
baseFilters.push({
|
|
579
|
+
field: "campaign.id",
|
|
580
|
+
operator: "IN",
|
|
581
|
+
value: [campaign_id]
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
if (adset_id) {
|
|
585
|
+
baseFilters.push({
|
|
586
|
+
field: "adset.id",
|
|
587
|
+
operator: "IN",
|
|
588
|
+
value: [adset_id]
|
|
589
|
+
});
|
|
562
590
|
}
|
|
591
|
+
params.filtering = JSON.stringify(baseFilters);
|
|
563
592
|
if (sort) {
|
|
564
593
|
params.sort = sort;
|
|
565
594
|
}
|
|
@@ -582,9 +611,10 @@ function registerGetMetaInsights(server2) {
|
|
|
582
611
|
]
|
|
583
612
|
};
|
|
584
613
|
}
|
|
585
|
-
let text = `**${rows.length} rows** | Level: ${
|
|
586
|
-
|
|
587
|
-
`;
|
|
614
|
+
let text = `**${rows.length} rows** | Level: ${effectiveLevel} | Event: ${convEvent}`;
|
|
615
|
+
if (campaign_id) text += ` | Campaign: ${campaign_id}`;
|
|
616
|
+
if (adset_id) text += ` | Ad Set: ${adset_id}`;
|
|
617
|
+
text += "\n\n";
|
|
588
618
|
for (const row of rows) {
|
|
589
619
|
const spend = parseFloat(row.spend || "0");
|
|
590
620
|
const impressions = parseInt(row.impressions || "0");
|
|
@@ -944,6 +974,30 @@ function formatGoogleAdsError(err) {
|
|
|
944
974
|
}
|
|
945
975
|
return `Google Ads API error (${code}): ${err.message}`;
|
|
946
976
|
}
|
|
977
|
+
async function googleAdsMutate(customerId, operations) {
|
|
978
|
+
const auth = getGoogleAdsAuth();
|
|
979
|
+
const normalizedId = normalizeCustomerId(customerId);
|
|
980
|
+
const headers = await auth.getHeaders(normalizedId);
|
|
981
|
+
const url = `${BASE_URL}/customers/${normalizedId}/googleAds:mutate`;
|
|
982
|
+
const response = await fetch(url, {
|
|
983
|
+
method: "POST",
|
|
984
|
+
headers: {
|
|
985
|
+
...headers,
|
|
986
|
+
"Content-Type": "application/json"
|
|
987
|
+
},
|
|
988
|
+
body: JSON.stringify({ mutateOperations: operations })
|
|
989
|
+
});
|
|
990
|
+
const body = await response.json();
|
|
991
|
+
if (!response.ok) {
|
|
992
|
+
if (isGoogleAdsError(body)) {
|
|
993
|
+
throw new Error(formatGoogleAdsError(body.error));
|
|
994
|
+
}
|
|
995
|
+
throw new Error(
|
|
996
|
+
`Google Ads API mutate returned ${response.status}: ${JSON.stringify(body)}`
|
|
997
|
+
);
|
|
998
|
+
}
|
|
999
|
+
return body;
|
|
1000
|
+
}
|
|
947
1001
|
async function googleAdsQuery(customerId, query) {
|
|
948
1002
|
const auth = getGoogleAdsAuth();
|
|
949
1003
|
const normalizedId = normalizeCustomerId(customerId);
|
|
@@ -2173,6 +2227,168 @@ Sources: goog-pdf-018, ab-pt-008, goog-pdf-019, ab-pt-007
|
|
|
2173
2227
|
);
|
|
2174
2228
|
}
|
|
2175
2229
|
|
|
2230
|
+
// src/tools/google-upload-assets.ts
|
|
2231
|
+
import { z as z13 } from "zod";
|
|
2232
|
+
import { readFile } from "fs/promises";
|
|
2233
|
+
import { basename, resolve } from "path";
|
|
2234
|
+
async function fetchAsBase64(url) {
|
|
2235
|
+
if (url.startsWith("data:")) {
|
|
2236
|
+
const commaIdx = url.indexOf(",");
|
|
2237
|
+
if (commaIdx === -1) throw new Error("Invalid data URI");
|
|
2238
|
+
return url.slice(commaIdx + 1);
|
|
2239
|
+
}
|
|
2240
|
+
const res = await fetch(url);
|
|
2241
|
+
if (!res.ok) throw new Error(`Failed to fetch ${url}: ${res.status}`);
|
|
2242
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
2243
|
+
return buf.toString("base64");
|
|
2244
|
+
}
|
|
2245
|
+
async function readFileAsBase64(filePath) {
|
|
2246
|
+
const buf = await readFile(resolve(filePath));
|
|
2247
|
+
return buf.toString("base64");
|
|
2248
|
+
}
|
|
2249
|
+
function registerUploadGoogleImageAssets(server2) {
|
|
2250
|
+
server2.tool(
|
|
2251
|
+
"upload_google_image_assets",
|
|
2252
|
+
"Upload image assets to a Google Ads account. Accepts URLs or local file paths. Each image becomes an Asset resource in the account. Optionally link assets to a campaign or ad group after upload. Supports batch upload (up to 50 images per call).",
|
|
2253
|
+
{
|
|
2254
|
+
customer_id: z13.string().describe("Google Ads customer ID"),
|
|
2255
|
+
images: z13.array(
|
|
2256
|
+
z13.object({
|
|
2257
|
+
source: z13.string().describe(
|
|
2258
|
+
"Image URL (https://...) or local file path (/path/to/image.png)"
|
|
2259
|
+
),
|
|
2260
|
+
name: z13.string().optional().describe(
|
|
2261
|
+
"Asset name in Google Ads (default: filename from source)"
|
|
2262
|
+
)
|
|
2263
|
+
})
|
|
2264
|
+
).min(1).max(50).describe("Array of images to upload (max 50 per call)"),
|
|
2265
|
+
campaign_id: z13.string().optional().describe(
|
|
2266
|
+
"Link uploaded assets to this campaign (creates CampaignAsset links)"
|
|
2267
|
+
),
|
|
2268
|
+
ad_group_id: z13.string().optional().describe(
|
|
2269
|
+
"Link uploaded assets to this ad group (creates AdGroupAsset links). Takes priority over campaign_id."
|
|
2270
|
+
),
|
|
2271
|
+
field_type: z13.enum(["IMAGE", "LANDSCAPE_LOGO", "LOGO", "MARKETING_IMAGE", "SQUARE_MARKETING_IMAGE", "PORTRAIT_MARKETING_IMAGE"]).optional().describe("Asset field type when linking to campaign/ad group (default: IMAGE)"),
|
|
2272
|
+
dry_run: z13.boolean().optional().describe("Preview what would be uploaded without making changes")
|
|
2273
|
+
},
|
|
2274
|
+
async ({
|
|
2275
|
+
customer_id,
|
|
2276
|
+
images,
|
|
2277
|
+
campaign_id,
|
|
2278
|
+
ad_group_id,
|
|
2279
|
+
field_type,
|
|
2280
|
+
dry_run
|
|
2281
|
+
}) => {
|
|
2282
|
+
try {
|
|
2283
|
+
const normalizedId = normalizeCustomerId(customer_id);
|
|
2284
|
+
const effectiveFieldType = field_type ?? "IMAGE";
|
|
2285
|
+
if (dry_run) {
|
|
2286
|
+
let text2 = `**Dry run** \u2014 would upload ${images.length} image(s) to customer ${customer_id}
|
|
2287
|
+
|
|
2288
|
+
`;
|
|
2289
|
+
for (const img of images) {
|
|
2290
|
+
const name = img.name ?? basename(img.source).replace(/\?.*$/, "") ?? "unnamed";
|
|
2291
|
+
text2 += `- ${name} \u2190 ${img.source}
|
|
2292
|
+
`;
|
|
2293
|
+
}
|
|
2294
|
+
if (ad_group_id) {
|
|
2295
|
+
text2 += `
|
|
2296
|
+
Would link to ad group ${ad_group_id} as ${effectiveFieldType}`;
|
|
2297
|
+
} else if (campaign_id) {
|
|
2298
|
+
text2 += `
|
|
2299
|
+
Would link to campaign ${campaign_id} as ${effectiveFieldType}`;
|
|
2300
|
+
}
|
|
2301
|
+
return { content: [{ type: "text", text: text2 }] };
|
|
2302
|
+
}
|
|
2303
|
+
const assetOps = [];
|
|
2304
|
+
const assetNames = [];
|
|
2305
|
+
for (const img of images) {
|
|
2306
|
+
const name = img.name ?? basename(img.source).replace(/\?.*$/, "") ?? "unnamed";
|
|
2307
|
+
assetNames.push(name);
|
|
2308
|
+
const isUrl = img.source.startsWith("http://") || img.source.startsWith("https://") || img.source.startsWith("data:");
|
|
2309
|
+
const data = isUrl ? await fetchAsBase64(img.source) : await readFileAsBase64(img.source);
|
|
2310
|
+
assetOps.push({
|
|
2311
|
+
assetOperation: {
|
|
2312
|
+
create: {
|
|
2313
|
+
name,
|
|
2314
|
+
type: "IMAGE",
|
|
2315
|
+
imageAsset: { data }
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
});
|
|
2319
|
+
}
|
|
2320
|
+
const assetResult = await googleAdsMutate(normalizedId, assetOps);
|
|
2321
|
+
const createdResourceNames = assetResult.mutateOperationResponses.map(
|
|
2322
|
+
(r) => r.assetResult?.resourceName ?? ""
|
|
2323
|
+
);
|
|
2324
|
+
const successCount = createdResourceNames.filter(Boolean).length;
|
|
2325
|
+
let text = `**Uploaded ${successCount}/${images.length} image assets**
|
|
2326
|
+
|
|
2327
|
+
`;
|
|
2328
|
+
for (let i = 0; i < images.length; i++) {
|
|
2329
|
+
const rn = createdResourceNames[i];
|
|
2330
|
+
if (rn) {
|
|
2331
|
+
text += `\u2713 ${assetNames[i]} \u2192 ${rn}
|
|
2332
|
+
`;
|
|
2333
|
+
} else {
|
|
2334
|
+
text += `\u2717 ${assetNames[i]} \u2014 failed
|
|
2335
|
+
`;
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
if (successCount > 0 && (ad_group_id || campaign_id)) {
|
|
2339
|
+
const linkOps = [];
|
|
2340
|
+
for (const resourceName of createdResourceNames) {
|
|
2341
|
+
if (!resourceName) continue;
|
|
2342
|
+
if (ad_group_id) {
|
|
2343
|
+
linkOps.push({
|
|
2344
|
+
adGroupAssetOperation: {
|
|
2345
|
+
create: {
|
|
2346
|
+
adGroup: `customers/${normalizedId}/adGroups/${ad_group_id}`,
|
|
2347
|
+
asset: resourceName,
|
|
2348
|
+
fieldType: effectiveFieldType
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
});
|
|
2352
|
+
} else if (campaign_id) {
|
|
2353
|
+
linkOps.push({
|
|
2354
|
+
campaignAssetOperation: {
|
|
2355
|
+
create: {
|
|
2356
|
+
campaign: `customers/${normalizedId}/campaigns/${campaign_id}`,
|
|
2357
|
+
asset: resourceName,
|
|
2358
|
+
fieldType: effectiveFieldType
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
});
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
try {
|
|
2365
|
+
await googleAdsMutate(normalizedId, linkOps);
|
|
2366
|
+
const linkTarget = ad_group_id ? `ad group ${ad_group_id}` : `campaign ${campaign_id}`;
|
|
2367
|
+
text += `
|
|
2368
|
+
\u2713 Linked ${successCount} assets to ${linkTarget} as ${effectiveFieldType}`;
|
|
2369
|
+
} catch (linkErr) {
|
|
2370
|
+
text += `
|
|
2371
|
+
\u26A0\uFE0F Assets uploaded but linking failed: ${linkErr instanceof Error ? linkErr.message : String(linkErr)}`;
|
|
2372
|
+
text += `
|
|
2373
|
+
Assets exist in the account and can be linked manually.`;
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
return { content: [{ type: "text", text }] };
|
|
2377
|
+
} catch (err) {
|
|
2378
|
+
return {
|
|
2379
|
+
content: [
|
|
2380
|
+
{
|
|
2381
|
+
type: "text",
|
|
2382
|
+
text: `Error: ${err instanceof Error ? err.message : String(err)}`
|
|
2383
|
+
}
|
|
2384
|
+
],
|
|
2385
|
+
isError: true
|
|
2386
|
+
};
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
);
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2176
2392
|
// src/tools/connection-status.ts
|
|
2177
2393
|
function registerConnectionStatus(server2, status2) {
|
|
2178
2394
|
server2.tool(
|
|
@@ -2455,8 +2671,10 @@ List ads, optionally scoped to an ad set.
|
|
|
2455
2671
|
- **fields, effective_status, limit, after** (optional)
|
|
2456
2672
|
|
|
2457
2673
|
### get_meta_insights
|
|
2458
|
-
Pull performance insights with configurable level, breakdowns, date range.
|
|
2674
|
+
Pull performance insights with configurable level, breakdowns, date range. Ad-level queries auto-include ad_id, ad_name, adset_id, adset_name.
|
|
2459
2675
|
- **ad_account_id** (required)
|
|
2676
|
+
- **campaign_id** (optional): Scope to a specific campaign
|
|
2677
|
+
- **adset_id** (optional): Scope to a specific ad set
|
|
2460
2678
|
- **level** (optional): account, campaign, adset, ad (default: campaign)
|
|
2461
2679
|
- **date_preset** (optional): default last_7d
|
|
2462
2680
|
- **time_range** (optional): {since, until} for custom dates
|
|
@@ -2689,7 +2907,7 @@ function getDotEnv() {
|
|
|
2689
2907
|
}
|
|
2690
2908
|
return dotEnvCache;
|
|
2691
2909
|
}
|
|
2692
|
-
function
|
|
2910
|
+
function resolve2(envName, cliName) {
|
|
2693
2911
|
const cli = getCliArg(cliName);
|
|
2694
2912
|
if (cli) return { value: cli, source: `--${cliName} argument` };
|
|
2695
2913
|
const env = process.env[envName]?.trim();
|
|
@@ -2699,17 +2917,17 @@ function resolve(envName, cliName) {
|
|
|
2699
2917
|
return { value: void 0, source: "not configured" };
|
|
2700
2918
|
}
|
|
2701
2919
|
function resolveApiKey() {
|
|
2702
|
-
return
|
|
2920
|
+
return resolve2("API_KEY", "api-key");
|
|
2703
2921
|
}
|
|
2704
2922
|
function resolveMetaToken() {
|
|
2705
|
-
return
|
|
2923
|
+
return resolve2("META_ACCESS_TOKEN", "meta-token");
|
|
2706
2924
|
}
|
|
2707
2925
|
function resolveGoogleAdsConfig() {
|
|
2708
|
-
const devToken =
|
|
2709
|
-
const clientId =
|
|
2710
|
-
const clientSecret =
|
|
2711
|
-
const refreshToken =
|
|
2712
|
-
const loginCustomerId =
|
|
2926
|
+
const devToken = resolve2("GOOGLE_ADS_DEVELOPER_TOKEN", "google-dev-token");
|
|
2927
|
+
const clientId = resolve2("GOOGLE_ADS_CLIENT_ID", "google-client-id");
|
|
2928
|
+
const clientSecret = resolve2("GOOGLE_ADS_CLIENT_SECRET", "google-client-secret");
|
|
2929
|
+
const refreshToken = resolve2("GOOGLE_ADS_REFRESH_TOKEN", "google-refresh-token");
|
|
2930
|
+
const loginCustomerId = resolve2("GOOGLE_ADS_LOGIN_CUSTOMER_ID", "google-login-customer-id");
|
|
2713
2931
|
const required = [
|
|
2714
2932
|
{ name: "GOOGLE_ADS_DEVELOPER_TOKEN", result: devToken },
|
|
2715
2933
|
{ name: "GOOGLE_ADS_CLIENT_ID", result: clientId },
|
|
@@ -2752,8 +2970,8 @@ async function maybeRunAuthCommand() {
|
|
|
2752
2970
|
process.exit(0);
|
|
2753
2971
|
}
|
|
2754
2972
|
function prompt(rl, question) {
|
|
2755
|
-
return new Promise((
|
|
2756
|
-
rl.question(question, (answer) =>
|
|
2973
|
+
return new Promise((resolve3) => {
|
|
2974
|
+
rl.question(question, (answer) => resolve3(answer.trim()));
|
|
2757
2975
|
});
|
|
2758
2976
|
}
|
|
2759
2977
|
function openBrowser(url) {
|
|
@@ -2841,7 +3059,7 @@ function saveToEnv(vars) {
|
|
|
2841
3059
|
}
|
|
2842
3060
|
}
|
|
2843
3061
|
async function waitForOAuthCallback(clientId, clientSecret) {
|
|
2844
|
-
return new Promise((
|
|
3062
|
+
return new Promise((resolve3, reject) => {
|
|
2845
3063
|
const server2 = createServer(
|
|
2846
3064
|
async (req, res) => {
|
|
2847
3065
|
const url = new URL(req.url ?? "/", `http://localhost:${OAUTH_PORT}`);
|
|
@@ -2896,7 +3114,7 @@ async function waitForOAuthCallback(clientId, clientSecret) {
|
|
|
2896
3114
|
"<html><body><h2>Success!</h2><p>You can close this tab and return to the terminal.</p></body></html>"
|
|
2897
3115
|
);
|
|
2898
3116
|
server2.close();
|
|
2899
|
-
|
|
3117
|
+
resolve3({ refreshToken: tokenData.refresh_token });
|
|
2900
3118
|
} catch (err) {
|
|
2901
3119
|
res.writeHead(500, { "Content-Type": "text/html" });
|
|
2902
3120
|
res.end(
|
|
@@ -3064,6 +3282,7 @@ registerGetGoogleAdsAssets(server);
|
|
|
3064
3282
|
registerGetGoogleAdsInsights(server);
|
|
3065
3283
|
registerGetGoogleAdsNetworkMix(server);
|
|
3066
3284
|
registerGetGoogleAdsAssetFatigue(server);
|
|
3285
|
+
registerUploadGoogleImageAssets(server);
|
|
3067
3286
|
registerConnectionStatus(server, status);
|
|
3068
3287
|
registerVocabularyResource(server);
|
|
3069
3288
|
registerInstructionsResource(server, status);
|
package/package.json
CHANGED