@koda-sl/baker-cli 0.107.0-dev.aaab11a02 → 0.109.0-dev.2acd65459

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 CHANGED
@@ -1006,6 +1006,14 @@ baker ads linkedin conversions create --name "Demo booked" --type LEAD --method
1006
1006
  # Safe duplication — stage-time read of the source → staged DRAFT create ("Duplicate ad", never "Link to original")
1007
1007
  baker ads linkedin campaigns duplicate 123456 --name "New variant"
1008
1008
 
1009
+ # Change copy/media/URL on a LIVE ad — direct-content ads (text/spotlight/follower/jobs) update IN PLACE;
1010
+ # pass only the changed fields, the rest of the ad's current content carries over
1011
+ baker ads linkedin creatives update 1458423674 --headline "New headline" --description "New copy."
1012
+
1013
+ # Post-based ads (image/video/carousel/…) sponsor a post whose content can't be edited — replace in ONE command:
1014
+ # duplicate with overrides (unchanged fields + status carry over) + --replace pauses the original once the copy publishes
1015
+ baker ads linkedin creatives duplicate 1458413484 --headline "New headline" --replace
1016
+
1009
1017
  # Amend staged ops in place — `update <li_temp_ref>` merges into the staged create and re-validates
1010
1018
  baker ads linkedin creatives update li_temp_x3 --headline "Sharper headline" --image-id <bakerImageId>
1011
1019
  baker ads linkedin campaigns update li_temp_x2 --daily-budget 100 --currency EUR
package/dist/cli.js CHANGED
@@ -1056,7 +1056,8 @@ var parentRefSchema = z3.union([
1056
1056
  ]);
1057
1057
  var moneySchema = z3.object({
1058
1058
  amount: z3.string().regex(/^\d+(\.\d{1,2})?$/, "expected a decimal amount like 50 or 50.00").refine((val) => Number(val) > 0, "amount must be greater than zero"),
1059
- currencyCode: z3.string().length(3)
1059
+ /** LinkedIn ad accounts are single-currency — when omitted, staging fills it from the account. */
1060
+ currencyCode: z3.string().length(3).optional()
1060
1061
  });
1061
1062
  var runScheduleSchema = z3.object({
1062
1063
  start: z3.number().int().positive(),
@@ -1125,7 +1126,7 @@ function validateCampaignBudgets(p, ctx, { requireBudget }) {
1125
1126
  message: "totalBudget-only campaigns need runSchedule.end"
1126
1127
  });
1127
1128
  }
