@productmaker/mcp 2.4.0 → 2.8.3
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 +29 -2
- package/dist/http.js +1097 -471
- package/dist/stdio.js +1097 -471
- package/dist/ui/meta-upload-app.html +2268 -0
- package/package.json +3 -2
package/dist/stdio.js
CHANGED
|
@@ -150,10 +150,60 @@ var ApiClient = class {
|
|
|
150
150
|
};
|
|
151
151
|
|
|
152
152
|
// src/index.ts
|
|
153
|
-
var MCP_VERSION = "2.
|
|
153
|
+
var MCP_VERSION = "2.8.3";
|
|
154
|
+
|
|
155
|
+
// src/meta-upload-app.ts
|
|
156
|
+
import { readFileSync } from "fs";
|
|
157
|
+
import { registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
|
|
158
|
+
var META_UPLOAD_APP_RESOURCE_URI = "ui://meta-upload/v1.html";
|
|
159
|
+
var META_UPLOAD_APP_NAME = "Meta upload app";
|
|
160
|
+
var META_UPLOAD_APP_HTML_URL = new URL("./ui/meta-upload-app.html", import.meta.url);
|
|
161
|
+
var META_UPLOAD_APP_CSP = {
|
|
162
|
+
connectDomains: [
|
|
163
|
+
"https://productmaker.app",
|
|
164
|
+
"https://www.productmaker.app",
|
|
165
|
+
"https://*.r2.cloudflarestorage.com",
|
|
166
|
+
"https://*.oaiusercontent.com"
|
|
167
|
+
]
|
|
168
|
+
};
|
|
169
|
+
function readMetaUploadAppHtml(htmlUrl = META_UPLOAD_APP_HTML_URL) {
|
|
170
|
+
try {
|
|
171
|
+
return readFileSync(htmlUrl, "utf8");
|
|
172
|
+
} catch (error) {
|
|
173
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
174
|
+
throw new Error(`Missing packaged Meta upload app HTML at ${htmlUrl.pathname}. Rebuild @productmaker/mcp so ${META_UPLOAD_APP_RESOURCE_URI} is bundled. Original error: ${message}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
__name(readMetaUploadAppHtml, "readMetaUploadAppHtml");
|
|
178
|
+
function registerMetaUploadAppResource(server) {
|
|
179
|
+
const html = readMetaUploadAppHtml();
|
|
180
|
+
registerAppResource(server, META_UPLOAD_APP_NAME, META_UPLOAD_APP_RESOURCE_URI, {
|
|
181
|
+
title: "Meta upload app",
|
|
182
|
+
mimeType: RESOURCE_MIME_TYPE,
|
|
183
|
+
_meta: {
|
|
184
|
+
ui: {
|
|
185
|
+
csp: META_UPLOAD_APP_CSP
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}, async () => ({
|
|
189
|
+
contents: [
|
|
190
|
+
{
|
|
191
|
+
uri: META_UPLOAD_APP_RESOURCE_URI,
|
|
192
|
+
mimeType: RESOURCE_MIME_TYPE,
|
|
193
|
+
text: html,
|
|
194
|
+
_meta: {
|
|
195
|
+
ui: {
|
|
196
|
+
csp: META_UPLOAD_APP_CSP
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
}));
|
|
202
|
+
}
|
|
203
|
+
__name(registerMetaUploadAppResource, "registerMetaUploadAppResource");
|
|
154
204
|
|
|
155
205
|
// src/tools/tasks.ts
|
|
156
|
-
import { z as
|
|
206
|
+
import { z as z45 } from "zod";
|
|
157
207
|
|
|
158
208
|
// src/resolve-image.ts
|
|
159
209
|
import { lookup as dnsLookup } from "dns/promises";
|
|
@@ -3717,6 +3767,25 @@ var ALLOWED_MEDIA_CONTENT_TYPES = [
|
|
|
3717
3767
|
"audio/wav",
|
|
3718
3768
|
"audio/x-wav"
|
|
3719
3769
|
];
|
|
3770
|
+
var META_STAGING_IMAGE_CONTENT_TYPES = /* @__PURE__ */ new Set([
|
|
3771
|
+
"image/jpeg",
|
|
3772
|
+
"image/png",
|
|
3773
|
+
"image/webp"
|
|
3774
|
+
]);
|
|
3775
|
+
var META_STAGING_MEDIA_CONTENT_TYPES = /* @__PURE__ */ new Set([
|
|
3776
|
+
...META_STAGING_IMAGE_CONTENT_TYPES,
|
|
3777
|
+
"video/mp4",
|
|
3778
|
+
"video/quicktime",
|
|
3779
|
+
"video/webm"
|
|
3780
|
+
]);
|
|
3781
|
+
var META_STAGING_CONTENT_TYPES = [
|
|
3782
|
+
"image/jpeg",
|
|
3783
|
+
"image/png",
|
|
3784
|
+
"image/webp",
|
|
3785
|
+
"video/mp4",
|
|
3786
|
+
"video/quicktime",
|
|
3787
|
+
"video/webm"
|
|
3788
|
+
];
|
|
3720
3789
|
var PresignUploadRequest = z31.object({
|
|
3721
3790
|
/** MIME type of the file to upload */
|
|
3722
3791
|
contentType: z31.enum(ALLOWED_MEDIA_CONTENT_TYPES),
|
|
@@ -3732,8 +3801,51 @@ var PresignUploadRequest = z31.object({
|
|
|
3732
3801
|
purpose: z31.enum([
|
|
3733
3802
|
"ads",
|
|
3734
3803
|
"service",
|
|
3735
|
-
"actor"
|
|
3804
|
+
"actor",
|
|
3805
|
+
"meta_staging"
|
|
3806
|
+
]).optional(),
|
|
3807
|
+
/** Required for purpose=meta_staging so media and thumbnails can be checked independently. */
|
|
3808
|
+
assetRole: z31.enum([
|
|
3809
|
+
"media",
|
|
3810
|
+
"thumbnail"
|
|
3736
3811
|
]).optional()
|
|
3812
|
+
}).superRefine((value, ctx) => {
|
|
3813
|
+
if (value.purpose === "meta_staging") {
|
|
3814
|
+
if (!value.assetRole) {
|
|
3815
|
+
ctx.addIssue({
|
|
3816
|
+
code: z31.ZodIssueCode.custom,
|
|
3817
|
+
path: [
|
|
3818
|
+
"assetRole"
|
|
3819
|
+
],
|
|
3820
|
+
message: "assetRole is required for Meta staging uploads"
|
|
3821
|
+
});
|
|
3822
|
+
} else if (!META_STAGING_MEDIA_CONTENT_TYPES.has(value.contentType)) {
|
|
3823
|
+
ctx.addIssue({
|
|
3824
|
+
code: z31.ZodIssueCode.custom,
|
|
3825
|
+
path: [
|
|
3826
|
+
"contentType"
|
|
3827
|
+
],
|
|
3828
|
+
message: "Meta staging accepts only image or video media"
|
|
3829
|
+
});
|
|
3830
|
+
} else if (value.assetRole === "thumbnail" && !META_STAGING_IMAGE_CONTENT_TYPES.has(value.contentType)) {
|
|
3831
|
+
ctx.addIssue({
|
|
3832
|
+
code: z31.ZodIssueCode.custom,
|
|
3833
|
+
path: [
|
|
3834
|
+
"contentType"
|
|
3835
|
+
],
|
|
3836
|
+
message: "Meta staging thumbnails must be images"
|
|
3837
|
+
});
|
|
3838
|
+
}
|
|
3839
|
+
}
|
|
3840
|
+
if (value.purpose !== "meta_staging" && value.assetRole) {
|
|
3841
|
+
ctx.addIssue({
|
|
3842
|
+
code: z31.ZodIssueCode.custom,
|
|
3843
|
+
path: [
|
|
3844
|
+
"assetRole"
|
|
3845
|
+
],
|
|
3846
|
+
message: "assetRole is only valid for Meta staging uploads"
|
|
3847
|
+
});
|
|
3848
|
+
}
|
|
3737
3849
|
});
|
|
3738
3850
|
var PresignUploadResponse = z31.object({
|
|
3739
3851
|
/** Presigned S3 PUT URL — browser sends file directly here */
|
|
@@ -3743,8 +3855,23 @@ var PresignUploadResponse = z31.object({
|
|
|
3743
3855
|
/** S3 key */
|
|
3744
3856
|
key: z31.string(),
|
|
3745
3857
|
/** Present only for purpose=service. */
|
|
3746
|
-
assetToken: z31.string().min(1).optional()
|
|
3858
|
+
assetToken: z31.string().min(1).optional(),
|
|
3859
|
+
/** Present only for purpose=meta_staging. */
|
|
3860
|
+
stagingAssetId: z31.string().uuid().optional()
|
|
3747
3861
|
});
|
|
3862
|
+
var MetaUploadMediaKind = z31.enum([
|
|
3863
|
+
"image",
|
|
3864
|
+
"video",
|
|
3865
|
+
"mixed"
|
|
3866
|
+
]);
|
|
3867
|
+
var META_UPLOAD_MAX_FILES = CAMPAIGN_LIMITS.MAX_CREATIVES;
|
|
3868
|
+
var MetaUploadAssetSummary = z31.object({
|
|
3869
|
+
stagingAssetId: z31.string().uuid(),
|
|
3870
|
+
mediaKind: z31.enum([
|
|
3871
|
+
"image",
|
|
3872
|
+
"video"
|
|
3873
|
+
])
|
|
3874
|
+
}).strict();
|
|
3748
3875
|
var MetaCampaignType = z31.enum([
|
|
3749
3876
|
"sales",
|
|
3750
3877
|
"traffic",
|
|
@@ -3770,6 +3897,81 @@ var HttpUrl = z31.string().url().refine((value) => {
|
|
|
3770
3897
|
return false;
|
|
3771
3898
|
}
|
|
3772
3899
|
}, "URL must use http or https");
|
|
3900
|
+
var MetaUploadSessionRequest = z31.object({
|
|
3901
|
+
/** The browser page will require a file of this media kind. */
|
|
3902
|
+
mediaKind: MetaUploadMediaKind,
|
|
3903
|
+
/** The staged object role; media is the normal creative input. */
|
|
3904
|
+
assetRole: z31.enum([
|
|
3905
|
+
"media",
|
|
3906
|
+
"thumbnail"
|
|
3907
|
+
]).default("media"),
|
|
3908
|
+
/** Maximum number of files; the API chooses 10 for mixed and 1 otherwise when omitted. */
|
|
3909
|
+
maxFiles: z31.number().int().min(1).max(META_UPLOAD_MAX_FILES).optional()
|
|
3910
|
+
}).strict().superRefine((value, ctx) => {
|
|
3911
|
+
if (value.mediaKind === "mixed" && value.assetRole === "thumbnail") {
|
|
3912
|
+
ctx.addIssue({
|
|
3913
|
+
code: z31.ZodIssueCode.custom,
|
|
3914
|
+
path: [
|
|
3915
|
+
"mediaKind"
|
|
3916
|
+
],
|
|
3917
|
+
message: "Mixed Meta upload sessions cannot be thumbnails"
|
|
3918
|
+
});
|
|
3919
|
+
}
|
|
3920
|
+
if (value.mediaKind !== "mixed" && value.maxFiles !== void 0 && value.maxFiles !== 1) {
|
|
3921
|
+
ctx.addIssue({
|
|
3922
|
+
code: z31.ZodIssueCode.custom,
|
|
3923
|
+
path: [
|
|
3924
|
+
"maxFiles"
|
|
3925
|
+
],
|
|
3926
|
+
message: "Non-mixed Meta upload sessions accept exactly one file"
|
|
3927
|
+
});
|
|
3928
|
+
}
|
|
3929
|
+
if (value.assetRole === "thumbnail" && value.mediaKind !== "image") {
|
|
3930
|
+
ctx.addIssue({
|
|
3931
|
+
code: z31.ZodIssueCode.custom,
|
|
3932
|
+
path: [
|
|
3933
|
+
"mediaKind"
|
|
3934
|
+
],
|
|
3935
|
+
message: "Meta thumbnails must be images"
|
|
3936
|
+
});
|
|
3937
|
+
}
|
|
3938
|
+
});
|
|
3939
|
+
var ResolveMetaUploadSessionRequest = z31.object({
|
|
3940
|
+
/** Opaque signed token embedded in the one-click upload page URL. */
|
|
3941
|
+
token: z31.string().min(1).max(4096),
|
|
3942
|
+
/** Stable browser-generated id so failed PUT retries reuse the same slot. */
|
|
3943
|
+
uploadId: z31.string().uuid().optional(),
|
|
3944
|
+
/** Browser-selected metadata; the API checks it against the session kind/role. */
|
|
3945
|
+
contentType: z31.enum(META_STAGING_CONTENT_TYPES),
|
|
3946
|
+
fileName: z31.string().min(1).max(255),
|
|
3947
|
+
fileSize: z31.number().int().positive().max(CAMPAIGN_LIMITS.MAX_FILE_SIZE_BYTES)
|
|
3948
|
+
}).strict();
|
|
3949
|
+
var MetaUploadSessionResponse = z31.object({
|
|
3950
|
+
sessionId: z31.string().uuid(),
|
|
3951
|
+
uploadPageUrl: HttpUrl,
|
|
3952
|
+
mediaKind: MetaUploadMediaKind,
|
|
3953
|
+
assetRole: z31.enum([
|
|
3954
|
+
"media",
|
|
3955
|
+
"thumbnail"
|
|
3956
|
+
]),
|
|
3957
|
+
maxFiles: z31.number().int().min(1).max(META_UPLOAD_MAX_FILES),
|
|
3958
|
+
expiresAt: z31.string().datetime()
|
|
3959
|
+
}).strict();
|
|
3960
|
+
var ResolveMetaUploadSessionResponse = z31.object({
|
|
3961
|
+
uploadUrl: z31.string().url(),
|
|
3962
|
+
stagingAssetId: z31.string().uuid(),
|
|
3963
|
+
expiresAt: z31.string().datetime()
|
|
3964
|
+
}).strict();
|
|
3965
|
+
var MetaUploadSessionStatusResponse = z31.object({
|
|
3966
|
+
sessionId: z31.string().uuid(),
|
|
3967
|
+
status: z31.enum([
|
|
3968
|
+
"pending",
|
|
3969
|
+
"uploaded"
|
|
3970
|
+
]),
|
|
3971
|
+
assets: z31.array(MetaUploadAssetSummary).max(META_UPLOAD_MAX_FILES),
|
|
3972
|
+
/** Legacy single-file response field retained for older callers. */
|
|
3973
|
+
stagingAssetId: z31.string().uuid().optional()
|
|
3974
|
+
}).strict();
|
|
3773
3975
|
var MetaAdAccountId = z31.string().trim().min(1, "Debes seleccionar una cuenta publicitaria").transform((value) => /^\d+$/.test(value) ? `act_${value}` : value).refine((value) => /^act_\d+$/.test(value), {
|
|
3774
3976
|
message: 'adAccountId must look like "act_1234567890" (a bare numeric id is also accepted)'
|
|
3775
3977
|
});
|
|
@@ -3856,18 +4058,26 @@ var UploadToMetaRequest = z31.object({
|
|
|
3856
4058
|
title: z31.string().max(CAMPAIGN_LIMITS.MAX_TITLE_LENGTH),
|
|
3857
4059
|
description: z31.string().max(CAMPAIGN_LIMITS.MAX_DESCRIPTION_LENGTH),
|
|
3858
4060
|
cta: MetaCtaType,
|
|
3859
|
-
/**
|
|
3860
|
-
mediaUrl: HttpUrl,
|
|
4061
|
+
/** Legacy public URL; new Meta flows should use mediaAssetId. */
|
|
4062
|
+
mediaUrl: HttpUrl.optional(),
|
|
4063
|
+
/** Owner-scoped temporary asset created by the Meta staging upload flow. */
|
|
4064
|
+
mediaAssetId: z31.string().uuid().optional(),
|
|
3861
4065
|
/**
|
|
3862
4066
|
* Public URL of an extracted thumbnail. Required for video ads — Meta
|
|
3863
4067
|
* rejects video ad creatives without `image_hash`/`image_url` in
|
|
3864
4068
|
* `video_data` (subcode 1443226). Frontend extracts a frame from the
|
|
3865
4069
|
* video and uploads it before submitting the request.
|
|
3866
4070
|
*/
|
|
3867
|
-
thumbnailUrl: HttpUrl.optional()
|
|
4071
|
+
thumbnailUrl: HttpUrl.optional(),
|
|
4072
|
+
/** Owner-scoped temporary thumbnail created by the Meta staging upload flow. */
|
|
4073
|
+
thumbnailAssetId: z31.string().uuid().optional()
|
|
3868
4074
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
3869
4075
|
/** Meta Ad Account ID */
|
|
3870
4076
|
adAccountId: MetaAdAccountId,
|
|
4077
|
+
/** Meta Business Portfolio selected by the user (optional for legacy callers). */
|
|
4078
|
+
portfolioId: z31.string().min(1).optional(),
|
|
4079
|
+
/** Facebook Page selected by the user (optional for legacy callers). */
|
|
4080
|
+
pageId: z31.string().min(1).optional(),
|
|
3871
4081
|
/** Optional Meta Pixel ID */
|
|
3872
4082
|
pixelId: z31.string().optional(),
|
|
3873
4083
|
/** Landing page URL (required for landing destinations; omitted for whatsapp) */
|
|
@@ -3887,6 +4097,30 @@ var UploadToMetaRequest = z31.object({
|
|
|
3887
4097
|
message: "landingUrl is required for landing destinations"
|
|
3888
4098
|
});
|
|
3889
4099
|
}
|
|
4100
|
+
value.creatives.forEach((creative, index) => {
|
|
4101
|
+
if (Boolean(creative.mediaUrl) === Boolean(creative.mediaAssetId)) {
|
|
4102
|
+
ctx.addIssue({
|
|
4103
|
+
code: z31.ZodIssueCode.custom,
|
|
4104
|
+
path: [
|
|
4105
|
+
"creatives",
|
|
4106
|
+
index,
|
|
4107
|
+
"mediaUrl"
|
|
4108
|
+
],
|
|
4109
|
+
message: "Provide exactly one of mediaUrl or mediaAssetId"
|
|
4110
|
+
});
|
|
4111
|
+
}
|
|
4112
|
+
if (creative.thumbnailUrl && creative.thumbnailAssetId) {
|
|
4113
|
+
ctx.addIssue({
|
|
4114
|
+
code: z31.ZodIssueCode.custom,
|
|
4115
|
+
path: [
|
|
4116
|
+
"creatives",
|
|
4117
|
+
index,
|
|
4118
|
+
"thumbnailUrl"
|
|
4119
|
+
],
|
|
4120
|
+
message: "Provide at most one of thumbnailUrl or thumbnailAssetId"
|
|
4121
|
+
});
|
|
4122
|
+
}
|
|
4123
|
+
});
|
|
3890
4124
|
});
|
|
3891
4125
|
var CampaignLifetimeMetricsSchema = z31.object({
|
|
3892
4126
|
spend: z31.number().nonnegative(),
|
|
@@ -3940,8 +4174,32 @@ var FacebookAdAccountSchema = z32.object({
|
|
|
3940
4174
|
source: z32.enum([
|
|
3941
4175
|
"business",
|
|
3942
4176
|
"personal"
|
|
4177
|
+
]),
|
|
4178
|
+
/** Meta Business Portfolio (or the synthetic `personal` group). */
|
|
4179
|
+
portfolioId: z32.string().min(1),
|
|
4180
|
+
portfolioName: z32.string().min(1),
|
|
4181
|
+
portfolioType: z32.enum([
|
|
4182
|
+
"business",
|
|
4183
|
+
"personal"
|
|
3943
4184
|
])
|
|
3944
4185
|
});
|
|
4186
|
+
var FacebookMetaPortfolioSchema = z32.object({
|
|
4187
|
+
id: z32.string().min(1),
|
|
4188
|
+
name: z32.string().min(1),
|
|
4189
|
+
type: z32.enum([
|
|
4190
|
+
"business",
|
|
4191
|
+
"personal"
|
|
4192
|
+
])
|
|
4193
|
+
});
|
|
4194
|
+
var FacebookMetaPageSchema = z32.object({
|
|
4195
|
+
id: z32.string().min(1),
|
|
4196
|
+
name: z32.string().min(1)
|
|
4197
|
+
});
|
|
4198
|
+
var FacebookMetaDestinationsResponseSchema = z32.object({
|
|
4199
|
+
portfolios: z32.array(FacebookMetaPortfolioSchema),
|
|
4200
|
+
adAccounts: z32.array(FacebookAdAccountSchema),
|
|
4201
|
+
pages: z32.array(FacebookMetaPageSchema)
|
|
4202
|
+
});
|
|
3945
4203
|
var MetaCampaignOverridesSchema = z32.object({
|
|
3946
4204
|
dailyBudgetUsd: z32.number().positive().optional(),
|
|
3947
4205
|
targetCountries: z32.array(z32.string().length(2)).max(1).optional(),
|
|
@@ -3950,6 +4208,8 @@ var MetaCampaignOverridesSchema = z32.object({
|
|
|
3950
4208
|
});
|
|
3951
4209
|
var FacebookCreateCampaignBodySchema = z32.object({
|
|
3952
4210
|
adAccountId: MetaAdAccountId,
|
|
4211
|
+
portfolioId: z32.string().min(1).optional(),
|
|
4212
|
+
pageId: z32.string().min(1).optional(),
|
|
3953
4213
|
pixelId: z32.string().optional(),
|
|
3954
4214
|
landingUrl: HttpUrl2.optional(),
|
|
3955
4215
|
campaignConfig: MetaCampaignConfig.optional(),
|
|
@@ -3980,6 +4240,8 @@ var FacebookCampaignStatusResponseSchema = z32.object({
|
|
|
3980
4240
|
var FacebookCreateCampaignResponseSchema = z32.object({
|
|
3981
4241
|
campaignId: z32.string(),
|
|
3982
4242
|
campaignName: z32.string(),
|
|
4243
|
+
portfolioId: z32.string().optional(),
|
|
4244
|
+
portfolioName: z32.string().optional(),
|
|
3983
4245
|
pageId: z32.string().optional(),
|
|
3984
4246
|
pageName: z32.string().optional(),
|
|
3985
4247
|
// Backward-compatible single ad fields (optional)
|
|
@@ -4022,11 +4284,15 @@ var FacebookCreateCampaignFromCreativesBodySchema = z32.object({
|
|
|
4022
4284
|
title: z32.string().max(120),
|
|
4023
4285
|
description: z32.string().max(500),
|
|
4024
4286
|
cta: MetaCtaType,
|
|
4025
|
-
mediaUrl: HttpUrl2,
|
|
4287
|
+
mediaUrl: HttpUrl2.optional(),
|
|
4288
|
+
mediaAssetId: z32.string().uuid().optional(),
|
|
4026
4289
|
thumbnailUrl: HttpUrl2.optional(),
|
|
4290
|
+
thumbnailAssetId: z32.string().uuid().optional(),
|
|
4027
4291
|
creativeId: z32.string().min(1).optional()
|
|
4028
4292
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
4029
4293
|
adAccountId: MetaAdAccountId,
|
|
4294
|
+
portfolioId: z32.string().min(1).optional(),
|
|
4295
|
+
pageId: z32.string().min(1).optional(),
|
|
4030
4296
|
pixelId: z32.string().optional(),
|
|
4031
4297
|
/** Required unless campaignConfig.destination is 'whatsapp' (see superRefine below). */
|
|
4032
4298
|
landingUrl: HttpUrl2.optional(),
|
|
@@ -4046,6 +4312,30 @@ var FacebookCreateCampaignFromCreativesBodySchema = z32.object({
|
|
|
4046
4312
|
message: "landingUrl is required unless destination is whatsapp"
|
|
4047
4313
|
});
|
|
4048
4314
|
}
|
|
4315
|
+
value.creatives.forEach((creative, index) => {
|
|
4316
|
+
if (Boolean(creative.mediaUrl) === Boolean(creative.mediaAssetId)) {
|
|
4317
|
+
ctx.addIssue({
|
|
4318
|
+
code: z32.ZodIssueCode.custom,
|
|
4319
|
+
path: [
|
|
4320
|
+
"creatives",
|
|
4321
|
+
index,
|
|
4322
|
+
"mediaUrl"
|
|
4323
|
+
],
|
|
4324
|
+
message: "Provide exactly one of mediaUrl or mediaAssetId"
|
|
4325
|
+
});
|
|
4326
|
+
}
|
|
4327
|
+
if (creative.thumbnailUrl && creative.thumbnailAssetId) {
|
|
4328
|
+
ctx.addIssue({
|
|
4329
|
+
code: z32.ZodIssueCode.custom,
|
|
4330
|
+
path: [
|
|
4331
|
+
"creatives",
|
|
4332
|
+
index,
|
|
4333
|
+
"thumbnailUrl"
|
|
4334
|
+
],
|
|
4335
|
+
message: "Provide at most one of thumbnailUrl or thumbnailAssetId"
|
|
4336
|
+
});
|
|
4337
|
+
}
|
|
4338
|
+
});
|
|
4049
4339
|
if (value.campaignConfig?.destination?.type === "landing" && value.landingUrl && value.campaignConfig.destination.landingUrl !== value.landingUrl) {
|
|
4050
4340
|
ctx.addIssue({
|
|
4051
4341
|
code: z32.ZodIssueCode.custom,
|
|
@@ -4123,8 +4413,10 @@ var FacebookTaskCreativeSourceSchema = z32.object({
|
|
|
4123
4413
|
});
|
|
4124
4414
|
var FacebookMediaCreativeSourceSchema = z32.object({
|
|
4125
4415
|
type: z32.literal("media"),
|
|
4126
|
-
mediaUrl: HttpUrl2,
|
|
4127
|
-
|
|
4416
|
+
mediaUrl: HttpUrl2.optional(),
|
|
4417
|
+
mediaAssetId: z32.string().uuid().optional(),
|
|
4418
|
+
thumbnailUrl: HttpUrl2.optional(),
|
|
4419
|
+
thumbnailAssetId: z32.string().uuid().optional()
|
|
4128
4420
|
});
|
|
4129
4421
|
var FacebookCreativeSourceSchema = z32.discriminatedUnion("type", [
|
|
4130
4422
|
FacebookTaskCreativeSourceSchema,
|
|
@@ -4143,6 +4435,9 @@ var FacebookCreateCampaignFromTasksBodySchema = z32.object({
|
|
|
4143
4435
|
creativeId: z32.string().min(1).optional()
|
|
4144
4436
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
4145
4437
|
adAccountId: MetaAdAccountId,
|
|
4438
|
+
/** Explicit destination is required for MCP/remote creative publishing. */
|
|
4439
|
+
portfolioId: z32.string().min(1),
|
|
4440
|
+
pageId: z32.string().min(1),
|
|
4146
4441
|
pixelId: z32.string().optional(),
|
|
4147
4442
|
landingUrl: HttpUrl2.optional(),
|
|
4148
4443
|
targetCountry: z32.string().length(2).default("US"),
|
|
@@ -4159,6 +4454,33 @@ var FacebookCreateCampaignFromTasksBodySchema = z32.object({
|
|
|
4159
4454
|
message: "landingUrl is required unless destination is whatsapp"
|
|
4160
4455
|
});
|
|
4161
4456
|
}
|
|
4457
|
+
value.creatives.forEach((creative, index) => {
|
|
4458
|
+
if (creative.source.type !== "media") return;
|
|
4459
|
+
if (Boolean(creative.source.mediaUrl) === Boolean(creative.source.mediaAssetId)) {
|
|
4460
|
+
ctx.addIssue({
|
|
4461
|
+
code: z32.ZodIssueCode.custom,
|
|
4462
|
+
path: [
|
|
4463
|
+
"creatives",
|
|
4464
|
+
index,
|
|
4465
|
+
"source",
|
|
4466
|
+
"mediaUrl"
|
|
4467
|
+
],
|
|
4468
|
+
message: "Provide exactly one of mediaUrl or mediaAssetId"
|
|
4469
|
+
});
|
|
4470
|
+
}
|
|
4471
|
+
if (creative.source.thumbnailUrl && creative.source.thumbnailAssetId) {
|
|
4472
|
+
ctx.addIssue({
|
|
4473
|
+
code: z32.ZodIssueCode.custom,
|
|
4474
|
+
path: [
|
|
4475
|
+
"creatives",
|
|
4476
|
+
index,
|
|
4477
|
+
"source",
|
|
4478
|
+
"thumbnailUrl"
|
|
4479
|
+
],
|
|
4480
|
+
message: "Provide at most one of thumbnailUrl or thumbnailAssetId"
|
|
4481
|
+
});
|
|
4482
|
+
}
|
|
4483
|
+
});
|
|
4162
4484
|
});
|
|
4163
4485
|
|
|
4164
4486
|
// ../../packages/types/src/tiktok.ts
|
|
@@ -7537,6 +7859,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7537
7859
|
confirmation: "credit_consuming",
|
|
7538
7860
|
schemaHash: "bab794a9700a8d3f42b97f444a776e7ed4dcd833038c52588f951b50970ff3fa"
|
|
7539
7861
|
},
|
|
7862
|
+
{
|
|
7863
|
+
name: "check_meta_media_upload",
|
|
7864
|
+
confirmation: "none",
|
|
7865
|
+
schemaHash: "62b1af1d796d9466f8926afc5e6d18c4ddeccd5b0269ca7b21c2f6ebcfd2a945"
|
|
7866
|
+
},
|
|
7540
7867
|
{
|
|
7541
7868
|
name: "cost_dropshipping_project",
|
|
7542
7869
|
confirmation: "state_change",
|
|
@@ -7555,7 +7882,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7555
7882
|
{
|
|
7556
7883
|
name: "create_product_task",
|
|
7557
7884
|
confirmation: "credit_consuming",
|
|
7558
|
-
schemaHash: "
|
|
7885
|
+
schemaHash: "5c5fe82a365c62e44265e79a4100a06820dbbdb2e0ddc296cd066eb2255b1d62"
|
|
7559
7886
|
},
|
|
7560
7887
|
{
|
|
7561
7888
|
name: "create_service_video",
|
|
@@ -7600,7 +7927,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7600
7927
|
{
|
|
7601
7928
|
name: "generate_video_creative",
|
|
7602
7929
|
confirmation: "credit_consuming",
|
|
7603
|
-
schemaHash: "
|
|
7930
|
+
schemaHash: "04e12ef523e4064b0c204d110baad35d414b605068087c625badf40c46429c8a"
|
|
7604
7931
|
},
|
|
7605
7932
|
{
|
|
7606
7933
|
name: "get_meta_campaign_status",
|
|
@@ -7642,6 +7969,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7642
7969
|
confirmation: "none",
|
|
7643
7970
|
schemaHash: "7a014b717a77aac971ea0ab5c0ff47bb13e6cae7ef9442d9dedb15ce36381b15"
|
|
7644
7971
|
},
|
|
7972
|
+
{
|
|
7973
|
+
name: "list_meta_destinations",
|
|
7974
|
+
confirmation: "none",
|
|
7975
|
+
schemaHash: "7a014b717a77aac971ea0ab5c0ff47bb13e6cae7ef9442d9dedb15ce36381b15"
|
|
7976
|
+
},
|
|
7645
7977
|
{
|
|
7646
7978
|
name: "list_product_research_runs",
|
|
7647
7979
|
confirmation: "none",
|
|
@@ -7670,12 +8002,12 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7670
8002
|
{
|
|
7671
8003
|
name: "publish_creatives_to_meta",
|
|
7672
8004
|
confirmation: "external_mutation",
|
|
7673
|
-
schemaHash: "
|
|
8005
|
+
schemaHash: "59b3d6d5ce25812fee3382b0b1c0cf5e07beb1063048dd0a0d0633686cf29da5"
|
|
7674
8006
|
},
|
|
7675
8007
|
{
|
|
7676
8008
|
name: "publish_to_meta",
|
|
7677
8009
|
confirmation: "external_mutation",
|
|
7678
|
-
schemaHash: "
|
|
8010
|
+
schemaHash: "b507735a1fe13657355cf93163b6ead0db151984e6f2702dcdbf76b4037133ca"
|
|
7679
8011
|
},
|
|
7680
8012
|
{
|
|
7681
8013
|
name: "publish_to_shopify",
|
|
@@ -7692,6 +8024,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7692
8024
|
confirmation: "credit_consuming",
|
|
7693
8025
|
schemaHash: "745687cb41f904e293105f93bbe6bc0471f63fe097ab8f85d3df8d9833084844"
|
|
7694
8026
|
},
|
|
8027
|
+
{
|
|
8028
|
+
name: "request_meta_media_upload",
|
|
8029
|
+
confirmation: "none",
|
|
8030
|
+
schemaHash: "32203d205f414c386ee6ab33e265eb2708c108fca4169d4c12e99d7a4109a132"
|
|
8031
|
+
},
|
|
7695
8032
|
{
|
|
7696
8033
|
name: "resolve_pending_action",
|
|
7697
8034
|
confirmation: "credit_consuming",
|
|
@@ -7767,6 +8104,88 @@ var ApproveActorRequest = z42.object({
|
|
|
7767
8104
|
candidateIndex: z42.number().int().min(0).max(ACTOR_CANDIDATE_COUNT - 1)
|
|
7768
8105
|
});
|
|
7769
8106
|
|
|
8107
|
+
// ../../packages/types/src/referral-dashboard.ts
|
|
8108
|
+
import { z as z43 } from "zod";
|
|
8109
|
+
var REFERRAL_DASHBOARD_RAW_STATUSES = [
|
|
8110
|
+
"ACTIVE",
|
|
8111
|
+
"PENDING",
|
|
8112
|
+
"GRACE",
|
|
8113
|
+
"CANCELLED",
|
|
8114
|
+
"EXPIRED"
|
|
8115
|
+
];
|
|
8116
|
+
var REFERRAL_DASHBOARD_STATES = [
|
|
8117
|
+
"active",
|
|
8118
|
+
"pending",
|
|
8119
|
+
"inactive",
|
|
8120
|
+
"no_plan"
|
|
8121
|
+
];
|
|
8122
|
+
var REFERRAL_DASHBOARD_PLAN_KINDS = [
|
|
8123
|
+
"current",
|
|
8124
|
+
"last",
|
|
8125
|
+
"none"
|
|
8126
|
+
];
|
|
8127
|
+
var ReferralDashboardRawStatusSchema = z43.enum(REFERRAL_DASHBOARD_RAW_STATUSES);
|
|
8128
|
+
var ReferralDashboardStateSchema = z43.enum(REFERRAL_DASHBOARD_STATES);
|
|
8129
|
+
var ReferralDashboardPlanKindSchema = z43.enum(REFERRAL_DASHBOARD_PLAN_KINDS);
|
|
8130
|
+
var ReferralDashboardStatusSchema = z43.enum([
|
|
8131
|
+
"all",
|
|
8132
|
+
"active",
|
|
8133
|
+
"pending",
|
|
8134
|
+
"inactive"
|
|
8135
|
+
]).default("all");
|
|
8136
|
+
var ReferralDashboardPlanSchema = z43.string().trim().min(1).max(100).optional();
|
|
8137
|
+
var ReferralDashboardFiltersSchema = z43.object({
|
|
8138
|
+
status: ReferralDashboardStatusSchema,
|
|
8139
|
+
plan: ReferralDashboardPlanSchema
|
|
8140
|
+
});
|
|
8141
|
+
var ReferralDashboardSubscriptionSchema = z43.object({
|
|
8142
|
+
planName: z43.string().nullable(),
|
|
8143
|
+
planKind: ReferralDashboardPlanKindSchema,
|
|
8144
|
+
rawStatus: ReferralDashboardRawStatusSchema.nullable(),
|
|
8145
|
+
state: ReferralDashboardStateSchema,
|
|
8146
|
+
currentPeriodEnd: z43.string().datetime({
|
|
8147
|
+
offset: true
|
|
8148
|
+
}).nullable(),
|
|
8149
|
+
cancelAtPeriodEnd: z43.boolean()
|
|
8150
|
+
});
|
|
8151
|
+
var ReferralDashboardReferralSchema = z43.object({
|
|
8152
|
+
id: z43.string().min(1),
|
|
8153
|
+
registeredAt: z43.string().datetime({
|
|
8154
|
+
offset: true
|
|
8155
|
+
}),
|
|
8156
|
+
// Kept in the response for compatibility with existing referral API clients.
|
|
8157
|
+
// The referrer dashboard intentionally does not render these legacy fields.
|
|
8158
|
+
expiresAt: z43.string().datetime({
|
|
8159
|
+
offset: true
|
|
8160
|
+
}).nullable().optional(),
|
|
8161
|
+
status: z43.string().min(1).optional(),
|
|
8162
|
+
referredUser: z43.object({
|
|
8163
|
+
email: z43.string().nullable()
|
|
8164
|
+
}),
|
|
8165
|
+
subscription: ReferralDashboardSubscriptionSchema
|
|
8166
|
+
});
|
|
8167
|
+
var ReferralDashboardReferralsPageSchema = z43.object({
|
|
8168
|
+
referrals: z43.array(ReferralDashboardReferralSchema),
|
|
8169
|
+
total: z43.number().int().nonnegative()
|
|
8170
|
+
});
|
|
8171
|
+
var ReferralDashboardPlanBreakdownSchema = z43.object({
|
|
8172
|
+
planName: z43.string().nullable(),
|
|
8173
|
+
count: z43.number().int().nonnegative()
|
|
8174
|
+
});
|
|
8175
|
+
var ReferralDashboardMonthlyRegistrationSchema = z43.object({
|
|
8176
|
+
month: z43.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/),
|
|
8177
|
+
count: z43.number().int().nonnegative()
|
|
8178
|
+
});
|
|
8179
|
+
var ReferralDashboardOverviewSchema = z43.object({
|
|
8180
|
+
totalReferredUsers: z43.number().int().nonnegative(),
|
|
8181
|
+
activeUsers: z43.number().int().nonnegative(),
|
|
8182
|
+
pendingUsers: z43.number().int().nonnegative(),
|
|
8183
|
+
inactiveUsers: z43.number().int().nonnegative(),
|
|
8184
|
+
noPlanUsers: z43.number().int().nonnegative(),
|
|
8185
|
+
planBreakdown: z43.array(ReferralDashboardPlanBreakdownSchema),
|
|
8186
|
+
monthlyRegistrations: z43.array(ReferralDashboardMonthlyRegistrationSchema).refine((months) => months.length === 6, "Expected six monthly registration buckets")
|
|
8187
|
+
});
|
|
8188
|
+
|
|
7770
8189
|
// src/resolve-media.ts
|
|
7771
8190
|
var MAX_MEDIA_BYTES = CAMPAIGN_LIMITS.MAX_FILE_SIZE_BYTES;
|
|
7772
8191
|
var MEDIA_FORMATS = [
|
|
@@ -8006,10 +8425,10 @@ async function resolveMediaRef(ref, opts) {
|
|
|
8006
8425
|
__name(resolveMediaRef, "resolveMediaRef");
|
|
8007
8426
|
|
|
8008
8427
|
// src/tools/shared.ts
|
|
8009
|
-
import { z as
|
|
8010
|
-
var Lang =
|
|
8011
|
-
var Country =
|
|
8012
|
-
var LandingStrategyEnum =
|
|
8428
|
+
import { z as z44 } from "zod";
|
|
8429
|
+
var Lang = z44.string().min(2).max(10);
|
|
8430
|
+
var Country = z44.string().min(2).max(3);
|
|
8431
|
+
var LandingStrategyEnum = z44.enum([
|
|
8013
8432
|
"auto",
|
|
8014
8433
|
"classic",
|
|
8015
8434
|
"pain_solution",
|
|
@@ -8018,12 +8437,12 @@ var LandingStrategyEnum = z43.enum([
|
|
|
8018
8437
|
"offer_value",
|
|
8019
8438
|
"lifestyle_identity"
|
|
8020
8439
|
]);
|
|
8021
|
-
var LandingImageFormatEnum =
|
|
8440
|
+
var LandingImageFormatEnum = z44.enum([
|
|
8022
8441
|
"9:16",
|
|
8023
8442
|
"4:5",
|
|
8024
8443
|
"1:1"
|
|
8025
8444
|
]);
|
|
8026
|
-
var VideoPresetEnum =
|
|
8445
|
+
var VideoPresetEnum = z44.enum([
|
|
8027
8446
|
"auto",
|
|
8028
8447
|
"ugc_dynamic",
|
|
8029
8448
|
"ugc_how_to",
|
|
@@ -8036,7 +8455,7 @@ var VideoPresetEnum = z43.enum([
|
|
|
8036
8455
|
"pixar_problem",
|
|
8037
8456
|
"pixar_sassy"
|
|
8038
8457
|
]);
|
|
8039
|
-
var TaskCreativeVariantVideoPresetEnum =
|
|
8458
|
+
var TaskCreativeVariantVideoPresetEnum = z44.enum([
|
|
8040
8459
|
"auto",
|
|
8041
8460
|
"ugc_dynamic",
|
|
8042
8461
|
"ugc_how_to",
|
|
@@ -8107,7 +8526,7 @@ function resolveVideoPreset(preset) {
|
|
|
8107
8526
|
return VIDEO_PRESET_RESOLUTION[preset] ?? null;
|
|
8108
8527
|
}
|
|
8109
8528
|
__name(resolveVideoPreset, "resolveVideoPreset");
|
|
8110
|
-
var VideoTierEnum =
|
|
8529
|
+
var VideoTierEnum = z44.enum([
|
|
8111
8530
|
"basic"
|
|
8112
8531
|
]);
|
|
8113
8532
|
function resolveVideoTier(_tier) {
|
|
@@ -8118,27 +8537,21 @@ function resolveWireVideoTier(tier, durationSeconds) {
|
|
|
8118
8537
|
return durationSeconds === 30 ? "standard" : resolveVideoTier(tier);
|
|
8119
8538
|
}
|
|
8120
8539
|
__name(resolveWireVideoTier, "resolveWireVideoTier");
|
|
8121
|
-
function assertVideoPresetDurationCompatibility(videoPreset, durationSeconds) {
|
|
8122
|
-
if (videoPreset === "problem_solution" && durationSeconds !== 30) {
|
|
8123
|
-
throw new Error("Problema y Soluci\xF3n solo est\xE1 disponible en videos Multiescena de 30 segundos.");
|
|
8124
|
-
}
|
|
8125
|
-
}
|
|
8126
|
-
__name(assertVideoPresetDurationCompatibility, "assertVideoPresetDurationCompatibility");
|
|
8127
8540
|
function resolveVideoFormatForDuration(durationSeconds) {
|
|
8128
8541
|
return durationSeconds === 30 ? "double_30s" : "single_15s";
|
|
8129
8542
|
}
|
|
8130
8543
|
__name(resolveVideoFormatForDuration, "resolveVideoFormatForDuration");
|
|
8131
|
-
var VideoResolutionEnum =
|
|
8544
|
+
var VideoResolutionEnum = z44.enum([
|
|
8132
8545
|
"480p",
|
|
8133
8546
|
"720p"
|
|
8134
8547
|
]);
|
|
8135
|
-
var VideoStyleEnum =
|
|
8548
|
+
var VideoStyleEnum = z44.enum([
|
|
8136
8549
|
"ugc",
|
|
8137
8550
|
"cartoon_3d",
|
|
8138
8551
|
"product_only",
|
|
8139
8552
|
"ai_dynamic"
|
|
8140
8553
|
]);
|
|
8141
|
-
var NarrativeStyleEnum =
|
|
8554
|
+
var NarrativeStyleEnum = z44.enum([
|
|
8142
8555
|
"review",
|
|
8143
8556
|
"ugc_how_to",
|
|
8144
8557
|
"problem_product_cta",
|
|
@@ -8155,7 +8568,7 @@ var NarrativeStyleEnum = z43.enum([
|
|
|
8155
8568
|
"ugc_skeptic_confession",
|
|
8156
8569
|
"ugc_demo_review"
|
|
8157
8570
|
]);
|
|
8158
|
-
var PublicNarrativeStyleEnum =
|
|
8571
|
+
var PublicNarrativeStyleEnum = z44.enum([
|
|
8159
8572
|
"review",
|
|
8160
8573
|
"ugc_how_to",
|
|
8161
8574
|
"ugc_unboxing_chaos",
|
|
@@ -8167,7 +8580,7 @@ var PublicNarrativeStyleEnum = z43.enum([
|
|
|
8167
8580
|
"animated_problem_pixar",
|
|
8168
8581
|
"sassy_product_pixar"
|
|
8169
8582
|
]);
|
|
8170
|
-
var VideoModelEnum =
|
|
8583
|
+
var VideoModelEnum = z44.enum([
|
|
8171
8584
|
"veo",
|
|
8172
8585
|
"wan",
|
|
8173
8586
|
"bytedance",
|
|
@@ -8175,25 +8588,25 @@ var VideoModelEnum = z43.enum([
|
|
|
8175
8588
|
"grok",
|
|
8176
8589
|
"kling"
|
|
8177
8590
|
]);
|
|
8178
|
-
var ProductReferenceUserChoiceEnum =
|
|
8591
|
+
var ProductReferenceUserChoiceEnum = z44.enum([
|
|
8179
8592
|
"same_product",
|
|
8180
8593
|
"variants",
|
|
8181
8594
|
"different_products",
|
|
8182
8595
|
"combo_distinct"
|
|
8183
8596
|
]);
|
|
8184
|
-
var ProductReferenceCandidateGroupingEnum =
|
|
8597
|
+
var ProductReferenceCandidateGroupingEnum = z44.enum([
|
|
8185
8598
|
"same_product_same_variant",
|
|
8186
8599
|
"same_product_different_variants",
|
|
8187
8600
|
"different_products",
|
|
8188
8601
|
"unclear"
|
|
8189
8602
|
]);
|
|
8190
|
-
var GapDurationChoiceMirror =
|
|
8191
|
-
angleIndex:
|
|
8192
|
-
recommendedSec:
|
|
8193
|
-
|
|
8194
|
-
|
|
8603
|
+
var GapDurationChoiceMirror = z44.object({
|
|
8604
|
+
angleIndex: z44.number().int().nonnegative(),
|
|
8605
|
+
recommendedSec: z44.union([
|
|
8606
|
+
z44.literal(15),
|
|
8607
|
+
z44.literal(30)
|
|
8195
8608
|
]),
|
|
8196
|
-
estimatedDeltaCredits:
|
|
8609
|
+
estimatedDeltaCredits: z44.number().int().nonnegative().optional()
|
|
8197
8610
|
});
|
|
8198
8611
|
function buildSelectedImageUrlsHash(urls) {
|
|
8199
8612
|
const input = [
|
|
@@ -8207,13 +8620,13 @@ function buildSelectedImageUrlsHash(urls) {
|
|
|
8207
8620
|
return `fnv1a:${hash.toString(16).padStart(8, "0")}:${urls.length}`;
|
|
8208
8621
|
}
|
|
8209
8622
|
__name(buildSelectedImageUrlsHash, "buildSelectedImageUrlsHash");
|
|
8210
|
-
var ImageCreativeStyleEnum =
|
|
8623
|
+
var ImageCreativeStyleEnum = z44.enum([
|
|
8211
8624
|
"studio",
|
|
8212
8625
|
"floating",
|
|
8213
8626
|
"ingredients",
|
|
8214
8627
|
"in_use"
|
|
8215
8628
|
]);
|
|
8216
|
-
var ImageCreativeArchetypeEnum =
|
|
8629
|
+
var ImageCreativeArchetypeEnum = z44.enum([
|
|
8217
8630
|
"direct_response_product_ad",
|
|
8218
8631
|
"hero_outcome",
|
|
8219
8632
|
"offer_value_prop",
|
|
@@ -8222,23 +8635,23 @@ var ImageCreativeArchetypeEnum = z43.enum([
|
|
|
8222
8635
|
"problem_solution",
|
|
8223
8636
|
"lifestyle_identity"
|
|
8224
8637
|
]);
|
|
8225
|
-
var ImageCreativeVisualTreatmentEnum =
|
|
8638
|
+
var ImageCreativeVisualTreatmentEnum = z44.enum([
|
|
8226
8639
|
"performance_product_ad",
|
|
8227
8640
|
"premium_dtc_carousel"
|
|
8228
8641
|
]);
|
|
8229
|
-
var ImageCreativeAspectRatioEnum =
|
|
8642
|
+
var ImageCreativeAspectRatioEnum = z44.enum([
|
|
8230
8643
|
"1:1",
|
|
8231
8644
|
"4:5",
|
|
8232
8645
|
"9:16",
|
|
8233
8646
|
"16:9"
|
|
8234
8647
|
]);
|
|
8235
|
-
var ImageRefSchema =
|
|
8236
|
-
url:
|
|
8237
|
-
path:
|
|
8648
|
+
var ImageRefSchema = z44.object({
|
|
8649
|
+
url: z44.string().url().optional().describe('URL p\xFAblica HTTPS de la foto. Ej: una URL de Shopify CDN, Cloudinary, Imgur. \xDAsala cuando el usuario te diga "est\xE1 en X.com/foto.jpg" o cuando la foto ya est\xE9 hospedada.'),
|
|
8650
|
+
path: z44.string().min(1).optional().describe('Ruta del archivo en el computador del usuario. Ej: "/Users/maria/Desktop/foto.jpg", "~/Pictures/camiseta.png", "C:\\Users\\Maria\\Desktop\\foto.jpg". El MCP local la lee del disco y la sube. SOLO funciona cuando el MCP corre local (stdio). NO funciona en mcp.productmaker.app (HTTP) \u2014 ah\xED usa `url`.')
|
|
8238
8651
|
}).refine((v) => Boolean(v.url) !== Boolean(v.path), {
|
|
8239
8652
|
message: "each image entry must have exactly one of `url` or `path`"
|
|
8240
8653
|
});
|
|
8241
|
-
var ImagesInput =
|
|
8654
|
+
var ImagesInput = z44.array(ImageRefSchema).min(1).max(5).describe('1 a 5 fotos del MISMO producto. La PRIMERA es la principal (hero); las dem\xE1s son \xE1ngulos adicionales. NO mezcles productos distintos en una sola llamada.\n\nCada entrada tiene EXACTAMENTE uno de:\n\u2022 `url` \u2014 URL p\xFAblica HTTPS (Shopify CDN, Cloudinary). Prefi\xE9rela si la foto ya est\xE1 hospedada.\n\u2022 `path` \u2014 Ruta del archivo en el computador del usuario (ej. "/Users/maria/Desktop/foto.jpg", "~/Downloads/camiseta.png"). \xDAsala cuando el usuario te dijo d\xF3nde guard\xF3 la foto. Solo MCP local \u2014 el cliente web rechaza paths.\n\nSi el usuario te peg\xF3 la foto en el chat pero no te dio ni URL ni ruta: PREG\xDANTALE d\xF3nde la tiene guardada (Escritorio, Descargas, etc.). Si el `path` falla con IMAGE_PATH_NOT_FOUND, prueba 1-2 ubicaciones obvias alternativas (~/Desktop, ~/Downloads) antes de molestar al usuario. Tras 3 intentos fallidos, p\xEDdele la ruta completa.');
|
|
8242
8655
|
function asText(value) {
|
|
8243
8656
|
return {
|
|
8244
8657
|
content: [
|
|
@@ -8291,14 +8704,14 @@ async function submitCreative(api, opts) {
|
|
|
8291
8704
|
});
|
|
8292
8705
|
}
|
|
8293
8706
|
__name(submitCreative, "submitCreative");
|
|
8294
|
-
var ResearchTargetCountryEnum =
|
|
8707
|
+
var ResearchTargetCountryEnum = z44.enum([
|
|
8295
8708
|
"CO",
|
|
8296
8709
|
"MX",
|
|
8297
8710
|
"PE",
|
|
8298
8711
|
"EC",
|
|
8299
8712
|
"CL"
|
|
8300
8713
|
]);
|
|
8301
|
-
var ResearchCategoryEnum =
|
|
8714
|
+
var ResearchCategoryEnum = z44.enum([
|
|
8302
8715
|
"auto",
|
|
8303
8716
|
"automotive",
|
|
8304
8717
|
"kitchen",
|
|
@@ -8371,6 +8784,15 @@ var McpErrorCode = {
|
|
|
8371
8784
|
SHOPIFY_PUBLISH_FAILED: "SHOPIFY_PUBLISH_FAILED",
|
|
8372
8785
|
// Meta / Facebook Ads
|
|
8373
8786
|
META_NO_FB_PAGE: "META_NO_FB_PAGE",
|
|
8787
|
+
META_PORTFOLIO_MISMATCH: "META_PORTFOLIO_MISMATCH",
|
|
8788
|
+
META_PAGE_SELECTION_REQUIRED: "META_PAGE_SELECTION_REQUIRED",
|
|
8789
|
+
META_INVALID_PAGE: "META_INVALID_PAGE",
|
|
8790
|
+
META_STAGED_ASSET_NOT_FOUND: "META_STAGED_ASSET_NOT_FOUND",
|
|
8791
|
+
META_STAGED_ASSET_AMBIGUOUS: "META_STAGED_ASSET_AMBIGUOUS",
|
|
8792
|
+
META_STAGED_ASSET_TYPE_MISMATCH: "META_STAGED_ASSET_TYPE_MISMATCH",
|
|
8793
|
+
META_STAGED_ASSET_INVALID: "META_STAGED_ASSET_INVALID",
|
|
8794
|
+
META_PUBLICATION_RECONCILIATION_REQUIRED: "META_PUBLICATION_RECONCILIATION_REQUIRED",
|
|
8795
|
+
META_PUBLICATION_IN_PROGRESS: "META_PUBLICATION_IN_PROGRESS",
|
|
8374
8796
|
META_NO_ADS_CREATED: "META_NO_ADS_CREATED",
|
|
8375
8797
|
META_NO_ASSETS_READY: "META_NO_ASSETS_READY",
|
|
8376
8798
|
META_NO_THUMBNAIL_SOURCE: "META_NO_THUMBNAIL_SOURCE",
|
|
@@ -8423,6 +8845,15 @@ var CODE_MESSAGES = {
|
|
|
8423
8845
|
SHOPIFY_PUBLISH_FAILED: "Shopify rejected the publish. Check that the product/landing does not already exist on the store and that your Shopify plan allows additional products.",
|
|
8424
8846
|
// Meta — high-frequency
|
|
8425
8847
|
META_NO_FB_PAGE: "No Facebook Page is associated with this account. Create one at https://www.facebook.com/pages/create before publishing (Meta requires a Page to run ads).",
|
|
8848
|
+
META_PORTFOLIO_MISMATCH: "The selected Meta portfolio does not contain the selected ad account. Call `list_meta_destinations` again and choose an account from that portfolio.",
|
|
8849
|
+
META_PAGE_SELECTION_REQUIRED: "Meta has multiple Facebook Pages available. Ask the user to choose one with `list_meta_destinations` and retry with its exact `pageId`.",
|
|
8850
|
+
META_INVALID_PAGE: "The selected Facebook Page is not available for this Meta connection. Call `list_meta_destinations` again and choose an available `pageId`.",
|
|
8851
|
+
META_STAGED_ASSET_NOT_FOUND: "The temporary media upload was not found or expired. Re-upload the media and retry the Meta publication.",
|
|
8852
|
+
META_STAGED_ASSET_AMBIGUOUS: "The temporary media upload could not be uniquely resolved. Re-upload the media and retry the Meta publication.",
|
|
8853
|
+
META_STAGED_ASSET_TYPE_MISMATCH: "The temporary media type does not match the creative. Re-upload the correct image or video and retry.",
|
|
8854
|
+
META_STAGED_ASSET_INVALID: "The temporary media is empty or exceeds the server size limit. Re-upload the file and retry.",
|
|
8855
|
+
META_PUBLICATION_RECONCILIATION_REQUIRED: "A previous Meta publication with these exact inputs may already exist. Check Meta before retrying so you do not create a duplicate campaign.",
|
|
8856
|
+
META_PUBLICATION_IN_PROGRESS: "A Meta publication with these exact inputs is already in progress. Wait for it to finish, then check the campaign status.",
|
|
8426
8857
|
META_NO_ADS_CREATED: "No ads could be created. Most likely: the Meta Ads account has no payment method, no Facebook Page is linked, or the Facebook app lost permissions. Verify all three at https://business.facebook.com.",
|
|
8427
8858
|
META_NO_ASSETS_READY: "The task has no videos or ad creative ready to publish yet. Wait for generation to finish or regenerate the assets.",
|
|
8428
8859
|
META_NO_THUMBNAIL_SOURCE: "No image is available to use as the video thumbnail. Upload a product image or generate the ad creative before publishing.",
|
|
@@ -8536,6 +8967,12 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
|
|
|
8536
8967
|
McpErrorCode.CAMPAIGN_MISSING_LANDING_URL,
|
|
8537
8968
|
McpErrorCode.CAMPAIGN_RECONCILIATION_REQUIRED,
|
|
8538
8969
|
McpErrorCode.META_NO_LANDING_URL,
|
|
8970
|
+
McpErrorCode.META_PORTFOLIO_MISMATCH,
|
|
8971
|
+
McpErrorCode.META_PAGE_SELECTION_REQUIRED,
|
|
8972
|
+
McpErrorCode.META_INVALID_PAGE,
|
|
8973
|
+
McpErrorCode.META_STAGED_ASSET_NOT_FOUND,
|
|
8974
|
+
McpErrorCode.META_STAGED_ASSET_AMBIGUOUS,
|
|
8975
|
+
McpErrorCode.META_STAGED_ASSET_TYPE_MISMATCH,
|
|
8539
8976
|
McpErrorCode.META_DAILY_BUDGET_TOO_HIGH,
|
|
8540
8977
|
McpErrorCode.META_CAMPAIGN_CONFIG_INVALID,
|
|
8541
8978
|
McpErrorCode.META_WHATSAPP_INVALID_NUMBER,
|
|
@@ -8557,6 +8994,15 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
|
|
|
8557
8994
|
McpErrorCode.META_MISSING_PAYMENT,
|
|
8558
8995
|
McpErrorCode.META_ACCOUNT_RESTRICTED,
|
|
8559
8996
|
McpErrorCode.META_NO_FB_PAGE,
|
|
8997
|
+
McpErrorCode.META_PORTFOLIO_MISMATCH,
|
|
8998
|
+
McpErrorCode.META_PAGE_SELECTION_REQUIRED,
|
|
8999
|
+
McpErrorCode.META_INVALID_PAGE,
|
|
9000
|
+
McpErrorCode.META_STAGED_ASSET_NOT_FOUND,
|
|
9001
|
+
McpErrorCode.META_STAGED_ASSET_AMBIGUOUS,
|
|
9002
|
+
McpErrorCode.META_STAGED_ASSET_TYPE_MISMATCH,
|
|
9003
|
+
McpErrorCode.META_STAGED_ASSET_INVALID,
|
|
9004
|
+
McpErrorCode.META_PUBLICATION_RECONCILIATION_REQUIRED,
|
|
9005
|
+
McpErrorCode.META_PUBLICATION_IN_PROGRESS,
|
|
8560
9006
|
McpErrorCode.TIKTOK_TOKEN_EXPIRED,
|
|
8561
9007
|
McpErrorCode.TIKTOK_PERMISSION_DENIED,
|
|
8562
9008
|
McpErrorCode.TIKTOK_MISSING_PAYMENT,
|
|
@@ -8910,53 +9356,53 @@ function renderOutputsMarkdown(outputs) {
|
|
|
8910
9356
|
__name(renderOutputsMarkdown, "renderOutputsMarkdown");
|
|
8911
9357
|
|
|
8912
9358
|
// src/tools/tasks.ts
|
|
8913
|
-
var CreateProductTaskInput =
|
|
9359
|
+
var CreateProductTaskInput = z45.object({
|
|
8914
9360
|
images: ImagesInput.optional().describe("1\u20135 fotos del mismo producto. Omitir \xFAnicamente cuando projectId apunta a un proyecto propio con imagen guardada."),
|
|
8915
|
-
projectId:
|
|
8916
|
-
productUrl:
|
|
9361
|
+
projectId: z45.string().uuid().optional().describe("Opcional. Proyecto guiado propio. Permite continuar sin volver a subir la imagen guardada; nunca evita cr\xE9ditos, cuotas ni confirmaciones."),
|
|
9362
|
+
productUrl: z45.string().url().optional().describe("Opcional. URL de la p\xE1gina del producto (Shopify, AliExpress, Amazon, etc.) para enriquecer el contexto. Si se omite, la IA infiere todo desde la imagen."),
|
|
8917
9363
|
language: Lang.describe('C\xF3digo de idioma para el contenido generado. Ejemplos: "es", "en", "pt". REQUERIDO \u2014 si el usuario no lo especific\xF3, PREG\xDANTALE antes de invocar. NUNCA infieras desde el contexto del chat ni asumas "es" por default.'),
|
|
8918
9364
|
country: Country.describe('C\xF3digo ISO del pa\xEDs objetivo. Ejemplos: "CO", "MX", "US", "AR". Define moneda, m\xE9todos de pago locales y tono. REQUERIDO \u2014 si el usuario no lo especific\xF3, PREG\xDANTALE antes de invocar. NUNCA infieras desde el contexto del chat.'),
|
|
8919
|
-
price:
|
|
8920
|
-
compareAtPrice:
|
|
8921
|
-
offer:
|
|
8922
|
-
paymentMethods:
|
|
8923
|
-
productContext:
|
|
8924
|
-
productLabel:
|
|
8925
|
-
videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla. Default "auto" (la IA escoge tras analizar el producto). Todos los presets funcionan a 15s y 30s
|
|
8926
|
-
durationSeconds:
|
|
8927
|
-
|
|
8928
|
-
|
|
9365
|
+
price: z45.string().min(1).max(120).optional().describe('Precio que se mostrar\xE1 en la landing y video. Formato libre \u2014 ej. "$89.900", "USD 29.99", "MXN 599". Opcional pero MUY IMPACTANTE: si el usuario no lo mencion\xF3, PREG\xDANTALE antes de invocar. Si te dice "t\xFA elige", om\xEDtelo y el backend sugiere uno.'),
|
|
9366
|
+
compareAtPrice: z45.string().min(1).max(120).optional().describe('Opcional. Precio anterior para comunicar un descuento real en landing e im\xE1genes. Ej. "$129.000" cuando price="$89.900".'),
|
|
9367
|
+
offer: z45.string().min(1).max(240).optional().describe('Oferta o promoci\xF3n a destacar. Ej. "2x1", "Env\xEDo gratis hoy", "50% OFF". Opcional pero MUY IMPACTANTE: si el usuario no lo mencion\xF3, PREG\xDANTALE antes de invocar (es v\xE1lido que diga "sin oferta"). Si te dice "t\xFA elige", om\xEDtelo.'),
|
|
9368
|
+
paymentMethods: z45.string().min(1).max(240).optional().describe('Opcional. M\xE9todos de pago que puede mostrar la landing. Ej. "Tarjeta, PSE, Nequi, Contraentrega". Si se omite, la landing no muestra m\xE9todos de pago.'),
|
|
9369
|
+
productContext: z45.string().max(3e3).optional().describe("Opcional. Informacion adicional del producto que la IA debe usar como contexto prioritario: uso real, comprador, restricciones, diferenciadores o direccion creativa. No reemplaza la investigacion de competencia; si hay competencia, guia el primer angulo y los outputs no deben contradecirlo."),
|
|
9370
|
+
productLabel: z45.string().trim().max(80).optional().describe('Opcional. Qu\xE9 es el producto, en pocas palabras (ej. "quita pelusas para ropa"). Es AUTORITATIVO: si lo env\xEDas, la IA usa esta identidad y no la reemplaza por otra categor\xEDa. Si lo omites, la IA detecta el producto desde la imagen.'),
|
|
9371
|
+
videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla. Default "auto" (la IA escoge tras analizar el producto). Todos los presets funcionan a 15s (single_call) y 30s (Multiescena). Opciones: "auto", "ugc_dynamic" (review con experiencia y prueba), "ugc_how_to" (tutorial con pasos y resultado visible), "ugc_unboxing_chaos" (apertura y primera prueba), "problem_solution" (problema concreto, producto como soluci\xF3n y CTA), "product_showcase" (producto ejecutando una funci\xF3n real), "wearable_showcase" (prenda o accesorio modelado), "cinematic" (TV spot con receta cinematogr\xE1fica), "wild_card" (una regla visual experimental original), "pixar_problem" (problema animado 3D), "pixar_sassy" (producto que habla). Si el usuario pide algo fuera de esta lista, p\xEDdele una opci\xF3n v\xE1lida.'),
|
|
9372
|
+
durationSeconds: z45.union([
|
|
9373
|
+
z45.literal(15),
|
|
9374
|
+
z45.literal(30)
|
|
8929
9375
|
]).default(15).describe("Opcional. Duraci\xF3n objetivo del video: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
|
|
8930
|
-
pauseForAngleReview:
|
|
9376
|
+
pauseForAngleReview: z45.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo para que el usuario lo confirme o edite antes del render. \xDAsalo si quiere decidir 15/30s cuando aparezca un winner. Default false."),
|
|
8931
9377
|
videoTier: VideoTierEnum.optional().describe('Opcional. Estilo del video. "basic" = "Realista" para 15s; al pedir durationSeconds=30 el sistema selecciona internamente Multiescena. No se expone el tier interno directamente.'),
|
|
8932
9378
|
videoResolution: VideoResolutionEnum.optional().describe('Opcional. Resoluci\xF3n del render. En la web se pregunta "\xBFResoluci\xF3n del video?". "480p" = excelente calidad y m\xE1s econ\xF3mico (RECOMENDADO, default si se omite); "720p" = m\xE1xima nitidez, entre 3% y 15% m\xE1s caro seg\xFAn formato y tier. Si el usuario no menciona resoluci\xF3n, OMITE este campo (el backend usa 480p).'),
|
|
8933
|
-
multiAngleVideos:
|
|
9379
|
+
multiAngleVideos: z45.boolean().optional().describe("Opcional. Si true, genera m\xFAltiples \xE1ngulos de venta en paralelo (3-5 videos). Si false/omitido, genera 1 video del \xE1ngulo principal. Default false."),
|
|
8934
9380
|
angleNarratives: PublicNarrativeStyleEnum.array().max(3).optional().describe('Opcional. Narrativa POR \xC1NGULO (posicional). Valores actuales: "review", "ugc_how_to", "ugc_unboxing_chaos", "problem_product_cta" (problema concreto, producto como soluci\xF3n y CTA), "product_showcase", "wearable_showcase", "cinematic", "wild_card", "animated_problem_pixar", "sassy_product_pixar". angleNarratives[i] fuerza la narrativa del \xE1ngulo i; las posiciones restantes las elige la IA. Requiere multiAngleVideos:true para varios \xE1ngulos.'),
|
|
8935
|
-
outputs:
|
|
9381
|
+
outputs: z45.array(z45.enum([
|
|
8936
9382
|
"landing",
|
|
8937
9383
|
"video",
|
|
8938
9384
|
"image"
|
|
8939
9385
|
])).min(2).max(3).optional().describe('Opcional. Qu\xE9 outputs generar. Default: todos ["landing","video","image"]. Para 2 outputs: ["landing","image"] (sin video), ["video","image"] (sin landing), ["landing","video"] (sin imagen). Para 1 solo output, usa generate_landing / generate_video_creative / generate_image_creatives.'),
|
|
8940
|
-
competitorScanEnabled:
|
|
8941
|
-
brandId:
|
|
8942
|
-
actorId:
|
|
8943
|
-
creativeDirection:
|
|
8944
|
-
headline:
|
|
8945
|
-
subheadline:
|
|
9386
|
+
competitorScanEnabled: z45.boolean().optional().describe("Opcional. Si true (default), el sistema analiza ads de competencia en Meta Ad Library y enriquece los \xE1ngulos generados con hooks y painPoints validados por el mercado (\u2248$0.20-0.48 extra por task \u2014 var\xEDa con cu\xE1ntas transcripciones de video se capturen). Si false, el pipeline genera \xE1ngulos sin esa se\xF1al \u2014 usa esto SOLO si el usuario ya tiene su propia investigaci\xF3n de mercado o quiere ahorrar costos en productos muy nicho sin presencia en Meta. Default true."),
|
|
9387
|
+
brandId: z45.string().uuid().optional().describe("Opcional. ID de una marca del usuario; si se env\xEDa, el sistema usa su voz, colores y persona para enriquecer los outputs generados."),
|
|
9388
|
+
actorId: z45.string().trim().min(1).optional().describe("Opcional. ID de un personaje guardado (usa list_actors para verlos). Si lo env\xEDas, su foto y su voz mandan sobre la descripci\xF3n de texto."),
|
|
9389
|
+
creativeDirection: z45.string().max(500).optional().describe('Opcional. Direcci\xF3n creativa en texto libre (colores, mood, estilo) que aplica a los outputs de IMAGEN y LANDING (no al video \u2014 el estilo/ambiente del video se dirige con scriptNotes). M\xC1XIMA PRIORIDAD: anula marca, colores y estilo por defecto. Ej. "amarillo y azul, minimalista". M\xE1x 500 caracteres.'),
|
|
9390
|
+
headline: z45.string().max(120).optional().describe("Opcional. Titular EXACTO a renderizar. Se imprime tal cual: la IA no lo reescribe, no lo acorta y no lo traduce. Se aplica a las IM\xC1GENES y a la portada de la landing de esta task (el video no lo usa; para el video usa scriptNotes). \xDAsalo cuando el copy es de marca y no es negociable. M\xE1x 120 caracteres. Si lo omites, la IA escribe el titular."),
|
|
9391
|
+
subheadline: z45.string().max(160).optional().describe("Opcional. Subt\xEDtulo EXACTO a renderizar, misma regla que headline. Independiente: puedes fijar solo el subt\xEDtulo. M\xE1x 160 caracteres."),
|
|
8946
9392
|
landingStrategy: LandingStrategyEnum.default("classic").describe('Opcional. Estructura de la landing cl\xE1sica cuando landing est\xE1 entre outputs: "classic" (estructura original probada y recomendada; default del primer rollout), "auto" (la IA decide; experimental), "pain_solution", "social_proof" (Landing Viral: UGC, WhatsApp, Instagram y evidencia social), "benefits_clarity", "offer_value" o "lifestyle_identity". Default "classic".'),
|
|
8947
9393
|
landingImageFormat: LandingImageFormatEnum.default("9:16").describe('Opcional. Formato/relaci\xF3n de aspecto de las im\xE1genes de la landing cl\xE1sica cuando landing est\xE1 entre outputs: "9:16" (vertical, por defecto), "4:5" (vertical compacto para feed) o "1:1" (cuadrado). Default "9:16".')
|
|
8948
9394
|
}).strict();
|
|
8949
|
-
var CreateServiceVideoInput =
|
|
8950
|
-
landingUrl:
|
|
8951
|
-
additionalContext:
|
|
9395
|
+
var CreateServiceVideoInput = z45.object({
|
|
9396
|
+
landingUrl: z45.string().url().describe("URL p\xFAblica de la landing del servicio. Es suficiente para generar el video; los medios son opcionales en la web."),
|
|
9397
|
+
additionalContext: z45.string().max(3e3).optional().describe('Instrucci\xF3n natural prioritaria. Ej.: "adapta el ritmo de estos competidores", "enfatiza reservas de demos" o restricciones de claims.'),
|
|
8952
9398
|
language: Lang.describe("Idioma del video: es, en o pt."),
|
|
8953
|
-
voiceId:
|
|
8954
|
-
brandId:
|
|
8955
|
-
reviewMode:
|
|
9399
|
+
voiceId: z45.string().trim().min(1).optional().describe('Opcional. Voz de ElevenLabs para toda la narraci\xF3n. Usa "auto" para que la IA la alinee con el presentador o env\xEDa un ID concreto de la lista de voces de la web.'),
|
|
9400
|
+
brandId: z45.string().uuid().optional().describe("Marca guardada del usuario. Aplica voz, mensajes, paleta y persona predeterminada."),
|
|
9401
|
+
reviewMode: z45.enum([
|
|
8956
9402
|
"automatic",
|
|
8957
9403
|
"guided"
|
|
8958
9404
|
]).default("automatic").describe("automatic = la IA elige concepto y guion sin pausas; guided = el usuario aprueba concepto y storyboard."),
|
|
8959
|
-
videoPreset:
|
|
9405
|
+
videoPreset: z45.enum([
|
|
8960
9406
|
"auto",
|
|
8961
9407
|
"ugc_dynamic",
|
|
8962
9408
|
"ugc_how_to",
|
|
@@ -8964,35 +9410,35 @@ var CreateServiceVideoInput = z44.object({
|
|
|
8964
9410
|
"wild_card"
|
|
8965
9411
|
]).default("auto")
|
|
8966
9412
|
}).strict();
|
|
8967
|
-
var GetTaskStatusInput =
|
|
8968
|
-
taskId:
|
|
8969
|
-
waitSeconds:
|
|
8970
|
-
});
|
|
8971
|
-
var ListTasksInput =
|
|
8972
|
-
limit:
|
|
8973
|
-
offset:
|
|
8974
|
-
search:
|
|
8975
|
-
});
|
|
8976
|
-
var EditTaskDraftInput =
|
|
8977
|
-
taskId:
|
|
8978
|
-
angleIndex:
|
|
8979
|
-
angle:
|
|
8980
|
-
angle:
|
|
8981
|
-
buyerPersona:
|
|
8982
|
-
actor:
|
|
8983
|
-
actorId:
|
|
8984
|
-
gender:
|
|
9413
|
+
var GetTaskStatusInput = z45.object({
|
|
9414
|
+
taskId: z45.string().min(1),
|
|
9415
|
+
waitSeconds: z45.number().int().min(0).max(120).optional()
|
|
9416
|
+
});
|
|
9417
|
+
var ListTasksInput = z45.object({
|
|
9418
|
+
limit: z45.number().int().min(1).max(50).optional(),
|
|
9419
|
+
offset: z45.number().int().min(0).optional(),
|
|
9420
|
+
search: z45.string().optional()
|
|
9421
|
+
});
|
|
9422
|
+
var EditTaskDraftInput = z45.object({
|
|
9423
|
+
taskId: z45.string().min(1),
|
|
9424
|
+
angleIndex: z45.number().int().min(0),
|
|
9425
|
+
angle: z45.object({
|
|
9426
|
+
angle: z45.string().min(1).max(480),
|
|
9427
|
+
buyerPersona: z45.string().min(1).max(360),
|
|
9428
|
+
actor: z45.string().min(1).max(360).optional(),
|
|
9429
|
+
actorId: z45.string().trim().min(1).optional().describe("Opcional. ID de un personaje guardado (usa list_actors para verlos). Si lo env\xEDas, su foto y su voz mandan sobre la descripci\xF3n de texto."),
|
|
9430
|
+
gender: z45.enum([
|
|
8985
9431
|
"female",
|
|
8986
9432
|
"male"
|
|
8987
9433
|
]).optional(),
|
|
8988
|
-
scriptNotes:
|
|
9434
|
+
scriptNotes: z45.string().max(2e3).optional()
|
|
8989
9435
|
})
|
|
8990
9436
|
});
|
|
8991
|
-
var PendingActionPayload =
|
|
8992
|
-
|
|
8993
|
-
kind:
|
|
9437
|
+
var PendingActionPayload = z45.discriminatedUnion("kind", [
|
|
9438
|
+
z45.object({
|
|
9439
|
+
kind: z45.literal("product_reference_confirmation"),
|
|
8994
9440
|
userChoice: ProductReferenceUserChoiceEnum,
|
|
8995
|
-
confirmedProductNames:
|
|
9441
|
+
confirmedProductNames: z45.array(z45.string().min(1).max(120)).min(2).max(8).optional(),
|
|
8996
9442
|
// SCOPED multi-scene opt-in — the ONLY way STANDARD ("Multiescena") is
|
|
8997
9443
|
// reachable via MCP. Valid ONLY together with userChoice='variants' (same
|
|
8998
9444
|
// product in several colors): renders one shot per color and stitches them,
|
|
@@ -9003,50 +9449,50 @@ var PendingActionPayload = z44.discriminatedUnion("kind", [
|
|
|
9003
9449
|
// safe because multi-scene IS the intended render for a color variant set,
|
|
9004
9450
|
// not a blind quality upsell. Maps to `videoTier: 'standard'` on the
|
|
9005
9451
|
// confirm call; the backend re-gates it (variants + variant_set only).
|
|
9006
|
-
useMultiSceneShowcase:
|
|
9452
|
+
useMultiSceneShowcase: z45.boolean().optional().describe('Opcional (SOLO con userChoice="variants"). true = render Multiescena: una toma por color, mostrando todas las variantes, hasta 30s. Se factura POR ESCENA (m\xE1s caro que el render normal de una sola toma) \u2014 AV\xCDSALE del mayor costo al usuario antes de activarlo. Omitir = render est\xE1ndar de una sola toma. Se ignora si userChoice no es "variants".')
|
|
9007
9453
|
}),
|
|
9008
|
-
|
|
9009
|
-
kind:
|
|
9454
|
+
z45.object({
|
|
9455
|
+
kind: z45.literal("angle_review_approval"),
|
|
9010
9456
|
// All edits are optional — empty action body just approves the
|
|
9011
9457
|
// backend-generated draft as-is. Field shapes mirror the angle-review
|
|
9012
9458
|
// request contracts in @packages/types/wizard.ts; the handler routes to
|
|
9013
9459
|
// the dedicated video/image/autopilot endpoint based on StatusResponse.kind.
|
|
9014
|
-
customAngle:
|
|
9460
|
+
customAngle: z45.string().min(1).max(480).optional(),
|
|
9015
9461
|
// Keep the shared MCP action within the strictest downstream contract:
|
|
9016
9462
|
// autopilot/image resume endpoints cap these fields at 1500 characters.
|
|
9017
|
-
painPoints:
|
|
9018
|
-
benefits:
|
|
9019
|
-
features:
|
|
9020
|
-
gapDurationSeconds:
|
|
9021
|
-
|
|
9022
|
-
|
|
9463
|
+
painPoints: z45.string().max(1500).optional(),
|
|
9464
|
+
benefits: z45.string().max(1500).optional(),
|
|
9465
|
+
features: z45.array(z45.string().min(1).max(300)).max(10).optional(),
|
|
9466
|
+
gapDurationSeconds: z45.union([
|
|
9467
|
+
z45.literal(15),
|
|
9468
|
+
z45.literal(30)
|
|
9023
9469
|
]).optional().describe("Opcional. Solo para aceptar expl\xEDcitamente la recomendaci\xF3n gapDurationChoice del status. 30 env\xEDa el override double_30s para el \xE1ngulo recomendado; 15 u omitido conserva la duraci\xF3n cotizada.")
|
|
9024
9470
|
}),
|
|
9025
|
-
|
|
9026
|
-
kind:
|
|
9027
|
-
angleId:
|
|
9028
|
-
content:
|
|
9029
|
-
creativeDirection:
|
|
9030
|
-
actor:
|
|
9471
|
+
z45.object({
|
|
9472
|
+
kind: z45.literal("service_angle_approval"),
|
|
9473
|
+
angleId: z45.string().uuid(),
|
|
9474
|
+
content: z45.string().min(1).max(480).optional(),
|
|
9475
|
+
creativeDirection: z45.string().min(1).max(1500).optional(),
|
|
9476
|
+
actor: z45.string().min(1).max(360).optional()
|
|
9031
9477
|
}),
|
|
9032
|
-
|
|
9033
|
-
kind:
|
|
9034
|
-
voiceovers:
|
|
9035
|
-
visualDirections:
|
|
9478
|
+
z45.object({
|
|
9479
|
+
kind: z45.literal("service_script_approval"),
|
|
9480
|
+
voiceovers: z45.record(z45.string(), z45.string().min(1).max(600)).optional(),
|
|
9481
|
+
visualDirections: z45.record(z45.string(), z45.string().min(1).max(1200)).optional()
|
|
9036
9482
|
})
|
|
9037
9483
|
]);
|
|
9038
|
-
var ResolvePendingActionInput =
|
|
9039
|
-
taskId:
|
|
9484
|
+
var ResolvePendingActionInput = z45.object({
|
|
9485
|
+
taskId: z45.string().min(1),
|
|
9040
9486
|
action: PendingActionPayload
|
|
9041
9487
|
}).strict();
|
|
9042
9488
|
function resolveVariantConfirmVideoTier(action) {
|
|
9043
9489
|
return action.useMultiSceneShowcase && action.userChoice === "variants" ? "standard" : void 0;
|
|
9044
9490
|
}
|
|
9045
9491
|
__name(resolveVariantConfirmVideoTier, "resolveVariantConfirmVideoTier");
|
|
9046
|
-
var StatusForResolveSchema =
|
|
9492
|
+
var StatusForResolveSchema = z45.object({
|
|
9047
9493
|
// Metadata used to select the backend resume contract. Older API responses
|
|
9048
9494
|
// may omit these fields, so the resolver keeps the generic fallback.
|
|
9049
|
-
kind:
|
|
9495
|
+
kind: z45.enum([
|
|
9050
9496
|
"autopilot",
|
|
9051
9497
|
"video",
|
|
9052
9498
|
"image",
|
|
@@ -9054,24 +9500,24 @@ var StatusForResolveSchema = z44.object({
|
|
|
9054
9500
|
"mix",
|
|
9055
9501
|
"service"
|
|
9056
9502
|
]).optional(),
|
|
9057
|
-
selectedOutputs:
|
|
9503
|
+
selectedOutputs: z45.array(z45.enum([
|
|
9058
9504
|
"landing",
|
|
9059
9505
|
"video",
|
|
9060
9506
|
"image"
|
|
9061
9507
|
])).optional(),
|
|
9062
|
-
videoAngleCount:
|
|
9063
|
-
ugcFormat:
|
|
9508
|
+
videoAngleCount: z45.number().int().min(1).max(3).optional(),
|
|
9509
|
+
ugcFormat: z45.enum([
|
|
9064
9510
|
"single_8s",
|
|
9065
9511
|
"single_15s",
|
|
9066
9512
|
"double_30s"
|
|
9067
9513
|
]).optional(),
|
|
9068
|
-
videoStyle:
|
|
9514
|
+
videoStyle: z45.enum([
|
|
9069
9515
|
"ugc",
|
|
9070
9516
|
"cartoon_3d",
|
|
9071
9517
|
"product_only",
|
|
9072
9518
|
"ai_dynamic"
|
|
9073
9519
|
]).optional(),
|
|
9074
|
-
videoModel:
|
|
9520
|
+
videoModel: z45.enum([
|
|
9075
9521
|
"veo",
|
|
9076
9522
|
"wan",
|
|
9077
9523
|
"bytedance",
|
|
@@ -9079,26 +9525,26 @@ var StatusForResolveSchema = z44.object({
|
|
|
9079
9525
|
"grok",
|
|
9080
9526
|
"kling"
|
|
9081
9527
|
]).optional(),
|
|
9082
|
-
videoTier:
|
|
9528
|
+
videoTier: z45.enum([
|
|
9083
9529
|
"legacy",
|
|
9084
9530
|
"standard",
|
|
9085
9531
|
"pro",
|
|
9086
9532
|
"single_call"
|
|
9087
9533
|
]).optional(),
|
|
9088
|
-
videoResolution:
|
|
9534
|
+
videoResolution: z45.enum([
|
|
9089
9535
|
"480p",
|
|
9090
9536
|
"720p"
|
|
9091
9537
|
]).optional(),
|
|
9092
9538
|
narrativeStyle: NarrativeStyleEnum.optional(),
|
|
9093
|
-
pendingUserAction:
|
|
9094
|
-
kind:
|
|
9095
|
-
candidateGrouping:
|
|
9096
|
-
selectedImageUrls:
|
|
9097
|
-
proposedProductNames:
|
|
9098
|
-
customAngleDraft:
|
|
9099
|
-
painPointsDraft:
|
|
9100
|
-
benefitsDraft:
|
|
9101
|
-
featuresDraft:
|
|
9539
|
+
pendingUserAction: z45.object({
|
|
9540
|
+
kind: z45.string(),
|
|
9541
|
+
candidateGrouping: z45.string().optional(),
|
|
9542
|
+
selectedImageUrls: z45.array(z45.string()).optional(),
|
|
9543
|
+
proposedProductNames: z45.array(z45.string()).optional(),
|
|
9544
|
+
customAngleDraft: z45.string().optional(),
|
|
9545
|
+
painPointsDraft: z45.string().optional(),
|
|
9546
|
+
benefitsDraft: z45.string().optional(),
|
|
9547
|
+
featuresDraft: z45.array(z45.string()).optional(),
|
|
9102
9548
|
// Recommendation consumed only when the caller explicitly sends
|
|
9103
9549
|
// gapDurationSeconds: 30 in the angle approval action.
|
|
9104
9550
|
gapDurationChoice: GapDurationChoiceMirror.optional(),
|
|
@@ -9107,17 +9553,17 @@ var StatusForResolveSchema = z44.object({
|
|
|
9107
9553
|
// keys. `actorId` (the saved custom actor the picker stored) was silently
|
|
9108
9554
|
// dropped here, so the approval handler below could not echo it back on the
|
|
9109
9555
|
// PATCH — the selection looked accepted and reached no render.
|
|
9110
|
-
angles:
|
|
9111
|
-
id:
|
|
9112
|
-
content:
|
|
9113
|
-
creativeDirection:
|
|
9114
|
-
actor:
|
|
9115
|
-
actorId:
|
|
9556
|
+
angles: z45.array(z45.object({
|
|
9557
|
+
id: z45.string().uuid(),
|
|
9558
|
+
content: z45.string(),
|
|
9559
|
+
creativeDirection: z45.string(),
|
|
9560
|
+
actor: z45.string(),
|
|
9561
|
+
actorId: z45.string().nullable().optional()
|
|
9116
9562
|
}).passthrough()).optional(),
|
|
9117
|
-
panels:
|
|
9118
|
-
beatId:
|
|
9119
|
-
voiceover:
|
|
9120
|
-
visualDirection:
|
|
9563
|
+
panels: z45.array(z45.object({
|
|
9564
|
+
beatId: z45.string(),
|
|
9565
|
+
voiceover: z45.string(),
|
|
9566
|
+
visualDirection: z45.string()
|
|
9121
9567
|
}).passthrough()).optional()
|
|
9122
9568
|
}).optional()
|
|
9123
9569
|
}).passthrough();
|
|
@@ -9158,7 +9604,6 @@ __name(resolveStatusTaskKind, "resolveStatusTaskKind");
|
|
|
9158
9604
|
function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http" } = {}) {
|
|
9159
9605
|
server.tool("create_product_task", "Crea una tarea de producto desde fotos o desde un proyecto guiado propio. Por defecto genera los 3 outputs (landing + video + image creatives) EN UN MISMO TASK. Devuelve un taskId.\n\nINPUTS REQUERIDOS: images (1-5 fotos del MISMO producto; la primera es la hero) O projectId si el proyecto ya tiene una imagen guardada; adem\xE1s language y country.\n\nANTES de invocar, PREG\xDANTALE al usuario por price, offer y paymentMethods si no los mencion\xF3 \u2014 son inputs de alto impacto en la landing y nunca debes asumirlos. Si hay descuento, pregunta tambi\xE9n compareAtPrice. NO defaultes country/language desde el contexto del chat: preg\xFAntale expl\xEDcitamente, incluso si est\xE1 en LATAM.\n\nSi env\xEDas projectId, debe ser el proyecto propio que el usuario acaba de costear; el backend resolver\xE1 la imagen privada y conservar\xE1 todos los controles de cr\xE9ditos/confirmaci\xF3n. Si el usuario adjunta images junto con projectId, esas fotos reemplazan la referencia solo para este task.\n\nANTES de invocar, DILE CLARAMENTE qu\xE9 outputs vas a generar y que `create_product_task` los genera juntos en un mismo task.\n\nUsa SOLO valores declarados en los enums.", CreateProductTaskInput.shape, async (input) => {
|
|
9160
9606
|
try {
|
|
9161
|
-
assertVideoPresetDurationCompatibility(input.videoPreset, input.durationSeconds);
|
|
9162
9607
|
if (!input.images && !input.projectId) {
|
|
9163
9608
|
throw new Error("images es requerido salvo que env\xEDes projectId.");
|
|
9164
9609
|
}
|
|
@@ -9558,58 +10003,58 @@ __name(registerTaskTools, "registerTaskTools");
|
|
|
9558
10003
|
|
|
9559
10004
|
// src/tools/creatives.ts
|
|
9560
10005
|
import { randomUUID } from "crypto";
|
|
9561
|
-
import { z as
|
|
10006
|
+
import { z as z46 } from "zod";
|
|
9562
10007
|
var CreativeRefs = {
|
|
9563
10008
|
images: ImagesInput,
|
|
9564
10009
|
language: Lang.describe('Idioma del contenido. Ej. "es", "en", "pt". REQUERIDO \u2014 si el usuario no lo dijo, PREG\xDANTALE antes de invocar. NUNCA infieras desde el contexto del chat ni asumas un default.'),
|
|
9565
10010
|
country: Country.describe('Pa\xEDs objetivo ISO. Ej. "CO", "MX", "US", "AR". Define moneda, m\xE9todos de pago y tono. REQUERIDO \u2014 si el usuario no lo dijo, PREG\xDANTALE antes de invocar. NUNCA infieras desde el contexto del chat ni asumas un default.'),
|
|
9566
|
-
price:
|
|
9567
|
-
offer:
|
|
9568
|
-
compareAtPrice:
|
|
9569
|
-
paymentMethods:
|
|
9570
|
-
productContext:
|
|
9571
|
-
productLabel:
|
|
9572
|
-
buyerPersona:
|
|
9573
|
-
isCombo:
|
|
9574
|
-
competitorScanEnabled:
|
|
9575
|
-
brandId:
|
|
9576
|
-
projectId:
|
|
9577
|
-
creativeDirection:
|
|
9578
|
-
headline:
|
|
9579
|
-
subheadline:
|
|
10011
|
+
price: z46.string().min(1).max(120).optional().describe('Precio en formato libre \u2014 ej. "$89.900", "USD 29.99". Opcional pero MUY IMPACTANTE para conversi\xF3n: si el usuario no lo mencion\xF3, PREG\xDANTALE antes de invocar. Si el usuario te dice "t\xFA elige", omite el campo y la IA del backend sugiere uno.'),
|
|
10012
|
+
offer: z46.string().min(1).max(240).optional().describe('Oferta a destacar \u2014 ej. "2x1", "Env\xEDo gratis", "50% OFF". Opcional pero MUY IMPACTANTE: si el usuario no lo mencion\xF3, PREG\xDANTALE antes de invocar (es v\xE1lido que diga "sin oferta"). Si el usuario te dice "t\xFA elige", omite el campo.'),
|
|
10013
|
+
compareAtPrice: z46.string().min(1).max(120).optional().describe('Opcional. Precio anterior o de referencia para mostrar descuento. Ej. "$129.000" cuando price="$89.900".'),
|
|
10014
|
+
paymentMethods: z46.string().min(1).max(240).optional().describe('Opcional. M\xE9todos de pago a mostrar. Ej. "Tarjeta, PSE, Nequi, Contraentrega". Si se omite, la landing no muestra m\xE9todos de pago.'),
|
|
10015
|
+
productContext: z46.string().max(3e3).optional().describe("Opcional. Informacion adicional del producto que la IA debe usar como contexto prioritario: uso real, comprador, restricciones, diferenciadores o direccion creativa. No reemplaza la investigacion de competencia; si hay competencia, guia el primer angulo y los outputs no deben contradecirlo."),
|
|
10016
|
+
productLabel: z46.string().trim().max(80).optional().describe('Opcional. Nombre o tipo del producto en pocas palabras (ej. "collar de oro", "quita pelusas para ropa"). Es AUTORITATIVO: la IA usa esta identidad y no la reemplaza por otra categor\xEDa. Si lo omites, la IA lo detecta desde la imagen. NO salta la extracci\xF3n.'),
|
|
10017
|
+
buyerPersona: z46.string().optional().describe('Opcional. Descripci\xF3n del comprador objetivo. Ej. "Mujeres 35-50, profesionales urbanas". Si se omite, la IA infiere desde el \xE1ngulo y producto.'),
|
|
10018
|
+
isCombo: z46.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
|
|
10019
|
+
competitorScanEnabled: z46.boolean().optional().describe("Opcional. Si true (default), el sistema analiza ads de competencia en Meta Ad Library y enriquece los \xE1ngulos generados con hooks y painPoints validados por el mercado (\u2248$0.20-0.48 extra por task \u2014 var\xEDa con cu\xE1ntas transcripciones de video se capturen). Si false, el pipeline genera \xE1ngulos sin esa se\xF1al \u2014 usa esto SOLO si el usuario ya tiene su propia investigaci\xF3n de mercado o quiere ahorrar costos en productos muy nicho sin presencia en Meta. Default true."),
|
|
10020
|
+
brandId: z46.string().uuid().optional().describe("Opcional. ID de una marca del usuario; si se env\xEDa, el sistema usa su voz, colores y persona para enriquecer el creativo."),
|
|
10021
|
+
projectId: z46.string().uuid().optional().describe("Opcional. ID de un proyecto guiado de dropshipping activo del usuario. Enlaza esta generaci\xF3n con su progreso y recompensas."),
|
|
10022
|
+
creativeDirection: z46.string().max(500).optional().describe('Opcional. Direcci\xF3n creativa en texto libre (colores, mood, estilo). M\xC1XIMA PRIORIDAD: anula marca, colores y estilo por defecto. Aplica SOLO a im\xE1genes y landing. Ej. im\xE1genes: "amarillo, azul y rojo, minimalista"; landing: "elegante, paleta fr\xEDa". Para video usa scriptNotes (ambiente/escenario). M\xE1x 500 caracteres.'),
|
|
10023
|
+
headline: z46.string().max(120).optional().describe("Opcional. Titular EXACTO a renderizar en la imagen (o en la portada de la landing). Se imprime tal cual: la IA no lo reescribe, no lo acorta y no lo traduce. \xDAsalo cuando el copy es de marca y no es negociable. M\xE1x 120 caracteres. Si lo omites, la IA escribe el titular."),
|
|
10024
|
+
subheadline: z46.string().max(160).optional().describe("Opcional. Subt\xEDtulo EXACTO a renderizar, misma regla que headline. Independiente: puedes fijar solo el subt\xEDtulo. M\xE1x 160 caracteres.")
|
|
9580
10025
|
};
|
|
9581
|
-
var VideoInput =
|
|
10026
|
+
var VideoInput = z46.object({
|
|
9582
10027
|
images: CreativeRefs.images,
|
|
9583
|
-
title:
|
|
9584
|
-
primaryAngle:
|
|
10028
|
+
title: z46.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
|
|
10029
|
+
primaryAngle: z46.string().min(1).describe('\xC1ngulo de venta principal (frase del beneficio). Ej. "Reduce arrugas en 14 d\xEDas" o "Para quienes odian las cremas grasosas".'),
|
|
9585
10030
|
language: CreativeRefs.language,
|
|
9586
10031
|
country: CreativeRefs.country,
|
|
9587
|
-
aspectRatio:
|
|
10032
|
+
aspectRatio: z46.enum([
|
|
9588
10033
|
"9:16",
|
|
9589
10034
|
"16:9",
|
|
9590
10035
|
"Auto"
|
|
9591
10036
|
]).describe('Relaci\xF3n de aspecto. "9:16" = vertical (TikTok/Reels \u2014 default recomendado para ads). "16:9" = horizontal (YouTube). "Auto" = decide la IA.'),
|
|
9592
|
-
durationSeconds:
|
|
9593
|
-
|
|
9594
|
-
|
|
10037
|
+
durationSeconds: z46.union([
|
|
10038
|
+
z46.literal(15),
|
|
10039
|
+
z46.literal(30)
|
|
9595
10040
|
]).default(15).describe("Opcional. Duraci\xF3n objetivo: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
|
|
9596
|
-
videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla. Default "auto" (la IA escoge tras analizar el producto). Todos los presets funcionan a 15s y 30s
|
|
10041
|
+
videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla. Default "auto" (la IA escoge tras analizar el producto). Todos los presets funcionan a 15s (single_call) y 30s (Multiescena). Opciones: "auto", "ugc_dynamic" (review con experiencia y prueba), "ugc_how_to" (tutorial con pasos y resultado visible), "ugc_unboxing_chaos" (apertura y primera prueba), "problem_solution" (problema concreto, producto como soluci\xF3n y CTA), "product_showcase" (producto ejecutando una funci\xF3n real), "wearable_showcase" (prenda o accesorio modelado), "cinematic" (TV spot con receta cinematogr\xE1fica), "wild_card" (una regla visual experimental original), "pixar_problem" (problema animado 3D), "pixar_sassy" (producto que habla).'),
|
|
9597
10042
|
videoTier: VideoTierEnum.optional().describe('Opcional. Estilo del video. "basic" = "Realista"; para durationSeconds=30 el sistema selecciona internamente Multiescena. No se expone el tier interno directamente.'),
|
|
9598
10043
|
videoResolution: VideoResolutionEnum.optional().describe('Opcional. Resoluci\xF3n del render. En la web se pregunta "\xBFResoluci\xF3n del video?". "480p" = excelente calidad y m\xE1s econ\xF3mico (RECOMENDADO, default si se omite); "720p" = m\xE1xima nitidez, entre 3% y 15% m\xE1s caro seg\xFAn formato y tier. Si el usuario no menciona resoluci\xF3n, OMITE este campo (el backend usa 480p).'),
|
|
9599
|
-
features:
|
|
10044
|
+
features: z46.array(z46.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
|
|
9600
10045
|
price: CreativeRefs.price,
|
|
9601
10046
|
offer: CreativeRefs.offer,
|
|
9602
10047
|
productContext: CreativeRefs.productContext,
|
|
9603
10048
|
productLabel: CreativeRefs.productLabel,
|
|
9604
|
-
cta:
|
|
9605
|
-
scriptNotes:
|
|
9606
|
-
pauseForVideoScriptReview:
|
|
9607
|
-
pauseForAngleReview:
|
|
10049
|
+
cta: z46.string().optional().describe('Opcional. Call-to-action final del video. Ej. "Compra ahora", "Pide el tuyo hoy", "Link en bio". Si se omite, la IA escribe uno.'),
|
|
10050
|
+
scriptNotes: z46.string().optional().describe('Opcional. Notas/restricciones para el guion (la IA las usa al redactar). Ej. "No mencionar la marca X", "Tono divertido, no cl\xEDnico", "Mencionar 2x1". NO es el guion literal \u2014 para editar el guion palabra-por-palabra, pasa `pauseForVideoScriptReview: true` y usa get_video_script / approve_video_script tras la pausa.'),
|
|
10051
|
+
pauseForVideoScriptReview: z46.boolean().optional().describe("Opcional. Si true, la tarea pausa despu\xE9s de generar el guion y antes de renderizar. \xDAsalo cuando el usuario quiera editar el guion antes de gastar cr\xE9ditos en el video. Tras la pausa: get_video_script para leerlo, approve_video_script para aprobar (con o sin ediciones). Default false."),
|
|
10052
|
+
pauseForAngleReview: z46.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo para que el usuario lo confirme o edite antes del render. Default false."),
|
|
9608
10053
|
competitorScanEnabled: CreativeRefs.competitorScanEnabled,
|
|
9609
10054
|
brandId: CreativeRefs.brandId,
|
|
9610
10055
|
projectId: CreativeRefs.projectId
|
|
9611
10056
|
}).strict();
|
|
9612
|
-
var FreeVideoQualityEnum =
|
|
10057
|
+
var FreeVideoQualityEnum = z46.enum([
|
|
9613
10058
|
"economical",
|
|
9614
10059
|
"high_detail"
|
|
9615
10060
|
]);
|
|
@@ -9631,38 +10076,38 @@ var FREE_VIDEO_DURATIONS_BY_QUALITY = {
|
|
|
9631
10076
|
15
|
|
9632
10077
|
]
|
|
9633
10078
|
};
|
|
9634
|
-
var FreeVideoInput =
|
|
10079
|
+
var FreeVideoInput = z46.object({
|
|
9635
10080
|
images: ImagesInput.optional().describe("Opcional. 1-5 fotos del producto. Om\xEDtelo para generar desde texto puro."),
|
|
9636
|
-
rawPrompt:
|
|
10081
|
+
rawPrompt: z46.string().trim().min(1).max(2e3).describe("Prompt o guion exacto del video. Si activas subt\xEDtulos, este mismo texto se usa como guion de subt\xEDtulos."),
|
|
9637
10082
|
quality: FreeVideoQualityEnum.describe('Calidad del video: "economical" = Econ\xF3mico o "high_detail" = Alto detalle.'),
|
|
9638
|
-
durationSeconds:
|
|
10083
|
+
durationSeconds: z46.number().int().min(1).max(15).describe("Duraci\xF3n exacta. Econ\xF3mico permite 6, 8, 10 o 15s; Alto detalle permite 4, 8, 12 o 15s."),
|
|
9639
10084
|
videoResolution: VideoResolutionEnum.describe("Resoluci\xF3n del video: 480p o 720p."),
|
|
9640
|
-
generateAiMusic:
|
|
9641
|
-
addSubtitles:
|
|
9642
|
-
idempotencyKey:
|
|
10085
|
+
generateAiMusic: z46.boolean().default(false).describe("Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false."),
|
|
10086
|
+
addSubtitles: z46.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
|
|
10087
|
+
idempotencyKey: z46.string().uuid().optional().describe("Opcional. UUID para reintentar exactamente esta misma solicitud sin crear ni cobrar otro video. Reutiliza el mismo valor si el cliente no recibi\xF3 la respuesta.")
|
|
9643
10088
|
}).strict();
|
|
9644
10089
|
var imageStyleList = ImageCreativeStyleEnum.options.join(" | ");
|
|
9645
10090
|
var archetypeList = ImageCreativeArchetypeEnum.options.join(" | ");
|
|
9646
10091
|
var visualTreatmentList = ImageCreativeVisualTreatmentEnum.options.join(" | ");
|
|
9647
10092
|
var aspectRatioList = ImageCreativeAspectRatioEnum.options.join(" | ");
|
|
9648
|
-
var ImageInput =
|
|
10093
|
+
var ImageInput = z46.object({
|
|
9649
10094
|
images: CreativeRefs.images,
|
|
9650
|
-
productTitle:
|
|
9651
|
-
primaryAngle:
|
|
10095
|
+
productTitle: z46.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
|
|
10096
|
+
primaryAngle: z46.string().min(1).describe('\xC1ngulo de venta principal. Ej. "Reduce arrugas en 14 d\xEDas".'),
|
|
9652
10097
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
9653
10098
|
language: CreativeRefs.language,
|
|
9654
10099
|
country: CreativeRefs.country,
|
|
9655
|
-
nVariants:
|
|
9656
|
-
imageModel:
|
|
10100
|
+
nVariants: z46.number().int().min(1).max(6).optional().describe("Opcional. N\xFAmero de variantes a generar (1-6). Default 3."),
|
|
10101
|
+
imageModel: z46.string().optional().describe('Opcional. Modelo de imagen. Ej. "gpt-image-2", "nano-banana-2". Om\xEDtelo para usar el default optimizado por costo/calidad.'),
|
|
9657
10102
|
imageStyle: ImageCreativeStyleEnum.optional().describe(`Opcional. Estilo del shot del producto. Valores: ${imageStyleList}. Si se omite, la IA escoge seg\xFAn el archetype.`),
|
|
9658
10103
|
creativeArchetype: ImageCreativeArchetypeEnum.optional().describe(`Opcional. Arquetipo CRO de la composici\xF3n. Valores: ${archetypeList}. Si se omite, la IA escoge seg\xFAn \xE1ngulo+oferta (ej. "offer_value_prop" cuando hay descuento, "social_proof_trust" cuando el \xE1ngulo es testimonial).`),
|
|
9659
10104
|
visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe(`Opcional. Tratamiento gr\xE1fico. Valores: ${visualTreatmentList} ("performance_product_ad" = dealer-style alto CTR para Meta/TikTok ads; "premium_dtc_carousel" = fotograf\xEDa limpia estilo Allbirds/Glossier). Si se omite, la IA escoge.`),
|
|
9660
|
-
styleMix:
|
|
10105
|
+
styleMix: z46.boolean().optional().describe("Opcional. Si true, las N variantes mezclan distintos imageStyle para A/B test. Si false/omitido, todas comparten estilo. Default false."),
|
|
9661
10106
|
aspectRatio: ImageCreativeAspectRatioEnum.optional().describe(`Opcional. Relaci\xF3n de aspecto. Valores: ${aspectRatioList}. Default "4:5" (vertical Instagram feed).`),
|
|
9662
|
-
generateAdCopy:
|
|
9663
|
-
pauseForAngleReview:
|
|
9664
|
-
features:
|
|
9665
|
-
brandColors:
|
|
10107
|
+
generateAdCopy: z46.boolean().optional().describe("Opcional. Si true, tambi\xE9n genera headline/body/CTA para el ad. Default false."),
|
|
10108
|
+
pauseForAngleReview: z46.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo y antes de las im\xE1genes para que el usuario edite v\xEDa edit_task_draft. Default false."),
|
|
10109
|
+
features: z46.array(z46.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
|
|
10110
|
+
brandColors: z46.array(z46.string()).optional().describe('Opcional. Paleta de marca en hex. Ej. ["#FF6B35", "#004E89"]. Si se omite, la IA elige seg\xFAn el producto.'),
|
|
9666
10111
|
price: CreativeRefs.price,
|
|
9667
10112
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
9668
10113
|
offer: CreativeRefs.offer,
|
|
@@ -9676,16 +10121,16 @@ var ImageInput = z45.object({
|
|
|
9676
10121
|
headline: CreativeRefs.headline,
|
|
9677
10122
|
subheadline: CreativeRefs.subheadline
|
|
9678
10123
|
});
|
|
9679
|
-
var EditImageCreativeInput =
|
|
9680
|
-
taskId:
|
|
9681
|
-
variantIndex:
|
|
9682
|
-
instruction:
|
|
10124
|
+
var EditImageCreativeInput = z46.object({
|
|
10125
|
+
taskId: z46.string().uuid().describe("ID de la tarea de imagen ya finalizada."),
|
|
10126
|
+
variantIndex: z46.number().int().min(0).max(10).describe("\xCDndice de la variante a corregir (0 = primera imagen)."),
|
|
10127
|
+
instruction: z46.string().min(1).max(1e3).describe('Qu\xE9 cambiar en esa imagen. Ej. "quita el badge de env\xEDo gratis", "cambia el fondo a blanco".')
|
|
9683
10128
|
});
|
|
9684
|
-
var LandingInput =
|
|
10129
|
+
var LandingInput = z46.object({
|
|
9685
10130
|
images: CreativeRefs.images,
|
|
9686
10131
|
language: CreativeRefs.language,
|
|
9687
10132
|
country: CreativeRefs.country,
|
|
9688
|
-
salesAngle:
|
|
10133
|
+
salesAngle: z46.string().optional().describe('Opcional. \xC1ngulo de venta principal. Ej. "Reduce arrugas en 14 d\xEDas". Si se omite, la IA lo deduce de la imagen.'),
|
|
9689
10134
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
9690
10135
|
price: CreativeRefs.price,
|
|
9691
10136
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
@@ -9736,7 +10181,6 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
9736
10181
|
});
|
|
9737
10182
|
server.tool("generate_video_creative", 'Genera UN video creativo independiente. Devuelve un taskId.\n\nREQUERIDOS: images (array de 1-5 fotos), title, primaryAngle, language, country, aspectRatio.\n\nANTES de invocar, PREG\xDANTALE al usuario por price y offer si no los mencion\xF3 \u2014 son los inputs de mayor impacto en conversi\xF3n y nunca debes asumirlos. NO defaultes country/language desde el contexto: preg\xFAntale expl\xEDcitamente. Si el usuario te dice "t\xFA elige" para price/offer, om\xEDtelos.\n\nOTROS OPCIONALES (puedes invocar sin ellos y dejar que la IA elija defaults sensatos): durationSeconds (15 o 30), videoPreset (tipo/narrativa del video), features, cta, scriptNotes (dirige el guion Y el ambiente/escenario/est\xE9tica del video, ej. "en una playa, opulento").\n\nCOMPETENCIA Y GAP REPLICATION: con videoPreset="auto" (default) y competitorScanEnabled=true (default), el sistema analiza anuncios de competencia. Si encuentra un winner de video coherente, ancla el primaryAngle y genera autom\xE1ticamente la narrativa `gap_replication` para copiar su estructura audiovisual. No la selecciones manualmente: es un resultado del an\xE1lisis. Si competitorScanEnabled=false, esta b\xFAsqueda se omite.\n\nESTILO: 15s usa "Realista" (single_call); 30s usa "Multiescena" (multi-toma, aproximadamente 30s). Multiescena se factura por escena, as\xED que avisa el costo antes de activarlo.\n\nGUION: el usuario NO escribe el guion desde cero \u2014 la IA lo redacta. Si quiere REVISARLO/EDITARLO antes de renderizar (igual que en la web), pasa `pauseForVideoScriptReview: true`; cuando la tarea quede pausada, usa get_video_script para leer las escenas, ofr\xE9celas al usuario, y llama approve_video_script con las ediciones. Para dirigir el guion ANTES de generar (sin pausar), usa scriptNotes. Para revisar el \xE1ngulo y decidir duraci\xF3n tras el an\xE1lisis de competencia, pasa `pauseForAngleReview: true` y luego resuelve `angle_review_approval` con `gapDurationSeconds`.', VideoInput.shape, async (input) => {
|
|
9738
10183
|
try {
|
|
9739
|
-
assertVideoPresetDurationCompatibility(input.videoPreset, input.durationSeconds);
|
|
9740
10184
|
const resolved = resolveVideoPreset(input.videoPreset);
|
|
9741
10185
|
const wireTier = resolveWireVideoTier(input.videoTier, input.durationSeconds);
|
|
9742
10186
|
const { videoTier: _publicTier, videoResolution: _vr, projectId, ...rest } = input;
|
|
@@ -9811,8 +10255,8 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
9811
10255
|
__name(registerCreativeTools, "registerCreativeTools");
|
|
9812
10256
|
|
|
9813
10257
|
// src/tools/creative-variants.ts
|
|
9814
|
-
import { z as
|
|
9815
|
-
var LandingSectionKeyEnum =
|
|
10258
|
+
import { z as z47 } from "zod";
|
|
10259
|
+
var LandingSectionKeyEnum = z47.enum([
|
|
9816
10260
|
"problem_hook",
|
|
9817
10261
|
"problem_cost",
|
|
9818
10262
|
"solution_mechanism",
|
|
@@ -9852,24 +10296,24 @@ var LandingSectionKeyEnum = z46.enum([
|
|
|
9852
10296
|
"lifestyle_fit",
|
|
9853
10297
|
"join_lifestyle"
|
|
9854
10298
|
]);
|
|
9855
|
-
var CreativeVariantKindEnum =
|
|
10299
|
+
var CreativeVariantKindEnum = z47.enum([
|
|
9856
10300
|
"video",
|
|
9857
10301
|
"image",
|
|
9858
10302
|
"landing"
|
|
9859
10303
|
]);
|
|
9860
|
-
var EstimateCreativeVariantInputBase =
|
|
9861
|
-
sourceTaskId:
|
|
10304
|
+
var EstimateCreativeVariantInputBase = z47.object({
|
|
10305
|
+
sourceTaskId: z47.string().uuid().describe("ID de la tarea original completada."),
|
|
9862
10306
|
kind: CreativeVariantKindEnum,
|
|
9863
10307
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Solo cambia el preset p\xFAblico; el servidor conserva el modelo/tier del task origen."),
|
|
9864
10308
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video. Resoluci\xF3n del nuevo render."),
|
|
9865
|
-
nVariants:
|
|
10309
|
+
nVariants: z47.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
9866
10310
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si ya conoces la secci\xF3n, evita cotizar la clasificaci\xF3n con IA."),
|
|
9867
|
-
instructions:
|
|
10311
|
+
instructions: z47.string().trim().min(1).max(2e3).optional().describe("Opcional para landing. Si se env\xEDa sin sectionKey, el estimado suma la clasificaci\xF3n pagada con IA.")
|
|
9868
10312
|
}).strict();
|
|
9869
10313
|
var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
9870
10314
|
if (input.kind === "video" && input.nVariants !== void 0) {
|
|
9871
10315
|
ctx.addIssue({
|
|
9872
|
-
code:
|
|
10316
|
+
code: z47.ZodIssueCode.custom,
|
|
9873
10317
|
path: [
|
|
9874
10318
|
"nVariants"
|
|
9875
10319
|
],
|
|
@@ -9879,7 +10323,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9879
10323
|
if (input.kind !== "video") {
|
|
9880
10324
|
if (input.videoPreset !== void 0) {
|
|
9881
10325
|
ctx.addIssue({
|
|
9882
|
-
code:
|
|
10326
|
+
code: z47.ZodIssueCode.custom,
|
|
9883
10327
|
path: [
|
|
9884
10328
|
"videoPreset"
|
|
9885
10329
|
],
|
|
@@ -9888,7 +10332,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9888
10332
|
}
|
|
9889
10333
|
if (input.videoResolution !== void 0) {
|
|
9890
10334
|
ctx.addIssue({
|
|
9891
|
-
code:
|
|
10335
|
+
code: z47.ZodIssueCode.custom,
|
|
9892
10336
|
path: [
|
|
9893
10337
|
"videoResolution"
|
|
9894
10338
|
],
|
|
@@ -9898,7 +10342,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9898
10342
|
}
|
|
9899
10343
|
if (input.kind !== "image" && input.nVariants !== void 0) {
|
|
9900
10344
|
ctx.addIssue({
|
|
9901
|
-
code:
|
|
10345
|
+
code: z47.ZodIssueCode.custom,
|
|
9902
10346
|
path: [
|
|
9903
10347
|
"nVariants"
|
|
9904
10348
|
],
|
|
@@ -9908,7 +10352,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9908
10352
|
if (input.kind !== "landing") {
|
|
9909
10353
|
if (input.sectionKey !== void 0) {
|
|
9910
10354
|
ctx.addIssue({
|
|
9911
|
-
code:
|
|
10355
|
+
code: z47.ZodIssueCode.custom,
|
|
9912
10356
|
path: [
|
|
9913
10357
|
"sectionKey"
|
|
9914
10358
|
],
|
|
@@ -9917,7 +10361,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9917
10361
|
}
|
|
9918
10362
|
if (input.instructions !== void 0) {
|
|
9919
10363
|
ctx.addIssue({
|
|
9920
|
-
code:
|
|
10364
|
+
code: z47.ZodIssueCode.custom,
|
|
9921
10365
|
path: [
|
|
9922
10366
|
"instructions"
|
|
9923
10367
|
],
|
|
@@ -9926,31 +10370,31 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9926
10370
|
}
|
|
9927
10371
|
}
|
|
9928
10372
|
});
|
|
9929
|
-
var CreateCreativeVariantInputBase =
|
|
9930
|
-
sourceTaskId:
|
|
10373
|
+
var CreateCreativeVariantInputBase = z47.object({
|
|
10374
|
+
sourceTaskId: z47.string().uuid().describe("ID de la tarea original completada."),
|
|
9931
10375
|
kind: CreativeVariantKindEnum,
|
|
9932
|
-
confirm:
|
|
9933
|
-
clientRequestId:
|
|
9934
|
-
angle:
|
|
9935
|
-
buyerPersona:
|
|
9936
|
-
scriptNotes:
|
|
9937
|
-
creativeDirection:
|
|
10376
|
+
confirm: z47.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
|
|
10377
|
+
clientRequestId: z47.string().uuid().describe("UUID estable y obligatorio para esta intenci\xF3n. Reutil\xEDzalo ante timeouts o transporte ambiguo. Si la API devuelve status=error, una nueva generaci\xF3n exige otra confirmaci\xF3n y un UUID nuevo."),
|
|
10378
|
+
angle: z47.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
|
|
10379
|
+
buyerPersona: z47.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
|
|
10380
|
+
scriptNotes: z47.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
|
|
10381
|
+
creativeDirection: z47.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
|
|
9938
10382
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Preset p\xFAblico; el servidor resuelve modelo/tier desde el task origen."),
|
|
9939
10383
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video."),
|
|
9940
|
-
generateAiMusic:
|
|
9941
|
-
nVariants:
|
|
10384
|
+
generateAiMusic: z47.boolean().optional().describe("Opcional para video."),
|
|
10385
|
+
nVariants: z47.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
9942
10386
|
imageStyle: ImageCreativeStyleEnum.optional().describe("Opcional para imagen."),
|
|
9943
10387
|
creativeArchetype: ImageCreativeArchetypeEnum.optional().describe("Opcional para imagen."),
|
|
9944
10388
|
visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe("Opcional para imagen."),
|
|
9945
10389
|
aspectRatio: ImageCreativeAspectRatioEnum.optional().describe("Opcional para imagen."),
|
|
9946
|
-
styleMix:
|
|
10390
|
+
styleMix: z47.boolean().optional().describe("Opcional para imagen."),
|
|
9947
10391
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si se omite, se clasifica con IA a partir de instructions."),
|
|
9948
|
-
instructions:
|
|
10392
|
+
instructions: z47.string().trim().min(1).max(2e3).optional().describe("Solicitud libre para una nueva secci\xF3n de landing. La IA la clasifica si no env\xEDas sectionKey.")
|
|
9949
10393
|
}).strict();
|
|
9950
10394
|
var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
9951
10395
|
if ((input.kind === "video" || input.kind === "image") && !input.angle) {
|
|
9952
10396
|
ctx.addIssue({
|
|
9953
|
-
code:
|
|
10397
|
+
code: z47.ZodIssueCode.custom,
|
|
9954
10398
|
path: [
|
|
9955
10399
|
"angle"
|
|
9956
10400
|
],
|
|
@@ -9959,7 +10403,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
9959
10403
|
}
|
|
9960
10404
|
if (input.kind === "landing" && !input.sectionKey && !input.instructions) {
|
|
9961
10405
|
ctx.addIssue({
|
|
9962
|
-
code:
|
|
10406
|
+
code: z47.ZodIssueCode.custom,
|
|
9963
10407
|
path: [
|
|
9964
10408
|
"instructions"
|
|
9965
10409
|
],
|
|
@@ -10005,7 +10449,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
10005
10449
|
for (const field of fieldsByKind[input.kind]) {
|
|
10006
10450
|
if (input[field] !== void 0) {
|
|
10007
10451
|
ctx.addIssue({
|
|
10008
|
-
code:
|
|
10452
|
+
code: z47.ZodIssueCode.custom,
|
|
10009
10453
|
path: [
|
|
10010
10454
|
field
|
|
10011
10455
|
],
|
|
@@ -10122,27 +10566,27 @@ function registerCreativeVariantTools(server, api) {
|
|
|
10122
10566
|
__name(registerCreativeVariantTools, "registerCreativeVariantTools");
|
|
10123
10567
|
|
|
10124
10568
|
// src/tools/script-review.ts
|
|
10125
|
-
import { z as
|
|
10126
|
-
var GetVideoScriptInput =
|
|
10127
|
-
taskId:
|
|
10128
|
-
angleKey:
|
|
10569
|
+
import { z as z48 } from "zod";
|
|
10570
|
+
var GetVideoScriptInput = z48.object({
|
|
10571
|
+
taskId: z48.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
|
|
10572
|
+
angleKey: z48.string().min(1).optional().describe('Clave del \xE1ngulo cuyo guion se quiere leer (formato "angle_0", "angle_1", ...). REQUERIDO para tareas multi-angle: si se omite, el backend resuelve a "angle_0" LITERAL (no "el primer pendiente"), as\xED que en multi-angle donde angle_0 ya est\xE9 aprobado/saltado obtendr\xEDas status="approved" y nunca alcanzar\xEDas el \xE1ngulo realmente pendiente. Para single-angle puede omitirse.')
|
|
10129
10573
|
}).strict();
|
|
10130
10574
|
var VIDEO_SCRIPT_APPROVAL_MAX_SCENES2 = 50;
|
|
10131
10575
|
var VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2 = 2e3;
|
|
10132
|
-
var ApproveSceneEdit =
|
|
10133
|
-
index:
|
|
10134
|
-
dialogue:
|
|
10576
|
+
var ApproveSceneEdit = z48.object({
|
|
10577
|
+
index: z48.number().int().nonnegative().describe("\xCDndice de la escena (0-based, mismo que devuelve get_video_script)."),
|
|
10578
|
+
dialogue: z48.string().min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2).refine((v) => v.trim().length > 0, {
|
|
10135
10579
|
message: "dialogue no puede ser s\xF3lo espacios"
|
|
10136
10580
|
}).describe("Di\xE1logo final de la escena. NO puede estar vac\xEDo ni ser s\xF3lo espacios. Puede ser el original sin cambios o la edici\xF3n del usuario.")
|
|
10137
10581
|
}).strict();
|
|
10138
|
-
var ApproveVideoScriptInput =
|
|
10139
|
-
taskId:
|
|
10140
|
-
angleKey:
|
|
10141
|
-
scenes:
|
|
10582
|
+
var ApproveVideoScriptInput = z48.object({
|
|
10583
|
+
taskId: z48.string().min(1).describe("ID de la tarea."),
|
|
10584
|
+
angleKey: z48.string().trim().min(1).max(120).optional().describe('Mismo angleKey usado en get_video_script. REQUERIDO en multi-angle (si se omite el backend resuelve a "angle_0" literal). En single-angle puede omitirse.'),
|
|
10585
|
+
scenes: z48.array(ApproveSceneEdit).min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_SCENES2).describe("Escenas con los di\xE1logos finales. El backend mergea por `index` \u2014 las escenas NO incluidas conservan el di\xE1logo original. Debe haber entre 1 y 50 entradas. Para cada entrada que mandes con un dialogue distinto del original, el backend valida minWords \u2264 words \u2264 maxWords y rechaza con 400 si te sales del rango.")
|
|
10142
10586
|
}).strict();
|
|
10143
|
-
var RegenerateVideoScriptInput =
|
|
10144
|
-
taskId:
|
|
10145
|
-
angleKey:
|
|
10587
|
+
var RegenerateVideoScriptInput = z48.object({
|
|
10588
|
+
taskId: z48.string().min(1).describe("ID de la tarea."),
|
|
10589
|
+
angleKey: z48.string().min(1).describe('Clave del \xE1ngulo a regenerar. Single-angle: pasa "angle_0". Multi-angle: el mismo que devuelve get_video_script. NO se puede omitir \u2014 el backend lo exige siempre.'),
|
|
10146
10590
|
narrativeStyle: NarrativeStyleEnum.describe('Narrativa para el nuevo guion. Incluye valores hist\xF3ricos porque esta herramienta tambi\xE9n regenera tareas antiguas. REQUERIDO \u2014 el backend NO acepta "mantener la actual"; siempre tienes que elegir una. Si el usuario quiere re-rolear con la misma narrativa, pasa la que ya tiene el \xE1ngulo (la ves en get_video_script o en get_task_status).')
|
|
10147
10591
|
}).strict();
|
|
10148
10592
|
function registerScriptReviewTools(server, api) {
|
|
@@ -10182,9 +10626,9 @@ function registerScriptReviewTools(server, api) {
|
|
|
10182
10626
|
__name(registerScriptReviewTools, "registerScriptReviewTools");
|
|
10183
10627
|
|
|
10184
10628
|
// src/tools/connections.ts
|
|
10185
|
-
import { z as
|
|
10629
|
+
import { z as z49 } from "zod";
|
|
10186
10630
|
function registerConnectionTools(server, api) {
|
|
10187
|
-
server.tool("list_shopify_shops", "Lista las tiendas Shopify conectadas por este usuario. Si la lista est\xE1 vac\xEDa, dile al usuario que conecte una tienda en productmaker.app/integrations antes de intentar publicar.",
|
|
10631
|
+
server.tool("list_shopify_shops", "Lista las tiendas Shopify conectadas por este usuario. Si la lista est\xE1 vac\xEDa, dile al usuario que conecte una tienda en productmaker.app/integrations antes de intentar publicar.", z49.object({}).shape, async () => {
|
|
10188
10632
|
try {
|
|
10189
10633
|
const r = await api.getJson("/v1/shopify/shops");
|
|
10190
10634
|
return asText(r);
|
|
@@ -10192,10 +10636,10 @@ function registerConnectionTools(server, api) {
|
|
|
10192
10636
|
throw translateError(e);
|
|
10193
10637
|
}
|
|
10194
10638
|
});
|
|
10195
|
-
server.tool("list_shopify_products", 'Busca productos en una tienda Shopify conectada para elegir el target de una republicaci\xF3n. Usa el `id` devuelto junto con `publicationMode="existing"` en `publish_to_shopify`. Si `taskId` se proporciona, la respuesta tambi\xE9n indica el producto publicado actualmente para esa tarea y tienda. No modifica Shopify.',
|
|
10196
|
-
shopDomain:
|
|
10197
|
-
query:
|
|
10198
|
-
taskId:
|
|
10639
|
+
server.tool("list_shopify_products", 'Busca productos en una tienda Shopify conectada para elegir el target de una republicaci\xF3n. Usa el `id` devuelto junto con `publicationMode="existing"` en `publish_to_shopify`. Si `taskId` se proporciona, la respuesta tambi\xE9n indica el producto publicado actualmente para esa tarea y tienda. No modifica Shopify.', z49.object({
|
|
10640
|
+
shopDomain: z49.string().min(3).max(120),
|
|
10641
|
+
query: z49.string().trim().max(120).optional(),
|
|
10642
|
+
taskId: z49.string().uuid().optional()
|
|
10199
10643
|
}).shape, async ({ shopDomain, query, taskId }) => {
|
|
10200
10644
|
try {
|
|
10201
10645
|
const params = new URLSearchParams({
|
|
@@ -10209,7 +10653,7 @@ function registerConnectionTools(server, api) {
|
|
|
10209
10653
|
throw translateError(e);
|
|
10210
10654
|
}
|
|
10211
10655
|
});
|
|
10212
|
-
server.tool("list_meta_ad_accounts", "Lista las cuentas publicitarias de Meta conectadas. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar Meta en productmaker.app/integrations.",
|
|
10656
|
+
server.tool("list_meta_ad_accounts", "Lista las cuentas publicitarias de Meta conectadas. Para publicar, prefiere `list_meta_destinations`, que tambi\xE9n devuelve el portfolio y las p\xE1ginas disponibles. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar Meta en productmaker.app/integrations.", z49.object({}).shape, async () => {
|
|
10213
10657
|
try {
|
|
10214
10658
|
const r = await api.getJson("/v1/facebook/ad-accounts");
|
|
10215
10659
|
return asText(r);
|
|
@@ -10217,7 +10661,15 @@ function registerConnectionTools(server, api) {
|
|
|
10217
10661
|
throw translateError(e);
|
|
10218
10662
|
}
|
|
10219
10663
|
});
|
|
10220
|
-
server.tool("
|
|
10664
|
+
server.tool("list_meta_destinations", "Lista los destinos de publicaci\xF3n de Meta del usuario: Business portfolios, cuentas publicitarias dentro de cada portfolio y Facebook Pages disponibles. Es obligatorio llamar esta herramienta antes de publicar, mostrar las opciones y enviar al publicador el `portfolioId`, `adAccountId` y `pageId` elegidos; nunca elijas silenciosamente la primera p\xE1gina o cuenta. No modifica Meta.", z49.object({}).shape, async () => {
|
|
10665
|
+
try {
|
|
10666
|
+
const r = await api.getJson("/v1/facebook/destinations");
|
|
10667
|
+
return asText(r);
|
|
10668
|
+
} catch (e) {
|
|
10669
|
+
throw translateError(e);
|
|
10670
|
+
}
|
|
10671
|
+
});
|
|
10672
|
+
server.tool("list_tiktok_advertisers", "Lista las cuentas de TikTok Ads conectadas. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar TikTok en productmaker.app/integrations.", z49.object({}).shape, async () => {
|
|
10221
10673
|
try {
|
|
10222
10674
|
const r = await api.getJson("/v1/tiktok/advertisers");
|
|
10223
10675
|
return asText(r);
|
|
@@ -10229,9 +10681,9 @@ function registerConnectionTools(server, api) {
|
|
|
10229
10681
|
__name(registerConnectionTools, "registerConnectionTools");
|
|
10230
10682
|
|
|
10231
10683
|
// src/tools/actors.ts
|
|
10232
|
-
import { z as
|
|
10684
|
+
import { z as z50 } from "zod";
|
|
10233
10685
|
function registerActorTools(server, api) {
|
|
10234
|
-
server.tool("list_actors", "Lista los personajes guardados del usuario (personas creadas con IA que pueden protagonizar los videos e im\xE1genes). Devuelve id, nombre, descripci\xF3n, g\xE9nero y voz. Usa el `id` como `actorId` al crear o editar una tarea. Solo aparecen los personajes listos para usar; si la lista est\xE1 vac\xEDa, dile al usuario que cree uno en productmaker.app antes de intentar asignarlo. No modifica nada.",
|
|
10686
|
+
server.tool("list_actors", "Lista los personajes guardados del usuario (personas creadas con IA que pueden protagonizar los videos e im\xE1genes). Devuelve id, nombre, descripci\xF3n, g\xE9nero y voz. Usa el `id` como `actorId` al crear o editar una tarea. Solo aparecen los personajes listos para usar; si la lista est\xE1 vac\xEDa, dile al usuario que cree uno en productmaker.app antes de intentar asignarlo. No modifica nada.", z50.object({}).shape, async () => {
|
|
10235
10687
|
try {
|
|
10236
10688
|
const rows = await api.getJson("/v1/actors");
|
|
10237
10689
|
const actors = (Array.isArray(rows) ? rows : []).filter((actor) => actor.status === "ready").map(({ id, name, description, gender, voiceId }) => ({
|
|
@@ -10252,105 +10704,116 @@ function registerActorTools(server, api) {
|
|
|
10252
10704
|
__name(registerActorTools, "registerActorTools");
|
|
10253
10705
|
|
|
10254
10706
|
// src/tools/publish.ts
|
|
10255
|
-
import { z as
|
|
10256
|
-
var ShopifyInput =
|
|
10257
|
-
taskId:
|
|
10258
|
-
shopDomain:
|
|
10707
|
+
import { z as z51 } from "zod";
|
|
10708
|
+
var ShopifyInput = z51.object({
|
|
10709
|
+
taskId: z51.string().min(1),
|
|
10710
|
+
shopDomain: z51.string().min(1),
|
|
10259
10711
|
/** Override: number of landing images to include. Default = ALL images the
|
|
10260
10712
|
* task generated (the MCP fetches /v1/results first to count). Bounded
|
|
10261
10713
|
* generously here; the backend validates each index against
|
|
10262
10714
|
* landing.images.length and rejects out-of-range. */
|
|
10263
|
-
numLandingImages:
|
|
10264
|
-
variantIndex:
|
|
10715
|
+
numLandingImages: z51.number().int().min(1).max(20).optional(),
|
|
10716
|
+
variantIndex: z51.number().int().min(0).optional(),
|
|
10265
10717
|
/** ASK THE USER before setting: "¿Tu producto tiene variantes (talla, color, etc.)?".
|
|
10266
10718
|
* When true, the landing renders variant-picker pills; when false, it ships
|
|
10267
10719
|
* a single-option product page. Defaults to false. */
|
|
10268
|
-
showVariantPicker:
|
|
10720
|
+
showVariantPicker: z51.boolean().optional(),
|
|
10269
10721
|
/** Default `auto` updates the product previously published for this task and
|
|
10270
10722
|
* shop. Use `existing` with targetProductId to choose another product, or
|
|
10271
10723
|
* `new` only when the user explicitly wants a duplicate/new listing. */
|
|
10272
|
-
publicationMode:
|
|
10724
|
+
publicationMode: z51.enum([
|
|
10273
10725
|
"auto",
|
|
10274
10726
|
"existing",
|
|
10275
10727
|
"new"
|
|
10276
10728
|
]).optional(),
|
|
10277
10729
|
/** Shopify product GID, numeric id, or admin product URL. Required when
|
|
10278
10730
|
* publicationMode is `existing`. */
|
|
10279
|
-
targetProductId:
|
|
10731
|
+
targetProductId: z51.string().trim().min(1).max(300).optional()
|
|
10280
10732
|
});
|
|
10281
|
-
var McpCampaignConfig =
|
|
10733
|
+
var McpCampaignConfig = z51.object({
|
|
10282
10734
|
/** ALWAYS in US dollars, whatever the ad account's currency is. ProductMaker
|
|
10283
10735
|
* converts to the account currency server-side. Do NOT pre-convert: on a COP
|
|
10284
10736
|
* account the user's "60.000 al día" is `dailyBudgetUsd: 15`, not 60000. */
|
|
10285
|
-
dailyBudgetUsd:
|
|
10737
|
+
dailyBudgetUsd: z51.number().positive().optional(),
|
|
10286
10738
|
/** ISO-3166-1 alpha-2 country codes. Meta accepts max 1 (single-country
|
|
10287
10739
|
* ad set). TikTok takes only the first entry. */
|
|
10288
|
-
targetCountries:
|
|
10289
|
-
campaignName:
|
|
10740
|
+
targetCountries: z51.array(z51.string().length(2)).max(1).optional(),
|
|
10741
|
+
campaignName: z51.string().trim().min(1).max(100).optional()
|
|
10290
10742
|
});
|
|
10291
10743
|
var McpAdAccountId = MetaAdAccountId.describe('Meta ad account id. Accepts "act_1234567890" or the bare numeric id "1234567890".');
|
|
10292
|
-
var MetaInput =
|
|
10293
|
-
taskId:
|
|
10744
|
+
var MetaInput = z51.object({
|
|
10745
|
+
taskId: z51.string().min(1),
|
|
10746
|
+
/** Exact Meta Business portfolio selected from `list_meta_destinations`. */
|
|
10747
|
+
portfolioId: z51.string().trim().min(1),
|
|
10748
|
+
/** Exact Facebook Page selected from `list_meta_destinations`. */
|
|
10749
|
+
pageId: z51.string().trim().min(1),
|
|
10294
10750
|
adAccountId: McpAdAccountId,
|
|
10295
|
-
pixelId:
|
|
10296
|
-
landingUrl:
|
|
10751
|
+
pixelId: z51.string().min(1).optional(),
|
|
10752
|
+
landingUrl: z51.string().url().optional(),
|
|
10297
10753
|
/** Select which angles to publish by 0-based index. Omit to publish all
|
|
10298
10754
|
* successful angles. Example: a task with 3 angles → `[0,2]` publishes #0 + #2. */
|
|
10299
|
-
angleIndexes:
|
|
10755
|
+
angleIndexes: z51.array(z51.number().int().nonnegative()).optional(),
|
|
10300
10756
|
campaignConfig: McpCampaignConfig.optional(),
|
|
10301
10757
|
/** Override the ad's primary text (body copy). If omitted, ProductMaker
|
|
10302
10758
|
* uses the auto-generated copy attached to the task. Capped at 500 chars
|
|
10303
10759
|
* (Meta's primary-text limit). */
|
|
10304
|
-
primaryText:
|
|
10760
|
+
primaryText: z51.string().trim().min(1).max(500).optional(),
|
|
10305
10761
|
/** Optional WhatsApp number. When set, the ad button opens a WhatsApp chat
|
|
10306
10762
|
* (wa.me) instead of a landing page. */
|
|
10307
|
-
phoneNumber:
|
|
10763
|
+
phoneNumber: z51.string().min(8).max(32).optional()
|
|
10308
10764
|
});
|
|
10309
|
-
var GroupedTaskSource =
|
|
10310
|
-
type:
|
|
10311
|
-
taskId:
|
|
10312
|
-
angleIndex:
|
|
10313
|
-
videoVariant:
|
|
10765
|
+
var GroupedTaskSource = z51.object({
|
|
10766
|
+
type: z51.literal("task"),
|
|
10767
|
+
taskId: z51.string().min(1),
|
|
10768
|
+
angleIndex: z51.number().int().min(0).optional(),
|
|
10769
|
+
videoVariant: z51.enum([
|
|
10314
10770
|
"with_subtitles",
|
|
10315
10771
|
"without_subtitles"
|
|
10316
10772
|
]).optional(),
|
|
10317
|
-
imageVariantIndex:
|
|
10318
|
-
});
|
|
10319
|
-
var GroupedMediaSource =
|
|
10320
|
-
type:
|
|
10321
|
-
url:
|
|
10322
|
-
path:
|
|
10323
|
-
thumbnailUrl:
|
|
10324
|
-
thumbnailPath:
|
|
10773
|
+
imageVariantIndex: z51.number().int().min(0).optional()
|
|
10774
|
+
});
|
|
10775
|
+
var GroupedMediaSource = z51.object({
|
|
10776
|
+
type: z51.literal("media"),
|
|
10777
|
+
url: z51.string().url().optional().describe("Public HTTPS media URL. For a file on the user laptop, use request_meta_media_upload on the hosted MCP instead."),
|
|
10778
|
+
path: z51.string().min(1).optional().describe('Local file path only when this MCP runs over stdio. On the hosted MCP, call request_meta_media_upload and then use source.type="staged".'),
|
|
10779
|
+
thumbnailUrl: z51.string().url().optional(),
|
|
10780
|
+
thumbnailPath: z51.string().min(1).optional()
|
|
10325
10781
|
}).refine((value) => Boolean(value.url) !== Boolean(value.path), {
|
|
10326
10782
|
message: "media source needs exactly one of `url` or `path`"
|
|
10327
10783
|
}).refine((value) => !(value.thumbnailUrl && value.thumbnailPath), {
|
|
10328
10784
|
message: "media source needs at most one of `thumbnailUrl` or `thumbnailPath`"
|
|
10329
10785
|
});
|
|
10330
|
-
var
|
|
10331
|
-
type:
|
|
10786
|
+
var GroupedStagedMediaSource = z51.object({
|
|
10787
|
+
type: z51.literal("staged"),
|
|
10788
|
+
/** Returned by check_meta_media_upload after the user selects a local file. */
|
|
10789
|
+
stagingAssetId: z51.string().uuid(),
|
|
10790
|
+
thumbnailAssetId: z51.string().uuid().optional()
|
|
10791
|
+
});
|
|
10792
|
+
var GroupedCreative = z51.object({
|
|
10793
|
+
type: z51.enum([
|
|
10332
10794
|
"image",
|
|
10333
10795
|
"video"
|
|
10334
10796
|
]),
|
|
10335
|
-
source:
|
|
10797
|
+
source: z51.union([
|
|
10336
10798
|
GroupedTaskSource,
|
|
10337
|
-
GroupedMediaSource
|
|
10799
|
+
GroupedMediaSource,
|
|
10800
|
+
GroupedStagedMediaSource
|
|
10338
10801
|
]),
|
|
10339
|
-
title:
|
|
10340
|
-
description:
|
|
10802
|
+
title: z51.string().max(120).optional(),
|
|
10803
|
+
description: z51.string().max(500).optional(),
|
|
10341
10804
|
cta: MetaCtaType.optional(),
|
|
10342
|
-
creativeId:
|
|
10805
|
+
creativeId: z51.string().min(1).optional()
|
|
10343
10806
|
});
|
|
10344
|
-
var GroupedCampaignConfig =
|
|
10807
|
+
var GroupedCampaignConfig = z51.object({
|
|
10345
10808
|
campaignType: MetaCampaignType.default("sales"),
|
|
10346
10809
|
optimizationMode: MetaOptimizationMode.default("advantage_plus"),
|
|
10347
10810
|
budgetMode: MetaBudgetMode.default("campaign"),
|
|
10348
|
-
campaignDailyBudget:
|
|
10349
|
-
adSets:
|
|
10350
|
-
tempId:
|
|
10351
|
-
name:
|
|
10352
|
-
dailyBudget:
|
|
10353
|
-
creativeIds:
|
|
10811
|
+
campaignDailyBudget: z51.number().int().positive().optional(),
|
|
10812
|
+
adSets: z51.array(z51.object({
|
|
10813
|
+
tempId: z51.string().min(1).max(80),
|
|
10814
|
+
name: z51.string().min(1).max(100),
|
|
10815
|
+
dailyBudget: z51.number().int().positive().optional(),
|
|
10816
|
+
creativeIds: z51.array(z51.string().min(1)).min(1)
|
|
10354
10817
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_AD_SETS),
|
|
10355
10818
|
/**
|
|
10356
10819
|
* Valid values are 'landing' and 'whatsapp' — there is no 'website'. This
|
|
@@ -10358,22 +10821,22 @@ var GroupedCampaignConfig = z50.object({
|
|
|
10358
10821
|
* opaque `invalid_literal` and made the WhatsApp destination — supported by
|
|
10359
10822
|
* the API since #849 — unreachable from this tool.
|
|
10360
10823
|
*/
|
|
10361
|
-
destination:
|
|
10362
|
-
|
|
10363
|
-
type:
|
|
10364
|
-
landingUrl:
|
|
10824
|
+
destination: z51.discriminatedUnion("type", [
|
|
10825
|
+
z51.object({
|
|
10826
|
+
type: z51.literal("landing"),
|
|
10827
|
+
landingUrl: z51.string().url()
|
|
10365
10828
|
}),
|
|
10366
|
-
|
|
10367
|
-
type:
|
|
10368
|
-
phoneNumber:
|
|
10829
|
+
z51.object({
|
|
10830
|
+
type: z51.literal("whatsapp"),
|
|
10831
|
+
phoneNumber: z51.string().min(8).max(32)
|
|
10369
10832
|
})
|
|
10370
10833
|
]),
|
|
10371
|
-
saveAsDefault:
|
|
10834
|
+
saveAsDefault: z51.boolean().default(false),
|
|
10372
10835
|
budgetPolicy: MetaBudgetPolicy.optional()
|
|
10373
10836
|
}).superRefine((value, ctx) => {
|
|
10374
10837
|
if (value.budgetMode === "campaign" && value.campaignDailyBudget === void 0) {
|
|
10375
10838
|
ctx.addIssue({
|
|
10376
|
-
code:
|
|
10839
|
+
code: z51.ZodIssueCode.custom,
|
|
10377
10840
|
path: [
|
|
10378
10841
|
"campaignDailyBudget"
|
|
10379
10842
|
],
|
|
@@ -10384,7 +10847,7 @@ var GroupedCampaignConfig = z50.object({
|
|
|
10384
10847
|
value.adSets.forEach((adSet, index) => {
|
|
10385
10848
|
if (adSet.dailyBudget === void 0) {
|
|
10386
10849
|
ctx.addIssue({
|
|
10387
|
-
code:
|
|
10850
|
+
code: z51.ZodIssueCode.custom,
|
|
10388
10851
|
path: [
|
|
10389
10852
|
"adSets",
|
|
10390
10853
|
index,
|
|
@@ -10396,22 +10859,26 @@ var GroupedCampaignConfig = z50.object({
|
|
|
10396
10859
|
});
|
|
10397
10860
|
}
|
|
10398
10861
|
});
|
|
10399
|
-
var PublishCreativesToMetaInputBase =
|
|
10400
|
-
creatives:
|
|
10862
|
+
var PublishCreativesToMetaInputBase = z51.object({
|
|
10863
|
+
creatives: z51.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
10864
|
+
/** Exact Meta Business portfolio selected from `list_meta_destinations`. */
|
|
10865
|
+
portfolioId: z51.string().trim().min(1),
|
|
10866
|
+
/** Exact Facebook Page selected from `list_meta_destinations`. */
|
|
10867
|
+
pageId: z51.string().trim().min(1),
|
|
10401
10868
|
adAccountId: McpAdAccountId,
|
|
10402
|
-
pixelId:
|
|
10869
|
+
pixelId: z51.string().min(1).optional(),
|
|
10403
10870
|
/** Required unless `campaignConfig.destination` is whatsapp — see the
|
|
10404
10871
|
* superRefine below. Mirrors the API's `UploadToMetaRequest`. */
|
|
10405
|
-
landingUrl:
|
|
10406
|
-
targetCountry:
|
|
10407
|
-
campaignName:
|
|
10872
|
+
landingUrl: z51.string().url().optional(),
|
|
10873
|
+
targetCountry: z51.string().length(2),
|
|
10874
|
+
campaignName: z51.string().trim().min(1).max(100),
|
|
10408
10875
|
campaignConfig: GroupedCampaignConfig.optional()
|
|
10409
10876
|
});
|
|
10410
10877
|
var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((value, ctx) => {
|
|
10411
10878
|
const destination = value.campaignConfig?.destination;
|
|
10412
10879
|
if (destination?.type !== "whatsapp" && !value.landingUrl) {
|
|
10413
10880
|
ctx.addIssue({
|
|
10414
|
-
code:
|
|
10881
|
+
code: z51.ZodIssueCode.custom,
|
|
10415
10882
|
path: [
|
|
10416
10883
|
"landingUrl"
|
|
10417
10884
|
],
|
|
@@ -10420,7 +10887,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
10420
10887
|
}
|
|
10421
10888
|
if (destination?.type === "landing" && destination.landingUrl !== value.landingUrl) {
|
|
10422
10889
|
ctx.addIssue({
|
|
10423
|
-
code:
|
|
10890
|
+
code: z51.ZodIssueCode.custom,
|
|
10424
10891
|
path: [
|
|
10425
10892
|
"campaignConfig",
|
|
10426
10893
|
"destination",
|
|
@@ -10430,46 +10897,50 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
10430
10897
|
});
|
|
10431
10898
|
}
|
|
10432
10899
|
});
|
|
10433
|
-
var PresignUploadResponseSchema =
|
|
10434
|
-
uploadUrl:
|
|
10900
|
+
var PresignUploadResponseSchema = z51.object({
|
|
10901
|
+
uploadUrl: z51.string().url().refine((value) => {
|
|
10435
10902
|
const url = new URL(value);
|
|
10436
10903
|
return !url.username && !url.password && url.protocol === "https:";
|
|
10437
10904
|
}, "presigned upload URL must be HTTPS without embedded credentials"),
|
|
10438
|
-
publicUrl:
|
|
10905
|
+
publicUrl: z51.string().url().refine((value) => {
|
|
10439
10906
|
const url = new URL(value);
|
|
10440
10907
|
return !url.username && !url.password;
|
|
10441
|
-
}, "public media URL must not contain embedded credentials")
|
|
10908
|
+
}, "public media URL must not contain embedded credentials"),
|
|
10909
|
+
stagingAssetId: z51.string().uuid()
|
|
10442
10910
|
});
|
|
10443
|
-
async function uploadResolvedMedia(api, media) {
|
|
10444
|
-
if (media.url) return media.url;
|
|
10911
|
+
async function uploadResolvedMedia(api, media, assetRole) {
|
|
10445
10912
|
if (!media.buffer) throw new ResolveMediaError("INVALID_INPUT", "resolved media has no bytes");
|
|
10446
10913
|
const presigned = PresignUploadResponseSchema.parse(await api.postJson("/v1/ads/presign-upload", {
|
|
10447
10914
|
contentType: media.mimetype,
|
|
10448
10915
|
fileName: media.filename,
|
|
10449
|
-
fileSize: media.buffer.byteLength
|
|
10916
|
+
fileSize: media.buffer.byteLength,
|
|
10917
|
+
purpose: "meta_staging",
|
|
10918
|
+
assetRole
|
|
10450
10919
|
}));
|
|
10451
10920
|
await api.putBytes(presigned.uploadUrl, media.buffer, media.mimetype);
|
|
10452
|
-
return
|
|
10921
|
+
return {
|
|
10922
|
+
stagingAssetId: presigned.stagingAssetId
|
|
10923
|
+
};
|
|
10453
10924
|
}
|
|
10454
10925
|
__name(uploadResolvedMedia, "uploadResolvedMedia");
|
|
10455
|
-
async function resolveAndUploadMedia(api, ref, expectedKind, transport) {
|
|
10926
|
+
async function resolveAndUploadMedia(api, ref, expectedKind, assetRole, transport) {
|
|
10456
10927
|
const media = await resolveMediaRef(ref, {
|
|
10457
10928
|
transport,
|
|
10458
10929
|
expectedKind
|
|
10459
10930
|
});
|
|
10460
|
-
return uploadResolvedMedia(api, media);
|
|
10931
|
+
return uploadResolvedMedia(api, media, assetRole);
|
|
10461
10932
|
}
|
|
10462
10933
|
__name(resolveAndUploadMedia, "resolveAndUploadMedia");
|
|
10463
|
-
var TikTokInput =
|
|
10464
|
-
taskId:
|
|
10465
|
-
advertiserId:
|
|
10466
|
-
pixelId:
|
|
10467
|
-
landingUrl:
|
|
10468
|
-
angleIndexes:
|
|
10934
|
+
var TikTokInput = z51.object({
|
|
10935
|
+
taskId: z51.string().min(1),
|
|
10936
|
+
advertiserId: z51.string().min(1),
|
|
10937
|
+
pixelId: z51.string().min(1).optional(),
|
|
10938
|
+
landingUrl: z51.string().url().optional(),
|
|
10939
|
+
angleIndexes: z51.array(z51.number().int().nonnegative()).optional(),
|
|
10469
10940
|
campaignConfig: McpCampaignConfig.optional(),
|
|
10470
10941
|
/** Override the ad's description (TikTok's body copy). If omitted,
|
|
10471
10942
|
* ProductMaker uses the auto-generated copy attached to the task. */
|
|
10472
|
-
primaryText:
|
|
10943
|
+
primaryText: z51.string().trim().min(1).max(500).optional()
|
|
10473
10944
|
});
|
|
10474
10945
|
function registerPublishTools(server, api, opts = {}) {
|
|
10475
10946
|
const transport = opts.transport ?? "http";
|
|
@@ -10508,7 +10979,7 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
10508
10979
|
throw translateError(e);
|
|
10509
10980
|
}
|
|
10510
10981
|
});
|
|
10511
|
-
server.tool("publish_to_meta", "Crea una campa\xF1a de Meta Ads (Facebook/Instagram) desde UNA sola tarea.
|
|
10982
|
+
server.tool("publish_to_meta", "Crea una campa\xF1a de Meta Ads (Facebook/Instagram) desde UNA sola tarea. Antes llama `list_meta_destinations`, deja que el usuario elija el `portfolioId` (Business portfolio), el `adAccountId` y el `pageId` exactos, y env\xEDa esos tres valores; no adivines ni uses la primera p\xE1gina. Si quieres juntar creativos de VARIAS tareas usa `publish_creatives_to_meta`. La campa\xF1a se crea SIEMPRE PAUSED y el usuario la activa manualmente. `pixelId` es opcional. `angleIndexes` permite seleccionar \xE1ngulos. `campaignConfig` admite presupuesto en USD, pa\xEDses ISO-2 y nombre. `primaryText` permite reemplazar el copy y `phoneNumber` configura WhatsApp. Despu\xE9s usa `get_meta_campaign_status`.", MetaInput.shape, async ({ taskId, campaignConfig, primaryText, phoneNumber, ...rest }) => {
|
|
10512
10983
|
try {
|
|
10513
10984
|
const body = {
|
|
10514
10985
|
...rest
|
|
@@ -10536,7 +11007,7 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
10536
11007
|
throw translateError(e);
|
|
10537
11008
|
}
|
|
10538
11009
|
});
|
|
10539
|
-
server.tool("publish_creatives_to_meta", "Crea UNA campa\xF1a de Meta Ads
|
|
11010
|
+
server.tool("publish_creatives_to_meta", "Crea UNA campa\xF1a de Meta Ads PAUSED agrupando hasta " + CAMPAIGN_LIMITS.MAX_CREATIVES + ' creativos. Antes llama `list_meta_destinations` y pide al usuario elegir el `portfolioId`, `adAccountId` y `pageId` exactos. Para archivos en la laptop con MCP remoto: llama `request_meta_media_upload`, espera al usuario y consulta `check_meta_media_upload`; por CADA entrada de `assets[]` crea un creativo `source.type="staged"`, con `type` igual a su `mediaKind`. Si la carga fue parcial, publica s\xF3lo los assets que ese estado confirm\xF3; nunca inventes assets ni superes diez creativos. Las rutas locales s\xF3lo funcionan con MCP stdio. Los medios propios se suben temporalmente al CDN de ProductMaker y se eliminan despu\xE9s de publicar correctamente; si falla, quedan s\xF3lo para reintento y el TTL los limpia. Para videos la miniatura es opcional y ProductMaker puede generarla con FFmpeg. `campaignConfig.destination` acepta landing o WhatsApp. No actives la campa\xF1a autom\xE1ticamente.', PublishCreativesToMetaInputBase.shape, async (rawInput) => {
|
|
10540
11011
|
try {
|
|
10541
11012
|
const parsed = PublishCreativesToMetaInput.safeParse(rawInput);
|
|
10542
11013
|
if (!parsed.success) throw invalidInputFromZod(parsed.error);
|
|
@@ -10562,25 +11033,44 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
10562
11033
|
});
|
|
10563
11034
|
continue;
|
|
10564
11035
|
}
|
|
10565
|
-
|
|
11036
|
+
if (creative.source.type === "staged") {
|
|
11037
|
+
creatives.push({
|
|
11038
|
+
type: creative.type,
|
|
11039
|
+
source: {
|
|
11040
|
+
type: "media",
|
|
11041
|
+
mediaAssetId: creative.source.stagingAssetId,
|
|
11042
|
+
...creative.source.thumbnailAssetId ? {
|
|
11043
|
+
thumbnailAssetId: creative.source.thumbnailAssetId
|
|
11044
|
+
} : {}
|
|
11045
|
+
},
|
|
11046
|
+
title: creative.title,
|
|
11047
|
+
description: creative.description,
|
|
11048
|
+
cta: creative.cta,
|
|
11049
|
+
creativeId
|
|
11050
|
+
});
|
|
11051
|
+
continue;
|
|
11052
|
+
}
|
|
11053
|
+
const stagedMedia = await resolveAndUploadMedia(api, {
|
|
10566
11054
|
url: creative.source.url,
|
|
10567
11055
|
path: creative.source.path
|
|
10568
|
-
}, creative.type, transport);
|
|
10569
|
-
let
|
|
11056
|
+
}, creative.type, "media", transport);
|
|
11057
|
+
let stagedThumbnail;
|
|
10570
11058
|
if (creative.type === "video") {
|
|
10571
11059
|
if (creative.source.thumbnailUrl || creative.source.thumbnailPath) {
|
|
10572
|
-
|
|
11060
|
+
stagedThumbnail = await resolveAndUploadMedia(api, {
|
|
10573
11061
|
url: creative.source.thumbnailUrl,
|
|
10574
11062
|
path: creative.source.thumbnailPath
|
|
10575
|
-
}, "image", transport);
|
|
11063
|
+
}, "image", "thumbnail", transport);
|
|
10576
11064
|
}
|
|
10577
11065
|
}
|
|
10578
11066
|
creatives.push({
|
|
10579
11067
|
type: creative.type,
|
|
10580
11068
|
source: {
|
|
10581
11069
|
type: "media",
|
|
10582
|
-
|
|
10583
|
-
|
|
11070
|
+
mediaAssetId: stagedMedia.stagingAssetId,
|
|
11071
|
+
...stagedThumbnail ? {
|
|
11072
|
+
thumbnailAssetId: stagedThumbnail.stagingAssetId
|
|
11073
|
+
} : {}
|
|
10584
11074
|
},
|
|
10585
11075
|
title: creative.title,
|
|
10586
11076
|
description: creative.description,
|
|
@@ -10590,6 +11080,8 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
10590
11080
|
}
|
|
10591
11081
|
const r = await api.postJson("/v1/facebook/campaigns/from-tasks", {
|
|
10592
11082
|
creatives,
|
|
11083
|
+
portfolioId: input.portfolioId,
|
|
11084
|
+
pageId: input.pageId,
|
|
10593
11085
|
adAccountId: input.adAccountId,
|
|
10594
11086
|
pixelId: input.pixelId,
|
|
10595
11087
|
landingUrl: input.landingUrl,
|
|
@@ -10679,14 +11171,146 @@ function toTikTokCampaignConfig(cfg) {
|
|
|
10679
11171
|
}
|
|
10680
11172
|
__name(toTikTokCampaignConfig, "toTikTokCampaignConfig");
|
|
10681
11173
|
|
|
11174
|
+
// src/tools/meta-upload.ts
|
|
11175
|
+
import { z as z52 } from "zod";
|
|
11176
|
+
var MetaUploadSessionInputBase = z52.object({
|
|
11177
|
+
mediaKind: z52.enum([
|
|
11178
|
+
"image",
|
|
11179
|
+
"video",
|
|
11180
|
+
"mixed"
|
|
11181
|
+
]).default("mixed").describe("The kind of file the user will select in the upload page. Use mixed for a normal batch of image and video creatives."),
|
|
11182
|
+
assetRole: z52.enum([
|
|
11183
|
+
"media",
|
|
11184
|
+
"thumbnail"
|
|
11185
|
+
]).default("media").describe("Use media for the creative itself; thumbnail only for an image thumbnail.")
|
|
11186
|
+
});
|
|
11187
|
+
var OpenAiFileReference = z52.object({
|
|
11188
|
+
download_url: z52.string().url(),
|
|
11189
|
+
file_id: z52.string().min(1),
|
|
11190
|
+
mime_type: z52.string().min(1).optional(),
|
|
11191
|
+
file_name: z52.string().min(1).optional()
|
|
11192
|
+
}).strict();
|
|
11193
|
+
var MetaUploadSessionToolInputShape = {
|
|
11194
|
+
...MetaUploadSessionInputBase.shape,
|
|
11195
|
+
files: z52.array(OpenAiFileReference).max(10).optional().describe("Optional OpenAI-hosted file references provided by ChatGPT. They are only metadata for the UI bridge and are never forwarded to ProductMaker.")
|
|
11196
|
+
};
|
|
11197
|
+
var MetaUploadSessionToolInput = z52.object(MetaUploadSessionToolInputShape).superRefine((value, ctx) => {
|
|
11198
|
+
if (value.assetRole === "thumbnail" && value.mediaKind !== "image") {
|
|
11199
|
+
ctx.addIssue({
|
|
11200
|
+
code: z52.ZodIssueCode.custom,
|
|
11201
|
+
path: [
|
|
11202
|
+
"mediaKind"
|
|
11203
|
+
],
|
|
11204
|
+
message: "Meta thumbnails must be images"
|
|
11205
|
+
});
|
|
11206
|
+
}
|
|
11207
|
+
});
|
|
11208
|
+
var MetaUploadSessionResponse2 = z52.object({
|
|
11209
|
+
sessionId: z52.string().uuid(),
|
|
11210
|
+
uploadPageUrl: z52.string().url(),
|
|
11211
|
+
mediaKind: z52.enum([
|
|
11212
|
+
"image",
|
|
11213
|
+
"video",
|
|
11214
|
+
"mixed"
|
|
11215
|
+
]),
|
|
11216
|
+
assetRole: z52.enum([
|
|
11217
|
+
"media",
|
|
11218
|
+
"thumbnail"
|
|
11219
|
+
]),
|
|
11220
|
+
maxFiles: z52.number().int().positive(),
|
|
11221
|
+
expiresAt: z52.string().datetime()
|
|
11222
|
+
});
|
|
11223
|
+
var MetaUploadStatusInput = z52.object({
|
|
11224
|
+
sessionId: z52.string().uuid().describe("The sessionId returned by request_meta_media_upload.")
|
|
11225
|
+
});
|
|
11226
|
+
var MetaUploadStatusResponse = z52.object({
|
|
11227
|
+
sessionId: z52.string().uuid(),
|
|
11228
|
+
status: z52.enum([
|
|
11229
|
+
"pending",
|
|
11230
|
+
"uploaded"
|
|
11231
|
+
]),
|
|
11232
|
+
assets: z52.array(z52.object({
|
|
11233
|
+
stagingAssetId: z52.string().uuid(),
|
|
11234
|
+
mediaKind: z52.enum([
|
|
11235
|
+
"image",
|
|
11236
|
+
"video"
|
|
11237
|
+
])
|
|
11238
|
+
})).max(10).default([]),
|
|
11239
|
+
stagingAssetId: z52.string().uuid().optional()
|
|
11240
|
+
});
|
|
11241
|
+
function uploadSessionText(result) {
|
|
11242
|
+
const mediaLabel = result.mediaKind === "mixed" ? "archivos" : result.mediaKind === "video" ? "video" : "imagen";
|
|
11243
|
+
return {
|
|
11244
|
+
content: [
|
|
11245
|
+
{
|
|
11246
|
+
type: "text",
|
|
11247
|
+
text: [
|
|
11248
|
+
`### Sube tu ${mediaLabel} a ProductMaker`,
|
|
11249
|
+
`[Abrir carga segura](${result.uploadPageUrl})`,
|
|
11250
|
+
`Puedes seleccionar hasta ${result.maxFiles} ${result.mediaKind === "mixed" ? "archivos" : "archivo"}. Cuando termine, escribe **\u201Clisto\u201D** en este chat para que ProductMaker contin\xFAe con Meta.`,
|
|
11251
|
+
`sessionId: \`${result.sessionId}\``,
|
|
11252
|
+
`Este enlace expira: ${result.expiresAt}`
|
|
11253
|
+
].join("\n\n")
|
|
11254
|
+
}
|
|
11255
|
+
]
|
|
11256
|
+
};
|
|
11257
|
+
}
|
|
11258
|
+
__name(uploadSessionText, "uploadSessionText");
|
|
11259
|
+
function registerMetaUploadTools(server, api) {
|
|
11260
|
+
server.registerTool("request_meta_media_upload", {
|
|
11261
|
+
title: "Solicitar carga segura de Meta",
|
|
11262
|
+
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. En ChatGPT puede recibir referencias opcionales de `files`, pero NO las descarga ni las reenv\xEDa: siempre devuelve un enlace de ProductMaker donde el usuario selecciona los archivos. No pidas URL CDN, ruta local, bytes ni base64. Despu\xE9s conserva el `sessionId` y llama `check_meta_media_upload`. Esta herramienta no publica ni consume cr\xE9ditos.",
|
|
11263
|
+
inputSchema: MetaUploadSessionToolInputShape,
|
|
11264
|
+
outputSchema: MetaUploadSessionResponse2,
|
|
11265
|
+
_meta: {
|
|
11266
|
+
ui: {
|
|
11267
|
+
resourceUri: META_UPLOAD_APP_RESOURCE_URI,
|
|
11268
|
+
visibility: [
|
|
11269
|
+
"model",
|
|
11270
|
+
"app"
|
|
11271
|
+
]
|
|
11272
|
+
},
|
|
11273
|
+
"openai/outputTemplate": META_UPLOAD_APP_RESOURCE_URI,
|
|
11274
|
+
"openai/widgetAccessible": true,
|
|
11275
|
+
"openai/fileParams": [
|
|
11276
|
+
"files"
|
|
11277
|
+
],
|
|
11278
|
+
"openai/toolInvocation/invoking": "Preparando carga segura\u2026",
|
|
11279
|
+
"openai/toolInvocation/invoked": "Carga segura lista"
|
|
11280
|
+
}
|
|
11281
|
+
}, async (rawInput) => {
|
|
11282
|
+
try {
|
|
11283
|
+
const input = MetaUploadSessionToolInput.parse(rawInput);
|
|
11284
|
+
const { files: _files, ...sessionInput } = input;
|
|
11285
|
+
const result = MetaUploadSessionResponse2.parse(await api.postJson("/v1/ads/meta-upload-sessions", sessionInput));
|
|
11286
|
+
return {
|
|
11287
|
+
structuredContent: result,
|
|
11288
|
+
...uploadSessionText(result)
|
|
11289
|
+
};
|
|
11290
|
+
} catch (error) {
|
|
11291
|
+
throw translateError(error);
|
|
11292
|
+
}
|
|
11293
|
+
});
|
|
11294
|
+
server.tool("check_meta_media_upload", 'Comprueba si el usuario ya termin\xF3 de subir desde el enlace de ProductMaker. Si status=uploaded, devuelve `assets[]`; crea un creativo `source.type="staged"` por cada asset, usando su `mediaKind` como el tipo del creativo, hasta diez en el batch. Si la carga fue parcial, publica \xFAnicamente los assets que `assets[]` reporta como subidos. Conserva el `stagingAssetId` legado si aparece. Si status=pending, pide al usuario que termine; no publiques todav\xEDa.', MetaUploadStatusInput.shape, async (rawInput) => {
|
|
11295
|
+
try {
|
|
11296
|
+
const input = MetaUploadStatusInput.parse(rawInput);
|
|
11297
|
+
const result = MetaUploadStatusResponse.parse(await api.getJson(`/v1/ads/meta-upload-sessions/${encodeURIComponent(input.sessionId)}`));
|
|
11298
|
+
return asText(result);
|
|
11299
|
+
} catch (error) {
|
|
11300
|
+
throw translateError(error);
|
|
11301
|
+
}
|
|
11302
|
+
});
|
|
11303
|
+
}
|
|
11304
|
+
__name(registerMetaUploadTools, "registerMetaUploadTools");
|
|
11305
|
+
|
|
10682
11306
|
// src/tools/campaigns.ts
|
|
10683
|
-
import { z as
|
|
10684
|
-
var MetaStatusInput =
|
|
10685
|
-
campaignId:
|
|
11307
|
+
import { z as z53 } from "zod";
|
|
11308
|
+
var MetaStatusInput = z53.object({
|
|
11309
|
+
campaignId: z53.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
|
|
10686
11310
|
});
|
|
10687
|
-
var TikTokStatusInput =
|
|
10688
|
-
campaignId:
|
|
10689
|
-
advertiserId:
|
|
11311
|
+
var TikTokStatusInput = z53.object({
|
|
11312
|
+
campaignId: z53.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
|
|
11313
|
+
advertiserId: z53.string().min(1).describe("The same advertiserId used to publish the campaign.")
|
|
10690
11314
|
});
|
|
10691
11315
|
function registerCampaignTools(server, api) {
|
|
10692
11316
|
server.tool("get_meta_campaign_status", "Consulta el estado actual + m\xE9tricas lifetime (spend, impressions, clicks, reach) de una campa\xF1a Meta publicada. Llama esto DESPU\xC9S de `publish_to_meta` para confirmar que la campa\xF1a se cre\xF3 correctamente y para reportar performance al usuario. Es read-only \u2014 no modifica nada en Meta Ads Manager.", MetaStatusInput.shape, async (input) => {
|
|
@@ -10783,10 +11407,10 @@ function renderTikTokStatusMarkdown(r) {
|
|
|
10783
11407
|
__name(renderTikTokStatusMarkdown, "renderTikTokStatusMarkdown");
|
|
10784
11408
|
|
|
10785
11409
|
// ../../packages/product-economics/src/product-costing.ts
|
|
10786
|
-
import { z as
|
|
11410
|
+
import { z as z55 } from "zod";
|
|
10787
11411
|
|
|
10788
11412
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
10789
|
-
import { z as
|
|
11413
|
+
import { z as z54 } from "zod";
|
|
10790
11414
|
|
|
10791
11415
|
// ../../packages/product-economics/src/product-costing-shipping-references.json
|
|
10792
11416
|
var product_costing_shipping_references_default = {
|
|
@@ -10850,21 +11474,21 @@ var product_costing_shipping_references_default = {
|
|
|
10850
11474
|
};
|
|
10851
11475
|
|
|
10852
11476
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
10853
|
-
var ShippingReferenceSchema =
|
|
10854
|
-
reference:
|
|
10855
|
-
min:
|
|
10856
|
-
max:
|
|
10857
|
-
updatedAt:
|
|
10858
|
-
confidence:
|
|
11477
|
+
var ShippingReferenceSchema = z54.object({
|
|
11478
|
+
reference: z54.number().positive(),
|
|
11479
|
+
min: z54.number().nonnegative(),
|
|
11480
|
+
max: z54.number().positive(),
|
|
11481
|
+
updatedAt: z54.string().date(),
|
|
11482
|
+
confidence: z54.enum([
|
|
10859
11483
|
"high",
|
|
10860
11484
|
"medium",
|
|
10861
11485
|
"medium-low"
|
|
10862
11486
|
]),
|
|
10863
|
-
sources:
|
|
11487
|
+
sources: z54.array(z54.string().url().startsWith("https://")).min(1).max(3)
|
|
10864
11488
|
}).superRefine(({ reference, min, max }, context) => {
|
|
10865
11489
|
if (min > reference || reference > max) {
|
|
10866
11490
|
context.addIssue({
|
|
10867
|
-
code:
|
|
11491
|
+
code: z54.ZodIssueCode.custom,
|
|
10868
11492
|
message: "shipping_reference_outside_range",
|
|
10869
11493
|
path: [
|
|
10870
11494
|
"reference"
|
|
@@ -10872,7 +11496,7 @@ var ShippingReferenceSchema = z52.object({
|
|
|
10872
11496
|
});
|
|
10873
11497
|
}
|
|
10874
11498
|
});
|
|
10875
|
-
var SHIPPING_REFERENCES =
|
|
11499
|
+
var SHIPPING_REFERENCES = z54.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
|
|
10876
11500
|
var PRODUCT_COSTING_COUNTRY_CODES = Object.freeze(Object.keys(SHIPPING_REFERENCES));
|
|
10877
11501
|
var SPANISH_LOCALE_COUNTRIES = /* @__PURE__ */ new Set([
|
|
10878
11502
|
"MX",
|
|
@@ -11054,18 +11678,18 @@ function getProductCostingCountryName(country) {
|
|
|
11054
11678
|
__name(getProductCostingCountryName, "getProductCostingCountryName");
|
|
11055
11679
|
|
|
11056
11680
|
// ../../packages/product-economics/src/product-costing.ts
|
|
11057
|
-
var ProductCostingInputSchema =
|
|
11058
|
-
country:
|
|
11059
|
-
supplierCost:
|
|
11060
|
-
shippingCost:
|
|
11061
|
-
returnRate:
|
|
11062
|
-
cancellationRate:
|
|
11063
|
-
adsManagerCpa:
|
|
11064
|
-
desiredMargin:
|
|
11681
|
+
var ProductCostingInputSchema = z55.object({
|
|
11682
|
+
country: z55.string().refine(isProductCostingCountryCode, "unsupported_country"),
|
|
11683
|
+
supplierCost: z55.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
|
|
11684
|
+
shippingCost: z55.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
|
|
11685
|
+
returnRate: z55.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
11686
|
+
cancellationRate: z55.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
11687
|
+
adsManagerCpa: z55.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
|
|
11688
|
+
desiredMargin: z55.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
|
|
11065
11689
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
11066
11690
|
if (returnRate + cancellationRate >= 1) {
|
|
11067
11691
|
context.addIssue({
|
|
11068
|
-
code:
|
|
11692
|
+
code: z55.ZodIssueCode.custom,
|
|
11069
11693
|
message: "delivery_rate_positive",
|
|
11070
11694
|
path: [
|
|
11071
11695
|
"cancellationRate"
|
|
@@ -11132,14 +11756,14 @@ function calculateProductCosting(rawInput) {
|
|
|
11132
11756
|
__name(calculateProductCosting, "calculateProductCosting");
|
|
11133
11757
|
|
|
11134
11758
|
// src/tools/product-costing.ts
|
|
11135
|
-
import { z as
|
|
11136
|
-
var optionalAmount = /* @__PURE__ */ __name((description) =>
|
|
11137
|
-
var optionalPercent = /* @__PURE__ */ __name((description) =>
|
|
11138
|
-
var ProductCostingToolInput =
|
|
11139
|
-
country:
|
|
11759
|
+
import { z as z56 } from "zod";
|
|
11760
|
+
var optionalAmount = /* @__PURE__ */ __name((description) => z56.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
|
|
11761
|
+
var optionalPercent = /* @__PURE__ */ __name((description) => z56.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
|
|
11762
|
+
var ProductCostingToolInput = z56.object({
|
|
11763
|
+
country: z56.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
|
|
11140
11764
|
message: "Pa\xEDs no soportado por ProductMaker."
|
|
11141
11765
|
}).optional().describe("C\xF3digo ISO-2 de cualquiera de los pa\xEDses disponibles en ProductMaker. Omite para Colombia."),
|
|
11142
|
-
supplierCost:
|
|
11766
|
+
supplierCost: z56.number().finite().max(Number.MAX_SAFE_INTEGER).positive().optional().describe("Costo real de una unidad con el proveedor, en la moneda del pa\xEDs. Requerido salvo que uses supplierCostCop para Colombia."),
|
|
11143
11767
|
supplierCostCop: optionalAmount("Alias anterior para costo del proveedor en Colombia. Prefiere supplierCost."),
|
|
11144
11768
|
shippingCost: optionalAmount("Costo de un env\xEDo en la moneda del pa\xEDs. Omite para usar el valor gu\xEDa vigente."),
|
|
11145
11769
|
shippingCostCop: optionalAmount("Alias anterior para flete en Colombia. Prefiere shippingCost."),
|
|
@@ -11271,7 +11895,7 @@ function formatPercent(value) {
|
|
|
11271
11895
|
__name(formatPercent, "formatPercent");
|
|
11272
11896
|
|
|
11273
11897
|
// src/tools/product-research.ts
|
|
11274
|
-
import { z as
|
|
11898
|
+
import { z as z57 } from "zod";
|
|
11275
11899
|
|
|
11276
11900
|
// src/tools/product-research-idempotency.ts
|
|
11277
11901
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -11387,17 +12011,17 @@ var TERMINAL2 = /* @__PURE__ */ new Set([
|
|
|
11387
12011
|
]);
|
|
11388
12012
|
var POLL_INTERVAL_MS = 5e3;
|
|
11389
12013
|
var MAX_WAIT_SECONDS = 120;
|
|
11390
|
-
var FindInput =
|
|
12014
|
+
var FindInput = z57.object({
|
|
11391
12015
|
targetCountry: ResearchTargetCountryEnum.describe("Pa\xEDs objetivo. S\xF3lo CO, MX, PE, EC y CL tienen cobertura de investigaci\xF3n."),
|
|
11392
12016
|
categoryKey: ResearchCategoryEnum.default("auto").describe('Categor\xEDa a investigar. Usa "auto" para que la IA elija seg\xFAn el mercado.'),
|
|
11393
|
-
confirmed:
|
|
11394
|
-
waitSeconds:
|
|
12017
|
+
confirmed: z57.boolean().describe(`Ponlo en true SOLO despu\xE9s de que el usuario haya aceptado el cargo de ${PRICE_CREDITS} cr\xE9ditos. No lo asumas.`),
|
|
12018
|
+
waitSeconds: z57.number().int().min(0).max(MAX_WAIT_SECONDS).optional().describe("Segundos a esperar el reporte antes de devolver el runId. M\xE1ximo 120.")
|
|
11395
12019
|
});
|
|
11396
|
-
var GetInput =
|
|
11397
|
-
runId:
|
|
12020
|
+
var GetInput = z57.object({
|
|
12021
|
+
runId: z57.string().uuid().describe("ID del run devuelto por find_winning_products.")
|
|
11398
12022
|
});
|
|
11399
|
-
var ListInput =
|
|
11400
|
-
limit:
|
|
12023
|
+
var ListInput = z57.object({
|
|
12024
|
+
limit: z57.number().int().min(1).max(20).default(10)
|
|
11401
12025
|
});
|
|
11402
12026
|
function textResult(text) {
|
|
11403
12027
|
return {
|
|
@@ -11489,14 +12113,14 @@ _Esta llamada no cobr\xF3 cr\xE9ditos: se reutiliz\xF3 una investigaci\xF3n reci
|
|
|
11489
12113
|
__name(registerProductResearchTools, "registerProductResearchTools");
|
|
11490
12114
|
|
|
11491
12115
|
// src/tools/dropshipping-projects.ts
|
|
11492
|
-
import { z as
|
|
11493
|
-
var ProjectId =
|
|
11494
|
-
var DropshippingProjectStatus =
|
|
12116
|
+
import { z as z58 } from "zod";
|
|
12117
|
+
var ProjectId = z58.string().uuid();
|
|
12118
|
+
var DropshippingProjectStatus = z58.enum([
|
|
11495
12119
|
"active",
|
|
11496
12120
|
"completed",
|
|
11497
12121
|
"archived"
|
|
11498
12122
|
]);
|
|
11499
|
-
var DropshippingProjectPhase =
|
|
12123
|
+
var DropshippingProjectPhase = z58.enum([
|
|
11500
12124
|
"research",
|
|
11501
12125
|
"costing",
|
|
11502
12126
|
"media",
|
|
@@ -11505,21 +12129,21 @@ var DropshippingProjectPhase = z56.enum([
|
|
|
11505
12129
|
"advertising",
|
|
11506
12130
|
"complete"
|
|
11507
12131
|
]);
|
|
11508
|
-
var DropshippingRewardMilestone =
|
|
12132
|
+
var DropshippingRewardMilestone = z58.enum([
|
|
11509
12133
|
"product_selected",
|
|
11510
12134
|
"costing_saved",
|
|
11511
12135
|
"product_media_ready",
|
|
11512
12136
|
"creatives_completed",
|
|
11513
12137
|
"campaign_created"
|
|
11514
12138
|
]);
|
|
11515
|
-
var TargetCountry =
|
|
12139
|
+
var TargetCountry = z58.enum([
|
|
11516
12140
|
"CO",
|
|
11517
12141
|
"MX",
|
|
11518
12142
|
"PE",
|
|
11519
12143
|
"EC",
|
|
11520
12144
|
"CL"
|
|
11521
12145
|
]);
|
|
11522
|
-
var ResearchCategory =
|
|
12146
|
+
var ResearchCategory = z58.enum([
|
|
11523
12147
|
"auto",
|
|
11524
12148
|
"automotive",
|
|
11525
12149
|
"kitchen",
|
|
@@ -11534,7 +12158,7 @@ var ResearchCategory = z56.enum([
|
|
|
11534
12158
|
"garden",
|
|
11535
12159
|
"cleaning"
|
|
11536
12160
|
]);
|
|
11537
|
-
var HttpUrl3 =
|
|
12161
|
+
var HttpUrl3 = z58.string().url().max(2e3).refine((value) => {
|
|
11538
12162
|
try {
|
|
11539
12163
|
return [
|
|
11540
12164
|
"http:",
|
|
@@ -11544,46 +12168,46 @@ var HttpUrl3 = z56.string().url().max(2e3).refine((value) => {
|
|
|
11544
12168
|
return false;
|
|
11545
12169
|
}
|
|
11546
12170
|
}, "URL must use HTTP(S)");
|
|
11547
|
-
var ResearchCandidateOrigin =
|
|
11548
|
-
type:
|
|
12171
|
+
var ResearchCandidateOrigin = z58.object({
|
|
12172
|
+
type: z58.literal("research_candidate"),
|
|
11549
12173
|
runId: ProjectId,
|
|
11550
|
-
candidateSourceId:
|
|
12174
|
+
candidateSourceId: z58.string().min(1).max(160)
|
|
11551
12175
|
});
|
|
11552
|
-
var TaskOrigin =
|
|
11553
|
-
type:
|
|
12176
|
+
var TaskOrigin = z58.object({
|
|
12177
|
+
type: z58.literal("task"),
|
|
11554
12178
|
taskId: ProjectId
|
|
11555
12179
|
});
|
|
11556
|
-
var ManualOrigin =
|
|
11557
|
-
type:
|
|
12180
|
+
var ManualOrigin = z58.object({
|
|
12181
|
+
type: z58.literal("manual"),
|
|
11558
12182
|
clientRequestId: ProjectId,
|
|
11559
|
-
country:
|
|
12183
|
+
country: z58.string().min(2).max(3).optional()
|
|
11560
12184
|
});
|
|
11561
|
-
var StartProjectInput =
|
|
11562
|
-
origin:
|
|
12185
|
+
var StartProjectInput = z58.object({
|
|
12186
|
+
origin: z58.discriminatedUnion("type", [
|
|
11563
12187
|
ResearchCandidateOrigin,
|
|
11564
12188
|
TaskOrigin,
|
|
11565
12189
|
ManualOrigin
|
|
11566
12190
|
]),
|
|
11567
|
-
productName:
|
|
12191
|
+
productName: z58.string().trim().min(1).max(240).optional(),
|
|
11568
12192
|
productUrl: HttpUrl3.optional(),
|
|
11569
|
-
country:
|
|
11570
|
-
languageCode:
|
|
12193
|
+
country: z58.string().min(2).max(3).optional(),
|
|
12194
|
+
languageCode: z58.string().min(2).max(16).optional()
|
|
11571
12195
|
}).strict();
|
|
11572
|
-
var CostProjectInput =
|
|
12196
|
+
var CostProjectInput = z58.object({
|
|
11573
12197
|
projectId: ProjectId,
|
|
11574
|
-
version:
|
|
11575
|
-
country:
|
|
11576
|
-
supplierCost:
|
|
11577
|
-
shippingCost:
|
|
11578
|
-
returnRate:
|
|
11579
|
-
cancellationRate:
|
|
11580
|
-
adsManagerCpa:
|
|
11581
|
-
desiredMargin:
|
|
11582
|
-
});
|
|
11583
|
-
var GuideInput =
|
|
12198
|
+
version: z58.number().int().positive(),
|
|
12199
|
+
country: z58.string().trim().min(2).max(3),
|
|
12200
|
+
supplierCost: z58.number().finite().positive(),
|
|
12201
|
+
shippingCost: z58.number().finite().nonnegative(),
|
|
12202
|
+
returnRate: z58.number().finite().min(0).lt(1),
|
|
12203
|
+
cancellationRate: z58.number().finite().min(0).lt(1),
|
|
12204
|
+
adsManagerCpa: z58.number().finite().nonnegative(),
|
|
12205
|
+
desiredMargin: z58.number().finite().min(0).lt(1)
|
|
12206
|
+
});
|
|
12207
|
+
var GuideInput = z58.object({
|
|
11584
12208
|
projectId: ProjectId.optional()
|
|
11585
12209
|
}).strict();
|
|
11586
|
-
var DropshippingProjectNextAction =
|
|
12210
|
+
var DropshippingProjectNextAction = z58.discriminatedUnion("kind", [
|
|
11587
12211
|
"cost_product",
|
|
11588
12212
|
"provide_product_media",
|
|
11589
12213
|
"generate_creatives",
|
|
@@ -11597,78 +12221,78 @@ var DropshippingProjectNextAction = z56.discriminatedUnion("kind", [
|
|
|
11597
12221
|
"create_meta_campaign",
|
|
11598
12222
|
"repair_meta_campaign",
|
|
11599
12223
|
"review_paused_campaign"
|
|
11600
|
-
].map((kind) =>
|
|
11601
|
-
kind:
|
|
11602
|
-
href:
|
|
12224
|
+
].map((kind) => z58.object({
|
|
12225
|
+
kind: z58.literal(kind),
|
|
12226
|
+
href: z58.string().min(1)
|
|
11603
12227
|
})));
|
|
11604
|
-
var CostingInputSchema =
|
|
11605
|
-
country:
|
|
11606
|
-
supplierCost:
|
|
11607
|
-
shippingCost:
|
|
11608
|
-
returnRate:
|
|
11609
|
-
cancellationRate:
|
|
11610
|
-
adsManagerCpa:
|
|
11611
|
-
desiredMargin:
|
|
12228
|
+
var CostingInputSchema = z58.object({
|
|
12229
|
+
country: z58.string().min(2).max(3),
|
|
12230
|
+
supplierCost: z58.number().finite().positive(),
|
|
12231
|
+
shippingCost: z58.number().finite().nonnegative(),
|
|
12232
|
+
returnRate: z58.number().finite().min(0).lt(1),
|
|
12233
|
+
cancellationRate: z58.number().finite().min(0).lt(1),
|
|
12234
|
+
adsManagerCpa: z58.number().finite().nonnegative(),
|
|
12235
|
+
desiredMargin: z58.number().finite().min(0).lt(1)
|
|
11612
12236
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
11613
12237
|
if (returnRate + cancellationRate >= 1) context.addIssue({
|
|
11614
|
-
code:
|
|
12238
|
+
code: z58.ZodIssueCode.custom,
|
|
11615
12239
|
path: [
|
|
11616
12240
|
"cancellationRate"
|
|
11617
12241
|
]
|
|
11618
12242
|
});
|
|
11619
12243
|
});
|
|
11620
|
-
var CostingResultSchema =
|
|
11621
|
-
country:
|
|
11622
|
-
deliveryRate:
|
|
11623
|
-
adjustedShippingCost:
|
|
11624
|
-
adjustedCpa:
|
|
11625
|
-
totalCost:
|
|
11626
|
-
suggestedPrice:
|
|
11627
|
-
baseCommercialPrice:
|
|
11628
|
-
minimumSafePrice:
|
|
11629
|
-
commercialPrice:
|
|
11630
|
-
comparisonPrice:
|
|
11631
|
-
estimatedProfit:
|
|
11632
|
-
actualMargin:
|
|
11633
|
-
breakEvenAdsManagerCpa:
|
|
11634
|
-
wasPriceAutoAdjusted:
|
|
11635
|
-
profitabilityStatus:
|
|
11636
|
-
});
|
|
11637
|
-
var RewardProgressSchema =
|
|
11638
|
-
campaignKey:
|
|
11639
|
-
totalCredits:
|
|
11640
|
-
earnedCredits:
|
|
11641
|
-
milestones:
|
|
12244
|
+
var CostingResultSchema = z58.object({
|
|
12245
|
+
country: z58.string().min(2).max(3),
|
|
12246
|
+
deliveryRate: z58.number().finite(),
|
|
12247
|
+
adjustedShippingCost: z58.number().finite(),
|
|
12248
|
+
adjustedCpa: z58.number().finite(),
|
|
12249
|
+
totalCost: z58.number().finite(),
|
|
12250
|
+
suggestedPrice: z58.number().finite(),
|
|
12251
|
+
baseCommercialPrice: z58.number().finite(),
|
|
12252
|
+
minimumSafePrice: z58.number().finite(),
|
|
12253
|
+
commercialPrice: z58.number().finite(),
|
|
12254
|
+
comparisonPrice: z58.number().finite(),
|
|
12255
|
+
estimatedProfit: z58.number().finite(),
|
|
12256
|
+
actualMargin: z58.number().finite(),
|
|
12257
|
+
breakEvenAdsManagerCpa: z58.number().finite(),
|
|
12258
|
+
wasPriceAutoAdjusted: z58.boolean(),
|
|
12259
|
+
profitabilityStatus: z58.literal("healthy")
|
|
12260
|
+
});
|
|
12261
|
+
var RewardProgressSchema = z58.object({
|
|
12262
|
+
campaignKey: z58.literal("dropshipping-launch-v1"),
|
|
12263
|
+
totalCredits: z58.number().int().positive(),
|
|
12264
|
+
earnedCredits: z58.number().int().nonnegative(),
|
|
12265
|
+
milestones: z58.array(z58.object({
|
|
11642
12266
|
milestone: DropshippingRewardMilestone,
|
|
11643
|
-
credits:
|
|
11644
|
-
available:
|
|
11645
|
-
earned:
|
|
12267
|
+
credits: z58.number().int().positive(),
|
|
12268
|
+
available: z58.boolean().optional(),
|
|
12269
|
+
earned: z58.boolean(),
|
|
11646
12270
|
grantId: ProjectId.nullable(),
|
|
11647
|
-
grantedAt:
|
|
12271
|
+
grantedAt: z58.string().datetime().nullable()
|
|
11648
12272
|
})).length(DropshippingRewardMilestone.options.length)
|
|
11649
12273
|
});
|
|
11650
|
-
var ProjectResponseSchema =
|
|
12274
|
+
var ProjectResponseSchema = z58.object({
|
|
11651
12275
|
id: ProjectId,
|
|
11652
12276
|
taskId: ProjectId.nullable(),
|
|
11653
12277
|
status: DropshippingProjectStatus,
|
|
11654
|
-
originKey:
|
|
12278
|
+
originKey: z58.string().min(1),
|
|
11655
12279
|
researchRunId: ProjectId.nullable(),
|
|
11656
|
-
candidateSourceId:
|
|
11657
|
-
productName:
|
|
12280
|
+
candidateSourceId: z58.string().nullable(),
|
|
12281
|
+
productName: z58.string().nullable(),
|
|
11658
12282
|
productUrl: HttpUrl3.nullable(),
|
|
11659
12283
|
referenceImageUrl: HttpUrl3.nullable(),
|
|
11660
|
-
targetCountry:
|
|
11661
|
-
languageCode:
|
|
11662
|
-
hasReferenceImage:
|
|
12284
|
+
targetCountry: z58.string().nullable(),
|
|
12285
|
+
languageCode: z58.string().nullable(),
|
|
12286
|
+
hasReferenceImage: z58.boolean(),
|
|
11663
12287
|
costingInput: CostingInputSchema.nullable(),
|
|
11664
12288
|
costingResult: CostingResultSchema.nullable(),
|
|
11665
|
-
costingVersion:
|
|
11666
|
-
version:
|
|
12289
|
+
costingVersion: z58.number().int().positive().nullable(),
|
|
12290
|
+
version: z58.number().int().positive(),
|
|
11667
12291
|
phase: DropshippingProjectPhase,
|
|
11668
12292
|
nextAction: DropshippingProjectNextAction.nullable(),
|
|
11669
12293
|
rewards: RewardProgressSchema,
|
|
11670
|
-
createdAt:
|
|
11671
|
-
updatedAt:
|
|
12294
|
+
createdAt: z58.string().datetime(),
|
|
12295
|
+
updatedAt: z58.string().datetime()
|
|
11672
12296
|
}).passthrough();
|
|
11673
12297
|
function projectText(raw) {
|
|
11674
12298
|
const project = ProjectResponseSchema.parse(raw);
|
|
@@ -11753,6 +12377,7 @@ function createServer(opts) {
|
|
|
11753
12377
|
}
|
|
11754
12378
|
});
|
|
11755
12379
|
const api = new ApiClient(apiBaseUrl, apiKey);
|
|
12380
|
+
registerMetaUploadAppResource(server);
|
|
11756
12381
|
registerProductCostingTools(server);
|
|
11757
12382
|
registerProductResearchTools(server, api);
|
|
11758
12383
|
registerDropshippingProjectTools(server, api);
|
|
@@ -11770,6 +12395,7 @@ function createServer(opts) {
|
|
|
11770
12395
|
registerPublishTools(server, api, {
|
|
11771
12396
|
transport
|
|
11772
12397
|
});
|
|
12398
|
+
registerMetaUploadTools(server, api);
|
|
11773
12399
|
registerCampaignTools(server, api);
|
|
11774
12400
|
return server;
|
|
11775
12401
|
}
|