@koda-sl/baker-cli 0.255.0 → 0.256.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 CHANGED
@@ -5843,6 +5843,10 @@ Pushing to `main` with changes in `packages/cli/` triggers the GitHub Actions wo
5843
5843
 
5844
5844
  Three more things the 202608 pin brought with it. **The conversion-type enum went from 8 values to all 34 LinkedIn accepts** — `QUALIFIED_LEAD`, `BOOK_APPOINTMENT`, `SUBMIT_APPLICATION` and 23 others were rejected by our own schema before the request ever left, and 202608 adds `MARKETING_QUALIFIED_LEAD` / `SALES_QUALIFIED_LEAD` on top. **Attribution windows gained 365** (and view-through gained 90), with the type-conditional rule enforced on `conversions create`: 365 is only legal for `SUBMIT_APPLICATION`, `PURCHASE`, `ADD_TO_CART`, `QUALIFIED_LEAD`, `LEAD`. And **a 429 carrying `SEGMENT_LIMIT_EXCEEDED` is no longer reported as a throttle** — 202608 caps an ad account at 1,000 DMP segments and reuses the rate-limit status for it, so an agent that read it as "retry in a second" would have retried forever against a cap only a human can clear in Campaign Manager.
5845
5845
 
5846
+ - **0.256.0**: **`assets attach` no longer offers an image link, because Google accepts none.** A production publish came back with six `AssetLinkError.FIELD_TYPE_INCOMPATIBLE_WITH_CAMPAIGN_TYPE` rows (trigger `MARKETING_IMAGE,SEARCH`) on an otherwise built and live Search campaign — staged exactly as this CLI's own advisory and the scaffold's ads-google doc instructed, at `"fieldType": "MARKETING_IMAGE"`. The obvious fix was `AD_IMAGE`, which is what Google's documentation and its own forum answers name for a Search image extension. **It does not work either.** Sent to a real account on v23, all four combinations are refused: `MARKETING_IMAGE` on a Search `CampaignAsset` → `FIELD_TYPE_INCOMPATIBLE_WITH_CAMPAIGN_TYPE`; `MARKETING_IMAGE` on a Search `AdGroupAsset` → `UNSUPPORTED_FIELD_TYPE`; `AD_IMAGE` on a `CampaignAsset` → `UNSUPPORTED_FIELD_TYPE`; `AD_IMAGE` on an `AdGroupAsset` → `UNSUPPORTED_FIELD_TYPE`. Image extensions never moved to asset-based extensions, so there is no request the API takes. Every image field type is now refused at the CLI boundary with the interface named instead of another field type to try, and the **"recommend at least 3 image assets" advisory is removed** — it had been firing on every Search campaign since 0.191.0 and could not be satisfied by any draft, because the write it named has never once succeeded. Performance Max asset-group images are a different mechanism and are untouched (`asset-groups attach`). Four force-synced scaffold surfaces were saying the same wrong thing and now agree with the API: the `ads-google.md` tool doc, the `assets` command group description, the account-audit playbook's Dynamic Images row, and `staged-writes.md`. An audit should still raise a missing-images gap — it is a real SERP disadvantage — it just has to name the Google Ads interface as the fix rather than a command.
5847
+
5848
+ - **0.256.0**: a structured snippet value over 25 characters is refused before it is staged. Google caps each VALUE at 25 and answers a longer one with a bare `Too long.` at `structured_snippet_asset.values[i]` — no header, no value, nothing to act on — and it takes the asset's campaign link down with it as `RESOURCE_NOT_FOUND`. `assets create --values` and `assets update --values` now cap each entry; the per-asset 3-10 count check was already there, and only the per-value length was missing. Found on a live publish where four of five values were fine and "Bases de datos vectoriales" (26) was not, and confirmed fixed by re-publishing the same snippet at 25 characters against a real account, where it applied and linked cleanly.
5849
+
5846
5850
  ### Testing a pre-release in sandboxes
5847
5851
 
