@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/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
- youtubeVideoId: z8.string().min(1),
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 clear staged Google Ads write ops for this chat" },
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 | performanceMaxAssetGroup | call | app | video | demandGen"
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 flags = campaignPayloadFromFlags(args);
8955
- flags.campaignGroup = void 0;
8956
- flags.type = void 0;
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
- async function listDraft() {
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
- writeJsonEnvelope(response);
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: "Show every LinkedIn write op staged in this chat, its mode (simulated = publish will NOT hit LinkedIn), dependencies, and post-publish results. Example: baker ads linkedin draft"
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
- run: listDraft
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: listDraft
10481
+ run: async () => {
10482
+ await listDraft(false);
10483
+ }
9967
10484
  });
9968
10485
 
9969
10486
  // src/commands/ads/linkedin/facets.ts