@productmaker/mcp 12.7.0 → 12.8.1

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.
Files changed (3) hide show
  1. package/dist/http.js +152 -14
  2. package/dist/stdio.js +152 -14
  3. package/package.json +1 -1
package/dist/http.js CHANGED
@@ -8352,6 +8352,7 @@ var LandingAssetRequestSchema = z23.discriminatedUnion("kind", [
8352
8352
  var CodeLandingTypographySchema = z23.object({
8353
8353
  googleFamilies: z23.array(z23.string().regex(/^[A-Za-z0-9 ]+(:wght@[0-9;]+)?$/)).max(4)
8354
8354
  }).strict();
8355
+ var MAX_CODE_LANDING_AUTHOR_ASSET_REQUESTS = 96;
8355
8356
  var CodeLandingAuthorEnvelopeSchema = z23.object({
8356
8357
  mode: LandingDocumentModeSchema,
8357
8358
  document: CodeLandingDocumentSchema,
@@ -8359,7 +8360,7 @@ var CodeLandingAuthorEnvelopeSchema = z23.object({
8359
8360
  anchors: z23.array(CodeLandingAnchorSchema).min(1).max(64),
8360
8361
  components: z23.array(ComponentManifestEntrySchema).max(64),
8361
8362
  interactions: z23.array(InteractionProgramSchema).max(64),
8362
- assetRequests: z23.array(LandingAssetRequestSchema).max(96),
8363
+ assetRequests: z23.array(LandingAssetRequestSchema).max(MAX_CODE_LANDING_AUTHOR_ASSET_REQUESTS),
8363
8364
  /** Optional: only the block library authoring path (compose.ts) sets this. */
8364
8365
  typography: CodeLandingTypographySchema.optional(),
8365
8366
  /**
@@ -8380,6 +8381,12 @@ var CodeLandingAuthorEnvelopeSchema = z23.object({
8380
8381
  */
8381
8382
  variantFacts: z23.array(LandingVariantFactSchema).max(16).optional()
8382
8383
  });
8384
+ var PreparedLandingAssetRequestSchema = z23.union(LandingAssetRequestSchema.options.map((schema) => schema.shape.kind.value === "generate" || schema.shape.kind.value === "adapt_reference" ? schema.extend({
8385
+ prompt: z23.string().min(1)
8386
+ }) : schema));
8387
+ var CodeLandingPreparedAuthorEnvelopeSchema = CodeLandingAuthorEnvelopeSchema.extend({
8388
+ assetRequests: z23.array(PreparedLandingAssetRequestSchema).max(MAX_CODE_LANDING_AUTHOR_ASSET_REQUESTS)
8389
+ });
8383
8390
  var CODE_LANDING_DEFERRED_AUTHOR_PROVIDER_RESULT_KIND = "code_landing_deferred_author_provider_v1";
8384
8391
  var CodeLandingDeferredAuthorProviderResultSchema = z23.object({
8385
8392
  kind: z23.literal(CODE_LANDING_DEFERRED_AUTHOR_PROVIDER_RESULT_KIND),
@@ -14140,6 +14147,10 @@ var StatusResponse = z49.object({
14140
14147
  /** Backend-owned guidance. Render verbatim instead of inspecting provider errors. */
14141
14148
  userMessage: z49.string().min(1).max(500).optional()
14142
14149
  }).optional(),
14150
+ /** Credits-cents handed back on this task: automatic error/expiry and
14151
+ * partial-delivery refunds from the ledger plus approved quality-report
14152
+ * refunds. Absent when nothing was refunded. Drives «+X créditos devueltos». */
14153
+ refundedCents: BillingNonNegativeAmount.optional(),
14143
14154
  /** Persisted target country (ISO 3166-1 alpha-2 or country name) used to pre-fill
14144
14155
  * campaign target geo when creating Meta ads from this task. */
14145
14156
  targetCountry: z49.string().optional(),
@@ -14381,6 +14392,9 @@ var TaskSummary = z49.object({
14381
14392
  * normal users. Multiply by CREDITS_PER_USD_CENT on the client to render
14382
14393
  * credits. */
14383
14394
  spentCents: BillingNonNegativeAmount.optional(),
14395
+ /** Credits-cents handed back on this task (same source as
14396
+ * `StatusResponse.refundedCents`), for every viewer. Absent when 0. */
14397
+ refundedCents: BillingNonNegativeAmount.optional(),
14384
14398
  product: z49.object({
14385
14399
  /** Absent for tasks with no scraped Product (free-mode video): those
14386
14400
  * carry only a thumbnail. Clients fall back to a "pending" label. */
@@ -18421,6 +18435,47 @@ var LandingRegenerationStatusResponse = z75.object({
18421
18435
  updatedAt: z75.string().datetime()
18422
18436
  });
18423
18437
 
18438
+ // ../../packages/types/src/classic-landing-selection.ts
18439
+ function buildClassicLandingSelection(document, gifs, selectedIds, images) {
18440
+ const imageChoices = new Map(images.map((image2) => [
18441
+ classicLandingBlockId({
18442
+ kind: "image",
18443
+ ownerTaskId: image2.ownerTaskId,
18444
+ sourceIndex: image2.sourceIndex
18445
+ }),
18446
+ image2
18447
+ ]));
18448
+ let imageCount = 0;
18449
+ const blocks = document.blocks.filter((block) => {
18450
+ if (block.kind === "image") return Boolean(imageChoices.get(classicLandingBlockId(block))?.selected) && imageCount++ < SHOPIFY_MAX_LANDING_IMAGES;
18451
+ const gif = gifs.find((item) => item.gifId === block.gifId && item.ownerTaskId === block.ownerTaskId);
18452
+ return selectedIds.includes(classicLandingBlockId(block)) && Boolean(gif && isDeliverableLandingGif(gif));
18453
+ });
18454
+ return {
18455
+ documentRevision: document.revision,
18456
+ blocks,
18457
+ gifs: blocks.flatMap((block) => block.kind === "gif" ? [
18458
+ {
18459
+ ownerTaskId: block.ownerTaskId,
18460
+ gifId: block.gifId,
18461
+ gifRevision: gifs.find((item) => item.gifId === block.gifId).gifRevision
18462
+ }
18463
+ ] : []),
18464
+ images: blocks.flatMap((block) => {
18465
+ if (block.kind !== "image") return [];
18466
+ const choice = imageChoices.get(classicLandingBlockId(block));
18467
+ return [
18468
+ {
18469
+ ownerTaskId: choice.ownerTaskId,
18470
+ sourceIndex: choice.sourceIndex,
18471
+ variantIndex: choice.variantIndex
18472
+ }
18473
+ ];
18474
+ })
18475
+ };
18476
+ }
18477
+ __name(buildClassicLandingSelection, "buildClassicLandingSelection");
18478
+
18424
18479
  // ../../packages/types/src/shopify-meta-tracking.ts
18425
18480
  import * as z76 from "zod";
18426
18481
  var MetaTrackingOwnerSchema = z76.enum([
@@ -24873,7 +24928,7 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
24873
24928
  ], [
24874
24929
  "meta_publication"
24875
24930
  ]),
24876
- defineCapability("publish_to_shopify", "00c4c7af09bb45a72896118ca154a2dc9518f05d74bb9386c825be541fa10fdb", "external_mutation", "publishing", [
24931
+ defineCapability("publish_to_shopify", "655a4e7e2d57493c38972ae31f7fcd0bba154abc7e42f30a74151253cedf59d3", "external_mutation", "publishing", [
24877
24932
  "external_mutation",
24878
24933
  "state_change"
24879
24934
  ], [
@@ -24883,7 +24938,11 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
24883
24938
  slot("shop", true, [
24884
24939
  "shopDomain"
24885
24940
  ]),
24941
+ // WhatsApp's publish schema has no GIF opt-out; only MCP exposes it.
24886
24942
  slot("landing_selection", false, [
24943
+ "numLandingImages",
24944
+ "includeLandingGif"
24945
+ ], [
24887
24946
  "numLandingImages"
24888
24947
  ]),
24889
24948
  slot("variant_selection", false, [
@@ -31738,7 +31797,7 @@ var ApiClient = class {
31738
31797
  };
31739
31798
 
31740
31799
  // src/index.ts
31741
- var MCP_VERSION = "12.7.0";
31800
+ var MCP_VERSION = "12.8.1";
31742
31801
 
31743
31802
  // src/meta-upload-app.ts
31744
31803
  import { readFileSync } from "fs";
@@ -39235,6 +39294,9 @@ var ShopifyInput = z149.object({
39235
39294
  /** Add an announcement bar text above add-to-cart (optional). */
39236
39295
  announcementBarEnabled: z149.boolean().optional(),
39237
39296
  announcementBarText: z149.string().trim().min(1).max(SHOPIFY_ANNOUNCEMENT_BAR_MAX_LENGTH).optional(),
39297
+ /** Default true: publish the task's QA-passed landing GIF, as the web
39298
+ * editor does when the seller keeps it selected. false omits it. */
39299
+ includeLandingGif: z149.boolean().optional(),
39238
39300
  commerceCorrection: PublishCommerceCorrectionSchema.optional().describe("Complete seller-confirmed commerce values, with the revision and fingerprint returned in recovery. Never invent missing prices or convert currency."),
39239
39301
  confirmed: z149.boolean().optional().describe("Required as true only when submitting commerceCorrection, after showing its values and obtaining explicit seller confirmation.")
39240
39302
  });
@@ -39541,7 +39603,7 @@ var TikTokInput = z149.object({
39541
39603
  function registerPublishTools(server, api, opts = {}) {
39542
39604
  const transport = opts.transport ?? "http";
39543
39605
  server.registerTool("publish_to_shopify", {
39544
- description: publishToShopifyGuidance.summary + " Precondici\xF3n: la tarea debe estar en status='complete' \u2014 usa `get_task_status` antes. Por defecto `publicationMode=auto`: si esta tarea ya fue publicada en esa tienda, actualiza ESE MISMO producto y conserva URL, precio, variantes, inventario y SKU; si no, crea uno. Usa `existing` + `targetProductId` cuando el usuario elija otro producto y `new` s\xF3lo si pide crear uno nuevo. ANTES de llamar preg\xFAntale \"\xBFTu producto tiene variantes (talla, color, etc.)?\" y pasa `showVariantPicker: true` si responde que s\xED. Por defecto publica TODAS las im\xE1genes generadas; pasa `numLandingImages` s\xF3lo si el usuario pide menos. En code landings con una oferta expl\xEDcita de packs, ProductMaker s\xF3lo publica el selector si variantes reales de Shopify cobran los totales exactos; la respuesta explica si las cre\xF3, verific\xF3 o descart\xF3 por no existir ahorro. Si devuelve SHOPIFY_PUBLISH_INPUT_REQUIRED, muestra todos los problemas de recovery y solicita los importes faltantes. Solo env\xEDa commerceCorrection con su revisi\xF3n/huella y confirmed:true despu\xE9s de confirmar los valores exactos; nunca inventes importes, conviertas moneda ni elimines ofertas. Si adem\xE1s trae correctionRejected, lo que el vendedor confirm\xF3 fue rechazado: l\xE9ele `reason` ANTES de volver a pedir datos. Un contexto obsoleto requiere volver a revisar; open_editor (incluido SHOPIFY_PUBLISH_CORRECTION_NOT_CONVERGED) requiere el editor de ProductMaker y NO se reenv\xEDa la misma correcci\xF3n. SHOPIFY_PUBLISH_VERIFICATION_FAILED trae en `reason` el siguiente paso: reintenta una vez y si se repite sigue ese paso. Si devuelve SHOPIFY_THEME_SETUP_REQUIRED, el tema de la tienda no tiene el bloque Code Landing y nada se public\xF3: env\xEDa al usuario el themeEditorDeepLink del error para abrir el editor del tema de Shopify, p\xEDdele que agregue el bloque y pulse Guardar (una sola vez por tienda), y reintenta s\xF3lo despu\xE9s. Ante resultado incierto, verifica la tarea/tienda antes de volver a publicar, especialmente con new; commerceCorrectionSaved no prueba publicaci\xF3n. La respuesta es markdown con el link a la landing y al producto.",
39606
+ description: publishToShopifyGuidance.summary + " Precondici\xF3n: la tarea debe estar en status='complete' \u2014 usa `get_task_status` antes. Por defecto `publicationMode=auto`: si esta tarea ya fue publicada en esa tienda, actualiza ESE MISMO producto y conserva URL, precio, variantes, inventario y SKU; si no, crea uno. Usa `existing` + `targetProductId` cuando el usuario elija otro producto y `new` s\xF3lo si pide crear uno nuevo. ANTES de llamar preg\xFAntale \"\xBFTu producto tiene variantes (talla, color, etc.)?\" y pasa `showVariantPicker: true` si responde que s\xED. Por defecto publica TODAS las im\xE1genes generadas; pasa `numLandingImages` s\xF3lo si el usuario pide menos. Tambi\xE9n publica el GIF de la landing si la tarea tiene uno aprobado por QA; pasa `includeLandingGif: false` s\xF3lo si el usuario no lo quiere. En code landings con una oferta expl\xEDcita de packs, ProductMaker s\xF3lo publica el selector si variantes reales de Shopify cobran los totales exactos; la respuesta explica si las cre\xF3, verific\xF3 o descart\xF3 por no existir ahorro. Si devuelve SHOPIFY_PUBLISH_INPUT_REQUIRED, muestra todos los problemas de recovery y solicita los importes faltantes. Solo env\xEDa commerceCorrection con su revisi\xF3n/huella y confirmed:true despu\xE9s de confirmar los valores exactos; nunca inventes importes, conviertas moneda ni elimines ofertas. Si adem\xE1s trae correctionRejected, lo que el vendedor confirm\xF3 fue rechazado: l\xE9ele `reason` ANTES de volver a pedir datos. Un contexto obsoleto requiere volver a revisar; open_editor (incluido SHOPIFY_PUBLISH_CORRECTION_NOT_CONVERGED) requiere el editor de ProductMaker y NO se reenv\xEDa la misma correcci\xF3n. SHOPIFY_PUBLISH_VERIFICATION_FAILED trae en `reason` el siguiente paso: reintenta una vez y si se repite sigue ese paso. Si devuelve SHOPIFY_THEME_SETUP_REQUIRED, el tema de la tienda no tiene el bloque Code Landing y nada se public\xF3: env\xEDa al usuario el themeEditorDeepLink del error para abrir el editor del tema de Shopify, p\xEDdele que agregue el bloque y pulse Guardar (una sola vez por tienda), y reintenta s\xF3lo despu\xE9s. Ante resultado incierto, verifica la tarea/tienda antes de volver a publicar, especialmente con new; commerceCorrectionSaved no prueba publicaci\xF3n. La respuesta es markdown con el link a la landing y al producto.",
39545
39607
  inputSchema: ShopifyInput.shape,
39546
39608
  // Publication reaches the public storefront, beyond private account data.
39547
39609
  annotations: {
@@ -39563,10 +39625,26 @@ function registerPublishTools(server, api, opts = {}) {
39563
39625
  };
39564
39626
  }
39565
39627
  const variantIndex = parsed.variantIndex ?? 0;
39566
- const availableImages = await fetchLandingImageCount(api, parsed.taskId);
39628
+ const defaults = await fetchLandingPublishDefaults(api, parsed.taskId);
39629
+ const includeLandingGif = parsed.includeLandingGif !== false;
39630
+ if (!defaults && includeLandingGif) {
39631
+ return {
39632
+ isError: true,
39633
+ content: [
39634
+ {
39635
+ type: "text",
39636
+ text: "No pude leer los resultados de la tarea para incluir su GIF; nada se public\xF3. Reintenta, o pasa includeLandingGif: false para publicar sin GIF."
39637
+ }
39638
+ ]
39639
+ };
39640
+ }
39641
+ const availableImages = defaults?.imageCount ?? 0;
39642
+ const selectedLandingGif = includeLandingGif ? defaults?.gif ?? null : null;
39567
39643
  const effectiveAvailable = availableImages > 0 ? availableImages : 3;
39568
39644
  const numImages = Math.min(parsed.numLandingImages ?? effectiveAvailable, effectiveAvailable, SHOPIFY_MAX_LANDING_IMAGES);
39569
- const selectedLandingImages = Array.from({
39645
+ const classic = includeLandingGif && defaults && variantIndex === 0 ? buildMcpClassicSelection(defaults, numImages) : null;
39646
+ const classicSelection = classic?.selection ?? null;
39647
+ const selectedLandingImages = classic?.images ?? Array.from({
39570
39648
  length: numImages
39571
39649
  }, (_, i) => ({
39572
39650
  imageIndex: i,
@@ -39580,6 +39658,15 @@ function registerPublishTools(server, api, opts = {}) {
39580
39658
  }),
39581
39659
  shopDomain: parsed.shopDomain,
39582
39660
  selectedLandingImages,
39661
+ // Classic block landings publish their GIF blocks through the
39662
+ // ordered document, exactly like the web editor; the legacy
39663
+ // primary GIF only rides selectedLandingGif.
39664
+ ...classicSelection ? {
39665
+ classicSelection,
39666
+ selectedLandingGif: null
39667
+ } : selectedLandingGif ? {
39668
+ selectedLandingGif
39669
+ } : {},
39583
39670
  showVariantPicker: parsed.showVariantPicker,
39584
39671
  publicationMode: parsed.publicationMode,
39585
39672
  targetProductId: parsed.targetProductId,
@@ -39867,19 +39954,70 @@ function renderBundleMappingMarkdown(mapping) {
39867
39954
  }
39868
39955
  }
39869
39956
  __name(renderBundleMappingMarkdown, "renderBundleMappingMarkdown");
39870
- async function fetchLandingImageCount(api, taskId) {
39957
+ async function fetchLandingPublishDefaults(api, taskId) {
39871
39958
  try {
39872
39959
  const r = await api.getJson(`/v1/results/${encodeURIComponent(taskId)}`);
39873
- if (!isRecord3(r)) return 0;
39874
- const landing = r.landing;
39875
- if (!isRecord3(landing)) return 0;
39876
- const images = landing.images;
39877
- return Array.isArray(images) ? images.length : 0;
39960
+ if (!isRecord3(r) || !isRecord3(r.landing)) return {
39961
+ imageCount: 0,
39962
+ images: [],
39963
+ gif: null,
39964
+ classic: null
39965
+ };
39966
+ const rawImages = Array.isArray(r.landing.images) ? r.landing.images : [];
39967
+ const images = rawImages.map((image2) => {
39968
+ const record = isRecord3(image2) ? image2 : {};
39969
+ return {
39970
+ ownerTaskId: typeof record.variantTaskId === "string" ? record.variantTaskId : taskId,
39971
+ sourceIndex: Number(record.sourceIndex ?? record.index)
39972
+ };
39973
+ });
39974
+ const demo = LandingGifReadableSchema.safeParse(r.landing.demoGif);
39975
+ const gif = demo.success && demo.data.ownerTaskId === taskId && demo.data.quality.verdict === "passed" ? {
39976
+ ownerTaskId: taskId,
39977
+ gifRevision: demo.data.gifRevision
39978
+ } : null;
39979
+ const document = ClassicLandingDocumentSchema.safeParse(r.landing.classicDocument);
39980
+ const gifs = (Array.isArray(r.landing.gifBlocks) ? r.landing.gifBlocks : []).flatMap((entry) => {
39981
+ const parsed = LandingGifItemSchema.safeParse(entry);
39982
+ return parsed.success ? [
39983
+ parsed.data
39984
+ ] : [];
39985
+ });
39986
+ return {
39987
+ imageCount: rawImages.length,
39988
+ images,
39989
+ gif,
39990
+ classic: document.success ? {
39991
+ document: document.data,
39992
+ gifs
39993
+ } : null
39994
+ };
39878
39995
  } catch {
39879
- return 0;
39996
+ return null;
39880
39997
  }
39881
39998
  }
39882
- __name(fetchLandingImageCount, "fetchLandingImageCount");
39999
+ __name(fetchLandingPublishDefaults, "fetchLandingPublishDefaults");
40000
+ function buildMcpClassicSelection(defaults, numImages) {
40001
+ if (!defaults.classic) return null;
40002
+ const { document, gifs } = defaults.classic;
40003
+ const gifBlockIds = document.blocks.flatMap((block) => block.kind === "gif" && gifs.some((item) => item.gifId === block.gifId && item.ownerTaskId === block.ownerTaskId && isDeliverableLandingGif(item)) ? [
40004
+ classicLandingBlockId(block)
40005
+ ] : []);
40006
+ if (gifBlockIds.length === 0) return null;
40007
+ const selection = buildClassicLandingSelection(document, gifs, gifBlockIds, defaults.images.map((image2, arrayIndex) => ({
40008
+ ...image2,
40009
+ selected: arrayIndex < numImages,
40010
+ variantIndex: 0
40011
+ })));
40012
+ return {
40013
+ selection,
40014
+ images: (selection.images ?? []).map((selected) => ({
40015
+ imageIndex: defaults.images.findIndex((image2) => image2.ownerTaskId === selected.ownerTaskId && image2.sourceIndex === selected.sourceIndex),
40016
+ variantIndex: selected.variantIndex
40017
+ }))
40018
+ };
40019
+ }
40020
+ __name(buildMcpClassicSelection, "buildMcpClassicSelection");
39883
40021
  function toMetaCampaignOverrides(cfg) {
39884
40022
  const out = {};
39885
40023
  if (cfg.dailyBudgetUsd !== void 0) out.dailyBudgetUsd = cfg.dailyBudgetUsd;
package/dist/stdio.js CHANGED
@@ -8127,6 +8127,7 @@ var LandingAssetRequestSchema = z23.discriminatedUnion("kind", [
8127
8127
  var CodeLandingTypographySchema = z23.object({
8128
8128
  googleFamilies: z23.array(z23.string().regex(/^[A-Za-z0-9 ]+(:wght@[0-9;]+)?$/)).max(4)
8129
8129
  }).strict();
8130
+ var MAX_CODE_LANDING_AUTHOR_ASSET_REQUESTS = 96;
8130
8131
  var CodeLandingAuthorEnvelopeSchema = z23.object({
8131
8132
  mode: LandingDocumentModeSchema,
8132
8133
  document: CodeLandingDocumentSchema,
@@ -8134,7 +8135,7 @@ var CodeLandingAuthorEnvelopeSchema = z23.object({
8134
8135
  anchors: z23.array(CodeLandingAnchorSchema).min(1).max(64),
8135
8136
  components: z23.array(ComponentManifestEntrySchema).max(64),
8136
8137
  interactions: z23.array(InteractionProgramSchema).max(64),
8137
- assetRequests: z23.array(LandingAssetRequestSchema).max(96),
8138
+ assetRequests: z23.array(LandingAssetRequestSchema).max(MAX_CODE_LANDING_AUTHOR_ASSET_REQUESTS),
8138
8139
  /** Optional: only the block library authoring path (compose.ts) sets this. */
8139
8140
  typography: CodeLandingTypographySchema.optional(),
8140
8141
  /**
@@ -8155,6 +8156,12 @@ var CodeLandingAuthorEnvelopeSchema = z23.object({
8155
8156
  */
8156
8157
  variantFacts: z23.array(LandingVariantFactSchema).max(16).optional()
8157
8158
  });
8159
+ var PreparedLandingAssetRequestSchema = z23.union(LandingAssetRequestSchema.options.map((schema) => schema.shape.kind.value === "generate" || schema.shape.kind.value === "adapt_reference" ? schema.extend({
8160
+ prompt: z23.string().min(1)
8161
+ }) : schema));
8162
+ var CodeLandingPreparedAuthorEnvelopeSchema = CodeLandingAuthorEnvelopeSchema.extend({
8163
+ assetRequests: z23.array(PreparedLandingAssetRequestSchema).max(MAX_CODE_LANDING_AUTHOR_ASSET_REQUESTS)
8164
+ });
8158
8165
  var CODE_LANDING_DEFERRED_AUTHOR_PROVIDER_RESULT_KIND = "code_landing_deferred_author_provider_v1";
8159
8166
  var CodeLandingDeferredAuthorProviderResultSchema = z23.object({
8160
8167
  kind: z23.literal(CODE_LANDING_DEFERRED_AUTHOR_PROVIDER_RESULT_KIND),
@@ -13915,6 +13922,10 @@ var StatusResponse = z49.object({
13915
13922
  /** Backend-owned guidance. Render verbatim instead of inspecting provider errors. */
13916
13923
  userMessage: z49.string().min(1).max(500).optional()
13917
13924
  }).optional(),
13925
+ /** Credits-cents handed back on this task: automatic error/expiry and
13926
+ * partial-delivery refunds from the ledger plus approved quality-report
13927
+ * refunds. Absent when nothing was refunded. Drives «+X créditos devueltos». */
13928
+ refundedCents: BillingNonNegativeAmount.optional(),
13918
13929
  /** Persisted target country (ISO 3166-1 alpha-2 or country name) used to pre-fill
13919
13930
  * campaign target geo when creating Meta ads from this task. */
13920
13931
  targetCountry: z49.string().optional(),
@@ -14156,6 +14167,9 @@ var TaskSummary = z49.object({
14156
14167
  * normal users. Multiply by CREDITS_PER_USD_CENT on the client to render
14157
14168
  * credits. */
14158
14169
  spentCents: BillingNonNegativeAmount.optional(),
14170
+ /** Credits-cents handed back on this task (same source as
14171
+ * `StatusResponse.refundedCents`), for every viewer. Absent when 0. */
14172
+ refundedCents: BillingNonNegativeAmount.optional(),
14159
14173
  product: z49.object({
14160
14174
  /** Absent for tasks with no scraped Product (free-mode video): those
14161
14175
  * carry only a thumbnail. Clients fall back to a "pending" label. */
@@ -18196,6 +18210,47 @@ var LandingRegenerationStatusResponse = z75.object({
18196
18210
  updatedAt: z75.string().datetime()
18197
18211
  });
18198
18212
 
18213
+ // ../../packages/types/src/classic-landing-selection.ts
18214
+ function buildClassicLandingSelection(document, gifs, selectedIds, images) {
18215
+ const imageChoices = new Map(images.map((image2) => [
18216
+ classicLandingBlockId({
18217
+ kind: "image",
18218
+ ownerTaskId: image2.ownerTaskId,
18219
+ sourceIndex: image2.sourceIndex
18220
+ }),
18221
+ image2
18222
+ ]));
18223
+ let imageCount = 0;
18224
+ const blocks = document.blocks.filter((block) => {
18225
+ if (block.kind === "image") return Boolean(imageChoices.get(classicLandingBlockId(block))?.selected) && imageCount++ < SHOPIFY_MAX_LANDING_IMAGES;
18226
+ const gif = gifs.find((item) => item.gifId === block.gifId && item.ownerTaskId === block.ownerTaskId);
18227
+ return selectedIds.includes(classicLandingBlockId(block)) && Boolean(gif && isDeliverableLandingGif(gif));
18228
+ });
18229
+ return {
18230
+ documentRevision: document.revision,
18231
+ blocks,
18232
+ gifs: blocks.flatMap((block) => block.kind === "gif" ? [
18233
+ {
18234
+ ownerTaskId: block.ownerTaskId,
18235
+ gifId: block.gifId,
18236
+ gifRevision: gifs.find((item) => item.gifId === block.gifId).gifRevision
18237
+ }
18238
+ ] : []),
18239
+ images: blocks.flatMap((block) => {
18240
+ if (block.kind !== "image") return [];
18241
+ const choice = imageChoices.get(classicLandingBlockId(block));
18242
+ return [
18243
+ {
18244
+ ownerTaskId: choice.ownerTaskId,
18245
+ sourceIndex: choice.sourceIndex,
18246
+ variantIndex: choice.variantIndex
18247
+ }
18248
+ ];
18249
+ })
18250
+ };
18251
+ }
18252
+ __name(buildClassicLandingSelection, "buildClassicLandingSelection");
18253
+
18199
18254
  // ../../packages/types/src/shopify-meta-tracking.ts
18200
18255
  import * as z76 from "zod";
18201
18256
  var MetaTrackingOwnerSchema = z76.enum([
@@ -24648,7 +24703,7 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
24648
24703
  ], [
24649
24704
  "meta_publication"
24650
24705
  ]),
24651
- defineCapability("publish_to_shopify", "00c4c7af09bb45a72896118ca154a2dc9518f05d74bb9386c825be541fa10fdb", "external_mutation", "publishing", [
24706
+ defineCapability("publish_to_shopify", "655a4e7e2d57493c38972ae31f7fcd0bba154abc7e42f30a74151253cedf59d3", "external_mutation", "publishing", [
24652
24707
  "external_mutation",
24653
24708
  "state_change"
24654
24709
  ], [
@@ -24658,7 +24713,11 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
24658
24713
  slot("shop", true, [
24659
24714
  "shopDomain"
24660
24715
  ]),
24716
+ // WhatsApp's publish schema has no GIF opt-out; only MCP exposes it.
24661
24717
  slot("landing_selection", false, [
24718
+ "numLandingImages",
24719
+ "includeLandingGif"
24720
+ ], [
24662
24721
  "numLandingImages"
24663
24722
  ]),
24664
24723
  slot("variant_selection", false, [
@@ -30065,7 +30124,7 @@ var ApiClient = class {
30065
30124
  };
30066
30125
 
30067
30126
  // src/index.ts
30068
- var MCP_VERSION = "12.7.0";
30127
+ var MCP_VERSION = "12.8.1";
30069
30128
 
30070
30129
  // src/meta-upload-app.ts
30071
30130
  import { readFileSync } from "fs";
@@ -37720,6 +37779,9 @@ var ShopifyInput = z148.object({
37720
37779
  /** Add an announcement bar text above add-to-cart (optional). */
37721
37780
  announcementBarEnabled: z148.boolean().optional(),
37722
37781
  announcementBarText: z148.string().trim().min(1).max(SHOPIFY_ANNOUNCEMENT_BAR_MAX_LENGTH).optional(),
37782
+ /** Default true: publish the task's QA-passed landing GIF, as the web
37783
+ * editor does when the seller keeps it selected. false omits it. */
37784
+ includeLandingGif: z148.boolean().optional(),
37723
37785
  commerceCorrection: PublishCommerceCorrectionSchema.optional().describe("Complete seller-confirmed commerce values, with the revision and fingerprint returned in recovery. Never invent missing prices or convert currency."),
37724
37786
  confirmed: z148.boolean().optional().describe("Required as true only when submitting commerceCorrection, after showing its values and obtaining explicit seller confirmation.")
37725
37787
  });
@@ -38026,7 +38088,7 @@ var TikTokInput = z148.object({
38026
38088
  function registerPublishTools(server, api, opts = {}) {
38027
38089
  const transport = opts.transport ?? "http";
38028
38090
  server.registerTool("publish_to_shopify", {
38029
- description: publishToShopifyGuidance.summary + " Precondici\xF3n: la tarea debe estar en status='complete' \u2014 usa `get_task_status` antes. Por defecto `publicationMode=auto`: si esta tarea ya fue publicada en esa tienda, actualiza ESE MISMO producto y conserva URL, precio, variantes, inventario y SKU; si no, crea uno. Usa `existing` + `targetProductId` cuando el usuario elija otro producto y `new` s\xF3lo si pide crear uno nuevo. ANTES de llamar preg\xFAntale \"\xBFTu producto tiene variantes (talla, color, etc.)?\" y pasa `showVariantPicker: true` si responde que s\xED. Por defecto publica TODAS las im\xE1genes generadas; pasa `numLandingImages` s\xF3lo si el usuario pide menos. En code landings con una oferta expl\xEDcita de packs, ProductMaker s\xF3lo publica el selector si variantes reales de Shopify cobran los totales exactos; la respuesta explica si las cre\xF3, verific\xF3 o descart\xF3 por no existir ahorro. Si devuelve SHOPIFY_PUBLISH_INPUT_REQUIRED, muestra todos los problemas de recovery y solicita los importes faltantes. Solo env\xEDa commerceCorrection con su revisi\xF3n/huella y confirmed:true despu\xE9s de confirmar los valores exactos; nunca inventes importes, conviertas moneda ni elimines ofertas. Si adem\xE1s trae correctionRejected, lo que el vendedor confirm\xF3 fue rechazado: l\xE9ele `reason` ANTES de volver a pedir datos. Un contexto obsoleto requiere volver a revisar; open_editor (incluido SHOPIFY_PUBLISH_CORRECTION_NOT_CONVERGED) requiere el editor de ProductMaker y NO se reenv\xEDa la misma correcci\xF3n. SHOPIFY_PUBLISH_VERIFICATION_FAILED trae en `reason` el siguiente paso: reintenta una vez y si se repite sigue ese paso. Si devuelve SHOPIFY_THEME_SETUP_REQUIRED, el tema de la tienda no tiene el bloque Code Landing y nada se public\xF3: env\xEDa al usuario el themeEditorDeepLink del error para abrir el editor del tema de Shopify, p\xEDdele que agregue el bloque y pulse Guardar (una sola vez por tienda), y reintenta s\xF3lo despu\xE9s. Ante resultado incierto, verifica la tarea/tienda antes de volver a publicar, especialmente con new; commerceCorrectionSaved no prueba publicaci\xF3n. La respuesta es markdown con el link a la landing y al producto.",
38091
+ description: publishToShopifyGuidance.summary + " Precondici\xF3n: la tarea debe estar en status='complete' \u2014 usa `get_task_status` antes. Por defecto `publicationMode=auto`: si esta tarea ya fue publicada en esa tienda, actualiza ESE MISMO producto y conserva URL, precio, variantes, inventario y SKU; si no, crea uno. Usa `existing` + `targetProductId` cuando el usuario elija otro producto y `new` s\xF3lo si pide crear uno nuevo. ANTES de llamar preg\xFAntale \"\xBFTu producto tiene variantes (talla, color, etc.)?\" y pasa `showVariantPicker: true` si responde que s\xED. Por defecto publica TODAS las im\xE1genes generadas; pasa `numLandingImages` s\xF3lo si el usuario pide menos. Tambi\xE9n publica el GIF de la landing si la tarea tiene uno aprobado por QA; pasa `includeLandingGif: false` s\xF3lo si el usuario no lo quiere. En code landings con una oferta expl\xEDcita de packs, ProductMaker s\xF3lo publica el selector si variantes reales de Shopify cobran los totales exactos; la respuesta explica si las cre\xF3, verific\xF3 o descart\xF3 por no existir ahorro. Si devuelve SHOPIFY_PUBLISH_INPUT_REQUIRED, muestra todos los problemas de recovery y solicita los importes faltantes. Solo env\xEDa commerceCorrection con su revisi\xF3n/huella y confirmed:true despu\xE9s de confirmar los valores exactos; nunca inventes importes, conviertas moneda ni elimines ofertas. Si adem\xE1s trae correctionRejected, lo que el vendedor confirm\xF3 fue rechazado: l\xE9ele `reason` ANTES de volver a pedir datos. Un contexto obsoleto requiere volver a revisar; open_editor (incluido SHOPIFY_PUBLISH_CORRECTION_NOT_CONVERGED) requiere el editor de ProductMaker y NO se reenv\xEDa la misma correcci\xF3n. SHOPIFY_PUBLISH_VERIFICATION_FAILED trae en `reason` el siguiente paso: reintenta una vez y si se repite sigue ese paso. Si devuelve SHOPIFY_THEME_SETUP_REQUIRED, el tema de la tienda no tiene el bloque Code Landing y nada se public\xF3: env\xEDa al usuario el themeEditorDeepLink del error para abrir el editor del tema de Shopify, p\xEDdele que agregue el bloque y pulse Guardar (una sola vez por tienda), y reintenta s\xF3lo despu\xE9s. Ante resultado incierto, verifica la tarea/tienda antes de volver a publicar, especialmente con new; commerceCorrectionSaved no prueba publicaci\xF3n. La respuesta es markdown con el link a la landing y al producto.",
38030
38092
  inputSchema: ShopifyInput.shape,
38031
38093
  // Publication reaches the public storefront, beyond private account data.
38032
38094
  annotations: {
@@ -38048,10 +38110,26 @@ function registerPublishTools(server, api, opts = {}) {
38048
38110
  };
38049
38111
  }
38050
38112
  const variantIndex = parsed.variantIndex ?? 0;
38051
- const availableImages = await fetchLandingImageCount(api, parsed.taskId);
38113
+ const defaults = await fetchLandingPublishDefaults(api, parsed.taskId);
38114
+ const includeLandingGif = parsed.includeLandingGif !== false;
38115
+ if (!defaults && includeLandingGif) {
38116
+ return {
38117
+ isError: true,
38118
+ content: [
38119
+ {
38120
+ type: "text",
38121
+ text: "No pude leer los resultados de la tarea para incluir su GIF; nada se public\xF3. Reintenta, o pasa includeLandingGif: false para publicar sin GIF."
38122
+ }
38123
+ ]
38124
+ };
38125
+ }
38126
+ const availableImages = defaults?.imageCount ?? 0;
38127
+ const selectedLandingGif = includeLandingGif ? defaults?.gif ?? null : null;
38052
38128
  const effectiveAvailable = availableImages > 0 ? availableImages : 3;
38053
38129
  const numImages = Math.min(parsed.numLandingImages ?? effectiveAvailable, effectiveAvailable, SHOPIFY_MAX_LANDING_IMAGES);
38054
- const selectedLandingImages = Array.from({
38130
+ const classic = includeLandingGif && defaults && variantIndex === 0 ? buildMcpClassicSelection(defaults, numImages) : null;
38131
+ const classicSelection = classic?.selection ?? null;
38132
+ const selectedLandingImages = classic?.images ?? Array.from({
38055
38133
  length: numImages
38056
38134
  }, (_, i) => ({
38057
38135
  imageIndex: i,
@@ -38065,6 +38143,15 @@ function registerPublishTools(server, api, opts = {}) {
38065
38143
  }),
38066
38144
  shopDomain: parsed.shopDomain,
38067
38145
  selectedLandingImages,
38146
+ // Classic block landings publish their GIF blocks through the
38147
+ // ordered document, exactly like the web editor; the legacy
38148
+ // primary GIF only rides selectedLandingGif.
38149
+ ...classicSelection ? {
38150
+ classicSelection,
38151
+ selectedLandingGif: null
38152
+ } : selectedLandingGif ? {
38153
+ selectedLandingGif
38154
+ } : {},
38068
38155
  showVariantPicker: parsed.showVariantPicker,
38069
38156
  publicationMode: parsed.publicationMode,
38070
38157
  targetProductId: parsed.targetProductId,
@@ -38352,19 +38439,70 @@ function renderBundleMappingMarkdown(mapping) {
38352
38439
  }
38353
38440
  }
38354
38441
  __name(renderBundleMappingMarkdown, "renderBundleMappingMarkdown");
38355
- async function fetchLandingImageCount(api, taskId) {
38442
+ async function fetchLandingPublishDefaults(api, taskId) {
38356
38443
  try {
38357
38444
  const r = await api.getJson(`/v1/results/${encodeURIComponent(taskId)}`);
38358
- if (!isRecord3(r)) return 0;
38359
- const landing = r.landing;
38360
- if (!isRecord3(landing)) return 0;
38361
- const images = landing.images;
38362
- return Array.isArray(images) ? images.length : 0;
38445
+ if (!isRecord3(r) || !isRecord3(r.landing)) return {
38446
+ imageCount: 0,
38447
+ images: [],
38448
+ gif: null,
38449
+ classic: null
38450
+ };
38451
+ const rawImages = Array.isArray(r.landing.images) ? r.landing.images : [];
38452
+ const images = rawImages.map((image2) => {
38453
+ const record = isRecord3(image2) ? image2 : {};
38454
+ return {
38455
+ ownerTaskId: typeof record.variantTaskId === "string" ? record.variantTaskId : taskId,
38456
+ sourceIndex: Number(record.sourceIndex ?? record.index)
38457
+ };
38458
+ });
38459
+ const demo = LandingGifReadableSchema.safeParse(r.landing.demoGif);
38460
+ const gif = demo.success && demo.data.ownerTaskId === taskId && demo.data.quality.verdict === "passed" ? {
38461
+ ownerTaskId: taskId,
38462
+ gifRevision: demo.data.gifRevision
38463
+ } : null;
38464
+ const document = ClassicLandingDocumentSchema.safeParse(r.landing.classicDocument);
38465
+ const gifs = (Array.isArray(r.landing.gifBlocks) ? r.landing.gifBlocks : []).flatMap((entry) => {
38466
+ const parsed = LandingGifItemSchema.safeParse(entry);
38467
+ return parsed.success ? [
38468
+ parsed.data
38469
+ ] : [];
38470
+ });
38471
+ return {
38472
+ imageCount: rawImages.length,
38473
+ images,
38474
+ gif,
38475
+ classic: document.success ? {
38476
+ document: document.data,
38477
+ gifs
38478
+ } : null
38479
+ };
38363
38480
  } catch {
38364
- return 0;
38481
+ return null;
38365
38482
  }
38366
38483
  }
38367
- __name(fetchLandingImageCount, "fetchLandingImageCount");
38484
+ __name(fetchLandingPublishDefaults, "fetchLandingPublishDefaults");
38485
+ function buildMcpClassicSelection(defaults, numImages) {
38486
+ if (!defaults.classic) return null;
38487
+ const { document, gifs } = defaults.classic;
38488
+ const gifBlockIds = document.blocks.flatMap((block) => block.kind === "gif" && gifs.some((item) => item.gifId === block.gifId && item.ownerTaskId === block.ownerTaskId && isDeliverableLandingGif(item)) ? [
38489
+ classicLandingBlockId(block)
38490
+ ] : []);
38491
+ if (gifBlockIds.length === 0) return null;
38492
+ const selection = buildClassicLandingSelection(document, gifs, gifBlockIds, defaults.images.map((image2, arrayIndex) => ({
38493
+ ...image2,
38494
+ selected: arrayIndex < numImages,
38495
+ variantIndex: 0
38496
+ })));
38497
+ return {
38498
+ selection,
38499
+ images: (selection.images ?? []).map((selected) => ({
38500
+ imageIndex: defaults.images.findIndex((image2) => image2.ownerTaskId === selected.ownerTaskId && image2.sourceIndex === selected.sourceIndex),
38501
+ variantIndex: selected.variantIndex
38502
+ }))
38503
+ };
38504
+ }
38505
+ __name(buildMcpClassicSelection, "buildMcpClassicSelection");
38368
38506
  function toMetaCampaignOverrides(cfg) {
38369
38507
  const out = {};
38370
38508
  if (cfg.dailyBudgetUsd !== void 0) out.dailyBudgetUsd = cfg.dailyBudgetUsd;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@productmaker/mcp",
3
- "version": "12.7.0",
3
+ "version": "12.8.1",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "description": "ProductMaker MCP server — cost products, generate landing pages, UGC videos, and ad creatives, and publish to Shopify, Meta Ads, and TikTok Ads.",