5848
5852
  ```bash
package/dist/cli.js CHANGED
@@ -2113,7 +2113,14 @@ var GOOGLE_ADS_LIMITS = {
2113
2113
  // Google's published headers (`structured-snippet-headers.ts`), the longest
2114
2114
  // of which is 29 characters. Only the values are capped.
2115
2115
  structuredSnippetValuesMin: 3,
2116
- structuredSnippetValuesMax: 10
2116
+ structuredSnippetValuesMax: 10,
2117
+ /**
2118
+ * Per VALUE, not per asset. Google rejects a 26-character value with a bare "Too long." at
2119
+ * `structured_snippet_asset.values[i]` — one red row inside an otherwise clean publish, plus
2120
+ * every link that depends on the asset. Confirmed live on v23: "Bases de datos vectoriales"
2121
+ * (26) rejected, its four neighbours accepted.
2122
+ */
2123
+ structuredSnippetValueMax: 25
2117
2124
  },
2118
2125
  conversionAction: {
2119
2126
  nameMax: 255
@@ -2296,9 +2303,24 @@ var ASSET_FIELD_TYPES = [
2296
2303
  "HEADLINE",
2297
2304
  "DESCRIPTION",
2298
2305
  "LOGO",
2306
+ /**
2307
+ * Kept because a draft staged before {@link IMAGE_ASSET_LINK_MESSAGE} shipped may still carry one,
2308
+ * and because it is the field type an asset-group image occupies. It is refused on an
2309
+ * `assetLink.attach` — see that message for what Google actually does with it.
2310
+ */
2299
2311
  "MARKETING_IMAGE",
2300
2312
  "BUSINESS_NAME"
2301
2313
  ];
2314
+ var IMAGE_ASSET_LINK_FIELD_TYPES = /* @__PURE__ */ new Set([
2315
+ "MARKETING_IMAGE",
2316
+ "SQUARE_MARKETING_IMAGE",
2317
+ "PORTRAIT_MARKETING_IMAGE",
2318
+ "AD_IMAGE"
2319
+ ]);
2320
+ var IMAGE_ASSET_LINK_MESSAGE = "Google does not accept an image through an asset link. MARKETING_IMAGE is a Performance Max asset-group slot (use `asset-groups attach`), and AD_IMAGE \u2014 the field type Google names for a Search image extension \u2014 comes back UNSUPPORTED_FIELD_TYPE at campaign and ad group level alike. Image extensions are not reachable through the API: tell the user to add them in the Google Ads interface, and carry on with the rest of the job.";
2321
+ function isImageAssetLinkFieldType(fieldType) {
2322
+ return IMAGE_ASSET_LINK_FIELD_TYPES.has(fieldType);
2323
+ }
2302
2324
  var ASSET_GROUP_ASSET_FIELD_TYPES = [
2303
2325
  "MARKETING_IMAGE",
2304
2326
  "SQUARE_MARKETING_IMAGE",
@@ -3151,7 +3173,7 @@ var structuredSnippetHeaderSchema = z4.string().min(1).transform((header, ctx) =
3151
3173
  var structuredSnippetAssetSchema = z4.object({
3152
3174
  type: z4.literal("structuredSnippet"),
3153
3175
  header: structuredSnippetHeaderSchema,
3154
- values: z4.array(z4.string().min(1)).min(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMin).max(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMax)
3176
+ values: z4.array(z4.string().min(1).max(GOOGLE_ADS_LIMITS.asset.structuredSnippetValueMax)).min(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMin).max(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMax)
3155
3177
  });
3156
3178
  var callToActionAssetSchema = z4.object({ type: z4.literal("callToAction"), callToAction: z4.string().min(1) });
3157
3179
  var assetCreateSchema = z4.discriminatedUnion("type", [
@@ -3171,7 +3193,7 @@ var assetUpdateSchema = z4.object({
3171
3193
  finalUrls: z4.array(httpsUrlSchema2).min(1).optional(),
3172
3194
  calloutText: z4.string().min(1).max(GOOGLE_ADS_LIMITS.asset.calloutTextMax).optional(),
3173
3195
  header: structuredSnippetHeaderSchema.optional(),
3174
- values: z4.array(z4.string().min(1)).min(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMin).max(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMax).optional(),
3196
+ values: z4.array(z4.string().min(1).max(GOOGLE_ADS_LIMITS.asset.structuredSnippetValueMax)).min(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMin).max(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMax).optional(),
3175
3197
  callToAction: z4.string().min(1).optional(),
3176
3198
  text: z4.string().min(1).optional()
3177
3199
  }).refine((p) => Object.values(p).some((v) => v !== void 0), "update needs at least one field");
@@ -3179,7 +3201,11 @@ var assetLinkAttachSchema = z4.object({
3179
3201
  level: z4.enum(["campaign", "adGroup", "customer"]),
3180
3202
  parent: refSchema.optional(),
3181
3203
  asset: refSchema,
3182
- fieldType: z4.enum(ASSET_FIELD_TYPES)
3204
+ // A plain string, not `z.enum(ASSET_FIELD_TYPES)`, so the image refusal below can answer with
3205
+ // the reason instead of an enum listing. An agent reaching for AD_IMAGE is following Google's
3206
+ // own documentation; "Invalid option: expected one of …" sends it to guess another field type,
3207
+ // which is the failure this whole refusal exists to end.
3208
+ fieldType: z4.string().min(1)
3183
3209
  }).superRefine((value, ctx) => {
3184
3210
  if (value.level !== "customer" && !value.parent) {
3185
3211
  ctx.addIssue({
@@ -3188,6 +3214,15 @@ var assetLinkAttachSchema = z4.object({
3188
3214
  message: `parent is required for a ${value.level}-level asset link (--parent-ref)`
3189
3215
  });
3190
3216
  }
3217
+ if (isImageAssetLinkFieldType(value.fieldType)) {
3218
+ ctx.addIssue({ code: z4.ZodIssueCode.custom, path: ["fieldType"], message: IMAGE_ASSET_LINK_MESSAGE });
3219
+ } else if (!ASSET_FIELD_TYPES.includes(value.fieldType)) {
3220
+ ctx.addIssue({
3221
+ code: z4.ZodIssueCode.custom,
3222
+ path: ["fieldType"],
3223
+ message: `expected one of ${ASSET_FIELD_TYPES.join(" | ")}`
3224
+ });
3225
+ }
3191
3226
  });
3192
3227
  var assetGroupCreateSchema = z4.object({
3193
3228
  campaign: refSchema,
@@ -14861,7 +14896,7 @@ var assetsUpdateCommand = defineCommand30({
14861
14896
  "final-urls": { type: "string", description: "Comma-separated sitelink final URLs" },
14862
14897
  "callout-text": { type: "string", description: "Callout text" },
14863
14898
  header: { type: "string", description: "Structured snippet header" },
14864
- values: { type: "string", description: "Comma-separated structured snippet values" },
14899
+ values: { type: "string", description: "Comma-separated structured snippet values (3-10, max 25 characters each)" },
14865
14900
  cta: { type: "string", description: "Call-to-action text" },
14866
14901
  text: { type: "string", description: "Text asset content" }
14867
14902
  },
@@ -14886,7 +14921,7 @@ var assetsUpdateCommand = defineCommand30({
14886
14921
  var assetsCommand = defineCommand30({
14887
14922
  meta: {
14888
14923
  name: "assets",
14889
- description: "Stage asset create/update + asset-link attach/detach (via --file). For a high-performance Search campaign add \u22654 sitelinks, \u22653 callouts, \u22651 structured snippet, and \u22653 image assets per campaign. Build the images with the creative-canvas-ads flow (platform preset `google_search`) \u2014 product or team in action, never stock, and never a logo on a plain background."
14924
+ 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. Images are not among them: Google refuses every image asset link, so image extensions have to be added in the Google Ads interface."
14890
14925
  },
14891
14926
  subCommands: {
14892
14927
  create: fileCreateCommand(
@@ -14895,7 +14930,9 @@ var assetsCommand = defineCommand30({
14895
14930
  "Stage an asset (text/image/sitelink/callout/structuredSnippet/\u2026)"
14896
14931
  ),
14897
14932
  update: assetsUpdateCommand,
14898
- attach: fileCreateCommand("attach", "google.assetLink.attach", "Attach an asset to a campaign/adGroup/customer"),
14933
+ attach: fileCreateCommand("attach", "google.assetLink.attach", "Attach an asset to a campaign/adGroup/customer", [
14934
+ "Images are not attachable here at all \u2014 Google refuses every image field type through an asset link, at every level. Image extensions have to be added in the Google Ads interface; say so and carry on. Performance Max asset-group images are a different mechanism and do work (`asset-groups attach`)."
14935
+ ]),
14899
14936
  detach: statusCommand2("google.assetLink.detach", "asset link")
14900
14937
  }
14901
14938
  });