@productmaker/mcp 2.9.3 → 2.9.4
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/http.js +18 -18
- package/dist/stdio.js +18 -18
- package/dist/ui/meta-upload-app.html +155 -30
- package/package.json +1 -1
package/dist/http.js
CHANGED
|
@@ -153,7 +153,7 @@ var ApiClient = class {
|
|
|
153
153
|
};
|
|
154
154
|
|
|
155
155
|
// src/index.ts
|
|
156
|
-
var MCP_VERSION = "2.9.
|
|
156
|
+
var MCP_VERSION = "2.9.4";
|
|
157
157
|
|
|
158
158
|
// src/meta-upload-app.ts
|
|
159
159
|
import { readFileSync } from "fs";
|
|
@@ -11386,8 +11386,7 @@ function uploadSessionText(result) {
|
|
|
11386
11386
|
text: [
|
|
11387
11387
|
`### Prepara tu ${mediaLabel} en ProductMaker`,
|
|
11388
11388
|
`Usa primero el App inline de este chat. Si tu host no soporta el bridge de archivos, usa este fallback: [Abrir carga segura](${result.uploadPageUrl})`,
|
|
11389
|
-
`Puedes seleccionar hasta ${result.maxFiles} ${result.mediaKind === "mixed" ? "archivos" : "archivo"}. Cuando
|
|
11390
|
-
`sessionId: \`${result.sessionId}\``,
|
|
11389
|
+
`Puedes seleccionar hasta ${result.maxFiles} ${result.mediaKind === "mixed" ? "archivos" : "archivo"}. Cuando termines, pulsa **OK** dentro del App para que ProductMaker contin\xFAe en este chat.`,
|
|
11391
11390
|
`Este enlace expira: ${result.expiresAt}`
|
|
11392
11391
|
].join("\n\n")
|
|
11393
11392
|
}
|
|
@@ -11398,7 +11397,7 @@ __name(uploadSessionText, "uploadSessionText");
|
|
|
11398
11397
|
function registerMetaUploadTools(server, api) {
|
|
11399
11398
|
server.registerTool("request_meta_media_upload", {
|
|
11400
11399
|
title: "Solicitar carga segura de Meta",
|
|
11401
|
-
description: "Inicia una carga segura desde la laptop del usuario para creativos propios de Meta. Por defecto crea una sesi\xF3n `mixed` de hasta diez archivos; tambi\xE9n admite una sola imagen o video. Si el usuario adjunta una foto o video en el mismo mensaje de ChatGPT, pasa esas referencias opcionales en `files` para que el MCP App inline las consuma dentro del chat. ProductMaker conserva `files` solo como metadata del UI bridge: NO los descarga y NO los reenv\xEDa. No pidas URL CDN, ruta local, bytes ni base64. El App es exclusivamente para cargar archivos: nunca debe pedir ni mostrar portafolio, cuenta publicitaria, p\xE1gina, IDs o JSON. El enlace externo de ProductMaker queda como fallback para hosts sin bridge de archivos.
|
|
11400
|
+
description: "Inicia una carga segura desde la laptop del usuario para creativos propios de Meta. Esta es la ruta DIRECTA para peticiones puntuales como \u2018sube esta imagen/video a Meta Ads\u2019 o \u2018publica este creativo\u2019; no conviertas esa intenci\xF3n en una generaci\xF3n completa de producto. No llames `guide_dropshipping_launch` para esta operaci\xF3n puntual. Por defecto crea una sesi\xF3n `mixed` de hasta diez archivos; tambi\xE9n admite una sola imagen o video. Si el usuario adjunta una foto o video en el mismo mensaje de ChatGPT, pasa esas referencias opcionales en `files` para que el MCP App inline las consuma dentro del chat. ProductMaker conserva `files` solo como metadata del UI bridge: NO los descarga y NO los reenv\xEDa. No pidas URL CDN, ruta local, bytes ni base64. El App es exclusivamente para cargar archivos: nunca debe pedir ni mostrar portafolio, cuenta publicitaria, p\xE1gina, IDs o JSON. El enlace externo de ProductMaker queda como fallback para hosts sin bridge de archivos. El usuario pulsa OK cuando termina; el App inserta en el chat los asset_id verificados para continuar la petici\xF3n anterior. Luego llama `list_meta_destinations` y pregunta en el chat qu\xE9 `portfolioId`, `adAccountId` y `pageId` desea usar. No muestres tokens, URLs temporales, `sessionId` ni un bloque JSON. Esta herramienta no publica ni consume cr\xE9ditos.",
|
|
11402
11401
|
inputSchema: MetaUploadSessionToolInputShape,
|
|
11403
11402
|
outputSchema: MetaUploadSessionResponse2,
|
|
11404
11403
|
_meta: {
|
|
@@ -12386,20 +12385,21 @@ var CostingInputSchema = z59.object({
|
|
|
12386
12385
|
]
|
|
12387
12386
|
});
|
|
12388
12387
|
});
|
|
12388
|
+
var FiniteCostingNumber = z59.number().finite();
|
|
12389
12389
|
var CostingResultSchema = z59.object({
|
|
12390
12390
|
country: z59.string().min(2).max(3),
|
|
12391
|
-
deliveryRate:
|
|
12392
|
-
adjustedShippingCost:
|
|
12393
|
-
adjustedCpa:
|
|
12394
|
-
totalCost:
|
|
12395
|
-
suggestedPrice:
|
|
12396
|
-
baseCommercialPrice:
|
|
12397
|
-
minimumSafePrice:
|
|
12398
|
-
commercialPrice:
|
|
12399
|
-
comparisonPrice:
|
|
12400
|
-
estimatedProfit:
|
|
12401
|
-
actualMargin:
|
|
12402
|
-
breakEvenAdsManagerCpa:
|
|
12391
|
+
deliveryRate: FiniteCostingNumber,
|
|
12392
|
+
adjustedShippingCost: FiniteCostingNumber,
|
|
12393
|
+
adjustedCpa: FiniteCostingNumber,
|
|
12394
|
+
totalCost: FiniteCostingNumber,
|
|
12395
|
+
suggestedPrice: FiniteCostingNumber,
|
|
12396
|
+
baseCommercialPrice: FiniteCostingNumber,
|
|
12397
|
+
minimumSafePrice: FiniteCostingNumber,
|
|
12398
|
+
commercialPrice: FiniteCostingNumber,
|
|
12399
|
+
comparisonPrice: FiniteCostingNumber,
|
|
12400
|
+
estimatedProfit: FiniteCostingNumber,
|
|
12401
|
+
actualMargin: FiniteCostingNumber,
|
|
12402
|
+
breakEvenAdsManagerCpa: FiniteCostingNumber,
|
|
12403
12403
|
wasPriceAutoAdjusted: z59.boolean(),
|
|
12404
12404
|
profitabilityStatus: z59.literal("healthy")
|
|
12405
12405
|
});
|
|
@@ -12488,13 +12488,13 @@ function registerDropshippingProjectTools(server, api) {
|
|
|
12488
12488
|
throw translateError(error);
|
|
12489
12489
|
}
|
|
12490
12490
|
});
|
|
12491
|
-
server.tool("guide_dropshipping_launch", "Gu\xEDa read-only
|
|
12491
|
+
server.tool("guide_dropshipping_launch", "Gu\xEDa read-only de un recorrido COMPLETO de dropshipping, desde producto/costeo hasta creativos, tienda y publicidad. No uses este tool para subir o publicar un creativo puntual en Meta: llama directamente `request_meta_media_upload`, `list_meta_destinations` y `publish_creatives_to_meta`. Sin projectId explica cu\xE1ndo usar un flujo directo versus uno guiado; con projectId devuelve la fase y la \xFAnica nextAction calculada por ProductMaker. Despu\xE9s de cada mutaci\xF3n vuelve a llamar este tool o lee el proyecto. Nunca activa Meta.", GuideInput.shape, async (input) => {
|
|
12492
12492
|
try {
|
|
12493
12493
|
if (!input.projectId) {
|
|
12494
12494
|
return asText({
|
|
12495
12495
|
mode: "choose",
|
|
12496
12496
|
guided: "Usa start_dropshipping_project cuando el usuario quiera encontrar un producto y dejarlo listo para publicar.",
|
|
12497
|
-
direct: "En modo directo usa
|
|
12497
|
+
direct: "En modo directo usa la herramienta puntual. Para subir/publicar un creativo propio en Meta: request_meta_media_upload \u2192 list_meta_destinations \u2192 publish_creatives_to_meta. No crees un proyecto guiado.",
|
|
12498
12498
|
confirmation: "Antes de acciones pagadas o externas, muestra el estimado y pide confirmaci\xF3n.",
|
|
12499
12499
|
safety: "Las campa\xF1as Meta quedan PAUSED; el usuario las activa manualmente en Ads Manager."
|
|
12500
12500
|
});
|
package/dist/stdio.js
CHANGED
|
@@ -150,7 +150,7 @@ var ApiClient = class {
|
|
|
150
150
|
};
|
|
151
151
|
|
|
152
152
|
// src/index.ts
|
|
153
|
-
var MCP_VERSION = "2.9.
|
|
153
|
+
var MCP_VERSION = "2.9.4";
|
|
154
154
|
|
|
155
155
|
// src/meta-upload-app.ts
|
|
156
156
|
import { readFileSync } from "fs";
|
|
@@ -11342,8 +11342,7 @@ function uploadSessionText(result) {
|
|
|
11342
11342
|
text: [
|
|
11343
11343
|
`### Prepara tu ${mediaLabel} en ProductMaker`,
|
|
11344
11344
|
`Usa primero el App inline de este chat. Si tu host no soporta el bridge de archivos, usa este fallback: [Abrir carga segura](${result.uploadPageUrl})`,
|
|
11345
|
-
`Puedes seleccionar hasta ${result.maxFiles} ${result.mediaKind === "mixed" ? "archivos" : "archivo"}. Cuando
|
|
11346
|
-
`sessionId: \`${result.sessionId}\``,
|
|
11345
|
+
`Puedes seleccionar hasta ${result.maxFiles} ${result.mediaKind === "mixed" ? "archivos" : "archivo"}. Cuando termines, pulsa **OK** dentro del App para que ProductMaker contin\xFAe en este chat.`,
|
|
11347
11346
|
`Este enlace expira: ${result.expiresAt}`
|
|
11348
11347
|
].join("\n\n")
|
|
11349
11348
|
}
|
|
@@ -11354,7 +11353,7 @@ __name(uploadSessionText, "uploadSessionText");
|
|
|
11354
11353
|
function registerMetaUploadTools(server, api) {
|
|
11355
11354
|
server.registerTool("request_meta_media_upload", {
|
|
11356
11355
|
title: "Solicitar carga segura de Meta",
|
|
11357
|
-
description: "Inicia una carga segura desde la laptop del usuario para creativos propios de Meta. Por defecto crea una sesi\xF3n `mixed` de hasta diez archivos; tambi\xE9n admite una sola imagen o video. Si el usuario adjunta una foto o video en el mismo mensaje de ChatGPT, pasa esas referencias opcionales en `files` para que el MCP App inline las consuma dentro del chat. ProductMaker conserva `files` solo como metadata del UI bridge: NO los descarga y NO los reenv\xEDa. No pidas URL CDN, ruta local, bytes ni base64. El App es exclusivamente para cargar archivos: nunca debe pedir ni mostrar portafolio, cuenta publicitaria, p\xE1gina, IDs o JSON. El enlace externo de ProductMaker queda como fallback para hosts sin bridge de archivos.
|
|
11356
|
+
description: "Inicia una carga segura desde la laptop del usuario para creativos propios de Meta. Esta es la ruta DIRECTA para peticiones puntuales como \u2018sube esta imagen/video a Meta Ads\u2019 o \u2018publica este creativo\u2019; no conviertas esa intenci\xF3n en una generaci\xF3n completa de producto. No llames `guide_dropshipping_launch` para esta operaci\xF3n puntual. Por defecto crea una sesi\xF3n `mixed` de hasta diez archivos; tambi\xE9n admite una sola imagen o video. Si el usuario adjunta una foto o video en el mismo mensaje de ChatGPT, pasa esas referencias opcionales en `files` para que el MCP App inline las consuma dentro del chat. ProductMaker conserva `files` solo como metadata del UI bridge: NO los descarga y NO los reenv\xEDa. No pidas URL CDN, ruta local, bytes ni base64. El App es exclusivamente para cargar archivos: nunca debe pedir ni mostrar portafolio, cuenta publicitaria, p\xE1gina, IDs o JSON. El enlace externo de ProductMaker queda como fallback para hosts sin bridge de archivos. El usuario pulsa OK cuando termina; el App inserta en el chat los asset_id verificados para continuar la petici\xF3n anterior. Luego llama `list_meta_destinations` y pregunta en el chat qu\xE9 `portfolioId`, `adAccountId` y `pageId` desea usar. No muestres tokens, URLs temporales, `sessionId` ni un bloque JSON. Esta herramienta no publica ni consume cr\xE9ditos.",
|
|
11358
11357
|
inputSchema: MetaUploadSessionToolInputShape,
|
|
11359
11358
|
outputSchema: MetaUploadSessionResponse2,
|
|
11360
11359
|
_meta: {
|
|
@@ -12342,20 +12341,21 @@ var CostingInputSchema = z59.object({
|
|
|
12342
12341
|
]
|
|
12343
12342
|
});
|
|
12344
12343
|
});
|
|
12344
|
+
var FiniteCostingNumber = z59.number().finite();
|
|
12345
12345
|
var CostingResultSchema = z59.object({
|
|
12346
12346
|
country: z59.string().min(2).max(3),
|
|
12347
|
-
deliveryRate:
|
|
12348
|
-
adjustedShippingCost:
|
|
12349
|
-
adjustedCpa:
|
|
12350
|
-
totalCost:
|
|
12351
|
-
suggestedPrice:
|
|
12352
|
-
baseCommercialPrice:
|
|
12353
|
-
minimumSafePrice:
|
|
12354
|
-
commercialPrice:
|
|
12355
|
-
comparisonPrice:
|
|
12356
|
-
estimatedProfit:
|
|
12357
|
-
actualMargin:
|
|
12358
|
-
breakEvenAdsManagerCpa:
|
|
12347
|
+
deliveryRate: FiniteCostingNumber,
|
|
12348
|
+
adjustedShippingCost: FiniteCostingNumber,
|
|
12349
|
+
adjustedCpa: FiniteCostingNumber,
|
|
12350
|
+
totalCost: FiniteCostingNumber,
|
|
12351
|
+
suggestedPrice: FiniteCostingNumber,
|
|
12352
|
+
baseCommercialPrice: FiniteCostingNumber,
|
|
12353
|
+
minimumSafePrice: FiniteCostingNumber,
|
|
12354
|
+
commercialPrice: FiniteCostingNumber,
|
|
12355
|
+
comparisonPrice: FiniteCostingNumber,
|
|
12356
|
+
estimatedProfit: FiniteCostingNumber,
|
|
12357
|
+
actualMargin: FiniteCostingNumber,
|
|
12358
|
+
breakEvenAdsManagerCpa: FiniteCostingNumber,
|
|
12359
12359
|
wasPriceAutoAdjusted: z59.boolean(),
|
|
12360
12360
|
profitabilityStatus: z59.literal("healthy")
|
|
12361
12361
|
});
|
|
@@ -12444,13 +12444,13 @@ function registerDropshippingProjectTools(server, api) {
|
|
|
12444
12444
|
throw translateError(error);
|
|
12445
12445
|
}
|
|
12446
12446
|
});
|
|
12447
|
-
server.tool("guide_dropshipping_launch", "Gu\xEDa read-only
|
|
12447
|
+
server.tool("guide_dropshipping_launch", "Gu\xEDa read-only de un recorrido COMPLETO de dropshipping, desde producto/costeo hasta creativos, tienda y publicidad. No uses este tool para subir o publicar un creativo puntual en Meta: llama directamente `request_meta_media_upload`, `list_meta_destinations` y `publish_creatives_to_meta`. Sin projectId explica cu\xE1ndo usar un flujo directo versus uno guiado; con projectId devuelve la fase y la \xFAnica nextAction calculada por ProductMaker. Despu\xE9s de cada mutaci\xF3n vuelve a llamar este tool o lee el proyecto. Nunca activa Meta.", GuideInput.shape, async (input) => {
|
|
12448
12448
|
try {
|
|
12449
12449
|
if (!input.projectId) {
|
|
12450
12450
|
return asText({
|
|
12451
12451
|
mode: "choose",
|
|
12452
12452
|
guided: "Usa start_dropshipping_project cuando el usuario quiera encontrar un producto y dejarlo listo para publicar.",
|
|
12453
|
-
direct: "En modo directo usa
|
|
12453
|
+
direct: "En modo directo usa la herramienta puntual. Para subir/publicar un creativo propio en Meta: request_meta_media_upload \u2192 list_meta_destinations \u2192 publish_creatives_to_meta. No crees un proyecto guiado.",
|
|
12454
12454
|
confirmation: "Antes de acciones pagadas o externas, muestra el estimado y pide confirmaci\xF3n.",
|
|
12455
12455
|
safety: "Las campa\xF1as Meta quedan PAUSED; el usuario las activa manualmente en Ads Manager."
|
|
12456
12456
|
});
|
|
@@ -98,10 +98,8 @@
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
.upload-feedback {
|
|
101
|
-
display:
|
|
102
|
-
|
|
103
|
-
flex-wrap: wrap;
|
|
104
|
-
gap: 10px;
|
|
101
|
+
display: grid;
|
|
102
|
+
gap: 8px;
|
|
105
103
|
margin-top: 10px;
|
|
106
104
|
}
|
|
107
105
|
|
|
@@ -202,21 +200,31 @@
|
|
|
202
200
|
cursor: not-allowed;
|
|
203
201
|
}
|
|
204
202
|
|
|
205
|
-
.
|
|
206
|
-
display: grid;
|
|
207
|
-
width: 48px;
|
|
208
|
-
height: 48px;
|
|
209
|
-
place-items: center;
|
|
210
|
-
border: 1px solid var(--pm-line);
|
|
211
|
-
border-radius: 50%;
|
|
212
|
-
background: var(--pm-card);
|
|
213
|
-
box-shadow: 0 7px 20px rgb(27 36 66 / 8%);
|
|
203
|
+
.add-symbol {
|
|
214
204
|
color: var(--pm-text);
|
|
215
|
-
font-size:
|
|
216
|
-
font-weight:
|
|
205
|
+
font-size: 44px;
|
|
206
|
+
font-weight: 300;
|
|
217
207
|
line-height: 1;
|
|
218
208
|
}
|
|
219
209
|
|
|
210
|
+
.complete-button {
|
|
211
|
+
justify-self: start;
|
|
212
|
+
min-width: 76px;
|
|
213
|
+
margin-top: 10px;
|
|
214
|
+
padding: 10px 20px;
|
|
215
|
+
border: 0;
|
|
216
|
+
border-radius: 12px;
|
|
217
|
+
background: var(--pm-primary);
|
|
218
|
+
color: #ffffff;
|
|
219
|
+
cursor: pointer;
|
|
220
|
+
font-weight: 780;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.complete-button:disabled {
|
|
224
|
+
cursor: not-allowed;
|
|
225
|
+
opacity: 0.5;
|
|
226
|
+
}
|
|
227
|
+
|
|
220
228
|
.drop-help {
|
|
221
229
|
position: absolute;
|
|
222
230
|
width: 1px;
|
|
@@ -447,7 +455,7 @@
|
|
|
447
455
|
aria-label="Agregar otro archivo"
|
|
448
456
|
disabled
|
|
449
457
|
>
|
|
450
|
-
<span class="
|
|
458
|
+
<span class="add-symbol" aria-hidden="true">+</span>
|
|
451
459
|
</button>
|
|
452
460
|
<button
|
|
453
461
|
class="compatibility-control"
|
|
@@ -493,6 +501,15 @@
|
|
|
493
501
|
aria-live="polite"
|
|
494
502
|
></p>
|
|
495
503
|
</div>
|
|
504
|
+
<button
|
|
505
|
+
class="complete-button"
|
|
506
|
+
type="button"
|
|
507
|
+
data-testid="meta-upload-complete-btn"
|
|
508
|
+
hidden
|
|
509
|
+
disabled
|
|
510
|
+
>
|
|
511
|
+
OK
|
|
512
|
+
</button>
|
|
496
513
|
</section>
|
|
497
514
|
</article>
|
|
498
515
|
</main>
|
|
@@ -548,6 +565,9 @@
|
|
|
548
565
|
batchError: document.querySelector(
|
|
549
566
|
'[data-testid="meta-upload-batch-error"]',
|
|
550
567
|
),
|
|
568
|
+
completeButton: document.querySelector(
|
|
569
|
+
'[data-testid="meta-upload-complete-btn"]',
|
|
570
|
+
),
|
|
551
571
|
fileList: document.querySelector(
|
|
552
572
|
'[data-testid="meta-upload-file-list"]',
|
|
553
573
|
),
|
|
@@ -643,6 +663,38 @@
|
|
|
643
663
|
scheduleIntrinsicSizeReport(true);
|
|
644
664
|
}
|
|
645
665
|
|
|
666
|
+
function canMessageHost() {
|
|
667
|
+
return (
|
|
668
|
+
hasOpenAiFunction("sendFollowUpMessage") || state.hostCanMessage
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function canCompleteUpload() {
|
|
673
|
+
return (
|
|
674
|
+
!state.disposed &&
|
|
675
|
+
Boolean(state.session) &&
|
|
676
|
+
!sessionExpired() &&
|
|
677
|
+
!state.busyBatch &&
|
|
678
|
+
!state.completionNotified &&
|
|
679
|
+
!state.completionNotificationInFlight &&
|
|
680
|
+
state.assets.length > 0 &&
|
|
681
|
+
state.items.length > 0 &&
|
|
682
|
+
state.assets.length === state.items.length &&
|
|
683
|
+
state.items.every((item) => item.status === "uploaded") &&
|
|
684
|
+
canMessageHost()
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
function renderCompletionAction() {
|
|
689
|
+
const hasVerifiedAssets = state.assets.length > 0;
|
|
690
|
+
elements.completeButton.hidden =
|
|
691
|
+
state.completionNotified || !hasVerifiedAssets || !canMessageHost();
|
|
692
|
+
elements.completeButton.disabled = !canCompleteUpload();
|
|
693
|
+
elements.completeButton.textContent =
|
|
694
|
+
state.completionNotificationInFlight ? "…" : "OK";
|
|
695
|
+
scheduleIntrinsicSizeReport(true);
|
|
696
|
+
}
|
|
697
|
+
|
|
646
698
|
function parseFinitePositiveNumber(value) {
|
|
647
699
|
return typeof value === "number" &&
|
|
648
700
|
Number.isFinite(value) &&
|
|
@@ -861,6 +913,8 @@
|
|
|
861
913
|
!state.session ||
|
|
862
914
|
sessionExpired() ||
|
|
863
915
|
state.uploadBlocked ||
|
|
916
|
+
state.completionNotified ||
|
|
917
|
+
state.completionNotificationInFlight ||
|
|
864
918
|
state.busyBatch ||
|
|
865
919
|
state.items.length >= safeSessionLimit();
|
|
866
920
|
elements.input.disabled = disabled;
|
|
@@ -1081,6 +1135,17 @@
|
|
|
1081
1135
|
const sessionChanged =
|
|
1082
1136
|
!state.session || state.session.sessionId !== session.sessionId;
|
|
1083
1137
|
if (sessionChanged) {
|
|
1138
|
+
abortActiveWork();
|
|
1139
|
+
for (const item of state.items) revokePreviewUrl(item);
|
|
1140
|
+
state.items = [];
|
|
1141
|
+
state.assets = [];
|
|
1142
|
+
state.consumedBridgeIds.clear();
|
|
1143
|
+
state.consumingBridgeFiles = false;
|
|
1144
|
+
state.bridgeConsumptionQueued = false;
|
|
1145
|
+
state.ingestionQueue = Promise.resolve();
|
|
1146
|
+
state.busyBatch = false;
|
|
1147
|
+
elements.input.value = "";
|
|
1148
|
+
renderItems();
|
|
1084
1149
|
state.uploadBlocked = false;
|
|
1085
1150
|
state.fallbackRequired = false;
|
|
1086
1151
|
state.completionNotified = false;
|
|
@@ -1103,6 +1168,7 @@
|
|
|
1103
1168
|
setSessionStatus("Sesión lista para recibir archivos.", "neutral");
|
|
1104
1169
|
}
|
|
1105
1170
|
setPickerAvailability();
|
|
1171
|
+
renderCompletionAction();
|
|
1106
1172
|
void consumePendingBridgeFiles();
|
|
1107
1173
|
scheduleIntrinsicSizeReport(true);
|
|
1108
1174
|
}
|
|
@@ -1218,7 +1284,9 @@
|
|
|
1218
1284
|
state.disposed ||
|
|
1219
1285
|
!state.session ||
|
|
1220
1286
|
sessionExpired() ||
|
|
1221
|
-
state.uploadBlocked
|
|
1287
|
+
state.uploadBlocked ||
|
|
1288
|
+
state.completionNotified ||
|
|
1289
|
+
state.completionNotificationInFlight
|
|
1222
1290
|
)
|
|
1223
1291
|
return;
|
|
1224
1292
|
state.workCancelled = false;
|
|
@@ -1237,7 +1305,9 @@
|
|
|
1237
1305
|
state.disposed ||
|
|
1238
1306
|
!state.session ||
|
|
1239
1307
|
sessionExpired() ||
|
|
1240
|
-
state.uploadBlocked
|
|
1308
|
+
state.uploadBlocked ||
|
|
1309
|
+
state.completionNotified ||
|
|
1310
|
+
state.completionNotificationInFlight
|
|
1241
1311
|
)
|
|
1242
1312
|
return;
|
|
1243
1313
|
const bridge = openAiBridge();
|
|
@@ -1411,6 +1481,8 @@
|
|
|
1411
1481
|
!state.session ||
|
|
1412
1482
|
sessionExpired() ||
|
|
1413
1483
|
state.uploadBlocked ||
|
|
1484
|
+
state.completionNotified ||
|
|
1485
|
+
state.completionNotificationInFlight ||
|
|
1414
1486
|
state.busyBatch
|
|
1415
1487
|
)
|
|
1416
1488
|
return;
|
|
@@ -1430,7 +1502,10 @@
|
|
|
1430
1502
|
if (!selected.length) return;
|
|
1431
1503
|
|
|
1432
1504
|
state.busyBatch = true;
|
|
1505
|
+
state.assets = [];
|
|
1506
|
+
state.completionNotified = false;
|
|
1433
1507
|
setPickerAvailability();
|
|
1508
|
+
renderCompletionAction();
|
|
1434
1509
|
for (const file of selected) {
|
|
1435
1510
|
const mediaKind = mediaKindForType(file.type) || "image";
|
|
1436
1511
|
const item = createItem(file, mediaKind);
|
|
@@ -1440,6 +1515,7 @@
|
|
|
1440
1515
|
}
|
|
1441
1516
|
state.busyBatch = false;
|
|
1442
1517
|
setPickerAvailability();
|
|
1518
|
+
renderCompletionAction();
|
|
1443
1519
|
if (state.items.some((item) => item.status === "uploaded")) {
|
|
1444
1520
|
await refreshSessionStatus();
|
|
1445
1521
|
}
|
|
@@ -2085,6 +2161,7 @@
|
|
|
2085
2161
|
fragment.append(card);
|
|
2086
2162
|
});
|
|
2087
2163
|
elements.fileList.replaceChildren(fragment);
|
|
2164
|
+
renderCompletionAction();
|
|
2088
2165
|
scheduleIntrinsicSizeReport(true);
|
|
2089
2166
|
}
|
|
2090
2167
|
|
|
@@ -2098,13 +2175,21 @@
|
|
|
2098
2175
|
|
|
2099
2176
|
async function refreshSessionStatus() {
|
|
2100
2177
|
if (!state.session) return;
|
|
2178
|
+
const requestedSessionId = state.session.sessionId;
|
|
2101
2179
|
setSessionStatus("Comprobando el staging privado…", "neutral");
|
|
2102
2180
|
try {
|
|
2103
2181
|
const result = await callReadOnlyTool("check_meta_media_upload", {
|
|
2104
|
-
sessionId:
|
|
2182
|
+
sessionId: requestedSessionId,
|
|
2105
2183
|
});
|
|
2106
|
-
|
|
2184
|
+
if (
|
|
2185
|
+
state.disposed ||
|
|
2186
|
+
!state.session ||
|
|
2187
|
+
state.session.sessionId !== requestedSessionId
|
|
2188
|
+
)
|
|
2189
|
+
return;
|
|
2190
|
+
const status = parseStatusResult(result, requestedSessionId);
|
|
2107
2191
|
state.assets = status.assets;
|
|
2192
|
+
renderCompletionAction();
|
|
2108
2193
|
if (!status.assets.length) {
|
|
2109
2194
|
if (status.legacyKindAmbiguous) {
|
|
2110
2195
|
requireFallback(
|
|
@@ -2138,7 +2223,6 @@
|
|
|
2138
2223
|
: "Carga completada.",
|
|
2139
2224
|
partial ? "warning" : "success",
|
|
2140
2225
|
);
|
|
2141
|
-
await notifyChatUploadComplete();
|
|
2142
2226
|
} catch (error) {
|
|
2143
2227
|
const expired =
|
|
2144
2228
|
sessionExpired() ||
|
|
@@ -2200,8 +2284,10 @@
|
|
|
2200
2284
|
return parsed;
|
|
2201
2285
|
}
|
|
2202
2286
|
|
|
2203
|
-
function parseStatusResult(result) {
|
|
2287
|
+
function parseStatusResult(result, expectedSessionId) {
|
|
2204
2288
|
const payload = resultPayload(result);
|
|
2289
|
+
if (payload.sessionId !== expectedSessionId)
|
|
2290
|
+
throw new Error("status_session_mismatch");
|
|
2205
2291
|
const status = payload.status;
|
|
2206
2292
|
if (!["pending", "uploaded"].includes(status))
|
|
2207
2293
|
throw new Error("invalid_status");
|
|
@@ -2253,38 +2339,77 @@
|
|
|
2253
2339
|
!state.session
|
|
2254
2340
|
)
|
|
2255
2341
|
return;
|
|
2342
|
+
const completionSessionId = state.session.sessionId;
|
|
2256
2343
|
state.completionNotificationInFlight = true;
|
|
2257
|
-
|
|
2258
|
-
|
|
2344
|
+
setPickerAvailability();
|
|
2345
|
+
renderCompletionAction();
|
|
2346
|
+
const assets = state.assets
|
|
2347
|
+
.map(
|
|
2348
|
+
(asset) => `${asset.mediaKind} asset_id ${asset.stagingAssetId}`,
|
|
2349
|
+
)
|
|
2350
|
+
.join(", ");
|
|
2351
|
+
const message = `The ProductMaker upload is complete. Continue my previous request using these uploaded assets: ${assets}. Do not ask me to upload them again. Do not publish anything until you have asked me for the Meta portfolio, ad account, and Facebook Page.`;
|
|
2259
2352
|
try {
|
|
2260
2353
|
const bridge = openAiBridge();
|
|
2261
2354
|
if (bridge && typeof bridge.sendFollowUpMessage === "function") {
|
|
2262
2355
|
await bridge.sendFollowUpMessage({ prompt: message });
|
|
2356
|
+
if (
|
|
2357
|
+
!state.session ||
|
|
2358
|
+
state.session.sessionId !== completionSessionId
|
|
2359
|
+
)
|
|
2360
|
+
return;
|
|
2263
2361
|
state.completionNotified = true;
|
|
2362
|
+
setPickerAvailability();
|
|
2363
|
+
renderCompletionAction();
|
|
2264
2364
|
return;
|
|
2265
2365
|
}
|
|
2266
2366
|
if (state.hostCanMessage) {
|
|
2267
|
-
await rpcRequest("ui/message", {
|
|
2367
|
+
const messageResult = await rpcRequest("ui/message", {
|
|
2268
2368
|
role: "user",
|
|
2269
2369
|
content: [{ type: "text", text: message }],
|
|
2270
2370
|
});
|
|
2371
|
+
if (isRecord(messageResult) && messageResult.isError === true)
|
|
2372
|
+
throw new Error("host_message_rejected");
|
|
2373
|
+
if (
|
|
2374
|
+
!state.session ||
|
|
2375
|
+
state.session.sessionId !== completionSessionId
|
|
2376
|
+
)
|
|
2377
|
+
return;
|
|
2271
2378
|
state.completionNotified = true;
|
|
2379
|
+
setPickerAvailability();
|
|
2380
|
+
renderCompletionAction();
|
|
2272
2381
|
return;
|
|
2273
2382
|
}
|
|
2274
2383
|
setSessionStatus(
|
|
2275
|
-
"
|
|
2276
|
-
"
|
|
2384
|
+
"La carga terminó, pero este host no puede continuar el chat desde el widget. Vuelve al chat para seguir.",
|
|
2385
|
+
"warning",
|
|
2277
2386
|
);
|
|
2278
2387
|
} catch {
|
|
2388
|
+
if (
|
|
2389
|
+
!state.session ||
|
|
2390
|
+
state.session.sessionId !== completionSessionId
|
|
2391
|
+
)
|
|
2392
|
+
return;
|
|
2279
2393
|
setSessionStatus(
|
|
2280
|
-
"
|
|
2281
|
-
"
|
|
2394
|
+
"No pudimos continuar el chat. Intenta nuevamente.",
|
|
2395
|
+
"danger",
|
|
2282
2396
|
);
|
|
2283
2397
|
} finally {
|
|
2284
|
-
|
|
2398
|
+
if (
|
|
2399
|
+
state.session &&
|
|
2400
|
+
state.session.sessionId === completionSessionId
|
|
2401
|
+
) {
|
|
2402
|
+
state.completionNotificationInFlight = false;
|
|
2403
|
+
setPickerAvailability();
|
|
2404
|
+
renderCompletionAction();
|
|
2405
|
+
}
|
|
2285
2406
|
}
|
|
2286
2407
|
}
|
|
2287
2408
|
|
|
2409
|
+
elements.completeButton.addEventListener("click", () => {
|
|
2410
|
+
if (canCompleteUpload()) void notifyChatUploadComplete();
|
|
2411
|
+
});
|
|
2412
|
+
|
|
2288
2413
|
document.addEventListener("DOMContentLoaded", initialize, {
|
|
2289
2414
|
once: true,
|
|
2290
2415
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@productmaker/mcp",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Product Maker MCP server — cost products, generate dropshipping landing pages, UGC videos, and ad creatives, and publish to Shopify and Meta Ads from Claude, ChatGPT, or Gemini.",
|