@koda-sl/baker-cli 0.434.0 → 0.435.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 +12 -0
- package/dist/cli.js +135 -2
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -621,6 +621,18 @@ baker ads google campaigns create --customer-id 1234567890 --name "Brand — Sea
|
|
|
621
621
|
# New campaigns are declared as NOT containing EU political advertising (Google requires the
|
|
622
622
|
# declaration on every create); pass --eu-political-ads only when the campaign does contain it.
|
|
623
623
|
# On update, --eu-political-ads / --eu-political-ads=false corrects an existing declaration.
|
|
624
|
+
# Text guidelines (AI Max Search and Performance Max) stage on update only:
|
|
625
|
+
baker ads google campaigns update <campaign-id> --customer-id 1234567890 \
|
|
626
|
+
--term-exclusion "cheap" --messaging-restriction "Never promise a delivery date"
|
|
627
|
+
# --term-exclusion (repeatable, up to 25 × 30 chars) and --messaging-restriction (repeatable,
|
|
628
|
+
# up to 40 × 300 chars) each REPLACE the campaign's list; --clear-term-exclusions /
|
|
629
|
+
# --clear-messaging-restrictions empty one. A Search campaign needs AI Max with text
|
|
630
|
+
# customization on (in the same update or before), or staging is refused with the reason.
|
|
631
|
+
# AI Max for Search stages on create and update: --ai-max / --ai-max=false (off turns both
|
|
632
|
+
# features off too), --ai-max-text (text customization, needs AI Max) and
|
|
633
|
+
# --ai-max-url-expansion (final URL expansion, needs text customization). An omitted switch
|
|
634
|
+
# keeps the campaign's value; a combination Google refuses is refused at staging.
|
|
635
|
+
baker ads google campaigns update <campaign-id> --customer-id 1234567890 --ai-max --ai-max-text
|
|
624
636
|
baker ads google ad-groups create --customer-id 1234567890 --name "Brand terms" --campaign-ref g_temp_<campaign>
|
|
625
637
|
baker ads google keywords add --customer-id 1234567890 --ad-group-ref g_temp_<adgroup> --text "brand name" --match-type EXACT
|
|
626
638
|
# Shared negative keyword list → attach to the campaign
|
package/dist/cli.js
CHANGED
|
@@ -24891,7 +24891,13 @@ var GOOGLE_ADS_LIMITS = {
|
|
|
24891
24891
|
/** `campaign.url_custom_parameters` — Google caps a single entity at 8 `{_name}` parameters. */
|
|
24892
24892
|
urlCustomParametersMax: 8,
|
|
24893
24893
|
urlCustomParameterKeyMax: 16,
|
|
24894
|
-
urlCustomParameterValueMax: 250
|
|
24894
|
+
urlCustomParameterValueMax: 250,
|
|
24895
|
+
/** `campaign.text_guidelines.term_exclusions` — words AI Max and Performance Max must never write. */
|
|
24896
|
+
termExclusionsMax: 25,
|
|
24897
|
+
termExclusionMax: 30,
|
|
24898
|
+
/** `campaign.text_guidelines.messaging_restrictions` — freeform rules for generated text. */
|
|
24899
|
+
messagingRestrictionsMax: 40,
|
|
24900
|
+
messagingRestrictionMax: 300
|
|
24895
24901
|
},
|
|
24896
24902
|
adGroup: {
|
|
24897
24903
|
nameMax: 255
|
|
@@ -25873,6 +25879,49 @@ var geoTargetTypeSettingSchema = z9.strictObject({
|
|
|
25873
25879
|
(p) => p.positiveGeoTargetType !== void 0 || p.negativeGeoTargetType !== void 0,
|
|
25874
25880
|
"geoTargetTypeSetting needs positiveGeoTargetType and/or negativeGeoTargetType"
|
|
25875
25881
|
);
|
|
25882
|
+
var textGuidelinesSchema = z9.strictObject({
|
|
25883
|
+
termExclusions: z9.array(z9.string().trim().min(1).max(GOOGLE_ADS_LIMITS.campaign.termExclusionMax)).max(GOOGLE_ADS_LIMITS.campaign.termExclusionsMax).optional(),
|
|
25884
|
+
messagingRestrictions: z9.array(z9.string().trim().min(1).max(GOOGLE_ADS_LIMITS.campaign.messagingRestrictionMax)).max(GOOGLE_ADS_LIMITS.campaign.messagingRestrictionsMax).refine(
|
|
25885
|
+
(rules) => new Set(rules.map((rule) => rule.toLowerCase())).size === rules.length,
|
|
25886
|
+
"each messaging restriction must be unique"
|
|
25887
|
+
).optional()
|
|
25888
|
+
}).refine(
|
|
25889
|
+
(p) => p.termExclusions !== void 0 || p.messagingRestrictions !== void 0,
|
|
25890
|
+
"textGuidelines needs termExclusions and/or messagingRestrictions"
|
|
25891
|
+
);
|
|
25892
|
+
var AI_MAX_TEXT_NEEDS_AI_MAX_MESSAGE = "AI Max text customization only works with AI Max on: also turn AI Max on, or leave text customization off.";
|
|
25893
|
+
var AI_MAX_URL_NEEDS_TEXT_MESSAGE = "AI Max final URL expansion only works with text customization on: also turn text customization on, or leave URL expansion off.";
|
|
25894
|
+
var aiMaxSchema = z9.strictObject({
|
|
25895
|
+
enabled: z9.boolean().optional(),
|
|
25896
|
+
textCustomization: z9.boolean().optional(),
|
|
25897
|
+
finalUrlExpansion: z9.boolean().optional()
|
|
25898
|
+
}).superRefine((p, ctx) => {
|
|
25899
|
+
if (p.enabled === void 0 && p.textCustomization === void 0 && p.finalUrlExpansion === void 0) {
|
|
25900
|
+
ctx.addIssue({ code: "custom", message: "aiMax needs enabled, textCustomization and/or finalUrlExpansion" });
|
|
25901
|
+
}
|
|
25902
|
+
if (p.enabled === false && p.textCustomization === true) {
|
|
25903
|
+
ctx.addIssue({ code: "custom", path: ["textCustomization"], message: AI_MAX_TEXT_NEEDS_AI_MAX_MESSAGE });
|
|
25904
|
+
}
|
|
25905
|
+
if (p.textCustomization === false && p.finalUrlExpansion === true) {
|
|
25906
|
+
ctx.addIssue({ code: "custom", path: ["finalUrlExpansion"], message: AI_MAX_URL_NEEDS_TEXT_MESSAGE });
|
|
25907
|
+
}
|
|
25908
|
+
});
|
|
25909
|
+
var AI_MAX_SEARCH_ONLY_MESSAGE = "AI Max exists only on Search campaigns.";
|
|
25910
|
+
function aiMaxCreateRefusal(channelType, aiMax) {
|
|
25911
|
+
if (!aiMax) {
|
|
25912
|
+
return void 0;
|
|
25913
|
+
}
|
|
25914
|
+
if (channelType !== "SEARCH") {
|
|
25915
|
+
return AI_MAX_SEARCH_ONLY_MESSAGE;
|
|
25916
|
+
}
|
|
25917
|
+
if (aiMax.enabled !== true && aiMax.textCustomization === true) {
|
|
25918
|
+
return AI_MAX_TEXT_NEEDS_AI_MAX_MESSAGE;
|
|
25919
|
+
}
|
|
25920
|
+
if (aiMax.textCustomization !== true && aiMax.finalUrlExpansion === true) {
|
|
25921
|
+
return AI_MAX_URL_NEEDS_TEXT_MESSAGE;
|
|
25922
|
+
}
|
|
25923
|
+
return void 0;
|
|
25924
|
+
}
|
|
25876
25925
|
var campaignCreateSchema2 = z9.strictObject({
|
|
25877
25926
|
name: z9.string().min(1).max(GOOGLE_ADS_LIMITS.campaign.nameMax),
|
|
25878
25927
|
channelType: z9.enum(ADVERTISING_CHANNEL_TYPES),
|
|
@@ -25902,6 +25951,8 @@ var campaignCreateSchema2 = z9.strictObject({
|
|
|
25902
25951
|
* every campaign create (FieldError.REQUIRED without it); omitted means it does not.
|
|
25903
25952
|
*/
|
|
25904
25953
|
euPoliticalAds: z9.boolean().optional(),
|
|
25954
|
+
/** AI Max for Search. A new campaign has it off unless this turns it on. */
|
|
25955
|
+
aiMax: aiMaxSchema.optional(),
|
|
25905
25956
|
status: stageableStatusSchema2.default("PAUSED")
|
|
25906
25957
|
}).superRefine((p, ctx) => {
|
|
25907
25958
|
if (!p.bidding && !p.biddingStrategy) {
|
|
@@ -25928,6 +25979,10 @@ var campaignCreateSchema2 = z9.strictObject({
|
|
|
25928
25979
|
if (p.startDate && p.endDate && p.endDate <= p.startDate) {
|
|
25929
25980
|
ctx.addIssue({ code: "custom", path: ["endDate"], message: "endDate must be after startDate" });
|
|
25930
25981
|
}
|
|
25982
|
+
const aiMaxRefusal = aiMaxCreateRefusal(p.channelType, p.aiMax);
|
|
25983
|
+
if (aiMaxRefusal) {
|
|
25984
|
+
ctx.addIssue({ code: "custom", path: ["aiMax"], message: aiMaxRefusal });
|
|
25985
|
+
}
|
|
25931
25986
|
});
|
|
25932
25987
|
var campaignUpdateSchema2 = z9.strictObject({
|
|
25933
25988
|
name: z9.string().min(1).max(GOOGLE_ADS_LIMITS.campaign.nameMax).optional(),
|
|
@@ -25946,6 +26001,10 @@ var campaignUpdateSchema2 = z9.strictObject({
|
|
|
25946
26001
|
urlCustomParameters: urlCustomParametersSchema.optional(),
|
|
25947
26002
|
/** Corrects the campaign's EU political advertising declaration (true = contains, false = does not). */
|
|
25948
26003
|
euPoliticalAds: z9.boolean().optional(),
|
|
26004
|
+
/** AI Max / Performance Max text guidelines. Google refuses them on a Search campaign without AI Max. */
|
|
26005
|
+
textGuidelines: textGuidelinesSchema.optional(),
|
|
26006
|
+
/** Turns AI Max or its features on or off. Search campaigns only (checked against the live channel). */
|
|
26007
|
+
aiMax: aiMaxSchema.optional(),
|
|
25949
26008
|
status: z9.enum(["ENABLED", "PAUSED", "REMOVED"]).optional()
|
|
25950
26009
|
}).refine((p) => Object.values(p).some((v) => v !== void 0), "update needs at least one field");
|
|
25951
26010
|
var adGroupCreateSchema = z9.strictObject({
|
|
@@ -49037,6 +49096,74 @@ function urlCustomParametersFromFlags(args8, rawArgs) {
|
|
|
49037
49096
|
return { key: entry.slice(0, split), value: entry.slice(split + 1) };
|
|
49038
49097
|
});
|
|
49039
49098
|
}
|
|
49099
|
+
var aiMaxArgs = {
|
|
49100
|
+
"ai-max": {
|
|
49101
|
+
type: "boolean",
|
|
49102
|
+
description: "AI Max for Search: --ai-max turns it on (Google then matches searches beyond your keywords), --ai-max=false turns it off along with its text customization and URL expansion. Search campaigns only."
|
|
49103
|
+
},
|
|
49104
|
+
"ai-max-text": {
|
|
49105
|
+
type: "boolean",
|
|
49106
|
+
description: "AI Max text customization: --ai-max-text lets Google write ad text from your landing page and ads (needs AI Max on); --ai-max-text=false stops it. Required before text guidelines."
|
|
49107
|
+
},
|
|
49108
|
+
"ai-max-url-expansion": {
|
|
49109
|
+
type: "boolean",
|
|
49110
|
+
description: "AI Max final URL expansion: --ai-max-url-expansion lets Google send clicks to the most relevant page of your site (needs text customization on); --ai-max-url-expansion=false keeps every click on your final URLs."
|
|
49111
|
+
}
|
|
49112
|
+
};
|
|
49113
|
+
function aiMaxFromFlags(args8) {
|
|
49114
|
+
const flag2 = (name) => typeof args8[name] === "boolean" ? args8[name] : void 0;
|
|
49115
|
+
const aiMax = {
|
|
49116
|
+
enabled: flag2("ai-max"),
|
|
49117
|
+
textCustomization: flag2("ai-max-text"),
|
|
49118
|
+
finalUrlExpansion: flag2("ai-max-url-expansion")
|
|
49119
|
+
};
|
|
49120
|
+
const stated = Object.entries(aiMax).filter(([, v]) => v !== void 0);
|
|
49121
|
+
return stated.length > 0 ? Object.fromEntries(stated) : void 0;
|
|
49122
|
+
}
|
|
49123
|
+
var textGuidelinesArgs = {
|
|
49124
|
+
"term-exclusion": {
|
|
49125
|
+
type: "string",
|
|
49126
|
+
description: `Text guidelines (AI Max Search and Performance Max): a word or phrase Google's generated ad text must never use, up to 30 characters (e.g. --term-exclusion "cheap"). Repeat for several; Google allows 25. REPLACES the campaign's whole list.`
|
|
49127
|
+
},
|
|
49128
|
+
"messaging-restriction": {
|
|
49129
|
+
type: "string",
|
|
49130
|
+
description: `Text guidelines (AI Max Search and Performance Max): a plain-language rule for Google's generated ad text, up to 300 characters (e.g. --messaging-restriction "Never promise a delivery date"). Repeat for several; Google allows 40. REPLACES the campaign's whole list.`
|
|
49131
|
+
},
|
|
49132
|
+
"clear-term-exclusions": {
|
|
49133
|
+
type: "boolean",
|
|
49134
|
+
description: "Remove every term exclusion from this campaign's text guidelines."
|
|
49135
|
+
},
|
|
49136
|
+
"clear-messaging-restrictions": {
|
|
49137
|
+
type: "boolean",
|
|
49138
|
+
description: "Remove every messaging restriction from this campaign's text guidelines."
|
|
49139
|
+
}
|
|
49140
|
+
};
|
|
49141
|
+
function guidelineListFromFlags(args8, rawArgs, flag2, clearFlag) {
|
|
49142
|
+
const values = repeatedValues(rawArgs, flag2, args8[flag2]).filter((v) => v.length > 0);
|
|
49143
|
+
if (values.length === 0) {
|
|
49144
|
+
return args8[clearFlag] === true ? [] : void 0;
|
|
49145
|
+
}
|
|
49146
|
+
if (args8[clearFlag] === true) {
|
|
49147
|
+
failWriteValidation2(`--${clearFlag} removes the whole list \u2014 pass it OR --${flag2}, not both`);
|
|
49148
|
+
}
|
|
49149
|
+
return values;
|
|
49150
|
+
}
|
|
49151
|
+
function textGuidelinesFromFlags(args8, rawArgs) {
|
|
49152
|
+
const termExclusions = guidelineListFromFlags(args8, rawArgs, "term-exclusion", "clear-term-exclusions");
|
|
49153
|
+
const messagingRestrictions = guidelineListFromFlags(
|
|
49154
|
+
args8,
|
|
49155
|
+
rawArgs,
|
|
49156
|
+
"messaging-restriction",
|
|
49157
|
+
"clear-messaging-restrictions"
|
|
49158
|
+
);
|
|
49159
|
+
if (termExclusions === void 0 && messagingRestrictions === void 0) {
|
|
49160
|
+
return void 0;
|
|
49161
|
+
}
|
|
49162
|
+
return {
|
|
49163
|
+
...termExclusions !== void 0 ? { termExclusions } : {},
|
|
49164
|
+
...messagingRestrictions !== void 0 ? { messagingRestrictions } : {}
|
|
49165
|
+
};
|
|
49166
|
+
}
|
|
49040
49167
|
var geoTargetTypeArgs = {
|
|
49041
49168
|
"geo-target-type": {
|
|
49042
49169
|
type: "string",
|
|
@@ -49188,6 +49315,7 @@ var campaignsCommand3 = defineCommand57({
|
|
|
49188
49315
|
...urlOptionsArgs,
|
|
49189
49316
|
...geoTargetTypeArgs,
|
|
49190
49317
|
...networksArg,
|
|
49318
|
+
...aiMaxArgs,
|
|
49191
49319
|
objective: { type: "string", description: "Advisory UI objective (SALES, LEADS, \u2026)" },
|
|
49192
49320
|
"start-date": { type: "string", description: "YYYY-MM-DD" },
|
|
49193
49321
|
"end-date": { type: "string", description: "YYYY-MM-DD" },
|
|
@@ -49214,6 +49342,7 @@ var campaignsCommand3 = defineCommand57({
|
|
|
49214
49342
|
startDate: args8["start-date"],
|
|
49215
49343
|
endDate: args8["end-date"],
|
|
49216
49344
|
euPoliticalAds: args8["eu-political-ads"],
|
|
49345
|
+
aiMax: aiMaxFromFlags(args8),
|
|
49217
49346
|
status: args8.status
|
|
49218
49347
|
});
|
|
49219
49348
|
await stageCreate("google.campaign.create", customerId, payload, [
|
|
@@ -49225,7 +49354,7 @@ var campaignsCommand3 = defineCommand57({
|
|
|
49225
49354
|
update: defineCommand57({
|
|
49226
49355
|
meta: {
|
|
49227
49356
|
name: "update",
|
|
49228
|
-
description: `Stage a change to an existing campaign's name, budget, bidding or
|
|
49357
|
+
description: `Stage a change to an existing campaign's name, budget, bidding, settings, AI Max switches or text guidelines (what AI Max and Performance Max ad text must avoid). ${STAGED_WRITE}`
|
|
49229
49358
|
},
|
|
49230
49359
|
args: {
|
|
49231
49360
|
...customerIdArg,
|
|
@@ -49253,6 +49382,8 @@ var campaignsCommand3 = defineCommand57({
|
|
|
49253
49382
|
...urlOptionsArgs,
|
|
49254
49383
|
...geoTargetTypeArgs,
|
|
49255
49384
|
...networksArg,
|
|
49385
|
+
...textGuidelinesArgs,
|
|
49386
|
+
...aiMaxArgs,
|
|
49256
49387
|
"eu-political-ads": {
|
|
49257
49388
|
type: "boolean",
|
|
49258
49389
|
description: "Corrects the campaign's EU political advertising declaration: --eu-political-ads if it contains EU political ads, --eu-political-ads=false if it does not. Existing campaigns without a declaration block location-targeting changes until one is set."
|
|
@@ -49283,6 +49414,8 @@ var campaignsCommand3 = defineCommand57({
|
|
|
49283
49414
|
trackingUrlTemplate: args8["tracking-template"],
|
|
49284
49415
|
urlCustomParameters: urlCustomParametersFromFlags(args8, rawArgs),
|
|
49285
49416
|
euPoliticalAds: args8["eu-political-ads"],
|
|
49417
|
+
textGuidelines: textGuidelinesFromFlags(args8, rawArgs),
|
|
49418
|
+
aiMax: aiMaxFromFlags(args8),
|
|
49286
49419
|
status: args8.status
|
|
49287
49420
|
});
|
|
49288
49421
|
await stageUpdate("google.campaign.update", customerId, target2, payload);
|