@koda-sl/baker-cli 0.392.0-dev.9666ad9b3 → 0.393.0-dev.10b6da011
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +76 -27
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -33300,7 +33300,13 @@ var imageCropSchema = z31.strictObject({
|
|
|
33300
33300
|
var budgetMicrosSchema = z31.number().int("budgets are integer micros \u2014 \u20AC20.00 is 20000000").min(CHATGPT_LIMITS.budgetMinMicros, "a budget is at least one unit of the account currency");
|
|
33301
33301
|
var bidMicrosSchema = z31.number().int("bids are integer micros \u2014 \u20AC2.50 is 2500000").min(1);
|
|
33302
33302
|
var httpsUrlSchema3 = z31.string().trim().max(CHATGPT_LIMITS.urlMax).url("expected a full URL").refine((value) => value.startsWith("https://"), "ChatGPT Ads needs an https:// address");
|
|
33303
|
-
var
|
|
33303
|
+
var DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
33304
|
+
var LOCAL_TIME = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?$/;
|
|
33305
|
+
var ZONED_TIME = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/;
|
|
33306
|
+
var isoTimeSchema2 = z31.string().trim().refine(
|
|
33307
|
+
(value) => DATE_ONLY.test(value) || LOCAL_TIME.test(value) || ZONED_TIME.test(value),
|
|
33308
|
+
"a date is YYYY-MM-DD, or YYYY-MM-DDTHH:MM in the account's time zone \u2014 e.g. 2026-10-12"
|
|
33309
|
+
).refine((value) => !Number.isNaN(Date.parse(value)), "that date does not exist").transform((value) => ZONED_TIME.test(value) ? new Date(value).toISOString() : value);
|
|
33304
33310
|
var LAST_SCHEDULABLE_MS = Date.parse("2100-01-01T00:00:00Z");
|
|
33305
33311
|
function notPast(what) {
|
|
33306
33312
|
return isoTimeSchema2.refine((value) => Date.parse(value) > Date.now() - 6e4, `the ${what} is in the past`).refine(
|
|
@@ -33308,7 +33314,7 @@ function notPast(what) {
|
|
|
33308
33314
|
`the ${what} is after 2099, which ChatGPT Ads cannot store`
|
|
33309
33315
|
);
|
|
33310
33316
|
}
|
|
33311
|
-
var startTimeSchema = notPast("start
|
|
33317
|
+
var startTimeSchema = notPast("start");
|
|
33312
33318
|
var endTimeSchema = notPast("end");
|
|
33313
33319
|
var countrySchema = z31.string().trim().regex(/^[A-Za-z]{2}$/, "countries are two-letter codes, e.g. ES").transform((value) => value.toUpperCase()).refine((value) => import_i18n_iso_countries.default.isValid(value), "that is not a country code (ISO 3166-1, e.g. ES)");
|
|
33314
33320
|
var locationIdSchema = z31.string().trim().regex(/^\d+$/, "a region id is the number `baker ads chatgpt locations <place>` returns");
|
|
@@ -33382,6 +33388,13 @@ var conversionEventCreateSchema = z31.strictObject({
|
|
|
33382
33388
|
message: "a custom conversion needs the event name the site sends"
|
|
33383
33389
|
});
|
|
33384
33390
|
}
|
|
33391
|
+
if (p.eventType === "app_installed" || p.eventType === "app_opened") {
|
|
33392
|
+
ctx.addIssue({
|
|
33393
|
+
code: "custom",
|
|
33394
|
+
path: ["eventType"],
|
|
33395
|
+
message: "an app event reaches ChatGPT Ads only through the Conversions API, never a website pixel \u2014 and a conversion cannot be deleted, so it is not staged"
|
|
33396
|
+
});
|
|
33397
|
+
}
|
|
33385
33398
|
if (p.eventType !== "custom" && p.customEventName !== void 0) {
|
|
33386
33399
|
ctx.addIssue({
|
|
33387
33400
|
code: "custom",
|
|
@@ -33422,17 +33435,21 @@ var COUNTRY_NAMES = new Map(
|
|
|
33422
33435
|
CHATGPT_GEOTARGETS.filter(([, , , type]) => type === "country").map(([, name, countryCode2]) => [countryCode2, name])
|
|
33423
33436
|
);
|
|
33424
33437
|
var MAX_PLACES = 2500;
|
|
33438
|
+
function uniqueList(item, min, max) {
|
|
33439
|
+
const list = z31.array(item).min(min);
|
|
33440
|
+
return (max === void 0 ? list : list.max(max)).transform((values) => [...new Set(values)]);
|
|
33441
|
+
}
|
|
33425
33442
|
function targetingFields(min) {
|
|
33426
33443
|
return {
|
|
33427
|
-
countries:
|
|
33444
|
+
countries: uniqueList(countrySchema, min, MAX_PLACES).optional(),
|
|
33428
33445
|
/** Region, market and postal-code ids from ChatGPT Ads' location lookup. */
|
|
33429
|
-
locationIds:
|
|
33446
|
+
locationIds: uniqueList(locationIdSchema, min, MAX_PLACES).optional(),
|
|
33430
33447
|
/** Countries and places the campaign never serves in, even inside an included one. */
|
|
33431
|
-
excludedCountries:
|
|
33432
|
-
excludedLocationIds:
|
|
33448
|
+
excludedCountries: uniqueList(countrySchema, min, MAX_PLACES).optional(),
|
|
33449
|
+
excludedLocationIds: uniqueList(locationIdSchema, min, MAX_PLACES).optional(),
|
|
33433
33450
|
/** Custom audiences the campaign serves only to, and ones it never serves. */
|
|
33434
|
-
audienceIds:
|
|
33435
|
-
excludedAudienceIds:
|
|
33451
|
+
audienceIds: uniqueList(audienceIdSchema, min).optional(),
|
|
33452
|
+
excludedAudienceIds: uniqueList(audienceIdSchema, min).optional(),
|
|
33436
33453
|
platforms: z31.array(z31.enum(CHATGPT_PLATFORMS)).min(1).optional()
|
|
33437
33454
|
};
|
|
33438
33455
|
}
|
|
@@ -34346,7 +34363,7 @@ var SHARED_ARGS = {
|
|
|
34346
34363
|
description: { type: "string", description: "Internal description" },
|
|
34347
34364
|
"context-hints": {
|
|
34348
34365
|
type: "string",
|
|
34349
|
-
description: "When the ad would be useful,
|
|
34366
|
+
description: "When the ad would be useful, short phrases separated by commas \u2014 or by | when a phrase has a comma. ChatGPT's counterpart to keywords. On update it replaces the whole list; none clears it"
|
|
34350
34367
|
},
|
|
34351
34368
|
"bid-strategy": {
|
|
34352
34369
|
type: "string",
|
|
@@ -34372,6 +34389,16 @@ var UPDATE_ARGS2 = {
|
|
|
34372
34389
|
id: { type: "positional", description: "Ad group id (adgrp_\u2026) or a staged chatgpt_temp_ ref", required: true },
|
|
34373
34390
|
...SHARED_ARGS
|
|
34374
34391
|
};
|
|
34392
|
+
function contextHintsArg(value) {
|
|
34393
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
34394
|
+
return void 0;
|
|
34395
|
+
}
|
|
34396
|
+
if (value.trim().toLowerCase() === "none") {
|
|
34397
|
+
return [];
|
|
34398
|
+
}
|
|
34399
|
+
const separator = value.includes("|") ? "|" : ",";
|
|
34400
|
+
return value.split(separator).map((hint) => hint.trim()).filter(Boolean);
|
|
34401
|
+
}
|
|
34375
34402
|
function productSetArg(value) {
|
|
34376
34403
|
const filters = productFiltersArg(value);
|
|
34377
34404
|
return filters === void 0 ? void 0 : { filters };
|
|
@@ -34380,7 +34407,7 @@ function sharedPayload(args8) {
|
|
|
34380
34407
|
return {
|
|
34381
34408
|
name: args8.name,
|
|
34382
34409
|
description: args8.description,
|
|
34383
|
-
contextHints:
|
|
34410
|
+
contextHints: contextHintsArg(args8["context-hints"]),
|
|
34384
34411
|
bidStrategy: lowerArg(args8["bid-strategy"]),
|
|
34385
34412
|
maxBidMicros: microsFromArg(args8["max-bid"], "--max-bid"),
|
|
34386
34413
|
trackingTemplate: clearableArg(args8["tracking-template"]),
|
|
@@ -34408,7 +34435,7 @@ Examples:
|
|
|
34408
34435
|
args: CREATE_ARGS,
|
|
34409
34436
|
run: async ({ args: args8 }) => {
|
|
34410
34437
|
await stageChatgptOp(adGroupCreateOp(args8), REFERENCE2, (staged) => [
|
|
34411
|
-
|
|
34438
|
+
adGroupNextHint(staged.ref, args8["product-filters"] !== void 0)
|
|
34412
34439
|
]);
|
|
34413
34440
|
}
|
|
34414
34441
|
});
|
|
@@ -34437,6 +34464,10 @@ Examples:
|
|
|
34437
34464
|
var adGroupsPauseCommand = statusCommand2("adGroup", "pause", REFERENCE2);
|
|
34438
34465
|
var adGroupsResumeCommand = statusCommand2("adGroup", "resume", REFERENCE2);
|
|
34439
34466
|
var adGroupsArchiveCommand = statusCommand2("adGroup", "archive", REFERENCE2);
|
|
34467
|
+
function adGroupNextHint(ref, productFilters) {
|
|
34468
|
+
const productAd = `baker ads chatgpt ads create-product --ad-group-id ${ref} --name "\u2026"`;
|
|
34469
|
+
return productFilters ? `Next, give it its product ad: ${productAd}` : `Next, give it a card: baker ads chatgpt ads create --ad-group-id ${ref} --name "\u2026" --title "\u2026" --body "\u2026" --target-url https://\u2026 --image-url https://\u2026 \u2014 under a catalog campaign, its product ad instead: ${productAd}`;
|
|
34470
|
+
}
|
|
34440
34471
|
|
|
34441
34472
|
// src/commands/ads/chatgpt/ad-groups.ts
|
|
34442
34473
|
var args = {
|
|
@@ -34707,7 +34738,7 @@ function sha256(value) {
|
|
|
34707
34738
|
}
|
|
34708
34739
|
function normalizedEmail(value) {
|
|
34709
34740
|
const email = value.trim().toLowerCase();
|
|
34710
|
-
return /^[^@\s]+@[^@\s]+$/.test(email) ? email : void 0;
|
|
34741
|
+
return /^[^@\s;,|]+@[^@\s;,|]+\.[^@\s;,|]+$/.test(email) ? email : void 0;
|
|
34711
34742
|
}
|
|
34712
34743
|
function normalizedPhone(value, defaultCountry) {
|
|
34713
34744
|
const country = defaultCountry && isSupportedCountry(defaultCountry) ? defaultCountry : void 0;
|
|
@@ -34728,13 +34759,22 @@ function hashedMembers(input) {
|
|
|
34728
34759
|
}
|
|
34729
34760
|
return { emailHashes: [...emailHashes], phoneHashes: [...phoneHashes], rejected };
|
|
34730
34761
|
}
|
|
34731
|
-
|
|
34732
|
-
|
|
34762
|
+
function headerWords(cell3) {
|
|
34763
|
+
return cell3.normalize("NFD").replace(new RegExp("\\p{Diacritic}", "gu"), "").toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
34764
|
+
}
|
|
34765
|
+
var EMAIL_HEADER = /\b(e ?mail|correo|mail)\b/;
|
|
34766
|
+
var PHONE_HEADER = /\b(phone|telefono|movil|mobile|tel|celular)\b/;
|
|
34733
34767
|
function membersFromFile(content) {
|
|
34734
|
-
const rows = parse(content, {
|
|
34735
|
-
|
|
34736
|
-
|
|
34737
|
-
|
|
34768
|
+
const rows = parse(content, {
|
|
34769
|
+
bom: true,
|
|
34770
|
+
skip_empty_lines: true,
|
|
34771
|
+
relax_column_count: true,
|
|
34772
|
+
trim: true,
|
|
34773
|
+
delimiter: [",", ";", " "]
|
|
34774
|
+
});
|
|
34775
|
+
const header = (rows[0] ?? []).map(headerWords);
|
|
34776
|
+
const emailColumn = header.findIndex((cell3) => EMAIL_HEADER.test(cell3));
|
|
34777
|
+
const phoneColumn = header.findIndex((cell3) => !EMAIL_HEADER.test(cell3) && PHONE_HEADER.test(cell3));
|
|
34738
34778
|
if (emailColumn >= 0 || phoneColumn >= 0) {
|
|
34739
34779
|
const cells = (column) => column < 0 ? [] : rows.slice(1).flatMap((row) => row[column] ? [row[column]] : []);
|
|
34740
34780
|
return { emails: cells(emailColumn), phones: cells(phoneColumn) };
|
|
@@ -34756,21 +34796,23 @@ var MEMBER_ARGS = {
|
|
|
34756
34796
|
};
|
|
34757
34797
|
function membersFromArgs(args8, readFile34 = (path48) => readFileSync2(path48, "utf8")) {
|
|
34758
34798
|
const fromFile = typeof args8.file === "string" && args8.file ? membersFromFile(readFile34(args8.file)) : { emails: [], phones: [] };
|
|
34799
|
+
const given = [...csvArg(args8.emails) ?? [], ...fromFile.emails];
|
|
34800
|
+
const givenPhones = [...csvArg(args8.phones) ?? [], ...fromFile.phones];
|
|
34759
34801
|
const members = hashedMembers({
|
|
34760
|
-
emails:
|
|
34761
|
-
phones:
|
|
34802
|
+
emails: given,
|
|
34803
|
+
phones: givenPhones,
|
|
34762
34804
|
defaultCountry: typeof args8.country === "string" ? args8.country.toUpperCase() : void 0
|
|
34763
34805
|
});
|
|
34764
34806
|
const count2 = members.emailHashes.length + members.phoneHashes.length;
|
|
34765
|
-
|
|
34766
|
-
|
|
34767
|
-
}
|
|
34807
|
+
const offered = given.length + givenPhones.length + (typeof args8.file === "string" && args8.file ? 1 : 0);
|
|
34808
|
+
const refusal = count2 > CHATGPT_MAX_STAGED_MEMBERS ? `${count2} members \u2014 at most ${CHATGPT_MAX_STAGED_MEMBERS} per change; split the file` : count2 === 0 && offered > 0 ? "none of the values was an email or a valid phone \u2014 name the columns email and phone, and give phones a +country code or --country" : void 0;
|
|
34768
34809
|
return {
|
|
34769
34810
|
payload: {
|
|
34770
34811
|
...members.emailHashes.length > 0 ? { emailHashes: members.emailHashes } : {},
|
|
34771
34812
|
...members.phoneHashes.length > 0 ? { phoneHashes: members.phoneHashes } : {}
|
|
34772
34813
|
},
|
|
34773
|
-
rejected: members.rejected.length
|
|
34814
|
+
rejected: members.rejected.length,
|
|
34815
|
+
...refusal ? { refusal } : {}
|
|
34774
34816
|
};
|
|
34775
34817
|
}
|
|
34776
34818
|
function rejectedHint(rejected) {
|
|
@@ -34825,7 +34867,10 @@ Examples:
|
|
|
34825
34867
|
},
|
|
34826
34868
|
args: CREATE_ARGS3,
|
|
34827
34869
|
run: async ({ args: args8 }) => {
|
|
34828
|
-
const { payload, rejected } = membersFromArgs(args8);
|
|
34870
|
+
const { payload, rejected, refusal } = membersFromArgs(args8);
|
|
34871
|
+
if (refusal) {
|
|
34872
|
+
failWriteValidation(refusal);
|
|
34873
|
+
}
|
|
34829
34874
|
await stageChatgptOp(
|
|
34830
34875
|
{
|
|
34831
34876
|
kind: "audience.create",
|
|
@@ -34859,7 +34904,10 @@ Example:
|
|
|
34859
34904
|
},
|
|
34860
34905
|
args: args8,
|
|
34861
34906
|
run: async ({ args: parsed }) => {
|
|
34862
|
-
const { payload, rejected } = membersFromArgs(parsed);
|
|
34907
|
+
const { payload, rejected, refusal } = membersFromArgs(parsed);
|
|
34908
|
+
if (refusal) {
|
|
34909
|
+
failWriteValidation(refusal);
|
|
34910
|
+
}
|
|
34863
34911
|
if (!payload.emailHashes && !payload.phoneHashes) {
|
|
34864
34912
|
failWriteValidation("name at least one email or phone with --emails, --phones or --file");
|
|
34865
34913
|
}
|
|
@@ -35025,7 +35073,8 @@ Examples:
|
|
|
35025
35073
|
args: CREATE_ARGS4,
|
|
35026
35074
|
run: async ({ args: args8 }) => {
|
|
35027
35075
|
await stageChatgptOp(campaignCreateOp(args8), REFERENCE5, (staged) => [
|
|
35028
|
-
`Next, give it an ad group: baker ads chatgpt ad-groups create --campaign-id ${staged.ref} --name "\u2026" --context-hints "\u2026" --max-bid 2.50
|
|
35076
|
+
`Next, give it an ad group: baker ads chatgpt ad-groups create --campaign-id ${staged.ref} --name "\u2026" --context-hints "\u2026" --max-bid 2.50`,
|
|
35077
|
+
"Check this summary against everything the user asked for \u2014 countries, places to leave out, budget, dates \u2014 and stage what is missing before saying it is ready."
|
|
35029
35078
|
]);
|
|
35030
35079
|
}
|
|
35031
35080
|
});
|