1128
- if (p.dailyBudget) {
1129
+ if (p.dailyBudget?.currencyCode) {
1129
1130
  const min = currencyMinimums(p.dailyBudget.currencyCode).dailyBudgetMin;
1130
1131
  if (Number(p.dailyBudget.amount) < min) {
1131
1132
  ctx.addIssue({
@@ -1135,7 +1136,7 @@ function validateCampaignBudgets(p, ctx, { requireBudget }) {
1135
1136
  });
1136
1137
  }
1137
1138
  }
1138
- if (p.unitCost) {
1139
+ if (p.unitCost?.currencyCode) {
1139
1140
  const min = currencyMinimums(p.unitCost.currencyCode).unitCostMin;
1140
1141
  if (Number(p.unitCost.amount) < min) {
1141
1142
  ctx.addIssue({
@@ -1225,14 +1226,17 @@ var spotlightCreativeSchema = z3.object({
1225
1226
  ctaLabel: z3.string().min(1).max(LINKEDIN_LIMITS.creative.spotlightCtaMax),
1226
1227
  logoImageId: bakerMediaIdSchema.optional(),
1227
1228
  logoUrn: z3.string().regex(IMAGE_URN_REGEX).optional(),
1228
- showMemberProfilePhoto: z3.boolean().default(true)
1229
+ showMemberProfilePhoto: z3.boolean().default(true),
1230
+ /** Carried from the live snapshot on updates so LinkedIn's whole-content $set preserves it. */
1231
+ organizationName: z3.string().min(1).max(200).optional()
1229
1232
  });
1230
1233
  var followerCreativeSchema = z3.object({
1231
1234
  format: z3.literal("follower"),
1232
1235
  headline: z3.string().max(LINKEDIN_LIMITS.creative.spotlightHeadlineMax).optional(),
1233
1236
  description: z3.string().max(LINKEDIN_LIMITS.creative.spotlightDescriptionMax).optional(),
1234
1237
  logoImageId: bakerMediaIdSchema.optional(),
1235
- logoUrn: z3.string().regex(IMAGE_URN_REGEX).optional()
1238
+ logoUrn: z3.string().regex(IMAGE_URN_REGEX).optional(),
1239
+ organizationName: z3.string().min(1).max(200).optional()
1236
1240
  });
1237
1241
  var documentCreativeSchema = z3.object({
1238
1242
  format: z3.literal("document"),
@@ -5413,7 +5417,11 @@ registerSchema({
5413
5417
  start: { type: "string", description: "Run schedule start (ISO date or epoch ms)", required: true },
5414
5418
  end: { type: "string", description: "Run schedule end", required: false },
5415
5419
  "total-budget": { type: "string", description: "Lifetime budget amount", required: false },
5416
- currency: { type: "string", description: "3-letter currency code (required with money flags)", required: false },
5420
+ currency: {
5421
+ type: "string",
5422
+ description: "3-letter currency code (defaults to the ad account currency)",
5423
+ required: false
5424
+ },
5417
5425
  status: { type: "string", description: "DRAFT|ACTIVE|PAUSED (default DRAFT)", required: false },
5418
5426
  file: { type: "string", description: "JSON payload file; flags override file keys", required: false }
5419
5427
  }
@@ -5432,7 +5440,11 @@ registerSchema({
5432
5440
  start: { type: "string", description: "New schedule start", required: false },
5433
5441
  end: { type: "string", description: "New schedule end", required: false },
5434
5442
  "total-budget": { type: "string", description: "New lifetime budget", required: false },
5435
- currency: { type: "string", description: "3-letter currency code", required: false },
5443
+ currency: {
5444
+ type: "string",
5445
+ description: "3-letter currency code (defaults to the ad account currency)",
5446
+ required: false
5447
+ },
5436
5448
  status: { type: "string", description: "ACTIVE|PAUSED|ARCHIVED", required: false },
5437
5449
  file: { type: "string", description: "JSON file with fields to change", required: false }
5438
5450
  }
@@ -5450,7 +5462,11 @@ registerSchema({
5450
5462
  bid: { type: "string", description: "Manual bid amount (unitCost)", required: false },
5451
5463
  "daily-budget": { type: "string", description: "Daily budget amount", required: false },
5452
5464
  "total-budget": { type: "string", description: "Lifetime budget amount", required: false },
5453
- currency: { type: "string", description: "3-letter currency code (required with money flags)", required: false },
5465
+ currency: {
5466
+ type: "string",
5467
+ description: "3-letter currency code (defaults to the ad account currency)",
5468
+ required: false
5469
+ },
5454
5470
  start: { type: "string", description: "Run schedule start", required: false },
5455
5471
  end: { type: "string", description: "Run schedule end", required: false },
5456
5472
  locale: { type: "string", description: "Profile locale like en_US (default en_US)", required: false },
@@ -5479,7 +5495,11 @@ registerSchema({
5479
5495
  bid: { type: "string", description: "New manual bid", required: false },
5480
5496
  "daily-budget": { type: "string", description: "New daily budget", required: false },
5481
5497
  "total-budget": { type: "string", description: "New lifetime budget", required: false },
5482
- currency: { type: "string", description: "3-letter currency code", required: false },
5498
+ currency: {
5499
+ type: "string",
5500
+ description: "3-letter currency code (defaults to the ad account currency)",
5501
+ required: false
5502
+ },
5483
5503
  start: { type: "string", description: "New schedule start", required: false },
5484
5504
  end: { type: "string", description: "New schedule end", required: false },
5485
5505
  "targeting-file": { type: "string", description: "JSON file with replacement targetingCriteria", required: false },
@@ -5548,8 +5568,27 @@ registerSchema({
5548
5568
  });
5549
5569
  registerSchema({
5550
5570
  command: "ads.linkedin.creatives.duplicate",
5551
- description: "Stage a copy of an existing creative by re-sponsoring its underlying post into a new DRAFT creative (--file can override the target campaign).",
5552
- args: duplicateArgs
5571
+ description: "Stage a copy of an existing ad, optionally with new content. Direct-content ads (text/spotlight/follower/jobs) copy field-by-field; post-based ads re-sponsor the same post \u2014 and since post content can't be edited, duplicate --replace is how post-based ads change copy (direct-content ads update in place via creatives.update instead). --replace stages pausing the original, gated on the new ad publishing successfully, and the copy inherits the source's status.",
5572
+ args: {
5573
+ id: { type: "positional", description: "Source creative id or URN", required: true },
5574
+ ...writeAccountArgs,
5575
+ campaign: { type: "string", description: "Stage the copy under a different ad set", required: false },
5576
+ "intended-status": { type: "string", description: "Status for the copy (default DRAFT)", required: false },
5577
+ replace: { type: "boolean", description: "Also pause the original once the new ad publishes", required: false },
5578
+ headline: { type: "string", description: "New headline", required: false },
5579
+ description: { type: "string", description: "New description (text/spotlight)", required: false },
5580
+ intro: { type: "string", description: "New commentary / intro text", required: false },
5581
+ "landing-url": { type: "string", description: "New https destination URL", required: false },
5582
+ cta: { type: "string", description: "New CTA", required: false },
5583
+ "cta-label": { type: "string", description: "Spotlight custom CTA label", required: false },
5584
+ "image-id": { type: "string", description: "Baker image library id", required: false },
5585
+ "image-urn": { type: "string", description: "Existing urn:li:image:*", required: false },
5586
+ "video-id": { type: "string", description: "Baker video library id", required: false },
5587
+ "video-urn": { type: "string", description: "Existing urn:li:video:*", required: false },
5588
+ "logo-image-id": { type: "string", description: "Baker image id for the logo", required: false },
5589
+ title: { type: "string", description: "Media title", required: false },
5590
+ file: { type: "string", description: "JSON file with field overrides for the copy", required: false }
5591
+ }
5553
5592
  });
5554
5593
  registerSchema({
5555
5594
  command: "ads.linkedin.creatives.create",
@@ -5581,27 +5620,27 @@ registerSchema({
5581
5620
  });
5582
5621
  registerSchema({
5583
5622
  command: "ads.linkedin.creatives.update",
5584
- description: "Stage changes to an existing creative (intendedStatus, name, headline, landing-url, cta), or AMEND a creative staged in this chat by passing its li_temp_* ref \u2014 amends accept the content flags from creatives.create (headline, intro, description, image-id, landing-url, cta, \u2026) and merge into the staged ad, re-validating the result. Use this to apply feedback to staged ads instead of remove + re-create. Applies on chat publish.",
5623
+ description: "Stage changes to an existing creative. Direct-content ads (text/spotlight/follower/jobs) update copy/media/URL IN PLACE \u2014 pass only the changed fields (headline, description, landing-url, image-id, \u2026); the ad's current content carries over. Post-based ads (image/video/carousel/\u2026) can't edit content \u2014 use creatives.duplicate with --replace for those. Passing a li_temp_* ref AMENDS the creative staged in this chat instead. Applies on chat publish.",
5585
5624
  args: {
5586
5625
  id: { type: "positional", description: "Creative id/URN, or li_temp_* ref staged in this chat", required: true },
5587
5626
  ...writeAccountArgs,
5588
5627
  "intended-status": { type: "string", description: "ACTIVE|PAUSED|ARCHIVED|DRAFT", required: false },
5589
5628
  name: { type: "string", description: "New creative name", required: false },
5590
5629
  headline: { type: "string", description: "New headline", required: false },
5591
- intro: { type: "string", description: "Amend only: commentary / intro text", required: false },
5592
- description: { type: "string", description: "Amend only: description (text/spotlight)", required: false },
5630
+ intro: { type: "string", description: "Commentary / intro text", required: false },
5631
+ description: { type: "string", description: "Description (text/spotlight)", required: false },
5593
5632
  "landing-url": { type: "string", description: "New https destination URL", required: false },
5594
5633
  cta: { type: "string", description: "New CTA", required: false },
5595
- "image-id": { type: "string", description: "Amend only: Baker image library id", required: false },
5596
- "image-urn": { type: "string", description: "Amend only: existing urn:li:image:*", required: false },
5597
- "video-id": { type: "string", description: "Amend only: Baker video library id", required: false },
5598
- "logo-image-id": { type: "string", description: "Amend only: Baker image id for the logo", required: false },
5634
+ "image-id": { type: "string", description: "Baker image library id", required: false },
5635
+ "image-urn": { type: "string", description: "Existing urn:li:image:*", required: false },
5636
+ "video-id": { type: "string", description: "Baker video library id", required: false },
5637
+ "logo-image-id": { type: "string", description: "Baker image id for the logo", required: false },
5599
5638
  "thumbnail-image-id": {
5600
5639
  type: "string",
5601
- description: "Amend only: Baker image id for the article thumbnail",
5640
+ description: "Baker image id for the article thumbnail",
5602
5641
  required: false
5603
5642
  },
5604
- title: { type: "string", description: "Amend only: media or article title", required: false },
5643
+ title: { type: "string", description: "Media or article title", required: false },
5605
5644
  file: { type: "string", description: "JSON file with fields to change; flags override file keys", required: false }
5606
5645
  }
5607
5646
  });
@@ -6606,8 +6645,11 @@ function parseMoneyFlag(amount, currency, flag) {
6606
6645
  if (amount === void 0 || amount === null || amount === "") {
6607
6646
  return void 0;
6608
6647
  }
6648
+ if (currency === void 0 || currency === null || currency === "") {
6649
+ return { amount: String(amount) };
6650
+ }
6609
6651
  if (typeof currency !== "string" || currency.length !== 3) {
6610
- failWriteValidation(`${flag} needs --currency (3-letter code matching the ad account, e.g. EUR)`);
6652
+ failWriteValidation(`${flag} got an invalid --currency (expected a 3-letter code like EUR)`);
6611
6653
  }
6612
6654
  return { amount: String(amount), currencyCode: currency.toUpperCase() };
6613
6655
  }
@@ -6787,7 +6829,7 @@ Example: baker ads linkedin campaign-groups create --name "Q3 ABM" --start 2026-
6787
6829
  start: { type: "string", description: "Run schedule start (ISO date or epoch ms)" },
6788
6830
  end: { type: "string", description: "Run schedule end" },
6789
6831
  "total-budget": { type: "string", description: "Lifetime budget amount" },
6790
- currency: { type: "string", description: "3-letter currency code (required with money flags)" },
6832
+ currency: { type: "string", description: "3-letter currency code (defaults to the ad account currency)" },
6791
6833
  status: { type: "string", description: "DRAFT|ACTIVE|PAUSED (default DRAFT)" },
6792
6834
  file: { type: "string", description: "JSON file with the full payload; flags override file keys" }
6793
6835
  },
@@ -6816,7 +6858,7 @@ Example: baker ads linkedin campaign-groups update 635137195 --total-budget 8000
6816
6858
  start: { type: "string", description: "New run schedule start" },
6817
6859
  end: { type: "string", description: "New run schedule end" },
6818
6860
  "total-budget": { type: "string", description: "New lifetime budget" },
6819
- currency: { type: "string", description: "3-letter currency code" },
6861
+ currency: { type: "string", description: "3-letter currency code (defaults to the ad account currency)" },
6820
6862
  status: { type: "string", description: "ACTIVE|PAUSED|ARCHIVED|\u2026" },
6821
6863
  file: { type: "string", description: "JSON file with fields to change" }
6822
6864
  },
@@ -6867,7 +6909,7 @@ var campaignFlagArgs = {
6867
6909
  bid: { type: "string", description: "Manual bid (unitCost) \u2014 playbook mandates manual bidding" },
6868
6910
  "daily-budget": { type: "string", description: "Daily budget amount" },
6869
6911
  "total-budget": { type: "string", description: "Lifetime budget amount" },
6870
- currency: { type: "string", description: "3-letter currency code (required with money flags)" },
6912
+ currency: { type: "string", description: "3-letter currency code (defaults to the ad account currency)" },
6871
6913
  start: { type: "string", description: "Run schedule start (ISO date or epoch ms)" },
6872
6914
  end: { type: "string", description: "Run schedule end" },
6873
6915
  locale: { type: "string", description: "Profile locale like en_US" },
@@ -6989,7 +7031,69 @@ Example: baker ads linkedin ${cliGroup} duplicate 123456 --name "New variant"`
6989
7031
  }
6990
7032
  var campaignsDuplicateCommand = duplicateCommand("campaign", "campaigns");
6991
7033
  var campaignGroupsDuplicateCommand = duplicateCommand("campaignGroup", "campaign-groups");
6992
- var creativesDuplicateCommand = duplicateCommand("creative", "creatives");
7034
+ var creativesDuplicateCommand = defineCommand36({
7035
+ meta: {
7036
+ name: "duplicate",
7037
+ description: `Stage a copy of an existing ad, optionally with new content \u2014 the ONLY way to change copy/media/URL on a live ad (LinkedIn makes ad content immutable). ${STAGED_NOTE}
7038
+ --replace also pauses the original once the new ad publishes (skipped if the new ad fails).
7039
+ Examples:
7040
+ baker ads linkedin creatives duplicate 1458413484 --headline "10x Demos, Same Team" --description "AI handles every demo request." --replace
7041
+ baker ads linkedin creatives duplicate urn:li:sponsoredCreative:789 --campaign 799305464`
7042
+ },
7043
+ args: {
7044
+ id: { type: "positional", description: "Source creative id or URN", required: true },
7045
+ ...accountArgs,
7046
+ campaign: { type: "string", description: "Stage the copy under a different ad set (id/URN or li_temp_*)" },
7047
+ "intended-status": {
7048
+ type: "string",
7049
+ description: "Status for the copy (default DRAFT; with --replace, the source's status)"
7050
+ },
7051
+ replace: {
7052
+ type: "boolean",
7053
+ description: "Also pause the original once the new ad publishes; the copy inherits the source's status"
7054
+ },
7055
+ intro: { type: "string", description: "New commentary / intro text" },
7056
+ headline: { type: "string", description: "New headline" },
7057
+ description: { type: "string", description: "New description (text/spotlight ads)" },
7058
+ "landing-url": { type: "string", description: "New https destination URL" },
7059
+ cta: { type: "string", description: "New CTA (LEARN_MORE|REQUEST_DEMO|\u2026)" },
7060
+ "cta-label": { type: "string", description: "Spotlight custom CTA label" },
7061
+ "image-id": { type: "string", description: "Baker image library id" },
7062
+ "image-urn": { type: "string", description: "Existing urn:li:image:*" },
7063
+ "video-id": { type: "string", description: "Baker video library id" },
7064
+ "video-urn": { type: "string", description: "Existing urn:li:video:*" },
7065
+ "logo-image-id": { type: "string", description: "Baker image id for the logo" },
7066
+ title: { type: "string", description: "Media title" },
7067
+ file: { type: "string", description: "JSON file with field overrides for the copy" }
7068
+ },
7069
+ run: async ({ args }) => {
7070
+ try {
7071
+ const chatId = requireChatId();
7072
+ const file = loadJsonFileArg(args.file);
7073
+ const fileContent = file.content !== null && typeof file.content === "object" && !Array.isArray(file.content) ? file.content : {};
7074
+ const content = { ...fileContent, ...creativeContentPatch(args) };
7075
+ const overrides = mergePayload(file, {
7076
+ campaign: args.campaign,
7077
+ intendedStatus: args["intended-status"] ? String(args["intended-status"]).toUpperCase() : void 0,
7078
+ content: Object.keys(content).length > 0 ? content : void 0
7079
+ });
7080
+ const response = await apiPost(
7081
+ "/api/ads/linkedin/draft/duplicate",
7082
+ {
7083
+ chatId,
7084
+ accountId: bareAccountId(args),
7085
+ entity: "creative",
7086
+ sourceId: requireTarget(args, "creative"),
7087
+ ...Object.keys(overrides).length > 0 ? { overrides } : {},
7088
+ ...args.replace ? { replace: true } : {}
7089
+ }
7090
+ );
7091
+ writeJsonEnvelope(response);
7092
+ } catch (err) {
7093
+ handleLinkedinError(err);
7094
+ }
7095
+ }
7096
+ });
6993
7097
  var CREATIVE_FORMATS = [
6994
7098
  "image",
6995
7099
  "video",
@@ -7093,7 +7197,6 @@ var CREATIVE_AMEND_CONTENT_FLAGS = [
7093
7197
  ["thumbnail-urn", "thumbnailUrn"],
7094
7198
  ["title", "title"]
7095
7199
  ];
7096
- var CREATIVE_LIVE_UPDATE_KEYS = /* @__PURE__ */ new Set(["headline", "landingUrl", "cta"]);
7097
7200
  function creativeContentPatch(args) {
7098
7201
  const patch = {};
7099
7202
  for (const [flag, key] of CREATIVE_AMEND_CONTENT_FLAGS) {
@@ -7110,30 +7213,30 @@ var creativesUpdateCommand = defineCommand36({
7110
7213
  meta: {
7111
7214
  name: "update",
7112
7215
  description: `Stage changes to an existing creative, or amend a creative staged in this chat by passing its li_temp_* ref. ${STAGED_NOTE}
7113
- Staged creatives (li_temp_*) accept the content flags from \`creatives create\` \u2014 fields merge into the staged ad and re-validate. Creatives already on LinkedIn only accept --intended-status/--name/--headline/--landing-url/--cta (content is immutable \u2014 use \`creatives duplicate\` for a new version).
7216
+ 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.
7114
7217
  Examples:
7115
- baker ads linkedin creatives update urn:li:sponsoredCreative:789 --intended-status PAUSED
7116
- baker ads linkedin creatives update li_temp_a1b2 --headline "Sharper headline" --image-id <bakerImageId>`
7218
+ baker ads linkedin creatives update 1458423674 --headline "Sharper headline" --description "New copy."
7219
+ baker ads linkedin creatives update urn:li:sponsoredCreative:789 --intended-status PAUSED`
7117
7220
  },
7118
7221
  args: {
7119
7222
  id: { type: "positional", description: "Creative id/URN, or li_temp_* ref staged in this chat", required: true },
7120
7223
  ...accountArgs,
7121
7224
  "intended-status": { type: "string", description: "ACTIVE|PAUSED|ARCHIVED|DRAFT" },
7122
7225
  name: { type: "string", description: "New creative name" },
7123
- intro: { type: "string", description: "Amend only: commentary / intro text" },
7226
+ intro: { type: "string", description: "Commentary / intro text" },
7124
7227
  headline: { type: "string", description: "New headline" },
7125
- description: { type: "string", description: "Amend only: description (text/spotlight ads)" },
7228
+ description: { type: "string", description: "Description (text/spotlight ads)" },
7126
7229
  "landing-url": { type: "string", description: "New https destination URL" },
7127
7230
  cta: { type: "string", description: "New CTA (LEARN_MORE|REQUEST_DEMO|\u2026)" },
7128
- "cta-label": { type: "string", description: "Amend only: spotlight custom CTA label" },
7129
- "image-id": { type: "string", description: "Amend only: Baker image library id" },
7130
- "image-urn": { type: "string", description: "Amend only: existing urn:li:image:*" },
7131
- "video-id": { type: "string", description: "Amend only: Baker video library id" },
7132
- "video-urn": { type: "string", description: "Amend only: existing urn:li:video:*" },
7133
- "logo-image-id": { type: "string", description: "Amend only: Baker image id for the logo" },
7134
- "thumbnail-image-id": { type: "string", description: "Amend only: Baker image id for the article thumbnail" },
7135
- "thumbnail-urn": { type: "string", description: "Amend only: existing urn:li:image:* thumbnail" },
7136
- title: { type: "string", description: "Amend only: media or article title" },
7231
+ "cta-label": { type: "string", description: "Spotlight custom CTA label" },
7232
+ "image-id": { type: "string", description: "Baker image library id" },
7233
+ "image-urn": { type: "string", description: "Existing urn:li:image:*" },
7234
+ "video-id": { type: "string", description: "Baker video library id" },
7235
+ "video-urn": { type: "string", description: "Existing urn:li:video:*" },
7236
+ "logo-image-id": { type: "string", description: "Baker image id for the logo" },
7237
+ "thumbnail-image-id": { type: "string", description: "Baker image id for the article thumbnail" },
7238
+ "thumbnail-urn": { type: "string", description: "Existing urn:li:image:* thumbnail" },
7239
+ title: { type: "string", description: "Media or article title" },
7137
7240
  file: { type: "string", description: "JSON file with fields to change; flags override file keys" }
7138
7241
  },
7139
7242
  run: async ({ args }) => {
@@ -7142,28 +7245,12 @@ Examples:
7142
7245
  const file = loadJsonFileArg(args.file);
7143
7246
  const contentPatch = creativeContentPatch(args);
7144
7247
  const intendedStatus = args["intended-status"] ? String(args["intended-status"]).toUpperCase() : void 0;
7145
- if (target.startsWith("li_temp_")) {
7146
- const fileContent = file.content !== null && typeof file.content === "object" && !Array.isArray(file.content) ? file.content : {};
7147
- const content = { ...fileContent, ...contentPatch };
7148
- const payload2 = mergePayload(file, {
7149
- intendedStatus,
7150
- content: Object.keys(content).length > 0 ? content : void 0
7151
- });
7152
- await stageOp({ kind: "creative.update", accountId, target, payload: payload2 });
7153
- return;
7154
- }
7155
- const immutable = Object.keys(contentPatch).filter((key) => !CREATIVE_LIVE_UPDATE_KEYS.has(key));
7156
- if (immutable.length > 0) {
7157
- failWriteValidation(
7158
- `creative content is immutable on LinkedIn (${immutable.join(", ")}) \u2014 pass a li_temp_* ref to amend a creative staged in this chat, or use \`creatives duplicate\``
7159
- );
7160
- }
7248
+ const fileContent = file.content !== null && typeof file.content === "object" && !Array.isArray(file.content) ? file.content : {};
7249
+ const content = { ...fileContent, ...contentPatch };
7161
7250
  const payload = mergePayload(file, {
7162
7251
  intendedStatus,
7163
7252
  name: args.name,
7164
- headline: contentPatch.headline,
7165
- landingUrl: contentPatch.landingUrl,
7166
- cta: contentPatch.cta
7253
+ content: Object.keys(content).length > 0 ? content : void 0
7167
7254
  });
7168
7255
  await stageOp({ kind: "creative.update", accountId, target, payload });
7169
7256
  }