@koda-sl/baker-cli 0.384.0 → 0.385.0-dev.ec7b0e42b
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/README.md +20 -8
- package/dist/{chunk-FGHMWBUD.js → chunk-JHKW6IDX.js} +4 -4
- package/dist/chunk-JHKW6IDX.js.map +1 -0
- package/dist/{chunk-VLUGGVJL.js → chunk-WLDM2TRF.js} +9 -1
- package/dist/chunk-WLDM2TRF.js.map +1 -0
- package/dist/cli.js +215 -106
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/dist/{shared-J7DQZ2ED.js → shared-6762QE6R.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-FGHMWBUD.js.map +0 -1
- package/dist/chunk-VLUGGVJL.js.map +0 -1
- /package/dist/{shared-J7DQZ2ED.js.map → shared-6762QE6R.js.map} +0 -0
|
@@ -13,6 +13,14 @@ import {
|
|
|
13
13
|
// src/commands/ads/meta/shared.ts
|
|
14
14
|
var DAY_MS = 864e5;
|
|
15
15
|
function metaForbiddenFix(message) {
|
|
16
|
+
if (message.includes("has not accepted Meta's Lead Generation Terms")) {
|
|
17
|
+
return {
|
|
18
|
+
action: "reject",
|
|
19
|
+
explanation: `${message}
|
|
20
|
+
|
|
21
|
+
Meta confirmed the Page's terms status using its Page access. Give the Page owner the terms link in the error and ask them to accept it before trying the form or lead ad set again. Do not suggest reconnecting Meta for this refusal; continue with other work that does not require the form or ad set.`
|
|
22
|
+
};
|
|
23
|
+
}
|
|
16
24
|
return {
|
|
17
25
|
action: "reject",
|
|
18
26
|
explanation: [
|
|
@@ -125,4 +133,4 @@ export {
|
|
|
125
133
|
resolveEffectiveStatus,
|
|
126
134
|
statusFilterHints
|
|
127
135
|
};
|
|
128
|
-
//# sourceMappingURL=chunk-
|
|
136
|
+
//# sourceMappingURL=chunk-WLDM2TRF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/ads/meta/shared.ts"],"sourcesContent":["import { ApiError } from \"../../../client.ts\";\nimport { getEnv } from \"../../../env.ts\";\nimport { handleConnectionError, needsConnectionFix } from \"../../../error-handler.ts\";\nimport { writeAdsJson } from \"../output.ts\";\nimport type { AdsErrorEnvelope, QueryFix } from \"../types.ts\";\n\nconst DAY_MS = 86_400_000;\n\n/**\n * Meta answers a permission refusal (Graph codes 200/10/294) with a bare\n * \"Permissions error\" that covers two causes needing opposite remedies: a\n * consent grant the connection never got — nothing validates Meta's granted\n * scopes after connect, unlike HubSpot's `requireCapability` — or a role the\n * user lacks on one asset, since a Pixel/Dataset, Page or catalog lives in a\n * Business and is assigned separately from the ad account. Only the blast radius\n * separates them, so hand the agent that test rather than a verdict: guessing\n * \"reconnect\" wastes the user's time on an unassigned asset, and ruling it out\n * strands a connection that really is short a scope.\n */\nexport function metaForbiddenFix(message: string): QueryFix {\n if (message.includes(\"has not accepted Meta's Lead Generation Terms\")) {\n return {\n action: \"reject\",\n explanation: `${message}\\n\\nMeta confirmed the Page's terms status using its Page access. Give the Page owner the terms link in the error and ask them to accept it before trying the form or lead ad set again. Do not suggest reconnecting Meta for this refusal; continue with other work that does not require the form or ad set.`,\n };\n }\n return {\n action: \"reject\",\n explanation: [\n message,\n \"This is a permission refusal, not a dead connection, and Meta returns the same code for two causes with opposite remedies. Tell them apart by how much else works before you answer.\",\n \"If other reads on this account are ALSO refused (campaigns, ad sets, insights), the connection is short a permission it was never granted. Only the user can widen it, by reconnecting Meta from the dashboard.\",\n \"If those reads work and only this object is refused, the asset (pixel/dataset, Page, or catalog) belongs to a Business the connected user holds no role in. Ad-account access does not carry asset access and reconnecting cannot grant it — the client assigns that asset to the connected user or partner in Meta Business settings, under the asset's own section (Data sources → Datasets for a pixel).\",\n \"Either way do not retry the same call, and do not abort the wider job. Continue with what you can read, and state in your answer that this specific check could not run and why — never present the task as verified when this step was skipped.\",\n ].join(\"\\n\\n\"),\n };\n}\n\nexport function handleMetaError(err: unknown): never {\n if (err instanceof ApiError) {\n if (needsConnectionFix(err.code, err.message)) {\n handleConnectionError(\"meta_ads\", err.message);\n }\n const envelope: AdsErrorEnvelope = {\n ok: false,\n error: {\n code: err.code,\n message: err.message,\n fix: err.code === \"FORBIDDEN\" ? metaForbiddenFix(err.message) : { action: \"reject\", explanation: err.message },\n retryable: err.code === \"RATE_LIMITED\" || err.code === \"UNAVAILABLE\" || err.code === \"INTERNAL_ERROR\",\n },\n };\n writeAdsJson(envelope);\n process.exit(1);\n }\n const message = err instanceof Error ? err.message : \"Unexpected error\";\n writeAdsJson({ ok: false, error: { code: \"NETWORK_ERROR\", message } });\n process.exit(1);\n}\n\nconst ACCOUNT_ID_RE = /^(act_)?[0-9]+$/;\n\n/** Pull the default Meta ad account ID from `--account-id` arg or `BAKER_META_AD_ACCOUNT_ID` env. */\nexport function resolveAccountIdArg(args: Record<string, unknown>): string {\n const fromArgs = args[\"account-id\"] as string | undefined;\n const id = fromArgs || getEnv().BAKER_META_AD_ACCOUNT_ID;\n if (!id) {\n writeAdsJson({\n ok: false,\n error: {\n code: \"MISSING_ACCOUNT_ID\",\n message: \"Pass --account-id or set BAKER_META_AD_ACCOUNT_ID. Run `baker ads meta accounts` to find IDs.\",\n },\n });\n process.exit(1);\n }\n if (!ACCOUNT_ID_RE.test(id)) {\n writeAdsJson({\n ok: false,\n error: {\n code: \"INVALID_ACCOUNT_ID\",\n message: `Invalid account ID \"${id}\". Expected numeric ID, optionally prefixed with \"act_\".`,\n },\n });\n process.exit(1);\n }\n return id.startsWith(\"act_\") ? id : `act_${id}`;\n}\n\nexport function getDateRange(args: Record<string, unknown>): { datePreset?: string; since?: string; until?: string } {\n const datePreset = args[\"date-preset\"] as string | undefined;\n const since = args.since as string | undefined;\n const until = args.until as string | undefined;\n if (since && until) return { since, until };\n return { datePreset: datePreset ?? \"last_7d\" };\n}\n\nexport function todayIso(): string {\n return new Date().toISOString().slice(0, 10);\n}\n\nexport function daysAgoIso(days: number): string {\n return new Date(Date.now() - days * DAY_MS).toISOString().slice(0, 10);\n}\n\nexport function csvOrJson(args: Record<string, unknown>): string {\n return (args.output as string | undefined) ?? \"json\";\n}\n\n/**\n * Matched by Meta itself, so finding one entity by name reads only the rows that\n * match instead of the whole account.\n */\nexport const NAME_CONTAINS_ARG = {\n type: \"string\",\n description:\n \"Only rows whose name contains this text, matched by Meta before anything is returned. Start here to find one entity by name: --name-contains octubre --all-statuses\",\n} as const;\n\n/**\n * Default to ACTIVE-only on listings — that's almost always what an AI agent or\n * a human glance wants. Pass `--effective-status ACTIVE,PAUSED` to override, or\n * `--all-statuses` to drop the filter entirely.\n */\nexport function resolveEffectiveStatus(args: Record<string, unknown>): string | undefined {\n if (args[\"all-statuses\"]) {\n return undefined;\n }\n const explicit = args[\"effective-status\"] as string | undefined;\n if (explicit) {\n return explicit;\n }\n return \"ACTIVE\";\n}\n\n/**\n * An empty listing under a status filter reads exactly like \"nothing exists\":\n * a paused campaign's ad sets are CAMPAIGN_PAUSED, so the ACTIVE default hides\n * every one of them. Say which filter produced the empty list and how to widen it.\n */\nexport function statusFilterHints(\n rows: ReadonlyArray<unknown>,\n effectiveStatus: string | undefined,\n noun: string,\n): string[] {\n if (rows.length > 0 || effectiveStatus === undefined) return [];\n if (effectiveStatus === \"ACTIVE\") {\n return [\n `No ACTIVE ${noun}. The ACTIVE filter hides paused, archived and in-review ones (under a paused campaign or ad set that is all of them), so this does NOT mean none exist. Re-run with --all-statuses before concluding there are none.`,\n ];\n }\n return [\n `No ${noun} with effective_status ${effectiveStatus}. Re-run with --all-statuses to see every status before concluding there are none.`,\n ];\n}\n"],"mappings":"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","names":[]}
|
package/dist/cli.js
CHANGED
|
@@ -70,7 +70,7 @@ import {
|
|
|
70
70
|
validateCanvasDeep,
|
|
71
71
|
withoutCastName,
|
|
72
72
|
ytDlpBlockSignal
|
|
73
|
-
} from "./chunk-
|
|
73
|
+
} from "./chunk-JHKW6IDX.js";
|
|
74
74
|
import {
|
|
75
75
|
NAME_CONTAINS_ARG,
|
|
76
76
|
csvOrJson,
|
|
@@ -81,7 +81,7 @@ import {
|
|
|
81
81
|
resolveEffectiveStatus,
|
|
82
82
|
statusFilterHints,
|
|
83
83
|
todayIso
|
|
84
|
-
} from "./chunk-
|
|
84
|
+
} from "./chunk-WLDM2TRF.js";
|
|
85
85
|
import {
|
|
86
86
|
buildQueryCacheKey,
|
|
87
87
|
cacheGet,
|
|
@@ -5124,6 +5124,16 @@ var FLOW_SIDE_EFFECT_CATALOG = [
|
|
|
5124
5124
|
},
|
|
5125
5125
|
"`advancedMatching` is how Meta re-identifies the visitor, and it is the difference between a matched conversion and a discarded one. Fill at least `em` and `ph` from the form: `baker flows map <slug> --map em=email --map ph=phone`."
|
|
5126
5126
|
),
|
|
5127
|
+
eventSideEffect(
|
|
5128
|
+
"chatgptAds",
|
|
5129
|
+
"Fires a ChatGPT Ads pixel conversion and mirrors it through the Conversions API when a key is configured.",
|
|
5130
|
+
{
|
|
5131
|
+
eventType: "standard",
|
|
5132
|
+
eventParams: [],
|
|
5133
|
+
advancedMatching: matchingSlots("em", "ph", "fn", "ln", "external_id", "ct", "st", "zp", "country")
|
|
5134
|
+
},
|
|
5135
|
+
"Use ChatGPT Ads event names such as lead_created, order_created or appointment_scheduled. For a custom event use a lowercase name of 1\u201364 letters, digits, underscores or dashes. Only value (major currency units) and currency are supported in eventParams. Map em from the form to improve conversion matching."
|
|
5136
|
+
),
|
|
5127
5137
|
eventSideEffect("googleAnalytics", "Fires a GA4 event.", { eventType: "custom", eventParams: [] }),
|
|
5128
5138
|
{
|
|
5129
5139
|
kind: "side-effect",
|
|
@@ -8036,6 +8046,7 @@ var studioImproveResponseSchema = z27.object({ prompt: z27.string(), original: z
|
|
|
8036
8046
|
import { z as z28 } from "zod";
|
|
8037
8047
|
var TAG_TYPES = [
|
|
8038
8048
|
"meta",
|
|
8049
|
+
"chatgptAds",
|
|
8039
8050
|
"amplitude",
|
|
8040
8051
|
"googleAds",
|
|
8041
8052
|
"tiktok",
|
|
@@ -8057,6 +8068,7 @@ var TAG_TYPES = [
|
|
|
8057
8068
|
var tagTypeSchema = z28.enum(TAG_TYPES);
|
|
8058
8069
|
var TAG_IDENTIFYING_FIELD = {
|
|
8059
8070
|
meta: "pixelId",
|
|
8071
|
+
chatgptAds: "pixelId",
|
|
8060
8072
|
googleAds: "conversionID",
|
|
8061
8073
|
tiktok: "pixelCode",
|
|
8062
8074
|
pinterest: "tagId",
|
|
@@ -15027,7 +15039,14 @@ function parseEventKey(key) {
|
|
|
15027
15039
|
}
|
|
15028
15040
|
|
|
15029
15041
|
// ../api/src/analytics/sends.ts
|
|
15030
|
-
var SEND_PLATFORMS = [
|
|
15042
|
+
var SEND_PLATFORMS = [
|
|
15043
|
+
"meta",
|
|
15044
|
+
"chatgptAds",
|
|
15045
|
+
"googleAds",
|
|
15046
|
+
"linkedinInsightTag",
|
|
15047
|
+
"twitterAds",
|
|
15048
|
+
"googleAnalytics"
|
|
15049
|
+
];
|
|
15031
15050
|
function isSendPlatform(value) {
|
|
15032
15051
|
return SEND_PLATFORMS.includes(value);
|
|
15033
15052
|
}
|
|
@@ -15039,6 +15058,13 @@ var SEND_PLATFORM_SPECS = {
|
|
|
15039
15058
|
credentialFields: ["accessToken"],
|
|
15040
15059
|
setupHint: "Add the Conversions API access token to the Meta pixel in Tracking"
|
|
15041
15060
|
},
|
|
15061
|
+
chatgptAds: {
|
|
15062
|
+
title: "ChatGPT Ads",
|
|
15063
|
+
eventMeans: "A ChatGPT Ads event \u2014 lead_created, order_created, or a custom name",
|
|
15064
|
+
eventKind: "name",
|
|
15065
|
+
credentialFields: ["apiKey"],
|
|
15066
|
+
setupHint: "Add the Conversions API key to the ChatGPT Ads pixel in Tracking"
|
|
15067
|
+
},
|
|
15042
15068
|
googleAds: {
|
|
15043
15069
|
title: "Google Ads",
|
|
15044
15070
|
eventMeans: "One of the account's conversion actions, picked from its list",
|
|
@@ -15089,6 +15115,7 @@ function isConversionSend(type) {
|
|
|
15089
15115
|
}
|
|
15090
15116
|
var SERVER_SIDE_SENDER_TAG = {
|
|
15091
15117
|
metaCapi: "meta",
|
|
15118
|
+
chatgptAdsCapi: "chatgptAds",
|
|
15092
15119
|
googleAdsCapi: "googleAds",
|
|
15093
15120
|
linkedinCapi: "linkedinInsightTag",
|
|
15094
15121
|
twitterAdsCapi: "twitterAds",
|
|
@@ -20545,9 +20572,8 @@ function requireTargets(args5, entity) {
|
|
|
20545
20572
|
var CHAT_READ_ARG_DESCRIPTION = CHAT_READ_ARG.description;
|
|
20546
20573
|
registerSchema({
|
|
20547
20574
|
command: "ads.linkedin.accounts",
|
|
20548
|
-
description: "List LinkedIn ad accounts in the company's connected scope.
|
|
20575
|
+
description: "List LinkedIn ad accounts in the company's connected scope.",
|
|
20549
20576
|
args: {
|
|
20550
|
-
"include-all": { type: "boolean", description: "Ignore picker scope", required: false },
|
|
20551
20577
|
"no-cache": NO_CACHE_ARG,
|
|
20552
20578
|
output: { type: "string", description: "json|csv|jsonl|md", required: false, default: "json" }
|
|
20553
20579
|
}
|
|
@@ -22258,11 +22284,7 @@ Examples:
|
|
|
22258
22284
|
|
|
22259
22285
|
// src/commands/ads/linkedin/accounts.ts
|
|
22260
22286
|
import { defineCommand as defineCommand35 } from "citty";
|
|
22261
|
-
|
|
22262
|
-
function accountHints2(accounts, includeAll) {
|
|
22263
|
-
if (includeAll) {
|
|
22264
|
-
return [];
|
|
22265
|
-
}
|
|
22287
|
+
function accountHints2(accounts) {
|
|
22266
22288
|
return connectedResourceHints({
|
|
22267
22289
|
plural: "LinkedIn ad accounts",
|
|
22268
22290
|
singular: "LinkedIn ad account",
|
|
@@ -22277,44 +22299,23 @@ var accountsCommand2 = defineCommand35({
|
|
|
22277
22299
|
|
|
22278
22300
|
Examples:
|
|
22279
22301
|
baker ads linkedin accounts
|
|
22280
|
-
baker ads linkedin accounts --include-all # ignore picker scope, list every account the token can see
|
|
22281
22302
|
baker ads linkedin accounts --output csv`
|
|
22282
22303
|
},
|
|
22283
22304
|
args: {
|
|
22284
|
-
"include-all": { type: "boolean", description: "List all accessible accounts, ignoring picker scope" },
|
|
22285
22305
|
"no-cache": NO_CACHE_ARG,
|
|
22286
22306
|
output: { type: "string", description: "Output format: json|csv|jsonl|md", default: "json" }
|
|
22287
22307
|
},
|
|
22288
22308
|
run: async ({ args: args5 }) => {
|
|
22289
|
-
const includeAll = args5["include-all"] === true;
|
|
22290
|
-
const useCache = !args5["no-cache"];
|
|
22291
|
-
const cacheKey = `accounts:${includeAll ? "all" : "scoped"}`;
|
|
22292
|
-
if (useCache) {
|
|
22293
|
-
const cached = cacheGet("linkedin-accounts", cacheKey);
|
|
22294
|
-
if (cached) {
|
|
22295
|
-
writeJsonEnvelope({
|
|
22296
|
-
ok: true,
|
|
22297
|
-
data: cached.data,
|
|
22298
|
-
cached: true,
|
|
22299
|
-
hints: accountHints2(cached.data, includeAll)
|
|
22300
|
-
});
|
|
22301
|
-
return;
|
|
22302
|
-
}
|
|
22303
|
-
}
|
|
22304
22309
|
try {
|
|
22305
22310
|
const params = {};
|
|
22306
|
-
if (
|
|
22307
|
-
if (!useCache) params["skip-cache"] = "true";
|
|
22311
|
+
if (args5["no-cache"]) params["skip-cache"] = "true";
|
|
22308
22312
|
const data = await apiGet("/api/ads/linkedin/accounts", params);
|
|
22309
|
-
if (useCache) {
|
|
22310
|
-
cacheSet("linkedin-accounts", cacheKey, data, ACCOUNTS_TTL_MS);
|
|
22311
|
-
}
|
|
22312
22313
|
const fmt = csvOrJson2(args5);
|
|
22313
22314
|
if (fmt !== "json") {
|
|
22314
22315
|
writeAdsOutput(data, fmt);
|
|
22315
22316
|
return;
|
|
22316
22317
|
}
|
|
22317
|
-
writeJsonEnvelope({ ok: true, data, hints: accountHints2(data
|
|
22318
|
+
writeJsonEnvelope({ ok: true, data, hints: accountHints2(data) });
|
|
22318
22319
|
} catch (err) {
|
|
22319
22320
|
handleLinkedinError(err);
|
|
22320
22321
|
}
|
|
@@ -24313,7 +24314,6 @@ var linkedinCommand = defineCommand53({
|
|
|
24313
24314
|
|
|
24314
24315
|
Discovery:
|
|
24315
24316
|
baker ads linkedin accounts \u2014 accounts in this company's connected scope
|
|
24316
|
-
baker ads linkedin accounts --include-all \u2014 every account the token can see
|
|
24317
24317
|
baker ads linkedin account --account-id 503001492 \u2014 single-account detail (currency, type, status)
|
|
24318
24318
|
baker ads linkedin url-params [--ad-sets] \u2014 UTMs in effect today, and keys set at two levels
|
|
24319
24319
|
|
|
@@ -24408,10 +24408,7 @@ Examples:
|
|
|
24408
24408
|
|
|
24409
24409
|
// src/commands/ads/meta/accounts.ts
|
|
24410
24410
|
import { defineCommand as defineCommand55 } from "citty";
|
|
24411
|
-
function accountHints3(accounts
|
|
24412
|
-
if (includeAll) {
|
|
24413
|
-
return [];
|
|
24414
|
-
}
|
|
24411
|
+
function accountHints3(accounts) {
|
|
24415
24412
|
return connectedResourceHints({
|
|
24416
24413
|
plural: "Meta ad accounts",
|
|
24417
24414
|
singular: "Meta ad account",
|
|
@@ -24429,44 +24426,23 @@ var accountsCommand3 = defineCommand55({
|
|
|
24429
24426
|
|
|
24430
24427
|
Examples:
|
|
24431
24428
|
baker ads meta accounts
|
|
24432
|
-
baker ads meta accounts --include-all # ignore picker scope and list every account the token can see
|
|
24433
24429
|
baker ads meta accounts --output csv`
|
|
24434
24430
|
},
|
|
24435
24431
|
args: {
|
|
24436
|
-
"include-all": { type: "boolean", description: "List all accessible accounts, ignoring picker scope" },
|
|
24437
24432
|
"no-cache": NO_CACHE_ARG,
|
|
24438
24433
|
output: { type: "string", description: "Output format: json|csv|jsonl|md", default: "json" }
|
|
24439
24434
|
},
|
|
24440
24435
|
run: async ({ args: args5 }) => {
|
|
24441
|
-
const includeAll = args5["include-all"] === true;
|
|
24442
|
-
const useCache = !args5["no-cache"];
|
|
24443
|
-
const cacheKey = `accounts:${includeAll ? "all" : "scoped"}`;
|
|
24444
|
-
if (useCache) {
|
|
24445
|
-
const cached = cacheGet("meta-accounts", cacheKey);
|
|
24446
|
-
if (cached) {
|
|
24447
|
-
writeJsonEnvelope({
|
|
24448
|
-
ok: true,
|
|
24449
|
-
data: cached.data,
|
|
24450
|
-
cached: true,
|
|
24451
|
-
hints: accountHints3(cached.data, includeAll)
|
|
24452
|
-
});
|
|
24453
|
-
return;
|
|
24454
|
-
}
|
|
24455
|
-
}
|
|
24456
24436
|
try {
|
|
24457
24437
|
const params = {};
|
|
24458
|
-
if (
|
|
24459
|
-
if (!useCache) params["skip-cache"] = "true";
|
|
24438
|
+
if (args5["no-cache"]) params["skip-cache"] = "true";
|
|
24460
24439
|
const data = await apiGet("/api/ads/meta/accounts", params);
|
|
24461
|
-
if (useCache) {
|
|
24462
|
-
cacheSet("meta-accounts", cacheKey, data, 60 * 60 * 1e3);
|
|
24463
|
-
}
|
|
24464
24440
|
const fmt = csvOrJson(args5);
|
|
24465
24441
|
if (fmt !== "json") {
|
|
24466
24442
|
writeAdsOutput(data, fmt);
|
|
24467
24443
|
return;
|
|
24468
24444
|
}
|
|
24469
|
-
writeJsonEnvelope({ ok: true, data, hints: accountHints3(data
|
|
24445
|
+
writeJsonEnvelope({ ok: true, data, hints: accountHints3(data) });
|
|
24470
24446
|
} catch (err) {
|
|
24471
24447
|
handleMetaError(err);
|
|
24472
24448
|
}
|
|
@@ -24767,7 +24743,7 @@ function leafRefusal(field, condition, path48) {
|
|
|
24767
24743
|
}
|
|
24768
24744
|
|
|
24769
24745
|
// ../api/src/ads-meta/leadAds.ts
|
|
24770
|
-
var META_LEAD_ADS_NOT_ENABLED_MESSAGE = "
|
|
24746
|
+
var META_LEAD_ADS_NOT_ENABLED_MESSAGE = "Using Meta instant forms and their leads is not switched on for this company. Everything else on Meta keeps working. Tell the user this needs switching on by the Baker team, and carry on with the rest of the job.";
|
|
24771
24747
|
var META_LEAD_ADS_RECONNECT_MESSAGE = "This Meta connection was made before lead ads were switched on for this company, so it cannot work with instant forms or read their leads. Ask the user to reconnect Meta in the dashboard \u2014 that grants the extra permissions \u2014 then ask for this again. Everything else on Meta keeps working meanwhile.";
|
|
24772
24748
|
function isMetaLeadAdsRefusal(message) {
|
|
24773
24749
|
return message === META_LEAD_ADS_NOT_ENABLED_MESSAGE || message === META_LEAD_ADS_RECONNECT_MESSAGE;
|
|
@@ -25853,7 +25829,7 @@ var callToActionSchema = z37.strictObject({
|
|
|
25853
25829
|
* Without it Meta refuses every ad in that ad set with subcode 3390001,
|
|
25854
25830
|
* "Choose or create an instant form for your leads campaign".
|
|
25855
25831
|
*/
|
|
25856
|
-
lead_gen_form_id:
|
|
25832
|
+
lead_gen_form_id: parentRefSchema3.optional()
|
|
25857
25833
|
}).superRefine((p, ctx) => {
|
|
25858
25834
|
if (p.lead_gen_form_id && !LEAD_FORM_CTA_TYPES.some((allowed) => allowed === p.type)) {
|
|
25859
25835
|
ctx.addIssue({
|
|
@@ -26176,6 +26152,50 @@ var mediaUploadSchema = z37.strictObject({
|
|
|
26176
26152
|
ctx.addIssue({ code: "custom", path: ["bakerVideoId"], message: "video uploads need a bakerVideoId" });
|
|
26177
26153
|
}
|
|
26178
26154
|
});
|
|
26155
|
+
var leadFormCreateSchema2 = z37.strictObject({
|
|
26156
|
+
page_id: z37.string().regex(NUMERIC_ID_REGEX3),
|
|
26157
|
+
name: z37.string().min(1).max(255),
|
|
26158
|
+
locale: z37.enum(["EN_US", "EN_GB", "ES_ES", "ES_LA", "FR_FR", "DE_DE", "PT_BR", "PT_PT"]).default("EN_US"),
|
|
26159
|
+
questionPageHeadline: z37.string().min(1).max(255).optional(),
|
|
26160
|
+
questions: z37.array(
|
|
26161
|
+
z37.strictObject({
|
|
26162
|
+
type: z37.enum([
|
|
26163
|
+
"FULL_NAME",
|
|
26164
|
+
"FIRST_NAME",
|
|
26165
|
+
"LAST_NAME",
|
|
26166
|
+
"EMAIL",
|
|
26167
|
+
"PHONE",
|
|
26168
|
+
"CITY",
|
|
26169
|
+
"STATE",
|
|
26170
|
+
"COUNTRY",
|
|
26171
|
+
"ZIP_CODE",
|
|
26172
|
+
"COMPANY_NAME",
|
|
26173
|
+
"JOB_TITLE",
|
|
26174
|
+
"CUSTOM"
|
|
26175
|
+
]),
|
|
26176
|
+
label: z37.string().min(1).max(255).optional()
|
|
26177
|
+
})
|
|
26178
|
+
).min(1).max(15),
|
|
26179
|
+
privacyPolicyUrl: httpsUrlSchema3,
|
|
26180
|
+
privacyPolicyLinkText: z37.string().min(1).max(70).default("Privacy Policy"),
|
|
26181
|
+
followUpActionUrl: httpsUrlSchema3,
|
|
26182
|
+
optimizedForQuality: z37.boolean().default(false),
|
|
26183
|
+
thankYou: z37.strictObject({
|
|
26184
|
+
title: z37.string().min(1).max(255),
|
|
26185
|
+
body: z37.string().min(1).max(1e3),
|
|
26186
|
+
buttonText: z37.string().min(1).max(70).default("View website")
|
|
26187
|
+
}).optional()
|
|
26188
|
+
}).superRefine((form, ctx) => {
|
|
26189
|
+
const seen = /* @__PURE__ */ new Set();
|
|
26190
|
+
for (const [index, question] of form.questions.entries()) {
|
|
26191
|
+
if (question.type === "CUSTOM" && !question.label) {
|
|
26192
|
+
ctx.addIssue({ code: "custom", path: ["questions", index, "label"], message: "custom questions need a label" });
|
|
26193
|
+
}
|
|
26194
|
+
const key = `${question.type}:${question.label ?? ""}`;
|
|
26195
|
+
if (seen.has(key)) ctx.addIssue({ code: "custom", path: ["questions", index], message: "duplicate question" });
|
|
26196
|
+
seen.add(key);
|
|
26197
|
+
}
|
|
26198
|
+
});
|
|
26179
26199
|
var META_DRAFT_OP_KINDS = [
|
|
26180
26200
|
"campaign.create",
|
|
26181
26201
|
"campaign.update",
|
|
@@ -26188,6 +26208,7 @@ var META_DRAFT_OP_KINDS = [
|
|
|
26188
26208
|
"customAudience.create",
|
|
26189
26209
|
"customAudience.update",
|
|
26190
26210
|
"customConversion.create",
|
|
26211
|
+
"leadForm.create",
|
|
26191
26212
|
"media.upload"
|
|
26192
26213
|
];
|
|
26193
26214
|
var metaDraftOpKindSchema = z37.enum(META_DRAFT_OP_KINDS);
|
|
@@ -26211,6 +26232,7 @@ var metaDraftOpInputSchema = z37.discriminatedUnion("kind", [
|
|
|
26211
26232
|
createOp4("customAudience.create", customAudienceCreateSchema),
|
|
26212
26233
|
updateOp4("customAudience.update", customAudienceUpdateSchema),
|
|
26213
26234
|
createOp4("customConversion.create", customConversionCreateSchema),
|
|
26235
|
+
createOp4("leadForm.create", leadFormCreateSchema2),
|
|
26214
26236
|
createOp4("media.upload", mediaUploadSchema)
|
|
26215
26237
|
]);
|
|
26216
26238
|
|
|
@@ -26801,7 +26823,7 @@ Example: baker ads meta creatives create --page 555 --message "Save now" --link
|
|
|
26801
26823
|
cta: { type: "string", description: "Call-to-action type (SHOP_NOW|LEARN_MORE|SIGN_UP|\u2026)" },
|
|
26802
26824
|
"lead-form": {
|
|
26803
26825
|
type: "string",
|
|
26804
|
-
description: "Instant form id
|
|
26826
|
+
description: "Instant form id or meta_temp_* ref from `baker ads meta lead-forms create` \u2014 REQUIRED for an ad set that collects leads on the ad. With a form, --cta must be APPLY_NOW|DOWNLOAD|GET_QUOTE|LEARN_MORE|SIGN_UP|SUBSCRIBE"
|
|
26805
26827
|
},
|
|
26806
26828
|
"image-hash": { type: "string", description: "Meta ad-image hash (already uploaded)" },
|
|
26807
26829
|
"image-ref": { type: "string", description: "meta_temp_* ref of a staged media upload" },
|
|
@@ -26945,7 +26967,7 @@ Example: baker ads meta ads retag 120252158038400247 --url-tags "utm_source={{si
|
|
|
26945
26967
|
const { apiPost: apiPost2 } = await import("./client-GR6YL3YX.js");
|
|
26946
26968
|
const { requireChatId: requireChatId2 } = await import("./env-4P5HOGNA.js");
|
|
26947
26969
|
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-NMQXZF3Z.js");
|
|
26948
|
-
const { handleMetaError: handleMetaError2 } = await import("./shared-
|
|
26970
|
+
const { handleMetaError: handleMetaError2 } = await import("./shared-6762QE6R.js");
|
|
26949
26971
|
try {
|
|
26950
26972
|
const accountId = bareAccountId2(args5);
|
|
26951
26973
|
const urlTags = typeof args5["url-tags"] === "string" ? args5["url-tags"] : void 0;
|
|
@@ -27099,7 +27121,7 @@ function duplicateCommand2(entity, label) {
|
|
|
27099
27121
|
const { apiPost: apiPost2 } = await import("./client-GR6YL3YX.js");
|
|
27100
27122
|
const { requireChatId: requireChatId2 } = await import("./env-4P5HOGNA.js");
|
|
27101
27123
|
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-NMQXZF3Z.js");
|
|
27102
|
-
const { handleMetaError: handleMetaError2 } = await import("./shared-
|
|
27124
|
+
const { handleMetaError: handleMetaError2 } = await import("./shared-6762QE6R.js");
|
|
27103
27125
|
try {
|
|
27104
27126
|
const accountId = bareAccountId2(args5);
|
|
27105
27127
|
const chatId = requireChatId2();
|
|
@@ -27352,7 +27374,7 @@ import { defineCommand as defineCommand61 } from "citty";
|
|
|
27352
27374
|
var businessesCommand = defineCommand61({
|
|
27353
27375
|
meta: {
|
|
27354
27376
|
name: "businesses",
|
|
27355
|
-
description: `List Meta Business Manager accounts
|
|
27377
|
+
description: `List Meta Business Manager accounts linked to this company's selected ad accounts. Required for ad-studies and product-catalogs commands.
|
|
27356
27378
|
|
|
27357
27379
|
Examples:
|
|
27358
27380
|
baker ads meta businesses`
|
|
@@ -28222,13 +28244,47 @@ Async is automatic for heavy queries; pass --async to force it, or --no-async to
|
|
|
28222
28244
|
import { defineCommand as defineCommand67 } from "citty";
|
|
28223
28245
|
registerSchema({
|
|
28224
28246
|
command: "ads.meta.lead-forms",
|
|
28225
|
-
description: "List the instant (lead) forms on a Facebook Page, with how many leads each has collected. Start here: a lead is fetched by form id
|
|
28247
|
+
description: "List the instant (lead) forms on a Facebook Page, with how many leads each has collected. Start here: a lead is fetched by form id. Use a Page from `baker ads meta pages` with hasPageAccessToken: true.",
|
|
28226
28248
|
args: {
|
|
28227
|
-
"page-id": {
|
|
28249
|
+
"page-id": {
|
|
28250
|
+
type: "string",
|
|
28251
|
+
description: "Facebook Page ID \u2014 from `baker ads meta pages` with hasPageAccessToken: true",
|
|
28252
|
+
required: true
|
|
28253
|
+
},
|
|
28228
28254
|
full: { type: "boolean", description: "Include each form's questions", required: false },
|
|
28229
28255
|
output: { type: "string", description: "json|csv", required: false, default: "json" }
|
|
28230
28256
|
}
|
|
28231
28257
|
});
|
|
28258
|
+
registerSchema({
|
|
28259
|
+
command: "ads.meta.lead-forms.create",
|
|
28260
|
+
description: "Start here to stage a Page-owned Meta Instant Form. The form is created only when this Session publishes. Use the returned meta_temp_* ref as --lead-form on a creative in the same Session.",
|
|
28261
|
+
args: {
|
|
28262
|
+
"account-id": { type: "string", description: "Selected Meta ad account, act_<id>", required: true },
|
|
28263
|
+
"page-id": {
|
|
28264
|
+
type: "string",
|
|
28265
|
+
description: "Facebook Page that owns the form \u2014 choose one with this account in promotingAccountIds and hasPageAccessToken: true from `baker ads meta pages`",
|
|
28266
|
+
required: true
|
|
28267
|
+
},
|
|
28268
|
+
name: { type: "string", description: "Form name", required: true },
|
|
28269
|
+
questions: {
|
|
28270
|
+
type: "string",
|
|
28271
|
+
description: "Comma-separated Meta fields, e.g. FULL_NAME,EMAIL,PHONE",
|
|
28272
|
+
required: true
|
|
28273
|
+
},
|
|
28274
|
+
"privacy-policy-url": { type: "string", description: "Public HTTPS privacy policy", required: true },
|
|
28275
|
+
"follow-up-url": { type: "string", description: "Public HTTPS link after submission", required: true },
|
|
28276
|
+
"optimized-for-quality": {
|
|
28277
|
+
type: "boolean",
|
|
28278
|
+
description: "Ask Meta to optimize form leads for quality",
|
|
28279
|
+
required: false
|
|
28280
|
+
},
|
|
28281
|
+
file: {
|
|
28282
|
+
type: "string",
|
|
28283
|
+
description: "JSON file with full form payload, including thankYou and custom questions",
|
|
28284
|
+
required: false
|
|
28285
|
+
}
|
|
28286
|
+
}
|
|
28287
|
+
});
|
|
28232
28288
|
registerSchema({
|
|
28233
28289
|
command: "ads.meta.leads",
|
|
28234
28290
|
description: "Read the leads an instant form collected. Contact details (email, phone) come back masked; pass --full only when the raw values are genuinely needed, e.g. handing them to a CRM.",
|
|
@@ -28294,25 +28350,56 @@ function leadPageHints(page, full) {
|
|
|
28294
28350
|
}
|
|
28295
28351
|
return hints2;
|
|
28296
28352
|
}
|
|
28297
|
-
var
|
|
28353
|
+
var leadFormsCreateCommand2 = defineCommand67({
|
|
28298
28354
|
meta: {
|
|
28299
|
-
name: "
|
|
28300
|
-
description: `
|
|
28301
|
-
|
|
28302
|
-
|
|
28303
|
-
baker ads meta lead-forms --page-id 100635183117758
|
|
28304
|
-
baker ads meta lead-forms --page-id 100635183117758 --full # also lists each form's questions`
|
|
28355
|
+
name: "create",
|
|
28356
|
+
description: `Stage a Meta Instant Form on a Facebook Page. The form appears in review and is created only when the Session publishes.
|
|
28357
|
+
Choose a Page with this account in promotingAccountIds and hasPageAccessToken: true from \`baker ads meta pages\`.
|
|
28358
|
+
Example: baker ads meta lead-forms create --account-id act_123 --page-id 456 --name "Free audit" --questions FULL_NAME,EMAIL,PHONE --privacy-policy-url https://example.com/privacy --follow-up-url https://example.com`
|
|
28305
28359
|
},
|
|
28306
28360
|
args: {
|
|
28361
|
+
"account-id": { type: "string", description: "Selected Meta ad account" },
|
|
28307
28362
|
"page-id": { type: "string", description: "Facebook Page ID" },
|
|
28308
|
-
|
|
28309
|
-
|
|
28363
|
+
name: { type: "string", description: "Form name" },
|
|
28364
|
+
questions: { type: "string", description: "FULL_NAME,EMAIL,PHONE or other Meta fields" },
|
|
28365
|
+
"privacy-policy-url": { type: "string", description: "HTTPS privacy policy" },
|
|
28366
|
+
"follow-up-url": { type: "string", description: "HTTPS thank-you link" },
|
|
28367
|
+
"optimized-for-quality": { type: "boolean", description: "Optimize form leads for quality" },
|
|
28368
|
+
file: { type: "string", description: "JSON form payload; flags override file keys" }
|
|
28310
28369
|
},
|
|
28370
|
+
run: async ({ args: args5 }) => {
|
|
28371
|
+
const accountId = bareAccountId2(args5);
|
|
28372
|
+
const payload = mergePayload3(loadJsonFileArg3(args5.file), {
|
|
28373
|
+
page_id: args5["page-id"],
|
|
28374
|
+
name: args5.name,
|
|
28375
|
+
questions: typeof args5.questions === "string" ? args5.questions.split(",").map((type) => ({ type: type.trim().toUpperCase() })) : void 0,
|
|
28376
|
+
privacyPolicyUrl: args5["privacy-policy-url"],
|
|
28377
|
+
followUpActionUrl: args5["follow-up-url"],
|
|
28378
|
+
optimizedForQuality: args5["optimized-for-quality"]
|
|
28379
|
+
});
|
|
28380
|
+
await stageOp2({ kind: "leadForm.create", accountId, payload });
|
|
28381
|
+
}
|
|
28382
|
+
});
|
|
28383
|
+
var leadFormsListArgs = {
|
|
28384
|
+
"page-id": { type: "string", description: "Facebook Page ID" },
|
|
28385
|
+
full: { type: "boolean", description: "Include each form's questions" },
|
|
28386
|
+
output: { type: "string", description: "Output format", default: "json" }
|
|
28387
|
+
};
|
|
28388
|
+
var leadFormsListCommand = defineCommand67({
|
|
28389
|
+
meta: {
|
|
28390
|
+
name: "list",
|
|
28391
|
+
description: "List the Instant Forms on a Facebook Page, with lead counts. Choose a Page with hasPageAccessToken: true from `baker ads meta pages`. Pass --full to include questions."
|
|
28392
|
+
},
|
|
28393
|
+
args: leadFormsListArgs,
|
|
28311
28394
|
run: async ({ args: args5 }) => {
|
|
28312
28395
|
try {
|
|
28313
28396
|
const pageId = args5["page-id"];
|
|
28314
28397
|
if (!pageId) {
|
|
28315
|
-
handleLeadsError(
|
|
28398
|
+
handleLeadsError(
|
|
28399
|
+
new Error(
|
|
28400
|
+
"--page-id is required. Run `baker ads meta pages` and choose a Page with hasPageAccessToken: true."
|
|
28401
|
+
)
|
|
28402
|
+
);
|
|
28316
28403
|
}
|
|
28317
28404
|
const params = { "page-id": pageId };
|
|
28318
28405
|
if (args5.full) params.full = "true";
|
|
@@ -28329,6 +28416,19 @@ Examples:
|
|
|
28329
28416
|
}
|
|
28330
28417
|
}
|
|
28331
28418
|
});
|
|
28419
|
+
var leadFormsCommand2 = defineCommand67({
|
|
28420
|
+
meta: {
|
|
28421
|
+
name: "lead-forms",
|
|
28422
|
+
description: `List Instant Forms on a Facebook Page or stage a new one.
|
|
28423
|
+
Examples:
|
|
28424
|
+
baker ads meta lead-forms --page-id 100635183117758
|
|
28425
|
+
baker ads meta lead-forms --page-id 100635183117758 --full
|
|
28426
|
+
baker ads meta lead-forms create --account-id act_123 --page-id 555 --file form.json`
|
|
28427
|
+
},
|
|
28428
|
+
args: leadFormsListArgs,
|
|
28429
|
+
subCommands: { list: leadFormsListCommand, create: leadFormsCreateCommand2 },
|
|
28430
|
+
default: "list"
|
|
28431
|
+
});
|
|
28332
28432
|
var leadsCommand2 = defineCommand67({
|
|
28333
28433
|
meta: {
|
|
28334
28434
|
name: "leads",
|
|
@@ -28398,27 +28498,21 @@ var metaMediaCommand = defineCommand68({
|
|
|
28398
28498
|
|
|
28399
28499
|
// src/commands/ads/meta/pages.ts
|
|
28400
28500
|
import { defineCommand as defineCommand69 } from "citty";
|
|
28401
|
-
function advertisablePages(data) {
|
|
28402
|
-
return data.filter((p) => Array.isArray(p.tasks) && p.tasks.includes("ADVERTISE"));
|
|
28403
|
-
}
|
|
28404
28501
|
function pageSelectionHints(data) {
|
|
28405
|
-
|
|
28406
|
-
|
|
28502
|
+
if (data.length === 0) return [];
|
|
28503
|
+
const accountHint = "Use a Page only with an ad account listed in its promotingAccountIds. hasPageAccessToken must be true to read instant forms; null means Meta could not check it. Form reads also require the company's lead-ads permissions.";
|
|
28504
|
+
if (data.length > 1) {
|
|
28407
28505
|
return [
|
|
28408
|
-
|
|
28506
|
+
accountHint,
|
|
28507
|
+
"MULTIPLE Pages can run ads from the selected Meta ad accounts \u2014 do NOT guess. Confirm with the user which Page an ad should run from before passing --page to `creatives create`; a wrong Page publishes the ad under the wrong brand identity and staging will not catch it."
|
|
28409
28508
|
];
|
|
28410
28509
|
}
|
|
28411
|
-
|
|
28412
|
-
return [
|
|
28413
|
-
"NO Page can run ads \u2014 none of the connected user's Pages carry the ADVERTISE task. Ask the user to grant Advertise access to the right Page before creating an ad; a readable-but-non-advertising Page still passes staging and would fail or run under the wrong identity."
|
|
28414
|
-
];
|
|
28415
|
-
}
|
|
28416
|
-
return [];
|
|
28510
|
+
return [accountHint];
|
|
28417
28511
|
}
|
|
28418
28512
|
var pagesCommand = defineCommand69({
|
|
28419
28513
|
meta: {
|
|
28420
28514
|
name: "pages",
|
|
28421
|
-
description: `List the Facebook Pages the
|
|
28515
|
+
description: `List the Facebook Pages that the selected Meta ad accounts can promote, showing which accounts can promote each Page. Every Meta ad publishes from a Page, so match its promotingAccountIds to the target account. hasPageAccessToken shows whether the connected person can also read that Page's instant forms. If more than one Page is listed, confirm the brand with the user.
|
|
28422
28516
|
|
|
28423
28517
|
Examples:
|
|
28424
28518
|
baker ads meta pages`
|
|
@@ -28677,10 +28771,10 @@ var metaCommand = defineCommand72({
|
|
|
28677
28771
|
|
|
28678
28772
|
Discovery:
|
|
28679
28773
|
baker ads meta accounts \u2014 accounts in this company's connected scope
|
|
28680
|
-
baker ads meta accounts --include-all \u2014 every account the token can see
|
|
28681
28774
|
baker ads meta businesses \u2014 Business Manager IDs
|
|
28682
|
-
baker ads meta pages \u2014 Facebook Pages the
|
|
28775
|
+
baker ads meta pages \u2014 Facebook Pages the selected ad accounts can promote (an ad's identity)
|
|
28683
28776
|
baker ads meta lead-forms --page-id 100635183117758 \u2014 instant (lead) forms on a Page, with lead counts
|
|
28777
|
+
baker ads meta lead-forms create --account-id act_123 --page-id 555 --file form.json \u2014 stage a new form
|
|
28684
28778
|
baker ads meta leads --form-id 987654321 \u2014 the leads one form collected (contact details masked)
|
|
28685
28779
|
|
|
28686
28780
|
Snapshot a running account (default = ACTIVE only):
|
|
@@ -49569,6 +49663,14 @@ var MAPPING_SLOTS = {
|
|
|
49569
49663
|
code: [PAYLOAD("eventProperties")],
|
|
49570
49664
|
datafast: [PAYLOAD("goalParams")],
|
|
49571
49665
|
meta: [PAYLOAD("eventParams"), { kind: "record", key: "advancedMatching", allowed: META_MATCHING }],
|
|
49666
|
+
chatgptAds: [
|
|
49667
|
+
PAYLOAD("eventParams"),
|
|
49668
|
+
{
|
|
49669
|
+
kind: "record",
|
|
49670
|
+
key: "advancedMatching",
|
|
49671
|
+
allowed: ["em", "ph", "fn", "ln", "external_id", "ct", "st", "zp", "country"]
|
|
49672
|
+
}
|
|
49673
|
+
],
|
|
49572
49674
|
tiktok: [
|
|
49573
49675
|
PAYLOAD("eventParams"),
|
|
49574
49676
|
{ kind: "record", key: "identify", allowed: ["email", "phone_number", "external_id"] }
|
|
@@ -66965,7 +67067,7 @@ var draftCommand5 = defineCommand218({
|
|
|
66965
67067
|
import { defineCommand as defineCommand219 } from "citty";
|
|
66966
67068
|
registerSchema({
|
|
66967
67069
|
command: "tagManager.containers",
|
|
66968
|
-
description: "List the Google Tag Manager containers this company
|
|
67070
|
+
description: "List the Google Tag Manager containers this company selected. There can be several, and Baker may read and change all of them. Start here to confirm which containers you are managing.",
|
|
66969
67071
|
args: {}
|
|
66970
67072
|
});
|
|
66971
67073
|
registerSchema({
|
|
@@ -66986,27 +67088,34 @@ registerSchema({
|
|
|
66986
67088
|
full: { type: "boolean", description: "Include the raw Tag Manager object for each entity", required: false }
|
|
66987
67089
|
}
|
|
66988
67090
|
});
|
|
66989
|
-
function containersHints(containers) {
|
|
66990
|
-
const
|
|
66991
|
-
if (connected.length === 0) {
|
|
66992
|
-
return [
|
|
66993
|
-
"None of the containers this company connected came back from Tag Manager. The connected Google account may have lost access to them \u2014 ask the user to reconnect or pick a container again."
|
|
66994
|
-
];
|
|
66995
|
-
}
|
|
66996
|
-
return connectedResourceHints({
|
|
67091
|
+
function containersHints(containers, unavailableContainers = []) {
|
|
67092
|
+
const hints2 = connectedResourceHints({
|
|
66997
67093
|
plural: "Tag Manager containers",
|
|
66998
67094
|
singular: "Tag Manager container",
|
|
66999
67095
|
flag: "--container",
|
|
67000
|
-
resources:
|
|
67096
|
+
resources: containers.map((container) => ({
|
|
67001
67097
|
id: container.containerId,
|
|
67002
67098
|
label: `${container.publicId} \xB7 ${container.name}`
|
|
67003
67099
|
}))
|
|
67004
67100
|
});
|
|
67101
|
+
if (unavailableContainers.length > 0) {
|
|
67102
|
+
const [available] = containers;
|
|
67103
|
+
if (containers.length === 1 && available) {
|
|
67104
|
+
hints2.length = 0;
|
|
67105
|
+
hints2.push(
|
|
67106
|
+
`Use --container ${available.containerId} for ${available.publicId} (${available.name}); another selected container cannot be opened.`
|
|
67107
|
+
);
|
|
67108
|
+
}
|
|
67109
|
+
hints2.push(
|
|
67110
|
+
`Tag Manager cannot open ${unavailableContainers.map((container) => `${container.publicId} (${container.name})`).join(", ")}. Ask the user to reconnect with a Google account that has access to these containers.`
|
|
67111
|
+
);
|
|
67112
|
+
}
|
|
67113
|
+
return hints2;
|
|
67005
67114
|
}
|
|
67006
67115
|
var containersCommand = defineCommand219({
|
|
67007
67116
|
meta: {
|
|
67008
67117
|
name: "containers",
|
|
67009
|
-
description: `List Tag Manager containers
|
|
67118
|
+
description: `List Tag Manager containers selected by this company.
|
|
67010
67119
|
|
|
67011
67120
|
Start here:
|
|
67012
67121
|
baker tag-manager containers`
|
|
@@ -67014,7 +67123,7 @@ Start here:
|
|
|
67014
67123
|
run: async () => {
|
|
67015
67124
|
try {
|
|
67016
67125
|
const data = await apiGet("/api/tag-manager/containers");
|
|
67017
|
-
writeJsonEnvelope({ ok: true, data, hints: containersHints(data.containers) });
|
|
67126
|
+
writeJsonEnvelope({ ok: true, data, hints: containersHints(data.containers, data.unavailableContainers) });
|
|
67018
67127
|
} catch (err) {
|
|
67019
67128
|
handleError8(err);
|
|
67020
67129
|
}
|