@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/http.js
CHANGED
|
@@ -153,10 +153,60 @@ var ApiClient = class {
|
|
|
153
153
|
};
|
|
154
154
|
|
|
155
155
|
// src/index.ts
|
|
156
|
-
var MCP_VERSION = "2.
|
|
156
|
+
var MCP_VERSION = "2.8.3";
|
|
157
|
+
|
|
158
|
+
// src/meta-upload-app.ts
|
|
159
|
+
import { readFileSync } from "fs";
|
|
160
|
+
import { registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
|
|
161
|
+
var META_UPLOAD_APP_RESOURCE_URI = "ui://meta-upload/v1.html";
|
|
162
|
+
var META_UPLOAD_APP_NAME = "Meta upload app";
|
|
163
|
+
var META_UPLOAD_APP_HTML_URL = new URL("./ui/meta-upload-app.html", import.meta.url);
|
|
164
|
+
var META_UPLOAD_APP_CSP = {
|
|
165
|
+
connectDomains: [
|
|
166
|
+
"https://productmaker.app",
|
|
167
|
+
"https://www.productmaker.app",
|
|
168
|
+
"https://*.r2.cloudflarestorage.com",
|
|
169
|
+
"https://*.oaiusercontent.com"
|
|
170
|
+
]
|
|
171
|
+
};
|
|
172
|
+
function readMetaUploadAppHtml(htmlUrl = META_UPLOAD_APP_HTML_URL) {
|
|
173
|
+
try {
|
|
174
|
+
return readFileSync(htmlUrl, "utf8");
|
|
175
|
+
} catch (error) {
|
|
176
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
177
|
+
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}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
__name(readMetaUploadAppHtml, "readMetaUploadAppHtml");
|
|
181
|
+
function registerMetaUploadAppResource(server) {
|
|
182
|
+
const html = readMetaUploadAppHtml();
|
|
183
|
+
registerAppResource(server, META_UPLOAD_APP_NAME, META_UPLOAD_APP_RESOURCE_URI, {
|
|
184
|
+
title: "Meta upload app",
|
|
185
|
+
mimeType: RESOURCE_MIME_TYPE,
|
|
186
|
+
_meta: {
|
|
187
|
+
ui: {
|
|
188
|
+
csp: META_UPLOAD_APP_CSP
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}, async () => ({
|
|
192
|
+
contents: [
|
|
193
|
+
{
|
|
194
|
+
uri: META_UPLOAD_APP_RESOURCE_URI,
|
|
195
|
+
mimeType: RESOURCE_MIME_TYPE,
|
|
196
|
+
text: html,
|
|
197
|
+
_meta: {
|
|
198
|
+
ui: {
|
|
199
|
+
csp: META_UPLOAD_APP_CSP
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
]
|
|
204
|
+
}));
|
|
205
|
+
}
|
|
206
|
+
__name(registerMetaUploadAppResource, "registerMetaUploadAppResource");
|
|
157
207
|
|
|
158
208
|
// src/tools/tasks.ts
|
|
159
|
-
import { z as
|
|
209
|
+
import { z as z45 } from "zod";
|
|
160
210
|
|
|
161
211
|
// src/resolve-image.ts
|
|
162
212
|
import { lookup as dnsLookup } from "dns/promises";
|
|
@@ -3720,6 +3770,25 @@ var ALLOWED_MEDIA_CONTENT_TYPES = [
|
|
|
3720
3770
|
"audio/wav",
|
|
3721
3771
|
"audio/x-wav"
|
|
3722
3772
|
];
|
|
3773
|
+
var META_STAGING_IMAGE_CONTENT_TYPES = /* @__PURE__ */ new Set([
|
|
3774
|
+
"image/jpeg",
|
|
3775
|
+
"image/png",
|
|
3776
|
+
"image/webp"
|
|
3777
|
+
]);
|
|
3778
|
+
var META_STAGING_MEDIA_CONTENT_TYPES = /* @__PURE__ */ new Set([
|
|
3779
|
+
...META_STAGING_IMAGE_CONTENT_TYPES,
|
|
3780
|
+
"video/mp4",
|
|
3781
|
+
"video/quicktime",
|
|
3782
|
+
"video/webm"
|
|
3783
|
+
]);
|
|
3784
|
+
var META_STAGING_CONTENT_TYPES = [
|
|
3785
|
+
"image/jpeg",
|
|
3786
|
+
"image/png",
|
|
3787
|
+
"image/webp",
|
|
3788
|
+
"video/mp4",
|
|
3789
|
+
"video/quicktime",
|
|
3790
|
+
"video/webm"
|
|
3791
|
+
];
|
|
3723
3792
|
var PresignUploadRequest = z31.object({
|
|
3724
3793
|
/** MIME type of the file to upload */
|
|
3725
3794
|
contentType: z31.enum(ALLOWED_MEDIA_CONTENT_TYPES),
|
|
@@ -3735,8 +3804,51 @@ var PresignUploadRequest = z31.object({
|
|
|
3735
3804
|
purpose: z31.enum([
|
|
3736
3805
|
"ads",
|
|
3737
3806
|
"service",
|
|
3738
|
-
"actor"
|
|
3807
|
+
"actor",
|
|
3808
|
+
"meta_staging"
|
|
3809
|
+
]).optional(),
|
|
3810
|
+
/** Required for purpose=meta_staging so media and thumbnails can be checked independently. */
|
|
3811
|
+
assetRole: z31.enum([
|
|
3812
|
+
"media",
|
|
3813
|
+
"thumbnail"
|
|
3739
3814
|
]).optional()
|
|
3815
|
+
}).superRefine((value, ctx) => {
|
|
3816
|
+
if (value.purpose === "meta_staging") {
|
|
3817
|
+
if (!value.assetRole) {
|
|
3818
|
+
ctx.addIssue({
|
|
3819
|
+
code: z31.ZodIssueCode.custom,
|
|
3820
|
+
path: [
|
|
3821
|
+
"assetRole"
|
|
3822
|
+
],
|
|
3823
|
+
message: "assetRole is required for Meta staging uploads"
|
|
3824
|
+
});
|
|
3825
|
+
} else if (!META_STAGING_MEDIA_CONTENT_TYPES.has(value.contentType)) {
|
|
3826
|
+
ctx.addIssue({
|
|
3827
|
+
code: z31.ZodIssueCode.custom,
|
|
3828
|
+
path: [
|
|
3829
|
+
"contentType"
|
|
3830
|
+
],
|
|
3831
|
+
message: "Meta staging accepts only image or video media"
|
|
3832
|
+
});
|
|
3833
|
+
} else if (value.assetRole === "thumbnail" && !META_STAGING_IMAGE_CONTENT_TYPES.has(value.contentType)) {
|
|
3834
|
+
ctx.addIssue({
|
|
3835
|
+
code: z31.ZodIssueCode.custom,
|
|
3836
|
+
path: [
|
|
3837
|
+
"contentType"
|
|
3838
|
+
],
|
|
3839
|
+
message: "Meta staging thumbnails must be images"
|
|
3840
|
+
});
|
|
3841
|
+
}
|
|
3842
|
+
}
|
|
3843
|
+
if (value.purpose !== "meta_staging" && value.assetRole) {
|
|
3844
|
+
ctx.addIssue({
|
|
3845
|
+
code: z31.ZodIssueCode.custom,
|
|
3846
|
+
path: [
|
|
3847
|
+
"assetRole"
|
|
3848
|
+
],
|
|
3849
|
+
message: "assetRole is only valid for Meta staging uploads"
|
|
3850
|
+
});
|
|
3851
|
+
}
|
|
3740
3852
|
});
|
|
3741
3853
|
var PresignUploadResponse = z31.object({
|
|
3742
3854
|
/** Presigned S3 PUT URL — browser sends file directly here */
|
|
@@ -3746,8 +3858,23 @@ var PresignUploadResponse = z31.object({
|
|
|
3746
3858
|
/** S3 key */
|
|
3747
3859
|
key: z31.string(),
|
|
3748
3860
|
/** Present only for purpose=service. */
|
|
3749
|
-
assetToken: z31.string().min(1).optional()
|
|
3861
|
+
assetToken: z31.string().min(1).optional(),
|
|
3862
|
+
/** Present only for purpose=meta_staging. */
|
|
3863
|
+
stagingAssetId: z31.string().uuid().optional()
|
|
3750
3864
|
});
|
|
3865
|
+
var MetaUploadMediaKind = z31.enum([
|
|
3866
|
+
"image",
|
|
3867
|
+
"video",
|
|
3868
|
+
"mixed"
|
|
3869
|
+
]);
|
|
3870
|
+
var META_UPLOAD_MAX_FILES = CAMPAIGN_LIMITS.MAX_CREATIVES;
|
|
3871
|
+
var MetaUploadAssetSummary = z31.object({
|
|
3872
|
+
stagingAssetId: z31.string().uuid(),
|
|
3873
|
+
mediaKind: z31.enum([
|
|
3874
|
+
"image",
|
|
3875
|
+
"video"
|
|
3876
|
+
])
|
|
3877
|
+
}).strict();
|
|
3751
3878
|
var MetaCampaignType = z31.enum([
|
|
3752
3879
|
"sales",
|
|
3753
3880
|
"traffic",
|
|
@@ -3773,6 +3900,81 @@ var HttpUrl = z31.string().url().refine((value) => {
|
|
|
3773
3900
|
return false;
|
|
3774
3901
|
}
|
|
3775
3902
|
}, "URL must use http or https");
|
|
3903
|
+
var MetaUploadSessionRequest = z31.object({
|
|
3904
|
+
/** The browser page will require a file of this media kind. */
|
|
3905
|
+
mediaKind: MetaUploadMediaKind,
|
|
3906
|
+
/** The staged object role; media is the normal creative input. */
|
|
3907
|
+
assetRole: z31.enum([
|
|
3908
|
+
"media",
|
|
3909
|
+
"thumbnail"
|
|
3910
|
+
]).default("media"),
|
|
3911
|
+
/** Maximum number of files; the API chooses 10 for mixed and 1 otherwise when omitted. */
|
|
3912
|
+
maxFiles: z31.number().int().min(1).max(META_UPLOAD_MAX_FILES).optional()
|
|
3913
|
+
}).strict().superRefine((value, ctx) => {
|
|
3914
|
+
if (value.mediaKind === "mixed" && value.assetRole === "thumbnail") {
|
|
3915
|
+
ctx.addIssue({
|
|
3916
|
+
code: z31.ZodIssueCode.custom,
|
|
3917
|
+
path: [
|
|
3918
|
+
"mediaKind"
|
|
3919
|
+
],
|
|
3920
|
+
message: "Mixed Meta upload sessions cannot be thumbnails"
|
|
3921
|
+
});
|
|
3922
|
+
}
|
|
3923
|
+
if (value.mediaKind !== "mixed" && value.maxFiles !== void 0 && value.maxFiles !== 1) {
|
|
3924
|
+
ctx.addIssue({
|
|
3925
|
+
code: z31.ZodIssueCode.custom,
|
|
3926
|
+
path: [
|
|
3927
|
+
"maxFiles"
|
|
3928
|
+
],
|
|
3929
|
+
message: "Non-mixed Meta upload sessions accept exactly one file"
|
|
3930
|
+
});
|
|
3931
|
+
}
|
|
3932
|
+
if (value.assetRole === "thumbnail" && value.mediaKind !== "image") {
|
|
3933
|
+
ctx.addIssue({
|
|
3934
|
+
code: z31.ZodIssueCode.custom,
|
|
3935
|
+
path: [
|
|
3936
|
+
"mediaKind"
|
|
3937
|
+
],
|
|
3938
|
+
message: "Meta thumbnails must be images"
|
|
3939
|
+
});
|
|
3940
|
+
}
|
|
3941
|
+
});
|
|
3942
|
+
var ResolveMetaUploadSessionRequest = z31.object({
|
|
3943
|
+
/** Opaque signed token embedded in the one-click upload page URL. */
|
|
3944
|
+
token: z31.string().min(1).max(4096),
|
|
3945
|
+
/** Stable browser-generated id so failed PUT retries reuse the same slot. */
|
|
3946
|
+
uploadId: z31.string().uuid().optional(),
|
|
3947
|
+
/** Browser-selected metadata; the API checks it against the session kind/role. */
|
|
3948
|
+
contentType: z31.enum(META_STAGING_CONTENT_TYPES),
|
|
3949
|
+
fileName: z31.string().min(1).max(255),
|
|
3950
|
+
fileSize: z31.number().int().positive().max(CAMPAIGN_LIMITS.MAX_FILE_SIZE_BYTES)
|
|
3951
|
+
}).strict();
|
|
3952
|
+
var MetaUploadSessionResponse = z31.object({
|
|
3953
|
+
sessionId: z31.string().uuid(),
|
|
3954
|
+
uploadPageUrl: HttpUrl,
|
|
3955
|
+
mediaKind: MetaUploadMediaKind,
|
|
3956
|
+
assetRole: z31.enum([
|
|
3957
|
+
"media",
|
|
3958
|
+
"thumbnail"
|
|
3959
|
+
]),
|
|
3960
|
+
maxFiles: z31.number().int().min(1).max(META_UPLOAD_MAX_FILES),
|
|
3961
|
+
expiresAt: z31.string().datetime()
|
|
3962
|
+
}).strict();
|
|
3963
|
+
var ResolveMetaUploadSessionResponse = z31.object({
|
|
3964
|
+
uploadUrl: z31.string().url(),
|
|
3965
|
+
stagingAssetId: z31.string().uuid(),
|
|
3966
|
+
expiresAt: z31.string().datetime()
|
|
3967
|
+
}).strict();
|
|
3968
|
+
var MetaUploadSessionStatusResponse = z31.object({
|
|
3969
|
+
sessionId: z31.string().uuid(),
|
|
3970
|
+
status: z31.enum([
|
|
3971
|
+
"pending",
|
|
3972
|
+
"uploaded"
|
|
3973
|
+
]),
|
|
3974
|
+
assets: z31.array(MetaUploadAssetSummary).max(META_UPLOAD_MAX_FILES),
|
|
3975
|
+
/** Legacy single-file response field retained for older callers. */
|
|
3976
|
+
stagingAssetId: z31.string().uuid().optional()
|
|
3977
|
+
}).strict();
|
|
3776
3978
|
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), {
|
|
3777
3979
|
message: 'adAccountId must look like "act_1234567890" (a bare numeric id is also accepted)'
|
|
3778
3980
|
});
|
|
@@ -3859,18 +4061,26 @@ var UploadToMetaRequest = z31.object({
|
|
|
3859
4061
|
title: z31.string().max(CAMPAIGN_LIMITS.MAX_TITLE_LENGTH),
|
|
3860
4062
|
description: z31.string().max(CAMPAIGN_LIMITS.MAX_DESCRIPTION_LENGTH),
|
|
3861
4063
|
cta: MetaCtaType,
|
|
3862
|
-
/**
|
|
3863
|
-
mediaUrl: HttpUrl,
|
|
4064
|
+
/** Legacy public URL; new Meta flows should use mediaAssetId. */
|
|
4065
|
+
mediaUrl: HttpUrl.optional(),
|
|
4066
|
+
/** Owner-scoped temporary asset created by the Meta staging upload flow. */
|
|
4067
|
+
mediaAssetId: z31.string().uuid().optional(),
|
|
3864
4068
|
/**
|
|
3865
4069
|
* Public URL of an extracted thumbnail. Required for video ads — Meta
|
|
3866
4070
|
* rejects video ad creatives without `image_hash`/`image_url` in
|
|
3867
4071
|
* `video_data` (subcode 1443226). Frontend extracts a frame from the
|
|
3868
4072
|
* video and uploads it before submitting the request.
|
|
3869
4073
|
*/
|
|
3870
|
-
thumbnailUrl: HttpUrl.optional()
|
|
4074
|
+
thumbnailUrl: HttpUrl.optional(),
|
|
4075
|
+
/** Owner-scoped temporary thumbnail created by the Meta staging upload flow. */
|
|
4076
|
+
thumbnailAssetId: z31.string().uuid().optional()
|
|
3871
4077
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
3872
4078
|
/** Meta Ad Account ID */
|
|
3873
4079
|
adAccountId: MetaAdAccountId,
|
|
4080
|
+
/** Meta Business Portfolio selected by the user (optional for legacy callers). */
|
|
4081
|
+
portfolioId: z31.string().min(1).optional(),
|
|
4082
|
+
/** Facebook Page selected by the user (optional for legacy callers). */
|
|
4083
|
+
pageId: z31.string().min(1).optional(),
|
|
3874
4084
|
/** Optional Meta Pixel ID */
|
|
3875
4085
|
pixelId: z31.string().optional(),
|
|
3876
4086
|
/** Landing page URL (required for landing destinations; omitted for whatsapp) */
|
|
@@ -3890,6 +4100,30 @@ var UploadToMetaRequest = z31.object({
|
|
|
3890
4100
|
message: "landingUrl is required for landing destinations"
|
|
3891
4101
|
});
|
|
3892
4102
|
}
|
|
4103
|
+
value.creatives.forEach((creative, index) => {
|
|
4104
|
+
if (Boolean(creative.mediaUrl) === Boolean(creative.mediaAssetId)) {
|
|
4105
|
+
ctx.addIssue({
|
|
4106
|
+
code: z31.ZodIssueCode.custom,
|
|
4107
|
+
path: [
|
|
4108
|
+
"creatives",
|
|
4109
|
+
index,
|
|
4110
|
+
"mediaUrl"
|
|
4111
|
+
],
|
|
4112
|
+
message: "Provide exactly one of mediaUrl or mediaAssetId"
|
|
4113
|
+
});
|
|
4114
|
+
}
|
|
4115
|
+
if (creative.thumbnailUrl && creative.thumbnailAssetId) {
|
|
4116
|
+
ctx.addIssue({
|
|
4117
|
+
code: z31.ZodIssueCode.custom,
|
|
4118
|
+
path: [
|
|
4119
|
+
"creatives",
|
|
4120
|
+
index,
|
|
4121
|
+
"thumbnailUrl"
|
|
4122
|
+
],
|
|
4123
|
+
message: "Provide at most one of thumbnailUrl or thumbnailAssetId"
|
|
4124
|
+
});
|
|
4125
|
+
}
|
|
4126
|
+
});
|
|
3893
4127
|
});
|
|
3894
4128
|
var CampaignLifetimeMetricsSchema = z31.object({
|
|
3895
4129
|
spend: z31.number().nonnegative(),
|
|
@@ -3943,8 +4177,32 @@ var FacebookAdAccountSchema = z32.object({
|
|
|
3943
4177
|
source: z32.enum([
|
|
3944
4178
|
"business",
|
|
3945
4179
|
"personal"
|
|
4180
|
+
]),
|
|
4181
|
+
/** Meta Business Portfolio (or the synthetic `personal` group). */
|
|
4182
|
+
portfolioId: z32.string().min(1),
|
|
4183
|
+
portfolioName: z32.string().min(1),
|
|
4184
|
+
portfolioType: z32.enum([
|
|
4185
|
+
"business",
|
|
4186
|
+
"personal"
|
|
3946
4187
|
])
|
|
3947
4188
|
});
|
|
4189
|
+
var FacebookMetaPortfolioSchema = z32.object({
|
|
4190
|
+
id: z32.string().min(1),
|
|
4191
|
+
name: z32.string().min(1),
|
|
4192
|
+
type: z32.enum([
|
|
4193
|
+
"business",
|
|
4194
|
+
"personal"
|
|
4195
|
+
])
|
|
4196
|
+
});
|
|
4197
|
+
var FacebookMetaPageSchema = z32.object({
|
|
4198
|
+
id: z32.string().min(1),
|
|
4199
|
+
name: z32.string().min(1)
|
|
4200
|
+
});
|
|
4201
|
+
var FacebookMetaDestinationsResponseSchema = z32.object({
|
|
4202
|
+
portfolios: z32.array(FacebookMetaPortfolioSchema),
|
|
4203
|
+
adAccounts: z32.array(FacebookAdAccountSchema),
|
|
4204
|
+
pages: z32.array(FacebookMetaPageSchema)
|
|
4205
|
+
});
|
|
3948
4206
|
var MetaCampaignOverridesSchema = z32.object({
|
|
3949
4207
|
dailyBudgetUsd: z32.number().positive().optional(),
|
|
3950
4208
|
targetCountries: z32.array(z32.string().length(2)).max(1).optional(),
|
|
@@ -3953,6 +4211,8 @@ var MetaCampaignOverridesSchema = z32.object({
|
|
|
3953
4211
|
});
|
|
3954
4212
|
var FacebookCreateCampaignBodySchema = z32.object({
|
|
3955
4213
|
adAccountId: MetaAdAccountId,
|
|
4214
|
+
portfolioId: z32.string().min(1).optional(),
|
|
4215
|
+
pageId: z32.string().min(1).optional(),
|
|
3956
4216
|
pixelId: z32.string().optional(),
|
|
3957
4217
|
landingUrl: HttpUrl2.optional(),
|
|
3958
4218
|
campaignConfig: MetaCampaignConfig.optional(),
|
|
@@ -3983,6 +4243,8 @@ var FacebookCampaignStatusResponseSchema = z32.object({
|
|
|
3983
4243
|
var FacebookCreateCampaignResponseSchema = z32.object({
|
|
3984
4244
|
campaignId: z32.string(),
|
|
3985
4245
|
campaignName: z32.string(),
|
|
4246
|
+
portfolioId: z32.string().optional(),
|
|
4247
|
+
portfolioName: z32.string().optional(),
|
|
3986
4248
|
pageId: z32.string().optional(),
|
|
3987
4249
|
pageName: z32.string().optional(),
|
|
3988
4250
|
// Backward-compatible single ad fields (optional)
|
|
@@ -4025,11 +4287,15 @@ var FacebookCreateCampaignFromCreativesBodySchema = z32.object({
|
|
|
4025
4287
|
title: z32.string().max(120),
|
|
4026
4288
|
description: z32.string().max(500),
|
|
4027
4289
|
cta: MetaCtaType,
|
|
4028
|
-
mediaUrl: HttpUrl2,
|
|
4290
|
+
mediaUrl: HttpUrl2.optional(),
|
|
4291
|
+
mediaAssetId: z32.string().uuid().optional(),
|
|
4029
4292
|
thumbnailUrl: HttpUrl2.optional(),
|
|
4293
|
+
thumbnailAssetId: z32.string().uuid().optional(),
|
|
4030
4294
|
creativeId: z32.string().min(1).optional()
|
|
4031
4295
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
4032
4296
|
adAccountId: MetaAdAccountId,
|
|
4297
|
+
portfolioId: z32.string().min(1).optional(),
|
|
4298
|
+
pageId: z32.string().min(1).optional(),
|
|
4033
4299
|
pixelId: z32.string().optional(),
|
|
4034
4300
|
/** Required unless campaignConfig.destination is 'whatsapp' (see superRefine below). */
|
|
4035
4301
|
landingUrl: HttpUrl2.optional(),
|
|
@@ -4049,6 +4315,30 @@ var FacebookCreateCampaignFromCreativesBodySchema = z32.object({
|
|
|
4049
4315
|
message: "landingUrl is required unless destination is whatsapp"
|
|
4050
4316
|
});
|
|
4051
4317
|
}
|
|
4318
|
+
value.creatives.forEach((creative, index) => {
|
|
4319
|
+
if (Boolean(creative.mediaUrl) === Boolean(creative.mediaAssetId)) {
|
|
4320
|
+
ctx.addIssue({
|
|
4321
|
+
code: z32.ZodIssueCode.custom,
|
|
4322
|
+
path: [
|
|
4323
|
+
"creatives",
|
|
4324
|
+
index,
|
|
4325
|
+
"mediaUrl"
|
|
4326
|
+
],
|
|
4327
|
+
message: "Provide exactly one of mediaUrl or mediaAssetId"
|
|
4328
|
+
});
|
|
4329
|
+
}
|
|
4330
|
+
if (creative.thumbnailUrl && creative.thumbnailAssetId) {
|
|
4331
|
+
ctx.addIssue({
|
|
4332
|
+
code: z32.ZodIssueCode.custom,
|
|
4333
|
+
path: [
|
|
4334
|
+
"creatives",
|
|
4335
|
+
index,
|
|
4336
|
+
"thumbnailUrl"
|
|
4337
|
+
],
|
|
4338
|
+
message: "Provide at most one of thumbnailUrl or thumbnailAssetId"
|
|
4339
|
+
});
|
|
4340
|
+
}
|
|
4341
|
+
});
|
|
4052
4342
|
if (value.campaignConfig?.destination?.type === "landing" && value.landingUrl && value.campaignConfig.destination.landingUrl !== value.landingUrl) {
|
|
4053
4343
|
ctx.addIssue({
|
|
4054
4344
|
code: z32.ZodIssueCode.custom,
|
|
@@ -4126,8 +4416,10 @@ var FacebookTaskCreativeSourceSchema = z32.object({
|
|
|
4126
4416
|
});
|
|
4127
4417
|
var FacebookMediaCreativeSourceSchema = z32.object({
|
|
4128
4418
|
type: z32.literal("media"),
|
|
4129
|
-
mediaUrl: HttpUrl2,
|
|
4130
|
-
|
|
4419
|
+
mediaUrl: HttpUrl2.optional(),
|
|
4420
|
+
mediaAssetId: z32.string().uuid().optional(),
|
|
4421
|
+
thumbnailUrl: HttpUrl2.optional(),
|
|
4422
|
+
thumbnailAssetId: z32.string().uuid().optional()
|
|
4131
4423
|
});
|
|
4132
4424
|
var FacebookCreativeSourceSchema = z32.discriminatedUnion("type", [
|
|
4133
4425
|
FacebookTaskCreativeSourceSchema,
|
|
@@ -4146,6 +4438,9 @@ var FacebookCreateCampaignFromTasksBodySchema = z32.object({
|
|
|
4146
4438
|
creativeId: z32.string().min(1).optional()
|
|
4147
4439
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
4148
4440
|
adAccountId: MetaAdAccountId,
|
|
4441
|
+
/** Explicit destination is required for MCP/remote creative publishing. */
|
|
4442
|
+
portfolioId: z32.string().min(1),
|
|
4443
|
+
pageId: z32.string().min(1),
|
|
4149
4444
|
pixelId: z32.string().optional(),
|
|
4150
4445
|
landingUrl: HttpUrl2.optional(),
|
|
4151
4446
|
targetCountry: z32.string().length(2).default("US"),
|
|
@@ -4162,6 +4457,33 @@ var FacebookCreateCampaignFromTasksBodySchema = z32.object({
|
|
|
4162
4457
|
message: "landingUrl is required unless destination is whatsapp"
|
|
4163
4458
|
});
|
|
4164
4459
|
}
|
|
4460
|
+
value.creatives.forEach((creative, index) => {
|
|
4461
|
+
if (creative.source.type !== "media") return;
|
|
4462
|
+
if (Boolean(creative.source.mediaUrl) === Boolean(creative.source.mediaAssetId)) {
|
|
4463
|
+
ctx.addIssue({
|
|
4464
|
+
code: z32.ZodIssueCode.custom,
|
|
4465
|
+
path: [
|
|
4466
|
+
"creatives",
|
|
4467
|
+
index,
|
|
4468
|
+
"source",
|
|
4469
|
+
"mediaUrl"
|
|
4470
|
+
],
|
|
4471
|
+
message: "Provide exactly one of mediaUrl or mediaAssetId"
|
|
4472
|
+
});
|
|
4473
|
+
}
|
|
4474
|
+
if (creative.source.thumbnailUrl && creative.source.thumbnailAssetId) {
|
|
4475
|
+
ctx.addIssue({
|
|
4476
|
+
code: z32.ZodIssueCode.custom,
|
|
4477
|
+
path: [
|
|
4478
|
+
"creatives",
|
|
4479
|
+
index,
|
|
4480
|
+
"source",
|
|
4481
|
+
"thumbnailUrl"
|
|
4482
|
+
],
|
|
4483
|
+
message: "Provide at most one of thumbnailUrl or thumbnailAssetId"
|
|
4484
|
+
});
|
|
4485
|
+
}
|
|
4486
|
+
});
|
|
4165
4487
|
});
|
|
4166
4488
|
|
|
4167
4489
|
// ../../packages/types/src/tiktok.ts
|
|
@@ -7540,6 +7862,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7540
7862
|
confirmation: "credit_consuming",
|
|
7541
7863
|
schemaHash: "bab794a9700a8d3f42b97f444a776e7ed4dcd833038c52588f951b50970ff3fa"
|
|
7542
7864
|
},
|
|
7865
|
+
{
|
|
7866
|
+
name: "check_meta_media_upload",
|
|
7867
|
+
confirmation: "none",
|
|
7868
|
+
schemaHash: "62b1af1d796d9466f8926afc5e6d18c4ddeccd5b0269ca7b21c2f6ebcfd2a945"
|
|
7869
|
+
},
|
|
7543
7870
|
{
|
|
7544
7871
|
name: "cost_dropshipping_project",
|
|
7545
7872
|
confirmation: "state_change",
|
|
@@ -7558,7 +7885,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7558
7885
|
{
|
|
7559
7886
|
name: "create_product_task",
|
|
7560
7887
|
confirmation: "credit_consuming",
|
|
7561
|
-
schemaHash: "
|
|
7888
|
+
schemaHash: "5c5fe82a365c62e44265e79a4100a06820dbbdb2e0ddc296cd066eb2255b1d62"
|
|
7562
7889
|
},
|
|
7563
7890
|
{
|
|
7564
7891
|
name: "create_service_video",
|
|
@@ -7603,7 +7930,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7603
7930
|
{
|
|
7604
7931
|
name: "generate_video_creative",
|
|
7605
7932
|
confirmation: "credit_consuming",
|
|
7606
|
-
schemaHash: "
|
|
7933
|
+
schemaHash: "04e12ef523e4064b0c204d110baad35d414b605068087c625badf40c46429c8a"
|
|
7607
7934
|
},
|
|
7608
7935
|
{
|
|
7609
7936
|
name: "get_meta_campaign_status",
|
|
@@ -7645,6 +7972,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7645
7972
|
confirmation: "none",
|
|
7646
7973
|
schemaHash: "7a014b717a77aac971ea0ab5c0ff47bb13e6cae7ef9442d9dedb15ce36381b15"
|
|
7647
7974
|
},
|
|
7975
|
+
{
|
|
7976
|
+
name: "list_meta_destinations",
|
|
7977
|
+
confirmation: "none",
|
|
7978
|
+
schemaHash: "7a014b717a77aac971ea0ab5c0ff47bb13e6cae7ef9442d9dedb15ce36381b15"
|
|
7979
|
+
},
|
|
7648
7980
|
{
|
|
7649
7981
|
name: "list_product_research_runs",
|
|
7650
7982
|
confirmation: "none",
|
|
@@ -7673,12 +8005,12 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7673
8005
|
{
|
|
7674
8006
|
name: "publish_creatives_to_meta",
|
|
7675
8007
|
confirmation: "external_mutation",
|
|
7676
|
-
schemaHash: "
|
|
8008
|
+
schemaHash: "59b3d6d5ce25812fee3382b0b1c0cf5e07beb1063048dd0a0d0633686cf29da5"
|
|
7677
8009
|
},
|
|
7678
8010
|
{
|
|
7679
8011
|
name: "publish_to_meta",
|
|
7680
8012
|
confirmation: "external_mutation",
|
|
7681
|
-
schemaHash: "
|
|
8013
|
+
schemaHash: "b507735a1fe13657355cf93163b6ead0db151984e6f2702dcdbf76b4037133ca"
|
|
7682
8014
|
},
|
|
7683
8015
|
{
|
|
7684
8016
|
name: "publish_to_shopify",
|
|
@@ -7695,6 +8027,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7695
8027
|
confirmation: "credit_consuming",
|
|
7696
8028
|
schemaHash: "745687cb41f904e293105f93bbe6bc0471f63fe097ab8f85d3df8d9833084844"
|
|
7697
8029
|
},
|
|
8030
|
+
{
|
|
8031
|
+
name: "request_meta_media_upload",
|
|
8032
|
+
confirmation: "none",
|
|
8033
|
+
schemaHash: "32203d205f414c386ee6ab33e265eb2708c108fca4169d4c12e99d7a4109a132"
|
|
8034
|
+
},
|
|
7698
8035
|
{
|
|
7699
8036
|
name: "resolve_pending_action",
|
|
7700
8037
|
confirmation: "credit_consuming",
|
|
@@ -7770,6 +8107,88 @@ var ApproveActorRequest = z42.object({
|
|
|
7770
8107
|
candidateIndex: z42.number().int().min(0).max(ACTOR_CANDIDATE_COUNT - 1)
|
|
7771
8108
|
});
|
|
7772
8109
|
|
|
8110
|
+
// ../../packages/types/src/referral-dashboard.ts
|
|
8111
|
+
import { z as z43 } from "zod";
|
|
8112
|
+
var REFERRAL_DASHBOARD_RAW_STATUSES = [
|
|
8113
|
+
"ACTIVE",
|
|
8114
|
+
"PENDING",
|
|
8115
|
+
"GRACE",
|
|
8116
|
+
"CANCELLED",
|
|
8117
|
+
"EXPIRED"
|
|
8118
|
+
];
|
|
8119
|
+
var REFERRAL_DASHBOARD_STATES = [
|
|
8120
|
+
"active",
|
|
8121
|
+
"pending",
|
|
8122
|
+
"inactive",
|
|
8123
|
+
"no_plan"
|
|
8124
|
+
];
|
|
8125
|
+
var REFERRAL_DASHBOARD_PLAN_KINDS = [
|
|
8126
|
+
"current",
|
|
8127
|
+
"last",
|
|
8128
|
+
"none"
|
|
8129
|
+
];
|
|
8130
|
+
var ReferralDashboardRawStatusSchema = z43.enum(REFERRAL_DASHBOARD_RAW_STATUSES);
|
|
8131
|
+
var ReferralDashboardStateSchema = z43.enum(REFERRAL_DASHBOARD_STATES);
|
|
8132
|
+
var ReferralDashboardPlanKindSchema = z43.enum(REFERRAL_DASHBOARD_PLAN_KINDS);
|
|
8133
|
+
var ReferralDashboardStatusSchema = z43.enum([
|
|
8134
|
+
"all",
|
|
8135
|
+
"active",
|
|
8136
|
+
"pending",
|
|
8137
|
+
"inactive"
|
|
8138
|
+
]).default("all");
|
|
8139
|
+
var ReferralDashboardPlanSchema = z43.string().trim().min(1).max(100).optional();
|
|
8140
|
+
var ReferralDashboardFiltersSchema = z43.object({
|
|
8141
|
+
status: ReferralDashboardStatusSchema,
|
|
8142
|
+
plan: ReferralDashboardPlanSchema
|
|
8143
|
+
});
|
|
8144
|
+
var ReferralDashboardSubscriptionSchema = z43.object({
|
|
8145
|
+
planName: z43.string().nullable(),
|
|
8146
|
+
planKind: ReferralDashboardPlanKindSchema,
|
|
8147
|
+
rawStatus: ReferralDashboardRawStatusSchema.nullable(),
|
|
8148
|
+
state: ReferralDashboardStateSchema,
|
|
8149
|
+
currentPeriodEnd: z43.string().datetime({
|
|
8150
|
+
offset: true
|
|
8151
|
+
}).nullable(),
|
|
8152
|
+
cancelAtPeriodEnd: z43.boolean()
|
|
8153
|
+
});
|
|
8154
|
+
var ReferralDashboardReferralSchema = z43.object({
|
|
8155
|
+
id: z43.string().min(1),
|
|
8156
|
+
registeredAt: z43.string().datetime({
|
|
8157
|
+
offset: true
|
|
8158
|
+
}),
|
|
8159
|
+
// Kept in the response for compatibility with existing referral API clients.
|
|
8160
|
+
// The referrer dashboard intentionally does not render these legacy fields.
|
|
8161
|
+
expiresAt: z43.string().datetime({
|
|
8162
|
+
offset: true
|
|
8163
|
+
}).nullable().optional(),
|
|
8164
|
+
status: z43.string().min(1).optional(),
|
|
8165
|
+
referredUser: z43.object({
|
|
8166
|
+
email: z43.string().nullable()
|
|
8167
|
+
}),
|
|
8168
|
+
subscription: ReferralDashboardSubscriptionSchema
|
|
8169
|
+
});
|
|
8170
|
+
var ReferralDashboardReferralsPageSchema = z43.object({
|
|
8171
|
+
referrals: z43.array(ReferralDashboardReferralSchema),
|
|
8172
|
+
total: z43.number().int().nonnegative()
|
|
8173
|
+
});
|
|
8174
|
+
var ReferralDashboardPlanBreakdownSchema = z43.object({
|
|
8175
|
+
planName: z43.string().nullable(),
|
|
8176
|
+
count: z43.number().int().nonnegative()
|
|
8177
|
+
});
|
|
8178
|
+
var ReferralDashboardMonthlyRegistrationSchema = z43.object({
|
|
8179
|
+
month: z43.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/),
|
|
8180
|
+
count: z43.number().int().nonnegative()
|
|
8181
|
+
});
|
|
8182
|
+
var ReferralDashboardOverviewSchema = z43.object({
|
|
8183
|
+
totalReferredUsers: z43.number().int().nonnegative(),
|
|
8184
|
+
activeUsers: z43.number().int().nonnegative(),
|
|
8185
|
+
pendingUsers: z43.number().int().nonnegative(),
|
|
8186
|
+
inactiveUsers: z43.number().int().nonnegative(),
|
|
8187
|
+
noPlanUsers: z43.number().int().nonnegative(),
|
|
8188
|
+
planBreakdown: z43.array(ReferralDashboardPlanBreakdownSchema),
|
|
8189
|
+
monthlyRegistrations: z43.array(ReferralDashboardMonthlyRegistrationSchema).refine((months) => months.length === 6, "Expected six monthly registration buckets")
|
|
8190
|
+
});
|
|
8191
|
+
|
|
7773
8192
|
// src/resolve-media.ts
|
|
7774
8193
|
var MAX_MEDIA_BYTES = CAMPAIGN_LIMITS.MAX_FILE_SIZE_BYTES;
|
|
7775
8194
|
var MEDIA_FORMATS = [
|
|
@@ -8009,10 +8428,10 @@ async function resolveMediaRef(ref, opts) {
|
|
|
8009
8428
|
__name(resolveMediaRef, "resolveMediaRef");
|
|
8010
8429
|
|
|
8011
8430
|
// src/tools/shared.ts
|
|
8012
|
-
import { z as
|
|
8013
|
-
var Lang =
|
|
8014
|
-
var Country =
|
|
8015
|
-
var LandingStrategyEnum =
|
|
8431
|
+
import { z as z44 } from "zod";
|
|
8432
|
+
var Lang = z44.string().min(2).max(10);
|
|
8433
|
+
var Country = z44.string().min(2).max(3);
|
|
8434
|
+
var LandingStrategyEnum = z44.enum([
|
|
8016
8435
|
"auto",
|
|
8017
8436
|
"classic",
|
|
8018
8437
|
"pain_solution",
|
|
@@ -8021,12 +8440,12 @@ var LandingStrategyEnum = z43.enum([
|
|
|
8021
8440
|
"offer_value",
|
|
8022
8441
|
"lifestyle_identity"
|
|
8023
8442
|
]);
|
|
8024
|
-
var LandingImageFormatEnum =
|
|
8443
|
+
var LandingImageFormatEnum = z44.enum([
|
|
8025
8444
|
"9:16",
|
|
8026
8445
|
"4:5",
|
|
8027
8446
|
"1:1"
|
|
8028
8447
|
]);
|
|
8029
|
-
var VideoPresetEnum =
|
|
8448
|
+
var VideoPresetEnum = z44.enum([
|
|
8030
8449
|
"auto",
|
|
8031
8450
|
"ugc_dynamic",
|
|
8032
8451
|
"ugc_how_to",
|
|
@@ -8039,7 +8458,7 @@ var VideoPresetEnum = z43.enum([
|
|
|
8039
8458
|
"pixar_problem",
|
|
8040
8459
|
"pixar_sassy"
|
|
8041
8460
|
]);
|
|
8042
|
-
var TaskCreativeVariantVideoPresetEnum =
|
|
8461
|
+
var TaskCreativeVariantVideoPresetEnum = z44.enum([
|
|
8043
8462
|
"auto",
|
|
8044
8463
|
"ugc_dynamic",
|
|
8045
8464
|
"ugc_how_to",
|
|
@@ -8110,7 +8529,7 @@ function resolveVideoPreset(preset) {
|
|
|
8110
8529
|
return VIDEO_PRESET_RESOLUTION[preset] ?? null;
|
|
8111
8530
|
}
|
|
8112
8531
|
__name(resolveVideoPreset, "resolveVideoPreset");
|
|
8113
|
-
var VideoTierEnum =
|
|
8532
|
+
var VideoTierEnum = z44.enum([
|
|
8114
8533
|
"basic"
|
|
8115
8534
|
]);
|
|
8116
8535
|
function resolveVideoTier(_tier) {
|
|
@@ -8121,27 +8540,21 @@ function resolveWireVideoTier(tier, durationSeconds) {
|
|
|
8121
8540
|
return durationSeconds === 30 ? "standard" : resolveVideoTier(tier);
|
|
8122
8541
|
}
|
|
8123
8542
|
__name(resolveWireVideoTier, "resolveWireVideoTier");
|
|
8124
|
-
function assertVideoPresetDurationCompatibility(videoPreset, durationSeconds) {
|
|
8125
|
-
if (videoPreset === "problem_solution" && durationSeconds !== 30) {
|
|
8126
|
-
throw new Error("Problema y Soluci\xF3n solo est\xE1 disponible en videos Multiescena de 30 segundos.");
|
|
8127
|
-
}
|
|
8128
|
-
}
|
|
8129
|
-
__name(assertVideoPresetDurationCompatibility, "assertVideoPresetDurationCompatibility");
|
|
8130
8543
|
function resolveVideoFormatForDuration(durationSeconds) {
|
|
8131
8544
|
return durationSeconds === 30 ? "double_30s" : "single_15s";
|
|
8132
8545
|
}
|
|
8133
8546
|
__name(resolveVideoFormatForDuration, "resolveVideoFormatForDuration");
|
|
8134
|
-
var VideoResolutionEnum =
|
|
8547
|
+
var VideoResolutionEnum = z44.enum([
|
|
8135
8548
|
"480p",
|
|
8136
8549
|
"720p"
|
|
8137
8550
|
]);
|
|
8138
|
-
var VideoStyleEnum =
|
|
8551
|
+
var VideoStyleEnum = z44.enum([
|
|
8139
8552
|
"ugc",
|
|
8140
8553
|
"cartoon_3d",
|
|
8141
8554
|
"product_only",
|
|
8142
8555
|
"ai_dynamic"
|
|
8143
8556
|
]);
|
|
8144
|
-
var NarrativeStyleEnum =
|
|
8557
|
+
var NarrativeStyleEnum = z44.enum([
|
|
8145
8558
|
"review",
|
|
8146
8559
|
"ugc_how_to",
|
|
8147
8560
|
"problem_product_cta",
|
|
@@ -8158,7 +8571,7 @@ var NarrativeStyleEnum = z43.enum([
|
|
|
8158
8571
|
"ugc_skeptic_confession",
|
|
8159
8572
|
"ugc_demo_review"
|
|
8160
8573
|
]);
|
|
8161
|
-
var PublicNarrativeStyleEnum =
|
|
8574
|
+
var PublicNarrativeStyleEnum = z44.enum([
|
|
8162
8575
|
"review",
|
|
8163
8576
|
"ugc_how_to",
|
|
8164
8577
|
"ugc_unboxing_chaos",
|
|
@@ -8170,7 +8583,7 @@ var PublicNarrativeStyleEnum = z43.enum([
|
|
|
8170
8583
|
"animated_problem_pixar",
|
|
8171
8584
|
"sassy_product_pixar"
|
|
8172
8585
|
]);
|
|
8173
|
-
var VideoModelEnum =
|
|
8586
|
+
var VideoModelEnum = z44.enum([
|
|
8174
8587
|
"veo",
|
|
8175
8588
|
"wan",
|
|
8176
8589
|
"bytedance",
|
|
@@ -8178,25 +8591,25 @@ var VideoModelEnum = z43.enum([
|
|
|
8178
8591
|
"grok",
|
|
8179
8592
|
"kling"
|
|
8180
8593
|
]);
|
|
8181
|
-
var ProductReferenceUserChoiceEnum =
|
|
8594
|
+
var ProductReferenceUserChoiceEnum = z44.enum([
|
|
8182
8595
|
"same_product",
|
|
8183
8596
|
"variants",
|
|
8184
8597
|
"different_products",
|
|
8185
8598
|
"combo_distinct"
|
|
8186
8599
|
]);
|
|
8187
|
-
var ProductReferenceCandidateGroupingEnum =
|
|
8600
|
+
var ProductReferenceCandidateGroupingEnum = z44.enum([
|
|
8188
8601
|
"same_product_same_variant",
|
|
8189
8602
|
"same_product_different_variants",
|
|
8190
8603
|
"different_products",
|
|
8191
8604
|
"unclear"
|
|
8192
8605
|
]);
|
|
8193
|
-
var GapDurationChoiceMirror =
|
|
8194
|
-
angleIndex:
|
|
8195
|
-
recommendedSec:
|
|
8196
|
-
|
|
8197
|
-
|
|
8606
|
+
var GapDurationChoiceMirror = z44.object({
|
|
8607
|
+
angleIndex: z44.number().int().nonnegative(),
|
|
8608
|
+
recommendedSec: z44.union([
|
|
8609
|
+
z44.literal(15),
|
|
8610
|
+
z44.literal(30)
|
|
8198
8611
|
]),
|
|
8199
|
-
estimatedDeltaCredits:
|
|
8612
|
+
estimatedDeltaCredits: z44.number().int().nonnegative().optional()
|
|
8200
8613
|
});
|
|
8201
8614
|
function buildSelectedImageUrlsHash(urls) {
|
|
8202
8615
|
const input = [
|
|
@@ -8210,13 +8623,13 @@ function buildSelectedImageUrlsHash(urls) {
|
|
|
8210
8623
|
return `fnv1a:${hash.toString(16).padStart(8, "0")}:${urls.length}`;
|
|
8211
8624
|
}
|
|
8212
8625
|
__name(buildSelectedImageUrlsHash, "buildSelectedImageUrlsHash");
|
|
8213
|
-
var ImageCreativeStyleEnum =
|
|
8626
|
+
var ImageCreativeStyleEnum = z44.enum([
|
|
8214
8627
|
"studio",
|
|
8215
8628
|
"floating",
|
|
8216
8629
|
"ingredients",
|
|
8217
8630
|
"in_use"
|
|
8218
8631
|
]);
|
|
8219
|
-
var ImageCreativeArchetypeEnum =
|
|
8632
|
+
var ImageCreativeArchetypeEnum = z44.enum([
|
|
8220
8633
|
"direct_response_product_ad",
|
|
8221
8634
|
"hero_outcome",
|
|
8222
8635
|
"offer_value_prop",
|
|
@@ -8225,23 +8638,23 @@ var ImageCreativeArchetypeEnum = z43.enum([
|
|
|
8225
8638
|
"problem_solution",
|
|
8226
8639
|
"lifestyle_identity"
|
|
8227
8640
|
]);
|
|
8228
|
-
var ImageCreativeVisualTreatmentEnum =
|
|
8641
|
+
var ImageCreativeVisualTreatmentEnum = z44.enum([
|
|
8229
8642
|
"performance_product_ad",
|
|
8230
8643
|
"premium_dtc_carousel"
|
|
8231
8644
|
]);
|
|
8232
|
-
var ImageCreativeAspectRatioEnum =
|
|
8645
|
+
var ImageCreativeAspectRatioEnum = z44.enum([
|
|
8233
8646
|
"1:1",
|
|
8234
8647
|
"4:5",
|
|
8235
8648
|
"9:16",
|
|
8236
8649
|
"16:9"
|
|
8237
8650
|
]);
|
|
8238
|
-
var ImageRefSchema =
|
|
8239
|
-
url:
|
|
8240
|
-
path:
|
|
8651
|
+
var ImageRefSchema = z44.object({
|
|
8652
|
+
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.'),
|
|
8653
|
+
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`.')
|
|
8241
8654
|
}).refine((v) => Boolean(v.url) !== Boolean(v.path), {
|
|
8242
8655
|
message: "each image entry must have exactly one of `url` or `path`"
|
|
8243
8656
|
});
|
|
8244
|
-
var ImagesInput =
|
|
8657
|
+
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.');
|
|
8245
8658
|
function asText(value) {
|
|
8246
8659
|
return {
|
|
8247
8660
|
content: [
|
|
@@ -8294,14 +8707,14 @@ async function submitCreative(api, opts) {
|
|
|
8294
8707
|
});
|
|
8295
8708
|
}
|
|
8296
8709
|
__name(submitCreative, "submitCreative");
|
|
8297
|
-
var ResearchTargetCountryEnum =
|
|
8710
|
+
var ResearchTargetCountryEnum = z44.enum([
|
|
8298
8711
|
"CO",
|
|
8299
8712
|
"MX",
|
|
8300
8713
|
"PE",
|
|
8301
8714
|
"EC",
|
|
8302
8715
|
"CL"
|
|
8303
8716
|
]);
|
|
8304
|
-
var ResearchCategoryEnum =
|
|
8717
|
+
var ResearchCategoryEnum = z44.enum([
|
|
8305
8718
|
"auto",
|
|
8306
8719
|
"automotive",
|
|
8307
8720
|
"kitchen",
|
|
@@ -8374,6 +8787,15 @@ var McpErrorCode = {
|
|
|
8374
8787
|
SHOPIFY_PUBLISH_FAILED: "SHOPIFY_PUBLISH_FAILED",
|
|
8375
8788
|
// Meta / Facebook Ads
|
|
8376
8789
|
META_NO_FB_PAGE: "META_NO_FB_PAGE",
|
|
8790
|
+
META_PORTFOLIO_MISMATCH: "META_PORTFOLIO_MISMATCH",
|
|
8791
|
+
META_PAGE_SELECTION_REQUIRED: "META_PAGE_SELECTION_REQUIRED",
|
|
8792
|
+
META_INVALID_PAGE: "META_INVALID_PAGE",
|
|
8793
|
+
META_STAGED_ASSET_NOT_FOUND: "META_STAGED_ASSET_NOT_FOUND",
|
|
8794
|
+
META_STAGED_ASSET_AMBIGUOUS: "META_STAGED_ASSET_AMBIGUOUS",
|
|
8795
|
+
META_STAGED_ASSET_TYPE_MISMATCH: "META_STAGED_ASSET_TYPE_MISMATCH",
|
|
8796
|
+
META_STAGED_ASSET_INVALID: "META_STAGED_ASSET_INVALID",
|
|
8797
|
+
META_PUBLICATION_RECONCILIATION_REQUIRED: "META_PUBLICATION_RECONCILIATION_REQUIRED",
|
|
8798
|
+
META_PUBLICATION_IN_PROGRESS: "META_PUBLICATION_IN_PROGRESS",
|
|
8377
8799
|
META_NO_ADS_CREATED: "META_NO_ADS_CREATED",
|
|
8378
8800
|
META_NO_ASSETS_READY: "META_NO_ASSETS_READY",
|
|
8379
8801
|
META_NO_THUMBNAIL_SOURCE: "META_NO_THUMBNAIL_SOURCE",
|
|
@@ -8426,6 +8848,15 @@ var CODE_MESSAGES = {
|
|
|
8426
8848
|
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.",
|
|
8427
8849
|
// Meta — high-frequency
|
|
8428
8850
|
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).",
|
|
8851
|
+
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.",
|
|
8852
|
+
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`.",
|
|
8853
|
+
META_INVALID_PAGE: "The selected Facebook Page is not available for this Meta connection. Call `list_meta_destinations` again and choose an available `pageId`.",
|
|
8854
|
+
META_STAGED_ASSET_NOT_FOUND: "The temporary media upload was not found or expired. Re-upload the media and retry the Meta publication.",
|
|
8855
|
+
META_STAGED_ASSET_AMBIGUOUS: "The temporary media upload could not be uniquely resolved. Re-upload the media and retry the Meta publication.",
|
|
8856
|
+
META_STAGED_ASSET_TYPE_MISMATCH: "The temporary media type does not match the creative. Re-upload the correct image or video and retry.",
|
|
8857
|
+
META_STAGED_ASSET_INVALID: "The temporary media is empty or exceeds the server size limit. Re-upload the file and retry.",
|
|
8858
|
+
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.",
|
|
8859
|
+
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.",
|
|
8429
8860
|
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.",
|
|
8430
8861
|
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.",
|
|
8431
8862
|
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.",
|
|
@@ -8539,6 +8970,12 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
|
|
|
8539
8970
|
McpErrorCode.CAMPAIGN_MISSING_LANDING_URL,
|
|
8540
8971
|
McpErrorCode.CAMPAIGN_RECONCILIATION_REQUIRED,
|
|
8541
8972
|
McpErrorCode.META_NO_LANDING_URL,
|
|
8973
|
+
McpErrorCode.META_PORTFOLIO_MISMATCH,
|
|
8974
|
+
McpErrorCode.META_PAGE_SELECTION_REQUIRED,
|
|
8975
|
+
McpErrorCode.META_INVALID_PAGE,
|
|
8976
|
+
McpErrorCode.META_STAGED_ASSET_NOT_FOUND,
|
|
8977
|
+
McpErrorCode.META_STAGED_ASSET_AMBIGUOUS,
|
|
8978
|
+
McpErrorCode.META_STAGED_ASSET_TYPE_MISMATCH,
|
|
8542
8979
|
McpErrorCode.META_DAILY_BUDGET_TOO_HIGH,
|
|
8543
8980
|
McpErrorCode.META_CAMPAIGN_CONFIG_INVALID,
|
|
8544
8981
|
McpErrorCode.META_WHATSAPP_INVALID_NUMBER,
|
|
@@ -8560,6 +8997,15 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
|
|
|
8560
8997
|
McpErrorCode.META_MISSING_PAYMENT,
|
|
8561
8998
|
McpErrorCode.META_ACCOUNT_RESTRICTED,
|
|
8562
8999
|
McpErrorCode.META_NO_FB_PAGE,
|
|
9000
|
+
McpErrorCode.META_PORTFOLIO_MISMATCH,
|
|
9001
|
+
McpErrorCode.META_PAGE_SELECTION_REQUIRED,
|
|
9002
|
+
McpErrorCode.META_INVALID_PAGE,
|
|
9003
|
+
McpErrorCode.META_STAGED_ASSET_NOT_FOUND,
|
|
9004
|
+
McpErrorCode.META_STAGED_ASSET_AMBIGUOUS,
|
|
9005
|
+
McpErrorCode.META_STAGED_ASSET_TYPE_MISMATCH,
|
|
9006
|
+
McpErrorCode.META_STAGED_ASSET_INVALID,
|
|
9007
|
+
McpErrorCode.META_PUBLICATION_RECONCILIATION_REQUIRED,
|
|
9008
|
+
McpErrorCode.META_PUBLICATION_IN_PROGRESS,
|
|
8563
9009
|
McpErrorCode.TIKTOK_TOKEN_EXPIRED,
|
|
8564
9010
|
McpErrorCode.TIKTOK_PERMISSION_DENIED,
|
|
8565
9011
|
McpErrorCode.TIKTOK_MISSING_PAYMENT,
|
|
@@ -8954,53 +9400,53 @@ function renderOutputsMarkdown(outputs) {
|
|
|
8954
9400
|
__name(renderOutputsMarkdown, "renderOutputsMarkdown");
|
|
8955
9401
|
|
|
8956
9402
|
// src/tools/tasks.ts
|
|
8957
|
-
var CreateProductTaskInput =
|
|
9403
|
+
var CreateProductTaskInput = z45.object({
|
|
8958
9404
|
images: ImagesInput.optional().describe("1\u20135 fotos del mismo producto. Omitir \xFAnicamente cuando projectId apunta a un proyecto propio con imagen guardada."),
|
|
8959
|
-
projectId:
|
|
8960
|
-
productUrl:
|
|
9405
|
+
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."),
|
|
9406
|
+
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."),
|
|
8961
9407
|
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.'),
|
|
8962
9408
|
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.'),
|
|
8963
|
-
price:
|
|
8964
|
-
compareAtPrice:
|
|
8965
|
-
offer:
|
|
8966
|
-
paymentMethods:
|
|
8967
|
-
productContext:
|
|
8968
|
-
productLabel:
|
|
8969
|
-
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
|
|
8970
|
-
durationSeconds:
|
|
8971
|
-
|
|
8972
|
-
|
|
9409
|
+
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.'),
|
|
9410
|
+
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".'),
|
|
9411
|
+
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.'),
|
|
9412
|
+
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.'),
|
|
9413
|
+
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."),
|
|
9414
|
+
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.'),
|
|
9415
|
+
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.'),
|
|
9416
|
+
durationSeconds: z45.union([
|
|
9417
|
+
z45.literal(15),
|
|
9418
|
+
z45.literal(30)
|
|
8973
9419
|
]).default(15).describe("Opcional. Duraci\xF3n objetivo del video: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
|
|
8974
|
-
pauseForAngleReview:
|
|
9420
|
+
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."),
|
|
8975
9421
|
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.'),
|
|
8976
9422
|
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).'),
|
|
8977
|
-
multiAngleVideos:
|
|
9423
|
+
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."),
|
|
8978
9424
|
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.'),
|
|
8979
|
-
outputs:
|
|
9425
|
+
outputs: z45.array(z45.enum([
|
|
8980
9426
|
"landing",
|
|
8981
9427
|
"video",
|
|
8982
9428
|
"image"
|
|
8983
9429
|
])).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.'),
|
|
8984
|
-
competitorScanEnabled:
|
|
8985
|
-
brandId:
|
|
8986
|
-
actorId:
|
|
8987
|
-
creativeDirection:
|
|
8988
|
-
headline:
|
|
8989
|
-
subheadline:
|
|
9430
|
+
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."),
|
|
9431
|
+
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."),
|
|
9432
|
+
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."),
|
|
9433
|
+
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.'),
|
|
9434
|
+
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."),
|
|
9435
|
+
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."),
|
|
8990
9436
|
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".'),
|
|
8991
9437
|
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".')
|
|
8992
9438
|
}).strict();
|
|
8993
|
-
var CreateServiceVideoInput =
|
|
8994
|
-
landingUrl:
|
|
8995
|
-
additionalContext:
|
|
9439
|
+
var CreateServiceVideoInput = z45.object({
|
|
9440
|
+
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."),
|
|
9441
|
+
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.'),
|
|
8996
9442
|
language: Lang.describe("Idioma del video: es, en o pt."),
|
|
8997
|
-
voiceId:
|
|
8998
|
-
brandId:
|
|
8999
|
-
reviewMode:
|
|
9443
|
+
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.'),
|
|
9444
|
+
brandId: z45.string().uuid().optional().describe("Marca guardada del usuario. Aplica voz, mensajes, paleta y persona predeterminada."),
|
|
9445
|
+
reviewMode: z45.enum([
|
|
9000
9446
|
"automatic",
|
|
9001
9447
|
"guided"
|
|
9002
9448
|
]).default("automatic").describe("automatic = la IA elige concepto y guion sin pausas; guided = el usuario aprueba concepto y storyboard."),
|
|
9003
|
-
videoPreset:
|
|
9449
|
+
videoPreset: z45.enum([
|
|
9004
9450
|
"auto",
|
|
9005
9451
|
"ugc_dynamic",
|
|
9006
9452
|
"ugc_how_to",
|
|
@@ -9008,35 +9454,35 @@ var CreateServiceVideoInput = z44.object({
|
|
|
9008
9454
|
"wild_card"
|
|
9009
9455
|
]).default("auto")
|
|
9010
9456
|
}).strict();
|
|
9011
|
-
var GetTaskStatusInput =
|
|
9012
|
-
taskId:
|
|
9013
|
-
waitSeconds:
|
|
9014
|
-
});
|
|
9015
|
-
var ListTasksInput =
|
|
9016
|
-
limit:
|
|
9017
|
-
offset:
|
|
9018
|
-
search:
|
|
9019
|
-
});
|
|
9020
|
-
var EditTaskDraftInput =
|
|
9021
|
-
taskId:
|
|
9022
|
-
angleIndex:
|
|
9023
|
-
angle:
|
|
9024
|
-
angle:
|
|
9025
|
-
buyerPersona:
|
|
9026
|
-
actor:
|
|
9027
|
-
actorId:
|
|
9028
|
-
gender:
|
|
9457
|
+
var GetTaskStatusInput = z45.object({
|
|
9458
|
+
taskId: z45.string().min(1),
|
|
9459
|
+
waitSeconds: z45.number().int().min(0).max(120).optional()
|
|
9460
|
+
});
|
|
9461
|
+
var ListTasksInput = z45.object({
|
|
9462
|
+
limit: z45.number().int().min(1).max(50).optional(),
|
|
9463
|
+
offset: z45.number().int().min(0).optional(),
|
|
9464
|
+
search: z45.string().optional()
|
|
9465
|
+
});
|
|
9466
|
+
var EditTaskDraftInput = z45.object({
|
|
9467
|
+
taskId: z45.string().min(1),
|
|
9468
|
+
angleIndex: z45.number().int().min(0),
|
|
9469
|
+
angle: z45.object({
|
|
9470
|
+
angle: z45.string().min(1).max(480),
|
|
9471
|
+
buyerPersona: z45.string().min(1).max(360),
|
|
9472
|
+
actor: z45.string().min(1).max(360).optional(),
|
|
9473
|
+
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."),
|
|
9474
|
+
gender: z45.enum([
|
|
9029
9475
|
"female",
|
|
9030
9476
|
"male"
|
|
9031
9477
|
]).optional(),
|
|
9032
|
-
scriptNotes:
|
|
9478
|
+
scriptNotes: z45.string().max(2e3).optional()
|
|
9033
9479
|
})
|
|
9034
9480
|
});
|
|
9035
|
-
var PendingActionPayload =
|
|
9036
|
-
|
|
9037
|
-
kind:
|
|
9481
|
+
var PendingActionPayload = z45.discriminatedUnion("kind", [
|
|
9482
|
+
z45.object({
|
|
9483
|
+
kind: z45.literal("product_reference_confirmation"),
|
|
9038
9484
|
userChoice: ProductReferenceUserChoiceEnum,
|
|
9039
|
-
confirmedProductNames:
|
|
9485
|
+
confirmedProductNames: z45.array(z45.string().min(1).max(120)).min(2).max(8).optional(),
|
|
9040
9486
|
// SCOPED multi-scene opt-in — the ONLY way STANDARD ("Multiescena") is
|
|
9041
9487
|
// reachable via MCP. Valid ONLY together with userChoice='variants' (same
|
|
9042
9488
|
// product in several colors): renders one shot per color and stitches them,
|
|
@@ -9047,50 +9493,50 @@ var PendingActionPayload = z44.discriminatedUnion("kind", [
|
|
|
9047
9493
|
// safe because multi-scene IS the intended render for a color variant set,
|
|
9048
9494
|
// not a blind quality upsell. Maps to `videoTier: 'standard'` on the
|
|
9049
9495
|
// confirm call; the backend re-gates it (variants + variant_set only).
|
|
9050
|
-
useMultiSceneShowcase:
|
|
9496
|
+
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".')
|
|
9051
9497
|
}),
|
|
9052
|
-
|
|
9053
|
-
kind:
|
|
9498
|
+
z45.object({
|
|
9499
|
+
kind: z45.literal("angle_review_approval"),
|
|
9054
9500
|
// All edits are optional — empty action body just approves the
|
|
9055
9501
|
// backend-generated draft as-is. Field shapes mirror the angle-review
|
|
9056
9502
|
// request contracts in @packages/types/wizard.ts; the handler routes to
|
|
9057
9503
|
// the dedicated video/image/autopilot endpoint based on StatusResponse.kind.
|
|
9058
|
-
customAngle:
|
|
9504
|
+
customAngle: z45.string().min(1).max(480).optional(),
|
|
9059
9505
|
// Keep the shared MCP action within the strictest downstream contract:
|
|
9060
9506
|
// autopilot/image resume endpoints cap these fields at 1500 characters.
|
|
9061
|
-
painPoints:
|
|
9062
|
-
benefits:
|
|
9063
|
-
features:
|
|
9064
|
-
gapDurationSeconds:
|
|
9065
|
-
|
|
9066
|
-
|
|
9507
|
+
painPoints: z45.string().max(1500).optional(),
|
|
9508
|
+
benefits: z45.string().max(1500).optional(),
|
|
9509
|
+
features: z45.array(z45.string().min(1).max(300)).max(10).optional(),
|
|
9510
|
+
gapDurationSeconds: z45.union([
|
|
9511
|
+
z45.literal(15),
|
|
9512
|
+
z45.literal(30)
|
|
9067
9513
|
]).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.")
|
|
9068
9514
|
}),
|
|
9069
|
-
|
|
9070
|
-
kind:
|
|
9071
|
-
angleId:
|
|
9072
|
-
content:
|
|
9073
|
-
creativeDirection:
|
|
9074
|
-
actor:
|
|
9515
|
+
z45.object({
|
|
9516
|
+
kind: z45.literal("service_angle_approval"),
|
|
9517
|
+
angleId: z45.string().uuid(),
|
|
9518
|
+
content: z45.string().min(1).max(480).optional(),
|
|
9519
|
+
creativeDirection: z45.string().min(1).max(1500).optional(),
|
|
9520
|
+
actor: z45.string().min(1).max(360).optional()
|
|
9075
9521
|
}),
|
|
9076
|
-
|
|
9077
|
-
kind:
|
|
9078
|
-
voiceovers:
|
|
9079
|
-
visualDirections:
|
|
9522
|
+
z45.object({
|
|
9523
|
+
kind: z45.literal("service_script_approval"),
|
|
9524
|
+
voiceovers: z45.record(z45.string(), z45.string().min(1).max(600)).optional(),
|
|
9525
|
+
visualDirections: z45.record(z45.string(), z45.string().min(1).max(1200)).optional()
|
|
9080
9526
|
})
|
|
9081
9527
|
]);
|
|
9082
|
-
var ResolvePendingActionInput =
|
|
9083
|
-
taskId:
|
|
9528
|
+
var ResolvePendingActionInput = z45.object({
|
|
9529
|
+
taskId: z45.string().min(1),
|
|
9084
9530
|
action: PendingActionPayload
|
|
9085
9531
|
}).strict();
|
|
9086
9532
|
function resolveVariantConfirmVideoTier(action) {
|
|
9087
9533
|
return action.useMultiSceneShowcase && action.userChoice === "variants" ? "standard" : void 0;
|
|
9088
9534
|
}
|
|
9089
9535
|
__name(resolveVariantConfirmVideoTier, "resolveVariantConfirmVideoTier");
|
|
9090
|
-
var StatusForResolveSchema =
|
|
9536
|
+
var StatusForResolveSchema = z45.object({
|
|
9091
9537
|
// Metadata used to select the backend resume contract. Older API responses
|
|
9092
9538
|
// may omit these fields, so the resolver keeps the generic fallback.
|
|
9093
|
-
kind:
|
|
9539
|
+
kind: z45.enum([
|
|
9094
9540
|
"autopilot",
|
|
9095
9541
|
"video",
|
|
9096
9542
|
"image",
|
|
@@ -9098,24 +9544,24 @@ var StatusForResolveSchema = z44.object({
|
|
|
9098
9544
|
"mix",
|
|
9099
9545
|
"service"
|
|
9100
9546
|
]).optional(),
|
|
9101
|
-
selectedOutputs:
|
|
9547
|
+
selectedOutputs: z45.array(z45.enum([
|
|
9102
9548
|
"landing",
|
|
9103
9549
|
"video",
|
|
9104
9550
|
"image"
|
|
9105
9551
|
])).optional(),
|
|
9106
|
-
videoAngleCount:
|
|
9107
|
-
ugcFormat:
|
|
9552
|
+
videoAngleCount: z45.number().int().min(1).max(3).optional(),
|
|
9553
|
+
ugcFormat: z45.enum([
|
|
9108
9554
|
"single_8s",
|
|
9109
9555
|
"single_15s",
|
|
9110
9556
|
"double_30s"
|
|
9111
9557
|
]).optional(),
|
|
9112
|
-
videoStyle:
|
|
9558
|
+
videoStyle: z45.enum([
|
|
9113
9559
|
"ugc",
|
|
9114
9560
|
"cartoon_3d",
|
|
9115
9561
|
"product_only",
|
|
9116
9562
|
"ai_dynamic"
|
|
9117
9563
|
]).optional(),
|
|
9118
|
-
videoModel:
|
|
9564
|
+
videoModel: z45.enum([
|
|
9119
9565
|
"veo",
|
|
9120
9566
|
"wan",
|
|
9121
9567
|
"bytedance",
|
|
@@ -9123,26 +9569,26 @@ var StatusForResolveSchema = z44.object({
|
|
|
9123
9569
|
"grok",
|
|
9124
9570
|
"kling"
|
|
9125
9571
|
]).optional(),
|
|
9126
|
-
videoTier:
|
|
9572
|
+
videoTier: z45.enum([
|
|
9127
9573
|
"legacy",
|
|
9128
9574
|
"standard",
|
|
9129
9575
|
"pro",
|
|
9130
9576
|
"single_call"
|
|
9131
9577
|
]).optional(),
|
|
9132
|
-
videoResolution:
|
|
9578
|
+
videoResolution: z45.enum([
|
|
9133
9579
|
"480p",
|
|
9134
9580
|
"720p"
|
|
9135
9581
|
]).optional(),
|
|
9136
9582
|
narrativeStyle: NarrativeStyleEnum.optional(),
|
|
9137
|
-
pendingUserAction:
|
|
9138
|
-
kind:
|
|
9139
|
-
candidateGrouping:
|
|
9140
|
-
selectedImageUrls:
|
|
9141
|
-
proposedProductNames:
|
|
9142
|
-
customAngleDraft:
|
|
9143
|
-
painPointsDraft:
|
|
9144
|
-
benefitsDraft:
|
|
9145
|
-
featuresDraft:
|
|
9583
|
+
pendingUserAction: z45.object({
|
|
9584
|
+
kind: z45.string(),
|
|
9585
|
+
candidateGrouping: z45.string().optional(),
|
|
9586
|
+
selectedImageUrls: z45.array(z45.string()).optional(),
|
|
9587
|
+
proposedProductNames: z45.array(z45.string()).optional(),
|
|
9588
|
+
customAngleDraft: z45.string().optional(),
|
|
9589
|
+
painPointsDraft: z45.string().optional(),
|
|
9590
|
+
benefitsDraft: z45.string().optional(),
|
|
9591
|
+
featuresDraft: z45.array(z45.string()).optional(),
|
|
9146
9592
|
// Recommendation consumed only when the caller explicitly sends
|
|
9147
9593
|
// gapDurationSeconds: 30 in the angle approval action.
|
|
9148
9594
|
gapDurationChoice: GapDurationChoiceMirror.optional(),
|
|
@@ -9151,17 +9597,17 @@ var StatusForResolveSchema = z44.object({
|
|
|
9151
9597
|
// keys. `actorId` (the saved custom actor the picker stored) was silently
|
|
9152
9598
|
// dropped here, so the approval handler below could not echo it back on the
|
|
9153
9599
|
// PATCH — the selection looked accepted and reached no render.
|
|
9154
|
-
angles:
|
|
9155
|
-
id:
|
|
9156
|
-
content:
|
|
9157
|
-
creativeDirection:
|
|
9158
|
-
actor:
|
|
9159
|
-
actorId:
|
|
9600
|
+
angles: z45.array(z45.object({
|
|
9601
|
+
id: z45.string().uuid(),
|
|
9602
|
+
content: z45.string(),
|
|
9603
|
+
creativeDirection: z45.string(),
|
|
9604
|
+
actor: z45.string(),
|
|
9605
|
+
actorId: z45.string().nullable().optional()
|
|
9160
9606
|
}).passthrough()).optional(),
|
|
9161
|
-
panels:
|
|
9162
|
-
beatId:
|
|
9163
|
-
voiceover:
|
|
9164
|
-
visualDirection:
|
|
9607
|
+
panels: z45.array(z45.object({
|
|
9608
|
+
beatId: z45.string(),
|
|
9609
|
+
voiceover: z45.string(),
|
|
9610
|
+
visualDirection: z45.string()
|
|
9165
9611
|
}).passthrough()).optional()
|
|
9166
9612
|
}).optional()
|
|
9167
9613
|
}).passthrough();
|
|
@@ -9202,7 +9648,6 @@ __name(resolveStatusTaskKind, "resolveStatusTaskKind");
|
|
|
9202
9648
|
function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http" } = {}) {
|
|
9203
9649
|
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) => {
|
|
9204
9650
|
try {
|
|
9205
|
-
assertVideoPresetDurationCompatibility(input.videoPreset, input.durationSeconds);
|
|
9206
9651
|
if (!input.images && !input.projectId) {
|
|
9207
9652
|
throw new Error("images es requerido salvo que env\xEDes projectId.");
|
|
9208
9653
|
}
|
|
@@ -9602,58 +10047,58 @@ __name(registerTaskTools, "registerTaskTools");
|
|
|
9602
10047
|
|
|
9603
10048
|
// src/tools/creatives.ts
|
|
9604
10049
|
import { randomUUID } from "crypto";
|
|
9605
|
-
import { z as
|
|
10050
|
+
import { z as z46 } from "zod";
|
|
9606
10051
|
var CreativeRefs = {
|
|
9607
10052
|
images: ImagesInput,
|
|
9608
10053
|
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.'),
|
|
9609
10054
|
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.'),
|
|
9610
|
-
price:
|
|
9611
|
-
offer:
|
|
9612
|
-
compareAtPrice:
|
|
9613
|
-
paymentMethods:
|
|
9614
|
-
productContext:
|
|
9615
|
-
productLabel:
|
|
9616
|
-
buyerPersona:
|
|
9617
|
-
isCombo:
|
|
9618
|
-
competitorScanEnabled:
|
|
9619
|
-
brandId:
|
|
9620
|
-
projectId:
|
|
9621
|
-
creativeDirection:
|
|
9622
|
-
headline:
|
|
9623
|
-
subheadline:
|
|
10055
|
+
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.'),
|
|
10056
|
+
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.'),
|
|
10057
|
+
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".'),
|
|
10058
|
+
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.'),
|
|
10059
|
+
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."),
|
|
10060
|
+
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.'),
|
|
10061
|
+
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.'),
|
|
10062
|
+
isCombo: z46.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
|
|
10063
|
+
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."),
|
|
10064
|
+
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."),
|
|
10065
|
+
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."),
|
|
10066
|
+
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.'),
|
|
10067
|
+
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."),
|
|
10068
|
+
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.")
|
|
9624
10069
|
};
|
|
9625
|
-
var VideoInput =
|
|
10070
|
+
var VideoInput = z46.object({
|
|
9626
10071
|
images: CreativeRefs.images,
|
|
9627
|
-
title:
|
|
9628
|
-
primaryAngle:
|
|
10072
|
+
title: z46.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
|
|
10073
|
+
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".'),
|
|
9629
10074
|
language: CreativeRefs.language,
|
|
9630
10075
|
country: CreativeRefs.country,
|
|
9631
|
-
aspectRatio:
|
|
10076
|
+
aspectRatio: z46.enum([
|
|
9632
10077
|
"9:16",
|
|
9633
10078
|
"16:9",
|
|
9634
10079
|
"Auto"
|
|
9635
10080
|
]).describe('Relaci\xF3n de aspecto. "9:16" = vertical (TikTok/Reels \u2014 default recomendado para ads). "16:9" = horizontal (YouTube). "Auto" = decide la IA.'),
|
|
9636
|
-
durationSeconds:
|
|
9637
|
-
|
|
9638
|
-
|
|
10081
|
+
durationSeconds: z46.union([
|
|
10082
|
+
z46.literal(15),
|
|
10083
|
+
z46.literal(30)
|
|
9639
10084
|
]).default(15).describe("Opcional. Duraci\xF3n objetivo: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
|
|
9640
|
-
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
|
|
10085
|
+
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).'),
|
|
9641
10086
|
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.'),
|
|
9642
10087
|
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).'),
|
|
9643
|
-
features:
|
|
10088
|
+
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"].'),
|
|
9644
10089
|
price: CreativeRefs.price,
|
|
9645
10090
|
offer: CreativeRefs.offer,
|
|
9646
10091
|
productContext: CreativeRefs.productContext,
|
|
9647
10092
|
productLabel: CreativeRefs.productLabel,
|
|
9648
|
-
cta:
|
|
9649
|
-
scriptNotes:
|
|
9650
|
-
pauseForVideoScriptReview:
|
|
9651
|
-
pauseForAngleReview:
|
|
10093
|
+
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.'),
|
|
10094
|
+
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.'),
|
|
10095
|
+
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."),
|
|
10096
|
+
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."),
|
|
9652
10097
|
competitorScanEnabled: CreativeRefs.competitorScanEnabled,
|
|
9653
10098
|
brandId: CreativeRefs.brandId,
|
|
9654
10099
|
projectId: CreativeRefs.projectId
|
|
9655
10100
|
}).strict();
|
|
9656
|
-
var FreeVideoQualityEnum =
|
|
10101
|
+
var FreeVideoQualityEnum = z46.enum([
|
|
9657
10102
|
"economical",
|
|
9658
10103
|
"high_detail"
|
|
9659
10104
|
]);
|
|
@@ -9675,38 +10120,38 @@ var FREE_VIDEO_DURATIONS_BY_QUALITY = {
|
|
|
9675
10120
|
15
|
|
9676
10121
|
]
|
|
9677
10122
|
};
|
|
9678
|
-
var FreeVideoInput =
|
|
10123
|
+
var FreeVideoInput = z46.object({
|
|
9679
10124
|
images: ImagesInput.optional().describe("Opcional. 1-5 fotos del producto. Om\xEDtelo para generar desde texto puro."),
|
|
9680
|
-
rawPrompt:
|
|
10125
|
+
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."),
|
|
9681
10126
|
quality: FreeVideoQualityEnum.describe('Calidad del video: "economical" = Econ\xF3mico o "high_detail" = Alto detalle.'),
|
|
9682
|
-
durationSeconds:
|
|
10127
|
+
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."),
|
|
9683
10128
|
videoResolution: VideoResolutionEnum.describe("Resoluci\xF3n del video: 480p o 720p."),
|
|
9684
|
-
generateAiMusic:
|
|
9685
|
-
addSubtitles:
|
|
9686
|
-
idempotencyKey:
|
|
10129
|
+
generateAiMusic: z46.boolean().default(false).describe("Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false."),
|
|
10130
|
+
addSubtitles: z46.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
|
|
10131
|
+
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.")
|
|
9687
10132
|
}).strict();
|
|
9688
10133
|
var imageStyleList = ImageCreativeStyleEnum.options.join(" | ");
|
|
9689
10134
|
var archetypeList = ImageCreativeArchetypeEnum.options.join(" | ");
|
|
9690
10135
|
var visualTreatmentList = ImageCreativeVisualTreatmentEnum.options.join(" | ");
|
|
9691
10136
|
var aspectRatioList = ImageCreativeAspectRatioEnum.options.join(" | ");
|
|
9692
|
-
var ImageInput =
|
|
10137
|
+
var ImageInput = z46.object({
|
|
9693
10138
|
images: CreativeRefs.images,
|
|
9694
|
-
productTitle:
|
|
9695
|
-
primaryAngle:
|
|
10139
|
+
productTitle: z46.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
|
|
10140
|
+
primaryAngle: z46.string().min(1).describe('\xC1ngulo de venta principal. Ej. "Reduce arrugas en 14 d\xEDas".'),
|
|
9696
10141
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
9697
10142
|
language: CreativeRefs.language,
|
|
9698
10143
|
country: CreativeRefs.country,
|
|
9699
|
-
nVariants:
|
|
9700
|
-
imageModel:
|
|
10144
|
+
nVariants: z46.number().int().min(1).max(6).optional().describe("Opcional. N\xFAmero de variantes a generar (1-6). Default 3."),
|
|
10145
|
+
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.'),
|
|
9701
10146
|
imageStyle: ImageCreativeStyleEnum.optional().describe(`Opcional. Estilo del shot del producto. Valores: ${imageStyleList}. Si se omite, la IA escoge seg\xFAn el archetype.`),
|
|
9702
10147
|
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).`),
|
|
9703
10148
|
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.`),
|
|
9704
|
-
styleMix:
|
|
10149
|
+
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."),
|
|
9705
10150
|
aspectRatio: ImageCreativeAspectRatioEnum.optional().describe(`Opcional. Relaci\xF3n de aspecto. Valores: ${aspectRatioList}. Default "4:5" (vertical Instagram feed).`),
|
|
9706
|
-
generateAdCopy:
|
|
9707
|
-
pauseForAngleReview:
|
|
9708
|
-
features:
|
|
9709
|
-
brandColors:
|
|
10151
|
+
generateAdCopy: z46.boolean().optional().describe("Opcional. Si true, tambi\xE9n genera headline/body/CTA para el ad. Default false."),
|
|
10152
|
+
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."),
|
|
10153
|
+
features: z46.array(z46.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
|
|
10154
|
+
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.'),
|
|
9710
10155
|
price: CreativeRefs.price,
|
|
9711
10156
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
9712
10157
|
offer: CreativeRefs.offer,
|
|
@@ -9720,16 +10165,16 @@ var ImageInput = z45.object({
|
|
|
9720
10165
|
headline: CreativeRefs.headline,
|
|
9721
10166
|
subheadline: CreativeRefs.subheadline
|
|
9722
10167
|
});
|
|
9723
|
-
var EditImageCreativeInput =
|
|
9724
|
-
taskId:
|
|
9725
|
-
variantIndex:
|
|
9726
|
-
instruction:
|
|
10168
|
+
var EditImageCreativeInput = z46.object({
|
|
10169
|
+
taskId: z46.string().uuid().describe("ID de la tarea de imagen ya finalizada."),
|
|
10170
|
+
variantIndex: z46.number().int().min(0).max(10).describe("\xCDndice de la variante a corregir (0 = primera imagen)."),
|
|
10171
|
+
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".')
|
|
9727
10172
|
});
|
|
9728
|
-
var LandingInput =
|
|
10173
|
+
var LandingInput = z46.object({
|
|
9729
10174
|
images: CreativeRefs.images,
|
|
9730
10175
|
language: CreativeRefs.language,
|
|
9731
10176
|
country: CreativeRefs.country,
|
|
9732
|
-
salesAngle:
|
|
10177
|
+
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.'),
|
|
9733
10178
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
9734
10179
|
price: CreativeRefs.price,
|
|
9735
10180
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
@@ -9780,7 +10225,6 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
9780
10225
|
});
|
|
9781
10226
|
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) => {
|
|
9782
10227
|
try {
|
|
9783
|
-
assertVideoPresetDurationCompatibility(input.videoPreset, input.durationSeconds);
|
|
9784
10228
|
const resolved = resolveVideoPreset(input.videoPreset);
|
|
9785
10229
|
const wireTier = resolveWireVideoTier(input.videoTier, input.durationSeconds);
|
|
9786
10230
|
const { videoTier: _publicTier, videoResolution: _vr, projectId, ...rest } = input;
|
|
@@ -9855,8 +10299,8 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
9855
10299
|
__name(registerCreativeTools, "registerCreativeTools");
|
|
9856
10300
|
|
|
9857
10301
|
// src/tools/creative-variants.ts
|
|
9858
|
-
import { z as
|
|
9859
|
-
var LandingSectionKeyEnum =
|
|
10302
|
+
import { z as z47 } from "zod";
|
|
10303
|
+
var LandingSectionKeyEnum = z47.enum([
|
|
9860
10304
|
"problem_hook",
|
|
9861
10305
|
"problem_cost",
|
|
9862
10306
|
"solution_mechanism",
|
|
@@ -9896,24 +10340,24 @@ var LandingSectionKeyEnum = z46.enum([
|
|
|
9896
10340
|
"lifestyle_fit",
|
|
9897
10341
|
"join_lifestyle"
|
|
9898
10342
|
]);
|
|
9899
|
-
var CreativeVariantKindEnum =
|
|
10343
|
+
var CreativeVariantKindEnum = z47.enum([
|
|
9900
10344
|
"video",
|
|
9901
10345
|
"image",
|
|
9902
10346
|
"landing"
|
|
9903
10347
|
]);
|
|
9904
|
-
var EstimateCreativeVariantInputBase =
|
|
9905
|
-
sourceTaskId:
|
|
10348
|
+
var EstimateCreativeVariantInputBase = z47.object({
|
|
10349
|
+
sourceTaskId: z47.string().uuid().describe("ID de la tarea original completada."),
|
|
9906
10350
|
kind: CreativeVariantKindEnum,
|
|
9907
10351
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Solo cambia el preset p\xFAblico; el servidor conserva el modelo/tier del task origen."),
|
|
9908
10352
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video. Resoluci\xF3n del nuevo render."),
|
|
9909
|
-
nVariants:
|
|
10353
|
+
nVariants: z47.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
9910
10354
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si ya conoces la secci\xF3n, evita cotizar la clasificaci\xF3n con IA."),
|
|
9911
|
-
instructions:
|
|
10355
|
+
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.")
|
|
9912
10356
|
}).strict();
|
|
9913
10357
|
var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
9914
10358
|
if (input.kind === "video" && input.nVariants !== void 0) {
|
|
9915
10359
|
ctx.addIssue({
|
|
9916
|
-
code:
|
|
10360
|
+
code: z47.ZodIssueCode.custom,
|
|
9917
10361
|
path: [
|
|
9918
10362
|
"nVariants"
|
|
9919
10363
|
],
|
|
@@ -9923,7 +10367,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9923
10367
|
if (input.kind !== "video") {
|
|
9924
10368
|
if (input.videoPreset !== void 0) {
|
|
9925
10369
|
ctx.addIssue({
|
|
9926
|
-
code:
|
|
10370
|
+
code: z47.ZodIssueCode.custom,
|
|
9927
10371
|
path: [
|
|
9928
10372
|
"videoPreset"
|
|
9929
10373
|
],
|
|
@@ -9932,7 +10376,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9932
10376
|
}
|
|
9933
10377
|
if (input.videoResolution !== void 0) {
|
|
9934
10378
|
ctx.addIssue({
|
|
9935
|
-
code:
|
|
10379
|
+
code: z47.ZodIssueCode.custom,
|
|
9936
10380
|
path: [
|
|
9937
10381
|
"videoResolution"
|
|
9938
10382
|
],
|
|
@@ -9942,7 +10386,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9942
10386
|
}
|
|
9943
10387
|
if (input.kind !== "image" && input.nVariants !== void 0) {
|
|
9944
10388
|
ctx.addIssue({
|
|
9945
|
-
code:
|
|
10389
|
+
code: z47.ZodIssueCode.custom,
|
|
9946
10390
|
path: [
|
|
9947
10391
|
"nVariants"
|
|
9948
10392
|
],
|
|
@@ -9952,7 +10396,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9952
10396
|
if (input.kind !== "landing") {
|
|
9953
10397
|
if (input.sectionKey !== void 0) {
|
|
9954
10398
|
ctx.addIssue({
|
|
9955
|
-
code:
|
|
10399
|
+
code: z47.ZodIssueCode.custom,
|
|
9956
10400
|
path: [
|
|
9957
10401
|
"sectionKey"
|
|
9958
10402
|
],
|
|
@@ -9961,7 +10405,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9961
10405
|
}
|
|
9962
10406
|
if (input.instructions !== void 0) {
|
|
9963
10407
|
ctx.addIssue({
|
|
9964
|
-
code:
|
|
10408
|
+
code: z47.ZodIssueCode.custom,
|
|
9965
10409
|
path: [
|
|
9966
10410
|
"instructions"
|
|
9967
10411
|
],
|
|
@@ -9970,31 +10414,31 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9970
10414
|
}
|
|
9971
10415
|
}
|
|
9972
10416
|
});
|
|
9973
|
-
var CreateCreativeVariantInputBase =
|
|
9974
|
-
sourceTaskId:
|
|
10417
|
+
var CreateCreativeVariantInputBase = z47.object({
|
|
10418
|
+
sourceTaskId: z47.string().uuid().describe("ID de la tarea original completada."),
|
|
9975
10419
|
kind: CreativeVariantKindEnum,
|
|
9976
|
-
confirm:
|
|
9977
|
-
clientRequestId:
|
|
9978
|
-
angle:
|
|
9979
|
-
buyerPersona:
|
|
9980
|
-
scriptNotes:
|
|
9981
|
-
creativeDirection:
|
|
10420
|
+
confirm: z47.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
|
|
10421
|
+
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."),
|
|
10422
|
+
angle: z47.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
|
|
10423
|
+
buyerPersona: z47.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
|
|
10424
|
+
scriptNotes: z47.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
|
|
10425
|
+
creativeDirection: z47.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
|
|
9982
10426
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Preset p\xFAblico; el servidor resuelve modelo/tier desde el task origen."),
|
|
9983
10427
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video."),
|
|
9984
|
-
generateAiMusic:
|
|
9985
|
-
nVariants:
|
|
10428
|
+
generateAiMusic: z47.boolean().optional().describe("Opcional para video."),
|
|
10429
|
+
nVariants: z47.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
9986
10430
|
imageStyle: ImageCreativeStyleEnum.optional().describe("Opcional para imagen."),
|
|
9987
10431
|
creativeArchetype: ImageCreativeArchetypeEnum.optional().describe("Opcional para imagen."),
|
|
9988
10432
|
visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe("Opcional para imagen."),
|
|
9989
10433
|
aspectRatio: ImageCreativeAspectRatioEnum.optional().describe("Opcional para imagen."),
|
|
9990
|
-
styleMix:
|
|
10434
|
+
styleMix: z47.boolean().optional().describe("Opcional para imagen."),
|
|
9991
10435
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si se omite, se clasifica con IA a partir de instructions."),
|
|
9992
|
-
instructions:
|
|
10436
|
+
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.")
|
|
9993
10437
|
}).strict();
|
|
9994
10438
|
var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
9995
10439
|
if ((input.kind === "video" || input.kind === "image") && !input.angle) {
|
|
9996
10440
|
ctx.addIssue({
|
|
9997
|
-
code:
|
|
10441
|
+
code: z47.ZodIssueCode.custom,
|
|
9998
10442
|
path: [
|
|
9999
10443
|
"angle"
|
|
10000
10444
|
],
|
|
@@ -10003,7 +10447,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
10003
10447
|
}
|
|
10004
10448
|
if (input.kind === "landing" && !input.sectionKey && !input.instructions) {
|
|
10005
10449
|
ctx.addIssue({
|
|
10006
|
-
code:
|
|
10450
|
+
code: z47.ZodIssueCode.custom,
|
|
10007
10451
|
path: [
|
|
10008
10452
|
"instructions"
|
|
10009
10453
|
],
|
|
@@ -10049,7 +10493,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
10049
10493
|
for (const field of fieldsByKind[input.kind]) {
|
|
10050
10494
|
if (input[field] !== void 0) {
|
|
10051
10495
|
ctx.addIssue({
|
|
10052
|
-
code:
|
|
10496
|
+
code: z47.ZodIssueCode.custom,
|
|
10053
10497
|
path: [
|
|
10054
10498
|
field
|
|
10055
10499
|
],
|
|
@@ -10166,27 +10610,27 @@ function registerCreativeVariantTools(server, api) {
|
|
|
10166
10610
|
__name(registerCreativeVariantTools, "registerCreativeVariantTools");
|
|
10167
10611
|
|
|
10168
10612
|
// src/tools/script-review.ts
|
|
10169
|
-
import { z as
|
|
10170
|
-
var GetVideoScriptInput =
|
|
10171
|
-
taskId:
|
|
10172
|
-
angleKey:
|
|
10613
|
+
import { z as z48 } from "zod";
|
|
10614
|
+
var GetVideoScriptInput = z48.object({
|
|
10615
|
+
taskId: z48.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
|
|
10616
|
+
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.')
|
|
10173
10617
|
}).strict();
|
|
10174
10618
|
var VIDEO_SCRIPT_APPROVAL_MAX_SCENES2 = 50;
|
|
10175
10619
|
var VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2 = 2e3;
|
|
10176
|
-
var ApproveSceneEdit =
|
|
10177
|
-
index:
|
|
10178
|
-
dialogue:
|
|
10620
|
+
var ApproveSceneEdit = z48.object({
|
|
10621
|
+
index: z48.number().int().nonnegative().describe("\xCDndice de la escena (0-based, mismo que devuelve get_video_script)."),
|
|
10622
|
+
dialogue: z48.string().min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2).refine((v) => v.trim().length > 0, {
|
|
10179
10623
|
message: "dialogue no puede ser s\xF3lo espacios"
|
|
10180
10624
|
}).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.")
|
|
10181
10625
|
}).strict();
|
|
10182
|
-
var ApproveVideoScriptInput =
|
|
10183
|
-
taskId:
|
|
10184
|
-
angleKey:
|
|
10185
|
-
scenes:
|
|
10626
|
+
var ApproveVideoScriptInput = z48.object({
|
|
10627
|
+
taskId: z48.string().min(1).describe("ID de la tarea."),
|
|
10628
|
+
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.'),
|
|
10629
|
+
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.")
|
|
10186
10630
|
}).strict();
|
|
10187
|
-
var RegenerateVideoScriptInput =
|
|
10188
|
-
taskId:
|
|
10189
|
-
angleKey:
|
|
10631
|
+
var RegenerateVideoScriptInput = z48.object({
|
|
10632
|
+
taskId: z48.string().min(1).describe("ID de la tarea."),
|
|
10633
|
+
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.'),
|
|
10190
10634
|
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).')
|
|
10191
10635
|
}).strict();
|
|
10192
10636
|
function registerScriptReviewTools(server, api) {
|
|
@@ -10226,9 +10670,9 @@ function registerScriptReviewTools(server, api) {
|
|
|
10226
10670
|
__name(registerScriptReviewTools, "registerScriptReviewTools");
|
|
10227
10671
|
|
|
10228
10672
|
// src/tools/connections.ts
|
|
10229
|
-
import { z as
|
|
10673
|
+
import { z as z49 } from "zod";
|
|
10230
10674
|
function registerConnectionTools(server, api) {
|
|
10231
|
-
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.",
|
|
10675
|
+
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 () => {
|
|
10232
10676
|
try {
|
|
10233
10677
|
const r = await api.getJson("/v1/shopify/shops");
|
|
10234
10678
|
return asText(r);
|
|
@@ -10236,10 +10680,10 @@ function registerConnectionTools(server, api) {
|
|
|
10236
10680
|
throw translateError(e);
|
|
10237
10681
|
}
|
|
10238
10682
|
});
|
|
10239
|
-
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.',
|
|
10240
|
-
shopDomain:
|
|
10241
|
-
query:
|
|
10242
|
-
taskId:
|
|
10683
|
+
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({
|
|
10684
|
+
shopDomain: z49.string().min(3).max(120),
|
|
10685
|
+
query: z49.string().trim().max(120).optional(),
|
|
10686
|
+
taskId: z49.string().uuid().optional()
|
|
10243
10687
|
}).shape, async ({ shopDomain, query, taskId }) => {
|
|
10244
10688
|
try {
|
|
10245
10689
|
const params = new URLSearchParams({
|
|
@@ -10253,7 +10697,7 @@ function registerConnectionTools(server, api) {
|
|
|
10253
10697
|
throw translateError(e);
|
|
10254
10698
|
}
|
|
10255
10699
|
});
|
|
10256
|
-
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.",
|
|
10700
|
+
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 () => {
|
|
10257
10701
|
try {
|
|
10258
10702
|
const r = await api.getJson("/v1/facebook/ad-accounts");
|
|
10259
10703
|
return asText(r);
|
|
@@ -10261,7 +10705,15 @@ function registerConnectionTools(server, api) {
|
|
|
10261
10705
|
throw translateError(e);
|
|
10262
10706
|
}
|
|
10263
10707
|
});
|
|
10264
|
-
server.tool("
|
|
10708
|
+
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 () => {
|
|
10709
|
+
try {
|
|
10710
|
+
const r = await api.getJson("/v1/facebook/destinations");
|
|
10711
|
+
return asText(r);
|
|
10712
|
+
} catch (e) {
|
|
10713
|
+
throw translateError(e);
|
|
10714
|
+
}
|
|
10715
|
+
});
|
|
10716
|
+
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 () => {
|
|
10265
10717
|
try {
|
|
10266
10718
|
const r = await api.getJson("/v1/tiktok/advertisers");
|
|
10267
10719
|
return asText(r);
|
|
@@ -10273,9 +10725,9 @@ function registerConnectionTools(server, api) {
|
|
|
10273
10725
|
__name(registerConnectionTools, "registerConnectionTools");
|
|
10274
10726
|
|
|
10275
10727
|
// src/tools/actors.ts
|
|
10276
|
-
import { z as
|
|
10728
|
+
import { z as z50 } from "zod";
|
|
10277
10729
|
function registerActorTools(server, api) {
|
|
10278
|
-
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.",
|
|
10730
|
+
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 () => {
|
|
10279
10731
|
try {
|
|
10280
10732
|
const rows = await api.getJson("/v1/actors");
|
|
10281
10733
|
const actors = (Array.isArray(rows) ? rows : []).filter((actor) => actor.status === "ready").map(({ id, name, description, gender, voiceId }) => ({
|
|
@@ -10296,105 +10748,116 @@ function registerActorTools(server, api) {
|
|
|
10296
10748
|
__name(registerActorTools, "registerActorTools");
|
|
10297
10749
|
|
|
10298
10750
|
// src/tools/publish.ts
|
|
10299
|
-
import { z as
|
|
10300
|
-
var ShopifyInput =
|
|
10301
|
-
taskId:
|
|
10302
|
-
shopDomain:
|
|
10751
|
+
import { z as z51 } from "zod";
|
|
10752
|
+
var ShopifyInput = z51.object({
|
|
10753
|
+
taskId: z51.string().min(1),
|
|
10754
|
+
shopDomain: z51.string().min(1),
|
|
10303
10755
|
/** Override: number of landing images to include. Default = ALL images the
|
|
10304
10756
|
* task generated (the MCP fetches /v1/results first to count). Bounded
|
|
10305
10757
|
* generously here; the backend validates each index against
|
|
10306
10758
|
* landing.images.length and rejects out-of-range. */
|
|
10307
|
-
numLandingImages:
|
|
10308
|
-
variantIndex:
|
|
10759
|
+
numLandingImages: z51.number().int().min(1).max(20).optional(),
|
|
10760
|
+
variantIndex: z51.number().int().min(0).optional(),
|
|
10309
10761
|
/** ASK THE USER before setting: "¿Tu producto tiene variantes (talla, color, etc.)?".
|
|
10310
10762
|
* When true, the landing renders variant-picker pills; when false, it ships
|
|
10311
10763
|
* a single-option product page. Defaults to false. */
|
|
10312
|
-
showVariantPicker:
|
|
10764
|
+
showVariantPicker: z51.boolean().optional(),
|
|
10313
10765
|
/** Default `auto` updates the product previously published for this task and
|
|
10314
10766
|
* shop. Use `existing` with targetProductId to choose another product, or
|
|
10315
10767
|
* `new` only when the user explicitly wants a duplicate/new listing. */
|
|
10316
|
-
publicationMode:
|
|
10768
|
+
publicationMode: z51.enum([
|
|
10317
10769
|
"auto",
|
|
10318
10770
|
"existing",
|
|
10319
10771
|
"new"
|
|
10320
10772
|
]).optional(),
|
|
10321
10773
|
/** Shopify product GID, numeric id, or admin product URL. Required when
|
|
10322
10774
|
* publicationMode is `existing`. */
|
|
10323
|
-
targetProductId:
|
|
10775
|
+
targetProductId: z51.string().trim().min(1).max(300).optional()
|
|
10324
10776
|
});
|
|
10325
|
-
var McpCampaignConfig =
|
|
10777
|
+
var McpCampaignConfig = z51.object({
|
|
10326
10778
|
/** ALWAYS in US dollars, whatever the ad account's currency is. ProductMaker
|
|
10327
10779
|
* converts to the account currency server-side. Do NOT pre-convert: on a COP
|
|
10328
10780
|
* account the user's "60.000 al día" is `dailyBudgetUsd: 15`, not 60000. */
|
|
10329
|
-
dailyBudgetUsd:
|
|
10781
|
+
dailyBudgetUsd: z51.number().positive().optional(),
|
|
10330
10782
|
/** ISO-3166-1 alpha-2 country codes. Meta accepts max 1 (single-country
|
|
10331
10783
|
* ad set). TikTok takes only the first entry. */
|
|
10332
|
-
targetCountries:
|
|
10333
|
-
campaignName:
|
|
10784
|
+
targetCountries: z51.array(z51.string().length(2)).max(1).optional(),
|
|
10785
|
+
campaignName: z51.string().trim().min(1).max(100).optional()
|
|
10334
10786
|
});
|
|
10335
10787
|
var McpAdAccountId = MetaAdAccountId.describe('Meta ad account id. Accepts "act_1234567890" or the bare numeric id "1234567890".');
|
|
10336
|
-
var MetaInput =
|
|
10337
|
-
taskId:
|
|
10788
|
+
var MetaInput = z51.object({
|
|
10789
|
+
taskId: z51.string().min(1),
|
|
10790
|
+
/** Exact Meta Business portfolio selected from `list_meta_destinations`. */
|
|
10791
|
+
portfolioId: z51.string().trim().min(1),
|
|
10792
|
+
/** Exact Facebook Page selected from `list_meta_destinations`. */
|
|
10793
|
+
pageId: z51.string().trim().min(1),
|
|
10338
10794
|
adAccountId: McpAdAccountId,
|
|
10339
|
-
pixelId:
|
|
10340
|
-
landingUrl:
|
|
10795
|
+
pixelId: z51.string().min(1).optional(),
|
|
10796
|
+
landingUrl: z51.string().url().optional(),
|
|
10341
10797
|
/** Select which angles to publish by 0-based index. Omit to publish all
|
|
10342
10798
|
* successful angles. Example: a task with 3 angles → `[0,2]` publishes #0 + #2. */
|
|
10343
|
-
angleIndexes:
|
|
10799
|
+
angleIndexes: z51.array(z51.number().int().nonnegative()).optional(),
|
|
10344
10800
|
campaignConfig: McpCampaignConfig.optional(),
|
|
10345
10801
|
/** Override the ad's primary text (body copy). If omitted, ProductMaker
|
|
10346
10802
|
* uses the auto-generated copy attached to the task. Capped at 500 chars
|
|
10347
10803
|
* (Meta's primary-text limit). */
|
|
10348
|
-
primaryText:
|
|
10804
|
+
primaryText: z51.string().trim().min(1).max(500).optional(),
|
|
10349
10805
|
/** Optional WhatsApp number. When set, the ad button opens a WhatsApp chat
|
|
10350
10806
|
* (wa.me) instead of a landing page. */
|
|
10351
|
-
phoneNumber:
|
|
10807
|
+
phoneNumber: z51.string().min(8).max(32).optional()
|
|
10352
10808
|
});
|
|
10353
|
-
var GroupedTaskSource =
|
|
10354
|
-
type:
|
|
10355
|
-
taskId:
|
|
10356
|
-
angleIndex:
|
|
10357
|
-
videoVariant:
|
|
10809
|
+
var GroupedTaskSource = z51.object({
|
|
10810
|
+
type: z51.literal("task"),
|
|
10811
|
+
taskId: z51.string().min(1),
|
|
10812
|
+
angleIndex: z51.number().int().min(0).optional(),
|
|
10813
|
+
videoVariant: z51.enum([
|
|
10358
10814
|
"with_subtitles",
|
|
10359
10815
|
"without_subtitles"
|
|
10360
10816
|
]).optional(),
|
|
10361
|
-
imageVariantIndex:
|
|
10362
|
-
});
|
|
10363
|
-
var GroupedMediaSource =
|
|
10364
|
-
type:
|
|
10365
|
-
url:
|
|
10366
|
-
path:
|
|
10367
|
-
thumbnailUrl:
|
|
10368
|
-
thumbnailPath:
|
|
10817
|
+
imageVariantIndex: z51.number().int().min(0).optional()
|
|
10818
|
+
});
|
|
10819
|
+
var GroupedMediaSource = z51.object({
|
|
10820
|
+
type: z51.literal("media"),
|
|
10821
|
+
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."),
|
|
10822
|
+
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".'),
|
|
10823
|
+
thumbnailUrl: z51.string().url().optional(),
|
|
10824
|
+
thumbnailPath: z51.string().min(1).optional()
|
|
10369
10825
|
}).refine((value) => Boolean(value.url) !== Boolean(value.path), {
|
|
10370
10826
|
message: "media source needs exactly one of `url` or `path`"
|
|
10371
10827
|
}).refine((value) => !(value.thumbnailUrl && value.thumbnailPath), {
|
|
10372
10828
|
message: "media source needs at most one of `thumbnailUrl` or `thumbnailPath`"
|
|
10373
10829
|
});
|
|
10374
|
-
var
|
|
10375
|
-
type:
|
|
10830
|
+
var GroupedStagedMediaSource = z51.object({
|
|
10831
|
+
type: z51.literal("staged"),
|
|
10832
|
+
/** Returned by check_meta_media_upload after the user selects a local file. */
|
|
10833
|
+
stagingAssetId: z51.string().uuid(),
|
|
10834
|
+
thumbnailAssetId: z51.string().uuid().optional()
|
|
10835
|
+
});
|
|
10836
|
+
var GroupedCreative = z51.object({
|
|
10837
|
+
type: z51.enum([
|
|
10376
10838
|
"image",
|
|
10377
10839
|
"video"
|
|
10378
10840
|
]),
|
|
10379
|
-
source:
|
|
10841
|
+
source: z51.union([
|
|
10380
10842
|
GroupedTaskSource,
|
|
10381
|
-
GroupedMediaSource
|
|
10843
|
+
GroupedMediaSource,
|
|
10844
|
+
GroupedStagedMediaSource
|
|
10382
10845
|
]),
|
|
10383
|
-
title:
|
|
10384
|
-
description:
|
|
10846
|
+
title: z51.string().max(120).optional(),
|
|
10847
|
+
description: z51.string().max(500).optional(),
|
|
10385
10848
|
cta: MetaCtaType.optional(),
|
|
10386
|
-
creativeId:
|
|
10849
|
+
creativeId: z51.string().min(1).optional()
|
|
10387
10850
|
});
|
|
10388
|
-
var GroupedCampaignConfig =
|
|
10851
|
+
var GroupedCampaignConfig = z51.object({
|
|
10389
10852
|
campaignType: MetaCampaignType.default("sales"),
|
|
10390
10853
|
optimizationMode: MetaOptimizationMode.default("advantage_plus"),
|
|
10391
10854
|
budgetMode: MetaBudgetMode.default("campaign"),
|
|
10392
|
-
campaignDailyBudget:
|
|
10393
|
-
adSets:
|
|
10394
|
-
tempId:
|
|
10395
|
-
name:
|
|
10396
|
-
dailyBudget:
|
|
10397
|
-
creativeIds:
|
|
10855
|
+
campaignDailyBudget: z51.number().int().positive().optional(),
|
|
10856
|
+
adSets: z51.array(z51.object({
|
|
10857
|
+
tempId: z51.string().min(1).max(80),
|
|
10858
|
+
name: z51.string().min(1).max(100),
|
|
10859
|
+
dailyBudget: z51.number().int().positive().optional(),
|
|
10860
|
+
creativeIds: z51.array(z51.string().min(1)).min(1)
|
|
10398
10861
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_AD_SETS),
|
|
10399
10862
|
/**
|
|
10400
10863
|
* Valid values are 'landing' and 'whatsapp' — there is no 'website'. This
|
|
@@ -10402,22 +10865,22 @@ var GroupedCampaignConfig = z50.object({
|
|
|
10402
10865
|
* opaque `invalid_literal` and made the WhatsApp destination — supported by
|
|
10403
10866
|
* the API since #849 — unreachable from this tool.
|
|
10404
10867
|
*/
|
|
10405
|
-
destination:
|
|
10406
|
-
|
|
10407
|
-
type:
|
|
10408
|
-
landingUrl:
|
|
10868
|
+
destination: z51.discriminatedUnion("type", [
|
|
10869
|
+
z51.object({
|
|
10870
|
+
type: z51.literal("landing"),
|
|
10871
|
+
landingUrl: z51.string().url()
|
|
10409
10872
|
}),
|
|
10410
|
-
|
|
10411
|
-
type:
|
|
10412
|
-
phoneNumber:
|
|
10873
|
+
z51.object({
|
|
10874
|
+
type: z51.literal("whatsapp"),
|
|
10875
|
+
phoneNumber: z51.string().min(8).max(32)
|
|
10413
10876
|
})
|
|
10414
10877
|
]),
|
|
10415
|
-
saveAsDefault:
|
|
10878
|
+
saveAsDefault: z51.boolean().default(false),
|
|
10416
10879
|
budgetPolicy: MetaBudgetPolicy.optional()
|
|
10417
10880
|
}).superRefine((value, ctx) => {
|
|
10418
10881
|
if (value.budgetMode === "campaign" && value.campaignDailyBudget === void 0) {
|
|
10419
10882
|
ctx.addIssue({
|
|
10420
|
-
code:
|
|
10883
|
+
code: z51.ZodIssueCode.custom,
|
|
10421
10884
|
path: [
|
|
10422
10885
|
"campaignDailyBudget"
|
|
10423
10886
|
],
|
|
@@ -10428,7 +10891,7 @@ var GroupedCampaignConfig = z50.object({
|
|
|
10428
10891
|
value.adSets.forEach((adSet, index) => {
|
|
10429
10892
|
if (adSet.dailyBudget === void 0) {
|
|
10430
10893
|
ctx.addIssue({
|
|
10431
|
-
code:
|
|
10894
|
+
code: z51.ZodIssueCode.custom,
|
|
10432
10895
|
path: [
|
|
10433
10896
|
"adSets",
|
|
10434
10897
|
index,
|
|
@@ -10440,22 +10903,26 @@ var GroupedCampaignConfig = z50.object({
|
|
|
10440
10903
|
});
|
|
10441
10904
|
}
|
|
10442
10905
|
});
|
|
10443
|
-
var PublishCreativesToMetaInputBase =
|
|
10444
|
-
creatives:
|
|
10906
|
+
var PublishCreativesToMetaInputBase = z51.object({
|
|
10907
|
+
creatives: z51.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
10908
|
+
/** Exact Meta Business portfolio selected from `list_meta_destinations`. */
|
|
10909
|
+
portfolioId: z51.string().trim().min(1),
|
|
10910
|
+
/** Exact Facebook Page selected from `list_meta_destinations`. */
|
|
10911
|
+
pageId: z51.string().trim().min(1),
|
|
10445
10912
|
adAccountId: McpAdAccountId,
|
|
10446
|
-
pixelId:
|
|
10913
|
+
pixelId: z51.string().min(1).optional(),
|
|
10447
10914
|
/** Required unless `campaignConfig.destination` is whatsapp — see the
|
|
10448
10915
|
* superRefine below. Mirrors the API's `UploadToMetaRequest`. */
|
|
10449
|
-
landingUrl:
|
|
10450
|
-
targetCountry:
|
|
10451
|
-
campaignName:
|
|
10916
|
+
landingUrl: z51.string().url().optional(),
|
|
10917
|
+
targetCountry: z51.string().length(2),
|
|
10918
|
+
campaignName: z51.string().trim().min(1).max(100),
|
|
10452
10919
|
campaignConfig: GroupedCampaignConfig.optional()
|
|
10453
10920
|
});
|
|
10454
10921
|
var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((value, ctx) => {
|
|
10455
10922
|
const destination = value.campaignConfig?.destination;
|
|
10456
10923
|
if (destination?.type !== "whatsapp" && !value.landingUrl) {
|
|
10457
10924
|
ctx.addIssue({
|
|
10458
|
-
code:
|
|
10925
|
+
code: z51.ZodIssueCode.custom,
|
|
10459
10926
|
path: [
|
|
10460
10927
|
"landingUrl"
|
|
10461
10928
|
],
|
|
@@ -10464,7 +10931,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
10464
10931
|
}
|
|
10465
10932
|
if (destination?.type === "landing" && destination.landingUrl !== value.landingUrl) {
|
|
10466
10933
|
ctx.addIssue({
|
|
10467
|
-
code:
|
|
10934
|
+
code: z51.ZodIssueCode.custom,
|
|
10468
10935
|
path: [
|
|
10469
10936
|
"campaignConfig",
|
|
10470
10937
|
"destination",
|
|
@@ -10474,46 +10941,50 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
10474
10941
|
});
|
|
10475
10942
|
}
|
|
10476
10943
|
});
|
|
10477
|
-
var PresignUploadResponseSchema =
|
|
10478
|
-
uploadUrl:
|
|
10944
|
+
var PresignUploadResponseSchema = z51.object({
|
|
10945
|
+
uploadUrl: z51.string().url().refine((value) => {
|
|
10479
10946
|
const url = new URL(value);
|
|
10480
10947
|
return !url.username && !url.password && url.protocol === "https:";
|
|
10481
10948
|
}, "presigned upload URL must be HTTPS without embedded credentials"),
|
|
10482
|
-
publicUrl:
|
|
10949
|
+
publicUrl: z51.string().url().refine((value) => {
|
|
10483
10950
|
const url = new URL(value);
|
|
10484
10951
|
return !url.username && !url.password;
|
|
10485
|
-
}, "public media URL must not contain embedded credentials")
|
|
10952
|
+
}, "public media URL must not contain embedded credentials"),
|
|
10953
|
+
stagingAssetId: z51.string().uuid()
|
|
10486
10954
|
});
|
|
10487
|
-
async function uploadResolvedMedia(api, media) {
|
|
10488
|
-
if (media.url) return media.url;
|
|
10955
|
+
async function uploadResolvedMedia(api, media, assetRole) {
|
|
10489
10956
|
if (!media.buffer) throw new ResolveMediaError("INVALID_INPUT", "resolved media has no bytes");
|
|
10490
10957
|
const presigned = PresignUploadResponseSchema.parse(await api.postJson("/v1/ads/presign-upload", {
|
|
10491
10958
|
contentType: media.mimetype,
|
|
10492
10959
|
fileName: media.filename,
|
|
10493
|
-
fileSize: media.buffer.byteLength
|
|
10960
|
+
fileSize: media.buffer.byteLength,
|
|
10961
|
+
purpose: "meta_staging",
|
|
10962
|
+
assetRole
|
|
10494
10963
|
}));
|
|
10495
10964
|
await api.putBytes(presigned.uploadUrl, media.buffer, media.mimetype);
|
|
10496
|
-
return
|
|
10965
|
+
return {
|
|
10966
|
+
stagingAssetId: presigned.stagingAssetId
|
|
10967
|
+
};
|
|
10497
10968
|
}
|
|
10498
10969
|
__name(uploadResolvedMedia, "uploadResolvedMedia");
|
|
10499
|
-
async function resolveAndUploadMedia(api, ref, expectedKind, transport) {
|
|
10970
|
+
async function resolveAndUploadMedia(api, ref, expectedKind, assetRole, transport) {
|
|
10500
10971
|
const media = await resolveMediaRef(ref, {
|
|
10501
10972
|
transport,
|
|
10502
10973
|
expectedKind
|
|
10503
10974
|
});
|
|
10504
|
-
return uploadResolvedMedia(api, media);
|
|
10975
|
+
return uploadResolvedMedia(api, media, assetRole);
|
|
10505
10976
|
}
|
|
10506
10977
|
__name(resolveAndUploadMedia, "resolveAndUploadMedia");
|
|
10507
|
-
var TikTokInput =
|
|
10508
|
-
taskId:
|
|
10509
|
-
advertiserId:
|
|
10510
|
-
pixelId:
|
|
10511
|
-
landingUrl:
|
|
10512
|
-
angleIndexes:
|
|
10978
|
+
var TikTokInput = z51.object({
|
|
10979
|
+
taskId: z51.string().min(1),
|
|
10980
|
+
advertiserId: z51.string().min(1),
|
|
10981
|
+
pixelId: z51.string().min(1).optional(),
|
|
10982
|
+
landingUrl: z51.string().url().optional(),
|
|
10983
|
+
angleIndexes: z51.array(z51.number().int().nonnegative()).optional(),
|
|
10513
10984
|
campaignConfig: McpCampaignConfig.optional(),
|
|
10514
10985
|
/** Override the ad's description (TikTok's body copy). If omitted,
|
|
10515
10986
|
* ProductMaker uses the auto-generated copy attached to the task. */
|
|
10516
|
-
primaryText:
|
|
10987
|
+
primaryText: z51.string().trim().min(1).max(500).optional()
|
|
10517
10988
|
});
|
|
10518
10989
|
function registerPublishTools(server, api, opts = {}) {
|
|
10519
10990
|
const transport = opts.transport ?? "http";
|
|
@@ -10552,7 +11023,7 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
10552
11023
|
throw translateError(e);
|
|
10553
11024
|
}
|
|
10554
11025
|
});
|
|
10555
|
-
server.tool("publish_to_meta", "Crea una campa\xF1a de Meta Ads (Facebook/Instagram) desde UNA sola tarea.
|
|
11026
|
+
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 }) => {
|
|
10556
11027
|
try {
|
|
10557
11028
|
const body = {
|
|
10558
11029
|
...rest
|
|
@@ -10580,7 +11051,7 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
10580
11051
|
throw translateError(e);
|
|
10581
11052
|
}
|
|
10582
11053
|
});
|
|
10583
|
-
server.tool("publish_creatives_to_meta", "Crea UNA campa\xF1a de Meta Ads
|
|
11054
|
+
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) => {
|
|
10584
11055
|
try {
|
|
10585
11056
|
const parsed = PublishCreativesToMetaInput.safeParse(rawInput);
|
|
10586
11057
|
if (!parsed.success) throw invalidInputFromZod(parsed.error);
|
|
@@ -10606,25 +11077,44 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
10606
11077
|
});
|
|
10607
11078
|
continue;
|
|
10608
11079
|
}
|
|
10609
|
-
|
|
11080
|
+
if (creative.source.type === "staged") {
|
|
11081
|
+
creatives.push({
|
|
11082
|
+
type: creative.type,
|
|
11083
|
+
source: {
|
|
11084
|
+
type: "media",
|
|
11085
|
+
mediaAssetId: creative.source.stagingAssetId,
|
|
11086
|
+
...creative.source.thumbnailAssetId ? {
|
|
11087
|
+
thumbnailAssetId: creative.source.thumbnailAssetId
|
|
11088
|
+
} : {}
|
|
11089
|
+
},
|
|
11090
|
+
title: creative.title,
|
|
11091
|
+
description: creative.description,
|
|
11092
|
+
cta: creative.cta,
|
|
11093
|
+
creativeId
|
|
11094
|
+
});
|
|
11095
|
+
continue;
|
|
11096
|
+
}
|
|
11097
|
+
const stagedMedia = await resolveAndUploadMedia(api, {
|
|
10610
11098
|
url: creative.source.url,
|
|
10611
11099
|
path: creative.source.path
|
|
10612
|
-
}, creative.type, transport);
|
|
10613
|
-
let
|
|
11100
|
+
}, creative.type, "media", transport);
|
|
11101
|
+
let stagedThumbnail;
|
|
10614
11102
|
if (creative.type === "video") {
|
|
10615
11103
|
if (creative.source.thumbnailUrl || creative.source.thumbnailPath) {
|
|
10616
|
-
|
|
11104
|
+
stagedThumbnail = await resolveAndUploadMedia(api, {
|
|
10617
11105
|
url: creative.source.thumbnailUrl,
|
|
10618
11106
|
path: creative.source.thumbnailPath
|
|
10619
|
-
}, "image", transport);
|
|
11107
|
+
}, "image", "thumbnail", transport);
|
|
10620
11108
|
}
|
|
10621
11109
|
}
|
|
10622
11110
|
creatives.push({
|
|
10623
11111
|
type: creative.type,
|
|
10624
11112
|
source: {
|
|
10625
11113
|
type: "media",
|
|
10626
|
-
|
|
10627
|
-
|
|
11114
|
+
mediaAssetId: stagedMedia.stagingAssetId,
|
|
11115
|
+
...stagedThumbnail ? {
|
|
11116
|
+
thumbnailAssetId: stagedThumbnail.stagingAssetId
|
|
11117
|
+
} : {}
|
|
10628
11118
|
},
|
|
10629
11119
|
title: creative.title,
|
|
10630
11120
|
description: creative.description,
|
|
@@ -10634,6 +11124,8 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
10634
11124
|
}
|
|
10635
11125
|
const r = await api.postJson("/v1/facebook/campaigns/from-tasks", {
|
|
10636
11126
|
creatives,
|
|
11127
|
+
portfolioId: input.portfolioId,
|
|
11128
|
+
pageId: input.pageId,
|
|
10637
11129
|
adAccountId: input.adAccountId,
|
|
10638
11130
|
pixelId: input.pixelId,
|
|
10639
11131
|
landingUrl: input.landingUrl,
|
|
@@ -10723,14 +11215,146 @@ function toTikTokCampaignConfig(cfg) {
|
|
|
10723
11215
|
}
|
|
10724
11216
|
__name(toTikTokCampaignConfig, "toTikTokCampaignConfig");
|
|
10725
11217
|
|
|
11218
|
+
// src/tools/meta-upload.ts
|
|
11219
|
+
import { z as z52 } from "zod";
|
|
11220
|
+
var MetaUploadSessionInputBase = z52.object({
|
|
11221
|
+
mediaKind: z52.enum([
|
|
11222
|
+
"image",
|
|
11223
|
+
"video",
|
|
11224
|
+
"mixed"
|
|
11225
|
+
]).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."),
|
|
11226
|
+
assetRole: z52.enum([
|
|
11227
|
+
"media",
|
|
11228
|
+
"thumbnail"
|
|
11229
|
+
]).default("media").describe("Use media for the creative itself; thumbnail only for an image thumbnail.")
|
|
11230
|
+
});
|
|
11231
|
+
var OpenAiFileReference = z52.object({
|
|
11232
|
+
download_url: z52.string().url(),
|
|
11233
|
+
file_id: z52.string().min(1),
|
|
11234
|
+
mime_type: z52.string().min(1).optional(),
|
|
11235
|
+
file_name: z52.string().min(1).optional()
|
|
11236
|
+
}).strict();
|
|
11237
|
+
var MetaUploadSessionToolInputShape = {
|
|
11238
|
+
...MetaUploadSessionInputBase.shape,
|
|
11239
|
+
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.")
|
|
11240
|
+
};
|
|
11241
|
+
var MetaUploadSessionToolInput = z52.object(MetaUploadSessionToolInputShape).superRefine((value, ctx) => {
|
|
11242
|
+
if (value.assetRole === "thumbnail" && value.mediaKind !== "image") {
|
|
11243
|
+
ctx.addIssue({
|
|
11244
|
+
code: z52.ZodIssueCode.custom,
|
|
11245
|
+
path: [
|
|
11246
|
+
"mediaKind"
|
|
11247
|
+
],
|
|
11248
|
+
message: "Meta thumbnails must be images"
|
|
11249
|
+
});
|
|
11250
|
+
}
|
|
11251
|
+
});
|
|
11252
|
+
var MetaUploadSessionResponse2 = z52.object({
|
|
11253
|
+
sessionId: z52.string().uuid(),
|
|
11254
|
+
uploadPageUrl: z52.string().url(),
|
|
11255
|
+
mediaKind: z52.enum([
|
|
11256
|
+
"image",
|
|
11257
|
+
"video",
|
|
11258
|
+
"mixed"
|
|
11259
|
+
]),
|
|
11260
|
+
assetRole: z52.enum([
|
|
11261
|
+
"media",
|
|
11262
|
+
"thumbnail"
|
|
11263
|
+
]),
|
|
11264
|
+
maxFiles: z52.number().int().positive(),
|
|
11265
|
+
expiresAt: z52.string().datetime()
|
|
11266
|
+
});
|
|
11267
|
+
var MetaUploadStatusInput = z52.object({
|
|
11268
|
+
sessionId: z52.string().uuid().describe("The sessionId returned by request_meta_media_upload.")
|
|
11269
|
+
});
|
|
11270
|
+
var MetaUploadStatusResponse = z52.object({
|
|
11271
|
+
sessionId: z52.string().uuid(),
|
|
11272
|
+
status: z52.enum([
|
|
11273
|
+
"pending",
|
|
11274
|
+
"uploaded"
|
|
11275
|
+
]),
|
|
11276
|
+
assets: z52.array(z52.object({
|
|
11277
|
+
stagingAssetId: z52.string().uuid(),
|
|
11278
|
+
mediaKind: z52.enum([
|
|
11279
|
+
"image",
|
|
11280
|
+
"video"
|
|
11281
|
+
])
|
|
11282
|
+
})).max(10).default([]),
|
|
11283
|
+
stagingAssetId: z52.string().uuid().optional()
|
|
11284
|
+
});
|
|
11285
|
+
function uploadSessionText(result) {
|
|
11286
|
+
const mediaLabel = result.mediaKind === "mixed" ? "archivos" : result.mediaKind === "video" ? "video" : "imagen";
|
|
11287
|
+
return {
|
|
11288
|
+
content: [
|
|
11289
|
+
{
|
|
11290
|
+
type: "text",
|
|
11291
|
+
text: [
|
|
11292
|
+
`### Sube tu ${mediaLabel} a ProductMaker`,
|
|
11293
|
+
`[Abrir carga segura](${result.uploadPageUrl})`,
|
|
11294
|
+
`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.`,
|
|
11295
|
+
`sessionId: \`${result.sessionId}\``,
|
|
11296
|
+
`Este enlace expira: ${result.expiresAt}`
|
|
11297
|
+
].join("\n\n")
|
|
11298
|
+
}
|
|
11299
|
+
]
|
|
11300
|
+
};
|
|
11301
|
+
}
|
|
11302
|
+
__name(uploadSessionText, "uploadSessionText");
|
|
11303
|
+
function registerMetaUploadTools(server, api) {
|
|
11304
|
+
server.registerTool("request_meta_media_upload", {
|
|
11305
|
+
title: "Solicitar carga segura de Meta",
|
|
11306
|
+
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.",
|
|
11307
|
+
inputSchema: MetaUploadSessionToolInputShape,
|
|
11308
|
+
outputSchema: MetaUploadSessionResponse2,
|
|
11309
|
+
_meta: {
|
|
11310
|
+
ui: {
|
|
11311
|
+
resourceUri: META_UPLOAD_APP_RESOURCE_URI,
|
|
11312
|
+
visibility: [
|
|
11313
|
+
"model",
|
|
11314
|
+
"app"
|
|
11315
|
+
]
|
|
11316
|
+
},
|
|
11317
|
+
"openai/outputTemplate": META_UPLOAD_APP_RESOURCE_URI,
|
|
11318
|
+
"openai/widgetAccessible": true,
|
|
11319
|
+
"openai/fileParams": [
|
|
11320
|
+
"files"
|
|
11321
|
+
],
|
|
11322
|
+
"openai/toolInvocation/invoking": "Preparando carga segura\u2026",
|
|
11323
|
+
"openai/toolInvocation/invoked": "Carga segura lista"
|
|
11324
|
+
}
|
|
11325
|
+
}, async (rawInput) => {
|
|
11326
|
+
try {
|
|
11327
|
+
const input = MetaUploadSessionToolInput.parse(rawInput);
|
|
11328
|
+
const { files: _files, ...sessionInput } = input;
|
|
11329
|
+
const result = MetaUploadSessionResponse2.parse(await api.postJson("/v1/ads/meta-upload-sessions", sessionInput));
|
|
11330
|
+
return {
|
|
11331
|
+
structuredContent: result,
|
|
11332
|
+
...uploadSessionText(result)
|
|
11333
|
+
};
|
|
11334
|
+
} catch (error) {
|
|
11335
|
+
throw translateError(error);
|
|
11336
|
+
}
|
|
11337
|
+
});
|
|
11338
|
+
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) => {
|
|
11339
|
+
try {
|
|
11340
|
+
const input = MetaUploadStatusInput.parse(rawInput);
|
|
11341
|
+
const result = MetaUploadStatusResponse.parse(await api.getJson(`/v1/ads/meta-upload-sessions/${encodeURIComponent(input.sessionId)}`));
|
|
11342
|
+
return asText(result);
|
|
11343
|
+
} catch (error) {
|
|
11344
|
+
throw translateError(error);
|
|
11345
|
+
}
|
|
11346
|
+
});
|
|
11347
|
+
}
|
|
11348
|
+
__name(registerMetaUploadTools, "registerMetaUploadTools");
|
|
11349
|
+
|
|
10726
11350
|
// src/tools/campaigns.ts
|
|
10727
|
-
import { z as
|
|
10728
|
-
var MetaStatusInput =
|
|
10729
|
-
campaignId:
|
|
11351
|
+
import { z as z53 } from "zod";
|
|
11352
|
+
var MetaStatusInput = z53.object({
|
|
11353
|
+
campaignId: z53.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
|
|
10730
11354
|
});
|
|
10731
|
-
var TikTokStatusInput =
|
|
10732
|
-
campaignId:
|
|
10733
|
-
advertiserId:
|
|
11355
|
+
var TikTokStatusInput = z53.object({
|
|
11356
|
+
campaignId: z53.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
|
|
11357
|
+
advertiserId: z53.string().min(1).describe("The same advertiserId used to publish the campaign.")
|
|
10734
11358
|
});
|
|
10735
11359
|
function registerCampaignTools(server, api) {
|
|
10736
11360
|
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) => {
|
|
@@ -10827,10 +11451,10 @@ function renderTikTokStatusMarkdown(r) {
|
|
|
10827
11451
|
__name(renderTikTokStatusMarkdown, "renderTikTokStatusMarkdown");
|
|
10828
11452
|
|
|
10829
11453
|
// ../../packages/product-economics/src/product-costing.ts
|
|
10830
|
-
import { z as
|
|
11454
|
+
import { z as z55 } from "zod";
|
|
10831
11455
|
|
|
10832
11456
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
10833
|
-
import { z as
|
|
11457
|
+
import { z as z54 } from "zod";
|
|
10834
11458
|
|
|
10835
11459
|
// ../../packages/product-economics/src/product-costing-shipping-references.json
|
|
10836
11460
|
var product_costing_shipping_references_default = {
|
|
@@ -10894,21 +11518,21 @@ var product_costing_shipping_references_default = {
|
|
|
10894
11518
|
};
|
|
10895
11519
|
|
|
10896
11520
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
10897
|
-
var ShippingReferenceSchema =
|
|
10898
|
-
reference:
|
|
10899
|
-
min:
|
|
10900
|
-
max:
|
|
10901
|
-
updatedAt:
|
|
10902
|
-
confidence:
|
|
11521
|
+
var ShippingReferenceSchema = z54.object({
|
|
11522
|
+
reference: z54.number().positive(),
|
|
11523
|
+
min: z54.number().nonnegative(),
|
|
11524
|
+
max: z54.number().positive(),
|
|
11525
|
+
updatedAt: z54.string().date(),
|
|
11526
|
+
confidence: z54.enum([
|
|
10903
11527
|
"high",
|
|
10904
11528
|
"medium",
|
|
10905
11529
|
"medium-low"
|
|
10906
11530
|
]),
|
|
10907
|
-
sources:
|
|
11531
|
+
sources: z54.array(z54.string().url().startsWith("https://")).min(1).max(3)
|
|
10908
11532
|
}).superRefine(({ reference, min, max }, context) => {
|
|
10909
11533
|
if (min > reference || reference > max) {
|
|
10910
11534
|
context.addIssue({
|
|
10911
|
-
code:
|
|
11535
|
+
code: z54.ZodIssueCode.custom,
|
|
10912
11536
|
message: "shipping_reference_outside_range",
|
|
10913
11537
|
path: [
|
|
10914
11538
|
"reference"
|
|
@@ -10916,7 +11540,7 @@ var ShippingReferenceSchema = z52.object({
|
|
|
10916
11540
|
});
|
|
10917
11541
|
}
|
|
10918
11542
|
});
|
|
10919
|
-
var SHIPPING_REFERENCES =
|
|
11543
|
+
var SHIPPING_REFERENCES = z54.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
|
|
10920
11544
|
var PRODUCT_COSTING_COUNTRY_CODES = Object.freeze(Object.keys(SHIPPING_REFERENCES));
|
|
10921
11545
|
var SPANISH_LOCALE_COUNTRIES = /* @__PURE__ */ new Set([
|
|
10922
11546
|
"MX",
|
|
@@ -11098,18 +11722,18 @@ function getProductCostingCountryName(country) {
|
|
|
11098
11722
|
__name(getProductCostingCountryName, "getProductCostingCountryName");
|
|
11099
11723
|
|
|
11100
11724
|
// ../../packages/product-economics/src/product-costing.ts
|
|
11101
|
-
var ProductCostingInputSchema =
|
|
11102
|
-
country:
|
|
11103
|
-
supplierCost:
|
|
11104
|
-
shippingCost:
|
|
11105
|
-
returnRate:
|
|
11106
|
-
cancellationRate:
|
|
11107
|
-
adsManagerCpa:
|
|
11108
|
-
desiredMargin:
|
|
11725
|
+
var ProductCostingInputSchema = z55.object({
|
|
11726
|
+
country: z55.string().refine(isProductCostingCountryCode, "unsupported_country"),
|
|
11727
|
+
supplierCost: z55.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
|
|
11728
|
+
shippingCost: z55.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
|
|
11729
|
+
returnRate: z55.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
11730
|
+
cancellationRate: z55.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
11731
|
+
adsManagerCpa: z55.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
|
|
11732
|
+
desiredMargin: z55.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
|
|
11109
11733
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
11110
11734
|
if (returnRate + cancellationRate >= 1) {
|
|
11111
11735
|
context.addIssue({
|
|
11112
|
-
code:
|
|
11736
|
+
code: z55.ZodIssueCode.custom,
|
|
11113
11737
|
message: "delivery_rate_positive",
|
|
11114
11738
|
path: [
|
|
11115
11739
|
"cancellationRate"
|
|
@@ -11176,14 +11800,14 @@ function calculateProductCosting(rawInput) {
|
|
|
11176
11800
|
__name(calculateProductCosting, "calculateProductCosting");
|
|
11177
11801
|
|
|
11178
11802
|
// src/tools/product-costing.ts
|
|
11179
|
-
import { z as
|
|
11180
|
-
var optionalAmount = /* @__PURE__ */ __name((description) =>
|
|
11181
|
-
var optionalPercent = /* @__PURE__ */ __name((description) =>
|
|
11182
|
-
var ProductCostingToolInput =
|
|
11183
|
-
country:
|
|
11803
|
+
import { z as z56 } from "zod";
|
|
11804
|
+
var optionalAmount = /* @__PURE__ */ __name((description) => z56.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
|
|
11805
|
+
var optionalPercent = /* @__PURE__ */ __name((description) => z56.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
|
|
11806
|
+
var ProductCostingToolInput = z56.object({
|
|
11807
|
+
country: z56.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
|
|
11184
11808
|
message: "Pa\xEDs no soportado por ProductMaker."
|
|
11185
11809
|
}).optional().describe("C\xF3digo ISO-2 de cualquiera de los pa\xEDses disponibles en ProductMaker. Omite para Colombia."),
|
|
11186
|
-
supplierCost:
|
|
11810
|
+
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."),
|
|
11187
11811
|
supplierCostCop: optionalAmount("Alias anterior para costo del proveedor en Colombia. Prefiere supplierCost."),
|
|
11188
11812
|
shippingCost: optionalAmount("Costo de un env\xEDo en la moneda del pa\xEDs. Omite para usar el valor gu\xEDa vigente."),
|
|
11189
11813
|
shippingCostCop: optionalAmount("Alias anterior para flete en Colombia. Prefiere shippingCost."),
|
|
@@ -11315,7 +11939,7 @@ function formatPercent(value) {
|
|
|
11315
11939
|
__name(formatPercent, "formatPercent");
|
|
11316
11940
|
|
|
11317
11941
|
// src/tools/product-research.ts
|
|
11318
|
-
import { z as
|
|
11942
|
+
import { z as z57 } from "zod";
|
|
11319
11943
|
|
|
11320
11944
|
// src/tools/product-research-idempotency.ts
|
|
11321
11945
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -11431,17 +12055,17 @@ var TERMINAL2 = /* @__PURE__ */ new Set([
|
|
|
11431
12055
|
]);
|
|
11432
12056
|
var POLL_INTERVAL_MS = 5e3;
|
|
11433
12057
|
var MAX_WAIT_SECONDS = 120;
|
|
11434
|
-
var FindInput =
|
|
12058
|
+
var FindInput = z57.object({
|
|
11435
12059
|
targetCountry: ResearchTargetCountryEnum.describe("Pa\xEDs objetivo. S\xF3lo CO, MX, PE, EC y CL tienen cobertura de investigaci\xF3n."),
|
|
11436
12060
|
categoryKey: ResearchCategoryEnum.default("auto").describe('Categor\xEDa a investigar. Usa "auto" para que la IA elija seg\xFAn el mercado.'),
|
|
11437
|
-
confirmed:
|
|
11438
|
-
waitSeconds:
|
|
12061
|
+
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.`),
|
|
12062
|
+
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.")
|
|
11439
12063
|
});
|
|
11440
|
-
var GetInput =
|
|
11441
|
-
runId:
|
|
12064
|
+
var GetInput = z57.object({
|
|
12065
|
+
runId: z57.string().uuid().describe("ID del run devuelto por find_winning_products.")
|
|
11442
12066
|
});
|
|
11443
|
-
var ListInput =
|
|
11444
|
-
limit:
|
|
12067
|
+
var ListInput = z57.object({
|
|
12068
|
+
limit: z57.number().int().min(1).max(20).default(10)
|
|
11445
12069
|
});
|
|
11446
12070
|
function textResult(text) {
|
|
11447
12071
|
return {
|
|
@@ -11533,14 +12157,14 @@ _Esta llamada no cobr\xF3 cr\xE9ditos: se reutiliz\xF3 una investigaci\xF3n reci
|
|
|
11533
12157
|
__name(registerProductResearchTools, "registerProductResearchTools");
|
|
11534
12158
|
|
|
11535
12159
|
// src/tools/dropshipping-projects.ts
|
|
11536
|
-
import { z as
|
|
11537
|
-
var ProjectId =
|
|
11538
|
-
var DropshippingProjectStatus =
|
|
12160
|
+
import { z as z58 } from "zod";
|
|
12161
|
+
var ProjectId = z58.string().uuid();
|
|
12162
|
+
var DropshippingProjectStatus = z58.enum([
|
|
11539
12163
|
"active",
|
|
11540
12164
|
"completed",
|
|
11541
12165
|
"archived"
|
|
11542
12166
|
]);
|
|
11543
|
-
var DropshippingProjectPhase =
|
|
12167
|
+
var DropshippingProjectPhase = z58.enum([
|
|
11544
12168
|
"research",
|
|
11545
12169
|
"costing",
|
|
11546
12170
|
"media",
|
|
@@ -11549,21 +12173,21 @@ var DropshippingProjectPhase = z56.enum([
|
|
|
11549
12173
|
"advertising",
|
|
11550
12174
|
"complete"
|
|
11551
12175
|
]);
|
|
11552
|
-
var DropshippingRewardMilestone =
|
|
12176
|
+
var DropshippingRewardMilestone = z58.enum([
|
|
11553
12177
|
"product_selected",
|
|
11554
12178
|
"costing_saved",
|
|
11555
12179
|
"product_media_ready",
|
|
11556
12180
|
"creatives_completed",
|
|
11557
12181
|
"campaign_created"
|
|
11558
12182
|
]);
|
|
11559
|
-
var TargetCountry =
|
|
12183
|
+
var TargetCountry = z58.enum([
|
|
11560
12184
|
"CO",
|
|
11561
12185
|
"MX",
|
|
11562
12186
|
"PE",
|
|
11563
12187
|
"EC",
|
|
11564
12188
|
"CL"
|
|
11565
12189
|
]);
|
|
11566
|
-
var ResearchCategory =
|
|
12190
|
+
var ResearchCategory = z58.enum([
|
|
11567
12191
|
"auto",
|
|
11568
12192
|
"automotive",
|
|
11569
12193
|
"kitchen",
|
|
@@ -11578,7 +12202,7 @@ var ResearchCategory = z56.enum([
|
|
|
11578
12202
|
"garden",
|
|
11579
12203
|
"cleaning"
|
|
11580
12204
|
]);
|
|
11581
|
-
var HttpUrl3 =
|
|
12205
|
+
var HttpUrl3 = z58.string().url().max(2e3).refine((value) => {
|
|
11582
12206
|
try {
|
|
11583
12207
|
return [
|
|
11584
12208
|
"http:",
|
|
@@ -11588,46 +12212,46 @@ var HttpUrl3 = z56.string().url().max(2e3).refine((value) => {
|
|
|
11588
12212
|
return false;
|
|
11589
12213
|
}
|
|
11590
12214
|
}, "URL must use HTTP(S)");
|
|
11591
|
-
var ResearchCandidateOrigin =
|
|
11592
|
-
type:
|
|
12215
|
+
var ResearchCandidateOrigin = z58.object({
|
|
12216
|
+
type: z58.literal("research_candidate"),
|
|
11593
12217
|
runId: ProjectId,
|
|
11594
|
-
candidateSourceId:
|
|
12218
|
+
candidateSourceId: z58.string().min(1).max(160)
|
|
11595
12219
|
});
|
|
11596
|
-
var TaskOrigin =
|
|
11597
|
-
type:
|
|
12220
|
+
var TaskOrigin = z58.object({
|
|
12221
|
+
type: z58.literal("task"),
|
|
11598
12222
|
taskId: ProjectId
|
|
11599
12223
|
});
|
|
11600
|
-
var ManualOrigin =
|
|
11601
|
-
type:
|
|
12224
|
+
var ManualOrigin = z58.object({
|
|
12225
|
+
type: z58.literal("manual"),
|
|
11602
12226
|
clientRequestId: ProjectId,
|
|
11603
|
-
country:
|
|
12227
|
+
country: z58.string().min(2).max(3).optional()
|
|
11604
12228
|
});
|
|
11605
|
-
var StartProjectInput =
|
|
11606
|
-
origin:
|
|
12229
|
+
var StartProjectInput = z58.object({
|
|
12230
|
+
origin: z58.discriminatedUnion("type", [
|
|
11607
12231
|
ResearchCandidateOrigin,
|
|
11608
12232
|
TaskOrigin,
|
|
11609
12233
|
ManualOrigin
|
|
11610
12234
|
]),
|
|
11611
|
-
productName:
|
|
12235
|
+
productName: z58.string().trim().min(1).max(240).optional(),
|
|
11612
12236
|
productUrl: HttpUrl3.optional(),
|
|
11613
|
-
country:
|
|
11614
|
-
languageCode:
|
|
12237
|
+
country: z58.string().min(2).max(3).optional(),
|
|
12238
|
+
languageCode: z58.string().min(2).max(16).optional()
|
|
11615
12239
|
}).strict();
|
|
11616
|
-
var CostProjectInput =
|
|
12240
|
+
var CostProjectInput = z58.object({
|
|
11617
12241
|
projectId: ProjectId,
|
|
11618
|
-
version:
|
|
11619
|
-
country:
|
|
11620
|
-
supplierCost:
|
|
11621
|
-
shippingCost:
|
|
11622
|
-
returnRate:
|
|
11623
|
-
cancellationRate:
|
|
11624
|
-
adsManagerCpa:
|
|
11625
|
-
desiredMargin:
|
|
11626
|
-
});
|
|
11627
|
-
var GuideInput =
|
|
12242
|
+
version: z58.number().int().positive(),
|
|
12243
|
+
country: z58.string().trim().min(2).max(3),
|
|
12244
|
+
supplierCost: z58.number().finite().positive(),
|
|
12245
|
+
shippingCost: z58.number().finite().nonnegative(),
|
|
12246
|
+
returnRate: z58.number().finite().min(0).lt(1),
|
|
12247
|
+
cancellationRate: z58.number().finite().min(0).lt(1),
|
|
12248
|
+
adsManagerCpa: z58.number().finite().nonnegative(),
|
|
12249
|
+
desiredMargin: z58.number().finite().min(0).lt(1)
|
|
12250
|
+
});
|
|
12251
|
+
var GuideInput = z58.object({
|
|
11628
12252
|
projectId: ProjectId.optional()
|
|
11629
12253
|
}).strict();
|
|
11630
|
-
var DropshippingProjectNextAction =
|
|
12254
|
+
var DropshippingProjectNextAction = z58.discriminatedUnion("kind", [
|
|
11631
12255
|
"cost_product",
|
|
11632
12256
|
"provide_product_media",
|
|
11633
12257
|
"generate_creatives",
|
|
@@ -11641,78 +12265,78 @@ var DropshippingProjectNextAction = z56.discriminatedUnion("kind", [
|
|
|
11641
12265
|
"create_meta_campaign",
|
|
11642
12266
|
"repair_meta_campaign",
|
|
11643
12267
|
"review_paused_campaign"
|
|
11644
|
-
].map((kind) =>
|
|
11645
|
-
kind:
|
|
11646
|
-
href:
|
|
12268
|
+
].map((kind) => z58.object({
|
|
12269
|
+
kind: z58.literal(kind),
|
|
12270
|
+
href: z58.string().min(1)
|
|
11647
12271
|
})));
|
|
11648
|
-
var CostingInputSchema =
|
|
11649
|
-
country:
|
|
11650
|
-
supplierCost:
|
|
11651
|
-
shippingCost:
|
|
11652
|
-
returnRate:
|
|
11653
|
-
cancellationRate:
|
|
11654
|
-
adsManagerCpa:
|
|
11655
|
-
desiredMargin:
|
|
12272
|
+
var CostingInputSchema = z58.object({
|
|
12273
|
+
country: z58.string().min(2).max(3),
|
|
12274
|
+
supplierCost: z58.number().finite().positive(),
|
|
12275
|
+
shippingCost: z58.number().finite().nonnegative(),
|
|
12276
|
+
returnRate: z58.number().finite().min(0).lt(1),
|
|
12277
|
+
cancellationRate: z58.number().finite().min(0).lt(1),
|
|
12278
|
+
adsManagerCpa: z58.number().finite().nonnegative(),
|
|
12279
|
+
desiredMargin: z58.number().finite().min(0).lt(1)
|
|
11656
12280
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
11657
12281
|
if (returnRate + cancellationRate >= 1) context.addIssue({
|
|
11658
|
-
code:
|
|
12282
|
+
code: z58.ZodIssueCode.custom,
|
|
11659
12283
|
path: [
|
|
11660
12284
|
"cancellationRate"
|
|
11661
12285
|
]
|
|
11662
12286
|
});
|
|
11663
12287
|
});
|
|
11664
|
-
var CostingResultSchema =
|
|
11665
|
-
country:
|
|
11666
|
-
deliveryRate:
|
|
11667
|
-
adjustedShippingCost:
|
|
11668
|
-
adjustedCpa:
|
|
11669
|
-
totalCost:
|
|
11670
|
-
suggestedPrice:
|
|
11671
|
-
baseCommercialPrice:
|
|
11672
|
-
minimumSafePrice:
|
|
11673
|
-
commercialPrice:
|
|
11674
|
-
comparisonPrice:
|
|
11675
|
-
estimatedProfit:
|
|
11676
|
-
actualMargin:
|
|
11677
|
-
breakEvenAdsManagerCpa:
|
|
11678
|
-
wasPriceAutoAdjusted:
|
|
11679
|
-
profitabilityStatus:
|
|
11680
|
-
});
|
|
11681
|
-
var RewardProgressSchema =
|
|
11682
|
-
campaignKey:
|
|
11683
|
-
totalCredits:
|
|
11684
|
-
earnedCredits:
|
|
11685
|
-
milestones:
|
|
12288
|
+
var CostingResultSchema = z58.object({
|
|
12289
|
+
country: z58.string().min(2).max(3),
|
|
12290
|
+
deliveryRate: z58.number().finite(),
|
|
12291
|
+
adjustedShippingCost: z58.number().finite(),
|
|
12292
|
+
adjustedCpa: z58.number().finite(),
|
|
12293
|
+
totalCost: z58.number().finite(),
|
|
12294
|
+
suggestedPrice: z58.number().finite(),
|
|
12295
|
+
baseCommercialPrice: z58.number().finite(),
|
|
12296
|
+
minimumSafePrice: z58.number().finite(),
|
|
12297
|
+
commercialPrice: z58.number().finite(),
|
|
12298
|
+
comparisonPrice: z58.number().finite(),
|
|
12299
|
+
estimatedProfit: z58.number().finite(),
|
|
12300
|
+
actualMargin: z58.number().finite(),
|
|
12301
|
+
breakEvenAdsManagerCpa: z58.number().finite(),
|
|
12302
|
+
wasPriceAutoAdjusted: z58.boolean(),
|
|
12303
|
+
profitabilityStatus: z58.literal("healthy")
|
|
12304
|
+
});
|
|
12305
|
+
var RewardProgressSchema = z58.object({
|
|
12306
|
+
campaignKey: z58.literal("dropshipping-launch-v1"),
|
|
12307
|
+
totalCredits: z58.number().int().positive(),
|
|
12308
|
+
earnedCredits: z58.number().int().nonnegative(),
|
|
12309
|
+
milestones: z58.array(z58.object({
|
|
11686
12310
|
milestone: DropshippingRewardMilestone,
|
|
11687
|
-
credits:
|
|
11688
|
-
available:
|
|
11689
|
-
earned:
|
|
12311
|
+
credits: z58.number().int().positive(),
|
|
12312
|
+
available: z58.boolean().optional(),
|
|
12313
|
+
earned: z58.boolean(),
|
|
11690
12314
|
grantId: ProjectId.nullable(),
|
|
11691
|
-
grantedAt:
|
|
12315
|
+
grantedAt: z58.string().datetime().nullable()
|
|
11692
12316
|
})).length(DropshippingRewardMilestone.options.length)
|
|
11693
12317
|
});
|
|
11694
|
-
var ProjectResponseSchema =
|
|
12318
|
+
var ProjectResponseSchema = z58.object({
|
|
11695
12319
|
id: ProjectId,
|
|
11696
12320
|
taskId: ProjectId.nullable(),
|
|
11697
12321
|
status: DropshippingProjectStatus,
|
|
11698
|
-
originKey:
|
|
12322
|
+
originKey: z58.string().min(1),
|
|
11699
12323
|
researchRunId: ProjectId.nullable(),
|
|
11700
|
-
candidateSourceId:
|
|
11701
|
-
productName:
|
|
12324
|
+
candidateSourceId: z58.string().nullable(),
|
|
12325
|
+
productName: z58.string().nullable(),
|
|
11702
12326
|
productUrl: HttpUrl3.nullable(),
|
|
11703
12327
|
referenceImageUrl: HttpUrl3.nullable(),
|
|
11704
|
-
targetCountry:
|
|
11705
|
-
languageCode:
|
|
11706
|
-
hasReferenceImage:
|
|
12328
|
+
targetCountry: z58.string().nullable(),
|
|
12329
|
+
languageCode: z58.string().nullable(),
|
|
12330
|
+
hasReferenceImage: z58.boolean(),
|
|
11707
12331
|
costingInput: CostingInputSchema.nullable(),
|
|
11708
12332
|
costingResult: CostingResultSchema.nullable(),
|
|
11709
|
-
costingVersion:
|
|
11710
|
-
version:
|
|
12333
|
+
costingVersion: z58.number().int().positive().nullable(),
|
|
12334
|
+
version: z58.number().int().positive(),
|
|
11711
12335
|
phase: DropshippingProjectPhase,
|
|
11712
12336
|
nextAction: DropshippingProjectNextAction.nullable(),
|
|
11713
12337
|
rewards: RewardProgressSchema,
|
|
11714
|
-
createdAt:
|
|
11715
|
-
updatedAt:
|
|
12338
|
+
createdAt: z58.string().datetime(),
|
|
12339
|
+
updatedAt: z58.string().datetime()
|
|
11716
12340
|
}).passthrough();
|
|
11717
12341
|
function projectText(raw) {
|
|
11718
12342
|
const project = ProjectResponseSchema.parse(raw);
|
|
@@ -11797,6 +12421,7 @@ function createServer(opts) {
|
|
|
11797
12421
|
}
|
|
11798
12422
|
});
|
|
11799
12423
|
const api = new ApiClient(apiBaseUrl, apiKey);
|
|
12424
|
+
registerMetaUploadAppResource(server);
|
|
11800
12425
|
registerProductCostingTools(server);
|
|
11801
12426
|
registerProductResearchTools(server, api);
|
|
11802
12427
|
registerDropshippingProjectTools(server, api);
|
|
@@ -11814,6 +12439,7 @@ function createServer(opts) {
|
|
|
11814
12439
|
registerPublishTools(server, api, {
|
|
11815
12440
|
transport
|
|
11816
12441
|
});
|
|
12442
|
+
registerMetaUploadTools(server, api);
|
|
11817
12443
|
registerCampaignTools(server, api);
|
|
11818
12444
|
return server;
|
|
11819
12445
|
}
|