@koda-sl/baker-cli 0.118.0 → 0.119.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 +38 -5
- package/dist/cli.js +560 -43
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1093,6 +1093,7 @@ var CURRENCY_MINIMUMS = {
|
|
|
1093
1093
|
GBP: { dailyBudgetMin: 8, unitCostMin: 2 }
|
|
1094
1094
|
};
|
|
1095
1095
|
var DEFAULT_CURRENCY_MINIMUM = { dailyBudgetMin: 10, unitCostMin: 2 };
|
|
1096
|
+
var TEMP_REF_PREFIX = "li_temp_";
|
|
1096
1097
|
var TEMP_REF_REGEX = /^li_temp_[A-Za-z0-9_-]{4,}$/;
|
|
1097
1098
|
var NUMERIC_ID_REGEX = /^\d+$/;
|
|
1098
1099
|
var URN_REGEX = /^urn:li:[a-zA-Z]+:.+$/;
|
|
@@ -1228,7 +1229,15 @@ var campaignUpdateSchema = z3.object({
|
|
|
1228
1229
|
offsiteDeliveryEnabled: z3.boolean().optional(),
|
|
1229
1230
|
connectedTelevisionOnly: z3.boolean().optional(),
|
|
1230
1231
|
creativeSelection: z3.enum(CREATIVE_SELECTIONS).optional(),
|
|
1231
|
-
status: entityStatusSchema.optional()
|
|
1232
|
+
status: entityStatusSchema.optional(),
|
|
1233
|
+
// Create-only on LinkedIn — a live campaign can't change these in place.
|
|
1234
|
+
// Accepted here only so `draft amend` can carry them into a staged CREATE
|
|
1235
|
+
// (li_temp_* target, re-validated as `campaignCreateSchema`); stage-time
|
|
1236
|
+
// rejects them outright when the update targets a live campaign.
|
|
1237
|
+
campaignGroup: campaignFields.campaignGroup.optional(),
|
|
1238
|
+
type: campaignFields.type.optional(),
|
|
1239
|
+
locale: campaignFields.locale.optional(),
|
|
1240
|
+
associatedEntity: campaignFields.associatedEntity
|
|
1232
1241
|
}).superRefine((p, ctx) => {
|
|
1233
1242
|
if (!Object.values(p).some((val) => val !== void 0)) {
|
|
1234
1243
|
ctx.addIssue({ code: "custom", message: "update needs at least one field" });
|
|
@@ -1443,7 +1452,12 @@ var creativeUpdateSchema = z3.object({
|
|
|
1443
1452
|
* live ad's snapshot content (real targets, direct-content formats) and
|
|
1444
1453
|
* re-validated against the full creative schema. Post-based ads reject it.
|
|
1445
1454
|
*/
|
|
1446
|
-
content: z3.record(z3.string(), z3.unknown()).optional()
|
|
1455
|
+
content: z3.record(z3.string(), z3.unknown()).optional(),
|
|
1456
|
+
// Re-parenting a creative — LinkedIn has no in-place move-to-campaign API.
|
|
1457
|
+
// Accepted here only so `draft amend` can carry it into a staged CREATE
|
|
1458
|
+
// (li_temp_* target, re-validated as `creativeCreateSchema`); stage-time
|
|
1459
|
+
// rejects it outright when the update targets a live creative.
|
|
1460
|
+
campaign: parentRefSchema.optional()
|
|
1447
1461
|
}).refine((p) => Object.values(p).some((val) => val !== void 0), "update needs at least one field");
|
|
1448
1462
|
var listRowSchema = z3.record(z3.string(), z3.string());
|
|
1449
1463
|
var audienceCreateSchema = z3.object({
|
|
@@ -1667,6 +1681,12 @@ var linkedinDraftStageResponseSchema = z4.object({
|
|
|
1667
1681
|
/** True when the op amended an already-staged op in place instead of appending a new one. */
|
|
1668
1682
|
amended: z4.boolean().optional()
|
|
1669
1683
|
});
|
|
1684
|
+
var linkedinDraftAmendRequestSchema = z4.object({
|
|
1685
|
+
chatId: z4.string(),
|
|
1686
|
+
/** The staged op's ref, or (for staged updates) the target id/URN — matched like `findAmendTarget`. */
|
|
1687
|
+
ref: z4.string(),
|
|
1688
|
+
patch: z4.record(z4.string(), z4.unknown())
|
|
1689
|
+
});
|
|
1670
1690
|
var linkedinDraftOpViewSchema = z4.object({
|
|
1671
1691
|
ref: z4.string(),
|
|
1672
1692
|
kind: linkedinDraftOpKindSchema,
|
|
@@ -1677,14 +1697,45 @@ var linkedinDraftOpViewSchema = z4.object({
|
|
|
1677
1697
|
stagedAt: z4.number(),
|
|
1678
1698
|
result: linkedinDraftOpResultSchema.optional()
|
|
1679
1699
|
});
|
|
1700
|
+
var linkedinDraftShowRequestSchema = z4.object({
|
|
1701
|
+
chatId: z4.string(),
|
|
1702
|
+
ref: z4.string()
|
|
1703
|
+
});
|
|
1704
|
+
var linkedinDraftShowResponseSchema = z4.object({
|
|
1705
|
+
op: linkedinDraftOpViewSchema.extend({
|
|
1706
|
+
payload: z4.unknown().optional(),
|
|
1707
|
+
warnings: z4.array(z4.string()).optional(),
|
|
1708
|
+
annotations: z4.unknown().optional()
|
|
1709
|
+
})
|
|
1710
|
+
});
|
|
1680
1711
|
var linkedinDraftListRequestSchema = z4.object({
|
|
1681
1712
|
chatId: z4.string()
|
|
1682
1713
|
});
|
|
1714
|
+
var linkedinDraftStatusCollectionSchema = z4.object({
|
|
1715
|
+
label: z4.string(),
|
|
1716
|
+
added: z4.number(),
|
|
1717
|
+
removed: z4.number(),
|
|
1718
|
+
existing: z4.number()
|
|
1719
|
+
});
|
|
1720
|
+
var linkedinDraftChangeOperationSchema = z4.enum(["create", "update", "pause", "resume", "archive"]);
|
|
1721
|
+
var linkedinDraftStatusNodeSchema = z4.lazy(
|
|
1722
|
+
() => z4.object({
|
|
1723
|
+
entity: z4.string(),
|
|
1724
|
+
name: z4.string(),
|
|
1725
|
+
operation: linkedinDraftChangeOperationSchema.optional(),
|
|
1726
|
+
existing: z4.boolean(),
|
|
1727
|
+
collections: z4.array(linkedinDraftStatusCollectionSchema),
|
|
1728
|
+
children: z4.array(linkedinDraftStatusNodeSchema),
|
|
1729
|
+
warnings: z4.array(z4.string()).optional()
|
|
1730
|
+
})
|
|
1731
|
+
);
|
|
1683
1732
|
var linkedinDraftListResponseSchema = z4.object({
|
|
1684
1733
|
status: z4.enum(["active", "publishing", "applied", "discarded", "none"]),
|
|
1685
1734
|
mode: linkedinWriteModeSchema,
|
|
1686
1735
|
count: z4.number(),
|
|
1687
|
-
ops: z4.array(linkedinDraftOpViewSchema)
|
|
1736
|
+
ops: z4.array(linkedinDraftOpViewSchema),
|
|
1737
|
+
/** Grouped Campaign group ▸ Campaign ▸ Creative tree for the readable CLI status view. */
|
|
1738
|
+
tree: z4.array(linkedinDraftStatusNodeSchema).optional()
|
|
1688
1739
|
});
|
|
1689
1740
|
var linkedinDraftRemoveRequestSchema = z4.object({
|
|
1690
1741
|
chatId: z4.string(),
|
|
@@ -4065,6 +4116,19 @@ var GOOGLE_ADS_LIMITS = {
|
|
|
4065
4116
|
},
|
|
4066
4117
|
label: {
|
|
4067
4118
|
nameMax: 255
|
|
4119
|
+
},
|
|
4120
|
+
assetGroup: {
|
|
4121
|
+
nameMax: 255,
|
|
4122
|
+
headlinesMin: 3,
|
|
4123
|
+
headlinesMax: 15,
|
|
4124
|
+
headlineTextMax: 30,
|
|
4125
|
+
longHeadlinesMin: 1,
|
|
4126
|
+
longHeadlinesMax: 5,
|
|
4127
|
+
longHeadlineTextMax: 90,
|
|
4128
|
+
descriptionsMin: 2,
|
|
4129
|
+
descriptionsMax: 5,
|
|
4130
|
+
descriptionTextMax: 90,
|
|
4131
|
+
businessNameMax: 25
|
|
4068
4132
|
}
|
|
4069
4133
|
};
|
|
4070
4134
|
var KEYWORD_MATCH_TYPES = ["EXACT", "PHRASE", "BROAD"];
|
|
@@ -4364,15 +4428,6 @@ var responsiveDisplayAdSchema = z8.object({
|
|
|
4364
4428
|
logoImageAssets: z8.array(refSchema).optional(),
|
|
4365
4429
|
finalUrls: z8.array(httpsUrlSchema2).min(1)
|
|
4366
4430
|
});
|
|
4367
|
-
var performanceMaxAssetGroupSchema = z8.object({
|
|
4368
|
-
format: z8.literal("performanceMaxAssetGroup"),
|
|
4369
|
-
name: z8.string().min(1).max(255),
|
|
4370
|
-
headlines: z8.array(z8.object({ text: z8.string().min(1).max(30) })).min(3).max(15),
|
|
4371
|
-
descriptions: z8.array(z8.object({ text: z8.string().min(1).max(90) })).min(2).max(5),
|
|
4372
|
-
finalUrls: z8.array(httpsUrlSchema2).min(1),
|
|
4373
|
-
imageAssets: z8.array(refSchema).optional(),
|
|
4374
|
-
logoAssets: z8.array(refSchema).optional()
|
|
4375
|
-
});
|
|
4376
4431
|
var callAdSchema = z8.object({
|
|
4377
4432
|
format: z8.literal("call"),
|
|
4378
4433
|
countryCode: z8.string().length(2),
|
|
@@ -4391,7 +4446,9 @@ var appAdSchema = z8.object({
|
|
|
4391
4446
|
});
|
|
4392
4447
|
var videoAdSchema = z8.object({
|
|
4393
4448
|
format: z8.literal("video"),
|
|
4394
|
-
|
|
4449
|
+
// A raw YouTube id is not a publishable Google Ads reference — the video must be staged as
|
|
4450
|
+
// its own `google.asset.create` (type: youtubeVideo) first, then referenced here by asset ref.
|
|
4451
|
+
videoAssets: z8.array(refSchema).min(1),
|
|
4395
4452
|
finalUrls: z8.array(httpsUrlSchema2).min(1)
|
|
4396
4453
|
});
|
|
4397
4454
|
var demandGenAdSchema = z8.object({
|
|
@@ -4400,12 +4457,13 @@ var demandGenAdSchema = z8.object({
|
|
|
4400
4457
|
descriptions: z8.array(z8.object({ text: z8.string().min(1).max(90) })).min(1).max(5),
|
|
4401
4458
|
businessName: z8.string().min(1).max(25),
|
|
4402
4459
|
finalUrls: z8.array(httpsUrlSchema2).min(1),
|
|
4403
|
-
imageAssets: z8.array(refSchema).optional()
|
|
4460
|
+
imageAssets: z8.array(refSchema).optional(),
|
|
4461
|
+
squareImageAssets: z8.array(refSchema).optional(),
|
|
4462
|
+
logoImageAssets: z8.array(refSchema).optional()
|
|
4404
4463
|
});
|
|
4405
4464
|
var adContentSchema = z8.discriminatedUnion("format", [
|
|
4406
4465
|
responsiveSearchAdSchema,
|
|
4407
4466
|
responsiveDisplayAdSchema,
|
|
4408
|
-
performanceMaxAssetGroupSchema,
|
|
4409
4467
|
callAdSchema,
|
|
4410
4468
|
appAdSchema,
|
|
4411
4469
|
videoAdSchema,
|
|
@@ -4458,6 +4516,18 @@ var assetCreateSchema = z8.discriminatedUnion("type", [
|
|
|
4458
4516
|
structuredSnippetAssetSchema,
|
|
4459
4517
|
callToActionAssetSchema
|
|
4460
4518
|
]);
|
|
4519
|
+
var assetUpdateSchema = z8.object({
|
|
4520
|
+
name: z8.string().min(1).optional(),
|
|
4521
|
+
linkText: z8.string().min(1).max(GOOGLE_ADS_LIMITS.asset.sitelinkLinkTextMax).optional(),
|
|
4522
|
+
description1: z8.string().max(GOOGLE_ADS_LIMITS.asset.sitelinkDescriptionMax).optional(),
|
|
4523
|
+
description2: z8.string().max(GOOGLE_ADS_LIMITS.asset.sitelinkDescriptionMax).optional(),
|
|
4524
|
+
finalUrls: z8.array(httpsUrlSchema2).min(1).optional(),
|
|
4525
|
+
calloutText: z8.string().min(1).max(GOOGLE_ADS_LIMITS.asset.calloutTextMax).optional(),
|
|
4526
|
+
header: z8.string().min(1).max(GOOGLE_ADS_LIMITS.asset.structuredSnippetHeaderMax).optional(),
|
|
4527
|
+
values: z8.array(z8.string().min(1)).min(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMin).max(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMax).optional(),
|
|
4528
|
+
callToAction: z8.string().min(1).optional(),
|
|
4529
|
+
text: z8.string().min(1).optional()
|
|
4530
|
+
}).refine((p) => Object.values(p).some((v) => v !== void 0), "update needs at least one field");
|
|
4461
4531
|
var assetLinkAttachSchema = z8.object({
|
|
4462
4532
|
level: z8.enum(["campaign", "adGroup", "customer"]),
|
|
4463
4533
|
parent: refSchema.optional(),
|
|
@@ -4472,6 +4542,24 @@ var assetLinkAttachSchema = z8.object({
|
|
|
4472
4542
|
});
|
|
4473
4543
|
}
|
|
4474
4544
|
});
|
|
4545
|
+
var assetGroupCreateSchema = z8.object({
|
|
4546
|
+
campaign: refSchema,
|
|
4547
|
+
name: z8.string().min(1).max(GOOGLE_ADS_LIMITS.assetGroup.nameMax),
|
|
4548
|
+
finalUrls: z8.array(httpsUrlSchema2).min(1),
|
|
4549
|
+
headlines: z8.array(z8.object({ text: z8.string().min(1).max(GOOGLE_ADS_LIMITS.assetGroup.headlineTextMax) })).min(GOOGLE_ADS_LIMITS.assetGroup.headlinesMin).max(GOOGLE_ADS_LIMITS.assetGroup.headlinesMax),
|
|
4550
|
+
longHeadlines: z8.array(z8.object({ text: z8.string().min(1).max(GOOGLE_ADS_LIMITS.assetGroup.longHeadlineTextMax) })).min(GOOGLE_ADS_LIMITS.assetGroup.longHeadlinesMin).max(GOOGLE_ADS_LIMITS.assetGroup.longHeadlinesMax),
|
|
4551
|
+
descriptions: z8.array(z8.object({ text: z8.string().min(1).max(GOOGLE_ADS_LIMITS.assetGroup.descriptionTextMax) })).min(GOOGLE_ADS_LIMITS.assetGroup.descriptionsMin).max(GOOGLE_ADS_LIMITS.assetGroup.descriptionsMax),
|
|
4552
|
+
businessName: z8.string().min(1).max(GOOGLE_ADS_LIMITS.assetGroup.businessNameMax),
|
|
4553
|
+
imageAssets: z8.array(refSchema).optional(),
|
|
4554
|
+
squareImageAssets: z8.array(refSchema).optional(),
|
|
4555
|
+
logoAssets: z8.array(refSchema).optional(),
|
|
4556
|
+
status: z8.enum(["ENABLED", "PAUSED"]).default("PAUSED")
|
|
4557
|
+
});
|
|
4558
|
+
var assetGroupUpdateSchema = z8.object({
|
|
4559
|
+
name: z8.string().min(1).max(GOOGLE_ADS_LIMITS.assetGroup.nameMax).optional(),
|
|
4560
|
+
finalUrls: z8.array(httpsUrlSchema2).min(1).optional(),
|
|
4561
|
+
status: z8.enum(["ENABLED", "PAUSED", "REMOVED"]).optional()
|
|
4562
|
+
}).refine((p) => Object.values(p).some((v) => v !== void 0), "update needs at least one field");
|
|
4475
4563
|
var audienceCreateSchema2 = z8.object({
|
|
4476
4564
|
name: z8.string().min(1).max(GOOGLE_ADS_LIMITS.audience.nameMax),
|
|
4477
4565
|
type: z8.enum(USER_LIST_TYPES).default("BASIC"),
|
|
@@ -4502,6 +4590,9 @@ var conversionActionUpdateSchema = z8.object({
|
|
|
4502
4590
|
category: z8.enum(CONVERSION_ACTION_CATEGORIES).optional(),
|
|
4503
4591
|
countingType: z8.enum(CONVERSION_COUNTING_TYPES).optional(),
|
|
4504
4592
|
defaultValueMicros: microsSchema.optional(),
|
|
4593
|
+
defaultCurrencyCode: z8.string().length(3).optional(),
|
|
4594
|
+
clickThroughLookbackWindowDays: z8.number().int().positive().optional(),
|
|
4595
|
+
viewThroughLookbackWindowDays: z8.number().int().positive().optional(),
|
|
4505
4596
|
status: z8.enum(["ENABLED", "REMOVED", "HIDDEN"]).optional()
|
|
4506
4597
|
}).refine((p) => Object.values(p).some((v) => v !== void 0), "update needs at least one field");
|
|
4507
4598
|
var biddingStrategyCreateSchema = z8.object({
|
|
@@ -4599,8 +4690,11 @@ var GOOGLE_DRAFT_OP_KINDS = [
|
|
|
4599
4690
|
"google.ad.resume",
|
|
4600
4691
|
"google.ad.remove",
|
|
4601
4692
|
"google.asset.create",
|
|
4693
|
+
"google.asset.update",
|
|
4602
4694
|
"google.assetLink.attach",
|
|
4603
4695
|
"google.assetLink.detach",
|
|
4696
|
+
"google.assetGroup.create",
|
|
4697
|
+
"google.assetGroup.update",
|
|
4604
4698
|
"google.audience.create",
|
|
4605
4699
|
"google.audienceCriterion.attach",
|
|
4606
4700
|
"google.audienceCriterion.detach",
|
|
@@ -4652,8 +4746,11 @@ var googleDraftOpInputSchema = z8.discriminatedUnion("kind", [
|
|
|
4652
4746
|
targetOp("google.ad.resume"),
|
|
4653
4747
|
targetOp("google.ad.remove"),
|
|
4654
4748
|
createOp2("google.asset.create", assetCreateSchema),
|
|
4749
|
+
updateOp2("google.asset.update", assetUpdateSchema),
|
|
4655
4750
|
createOp2("google.assetLink.attach", assetLinkAttachSchema),
|
|
4656
4751
|
targetOp("google.assetLink.detach"),
|
|
4752
|
+
createOp2("google.assetGroup.create", assetGroupCreateSchema),
|
|
4753
|
+
updateOp2("google.assetGroup.update", assetGroupUpdateSchema),
|
|
4657
4754
|
createOp2("google.audience.create", audienceCreateSchema2),
|
|
4658
4755
|
createOp2("google.audienceCriterion.attach", audienceCriterionAttachSchema),
|
|
4659
4756
|
targetOp("google.audienceCriterion.detach"),
|
|
@@ -4692,6 +4789,15 @@ var googleDraftStageResponseSchema = z9.object({
|
|
|
4692
4789
|
/** True when the op amended an already-staged op in place instead of appending a new one. */
|
|
4693
4790
|
amended: z9.boolean().optional()
|
|
4694
4791
|
});
|
|
4792
|
+
var googleDraftAmendRequestSchema = z9.object({
|
|
4793
|
+
chatId: z9.string(),
|
|
4794
|
+
ref: z9.string(),
|
|
4795
|
+
patch: z9.record(z9.string(), z9.unknown())
|
|
4796
|
+
});
|
|
4797
|
+
var googleDraftShowRequestSchema = z9.object({
|
|
4798
|
+
chatId: z9.string(),
|
|
4799
|
+
ref: z9.string()
|
|
4800
|
+
});
|
|
4695
4801
|
var GOOGLE_DRAFT_BATCH_MAX = 500;
|
|
4696
4802
|
var googleDraftStageBatchRequestSchema = z9.object({
|
|
4697
4803
|
chatId: z9.string(),
|
|
@@ -4721,6 +4827,13 @@ var googleDraftOpViewSchema = z9.object({
|
|
|
4721
4827
|
stagedAt: z9.number(),
|
|
4722
4828
|
result: googleDraftOpResultSchema.optional()
|
|
4723
4829
|
});
|
|
4830
|
+
var googleDraftShowResponseSchema = z9.object({
|
|
4831
|
+
op: googleDraftOpViewSchema.extend({
|
|
4832
|
+
payload: z9.unknown().optional(),
|
|
4833
|
+
warnings: z9.array(z9.string()).optional(),
|
|
4834
|
+
annotations: z9.unknown().optional()
|
|
4835
|
+
})
|
|
4836
|
+
});
|
|
4724
4837
|
var googleDraftListRequestSchema = z9.object({
|
|
4725
4838
|
chatId: z9.string()
|
|
4726
4839
|
});
|
|
@@ -4995,6 +5108,28 @@ function mergePayload(file, flags) {
|
|
|
4995
5108
|
}
|
|
4996
5109
|
return merged;
|
|
4997
5110
|
}
|
|
5111
|
+
function loadPatchArg(args) {
|
|
5112
|
+
const file = typeof args.file === "string" && args.file.length > 0 ? args.file : void 0;
|
|
5113
|
+
const inline = typeof args.patch === "string" && args.patch.length > 0 ? args.patch : void 0;
|
|
5114
|
+
if (Boolean(file) === Boolean(inline)) {
|
|
5115
|
+
failWriteValidation("pass exactly one of --file <patch.json> or --patch '<json>'");
|
|
5116
|
+
}
|
|
5117
|
+
if (file) {
|
|
5118
|
+
return loadJsonFileArg(file);
|
|
5119
|
+
}
|
|
5120
|
+
try {
|
|
5121
|
+
const parsed = JSON.parse(inline);
|
|
5122
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
5123
|
+
failWriteValidation("--patch must be a JSON object");
|
|
5124
|
+
}
|
|
5125
|
+
return parsed;
|
|
5126
|
+
} catch (err) {
|
|
5127
|
+
if (err instanceof SyntaxError) {
|
|
5128
|
+
failWriteValidation(`--patch is not valid JSON: ${err.message}`);
|
|
5129
|
+
}
|
|
5130
|
+
throw err;
|
|
5131
|
+
}
|
|
5132
|
+
}
|
|
4998
5133
|
function handleGoogleError(err) {
|
|
4999
5134
|
if (err instanceof ApiError) {
|
|
5000
5135
|
writeJsonEnvelope({ ok: false, error: { code: err.code ?? "API_ERROR", message: err.message } });
|
|
@@ -5096,7 +5231,7 @@ async function draftListAction(json) {
|
|
|
5096
5231
|
|
|
5097
5232
|
// src/commands/ads/google/draft.ts
|
|
5098
5233
|
var draftCommand2 = defineCommand22({
|
|
5099
|
-
meta: { name: "draft", description: "List, remove, or
|
|
5234
|
+
meta: { name: "draft", description: "List, remove, clear, amend, or show staged Google Ads write ops for this chat" },
|
|
5100
5235
|
subCommands: {
|
|
5101
5236
|
list: defineCommand22({
|
|
5102
5237
|
meta: {
|
|
@@ -5120,6 +5255,42 @@ var draftCommand2 = defineCommand22({
|
|
|
5120
5255
|
run: async () => {
|
|
5121
5256
|
await draftAction("/api/ads/google/draft/clear", {});
|
|
5122
5257
|
}
|
|
5258
|
+
}),
|
|
5259
|
+
amend: defineCommand22({
|
|
5260
|
+
meta: {
|
|
5261
|
+
name: "amend",
|
|
5262
|
+
description: "Update a staged op in place \u2014 merges a JSON patch into its payload (RFC-7386: objects deep-merge, null deletes a key, arrays/scalars replace) and re-validates. Use this instead of remove + re-create."
|
|
5263
|
+
},
|
|
5264
|
+
args: {
|
|
5265
|
+
ref: {
|
|
5266
|
+
type: "positional",
|
|
5267
|
+
description: "Staged op ref (g_temp_*) or target id/resource name",
|
|
5268
|
+
required: false
|
|
5269
|
+
},
|
|
5270
|
+
file: { type: "string", description: "JSON file with the patch object" },
|
|
5271
|
+
patch: { type: "string", description: "Inline JSON patch object" }
|
|
5272
|
+
},
|
|
5273
|
+
run: async ({ args }) => {
|
|
5274
|
+
const ref = requireTarget(args, "op");
|
|
5275
|
+
const patch = loadPatchArg(args);
|
|
5276
|
+
await draftAction("/api/ads/google/draft/amend", { ref, patch });
|
|
5277
|
+
}
|
|
5278
|
+
}),
|
|
5279
|
+
show: defineCommand22({
|
|
5280
|
+
meta: {
|
|
5281
|
+
name: "show",
|
|
5282
|
+
description: "Print the full staged payload for one op \u2014 the receipt to verify a change looks right before publish (never truncated)."
|
|
5283
|
+
},
|
|
5284
|
+
args: {
|
|
5285
|
+
ref: {
|
|
5286
|
+
type: "positional",
|
|
5287
|
+
description: "Staged op ref (g_temp_*) or target id/resource name",
|
|
5288
|
+
required: false
|
|
5289
|
+
}
|
|
5290
|
+
},
|
|
5291
|
+
run: async ({ args }) => {
|
|
5292
|
+
await draftAction("/api/ads/google/draft/show", { ref: requireTarget(args, "op") });
|
|
5293
|
+
}
|
|
5123
5294
|
})
|
|
5124
5295
|
}
|
|
5125
5296
|
});
|
|
@@ -6843,6 +7014,55 @@ function rdaContentFromFlags(args, base) {
|
|
|
6843
7014
|
}
|
|
6844
7015
|
return content;
|
|
6845
7016
|
}
|
|
7017
|
+
function videoContentFromFlags(args, base) {
|
|
7018
|
+
const content = {
|
|
7019
|
+
format: "video",
|
|
7020
|
+
...base !== null && typeof base === "object" && !Array.isArray(base) ? base : {}
|
|
7021
|
+
};
|
|
7022
|
+
const videoAssets = listFlag(args["video-assets"]);
|
|
7023
|
+
if (videoAssets) {
|
|
7024
|
+
content.videoAssets = videoAssets;
|
|
7025
|
+
}
|
|
7026
|
+
const finalUrls = listFlag(args["final-url"]);
|
|
7027
|
+
if (finalUrls) {
|
|
7028
|
+
content.finalUrls = finalUrls;
|
|
7029
|
+
}
|
|
7030
|
+
return content;
|
|
7031
|
+
}
|
|
7032
|
+
function demandGenContentFromFlags(args, base) {
|
|
7033
|
+
const content = {
|
|
7034
|
+
format: "demandGen",
|
|
7035
|
+
...base !== null && typeof base === "object" && !Array.isArray(base) ? base : {}
|
|
7036
|
+
};
|
|
7037
|
+
const headlines = listFlag(args.headlines);
|
|
7038
|
+
if (headlines) {
|
|
7039
|
+
content.headlines = headlines.map((text) => ({ text }));
|
|
7040
|
+
}
|
|
7041
|
+
const descriptions = listFlag(args.descriptions);
|
|
7042
|
+
if (descriptions) {
|
|
7043
|
+
content.descriptions = descriptions.map((text) => ({ text }));
|
|
7044
|
+
}
|
|
7045
|
+
if (typeof args["business-name"] === "string") {
|
|
7046
|
+
content.businessName = args["business-name"];
|
|
7047
|
+
}
|
|
7048
|
+
const finalUrls = listFlag(args["final-url"]);
|
|
7049
|
+
if (finalUrls) {
|
|
7050
|
+
content.finalUrls = finalUrls;
|
|
7051
|
+
}
|
|
7052
|
+
const images = listFlag(args["image-assets"]);
|
|
7053
|
+
if (images) {
|
|
7054
|
+
content.imageAssets = images;
|
|
7055
|
+
}
|
|
7056
|
+
const squareImages = listFlag(args["square-image-assets"]);
|
|
7057
|
+
if (squareImages) {
|
|
7058
|
+
content.squareImageAssets = squareImages;
|
|
7059
|
+
}
|
|
7060
|
+
const logoImages = listFlag(args["logo-image-assets"]);
|
|
7061
|
+
if (logoImages) {
|
|
7062
|
+
content.logoImageAssets = logoImages;
|
|
7063
|
+
}
|
|
7064
|
+
return content;
|
|
7065
|
+
}
|
|
6846
7066
|
function adContentFromFlags(args, format, fileContent) {
|
|
6847
7067
|
if (format === "responsiveSearch") {
|
|
6848
7068
|
return fileContent ?? rsaContentFromFlags(args);
|
|
@@ -6850,6 +7070,12 @@ function adContentFromFlags(args, format, fileContent) {
|
|
|
6850
7070
|
if (format === "responsiveDisplay") {
|
|
6851
7071
|
return rdaContentFromFlags(args, fileContent);
|
|
6852
7072
|
}
|
|
7073
|
+
if (format === "video") {
|
|
7074
|
+
return videoContentFromFlags(args, fileContent);
|
|
7075
|
+
}
|
|
7076
|
+
if (format === "demandGen") {
|
|
7077
|
+
return demandGenContentFromFlags(args, fileContent);
|
|
7078
|
+
}
|
|
6853
7079
|
return fileContent ?? failWriteValidation(`--format ${format} needs --file with the ad content`);
|
|
6854
7080
|
}
|
|
6855
7081
|
var adsCommand = defineCommand30({
|
|
@@ -6858,7 +7084,7 @@ var adsCommand = defineCommand30({
|
|
|
6858
7084
|
create: defineCommand30({
|
|
6859
7085
|
meta: {
|
|
6860
7086
|
name: "create",
|
|
6861
|
-
description: "Stage an ad (responsive search via flags; other formats via --file). RSA needs 3\u201315 headlines and 2\u20134 descriptions \u2014 give Google 8\u201312 varied headlines to test, and stage 2\u20134 ads per ad group."
|
|
7087
|
+
description: "Stage an ad (responsive search via flags; other formats via --file). RSA needs 3\u201315 headlines and 2\u20134 descriptions \u2014 give Google 8\u201312 varied headlines to test, and stage 2\u20134 ads per ad group. Performance Max has no ad format of its own \u2014 stage its copy with `asset-groups create`."
|
|
6862
7088
|
},
|
|
6863
7089
|
args: {
|
|
6864
7090
|
...customerIdArg,
|
|
@@ -6866,14 +7092,14 @@ var adsCommand = defineCommand30({
|
|
|
6866
7092
|
"ad-group-ref": { type: "string", description: "Ad group ref" },
|
|
6867
7093
|
format: {
|
|
6868
7094
|
type: "string",
|
|
6869
|
-
description: "responsiveSearch (default) | responsiveDisplay |
|
|
7095
|
+
description: "responsiveSearch (default) | responsiveDisplay | call | app | video | demandGen"
|
|
6870
7096
|
},
|
|
6871
|
-
headlines: { type: "string", description: "Comma-separated headlines (RSA/RDA)" },
|
|
6872
|
-
descriptions: { type: "string", description: "Comma-separated descriptions (RSA/RDA)" },
|
|
7097
|
+
headlines: { type: "string", description: "Comma-separated headlines (RSA/RDA/demandGen)" },
|
|
7098
|
+
descriptions: { type: "string", description: "Comma-separated descriptions (RSA/RDA/demandGen)" },
|
|
6873
7099
|
path1: { type: "string" },
|
|
6874
7100
|
path2: { type: "string" },
|
|
6875
7101
|
"long-headline": { type: "string", description: "Long headline (responsiveDisplay)" },
|
|
6876
|
-
"business-name": { type: "string", description: "Business name (responsiveDisplay)" },
|
|
7102
|
+
"business-name": { type: "string", description: "Business name (responsiveDisplay/demandGen)" },
|
|
6877
7103
|
"marketing-images": {
|
|
6878
7104
|
type: "string",
|
|
6879
7105
|
description: "Comma-separated marketing image asset refs (responsiveDisplay)"
|
|
@@ -6883,6 +7109,13 @@ var adsCommand = defineCommand30({
|
|
|
6883
7109
|
description: "Comma-separated square marketing image asset refs (responsiveDisplay)"
|
|
6884
7110
|
},
|
|
6885
7111
|
"logo-images": { type: "string", description: "Comma-separated logo image asset refs (responsiveDisplay)" },
|
|
7112
|
+
"video-assets": {
|
|
7113
|
+
type: "string",
|
|
7114
|
+
description: `Comma-separated video asset refs (video format) \u2014 stage the YouTube video as an asset first (assets create --file '{"type":"youtubeVideo",\u2026}')`
|
|
7115
|
+
},
|
|
7116
|
+
"image-assets": { type: "string", description: "Comma-separated image asset refs (demandGen)" },
|
|
7117
|
+
"square-image-assets": { type: "string", description: "Comma-separated square image asset refs (demandGen)" },
|
|
7118
|
+
"logo-image-assets": { type: "string", description: "Comma-separated logo image asset refs (demandGen)" },
|
|
6886
7119
|
"final-url": { type: "string", description: "Comma-separated final URLs" },
|
|
6887
7120
|
status: { type: "string" }
|
|
6888
7121
|
},
|
|
@@ -6917,6 +7150,78 @@ var adsCommand = defineCommand30({
|
|
|
6917
7150
|
remove: statusCommand2("google.ad.remove", "ad")
|
|
6918
7151
|
}
|
|
6919
7152
|
});
|
|
7153
|
+
var assetGroupsCommand = defineCommand30({
|
|
7154
|
+
meta: {
|
|
7155
|
+
name: "asset-groups",
|
|
7156
|
+
description: "Stage Performance Max asset group create/update \u2014 the PMax campaign's copy and images."
|
|
7157
|
+
},
|
|
7158
|
+
subCommands: {
|
|
7159
|
+
create: defineCommand30({
|
|
7160
|
+
meta: {
|
|
7161
|
+
name: "create",
|
|
7162
|
+
description: "Stage a Performance Max asset group: 3\u201315 headlines (\u226430ch), 1\u20135 long headlines (\u226490ch), 2\u20135 descriptions (\u226490ch), a business name, and image refs."
|
|
7163
|
+
},
|
|
7164
|
+
args: {
|
|
7165
|
+
...customerIdArg,
|
|
7166
|
+
...fileArg,
|
|
7167
|
+
"campaign-ref": {
|
|
7168
|
+
type: "string",
|
|
7169
|
+
description: "Performance Max campaign ref (g_temp_*, id, or resource name)"
|
|
7170
|
+
},
|
|
7171
|
+
name: { type: "string", description: "Asset group name" },
|
|
7172
|
+
"final-urls": { type: "string", description: "Comma-separated final URLs" },
|
|
7173
|
+
headlines: { type: "string", description: "Comma-separated headlines (3\u201315, \u226430 chars)" },
|
|
7174
|
+
"long-headlines": { type: "string", description: "Comma-separated long headlines (1\u20135, \u226490 chars)" },
|
|
7175
|
+
descriptions: { type: "string", description: "Comma-separated descriptions (2\u20135, \u226490 chars)" },
|
|
7176
|
+
"business-name": { type: "string" },
|
|
7177
|
+
"image-assets": { type: "string", description: "Comma-separated marketing image asset refs" },
|
|
7178
|
+
"square-image-assets": { type: "string", description: "Comma-separated square marketing image asset refs" },
|
|
7179
|
+
"logo-assets": { type: "string", description: "Comma-separated logo image asset refs" },
|
|
7180
|
+
status: { type: "string", description: "ENABLED | PAUSED (default PAUSED)" }
|
|
7181
|
+
},
|
|
7182
|
+
run: async ({ args }) => {
|
|
7183
|
+
const customerId = requireCustomerId(args);
|
|
7184
|
+
const headlines = listFlag(args.headlines);
|
|
7185
|
+
const longHeadlines = listFlag(args["long-headlines"]);
|
|
7186
|
+
const descriptions = listFlag(args.descriptions);
|
|
7187
|
+
const payload = mergePayload(loadJsonFileArg(args.file), {
|
|
7188
|
+
campaign: args["campaign-ref"],
|
|
7189
|
+
name: args.name,
|
|
7190
|
+
finalUrls: listFlag(args["final-urls"]),
|
|
7191
|
+
headlines: headlines?.map((text) => ({ text })),
|
|
7192
|
+
longHeadlines: longHeadlines?.map((text) => ({ text })),
|
|
7193
|
+
descriptions: descriptions?.map((text) => ({ text })),
|
|
7194
|
+
businessName: args["business-name"],
|
|
7195
|
+
imageAssets: listFlag(args["image-assets"]),
|
|
7196
|
+
squareImageAssets: listFlag(args["square-image-assets"]),
|
|
7197
|
+
logoAssets: listFlag(args["logo-assets"]),
|
|
7198
|
+
status: args.status
|
|
7199
|
+
});
|
|
7200
|
+
await stageCreate("google.assetGroup.create", customerId, payload);
|
|
7201
|
+
}
|
|
7202
|
+
}),
|
|
7203
|
+
update: defineCommand30({
|
|
7204
|
+
meta: { name: "update", description: "Stage a Performance Max asset group update" },
|
|
7205
|
+
args: {
|
|
7206
|
+
...customerIdArg,
|
|
7207
|
+
...fileArg,
|
|
7208
|
+
name: { type: "string" },
|
|
7209
|
+
"final-urls": { type: "string", description: "Comma-separated final URLs" },
|
|
7210
|
+
status: { type: "string", description: "ENABLED | PAUSED | REMOVED" }
|
|
7211
|
+
},
|
|
7212
|
+
run: async ({ args }) => {
|
|
7213
|
+
const customerId = requireCustomerId(args);
|
|
7214
|
+
const target = requireTarget(args, "asset group");
|
|
7215
|
+
const payload = mergePayload(loadJsonFileArg(args.file), {
|
|
7216
|
+
name: args.name,
|
|
7217
|
+
finalUrls: listFlag(args["final-urls"]),
|
|
7218
|
+
status: args.status
|
|
7219
|
+
});
|
|
7220
|
+
await stageUpdate("google.assetGroup.update", customerId, target, payload);
|
|
7221
|
+
}
|
|
7222
|
+
})
|
|
7223
|
+
}
|
|
7224
|
+
});
|
|
6920
7225
|
function fileCreateCommand(name, kind, description) {
|
|
6921
7226
|
return defineCommand30({
|
|
6922
7227
|
meta: { name, description },
|
|
@@ -6931,10 +7236,47 @@ function fileCreateCommand(name, kind, description) {
|
|
|
6931
7236
|
}
|
|
6932
7237
|
});
|
|
6933
7238
|
}
|
|
7239
|
+
var assetsUpdateCommand = defineCommand30({
|
|
7240
|
+
meta: {
|
|
7241
|
+
name: "update",
|
|
7242
|
+
description: "Update a staged or live asset in place (flat partial \u2014 set only the fields that changed). Works across asset types: sitelink (--link-text/--description1/--description2/--final-urls), callout (--callout-text), structured snippet (--header/--values), call-to-action (--cta), text (--text), or --name."
|
|
7243
|
+
},
|
|
7244
|
+
args: {
|
|
7245
|
+
...customerIdArg,
|
|
7246
|
+
...fileArg,
|
|
7247
|
+
name: { type: "string", description: "Asset display name" },
|
|
7248
|
+
"link-text": { type: "string", description: "Sitelink link text" },
|
|
7249
|
+
description1: { type: "string", description: "Sitelink description line 1" },
|
|
7250
|
+
description2: { type: "string", description: "Sitelink description line 2" },
|
|
7251
|
+
"final-urls": { type: "string", description: "Comma-separated sitelink final URLs" },
|
|
7252
|
+
"callout-text": { type: "string", description: "Callout text" },
|
|
7253
|
+
header: { type: "string", description: "Structured snippet header" },
|
|
7254
|
+
values: { type: "string", description: "Comma-separated structured snippet values" },
|
|
7255
|
+
cta: { type: "string", description: "Call-to-action text" },
|
|
7256
|
+
text: { type: "string", description: "Text asset content" }
|
|
7257
|
+
},
|
|
7258
|
+
run: async ({ args }) => {
|
|
7259
|
+
const customerId = requireCustomerId(args);
|
|
7260
|
+
const target = requireTarget(args, "asset");
|
|
7261
|
+
const payload = mergePayload(loadJsonFileArg(args.file), {
|
|
7262
|
+
name: args.name,
|
|
7263
|
+
linkText: args["link-text"],
|
|
7264
|
+
description1: args.description1,
|
|
7265
|
+
description2: args.description2,
|
|
7266
|
+
finalUrls: listFlag(args["final-urls"]),
|
|
7267
|
+
calloutText: args["callout-text"],
|
|
7268
|
+
header: args.header,
|
|
7269
|
+
values: listFlag(args.values),
|
|
7270
|
+
callToAction: args.cta,
|
|
7271
|
+
text: args.text
|
|
7272
|
+
});
|
|
7273
|
+
await stageUpdate("google.asset.update", customerId, target, payload);
|
|
7274
|
+
}
|
|
7275
|
+
});
|
|
6934
7276
|
var assetsCommand = defineCommand30({
|
|
6935
7277
|
meta: {
|
|
6936
7278
|
name: "assets",
|
|
6937
|
-
description: "Stage asset create + asset-link attach/detach (via --file). For a high-performance Search campaign add \u22654 sitelinks, \u22653 callouts, and \u22651 structured snippet per campaign."
|
|
7279
|
+
description: "Stage asset create/update + asset-link attach/detach (via --file). For a high-performance Search campaign add \u22654 sitelinks, \u22653 callouts, and \u22651 structured snippet per campaign."
|
|
6938
7280
|
},
|
|
6939
7281
|
subCommands: {
|
|
6940
7282
|
create: fileCreateCommand(
|
|
@@ -6942,6 +7284,7 @@ var assetsCommand = defineCommand30({
|
|
|
6942
7284
|
"google.asset.create",
|
|
6943
7285
|
"Stage an asset (text/image/sitelink/callout/structuredSnippet/\u2026)"
|
|
6944
7286
|
),
|
|
7287
|
+
update: assetsUpdateCommand,
|
|
6945
7288
|
attach: fileCreateCommand("attach", "google.assetLink.attach", "Attach an asset to a campaign/adGroup/customer"),
|
|
6946
7289
|
detach: statusCommand2("google.assetLink.detach", "asset link")
|
|
6947
7290
|
}
|
|
@@ -7037,6 +7380,7 @@ var googleWriteCommands = {
|
|
|
7037
7380
|
"keyword-lists": keywordListsCommand,
|
|
7038
7381
|
ads: adsCommand,
|
|
7039
7382
|
assets: assetsCommand,
|
|
7383
|
+
"asset-groups": assetGroupsCommand,
|
|
7040
7384
|
audiences: audiencesCommand,
|
|
7041
7385
|
conversions: conversionsCommand,
|
|
7042
7386
|
"bidding-strategies": biddingStrategiesCommand,
|
|
@@ -7622,6 +7966,11 @@ registerSchema({
|
|
|
7622
7966
|
...writeAccountArgs,
|
|
7623
7967
|
"intended-status": { type: "string", description: "ACTIVE|PAUSED|ARCHIVED|DRAFT", required: false },
|
|
7624
7968
|
name: { type: "string", description: "New creative name", required: false },
|
|
7969
|
+
campaign: {
|
|
7970
|
+
type: "string",
|
|
7971
|
+
description: "Re-parent to a different ad set (id/URN or li_temp_*) \u2014 staged creatives only",
|
|
7972
|
+
required: false
|
|
7973
|
+
},
|
|
7625
7974
|
headline: { type: "string", description: "New headline", required: false },
|
|
7626
7975
|
intro: { type: "string", description: "Commentary / intro text", required: false },
|
|
7627
7976
|
description: { type: "string", description: "Description (text/spotlight)", required: false },
|
|
@@ -8640,6 +8989,28 @@ function mergePayload2(file, flags) {
|
|
|
8640
8989
|
}
|
|
8641
8990
|
return merged;
|
|
8642
8991
|
}
|
|
8992
|
+
function loadPatchArg2(args) {
|
|
8993
|
+
const file = typeof args.file === "string" && args.file.length > 0 ? args.file : void 0;
|
|
8994
|
+
const inline = typeof args.patch === "string" && args.patch.length > 0 ? args.patch : void 0;
|
|
8995
|
+
if (Boolean(file) === Boolean(inline)) {
|
|
8996
|
+
failWriteValidation2("pass exactly one of --file <patch.json> or --patch '<json>'");
|
|
8997
|
+
}
|
|
8998
|
+
if (file) {
|
|
8999
|
+
return loadJsonFileArg2(file);
|
|
9000
|
+
}
|
|
9001
|
+
try {
|
|
9002
|
+
const parsed = JSON.parse(inline);
|
|
9003
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
9004
|
+
failWriteValidation2("--patch must be a JSON object");
|
|
9005
|
+
}
|
|
9006
|
+
return parsed;
|
|
9007
|
+
} catch (err) {
|
|
9008
|
+
if (err instanceof SyntaxError) {
|
|
9009
|
+
failWriteValidation2(`--patch is not valid JSON: ${err.message}`);
|
|
9010
|
+
}
|
|
9011
|
+
throw err;
|
|
9012
|
+
}
|
|
9013
|
+
}
|
|
8643
9014
|
function parseMoneyFlag(amount, currency, flag) {
|
|
8644
9015
|
if (amount === void 0 || amount === null || amount === "") {
|
|
8645
9016
|
return void 0;
|
|
@@ -8938,11 +9309,21 @@ Example: baker ads linkedin campaigns create --name "ABM Tier-1" --group li_temp
|
|
|
8938
9309
|
await stageOp({ kind: "campaign.create", accountId, payload });
|
|
8939
9310
|
}
|
|
8940
9311
|
});
|
|
9312
|
+
function campaignUpdatePayloadFromFlags(args, target, file) {
|
|
9313
|
+
const flags = campaignPayloadFromFlags(args);
|
|
9314
|
+
if (!target.startsWith(TEMP_REF_PREFIX)) {
|
|
9315
|
+
flags.campaignGroup = void 0;
|
|
9316
|
+
flags.type = void 0;
|
|
9317
|
+
flags.locale = void 0;
|
|
9318
|
+
flags.associatedEntity = void 0;
|
|
9319
|
+
}
|
|
9320
|
+
return mergePayload2(file, flags);
|
|
9321
|
+
}
|
|
8941
9322
|
var campaignsUpdateCommand = defineCommand38({
|
|
8942
9323
|
meta: {
|
|
8943
9324
|
name: "update",
|
|
8944
9325
|
description: `Stage changes to an existing campaign (budget, bid, targeting, flags, schedule, status). ${STAGED_NOTE}
|
|
8945
|
-
Pass a li_temp_* ref to amend a campaign staged in this chat \u2014 fields merge into the staged create.
|
|
9326
|
+
Pass a li_temp_* ref to amend a campaign staged in this chat \u2014 fields merge into the staged create, and --group/--type/--locale/--associated-entity carry through too (they're create-only on a LIVE campaign, but fair game while it's still staged).
|
|
8946
9327
|
Example: baker ads linkedin campaigns update 123456 --daily-budget 100 --currency EUR --audience-expansion off`
|
|
8947
9328
|
},
|
|
8948
9329
|
args: {
|
|
@@ -8951,18 +9332,9 @@ Example: baker ads linkedin campaigns update 123456 --daily-budget 100 --currenc
|
|
|
8951
9332
|
},
|
|
8952
9333
|
run: async ({ args }) => {
|
|
8953
9334
|
const accountId = bareAccountId(args);
|
|
8954
|
-
const
|
|
8955
|
-
|
|
8956
|
-
|
|
8957
|
-
flags.locale = void 0;
|
|
8958
|
-
flags.associatedEntity = void 0;
|
|
8959
|
-
const payload = mergePayload2(loadJsonFileArg2(args.file), flags);
|
|
8960
|
-
await stageOp({
|
|
8961
|
-
kind: "campaign.update",
|
|
8962
|
-
accountId,
|
|
8963
|
-
target: requireTarget2(args, "campaign"),
|
|
8964
|
-
payload
|
|
8965
|
-
});
|
|
9335
|
+
const target = requireTarget2(args, "campaign");
|
|
9336
|
+
const payload = campaignUpdatePayloadFromFlags(args, target, loadJsonFileArg2(args.file));
|
|
9337
|
+
await stageOp({ kind: "campaign.update", accountId, target, payload });
|
|
8966
9338
|
}
|
|
8967
9339
|
});
|
|
8968
9340
|
function statusSugarCommand(entity, kind, name) {
|
|
@@ -9213,15 +9585,21 @@ var creativesUpdateCommand = defineCommand38({
|
|
|
9213
9585
|
name: "update",
|
|
9214
9586
|
description: `Stage changes to an existing creative, or amend a creative staged in this chat by passing its li_temp_* ref. ${STAGED_NOTE}
|
|
9215
9587
|
Direct-content ads (text/spotlight/follower/jobs) update copy/media/URL IN PLACE \u2014 changed fields merge into the ad's current content. Post-based ads (image/video/carousel/\u2026) can't edit content; use \`creatives duplicate <id> --headline "\u2026" --replace\` for those. Staged creatives (li_temp_*) accept all content flags and re-validate.
|
|
9588
|
+
--campaign re-parents the ad to a different ad set \u2014 LinkedIn has no in-place move API, so this only works while the creative is still staged (li_temp_*); passing it against a live creative id/URN is rejected at stage time.
|
|
9216
9589
|
Examples:
|
|
9217
9590
|
baker ads linkedin creatives update 1458423674 --headline "Sharper headline" --description "New copy."
|
|
9218
|
-
baker ads linkedin creatives update urn:li:sponsoredCreative:789 --intended-status PAUSED
|
|
9591
|
+
baker ads linkedin creatives update urn:li:sponsoredCreative:789 --intended-status PAUSED
|
|
9592
|
+
baker ads linkedin creatives update li_temp_x3 --campaign li_temp_x2`
|
|
9219
9593
|
},
|
|
9220
9594
|
args: {
|
|
9221
9595
|
id: { type: "positional", description: "Creative id/URN, or li_temp_* ref staged in this chat", required: true },
|
|
9222
9596
|
...accountArgs,
|
|
9223
9597
|
"intended-status": { type: "string", description: "ACTIVE|PAUSED|ARCHIVED|DRAFT" },
|
|
9224
9598
|
name: { type: "string", description: "New creative name" },
|
|
9599
|
+
campaign: {
|
|
9600
|
+
type: "string",
|
|
9601
|
+
description: "Re-parent to a different ad set (id/URN or li_temp_*) \u2014 staged creatives only"
|
|
9602
|
+
},
|
|
9225
9603
|
intro: { type: "string", description: "Commentary / intro text" },
|
|
9226
9604
|
headline: { type: "string", description: "New headline" },
|
|
9227
9605
|
description: { type: "string", description: "Description (text/spotlight ads)" },
|
|
@@ -9249,6 +9627,7 @@ Examples:
|
|
|
9249
9627
|
const payload = mergePayload2(file, {
|
|
9250
9628
|
intendedStatus,
|
|
9251
9629
|
name: args.name,
|
|
9630
|
+
campaign: args.campaign,
|
|
9252
9631
|
content: Object.keys(content).length > 0 ? content : void 0
|
|
9253
9632
|
});
|
|
9254
9633
|
await stageOp({ kind: "creative.update", accountId, target, payload });
|
|
@@ -9893,13 +10272,95 @@ function resolveRange(args) {
|
|
|
9893
10272
|
|
|
9894
10273
|
// src/commands/ads/linkedin/draft.ts
|
|
9895
10274
|
import { defineCommand as defineCommand45 } from "citty";
|
|
9896
|
-
|
|
10275
|
+
|
|
10276
|
+
// src/commands/ads/linkedin/draft-status.ts
|
|
10277
|
+
var OPERATION_LABELS2 = {
|
|
10278
|
+
create: "Creating",
|
|
10279
|
+
update: "Updating",
|
|
10280
|
+
pause: "Pausing",
|
|
10281
|
+
resume: "Resuming",
|
|
10282
|
+
archive: "Archiving"
|
|
10283
|
+
};
|
|
10284
|
+
function badge2(node) {
|
|
10285
|
+
const label = node.operation ? OPERATION_LABELS2[node.operation] : void 0;
|
|
10286
|
+
if (label) {
|
|
10287
|
+
return label;
|
|
10288
|
+
}
|
|
10289
|
+
return node.existing ? "on LinkedIn" : "staged";
|
|
10290
|
+
}
|
|
10291
|
+
function collectionLine2(collection) {
|
|
10292
|
+
const parts = [];
|
|
10293
|
+
if (collection.added > 0) {
|
|
10294
|
+
parts.push(`+${collection.added}`);
|
|
10295
|
+
}
|
|
10296
|
+
if (collection.existing > 0) {
|
|
10297
|
+
parts.push(`${collection.existing} existing`);
|
|
10298
|
+
}
|
|
10299
|
+
if (collection.removed > 0) {
|
|
10300
|
+
parts.push(`-${collection.removed}`);
|
|
10301
|
+
}
|
|
10302
|
+
return parts.length > 0 ? `${collection.label}: ${parts.join(", ")}` : void 0;
|
|
10303
|
+
}
|
|
10304
|
+
function renderNode2(node, depth, lines) {
|
|
10305
|
+
const indent = " ".repeat(depth);
|
|
10306
|
+
lines.push(`${indent}\u2022 ${node.name} \xB7 ${badge2(node)}`);
|
|
10307
|
+
for (const warning of node.warnings ?? []) {
|
|
10308
|
+
lines.push(`${indent} \u26A0 ${warning}`);
|
|
10309
|
+
}
|
|
10310
|
+
for (const collection of node.collections) {
|
|
10311
|
+
const line = collectionLine2(collection);
|
|
10312
|
+
if (line) {
|
|
10313
|
+
lines.push(`${indent} ${line}`);
|
|
10314
|
+
}
|
|
10315
|
+
}
|
|
10316
|
+
for (const child of node.children) {
|
|
10317
|
+
renderNode2(child, depth + 1, lines);
|
|
10318
|
+
}
|
|
10319
|
+
}
|
|
10320
|
+
function renderSection2(title, nodes, lines) {
|
|
10321
|
+
if (nodes.length === 0) {
|
|
10322
|
+
return;
|
|
10323
|
+
}
|
|
10324
|
+
lines.push("");
|
|
10325
|
+
lines.push(title);
|
|
10326
|
+
for (const node of nodes) {
|
|
10327
|
+
renderNode2(node, 1, lines);
|
|
10328
|
+
}
|
|
10329
|
+
}
|
|
10330
|
+
var CAMPAIGN_TREE_ENTITIES = /* @__PURE__ */ new Set(["campaignGroup", "campaign", "creative"]);
|
|
10331
|
+
function renderDraftStatus2(data) {
|
|
10332
|
+
if (data.status === "none" || data.count === 0) {
|
|
10333
|
+
return "No staged LinkedIn changes on this chat.";
|
|
10334
|
+
}
|
|
10335
|
+
const modeNote = data.mode === "live" ? "Live \u2014 publishing writes directly to LinkedIn." : "Simulated \u2014 publishing completes the whole draft without calling LinkedIn.";
|
|
10336
|
+
const lines = [
|
|
10337
|
+
`LinkedIn Ads \u2014 ${data.count} staged change${data.count === 1 ? "" : "s"} \xB7 ${data.mode}`,
|
|
10338
|
+
modeNote
|
|
10339
|
+
];
|
|
10340
|
+
const tree = data.tree ?? [];
|
|
10341
|
+
renderSection2(
|
|
10342
|
+
"Campaigns",
|
|
10343
|
+
tree.filter((node) => CAMPAIGN_TREE_ENTITIES.has(node.entity)),
|
|
10344
|
+
lines
|
|
10345
|
+
);
|
|
10346
|
+
const rest = tree.filter((node) => !CAMPAIGN_TREE_ENTITIES.has(node.entity));
|
|
10347
|
+
renderSection2("Other", rest, lines);
|
|
10348
|
+
return lines.join("\n");
|
|
10349
|
+
}
|
|
10350
|
+
|
|
10351
|
+
// src/commands/ads/linkedin/draft.ts
|
|
10352
|
+
async function listDraft(json) {
|
|
9897
10353
|
try {
|
|
9898
10354
|
const chatId = requireChatId();
|
|
9899
10355
|
const response = await apiPost("/api/ads/linkedin/draft", {
|
|
9900
10356
|
chatId
|
|
9901
10357
|
});
|
|
9902
|
-
|
|
10358
|
+
if (json || !response.ok || !response.data) {
|
|
10359
|
+
writeJsonEnvelope(response);
|
|
10360
|
+
return;
|
|
10361
|
+
}
|
|
10362
|
+
process.stdout.write(`${renderDraftStatus2(response.data)}
|
|
10363
|
+
`);
|
|
9903
10364
|
} catch (err) {
|
|
9904
10365
|
handleLinkedinError(err);
|
|
9905
10366
|
}
|
|
@@ -9907,9 +10368,12 @@ async function listDraft() {
|
|
|
9907
10368
|
var listCommand3 = defineCommand45({
|
|
9908
10369
|
meta: {
|
|
9909
10370
|
name: "list",
|
|
9910
|
-
description: "
|
|
10371
|
+
description: "Review everything staged as a Campaign group \u25B8 Campaign \u25B8 Creative tree (--json for the raw envelope). Example: baker ads linkedin draft"
|
|
9911
10372
|
},
|
|
9912
|
-
|
|
10373
|
+
args: { json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable tree" } },
|
|
10374
|
+
run: async ({ args }) => {
|
|
10375
|
+
await listDraft(args.json === true);
|
|
10376
|
+
}
|
|
9913
10377
|
});
|
|
9914
10378
|
var removeCommand2 = defineCommand45({
|
|
9915
10379
|
meta: {
|
|
@@ -9953,17 +10417,70 @@ var clearCommand2 = defineCommand45({
|
|
|
9953
10417
|
}
|
|
9954
10418
|
}
|
|
9955
10419
|
});
|
|
10420
|
+
var amendCommand = defineCommand45({
|
|
10421
|
+
meta: {
|
|
10422
|
+
name: "amend",
|
|
10423
|
+
description: "Update a staged op in place \u2014 merges a JSON patch into its payload (RFC-7386: objects deep-merge, null deletes a key, arrays/scalars replace) and re-validates. Use this instead of remove + re-create."
|
|
10424
|
+
},
|
|
10425
|
+
args: {
|
|
10426
|
+
ref: { type: "positional", description: "Staged op ref (li_temp_*) or target id/URN", required: true },
|
|
10427
|
+
file: { type: "string", description: "JSON file with the patch object" },
|
|
10428
|
+
patch: { type: "string", description: "Inline JSON patch object" }
|
|
10429
|
+
},
|
|
10430
|
+
run: async ({ args }) => {
|
|
10431
|
+
const ref = requireTarget2(args, "op");
|
|
10432
|
+
const patch = loadPatchArg2(args);
|
|
10433
|
+
try {
|
|
10434
|
+
const chatId = requireChatId();
|
|
10435
|
+
const body = { chatId, ref, patch };
|
|
10436
|
+
const response = await apiPost(
|
|
10437
|
+
"/api/ads/linkedin/draft/amend",
|
|
10438
|
+
body
|
|
10439
|
+
);
|
|
10440
|
+
writeJsonEnvelope(response);
|
|
10441
|
+
} catch (err) {
|
|
10442
|
+
handleLinkedinError(err);
|
|
10443
|
+
}
|
|
10444
|
+
}
|
|
10445
|
+
});
|
|
10446
|
+
var showCommand = defineCommand45({
|
|
10447
|
+
meta: {
|
|
10448
|
+
name: "show",
|
|
10449
|
+
description: "Print the full staged payload for one op \u2014 the receipt to verify a change looks right before publish (never truncated)."
|
|
10450
|
+
},
|
|
10451
|
+
args: {
|
|
10452
|
+
ref: { type: "positional", description: "Staged op ref (li_temp_*) or target id/URN", required: true }
|
|
10453
|
+
},
|
|
10454
|
+
run: async ({ args }) => {
|
|
10455
|
+
const ref = requireTarget2(args, "op");
|
|
10456
|
+
try {
|
|
10457
|
+
const chatId = requireChatId();
|
|
10458
|
+
const body = { chatId, ref };
|
|
10459
|
+
const response = await apiPost(
|
|
10460
|
+
"/api/ads/linkedin/draft/show",
|
|
10461
|
+
body
|
|
10462
|
+
);
|
|
10463
|
+
writeJsonEnvelope(response);
|
|
10464
|
+
} catch (err) {
|
|
10465
|
+
handleLinkedinError(err);
|
|
10466
|
+
}
|
|
10467
|
+
}
|
|
10468
|
+
});
|
|
9956
10469
|
var linkedinDraftCommand = defineCommand45({
|
|
9957
10470
|
meta: {
|
|
9958
10471
|
name: "draft",
|
|
9959
|
-
description: "Review and edit the LinkedIn write ops staged in this chat BEFORE publish. Subcommands: list (default), remove, clear. Ops never hit LinkedIn until the chat is published."
|
|
10472
|
+
description: "Review and edit the LinkedIn write ops staged in this chat BEFORE publish. Subcommands: list (default), remove, clear, amend, show. Ops never hit LinkedIn until the chat is published."
|
|
9960
10473
|
},
|
|
9961
10474
|
subCommands: {
|
|
9962
10475
|
list: listCommand3,
|
|
9963
10476
|
remove: removeCommand2,
|
|
9964
|
-
clear: clearCommand2
|
|
10477
|
+
clear: clearCommand2,
|
|
10478
|
+
amend: amendCommand,
|
|
10479
|
+
show: showCommand
|
|
9965
10480
|
},
|
|
9966
|
-
run:
|
|
10481
|
+
run: async () => {
|
|
10482
|
+
await listDraft(false);
|
|
10483
|
+
}
|
|
9967
10484
|
});
|
|
9968
10485
|
|
|
9969
10486
|
// src/commands/ads/linkedin/facets.ts
|