@koda-sl/baker-cli 0.386.0-dev.8b1f99042 → 0.386.0
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 +5 -0
- package/dist/{chunk-CHJOTJTZ.js → chunk-FGHMWBUD.js} +4 -4
- package/dist/chunk-FGHMWBUD.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 +133 -16
- 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-CHJOTJTZ.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-FGHMWBUD.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,
|
|
@@ -24716,7 +24716,7 @@ function leafRefusal(field, condition, path48) {
|
|
|
24716
24716
|
}
|
|
24717
24717
|
|
|
24718
24718
|
// ../api/src/ads-meta/leadAds.ts
|
|
24719
|
-
var META_LEAD_ADS_NOT_ENABLED_MESSAGE = "
|
|
24719
|
+
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.";
|
|
24720
24720
|
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.";
|
|
24721
24721
|
function isMetaLeadAdsRefusal(message) {
|
|
24722
24722
|
return message === META_LEAD_ADS_NOT_ENABLED_MESSAGE || message === META_LEAD_ADS_RECONNECT_MESSAGE;
|
|
@@ -25802,7 +25802,7 @@ var callToActionSchema = z37.strictObject({
|
|
|
25802
25802
|
* Without it Meta refuses every ad in that ad set with subcode 3390001,
|
|
25803
25803
|
* "Choose or create an instant form for your leads campaign".
|
|
25804
25804
|
*/
|
|
25805
|
-
lead_gen_form_id:
|
|
25805
|
+
lead_gen_form_id: parentRefSchema3.optional()
|
|
25806
25806
|
}).superRefine((p, ctx) => {
|
|
25807
25807
|
if (p.lead_gen_form_id && !LEAD_FORM_CTA_TYPES.some((allowed) => allowed === p.type)) {
|
|
25808
25808
|
ctx.addIssue({
|
|
@@ -26125,6 +26125,50 @@ var mediaUploadSchema = z37.strictObject({
|
|
|
26125
26125
|
ctx.addIssue({ code: "custom", path: ["bakerVideoId"], message: "video uploads need a bakerVideoId" });
|
|
26126
26126
|
}
|
|
26127
26127
|
});
|
|
26128
|
+
var leadFormCreateSchema2 = z37.strictObject({
|
|
26129
|
+
page_id: z37.string().regex(NUMERIC_ID_REGEX3),
|
|
26130
|
+
name: z37.string().min(1).max(255),
|
|
26131
|
+
locale: z37.enum(["EN_US", "EN_GB", "ES_ES", "ES_LA", "FR_FR", "DE_DE", "PT_BR", "PT_PT"]).default("EN_US"),
|
|
26132
|
+
questionPageHeadline: z37.string().min(1).max(255).optional(),
|
|
26133
|
+
questions: z37.array(
|
|
26134
|
+
z37.strictObject({
|
|
26135
|
+
type: z37.enum([
|
|
26136
|
+
"FULL_NAME",
|
|
26137
|
+
"FIRST_NAME",
|
|
26138
|
+
"LAST_NAME",
|
|
26139
|
+
"EMAIL",
|
|
26140
|
+
"PHONE",
|
|
26141
|
+
"CITY",
|
|
26142
|
+
"STATE",
|
|
26143
|
+
"COUNTRY",
|
|
26144
|
+
"ZIP_CODE",
|
|
26145
|
+
"COMPANY_NAME",
|
|
26146
|
+
"JOB_TITLE",
|
|
26147
|
+
"CUSTOM"
|
|
26148
|
+
]),
|
|
26149
|
+
label: z37.string().min(1).max(255).optional()
|
|
26150
|
+
})
|
|
26151
|
+
).min(1).max(15),
|
|
26152
|
+
privacyPolicyUrl: httpsUrlSchema3,
|
|
26153
|
+
privacyPolicyLinkText: z37.string().min(1).max(70).default("Privacy Policy"),
|
|
26154
|
+
followUpActionUrl: httpsUrlSchema3,
|
|
26155
|
+
optimizedForQuality: z37.boolean().default(false),
|
|
26156
|
+
thankYou: z37.strictObject({
|
|
26157
|
+
title: z37.string().min(1).max(255),
|
|
26158
|
+
body: z37.string().min(1).max(1e3),
|
|
26159
|
+
buttonText: z37.string().min(1).max(70).default("View website")
|
|
26160
|
+
}).optional()
|
|
26161
|
+
}).superRefine((form, ctx) => {
|
|
26162
|
+
const seen = /* @__PURE__ */ new Set();
|
|
26163
|
+
for (const [index, question] of form.questions.entries()) {
|
|
26164
|
+
if (question.type === "CUSTOM" && !question.label) {
|
|
26165
|
+
ctx.addIssue({ code: "custom", path: ["questions", index, "label"], message: "custom questions need a label" });
|
|
26166
|
+
}
|
|
26167
|
+
const key = `${question.type}:${question.label ?? ""}`;
|
|
26168
|
+
if (seen.has(key)) ctx.addIssue({ code: "custom", path: ["questions", index], message: "duplicate question" });
|
|
26169
|
+
seen.add(key);
|
|
26170
|
+
}
|
|
26171
|
+
});
|
|
26128
26172
|
var META_DRAFT_OP_KINDS = [
|
|
26129
26173
|
"campaign.create",
|
|
26130
26174
|
"campaign.update",
|
|
@@ -26137,6 +26181,7 @@ var META_DRAFT_OP_KINDS = [
|
|
|
26137
26181
|
"customAudience.create",
|
|
26138
26182
|
"customAudience.update",
|
|
26139
26183
|
"customConversion.create",
|
|
26184
|
+
"leadForm.create",
|
|
26140
26185
|
"media.upload"
|
|
26141
26186
|
];
|
|
26142
26187
|
var metaDraftOpKindSchema = z37.enum(META_DRAFT_OP_KINDS);
|
|
@@ -26160,6 +26205,7 @@ var metaDraftOpInputSchema = z37.discriminatedUnion("kind", [
|
|
|
26160
26205
|
createOp4("customAudience.create", customAudienceCreateSchema),
|
|
26161
26206
|
updateOp4("customAudience.update", customAudienceUpdateSchema),
|
|
26162
26207
|
createOp4("customConversion.create", customConversionCreateSchema),
|
|
26208
|
+
createOp4("leadForm.create", leadFormCreateSchema2),
|
|
26163
26209
|
createOp4("media.upload", mediaUploadSchema)
|
|
26164
26210
|
]);
|
|
26165
26211
|
|
|
@@ -26750,7 +26796,7 @@ Example: baker ads meta creatives create --page 555 --message "Save now" --link
|
|
|
26750
26796
|
cta: { type: "string", description: "Call-to-action type (SHOP_NOW|LEARN_MORE|SIGN_UP|\u2026)" },
|
|
26751
26797
|
"lead-form": {
|
|
26752
26798
|
type: "string",
|
|
26753
|
-
description: "Instant form id
|
|
26799
|
+
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"
|
|
26754
26800
|
},
|
|
26755
26801
|
"image-hash": { type: "string", description: "Meta ad-image hash (already uploaded)" },
|
|
26756
26802
|
"image-ref": { type: "string", description: "meta_temp_* ref of a staged media upload" },
|
|
@@ -26894,7 +26940,7 @@ Example: baker ads meta ads retag 120252158038400247 --url-tags "utm_source={{si
|
|
|
26894
26940
|
const { apiPost: apiPost2 } = await import("./client-GR6YL3YX.js");
|
|
26895
26941
|
const { requireChatId: requireChatId2 } = await import("./env-4P5HOGNA.js");
|
|
26896
26942
|
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-NMQXZF3Z.js");
|
|
26897
|
-
const { handleMetaError: handleMetaError2 } = await import("./shared-
|
|
26943
|
+
const { handleMetaError: handleMetaError2 } = await import("./shared-6762QE6R.js");
|
|
26898
26944
|
try {
|
|
26899
26945
|
const accountId = bareAccountId2(args5);
|
|
26900
26946
|
const urlTags = typeof args5["url-tags"] === "string" ? args5["url-tags"] : void 0;
|
|
@@ -27048,7 +27094,7 @@ function duplicateCommand2(entity, label) {
|
|
|
27048
27094
|
const { apiPost: apiPost2 } = await import("./client-GR6YL3YX.js");
|
|
27049
27095
|
const { requireChatId: requireChatId2 } = await import("./env-4P5HOGNA.js");
|
|
27050
27096
|
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-NMQXZF3Z.js");
|
|
27051
|
-
const { handleMetaError: handleMetaError2 } = await import("./shared-
|
|
27097
|
+
const { handleMetaError: handleMetaError2 } = await import("./shared-6762QE6R.js");
|
|
27052
27098
|
try {
|
|
27053
27099
|
const accountId = bareAccountId2(args5);
|
|
27054
27100
|
const chatId = requireChatId2();
|
|
@@ -28182,6 +28228,36 @@ registerSchema({
|
|
|
28182
28228
|
output: { type: "string", description: "json|csv", required: false, default: "json" }
|
|
28183
28229
|
}
|
|
28184
28230
|
});
|
|
28231
|
+
registerSchema({
|
|
28232
|
+
command: "ads.meta.lead-forms.create",
|
|
28233
|
+
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.",
|
|
28234
|
+
args: {
|
|
28235
|
+
"account-id": { type: "string", description: "Selected Meta ad account, act_<id>", required: true },
|
|
28236
|
+
"page-id": {
|
|
28237
|
+
type: "string",
|
|
28238
|
+
description: "Facebook Page that owns the form \u2014 choose one with this account in promotingAccountIds and hasPageAccessToken: true from `baker ads meta pages`",
|
|
28239
|
+
required: true
|
|
28240
|
+
},
|
|
28241
|
+
name: { type: "string", description: "Form name", required: true },
|
|
28242
|
+
questions: {
|
|
28243
|
+
type: "string",
|
|
28244
|
+
description: "Comma-separated Meta fields, e.g. FULL_NAME,EMAIL,PHONE",
|
|
28245
|
+
required: true
|
|
28246
|
+
},
|
|
28247
|
+
"privacy-policy-url": { type: "string", description: "Public HTTPS privacy policy", required: true },
|
|
28248
|
+
"follow-up-url": { type: "string", description: "Public HTTPS link after submission", required: true },
|
|
28249
|
+
"optimized-for-quality": {
|
|
28250
|
+
type: "boolean",
|
|
28251
|
+
description: "Ask Meta to optimize form leads for quality",
|
|
28252
|
+
required: false
|
|
28253
|
+
},
|
|
28254
|
+
file: {
|
|
28255
|
+
type: "string",
|
|
28256
|
+
description: "JSON file with full form payload, including thankYou and custom questions",
|
|
28257
|
+
required: false
|
|
28258
|
+
}
|
|
28259
|
+
}
|
|
28260
|
+
});
|
|
28185
28261
|
registerSchema({
|
|
28186
28262
|
command: "ads.meta.leads",
|
|
28187
28263
|
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.",
|
|
@@ -28247,20 +28323,47 @@ function leadPageHints(page, full) {
|
|
|
28247
28323
|
}
|
|
28248
28324
|
return hints2;
|
|
28249
28325
|
}
|
|
28250
|
-
var
|
|
28326
|
+
var leadFormsCreateCommand2 = defineCommand67({
|
|
28251
28327
|
meta: {
|
|
28252
|
-
name: "
|
|
28253
|
-
description: `
|
|
28254
|
-
|
|
28255
|
-
|
|
28256
|
-
baker ads meta lead-forms --page-id 100635183117758
|
|
28257
|
-
baker ads meta lead-forms --page-id 100635183117758 --full # also lists each form's questions`
|
|
28328
|
+
name: "create",
|
|
28329
|
+
description: `Stage a Meta Instant Form on a Facebook Page. The form appears in review and is created only when the Session publishes.
|
|
28330
|
+
Choose a Page with this account in promotingAccountIds and hasPageAccessToken: true from \`baker ads meta pages\`.
|
|
28331
|
+
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`
|
|
28258
28332
|
},
|
|
28259
28333
|
args: {
|
|
28334
|
+
"account-id": { type: "string", description: "Selected Meta ad account" },
|
|
28260
28335
|
"page-id": { type: "string", description: "Facebook Page ID" },
|
|
28261
|
-
|
|
28262
|
-
|
|
28336
|
+
name: { type: "string", description: "Form name" },
|
|
28337
|
+
questions: { type: "string", description: "FULL_NAME,EMAIL,PHONE or other Meta fields" },
|
|
28338
|
+
"privacy-policy-url": { type: "string", description: "HTTPS privacy policy" },
|
|
28339
|
+
"follow-up-url": { type: "string", description: "HTTPS thank-you link" },
|
|
28340
|
+
"optimized-for-quality": { type: "boolean", description: "Optimize form leads for quality" },
|
|
28341
|
+
file: { type: "string", description: "JSON form payload; flags override file keys" }
|
|
28342
|
+
},
|
|
28343
|
+
run: async ({ args: args5 }) => {
|
|
28344
|
+
const accountId = bareAccountId2(args5);
|
|
28345
|
+
const payload = mergePayload3(loadJsonFileArg3(args5.file), {
|
|
28346
|
+
page_id: args5["page-id"],
|
|
28347
|
+
name: args5.name,
|
|
28348
|
+
questions: typeof args5.questions === "string" ? args5.questions.split(",").map((type) => ({ type: type.trim().toUpperCase() })) : void 0,
|
|
28349
|
+
privacyPolicyUrl: args5["privacy-policy-url"],
|
|
28350
|
+
followUpActionUrl: args5["follow-up-url"],
|
|
28351
|
+
optimizedForQuality: args5["optimized-for-quality"]
|
|
28352
|
+
});
|
|
28353
|
+
await stageOp2({ kind: "leadForm.create", accountId, payload });
|
|
28354
|
+
}
|
|
28355
|
+
});
|
|
28356
|
+
var leadFormsListArgs = {
|
|
28357
|
+
"page-id": { type: "string", description: "Facebook Page ID" },
|
|
28358
|
+
full: { type: "boolean", description: "Include each form's questions" },
|
|
28359
|
+
output: { type: "string", description: "Output format", default: "json" }
|
|
28360
|
+
};
|
|
28361
|
+
var leadFormsListCommand = defineCommand67({
|
|
28362
|
+
meta: {
|
|
28363
|
+
name: "list",
|
|
28364
|
+
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."
|
|
28263
28365
|
},
|
|
28366
|
+
args: leadFormsListArgs,
|
|
28264
28367
|
run: async ({ args: args5 }) => {
|
|
28265
28368
|
try {
|
|
28266
28369
|
const pageId = args5["page-id"];
|
|
@@ -28286,6 +28389,19 @@ Examples:
|
|
|
28286
28389
|
}
|
|
28287
28390
|
}
|
|
28288
28391
|
});
|
|
28392
|
+
var leadFormsCommand2 = defineCommand67({
|
|
28393
|
+
meta: {
|
|
28394
|
+
name: "lead-forms",
|
|
28395
|
+
description: `List Instant Forms on a Facebook Page or stage a new one.
|
|
28396
|
+
Examples:
|
|
28397
|
+
baker ads meta lead-forms --page-id 100635183117758
|
|
28398
|
+
baker ads meta lead-forms --page-id 100635183117758 --full
|
|
28399
|
+
baker ads meta lead-forms create --account-id act_123 --page-id 555 --file form.json`
|
|
28400
|
+
},
|
|
28401
|
+
args: leadFormsListArgs,
|
|
28402
|
+
subCommands: { list: leadFormsListCommand, create: leadFormsCreateCommand2 },
|
|
28403
|
+
default: "list"
|
|
28404
|
+
});
|
|
28289
28405
|
var leadsCommand2 = defineCommand67({
|
|
28290
28406
|
meta: {
|
|
28291
28407
|
name: "leads",
|
|
@@ -28631,6 +28747,7 @@ Discovery:
|
|
|
28631
28747
|
baker ads meta businesses \u2014 Business Manager IDs
|
|
28632
28748
|
baker ads meta pages \u2014 Facebook Pages the selected ad accounts can promote (an ad's identity)
|
|
28633
28749
|
baker ads meta lead-forms --page-id 100635183117758 \u2014 instant (lead) forms on a Page, with lead counts
|
|
28750
|
+
baker ads meta lead-forms create --account-id act_123 --page-id 555 --file form.json \u2014 stage a new form
|
|
28634
28751
|
baker ads meta leads --form-id 987654321 \u2014 the leads one form collected (contact details masked)
|
|
28635
28752
|
|
|
28636
28753
|
Snapshot a running account (default = ACTIVE only):
|