@productmaker/mcp 2.3.2 → 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 +32 -2
- package/dist/http.js +1283 -477
- package/dist/stdio.js +1283 -477
- package/dist/ui/meta-upload-app.html +2268 -0
- package/package.json +5 -4
package/dist/stdio.js
CHANGED
|
@@ -150,10 +150,60 @@ var ApiClient = class {
|
|
|
150
150
|
};
|
|
151
151
|
|
|
152
152
|
// src/index.ts
|
|
153
|
-
var MCP_VERSION = "2.3
|
|
153
|
+
var MCP_VERSION = "2.8.3";
|
|
154
|
+
|
|
155
|
+
// src/meta-upload-app.ts
|
|
156
|
+
import { readFileSync } from "fs";
|
|
157
|
+
import { registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
|
|
158
|
+
var META_UPLOAD_APP_RESOURCE_URI = "ui://meta-upload/v1.html";
|
|
159
|
+
var META_UPLOAD_APP_NAME = "Meta upload app";
|
|
160
|
+
var META_UPLOAD_APP_HTML_URL = new URL("./ui/meta-upload-app.html", import.meta.url);
|
|
161
|
+
var META_UPLOAD_APP_CSP = {
|
|
162
|
+
connectDomains: [
|
|
163
|
+
"https://productmaker.app",
|
|
164
|
+
"https://www.productmaker.app",
|
|
165
|
+
"https://*.r2.cloudflarestorage.com",
|
|
166
|
+
"https://*.oaiusercontent.com"
|
|
167
|
+
]
|
|
168
|
+
};
|
|
169
|
+
function readMetaUploadAppHtml(htmlUrl = META_UPLOAD_APP_HTML_URL) {
|
|
170
|
+
try {
|
|
171
|
+
return readFileSync(htmlUrl, "utf8");
|
|
172
|
+
} catch (error) {
|
|
173
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
174
|
+
throw new Error(`Missing packaged Meta upload app HTML at ${htmlUrl.pathname}. Rebuild @productmaker/mcp so ${META_UPLOAD_APP_RESOURCE_URI} is bundled. Original error: ${message}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
__name(readMetaUploadAppHtml, "readMetaUploadAppHtml");
|
|
178
|
+
function registerMetaUploadAppResource(server) {
|
|
179
|
+
const html = readMetaUploadAppHtml();
|
|
180
|
+
registerAppResource(server, META_UPLOAD_APP_NAME, META_UPLOAD_APP_RESOURCE_URI, {
|
|
181
|
+
title: "Meta upload app",
|
|
182
|
+
mimeType: RESOURCE_MIME_TYPE,
|
|
183
|
+
_meta: {
|
|
184
|
+
ui: {
|
|
185
|
+
csp: META_UPLOAD_APP_CSP
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}, async () => ({
|
|
189
|
+
contents: [
|
|
190
|
+
{
|
|
191
|
+
uri: META_UPLOAD_APP_RESOURCE_URI,
|
|
192
|
+
mimeType: RESOURCE_MIME_TYPE,
|
|
193
|
+
text: html,
|
|
194
|
+
_meta: {
|
|
195
|
+
ui: {
|
|
196
|
+
csp: META_UPLOAD_APP_CSP
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
}));
|
|
202
|
+
}
|
|
203
|
+
__name(registerMetaUploadAppResource, "registerMetaUploadAppResource");
|
|
154
204
|
|
|
155
205
|
// src/tools/tasks.ts
|
|
156
|
-
import { z as
|
|
206
|
+
import { z as z45 } from "zod";
|
|
157
207
|
|
|
158
208
|
// src/resolve-image.ts
|
|
159
209
|
import { lookup as dnsLookup } from "dns/promises";
|
|
@@ -1042,6 +1092,8 @@ var Angle = z6.object({
|
|
|
1042
1092
|
buyerPersona: z6.string().min(1).max(360),
|
|
1043
1093
|
/** Character description for video (role + appearance + attitude). Drives voice selection and Frame0Spec. */
|
|
1044
1094
|
actor: z6.string().min(1).max(360).optional(),
|
|
1095
|
+
/** Saved custom actor (Actor.id) resolved for this angle. Its approved photo anchors the identity. */
|
|
1096
|
+
actorId: z6.string().optional(),
|
|
1045
1097
|
// Binary only: voice catalog has no non-binary voices.
|
|
1046
1098
|
// See packages/angles/src/index.ts + getAngles coercion in angle.repository.ts.
|
|
1047
1099
|
gender: z6.enum([
|
|
@@ -1632,6 +1684,8 @@ var IngestOptions = z14.object({
|
|
|
1632
1684
|
generateAdCopy: z14.boolean().default(true),
|
|
1633
1685
|
generateAiMusic: z14.boolean().optional(),
|
|
1634
1686
|
voiceId: VoiceId.optional(),
|
|
1687
|
+
/** Global saved custom actor (Actor.id). Default for all angles. */
|
|
1688
|
+
actorId: z14.string().optional(),
|
|
1635
1689
|
isCombo: z14.boolean().default(false),
|
|
1636
1690
|
productContext: z14.string().transform((value) => value.trim()).pipe(z14.string().max(PRODUCT_CONTEXT_MAX_CHARS)).optional(),
|
|
1637
1691
|
creativeDirection: z14.string().transform((value) => value.trim()).pipe(z14.string().max(500)).optional(),
|
|
@@ -1646,7 +1700,7 @@ var IngestOptions = z14.object({
|
|
|
1646
1700
|
"landing",
|
|
1647
1701
|
"video",
|
|
1648
1702
|
"image"
|
|
1649
|
-
])).min(
|
|
1703
|
+
])).min(1).max(3).optional(),
|
|
1650
1704
|
// Landing page output: 'classic' = image-based (default), 'code' = structured
|
|
1651
1705
|
// code landing rendered by the theme app extension. Absent ⇒ classic.
|
|
1652
1706
|
landingMode: LandingModeSchema.optional(),
|
|
@@ -1697,7 +1751,13 @@ var VideoOptionsOverride = z14.object({
|
|
|
1697
1751
|
videoResolution: VideoResolution.optional(),
|
|
1698
1752
|
voiceId: VoiceId.optional(),
|
|
1699
1753
|
/** Actor/character description (role + appearance + attitude). Drives voice and video character. */
|
|
1700
|
-
actor: z14.string().max(360).optional()
|
|
1754
|
+
actor: z14.string().max(360).optional(),
|
|
1755
|
+
/** Saved custom actor (Actor.id). Its approved photo anchors the identity.
|
|
1756
|
+
* `null` is an explicit REMOVAL (the user cleared the picker) and must be
|
|
1757
|
+
* sent as such: an omitted key means "not mentioned", which every writer
|
|
1758
|
+
* reads as "leave the stored selection alone". Same contract as
|
|
1759
|
+
* service-angles.controller.ts. */
|
|
1760
|
+
actorId: z14.string().nullable().optional()
|
|
1701
1761
|
});
|
|
1702
1762
|
var AngleVideoOptions = VideoOptionsOverride.extend({
|
|
1703
1763
|
angleIndex: z14.number().int().nonnegative(),
|
|
@@ -1708,7 +1768,11 @@ var AngleVideoOptions = VideoOptionsOverride.extend({
|
|
|
1708
1768
|
/** Per-angle buyer persona override (describes the target audience) */
|
|
1709
1769
|
buyerPersona: z14.string().max(360).optional(),
|
|
1710
1770
|
/** Per-angle actor/character description (role + appearance + attitude). Drives voice and video character. */
|
|
1711
|
-
actor: z14.string().max(360).optional()
|
|
1771
|
+
actor: z14.string().max(360).optional(),
|
|
1772
|
+
/** Per-angle saved custom actor (Actor.id). Overrides the global actorId.
|
|
1773
|
+
* `null` = the user removed the personaje on this angle; it must NOT fall
|
|
1774
|
+
* back to the global default. Omitted = not mentioned. */
|
|
1775
|
+
actorId: z14.string().nullable().optional()
|
|
1712
1776
|
});
|
|
1713
1777
|
var RapidScriptBlueprintShotForApproval = z14.object({
|
|
1714
1778
|
id: z14.string().min(1),
|
|
@@ -1771,6 +1835,8 @@ var IngestResumePayload = z14.object({
|
|
|
1771
1835
|
voiceId: VoiceId.optional(),
|
|
1772
1836
|
/** Global actor/character description (role + appearance + attitude). Default for all angles. */
|
|
1773
1837
|
actor: z14.string().max(360).optional(),
|
|
1838
|
+
/** Global saved custom actor (Actor.id). Default for all angles. */
|
|
1839
|
+
actorId: z14.string().optional(),
|
|
1774
1840
|
/** Custom angle override for landing/creative (uses this instead of first angle) */
|
|
1775
1841
|
customAngleOverride: z14.string().min(1).max(480).optional(),
|
|
1776
1842
|
/** Pain points to emphasize in landings, creatives, and videos */
|
|
@@ -2469,6 +2535,8 @@ var RefineAngleResponse = z20.object({
|
|
|
2469
2535
|
painPoints: z20.array(z20.string()).optional(),
|
|
2470
2536
|
benefits: z20.array(z20.string()).optional(),
|
|
2471
2537
|
actor: z20.string().optional(),
|
|
2538
|
+
/** Saved custom actor (Actor.id) carried through the refine round-trip. */
|
|
2539
|
+
actorId: z20.string().optional(),
|
|
2472
2540
|
buyerPersona: z20.string().optional()
|
|
2473
2541
|
});
|
|
2474
2542
|
|
|
@@ -2848,7 +2916,9 @@ var PendingUserActionSchema = z23.discriminatedUnion("kind", [
|
|
|
2848
2916
|
id: z23.string().uuid(),
|
|
2849
2917
|
content: z23.string(),
|
|
2850
2918
|
creativeDirection: z23.string(),
|
|
2851
|
-
actor: z23.string()
|
|
2919
|
+
actor: z23.string(),
|
|
2920
|
+
/** Saved custom actor (Actor.id) backing this angle, when one was chosen. */
|
|
2921
|
+
actorId: z23.string().optional()
|
|
2852
2922
|
})).min(1).max(3)
|
|
2853
2923
|
}),
|
|
2854
2924
|
z23.object({
|
|
@@ -3001,7 +3071,8 @@ var CostEstimationType = z24.enum([
|
|
|
3001
3071
|
"landing-regenerate",
|
|
3002
3072
|
"landing-creative",
|
|
3003
3073
|
"campaign-copy",
|
|
3004
|
-
"service-creative"
|
|
3074
|
+
"service-creative",
|
|
3075
|
+
"actor-grid"
|
|
3005
3076
|
]);
|
|
3006
3077
|
var IngestEstimationOptions = z24.object({
|
|
3007
3078
|
/** Number of product references that the extract stage will clean. */
|
|
@@ -3102,6 +3173,8 @@ var VideoCreativeEstimationOptions = z24.object({
|
|
|
3102
3173
|
requiresAngles: z24.boolean().optional(),
|
|
3103
3174
|
/** When false, omit the Meta/Whisper competitor-scan ceiling from the estimate. */
|
|
3104
3175
|
competitorScanEnabled: z24.boolean().optional(),
|
|
3176
|
+
/** Quote the competitor scan even when product extraction is skipped (direct video auto path). */
|
|
3177
|
+
requiresCompetitorScan: z24.boolean().optional(),
|
|
3105
3178
|
generateAiMusic: z24.boolean().optional(),
|
|
3106
3179
|
/** Free mode burns one Whisper pass over the final clip when enabled. */
|
|
3107
3180
|
addSubtitles: z24.boolean().optional()
|
|
@@ -3170,6 +3243,7 @@ var ServiceCreativeEstimationOptions = z24.object({
|
|
|
3170
3243
|
mediaAssetCount: z24.number().int().min(0).max(8).optional(),
|
|
3171
3244
|
uploadedVideoDurationSeconds: z24.number().min(0).max(240).optional()
|
|
3172
3245
|
});
|
|
3246
|
+
var ActorGridEstimationOptions = z24.object({}).strict();
|
|
3173
3247
|
var CostEstimationRequest = z24.discriminatedUnion("type", [
|
|
3174
3248
|
z24.object({
|
|
3175
3249
|
type: z24.literal("ingest"),
|
|
@@ -3206,6 +3280,10 @@ var CostEstimationRequest = z24.discriminatedUnion("type", [
|
|
|
3206
3280
|
z24.object({
|
|
3207
3281
|
type: z24.literal("service-creative"),
|
|
3208
3282
|
options: ServiceCreativeEstimationOptions
|
|
3283
|
+
}),
|
|
3284
|
+
z24.object({
|
|
3285
|
+
type: z24.literal("actor-grid"),
|
|
3286
|
+
options: ActorGridEstimationOptions
|
|
3209
3287
|
})
|
|
3210
3288
|
]);
|
|
3211
3289
|
var CostEstimationResponse = z24.object({
|
|
@@ -3689,6 +3767,25 @@ var ALLOWED_MEDIA_CONTENT_TYPES = [
|
|
|
3689
3767
|
"audio/wav",
|
|
3690
3768
|
"audio/x-wav"
|
|
3691
3769
|
];
|
|
3770
|
+
var META_STAGING_IMAGE_CONTENT_TYPES = /* @__PURE__ */ new Set([
|
|
3771
|
+
"image/jpeg",
|
|
3772
|
+
"image/png",
|
|
3773
|
+
"image/webp"
|
|
3774
|
+
]);
|
|
3775
|
+
var META_STAGING_MEDIA_CONTENT_TYPES = /* @__PURE__ */ new Set([
|
|
3776
|
+
...META_STAGING_IMAGE_CONTENT_TYPES,
|
|
3777
|
+
"video/mp4",
|
|
3778
|
+
"video/quicktime",
|
|
3779
|
+
"video/webm"
|
|
3780
|
+
]);
|
|
3781
|
+
var META_STAGING_CONTENT_TYPES = [
|
|
3782
|
+
"image/jpeg",
|
|
3783
|
+
"image/png",
|
|
3784
|
+
"image/webp",
|
|
3785
|
+
"video/mp4",
|
|
3786
|
+
"video/quicktime",
|
|
3787
|
+
"video/webm"
|
|
3788
|
+
];
|
|
3692
3789
|
var PresignUploadRequest = z31.object({
|
|
3693
3790
|
/** MIME type of the file to upload */
|
|
3694
3791
|
contentType: z31.enum(ALLOWED_MEDIA_CONTENT_TYPES),
|
|
@@ -3696,11 +3793,59 @@ var PresignUploadRequest = z31.object({
|
|
|
3696
3793
|
fileName: z31.string().min(1).max(255),
|
|
3697
3794
|
/** File size in bytes (validated against MAX_FILE_SIZE_BYTES) */
|
|
3698
3795
|
fileSize: z31.number().int().positive().max(CAMPAIGN_LIMITS.MAX_FILE_SIZE_BYTES),
|
|
3699
|
-
/**
|
|
3796
|
+
/**
|
|
3797
|
+
* Service uploads receive a user-bound asset ticket; ads keep the legacy path.
|
|
3798
|
+
* `actor` uploads the identity photo of a custom actor and is stored under
|
|
3799
|
+
* `actors/<userId>/…` so `AssetAccessGuard` can resolve its owner.
|
|
3800
|
+
*/
|
|
3700
3801
|
purpose: z31.enum([
|
|
3701
3802
|
"ads",
|
|
3702
|
-
"service"
|
|
3803
|
+
"service",
|
|
3804
|
+
"actor",
|
|
3805
|
+
"meta_staging"
|
|
3806
|
+
]).optional(),
|
|
3807
|
+
/** Required for purpose=meta_staging so media and thumbnails can be checked independently. */
|
|
3808
|
+
assetRole: z31.enum([
|
|
3809
|
+
"media",
|
|
3810
|
+
"thumbnail"
|
|
3703
3811
|
]).optional()
|
|
3812
|
+
}).superRefine((value, ctx) => {
|
|
3813
|
+
if (value.purpose === "meta_staging") {
|
|
3814
|
+
if (!value.assetRole) {
|
|
3815
|
+
ctx.addIssue({
|
|
3816
|
+
code: z31.ZodIssueCode.custom,
|
|
3817
|
+
path: [
|
|
3818
|
+
"assetRole"
|
|
3819
|
+
],
|
|
3820
|
+
message: "assetRole is required for Meta staging uploads"
|
|
3821
|
+
});
|
|
3822
|
+
} else if (!META_STAGING_MEDIA_CONTENT_TYPES.has(value.contentType)) {
|
|
3823
|
+
ctx.addIssue({
|
|
3824
|
+
code: z31.ZodIssueCode.custom,
|
|
3825
|
+
path: [
|
|
3826
|
+
"contentType"
|
|
3827
|
+
],
|
|
3828
|
+
message: "Meta staging accepts only image or video media"
|
|
3829
|
+
});
|
|
3830
|
+
} else if (value.assetRole === "thumbnail" && !META_STAGING_IMAGE_CONTENT_TYPES.has(value.contentType)) {
|
|
3831
|
+
ctx.addIssue({
|
|
3832
|
+
code: z31.ZodIssueCode.custom,
|
|
3833
|
+
path: [
|
|
3834
|
+
"contentType"
|
|
3835
|
+
],
|
|
3836
|
+
message: "Meta staging thumbnails must be images"
|
|
3837
|
+
});
|
|
3838
|
+
}
|
|
3839
|
+
}
|
|
3840
|
+
if (value.purpose !== "meta_staging" && value.assetRole) {
|
|
3841
|
+
ctx.addIssue({
|
|
3842
|
+
code: z31.ZodIssueCode.custom,
|
|
3843
|
+
path: [
|
|
3844
|
+
"assetRole"
|
|
3845
|
+
],
|
|
3846
|
+
message: "assetRole is only valid for Meta staging uploads"
|
|
3847
|
+
});
|
|
3848
|
+
}
|
|
3704
3849
|
});
|
|
3705
3850
|
var PresignUploadResponse = z31.object({
|
|
3706
3851
|
/** Presigned S3 PUT URL — browser sends file directly here */
|
|
@@ -3710,8 +3855,23 @@ var PresignUploadResponse = z31.object({
|
|
|
3710
3855
|
/** S3 key */
|
|
3711
3856
|
key: z31.string(),
|
|
3712
3857
|
/** Present only for purpose=service. */
|
|
3713
|
-
assetToken: z31.string().min(1).optional()
|
|
3858
|
+
assetToken: z31.string().min(1).optional(),
|
|
3859
|
+
/** Present only for purpose=meta_staging. */
|
|
3860
|
+
stagingAssetId: z31.string().uuid().optional()
|
|
3714
3861
|
});
|
|
3862
|
+
var MetaUploadMediaKind = z31.enum([
|
|
3863
|
+
"image",
|
|
3864
|
+
"video",
|
|
3865
|
+
"mixed"
|
|
3866
|
+
]);
|
|
3867
|
+
var META_UPLOAD_MAX_FILES = CAMPAIGN_LIMITS.MAX_CREATIVES;
|
|
3868
|
+
var MetaUploadAssetSummary = z31.object({
|
|
3869
|
+
stagingAssetId: z31.string().uuid(),
|
|
3870
|
+
mediaKind: z31.enum([
|
|
3871
|
+
"image",
|
|
3872
|
+
"video"
|
|
3873
|
+
])
|
|
3874
|
+
}).strict();
|
|
3715
3875
|
var MetaCampaignType = z31.enum([
|
|
3716
3876
|
"sales",
|
|
3717
3877
|
"traffic",
|
|
@@ -3737,6 +3897,81 @@ var HttpUrl = z31.string().url().refine((value) => {
|
|
|
3737
3897
|
return false;
|
|
3738
3898
|
}
|
|
3739
3899
|
}, "URL must use http or https");
|
|
3900
|
+
var MetaUploadSessionRequest = z31.object({
|
|
3901
|
+
/** The browser page will require a file of this media kind. */
|
|
3902
|
+
mediaKind: MetaUploadMediaKind,
|
|
3903
|
+
/** The staged object role; media is the normal creative input. */
|
|
3904
|
+
assetRole: z31.enum([
|
|
3905
|
+
"media",
|
|
3906
|
+
"thumbnail"
|
|
3907
|
+
]).default("media"),
|
|
3908
|
+
/** Maximum number of files; the API chooses 10 for mixed and 1 otherwise when omitted. */
|
|
3909
|
+
maxFiles: z31.number().int().min(1).max(META_UPLOAD_MAX_FILES).optional()
|
|
3910
|
+
}).strict().superRefine((value, ctx) => {
|
|
3911
|
+
if (value.mediaKind === "mixed" && value.assetRole === "thumbnail") {
|
|
3912
|
+
ctx.addIssue({
|
|
3913
|
+
code: z31.ZodIssueCode.custom,
|
|
3914
|
+
path: [
|
|
3915
|
+
"mediaKind"
|
|
3916
|
+
],
|
|
3917
|
+
message: "Mixed Meta upload sessions cannot be thumbnails"
|
|
3918
|
+
});
|
|
3919
|
+
}
|
|
3920
|
+
if (value.mediaKind !== "mixed" && value.maxFiles !== void 0 && value.maxFiles !== 1) {
|
|
3921
|
+
ctx.addIssue({
|
|
3922
|
+
code: z31.ZodIssueCode.custom,
|
|
3923
|
+
path: [
|
|
3924
|
+
"maxFiles"
|
|
3925
|
+
],
|
|
3926
|
+
message: "Non-mixed Meta upload sessions accept exactly one file"
|
|
3927
|
+
});
|
|
3928
|
+
}
|
|
3929
|
+
if (value.assetRole === "thumbnail" && value.mediaKind !== "image") {
|
|
3930
|
+
ctx.addIssue({
|
|
3931
|
+
code: z31.ZodIssueCode.custom,
|
|
3932
|
+
path: [
|
|
3933
|
+
"mediaKind"
|
|
3934
|
+
],
|
|
3935
|
+
message: "Meta thumbnails must be images"
|
|
3936
|
+
});
|
|
3937
|
+
}
|
|
3938
|
+
});
|
|
3939
|
+
var ResolveMetaUploadSessionRequest = z31.object({
|
|
3940
|
+
/** Opaque signed token embedded in the one-click upload page URL. */
|
|
3941
|
+
token: z31.string().min(1).max(4096),
|
|
3942
|
+
/** Stable browser-generated id so failed PUT retries reuse the same slot. */
|
|
3943
|
+
uploadId: z31.string().uuid().optional(),
|
|
3944
|
+
/** Browser-selected metadata; the API checks it against the session kind/role. */
|
|
3945
|
+
contentType: z31.enum(META_STAGING_CONTENT_TYPES),
|
|
3946
|
+
fileName: z31.string().min(1).max(255),
|
|
3947
|
+
fileSize: z31.number().int().positive().max(CAMPAIGN_LIMITS.MAX_FILE_SIZE_BYTES)
|
|
3948
|
+
}).strict();
|
|
3949
|
+
var MetaUploadSessionResponse = z31.object({
|
|
3950
|
+
sessionId: z31.string().uuid(),
|
|
3951
|
+
uploadPageUrl: HttpUrl,
|
|
3952
|
+
mediaKind: MetaUploadMediaKind,
|
|
3953
|
+
assetRole: z31.enum([
|
|
3954
|
+
"media",
|
|
3955
|
+
"thumbnail"
|
|
3956
|
+
]),
|
|
3957
|
+
maxFiles: z31.number().int().min(1).max(META_UPLOAD_MAX_FILES),
|
|
3958
|
+
expiresAt: z31.string().datetime()
|
|
3959
|
+
}).strict();
|
|
3960
|
+
var ResolveMetaUploadSessionResponse = z31.object({
|
|
3961
|
+
uploadUrl: z31.string().url(),
|
|
3962
|
+
stagingAssetId: z31.string().uuid(),
|
|
3963
|
+
expiresAt: z31.string().datetime()
|
|
3964
|
+
}).strict();
|
|
3965
|
+
var MetaUploadSessionStatusResponse = z31.object({
|
|
3966
|
+
sessionId: z31.string().uuid(),
|
|
3967
|
+
status: z31.enum([
|
|
3968
|
+
"pending",
|
|
3969
|
+
"uploaded"
|
|
3970
|
+
]),
|
|
3971
|
+
assets: z31.array(MetaUploadAssetSummary).max(META_UPLOAD_MAX_FILES),
|
|
3972
|
+
/** Legacy single-file response field retained for older callers. */
|
|
3973
|
+
stagingAssetId: z31.string().uuid().optional()
|
|
3974
|
+
}).strict();
|
|
3740
3975
|
var MetaAdAccountId = z31.string().trim().min(1, "Debes seleccionar una cuenta publicitaria").transform((value) => /^\d+$/.test(value) ? `act_${value}` : value).refine((value) => /^act_\d+$/.test(value), {
|
|
3741
3976
|
message: 'adAccountId must look like "act_1234567890" (a bare numeric id is also accepted)'
|
|
3742
3977
|
});
|
|
@@ -3823,18 +4058,26 @@ var UploadToMetaRequest = z31.object({
|
|
|
3823
4058
|
title: z31.string().max(CAMPAIGN_LIMITS.MAX_TITLE_LENGTH),
|
|
3824
4059
|
description: z31.string().max(CAMPAIGN_LIMITS.MAX_DESCRIPTION_LENGTH),
|
|
3825
4060
|
cta: MetaCtaType,
|
|
3826
|
-
/**
|
|
3827
|
-
mediaUrl: HttpUrl,
|
|
4061
|
+
/** Legacy public URL; new Meta flows should use mediaAssetId. */
|
|
4062
|
+
mediaUrl: HttpUrl.optional(),
|
|
4063
|
+
/** Owner-scoped temporary asset created by the Meta staging upload flow. */
|
|
4064
|
+
mediaAssetId: z31.string().uuid().optional(),
|
|
3828
4065
|
/**
|
|
3829
4066
|
* Public URL of an extracted thumbnail. Required for video ads — Meta
|
|
3830
4067
|
* rejects video ad creatives without `image_hash`/`image_url` in
|
|
3831
4068
|
* `video_data` (subcode 1443226). Frontend extracts a frame from the
|
|
3832
4069
|
* video and uploads it before submitting the request.
|
|
3833
4070
|
*/
|
|
3834
|
-
thumbnailUrl: HttpUrl.optional()
|
|
4071
|
+
thumbnailUrl: HttpUrl.optional(),
|
|
4072
|
+
/** Owner-scoped temporary thumbnail created by the Meta staging upload flow. */
|
|
4073
|
+
thumbnailAssetId: z31.string().uuid().optional()
|
|
3835
4074
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
3836
4075
|
/** Meta Ad Account ID */
|
|
3837
4076
|
adAccountId: MetaAdAccountId,
|
|
4077
|
+
/** Meta Business Portfolio selected by the user (optional for legacy callers). */
|
|
4078
|
+
portfolioId: z31.string().min(1).optional(),
|
|
4079
|
+
/** Facebook Page selected by the user (optional for legacy callers). */
|
|
4080
|
+
pageId: z31.string().min(1).optional(),
|
|
3838
4081
|
/** Optional Meta Pixel ID */
|
|
3839
4082
|
pixelId: z31.string().optional(),
|
|
3840
4083
|
/** Landing page URL (required for landing destinations; omitted for whatsapp) */
|
|
@@ -3854,6 +4097,30 @@ var UploadToMetaRequest = z31.object({
|
|
|
3854
4097
|
message: "landingUrl is required for landing destinations"
|
|
3855
4098
|
});
|
|
3856
4099
|
}
|
|
4100
|
+
value.creatives.forEach((creative, index) => {
|
|
4101
|
+
if (Boolean(creative.mediaUrl) === Boolean(creative.mediaAssetId)) {
|
|
4102
|
+
ctx.addIssue({
|
|
4103
|
+
code: z31.ZodIssueCode.custom,
|
|
4104
|
+
path: [
|
|
4105
|
+
"creatives",
|
|
4106
|
+
index,
|
|
4107
|
+
"mediaUrl"
|
|
4108
|
+
],
|
|
4109
|
+
message: "Provide exactly one of mediaUrl or mediaAssetId"
|
|
4110
|
+
});
|
|
4111
|
+
}
|
|
4112
|
+
if (creative.thumbnailUrl && creative.thumbnailAssetId) {
|
|
4113
|
+
ctx.addIssue({
|
|
4114
|
+
code: z31.ZodIssueCode.custom,
|
|
4115
|
+
path: [
|
|
4116
|
+
"creatives",
|
|
4117
|
+
index,
|
|
4118
|
+
"thumbnailUrl"
|
|
4119
|
+
],
|
|
4120
|
+
message: "Provide at most one of thumbnailUrl or thumbnailAssetId"
|
|
4121
|
+
});
|
|
4122
|
+
}
|
|
4123
|
+
});
|
|
3857
4124
|
});
|
|
3858
4125
|
var CampaignLifetimeMetricsSchema = z31.object({
|
|
3859
4126
|
spend: z31.number().nonnegative(),
|
|
@@ -3907,8 +4174,32 @@ var FacebookAdAccountSchema = z32.object({
|
|
|
3907
4174
|
source: z32.enum([
|
|
3908
4175
|
"business",
|
|
3909
4176
|
"personal"
|
|
4177
|
+
]),
|
|
4178
|
+
/** Meta Business Portfolio (or the synthetic `personal` group). */
|
|
4179
|
+
portfolioId: z32.string().min(1),
|
|
4180
|
+
portfolioName: z32.string().min(1),
|
|
4181
|
+
portfolioType: z32.enum([
|
|
4182
|
+
"business",
|
|
4183
|
+
"personal"
|
|
3910
4184
|
])
|
|
3911
4185
|
});
|
|
4186
|
+
var FacebookMetaPortfolioSchema = z32.object({
|
|
4187
|
+
id: z32.string().min(1),
|
|
4188
|
+
name: z32.string().min(1),
|
|
4189
|
+
type: z32.enum([
|
|
4190
|
+
"business",
|
|
4191
|
+
"personal"
|
|
4192
|
+
])
|
|
4193
|
+
});
|
|
4194
|
+
var FacebookMetaPageSchema = z32.object({
|
|
4195
|
+
id: z32.string().min(1),
|
|
4196
|
+
name: z32.string().min(1)
|
|
4197
|
+
});
|
|
4198
|
+
var FacebookMetaDestinationsResponseSchema = z32.object({
|
|
4199
|
+
portfolios: z32.array(FacebookMetaPortfolioSchema),
|
|
4200
|
+
adAccounts: z32.array(FacebookAdAccountSchema),
|
|
4201
|
+
pages: z32.array(FacebookMetaPageSchema)
|
|
4202
|
+
});
|
|
3912
4203
|
var MetaCampaignOverridesSchema = z32.object({
|
|
3913
4204
|
dailyBudgetUsd: z32.number().positive().optional(),
|
|
3914
4205
|
targetCountries: z32.array(z32.string().length(2)).max(1).optional(),
|
|
@@ -3917,6 +4208,8 @@ var MetaCampaignOverridesSchema = z32.object({
|
|
|
3917
4208
|
});
|
|
3918
4209
|
var FacebookCreateCampaignBodySchema = z32.object({
|
|
3919
4210
|
adAccountId: MetaAdAccountId,
|
|
4211
|
+
portfolioId: z32.string().min(1).optional(),
|
|
4212
|
+
pageId: z32.string().min(1).optional(),
|
|
3920
4213
|
pixelId: z32.string().optional(),
|
|
3921
4214
|
landingUrl: HttpUrl2.optional(),
|
|
3922
4215
|
campaignConfig: MetaCampaignConfig.optional(),
|
|
@@ -3947,6 +4240,8 @@ var FacebookCampaignStatusResponseSchema = z32.object({
|
|
|
3947
4240
|
var FacebookCreateCampaignResponseSchema = z32.object({
|
|
3948
4241
|
campaignId: z32.string(),
|
|
3949
4242
|
campaignName: z32.string(),
|
|
4243
|
+
portfolioId: z32.string().optional(),
|
|
4244
|
+
portfolioName: z32.string().optional(),
|
|
3950
4245
|
pageId: z32.string().optional(),
|
|
3951
4246
|
pageName: z32.string().optional(),
|
|
3952
4247
|
// Backward-compatible single ad fields (optional)
|
|
@@ -3989,11 +4284,15 @@ var FacebookCreateCampaignFromCreativesBodySchema = z32.object({
|
|
|
3989
4284
|
title: z32.string().max(120),
|
|
3990
4285
|
description: z32.string().max(500),
|
|
3991
4286
|
cta: MetaCtaType,
|
|
3992
|
-
mediaUrl: HttpUrl2,
|
|
4287
|
+
mediaUrl: HttpUrl2.optional(),
|
|
4288
|
+
mediaAssetId: z32.string().uuid().optional(),
|
|
3993
4289
|
thumbnailUrl: HttpUrl2.optional(),
|
|
4290
|
+
thumbnailAssetId: z32.string().uuid().optional(),
|
|
3994
4291
|
creativeId: z32.string().min(1).optional()
|
|
3995
4292
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
3996
4293
|
adAccountId: MetaAdAccountId,
|
|
4294
|
+
portfolioId: z32.string().min(1).optional(),
|
|
4295
|
+
pageId: z32.string().min(1).optional(),
|
|
3997
4296
|
pixelId: z32.string().optional(),
|
|
3998
4297
|
/** Required unless campaignConfig.destination is 'whatsapp' (see superRefine below). */
|
|
3999
4298
|
landingUrl: HttpUrl2.optional(),
|
|
@@ -4013,6 +4312,30 @@ var FacebookCreateCampaignFromCreativesBodySchema = z32.object({
|
|
|
4013
4312
|
message: "landingUrl is required unless destination is whatsapp"
|
|
4014
4313
|
});
|
|
4015
4314
|
}
|
|
4315
|
+
value.creatives.forEach((creative, index) => {
|
|
4316
|
+
if (Boolean(creative.mediaUrl) === Boolean(creative.mediaAssetId)) {
|
|
4317
|
+
ctx.addIssue({
|
|
4318
|
+
code: z32.ZodIssueCode.custom,
|
|
4319
|
+
path: [
|
|
4320
|
+
"creatives",
|
|
4321
|
+
index,
|
|
4322
|
+
"mediaUrl"
|
|
4323
|
+
],
|
|
4324
|
+
message: "Provide exactly one of mediaUrl or mediaAssetId"
|
|
4325
|
+
});
|
|
4326
|
+
}
|
|
4327
|
+
if (creative.thumbnailUrl && creative.thumbnailAssetId) {
|
|
4328
|
+
ctx.addIssue({
|
|
4329
|
+
code: z32.ZodIssueCode.custom,
|
|
4330
|
+
path: [
|
|
4331
|
+
"creatives",
|
|
4332
|
+
index,
|
|
4333
|
+
"thumbnailUrl"
|
|
4334
|
+
],
|
|
4335
|
+
message: "Provide at most one of thumbnailUrl or thumbnailAssetId"
|
|
4336
|
+
});
|
|
4337
|
+
}
|
|
4338
|
+
});
|
|
4016
4339
|
if (value.campaignConfig?.destination?.type === "landing" && value.landingUrl && value.campaignConfig.destination.landingUrl !== value.landingUrl) {
|
|
4017
4340
|
ctx.addIssue({
|
|
4018
4341
|
code: z32.ZodIssueCode.custom,
|
|
@@ -4090,8 +4413,10 @@ var FacebookTaskCreativeSourceSchema = z32.object({
|
|
|
4090
4413
|
});
|
|
4091
4414
|
var FacebookMediaCreativeSourceSchema = z32.object({
|
|
4092
4415
|
type: z32.literal("media"),
|
|
4093
|
-
mediaUrl: HttpUrl2,
|
|
4094
|
-
|
|
4416
|
+
mediaUrl: HttpUrl2.optional(),
|
|
4417
|
+
mediaAssetId: z32.string().uuid().optional(),
|
|
4418
|
+
thumbnailUrl: HttpUrl2.optional(),
|
|
4419
|
+
thumbnailAssetId: z32.string().uuid().optional()
|
|
4095
4420
|
});
|
|
4096
4421
|
var FacebookCreativeSourceSchema = z32.discriminatedUnion("type", [
|
|
4097
4422
|
FacebookTaskCreativeSourceSchema,
|
|
@@ -4110,6 +4435,9 @@ var FacebookCreateCampaignFromTasksBodySchema = z32.object({
|
|
|
4110
4435
|
creativeId: z32.string().min(1).optional()
|
|
4111
4436
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
4112
4437
|
adAccountId: MetaAdAccountId,
|
|
4438
|
+
/** Explicit destination is required for MCP/remote creative publishing. */
|
|
4439
|
+
portfolioId: z32.string().min(1),
|
|
4440
|
+
pageId: z32.string().min(1),
|
|
4113
4441
|
pixelId: z32.string().optional(),
|
|
4114
4442
|
landingUrl: HttpUrl2.optional(),
|
|
4115
4443
|
targetCountry: z32.string().length(2).default("US"),
|
|
@@ -4126,6 +4454,33 @@ var FacebookCreateCampaignFromTasksBodySchema = z32.object({
|
|
|
4126
4454
|
message: "landingUrl is required unless destination is whatsapp"
|
|
4127
4455
|
});
|
|
4128
4456
|
}
|
|
4457
|
+
value.creatives.forEach((creative, index) => {
|
|
4458
|
+
if (creative.source.type !== "media") return;
|
|
4459
|
+
if (Boolean(creative.source.mediaUrl) === Boolean(creative.source.mediaAssetId)) {
|
|
4460
|
+
ctx.addIssue({
|
|
4461
|
+
code: z32.ZodIssueCode.custom,
|
|
4462
|
+
path: [
|
|
4463
|
+
"creatives",
|
|
4464
|
+
index,
|
|
4465
|
+
"source",
|
|
4466
|
+
"mediaUrl"
|
|
4467
|
+
],
|
|
4468
|
+
message: "Provide exactly one of mediaUrl or mediaAssetId"
|
|
4469
|
+
});
|
|
4470
|
+
}
|
|
4471
|
+
if (creative.source.thumbnailUrl && creative.source.thumbnailAssetId) {
|
|
4472
|
+
ctx.addIssue({
|
|
4473
|
+
code: z32.ZodIssueCode.custom,
|
|
4474
|
+
path: [
|
|
4475
|
+
"creatives",
|
|
4476
|
+
index,
|
|
4477
|
+
"source",
|
|
4478
|
+
"thumbnailUrl"
|
|
4479
|
+
],
|
|
4480
|
+
message: "Provide at most one of thumbnailUrl or thumbnailAssetId"
|
|
4481
|
+
});
|
|
4482
|
+
}
|
|
4483
|
+
});
|
|
4129
4484
|
});
|
|
4130
4485
|
|
|
4131
4486
|
// ../../packages/types/src/tiktok.ts
|
|
@@ -4692,6 +5047,13 @@ var DEFAULT_PRICING = {
|
|
|
4692
5047
|
ourCostCents: 3,
|
|
4693
5048
|
userPriceCents: 6
|
|
4694
5049
|
},
|
|
5050
|
+
// Custom actor identity photos (gpt-image-2 @ 1K, 4 candidates per attempt).
|
|
5051
|
+
// Recorded once per PRODUCED candidate. Without this row billingService.record
|
|
5052
|
+
// logs "No pricing rule found; skipping billing record" and the whole grid is free.
|
|
5053
|
+
"kie:image-generation:actor": {
|
|
5054
|
+
ourCostCents: 3,
|
|
5055
|
+
userPriceCents: 6
|
|
5056
|
+
},
|
|
4695
5057
|
"kie:image-generation:seedream-5-lite-text-to-image": {
|
|
4696
5058
|
ourCostCents: 3,
|
|
4697
5059
|
userPriceCents: 6
|
|
@@ -5628,6 +5990,32 @@ var DEFAULT_PRICING = {
|
|
|
5628
5990
|
ourCostCents: 1.5,
|
|
5629
5991
|
userPriceCents: 3
|
|
5630
5992
|
},
|
|
5993
|
+
// Frame0 per-person anatomy gate. Priced PER PANEL, like the inventory above and for the same
|
|
5994
|
+
// reason: it is one vision call per rendered panel, so quoting it per VIDEO would under-quote
|
|
5995
|
+
// by the panel count (~10x). Same single-image, short-answer shape as the inventory, so it
|
|
5996
|
+
// carries the same rate until measured otherwise.
|
|
5997
|
+
"openrouter:rapid-panel-anatomy-gate": {
|
|
5998
|
+
ourCostCents: 1.5,
|
|
5999
|
+
userPriceCents: 3
|
|
6000
|
+
},
|
|
6001
|
+
// Plan hand-budget audit: one TEXT call per shot (no image), so materially cheaper than the
|
|
6002
|
+
// vision passes above. Billed at the call site, therefore it MUST be quoted — an unquoted paid
|
|
6003
|
+
// call is the failure this repo already has on record.
|
|
6004
|
+
"openrouter:plan-hand-budget-audit": {
|
|
6005
|
+
ourCostCents: 0.3,
|
|
6006
|
+
userPriceCents: 0.6
|
|
6007
|
+
},
|
|
6008
|
+
// Hand-budget REPAIR: the rewrite that fires only on a shot whose brief demands more hand jobs
|
|
6009
|
+
// than its cast has hands. One TEXT call, same shape as the audit above but with the brief on
|
|
6010
|
+
// both sides (prompt in, prose out), so twice the rate rather than the audit's.
|
|
6011
|
+
//
|
|
6012
|
+
// Kept on its OWN key rather than folded into the audit for the reason the frame-inventory delta
|
|
6013
|
+
// is: the audit runs on every shot and the repair on a minority, so one blended rate would price
|
|
6014
|
+
// both wrong and neither line would be auditable against the ledger.
|
|
6015
|
+
"openrouter:rapid-hand-budget-repair": {
|
|
6016
|
+
ourCostCents: 0.6,
|
|
6017
|
+
userPriceCents: 1.2
|
|
6018
|
+
},
|
|
5631
6019
|
// Unit = one shot-action flatten, once per video, quoted ONLY when frame inventories are on.
|
|
5632
6020
|
// Carrying ~10 inventories as text raised that call from a 1.7078¢ production median to a
|
|
5633
6021
|
// measured 3.1481¢ / 3.4551¢. This is the difference, kept on its own key so the base rate above
|
|
@@ -7058,6 +7446,7 @@ var MAX_SEGMENT_SECONDS = 3;
|
|
|
7058
7446
|
var VideoEditStatusSchema = z38.enum([
|
|
7059
7447
|
"pending",
|
|
7060
7448
|
"processing",
|
|
7449
|
+
"assembling",
|
|
7061
7450
|
"ready",
|
|
7062
7451
|
"accepted",
|
|
7063
7452
|
"discarded",
|
|
@@ -7085,6 +7474,13 @@ var VideoEditModeSchema = z38.enum([
|
|
|
7085
7474
|
"wan_edit",
|
|
7086
7475
|
"regenerate"
|
|
7087
7476
|
]);
|
|
7477
|
+
var VideoRegenerationEngineSchema = z38.enum([
|
|
7478
|
+
"free_text",
|
|
7479
|
+
"free_image",
|
|
7480
|
+
"single_call",
|
|
7481
|
+
"multi_shot"
|
|
7482
|
+
]);
|
|
7483
|
+
var VideoRegenerationRouteFingerprintSchema = z38.string().trim().min(1).max(128);
|
|
7088
7484
|
var RegenerateEditRequestSchema = z38.object({
|
|
7089
7485
|
angleKey: z38.string().min(1),
|
|
7090
7486
|
comment: z38.string().min(1).max(2e3)
|
|
@@ -7463,6 +7859,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7463
7859
|
confirmation: "credit_consuming",
|
|
7464
7860
|
schemaHash: "bab794a9700a8d3f42b97f444a776e7ed4dcd833038c52588f951b50970ff3fa"
|
|
7465
7861
|
},
|
|
7862
|
+
{
|
|
7863
|
+
name: "check_meta_media_upload",
|
|
7864
|
+
confirmation: "none",
|
|
7865
|
+
schemaHash: "62b1af1d796d9466f8926afc5e6d18c4ddeccd5b0269ca7b21c2f6ebcfd2a945"
|
|
7866
|
+
},
|
|
7466
7867
|
{
|
|
7467
7868
|
name: "cost_dropshipping_project",
|
|
7468
7869
|
confirmation: "state_change",
|
|
@@ -7481,7 +7882,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7481
7882
|
{
|
|
7482
7883
|
name: "create_product_task",
|
|
7483
7884
|
confirmation: "credit_consuming",
|
|
7484
|
-
schemaHash: "
|
|
7885
|
+
schemaHash: "5c5fe82a365c62e44265e79a4100a06820dbbdb2e0ddc296cd066eb2255b1d62"
|
|
7485
7886
|
},
|
|
7486
7887
|
{
|
|
7487
7888
|
name: "create_service_video",
|
|
@@ -7496,7 +7897,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7496
7897
|
{
|
|
7497
7898
|
name: "edit_task_draft",
|
|
7498
7899
|
confirmation: "state_change",
|
|
7499
|
-
schemaHash: "
|
|
7900
|
+
schemaHash: "1c9343bdce51aac36ec6254557c8a904037746523168527f012b4c5b6361e98e"
|
|
7500
7901
|
},
|
|
7501
7902
|
{
|
|
7502
7903
|
name: "estimate_creative_variant",
|
|
@@ -7526,7 +7927,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7526
7927
|
{
|
|
7527
7928
|
name: "generate_video_creative",
|
|
7528
7929
|
confirmation: "credit_consuming",
|
|
7529
|
-
schemaHash: "
|
|
7930
|
+
schemaHash: "04e12ef523e4064b0c204d110baad35d414b605068087c625badf40c46429c8a"
|
|
7530
7931
|
},
|
|
7531
7932
|
{
|
|
7532
7933
|
name: "get_meta_campaign_status",
|
|
@@ -7558,11 +7959,21 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7558
7959
|
confirmation: "none",
|
|
7559
7960
|
schemaHash: "0e7a1cb5ad5c6681473df5689336edcd7a63b8d68853359982e44bfd98a65db8"
|
|
7560
7961
|
},
|
|
7962
|
+
{
|
|
7963
|
+
name: "list_actors",
|
|
7964
|
+
confirmation: "none",
|
|
7965
|
+
schemaHash: "7a014b717a77aac971ea0ab5c0ff47bb13e6cae7ef9442d9dedb15ce36381b15"
|
|
7966
|
+
},
|
|
7561
7967
|
{
|
|
7562
7968
|
name: "list_meta_ad_accounts",
|
|
7563
7969
|
confirmation: "none",
|
|
7564
7970
|
schemaHash: "7a014b717a77aac971ea0ab5c0ff47bb13e6cae7ef9442d9dedb15ce36381b15"
|
|
7565
7971
|
},
|
|
7972
|
+
{
|
|
7973
|
+
name: "list_meta_destinations",
|
|
7974
|
+
confirmation: "none",
|
|
7975
|
+
schemaHash: "7a014b717a77aac971ea0ab5c0ff47bb13e6cae7ef9442d9dedb15ce36381b15"
|
|
7976
|
+
},
|
|
7566
7977
|
{
|
|
7567
7978
|
name: "list_product_research_runs",
|
|
7568
7979
|
confirmation: "none",
|
|
@@ -7591,12 +8002,12 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7591
8002
|
{
|
|
7592
8003
|
name: "publish_creatives_to_meta",
|
|
7593
8004
|
confirmation: "external_mutation",
|
|
7594
|
-
schemaHash: "
|
|
8005
|
+
schemaHash: "59b3d6d5ce25812fee3382b0b1c0cf5e07beb1063048dd0a0d0633686cf29da5"
|
|
7595
8006
|
},
|
|
7596
8007
|
{
|
|
7597
8008
|
name: "publish_to_meta",
|
|
7598
8009
|
confirmation: "external_mutation",
|
|
7599
|
-
schemaHash: "
|
|
8010
|
+
schemaHash: "b507735a1fe13657355cf93163b6ead0db151984e6f2702dcdbf76b4037133ca"
|
|
7600
8011
|
},
|
|
7601
8012
|
{
|
|
7602
8013
|
name: "publish_to_shopify",
|
|
@@ -7613,6 +8024,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7613
8024
|
confirmation: "credit_consuming",
|
|
7614
8025
|
schemaHash: "745687cb41f904e293105f93bbe6bc0471f63fe097ab8f85d3df8d9833084844"
|
|
7615
8026
|
},
|
|
8027
|
+
{
|
|
8028
|
+
name: "request_meta_media_upload",
|
|
8029
|
+
confirmation: "none",
|
|
8030
|
+
schemaHash: "32203d205f414c386ee6ab33e265eb2708c108fca4169d4c12e99d7a4109a132"
|
|
8031
|
+
},
|
|
7616
8032
|
{
|
|
7617
8033
|
name: "resolve_pending_action",
|
|
7618
8034
|
confirmation: "credit_consuming",
|
|
@@ -7626,6 +8042,150 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7626
8042
|
];
|
|
7627
8043
|
var PRODUCTMAKER_MCP_CAPABILITY_NAMES = PRODUCTMAKER_MCP_CAPABILITIES.map((capability) => capability.name);
|
|
7628
8044
|
|
|
8045
|
+
// ../../packages/types/src/actor.ts
|
|
8046
|
+
import { z as z42 } from "zod";
|
|
8047
|
+
var ActorStatusSchema = z42.enum([
|
|
8048
|
+
"draft",
|
|
8049
|
+
"generating",
|
|
8050
|
+
"ready",
|
|
8051
|
+
"failed"
|
|
8052
|
+
]);
|
|
8053
|
+
var ACTOR_NAME_MAX_CHARS = 60;
|
|
8054
|
+
var ACTOR_DESCRIPTION_MAX_CHARS = 600;
|
|
8055
|
+
var ACTOR_CANDIDATE_COUNT = 4;
|
|
8056
|
+
var ActorGenderSchema = z42.enum([
|
|
8057
|
+
"female",
|
|
8058
|
+
"male",
|
|
8059
|
+
"non_binary"
|
|
8060
|
+
]);
|
|
8061
|
+
var ActorSchema = z42.object({
|
|
8062
|
+
id: z42.string(),
|
|
8063
|
+
name: z42.string().min(1).max(ACTOR_NAME_MAX_CHARS),
|
|
8064
|
+
description: z42.string().max(ACTOR_DESCRIPTION_MAX_CHARS),
|
|
8065
|
+
gender: ActorGenderSchema,
|
|
8066
|
+
voiceId: z42.string().nullable().optional(),
|
|
8067
|
+
brandId: z42.string().nullable().optional(),
|
|
8068
|
+
status: ActorStatusSchema,
|
|
8069
|
+
/**
|
|
8070
|
+
* A candidate-grid run is in flight RIGHT NOW.
|
|
8071
|
+
*
|
|
8072
|
+
* Separate from `status` because the two answer different questions:
|
|
8073
|
+
* `status` is the PUBLISHED state (a personaje being regenerated stays
|
|
8074
|
+
* `ready`, so it keeps serving the renders that already reference it), and
|
|
8075
|
+
* this flag is the live one the wizard polls. It is derived from a lock
|
|
8076
|
+
* timestamp with a TTL, so an orphaned run reads as `false` instead of
|
|
8077
|
+
* pinning the personaje forever.
|
|
8078
|
+
*/
|
|
8079
|
+
generating: z42.boolean().optional(),
|
|
8080
|
+
photoUrl: z42.string().url().nullable().optional(),
|
|
8081
|
+
avatarUrl: z42.string().url().nullable().optional(),
|
|
8082
|
+
candidateUrls: z42.array(z42.string().url()).optional(),
|
|
8083
|
+
/** Indices of candidateUrls that failed the anatomy check (Task 6). */
|
|
8084
|
+
flaggedCandidateIndices: z42.array(z42.number().int()).optional()
|
|
8085
|
+
});
|
|
8086
|
+
var CreateActorRequest = z42.object({
|
|
8087
|
+
name: z42.string().min(1).max(ACTOR_NAME_MAX_CHARS),
|
|
8088
|
+
description: z42.string().max(ACTOR_DESCRIPTION_MAX_CHARS).default(""),
|
|
8089
|
+
gender: ActorGenderSchema,
|
|
8090
|
+
voiceId: z42.string().optional(),
|
|
8091
|
+
brandId: z42.string().optional(),
|
|
8092
|
+
sourcePhotoKey: z42.string().optional()
|
|
8093
|
+
}).refine((v) => v.description.trim().length > 0 || !!v.sourcePhotoKey, {
|
|
8094
|
+
message: "Se necesita una descripci\xF3n o una foto"
|
|
8095
|
+
});
|
|
8096
|
+
var UpdateActorRequest = z42.object({
|
|
8097
|
+
name: z42.string().min(1).max(ACTOR_NAME_MAX_CHARS).optional(),
|
|
8098
|
+
description: z42.string().max(ACTOR_DESCRIPTION_MAX_CHARS).optional(),
|
|
8099
|
+
gender: ActorGenderSchema.optional(),
|
|
8100
|
+
voiceId: z42.string().nullable().optional(),
|
|
8101
|
+
brandId: z42.string().nullable().optional()
|
|
8102
|
+
});
|
|
8103
|
+
var ApproveActorRequest = z42.object({
|
|
8104
|
+
candidateIndex: z42.number().int().min(0).max(ACTOR_CANDIDATE_COUNT - 1)
|
|
8105
|
+
});
|
|
8106
|
+
|
|
8107
|
+
// ../../packages/types/src/referral-dashboard.ts
|
|
8108
|
+
import { z as z43 } from "zod";
|
|
8109
|
+
var REFERRAL_DASHBOARD_RAW_STATUSES = [
|
|
8110
|
+
"ACTIVE",
|
|
8111
|
+
"PENDING",
|
|
8112
|
+
"GRACE",
|
|
8113
|
+
"CANCELLED",
|
|
8114
|
+
"EXPIRED"
|
|
8115
|
+
];
|
|
8116
|
+
var REFERRAL_DASHBOARD_STATES = [
|
|
8117
|
+
"active",
|
|
8118
|
+
"pending",
|
|
8119
|
+
"inactive",
|
|
8120
|
+
"no_plan"
|
|
8121
|
+
];
|
|
8122
|
+
var REFERRAL_DASHBOARD_PLAN_KINDS = [
|
|
8123
|
+
"current",
|
|
8124
|
+
"last",
|
|
8125
|
+
"none"
|
|
8126
|
+
];
|
|
8127
|
+
var ReferralDashboardRawStatusSchema = z43.enum(REFERRAL_DASHBOARD_RAW_STATUSES);
|
|
8128
|
+
var ReferralDashboardStateSchema = z43.enum(REFERRAL_DASHBOARD_STATES);
|
|
8129
|
+
var ReferralDashboardPlanKindSchema = z43.enum(REFERRAL_DASHBOARD_PLAN_KINDS);
|
|
8130
|
+
var ReferralDashboardStatusSchema = z43.enum([
|
|
8131
|
+
"all",
|
|
8132
|
+
"active",
|
|
8133
|
+
"pending",
|
|
8134
|
+
"inactive"
|
|
8135
|
+
]).default("all");
|
|
8136
|
+
var ReferralDashboardPlanSchema = z43.string().trim().min(1).max(100).optional();
|
|
8137
|
+
var ReferralDashboardFiltersSchema = z43.object({
|
|
8138
|
+
status: ReferralDashboardStatusSchema,
|
|
8139
|
+
plan: ReferralDashboardPlanSchema
|
|
8140
|
+
});
|
|
8141
|
+
var ReferralDashboardSubscriptionSchema = z43.object({
|
|
8142
|
+
planName: z43.string().nullable(),
|
|
8143
|
+
planKind: ReferralDashboardPlanKindSchema,
|
|
8144
|
+
rawStatus: ReferralDashboardRawStatusSchema.nullable(),
|
|
8145
|
+
state: ReferralDashboardStateSchema,
|
|
8146
|
+
currentPeriodEnd: z43.string().datetime({
|
|
8147
|
+
offset: true
|
|
8148
|
+
}).nullable(),
|
|
8149
|
+
cancelAtPeriodEnd: z43.boolean()
|
|
8150
|
+
});
|
|
8151
|
+
var ReferralDashboardReferralSchema = z43.object({
|
|
8152
|
+
id: z43.string().min(1),
|
|
8153
|
+
registeredAt: z43.string().datetime({
|
|
8154
|
+
offset: true
|
|
8155
|
+
}),
|
|
8156
|
+
// Kept in the response for compatibility with existing referral API clients.
|
|
8157
|
+
// The referrer dashboard intentionally does not render these legacy fields.
|
|
8158
|
+
expiresAt: z43.string().datetime({
|
|
8159
|
+
offset: true
|
|
8160
|
+
}).nullable().optional(),
|
|
8161
|
+
status: z43.string().min(1).optional(),
|
|
8162
|
+
referredUser: z43.object({
|
|
8163
|
+
email: z43.string().nullable()
|
|
8164
|
+
}),
|
|
8165
|
+
subscription: ReferralDashboardSubscriptionSchema
|
|
8166
|
+
});
|
|
8167
|
+
var ReferralDashboardReferralsPageSchema = z43.object({
|
|
8168
|
+
referrals: z43.array(ReferralDashboardReferralSchema),
|
|
8169
|
+
total: z43.number().int().nonnegative()
|
|
8170
|
+
});
|
|
8171
|
+
var ReferralDashboardPlanBreakdownSchema = z43.object({
|
|
8172
|
+
planName: z43.string().nullable(),
|
|
8173
|
+
count: z43.number().int().nonnegative()
|
|
8174
|
+
});
|
|
8175
|
+
var ReferralDashboardMonthlyRegistrationSchema = z43.object({
|
|
8176
|
+
month: z43.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/),
|
|
8177
|
+
count: z43.number().int().nonnegative()
|
|
8178
|
+
});
|
|
8179
|
+
var ReferralDashboardOverviewSchema = z43.object({
|
|
8180
|
+
totalReferredUsers: z43.number().int().nonnegative(),
|
|
8181
|
+
activeUsers: z43.number().int().nonnegative(),
|
|
8182
|
+
pendingUsers: z43.number().int().nonnegative(),
|
|
8183
|
+
inactiveUsers: z43.number().int().nonnegative(),
|
|
8184
|
+
noPlanUsers: z43.number().int().nonnegative(),
|
|
8185
|
+
planBreakdown: z43.array(ReferralDashboardPlanBreakdownSchema),
|
|
8186
|
+
monthlyRegistrations: z43.array(ReferralDashboardMonthlyRegistrationSchema).refine((months) => months.length === 6, "Expected six monthly registration buckets")
|
|
8187
|
+
});
|
|
8188
|
+
|
|
7629
8189
|
// src/resolve-media.ts
|
|
7630
8190
|
var MAX_MEDIA_BYTES = CAMPAIGN_LIMITS.MAX_FILE_SIZE_BYTES;
|
|
7631
8191
|
var MEDIA_FORMATS = [
|
|
@@ -7865,10 +8425,10 @@ async function resolveMediaRef(ref, opts) {
|
|
|
7865
8425
|
__name(resolveMediaRef, "resolveMediaRef");
|
|
7866
8426
|
|
|
7867
8427
|
// src/tools/shared.ts
|
|
7868
|
-
import { z as
|
|
7869
|
-
var Lang =
|
|
7870
|
-
var Country =
|
|
7871
|
-
var LandingStrategyEnum =
|
|
8428
|
+
import { z as z44 } from "zod";
|
|
8429
|
+
var Lang = z44.string().min(2).max(10);
|
|
8430
|
+
var Country = z44.string().min(2).max(3);
|
|
8431
|
+
var LandingStrategyEnum = z44.enum([
|
|
7872
8432
|
"auto",
|
|
7873
8433
|
"classic",
|
|
7874
8434
|
"pain_solution",
|
|
@@ -7877,12 +8437,12 @@ var LandingStrategyEnum = z42.enum([
|
|
|
7877
8437
|
"offer_value",
|
|
7878
8438
|
"lifestyle_identity"
|
|
7879
8439
|
]);
|
|
7880
|
-
var LandingImageFormatEnum =
|
|
8440
|
+
var LandingImageFormatEnum = z44.enum([
|
|
7881
8441
|
"9:16",
|
|
7882
8442
|
"4:5",
|
|
7883
8443
|
"1:1"
|
|
7884
8444
|
]);
|
|
7885
|
-
var VideoPresetEnum =
|
|
8445
|
+
var VideoPresetEnum = z44.enum([
|
|
7886
8446
|
"auto",
|
|
7887
8447
|
"ugc_dynamic",
|
|
7888
8448
|
"ugc_how_to",
|
|
@@ -7895,7 +8455,7 @@ var VideoPresetEnum = z42.enum([
|
|
|
7895
8455
|
"pixar_problem",
|
|
7896
8456
|
"pixar_sassy"
|
|
7897
8457
|
]);
|
|
7898
|
-
var TaskCreativeVariantVideoPresetEnum =
|
|
8458
|
+
var TaskCreativeVariantVideoPresetEnum = z44.enum([
|
|
7899
8459
|
"auto",
|
|
7900
8460
|
"ugc_dynamic",
|
|
7901
8461
|
"ugc_how_to",
|
|
@@ -7966,7 +8526,7 @@ function resolveVideoPreset(preset) {
|
|
|
7966
8526
|
return VIDEO_PRESET_RESOLUTION[preset] ?? null;
|
|
7967
8527
|
}
|
|
7968
8528
|
__name(resolveVideoPreset, "resolveVideoPreset");
|
|
7969
|
-
var VideoTierEnum =
|
|
8529
|
+
var VideoTierEnum = z44.enum([
|
|
7970
8530
|
"basic"
|
|
7971
8531
|
]);
|
|
7972
8532
|
function resolveVideoTier(_tier) {
|
|
@@ -7977,27 +8537,21 @@ function resolveWireVideoTier(tier, durationSeconds) {
|
|
|
7977
8537
|
return durationSeconds === 30 ? "standard" : resolveVideoTier(tier);
|
|
7978
8538
|
}
|
|
7979
8539
|
__name(resolveWireVideoTier, "resolveWireVideoTier");
|
|
7980
|
-
function assertVideoPresetDurationCompatibility(videoPreset, durationSeconds) {
|
|
7981
|
-
if (videoPreset === "problem_solution" && durationSeconds !== 30) {
|
|
7982
|
-
throw new Error("Problema y Soluci\xF3n solo est\xE1 disponible en videos Multiescena de 30 segundos.");
|
|
7983
|
-
}
|
|
7984
|
-
}
|
|
7985
|
-
__name(assertVideoPresetDurationCompatibility, "assertVideoPresetDurationCompatibility");
|
|
7986
8540
|
function resolveVideoFormatForDuration(durationSeconds) {
|
|
7987
8541
|
return durationSeconds === 30 ? "double_30s" : "single_15s";
|
|
7988
8542
|
}
|
|
7989
8543
|
__name(resolveVideoFormatForDuration, "resolveVideoFormatForDuration");
|
|
7990
|
-
var VideoResolutionEnum =
|
|
8544
|
+
var VideoResolutionEnum = z44.enum([
|
|
7991
8545
|
"480p",
|
|
7992
8546
|
"720p"
|
|
7993
8547
|
]);
|
|
7994
|
-
var VideoStyleEnum =
|
|
8548
|
+
var VideoStyleEnum = z44.enum([
|
|
7995
8549
|
"ugc",
|
|
7996
8550
|
"cartoon_3d",
|
|
7997
8551
|
"product_only",
|
|
7998
8552
|
"ai_dynamic"
|
|
7999
8553
|
]);
|
|
8000
|
-
var NarrativeStyleEnum =
|
|
8554
|
+
var NarrativeStyleEnum = z44.enum([
|
|
8001
8555
|
"review",
|
|
8002
8556
|
"ugc_how_to",
|
|
8003
8557
|
"problem_product_cta",
|
|
@@ -8014,7 +8568,7 @@ var NarrativeStyleEnum = z42.enum([
|
|
|
8014
8568
|
"ugc_skeptic_confession",
|
|
8015
8569
|
"ugc_demo_review"
|
|
8016
8570
|
]);
|
|
8017
|
-
var PublicNarrativeStyleEnum =
|
|
8571
|
+
var PublicNarrativeStyleEnum = z44.enum([
|
|
8018
8572
|
"review",
|
|
8019
8573
|
"ugc_how_to",
|
|
8020
8574
|
"ugc_unboxing_chaos",
|
|
@@ -8026,7 +8580,7 @@ var PublicNarrativeStyleEnum = z42.enum([
|
|
|
8026
8580
|
"animated_problem_pixar",
|
|
8027
8581
|
"sassy_product_pixar"
|
|
8028
8582
|
]);
|
|
8029
|
-
var VideoModelEnum =
|
|
8583
|
+
var VideoModelEnum = z44.enum([
|
|
8030
8584
|
"veo",
|
|
8031
8585
|
"wan",
|
|
8032
8586
|
"bytedance",
|
|
@@ -8034,25 +8588,25 @@ var VideoModelEnum = z42.enum([
|
|
|
8034
8588
|
"grok",
|
|
8035
8589
|
"kling"
|
|
8036
8590
|
]);
|
|
8037
|
-
var ProductReferenceUserChoiceEnum =
|
|
8591
|
+
var ProductReferenceUserChoiceEnum = z44.enum([
|
|
8038
8592
|
"same_product",
|
|
8039
8593
|
"variants",
|
|
8040
8594
|
"different_products",
|
|
8041
8595
|
"combo_distinct"
|
|
8042
8596
|
]);
|
|
8043
|
-
var ProductReferenceCandidateGroupingEnum =
|
|
8597
|
+
var ProductReferenceCandidateGroupingEnum = z44.enum([
|
|
8044
8598
|
"same_product_same_variant",
|
|
8045
8599
|
"same_product_different_variants",
|
|
8046
8600
|
"different_products",
|
|
8047
8601
|
"unclear"
|
|
8048
8602
|
]);
|
|
8049
|
-
var GapDurationChoiceMirror =
|
|
8050
|
-
angleIndex:
|
|
8051
|
-
recommendedSec:
|
|
8052
|
-
|
|
8053
|
-
|
|
8603
|
+
var GapDurationChoiceMirror = z44.object({
|
|
8604
|
+
angleIndex: z44.number().int().nonnegative(),
|
|
8605
|
+
recommendedSec: z44.union([
|
|
8606
|
+
z44.literal(15),
|
|
8607
|
+
z44.literal(30)
|
|
8054
8608
|
]),
|
|
8055
|
-
estimatedDeltaCredits:
|
|
8609
|
+
estimatedDeltaCredits: z44.number().int().nonnegative().optional()
|
|
8056
8610
|
});
|
|
8057
8611
|
function buildSelectedImageUrlsHash(urls) {
|
|
8058
8612
|
const input = [
|
|
@@ -8066,13 +8620,13 @@ function buildSelectedImageUrlsHash(urls) {
|
|
|
8066
8620
|
return `fnv1a:${hash.toString(16).padStart(8, "0")}:${urls.length}`;
|
|
8067
8621
|
}
|
|
8068
8622
|
__name(buildSelectedImageUrlsHash, "buildSelectedImageUrlsHash");
|
|
8069
|
-
var ImageCreativeStyleEnum =
|
|
8623
|
+
var ImageCreativeStyleEnum = z44.enum([
|
|
8070
8624
|
"studio",
|
|
8071
8625
|
"floating",
|
|
8072
8626
|
"ingredients",
|
|
8073
8627
|
"in_use"
|
|
8074
8628
|
]);
|
|
8075
|
-
var ImageCreativeArchetypeEnum =
|
|
8629
|
+
var ImageCreativeArchetypeEnum = z44.enum([
|
|
8076
8630
|
"direct_response_product_ad",
|
|
8077
8631
|
"hero_outcome",
|
|
8078
8632
|
"offer_value_prop",
|
|
@@ -8081,23 +8635,23 @@ var ImageCreativeArchetypeEnum = z42.enum([
|
|
|
8081
8635
|
"problem_solution",
|
|
8082
8636
|
"lifestyle_identity"
|
|
8083
8637
|
]);
|
|
8084
|
-
var ImageCreativeVisualTreatmentEnum =
|
|
8638
|
+
var ImageCreativeVisualTreatmentEnum = z44.enum([
|
|
8085
8639
|
"performance_product_ad",
|
|
8086
8640
|
"premium_dtc_carousel"
|
|
8087
8641
|
]);
|
|
8088
|
-
var ImageCreativeAspectRatioEnum =
|
|
8642
|
+
var ImageCreativeAspectRatioEnum = z44.enum([
|
|
8089
8643
|
"1:1",
|
|
8090
8644
|
"4:5",
|
|
8091
8645
|
"9:16",
|
|
8092
8646
|
"16:9"
|
|
8093
8647
|
]);
|
|
8094
|
-
var ImageRefSchema =
|
|
8095
|
-
url:
|
|
8096
|
-
path:
|
|
8648
|
+
var ImageRefSchema = z44.object({
|
|
8649
|
+
url: z44.string().url().optional().describe('URL p\xFAblica HTTPS de la foto. Ej: una URL de Shopify CDN, Cloudinary, Imgur. \xDAsala cuando el usuario te diga "est\xE1 en X.com/foto.jpg" o cuando la foto ya est\xE9 hospedada.'),
|
|
8650
|
+
path: z44.string().min(1).optional().describe('Ruta del archivo en el computador del usuario. Ej: "/Users/maria/Desktop/foto.jpg", "~/Pictures/camiseta.png", "C:\\Users\\Maria\\Desktop\\foto.jpg". El MCP local la lee del disco y la sube. SOLO funciona cuando el MCP corre local (stdio). NO funciona en mcp.productmaker.app (HTTP) \u2014 ah\xED usa `url`.')
|
|
8097
8651
|
}).refine((v) => Boolean(v.url) !== Boolean(v.path), {
|
|
8098
8652
|
message: "each image entry must have exactly one of `url` or `path`"
|
|
8099
8653
|
});
|
|
8100
|
-
var ImagesInput =
|
|
8654
|
+
var ImagesInput = z44.array(ImageRefSchema).min(1).max(5).describe('1 a 5 fotos del MISMO producto. La PRIMERA es la principal (hero); las dem\xE1s son \xE1ngulos adicionales. NO mezcles productos distintos en una sola llamada.\n\nCada entrada tiene EXACTAMENTE uno de:\n\u2022 `url` \u2014 URL p\xFAblica HTTPS (Shopify CDN, Cloudinary). Prefi\xE9rela si la foto ya est\xE1 hospedada.\n\u2022 `path` \u2014 Ruta del archivo en el computador del usuario (ej. "/Users/maria/Desktop/foto.jpg", "~/Downloads/camiseta.png"). \xDAsala cuando el usuario te dijo d\xF3nde guard\xF3 la foto. Solo MCP local \u2014 el cliente web rechaza paths.\n\nSi el usuario te peg\xF3 la foto en el chat pero no te dio ni URL ni ruta: PREG\xDANTALE d\xF3nde la tiene guardada (Escritorio, Descargas, etc.). Si el `path` falla con IMAGE_PATH_NOT_FOUND, prueba 1-2 ubicaciones obvias alternativas (~/Desktop, ~/Downloads) antes de molestar al usuario. Tras 3 intentos fallidos, p\xEDdele la ruta completa.');
|
|
8101
8655
|
function asText(value) {
|
|
8102
8656
|
return {
|
|
8103
8657
|
content: [
|
|
@@ -8150,14 +8704,14 @@ async function submitCreative(api, opts) {
|
|
|
8150
8704
|
});
|
|
8151
8705
|
}
|
|
8152
8706
|
__name(submitCreative, "submitCreative");
|
|
8153
|
-
var ResearchTargetCountryEnum =
|
|
8707
|
+
var ResearchTargetCountryEnum = z44.enum([
|
|
8154
8708
|
"CO",
|
|
8155
8709
|
"MX",
|
|
8156
8710
|
"PE",
|
|
8157
8711
|
"EC",
|
|
8158
8712
|
"CL"
|
|
8159
8713
|
]);
|
|
8160
|
-
var ResearchCategoryEnum =
|
|
8714
|
+
var ResearchCategoryEnum = z44.enum([
|
|
8161
8715
|
"auto",
|
|
8162
8716
|
"automotive",
|
|
8163
8717
|
"kitchen",
|
|
@@ -8230,6 +8784,15 @@ var McpErrorCode = {
|
|
|
8230
8784
|
SHOPIFY_PUBLISH_FAILED: "SHOPIFY_PUBLISH_FAILED",
|
|
8231
8785
|
// Meta / Facebook Ads
|
|
8232
8786
|
META_NO_FB_PAGE: "META_NO_FB_PAGE",
|
|
8787
|
+
META_PORTFOLIO_MISMATCH: "META_PORTFOLIO_MISMATCH",
|
|
8788
|
+
META_PAGE_SELECTION_REQUIRED: "META_PAGE_SELECTION_REQUIRED",
|
|
8789
|
+
META_INVALID_PAGE: "META_INVALID_PAGE",
|
|
8790
|
+
META_STAGED_ASSET_NOT_FOUND: "META_STAGED_ASSET_NOT_FOUND",
|
|
8791
|
+
META_STAGED_ASSET_AMBIGUOUS: "META_STAGED_ASSET_AMBIGUOUS",
|
|
8792
|
+
META_STAGED_ASSET_TYPE_MISMATCH: "META_STAGED_ASSET_TYPE_MISMATCH",
|
|
8793
|
+
META_STAGED_ASSET_INVALID: "META_STAGED_ASSET_INVALID",
|
|
8794
|
+
META_PUBLICATION_RECONCILIATION_REQUIRED: "META_PUBLICATION_RECONCILIATION_REQUIRED",
|
|
8795
|
+
META_PUBLICATION_IN_PROGRESS: "META_PUBLICATION_IN_PROGRESS",
|
|
8233
8796
|
META_NO_ADS_CREATED: "META_NO_ADS_CREATED",
|
|
8234
8797
|
META_NO_ASSETS_READY: "META_NO_ASSETS_READY",
|
|
8235
8798
|
META_NO_THUMBNAIL_SOURCE: "META_NO_THUMBNAIL_SOURCE",
|
|
@@ -8282,6 +8845,15 @@ var CODE_MESSAGES = {
|
|
|
8282
8845
|
SHOPIFY_PUBLISH_FAILED: "Shopify rejected the publish. Check that the product/landing does not already exist on the store and that your Shopify plan allows additional products.",
|
|
8283
8846
|
// Meta — high-frequency
|
|
8284
8847
|
META_NO_FB_PAGE: "No Facebook Page is associated with this account. Create one at https://www.facebook.com/pages/create before publishing (Meta requires a Page to run ads).",
|
|
8848
|
+
META_PORTFOLIO_MISMATCH: "The selected Meta portfolio does not contain the selected ad account. Call `list_meta_destinations` again and choose an account from that portfolio.",
|
|
8849
|
+
META_PAGE_SELECTION_REQUIRED: "Meta has multiple Facebook Pages available. Ask the user to choose one with `list_meta_destinations` and retry with its exact `pageId`.",
|
|
8850
|
+
META_INVALID_PAGE: "The selected Facebook Page is not available for this Meta connection. Call `list_meta_destinations` again and choose an available `pageId`.",
|
|
8851
|
+
META_STAGED_ASSET_NOT_FOUND: "The temporary media upload was not found or expired. Re-upload the media and retry the Meta publication.",
|
|
8852
|
+
META_STAGED_ASSET_AMBIGUOUS: "The temporary media upload could not be uniquely resolved. Re-upload the media and retry the Meta publication.",
|
|
8853
|
+
META_STAGED_ASSET_TYPE_MISMATCH: "The temporary media type does not match the creative. Re-upload the correct image or video and retry.",
|
|
8854
|
+
META_STAGED_ASSET_INVALID: "The temporary media is empty or exceeds the server size limit. Re-upload the file and retry.",
|
|
8855
|
+
META_PUBLICATION_RECONCILIATION_REQUIRED: "A previous Meta publication with these exact inputs may already exist. Check Meta before retrying so you do not create a duplicate campaign.",
|
|
8856
|
+
META_PUBLICATION_IN_PROGRESS: "A Meta publication with these exact inputs is already in progress. Wait for it to finish, then check the campaign status.",
|
|
8285
8857
|
META_NO_ADS_CREATED: "No ads could be created. Most likely: the Meta Ads account has no payment method, no Facebook Page is linked, or the Facebook app lost permissions. Verify all three at https://business.facebook.com.",
|
|
8286
8858
|
META_NO_ASSETS_READY: "The task has no videos or ad creative ready to publish yet. Wait for generation to finish or regenerate the assets.",
|
|
8287
8859
|
META_NO_THUMBNAIL_SOURCE: "No image is available to use as the video thumbnail. Upload a product image or generate the ad creative before publishing.",
|
|
@@ -8395,6 +8967,12 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
|
|
|
8395
8967
|
McpErrorCode.CAMPAIGN_MISSING_LANDING_URL,
|
|
8396
8968
|
McpErrorCode.CAMPAIGN_RECONCILIATION_REQUIRED,
|
|
8397
8969
|
McpErrorCode.META_NO_LANDING_URL,
|
|
8970
|
+
McpErrorCode.META_PORTFOLIO_MISMATCH,
|
|
8971
|
+
McpErrorCode.META_PAGE_SELECTION_REQUIRED,
|
|
8972
|
+
McpErrorCode.META_INVALID_PAGE,
|
|
8973
|
+
McpErrorCode.META_STAGED_ASSET_NOT_FOUND,
|
|
8974
|
+
McpErrorCode.META_STAGED_ASSET_AMBIGUOUS,
|
|
8975
|
+
McpErrorCode.META_STAGED_ASSET_TYPE_MISMATCH,
|
|
8398
8976
|
McpErrorCode.META_DAILY_BUDGET_TOO_HIGH,
|
|
8399
8977
|
McpErrorCode.META_CAMPAIGN_CONFIG_INVALID,
|
|
8400
8978
|
McpErrorCode.META_WHATSAPP_INVALID_NUMBER,
|
|
@@ -8416,6 +8994,15 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
|
|
|
8416
8994
|
McpErrorCode.META_MISSING_PAYMENT,
|
|
8417
8995
|
McpErrorCode.META_ACCOUNT_RESTRICTED,
|
|
8418
8996
|
McpErrorCode.META_NO_FB_PAGE,
|
|
8997
|
+
McpErrorCode.META_PORTFOLIO_MISMATCH,
|
|
8998
|
+
McpErrorCode.META_PAGE_SELECTION_REQUIRED,
|
|
8999
|
+
McpErrorCode.META_INVALID_PAGE,
|
|
9000
|
+
McpErrorCode.META_STAGED_ASSET_NOT_FOUND,
|
|
9001
|
+
McpErrorCode.META_STAGED_ASSET_AMBIGUOUS,
|
|
9002
|
+
McpErrorCode.META_STAGED_ASSET_TYPE_MISMATCH,
|
|
9003
|
+
McpErrorCode.META_STAGED_ASSET_INVALID,
|
|
9004
|
+
McpErrorCode.META_PUBLICATION_RECONCILIATION_REQUIRED,
|
|
9005
|
+
McpErrorCode.META_PUBLICATION_IN_PROGRESS,
|
|
8419
9006
|
McpErrorCode.TIKTOK_TOKEN_EXPIRED,
|
|
8420
9007
|
McpErrorCode.TIKTOK_PERMISSION_DENIED,
|
|
8421
9008
|
McpErrorCode.TIKTOK_MISSING_PAYMENT,
|
|
@@ -8769,52 +9356,53 @@ function renderOutputsMarkdown(outputs) {
|
|
|
8769
9356
|
__name(renderOutputsMarkdown, "renderOutputsMarkdown");
|
|
8770
9357
|
|
|
8771
9358
|
// src/tools/tasks.ts
|
|
8772
|
-
var CreateProductTaskInput =
|
|
9359
|
+
var CreateProductTaskInput = z45.object({
|
|
8773
9360
|
images: ImagesInput.optional().describe("1\u20135 fotos del mismo producto. Omitir \xFAnicamente cuando projectId apunta a un proyecto propio con imagen guardada."),
|
|
8774
|
-
projectId:
|
|
8775
|
-
productUrl:
|
|
9361
|
+
projectId: z45.string().uuid().optional().describe("Opcional. Proyecto guiado propio. Permite continuar sin volver a subir la imagen guardada; nunca evita cr\xE9ditos, cuotas ni confirmaciones."),
|
|
9362
|
+
productUrl: z45.string().url().optional().describe("Opcional. URL de la p\xE1gina del producto (Shopify, AliExpress, Amazon, etc.) para enriquecer el contexto. Si se omite, la IA infiere todo desde la imagen."),
|
|
8776
9363
|
language: Lang.describe('C\xF3digo de idioma para el contenido generado. Ejemplos: "es", "en", "pt". REQUERIDO \u2014 si el usuario no lo especific\xF3, PREG\xDANTALE antes de invocar. NUNCA infieras desde el contexto del chat ni asumas "es" por default.'),
|
|
8777
9364
|
country: Country.describe('C\xF3digo ISO del pa\xEDs objetivo. Ejemplos: "CO", "MX", "US", "AR". Define moneda, m\xE9todos de pago locales y tono. REQUERIDO \u2014 si el usuario no lo especific\xF3, PREG\xDANTALE antes de invocar. NUNCA infieras desde el contexto del chat.'),
|
|
8778
|
-
price:
|
|
8779
|
-
compareAtPrice:
|
|
8780
|
-
offer:
|
|
8781
|
-
paymentMethods:
|
|
8782
|
-
productContext:
|
|
8783
|
-
productLabel:
|
|
8784
|
-
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
|
|
8785
|
-
durationSeconds:
|
|
8786
|
-
|
|
8787
|
-
|
|
9365
|
+
price: z45.string().min(1).max(120).optional().describe('Precio que se mostrar\xE1 en la landing y video. Formato libre \u2014 ej. "$89.900", "USD 29.99", "MXN 599". Opcional pero MUY IMPACTANTE: si el usuario no lo mencion\xF3, PREG\xDANTALE antes de invocar. Si te dice "t\xFA elige", om\xEDtelo y el backend sugiere uno.'),
|
|
9366
|
+
compareAtPrice: z45.string().min(1).max(120).optional().describe('Opcional. Precio anterior para comunicar un descuento real en landing e im\xE1genes. Ej. "$129.000" cuando price="$89.900".'),
|
|
9367
|
+
offer: z45.string().min(1).max(240).optional().describe('Oferta o promoci\xF3n a destacar. Ej. "2x1", "Env\xEDo gratis hoy", "50% OFF". Opcional pero MUY IMPACTANTE: si el usuario no lo mencion\xF3, PREG\xDANTALE antes de invocar (es v\xE1lido que diga "sin oferta"). Si te dice "t\xFA elige", om\xEDtelo.'),
|
|
9368
|
+
paymentMethods: z45.string().min(1).max(240).optional().describe('Opcional. M\xE9todos de pago que puede mostrar la landing. Ej. "Tarjeta, PSE, Nequi, Contraentrega". Si se omite, la landing no muestra m\xE9todos de pago.'),
|
|
9369
|
+
productContext: z45.string().max(3e3).optional().describe("Opcional. Informacion adicional del producto que la IA debe usar como contexto prioritario: uso real, comprador, restricciones, diferenciadores o direccion creativa. No reemplaza la investigacion de competencia; si hay competencia, guia el primer angulo y los outputs no deben contradecirlo."),
|
|
9370
|
+
productLabel: z45.string().trim().max(80).optional().describe('Opcional. Qu\xE9 es el producto, en pocas palabras (ej. "quita pelusas para ropa"). Es AUTORITATIVO: si lo env\xEDas, la IA usa esta identidad y no la reemplaza por otra categor\xEDa. Si lo omites, la IA detecta el producto desde la imagen.'),
|
|
9371
|
+
videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla. Default "auto" (la IA escoge tras analizar el producto). Todos los presets funcionan a 15s (single_call) y 30s (Multiescena). Opciones: "auto", "ugc_dynamic" (review con experiencia y prueba), "ugc_how_to" (tutorial con pasos y resultado visible), "ugc_unboxing_chaos" (apertura y primera prueba), "problem_solution" (problema concreto, producto como soluci\xF3n y CTA), "product_showcase" (producto ejecutando una funci\xF3n real), "wearable_showcase" (prenda o accesorio modelado), "cinematic" (TV spot con receta cinematogr\xE1fica), "wild_card" (una regla visual experimental original), "pixar_problem" (problema animado 3D), "pixar_sassy" (producto que habla). Si el usuario pide algo fuera de esta lista, p\xEDdele una opci\xF3n v\xE1lida.'),
|
|
9372
|
+
durationSeconds: z45.union([
|
|
9373
|
+
z45.literal(15),
|
|
9374
|
+
z45.literal(30)
|
|
8788
9375
|
]).default(15).describe("Opcional. Duraci\xF3n objetivo del video: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
|
|
8789
|
-
pauseForAngleReview:
|
|
9376
|
+
pauseForAngleReview: z45.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo para que el usuario lo confirme o edite antes del render. \xDAsalo si quiere decidir 15/30s cuando aparezca un winner. Default false."),
|
|
8790
9377
|
videoTier: VideoTierEnum.optional().describe('Opcional. Estilo del video. "basic" = "Realista" para 15s; al pedir durationSeconds=30 el sistema selecciona internamente Multiescena. No se expone el tier interno directamente.'),
|
|
8791
9378
|
videoResolution: VideoResolutionEnum.optional().describe('Opcional. Resoluci\xF3n del render. En la web se pregunta "\xBFResoluci\xF3n del video?". "480p" = excelente calidad y m\xE1s econ\xF3mico (RECOMENDADO, default si se omite); "720p" = m\xE1xima nitidez, entre 3% y 15% m\xE1s caro seg\xFAn formato y tier. Si el usuario no menciona resoluci\xF3n, OMITE este campo (el backend usa 480p).'),
|
|
8792
|
-
multiAngleVideos:
|
|
9379
|
+
multiAngleVideos: z45.boolean().optional().describe("Opcional. Si true, genera m\xFAltiples \xE1ngulos de venta en paralelo (3-5 videos). Si false/omitido, genera 1 video del \xE1ngulo principal. Default false."),
|
|
8793
9380
|
angleNarratives: PublicNarrativeStyleEnum.array().max(3).optional().describe('Opcional. Narrativa POR \xC1NGULO (posicional). Valores actuales: "review", "ugc_how_to", "ugc_unboxing_chaos", "problem_product_cta" (problema concreto, producto como soluci\xF3n y CTA), "product_showcase", "wearable_showcase", "cinematic", "wild_card", "animated_problem_pixar", "sassy_product_pixar". angleNarratives[i] fuerza la narrativa del \xE1ngulo i; las posiciones restantes las elige la IA. Requiere multiAngleVideos:true para varios \xE1ngulos.'),
|
|
8794
|
-
outputs:
|
|
9381
|
+
outputs: z45.array(z45.enum([
|
|
8795
9382
|
"landing",
|
|
8796
9383
|
"video",
|
|
8797
9384
|
"image"
|
|
8798
9385
|
])).min(2).max(3).optional().describe('Opcional. Qu\xE9 outputs generar. Default: todos ["landing","video","image"]. Para 2 outputs: ["landing","image"] (sin video), ["video","image"] (sin landing), ["landing","video"] (sin imagen). Para 1 solo output, usa generate_landing / generate_video_creative / generate_image_creatives.'),
|
|
8799
|
-
competitorScanEnabled:
|
|
8800
|
-
brandId:
|
|
8801
|
-
|
|
8802
|
-
|
|
8803
|
-
|
|
9386
|
+
competitorScanEnabled: z45.boolean().optional().describe("Opcional. Si true (default), el sistema analiza ads de competencia en Meta Ad Library y enriquece los \xE1ngulos generados con hooks y painPoints validados por el mercado (\u2248$0.20-0.48 extra por task \u2014 var\xEDa con cu\xE1ntas transcripciones de video se capturen). Si false, el pipeline genera \xE1ngulos sin esa se\xF1al \u2014 usa esto SOLO si el usuario ya tiene su propia investigaci\xF3n de mercado o quiere ahorrar costos en productos muy nicho sin presencia en Meta. Default true."),
|
|
9387
|
+
brandId: z45.string().uuid().optional().describe("Opcional. ID de una marca del usuario; si se env\xEDa, el sistema usa su voz, colores y persona para enriquecer los outputs generados."),
|
|
9388
|
+
actorId: z45.string().trim().min(1).optional().describe("Opcional. ID de un personaje guardado (usa list_actors para verlos). Si lo env\xEDas, su foto y su voz mandan sobre la descripci\xF3n de texto."),
|
|
9389
|
+
creativeDirection: z45.string().max(500).optional().describe('Opcional. Direcci\xF3n creativa en texto libre (colores, mood, estilo) que aplica a los outputs de IMAGEN y LANDING (no al video \u2014 el estilo/ambiente del video se dirige con scriptNotes). M\xC1XIMA PRIORIDAD: anula marca, colores y estilo por defecto. Ej. "amarillo y azul, minimalista". M\xE1x 500 caracteres.'),
|
|
9390
|
+
headline: z45.string().max(120).optional().describe("Opcional. Titular EXACTO a renderizar. Se imprime tal cual: la IA no lo reescribe, no lo acorta y no lo traduce. Se aplica a las IM\xC1GENES y a la portada de la landing de esta task (el video no lo usa; para el video usa scriptNotes). \xDAsalo cuando el copy es de marca y no es negociable. M\xE1x 120 caracteres. Si lo omites, la IA escribe el titular."),
|
|
9391
|
+
subheadline: z45.string().max(160).optional().describe("Opcional. Subt\xEDtulo EXACTO a renderizar, misma regla que headline. Independiente: puedes fijar solo el subt\xEDtulo. M\xE1x 160 caracteres."),
|
|
8804
9392
|
landingStrategy: LandingStrategyEnum.default("classic").describe('Opcional. Estructura de la landing cl\xE1sica cuando landing est\xE1 entre outputs: "classic" (estructura original probada y recomendada; default del primer rollout), "auto" (la IA decide; experimental), "pain_solution", "social_proof" (Landing Viral: UGC, WhatsApp, Instagram y evidencia social), "benefits_clarity", "offer_value" o "lifestyle_identity". Default "classic".'),
|
|
8805
9393
|
landingImageFormat: LandingImageFormatEnum.default("9:16").describe('Opcional. Formato/relaci\xF3n de aspecto de las im\xE1genes de la landing cl\xE1sica cuando landing est\xE1 entre outputs: "9:16" (vertical, por defecto), "4:5" (vertical compacto para feed) o "1:1" (cuadrado). Default "9:16".')
|
|
8806
9394
|
}).strict();
|
|
8807
|
-
var CreateServiceVideoInput =
|
|
8808
|
-
landingUrl:
|
|
8809
|
-
additionalContext:
|
|
9395
|
+
var CreateServiceVideoInput = z45.object({
|
|
9396
|
+
landingUrl: z45.string().url().describe("URL p\xFAblica de la landing del servicio. Es suficiente para generar el video; los medios son opcionales en la web."),
|
|
9397
|
+
additionalContext: z45.string().max(3e3).optional().describe('Instrucci\xF3n natural prioritaria. Ej.: "adapta el ritmo de estos competidores", "enfatiza reservas de demos" o restricciones de claims.'),
|
|
8810
9398
|
language: Lang.describe("Idioma del video: es, en o pt."),
|
|
8811
|
-
voiceId:
|
|
8812
|
-
brandId:
|
|
8813
|
-
reviewMode:
|
|
9399
|
+
voiceId: z45.string().trim().min(1).optional().describe('Opcional. Voz de ElevenLabs para toda la narraci\xF3n. Usa "auto" para que la IA la alinee con el presentador o env\xEDa un ID concreto de la lista de voces de la web.'),
|
|
9400
|
+
brandId: z45.string().uuid().optional().describe("Marca guardada del usuario. Aplica voz, mensajes, paleta y persona predeterminada."),
|
|
9401
|
+
reviewMode: z45.enum([
|
|
8814
9402
|
"automatic",
|
|
8815
9403
|
"guided"
|
|
8816
9404
|
]).default("automatic").describe("automatic = la IA elige concepto y guion sin pausas; guided = el usuario aprueba concepto y storyboard."),
|
|
8817
|
-
videoPreset:
|
|
9405
|
+
videoPreset: z45.enum([
|
|
8818
9406
|
"auto",
|
|
8819
9407
|
"ugc_dynamic",
|
|
8820
9408
|
"ugc_how_to",
|
|
@@ -8822,34 +9410,35 @@ var CreateServiceVideoInput = z43.object({
|
|
|
8822
9410
|
"wild_card"
|
|
8823
9411
|
]).default("auto")
|
|
8824
9412
|
}).strict();
|
|
8825
|
-
var GetTaskStatusInput =
|
|
8826
|
-
taskId:
|
|
8827
|
-
waitSeconds:
|
|
8828
|
-
});
|
|
8829
|
-
var ListTasksInput =
|
|
8830
|
-
limit:
|
|
8831
|
-
offset:
|
|
8832
|
-
search:
|
|
8833
|
-
});
|
|
8834
|
-
var EditTaskDraftInput =
|
|
8835
|
-
taskId:
|
|
8836
|
-
angleIndex:
|
|
8837
|
-
angle:
|
|
8838
|
-
angle:
|
|
8839
|
-
buyerPersona:
|
|
8840
|
-
actor:
|
|
8841
|
-
|
|
9413
|
+
var GetTaskStatusInput = z45.object({
|
|
9414
|
+
taskId: z45.string().min(1),
|
|
9415
|
+
waitSeconds: z45.number().int().min(0).max(120).optional()
|
|
9416
|
+
});
|
|
9417
|
+
var ListTasksInput = z45.object({
|
|
9418
|
+
limit: z45.number().int().min(1).max(50).optional(),
|
|
9419
|
+
offset: z45.number().int().min(0).optional(),
|
|
9420
|
+
search: z45.string().optional()
|
|
9421
|
+
});
|
|
9422
|
+
var EditTaskDraftInput = z45.object({
|
|
9423
|
+
taskId: z45.string().min(1),
|
|
9424
|
+
angleIndex: z45.number().int().min(0),
|
|
9425
|
+
angle: z45.object({
|
|
9426
|
+
angle: z45.string().min(1).max(480),
|
|
9427
|
+
buyerPersona: z45.string().min(1).max(360),
|
|
9428
|
+
actor: z45.string().min(1).max(360).optional(),
|
|
9429
|
+
actorId: z45.string().trim().min(1).optional().describe("Opcional. ID de un personaje guardado (usa list_actors para verlos). Si lo env\xEDas, su foto y su voz mandan sobre la descripci\xF3n de texto."),
|
|
9430
|
+
gender: z45.enum([
|
|
8842
9431
|
"female",
|
|
8843
9432
|
"male"
|
|
8844
9433
|
]).optional(),
|
|
8845
|
-
scriptNotes:
|
|
9434
|
+
scriptNotes: z45.string().max(2e3).optional()
|
|
8846
9435
|
})
|
|
8847
9436
|
});
|
|
8848
|
-
var PendingActionPayload =
|
|
8849
|
-
|
|
8850
|
-
kind:
|
|
9437
|
+
var PendingActionPayload = z45.discriminatedUnion("kind", [
|
|
9438
|
+
z45.object({
|
|
9439
|
+
kind: z45.literal("product_reference_confirmation"),
|
|
8851
9440
|
userChoice: ProductReferenceUserChoiceEnum,
|
|
8852
|
-
confirmedProductNames:
|
|
9441
|
+
confirmedProductNames: z45.array(z45.string().min(1).max(120)).min(2).max(8).optional(),
|
|
8853
9442
|
// SCOPED multi-scene opt-in — the ONLY way STANDARD ("Multiescena") is
|
|
8854
9443
|
// reachable via MCP. Valid ONLY together with userChoice='variants' (same
|
|
8855
9444
|
// product in several colors): renders one shot per color and stitches them,
|
|
@@ -8860,50 +9449,50 @@ var PendingActionPayload = z43.discriminatedUnion("kind", [
|
|
|
8860
9449
|
// safe because multi-scene IS the intended render for a color variant set,
|
|
8861
9450
|
// not a blind quality upsell. Maps to `videoTier: 'standard'` on the
|
|
8862
9451
|
// confirm call; the backend re-gates it (variants + variant_set only).
|
|
8863
|
-
useMultiSceneShowcase:
|
|
9452
|
+
useMultiSceneShowcase: z45.boolean().optional().describe('Opcional (SOLO con userChoice="variants"). true = render Multiescena: una toma por color, mostrando todas las variantes, hasta 30s. Se factura POR ESCENA (m\xE1s caro que el render normal de una sola toma) \u2014 AV\xCDSALE del mayor costo al usuario antes de activarlo. Omitir = render est\xE1ndar de una sola toma. Se ignora si userChoice no es "variants".')
|
|
8864
9453
|
}),
|
|
8865
|
-
|
|
8866
|
-
kind:
|
|
9454
|
+
z45.object({
|
|
9455
|
+
kind: z45.literal("angle_review_approval"),
|
|
8867
9456
|
// All edits are optional — empty action body just approves the
|
|
8868
9457
|
// backend-generated draft as-is. Field shapes mirror the angle-review
|
|
8869
9458
|
// request contracts in @packages/types/wizard.ts; the handler routes to
|
|
8870
9459
|
// the dedicated video/image/autopilot endpoint based on StatusResponse.kind.
|
|
8871
|
-
customAngle:
|
|
9460
|
+
customAngle: z45.string().min(1).max(480).optional(),
|
|
8872
9461
|
// Keep the shared MCP action within the strictest downstream contract:
|
|
8873
9462
|
// autopilot/image resume endpoints cap these fields at 1500 characters.
|
|
8874
|
-
painPoints:
|
|
8875
|
-
benefits:
|
|
8876
|
-
features:
|
|
8877
|
-
gapDurationSeconds:
|
|
8878
|
-
|
|
8879
|
-
|
|
9463
|
+
painPoints: z45.string().max(1500).optional(),
|
|
9464
|
+
benefits: z45.string().max(1500).optional(),
|
|
9465
|
+
features: z45.array(z45.string().min(1).max(300)).max(10).optional(),
|
|
9466
|
+
gapDurationSeconds: z45.union([
|
|
9467
|
+
z45.literal(15),
|
|
9468
|
+
z45.literal(30)
|
|
8880
9469
|
]).optional().describe("Opcional. Solo para aceptar expl\xEDcitamente la recomendaci\xF3n gapDurationChoice del status. 30 env\xEDa el override double_30s para el \xE1ngulo recomendado; 15 u omitido conserva la duraci\xF3n cotizada.")
|
|
8881
9470
|
}),
|
|
8882
|
-
|
|
8883
|
-
kind:
|
|
8884
|
-
angleId:
|
|
8885
|
-
content:
|
|
8886
|
-
creativeDirection:
|
|
8887
|
-
actor:
|
|
9471
|
+
z45.object({
|
|
9472
|
+
kind: z45.literal("service_angle_approval"),
|
|
9473
|
+
angleId: z45.string().uuid(),
|
|
9474
|
+
content: z45.string().min(1).max(480).optional(),
|
|
9475
|
+
creativeDirection: z45.string().min(1).max(1500).optional(),
|
|
9476
|
+
actor: z45.string().min(1).max(360).optional()
|
|
8888
9477
|
}),
|
|
8889
|
-
|
|
8890
|
-
kind:
|
|
8891
|
-
voiceovers:
|
|
8892
|
-
visualDirections:
|
|
9478
|
+
z45.object({
|
|
9479
|
+
kind: z45.literal("service_script_approval"),
|
|
9480
|
+
voiceovers: z45.record(z45.string(), z45.string().min(1).max(600)).optional(),
|
|
9481
|
+
visualDirections: z45.record(z45.string(), z45.string().min(1).max(1200)).optional()
|
|
8893
9482
|
})
|
|
8894
9483
|
]);
|
|
8895
|
-
var ResolvePendingActionInput =
|
|
8896
|
-
taskId:
|
|
9484
|
+
var ResolvePendingActionInput = z45.object({
|
|
9485
|
+
taskId: z45.string().min(1),
|
|
8897
9486
|
action: PendingActionPayload
|
|
8898
9487
|
}).strict();
|
|
8899
9488
|
function resolveVariantConfirmVideoTier(action) {
|
|
8900
9489
|
return action.useMultiSceneShowcase && action.userChoice === "variants" ? "standard" : void 0;
|
|
8901
9490
|
}
|
|
8902
9491
|
__name(resolveVariantConfirmVideoTier, "resolveVariantConfirmVideoTier");
|
|
8903
|
-
var StatusForResolveSchema =
|
|
9492
|
+
var StatusForResolveSchema = z45.object({
|
|
8904
9493
|
// Metadata used to select the backend resume contract. Older API responses
|
|
8905
9494
|
// may omit these fields, so the resolver keeps the generic fallback.
|
|
8906
|
-
kind:
|
|
9495
|
+
kind: z45.enum([
|
|
8907
9496
|
"autopilot",
|
|
8908
9497
|
"video",
|
|
8909
9498
|
"image",
|
|
@@ -8911,24 +9500,24 @@ var StatusForResolveSchema = z43.object({
|
|
|
8911
9500
|
"mix",
|
|
8912
9501
|
"service"
|
|
8913
9502
|
]).optional(),
|
|
8914
|
-
selectedOutputs:
|
|
9503
|
+
selectedOutputs: z45.array(z45.enum([
|
|
8915
9504
|
"landing",
|
|
8916
9505
|
"video",
|
|
8917
9506
|
"image"
|
|
8918
9507
|
])).optional(),
|
|
8919
|
-
videoAngleCount:
|
|
8920
|
-
ugcFormat:
|
|
9508
|
+
videoAngleCount: z45.number().int().min(1).max(3).optional(),
|
|
9509
|
+
ugcFormat: z45.enum([
|
|
8921
9510
|
"single_8s",
|
|
8922
9511
|
"single_15s",
|
|
8923
9512
|
"double_30s"
|
|
8924
9513
|
]).optional(),
|
|
8925
|
-
videoStyle:
|
|
9514
|
+
videoStyle: z45.enum([
|
|
8926
9515
|
"ugc",
|
|
8927
9516
|
"cartoon_3d",
|
|
8928
9517
|
"product_only",
|
|
8929
9518
|
"ai_dynamic"
|
|
8930
9519
|
]).optional(),
|
|
8931
|
-
videoModel:
|
|
9520
|
+
videoModel: z45.enum([
|
|
8932
9521
|
"veo",
|
|
8933
9522
|
"wan",
|
|
8934
9523
|
"bytedance",
|
|
@@ -8936,39 +9525,45 @@ var StatusForResolveSchema = z43.object({
|
|
|
8936
9525
|
"grok",
|
|
8937
9526
|
"kling"
|
|
8938
9527
|
]).optional(),
|
|
8939
|
-
videoTier:
|
|
9528
|
+
videoTier: z45.enum([
|
|
8940
9529
|
"legacy",
|
|
8941
9530
|
"standard",
|
|
8942
9531
|
"pro",
|
|
8943
9532
|
"single_call"
|
|
8944
9533
|
]).optional(),
|
|
8945
|
-
videoResolution:
|
|
9534
|
+
videoResolution: z45.enum([
|
|
8946
9535
|
"480p",
|
|
8947
9536
|
"720p"
|
|
8948
9537
|
]).optional(),
|
|
8949
9538
|
narrativeStyle: NarrativeStyleEnum.optional(),
|
|
8950
|
-
pendingUserAction:
|
|
8951
|
-
kind:
|
|
8952
|
-
candidateGrouping:
|
|
8953
|
-
selectedImageUrls:
|
|
8954
|
-
proposedProductNames:
|
|
8955
|
-
customAngleDraft:
|
|
8956
|
-
painPointsDraft:
|
|
8957
|
-
benefitsDraft:
|
|
8958
|
-
featuresDraft:
|
|
9539
|
+
pendingUserAction: z45.object({
|
|
9540
|
+
kind: z45.string(),
|
|
9541
|
+
candidateGrouping: z45.string().optional(),
|
|
9542
|
+
selectedImageUrls: z45.array(z45.string()).optional(),
|
|
9543
|
+
proposedProductNames: z45.array(z45.string()).optional(),
|
|
9544
|
+
customAngleDraft: z45.string().optional(),
|
|
9545
|
+
painPointsDraft: z45.string().optional(),
|
|
9546
|
+
benefitsDraft: z45.string().optional(),
|
|
9547
|
+
featuresDraft: z45.array(z45.string()).optional(),
|
|
8959
9548
|
// Recommendation consumed only when the caller explicitly sends
|
|
8960
9549
|
// gapDurationSeconds: 30 in the angle approval action.
|
|
8961
9550
|
gapDurationChoice: GapDurationChoiceMirror.optional(),
|
|
8962
|
-
|
|
8963
|
-
|
|
8964
|
-
|
|
8965
|
-
|
|
8966
|
-
|
|
8967
|
-
|
|
8968
|
-
|
|
8969
|
-
|
|
8970
|
-
|
|
8971
|
-
|
|
9551
|
+
// `.passthrough()` + an explicit `actorId`: this object is a MIRROR of the
|
|
9552
|
+
// backend's pending service angle, and a bare z.object() STRIPS unknown
|
|
9553
|
+
// keys. `actorId` (the saved custom actor the picker stored) was silently
|
|
9554
|
+
// dropped here, so the approval handler below could not echo it back on the
|
|
9555
|
+
// PATCH — the selection looked accepted and reached no render.
|
|
9556
|
+
angles: z45.array(z45.object({
|
|
9557
|
+
id: z45.string().uuid(),
|
|
9558
|
+
content: z45.string(),
|
|
9559
|
+
creativeDirection: z45.string(),
|
|
9560
|
+
actor: z45.string(),
|
|
9561
|
+
actorId: z45.string().nullable().optional()
|
|
9562
|
+
}).passthrough()).optional(),
|
|
9563
|
+
panels: z45.array(z45.object({
|
|
9564
|
+
beatId: z45.string(),
|
|
9565
|
+
voiceover: z45.string(),
|
|
9566
|
+
visualDirection: z45.string()
|
|
8972
9567
|
}).passthrough()).optional()
|
|
8973
9568
|
}).optional()
|
|
8974
9569
|
}).passthrough();
|
|
@@ -9009,7 +9604,6 @@ __name(resolveStatusTaskKind, "resolveStatusTaskKind");
|
|
|
9009
9604
|
function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http" } = {}) {
|
|
9010
9605
|
server.tool("create_product_task", "Crea una tarea de producto desde fotos o desde un proyecto guiado propio. Por defecto genera los 3 outputs (landing + video + image creatives) EN UN MISMO TASK. Devuelve un taskId.\n\nINPUTS REQUERIDOS: images (1-5 fotos del MISMO producto; la primera es la hero) O projectId si el proyecto ya tiene una imagen guardada; adem\xE1s language y country.\n\nANTES de invocar, PREG\xDANTALE al usuario por price, offer y paymentMethods si no los mencion\xF3 \u2014 son inputs de alto impacto en la landing y nunca debes asumirlos. Si hay descuento, pregunta tambi\xE9n compareAtPrice. NO defaultes country/language desde el contexto del chat: preg\xFAntale expl\xEDcitamente, incluso si est\xE1 en LATAM.\n\nSi env\xEDas projectId, debe ser el proyecto propio que el usuario acaba de costear; el backend resolver\xE1 la imagen privada y conservar\xE1 todos los controles de cr\xE9ditos/confirmaci\xF3n. Si el usuario adjunta images junto con projectId, esas fotos reemplazan la referencia solo para este task.\n\nANTES de invocar, DILE CLARAMENTE qu\xE9 outputs vas a generar y que `create_product_task` los genera juntos en un mismo task.\n\nUsa SOLO valores declarados en los enums.", CreateProductTaskInput.shape, async (input) => {
|
|
9011
9606
|
try {
|
|
9012
|
-
assertVideoPresetDurationCompatibility(input.videoPreset, input.durationSeconds);
|
|
9013
9607
|
if (!input.images && !input.projectId) {
|
|
9014
9608
|
throw new Error("images es requerido salvo que env\xEDes projectId.");
|
|
9015
9609
|
}
|
|
@@ -9044,6 +9638,7 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
|
|
|
9044
9638
|
"options.selectedOutputs": input.outputs,
|
|
9045
9639
|
"options.competitorScanEnabled": input.competitorScanEnabled,
|
|
9046
9640
|
"options.brandId": input.brandId,
|
|
9641
|
+
"options.actorId": input.actorId,
|
|
9047
9642
|
"options.creativeDirection": input.creativeDirection,
|
|
9048
9643
|
"options.headline": input.headline,
|
|
9049
9644
|
"options.subheadline": input.subheadline,
|
|
@@ -9185,7 +9780,7 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
|
|
|
9185
9780
|
throw translateError(e);
|
|
9186
9781
|
}
|
|
9187
9782
|
});
|
|
9188
|
-
server.tool("edit_task_draft", "Edita el draft de un angle de marketing antes de la generaci\xF3n. Campos editables: angle (texto del angle), buyerPersona, actor (descripci\xF3n del personaje), gender (female|male), scriptNotes. Use this only before generation completes. Editing already-generated images or videos is not supported.", EditTaskDraftInput.shape, async (input) => {
|
|
9783
|
+
server.tool("edit_task_draft", "Edita el draft de un angle de marketing antes de la generaci\xF3n. Campos editables: angle (texto del angle), buyerPersona, actor (descripci\xF3n del personaje), actorId (ID de un personaje guardado; usa list_actors), gender (female|male), scriptNotes. Use this only before generation completes. Editing already-generated images or videos is not supported.", EditTaskDraftInput.shape, async (input) => {
|
|
9189
9784
|
try {
|
|
9190
9785
|
const angle = await api.patchJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/angles/${input.angleIndex}`, input.angle ?? {});
|
|
9191
9786
|
return asText({
|
|
@@ -9361,7 +9956,13 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
|
|
|
9361
9956
|
const edits = {
|
|
9362
9957
|
content: action.content ?? candidate.content,
|
|
9363
9958
|
creativeDirection: action.creativeDirection ?? candidate.creativeDirection,
|
|
9364
|
-
actor: action.actor ?? candidate.actor
|
|
9959
|
+
actor: action.actor ?? candidate.actor,
|
|
9960
|
+
// Echo the persisted saved actor. Omitted when absent so the PATCH
|
|
9961
|
+
// never writes a null over a selection made elsewhere — the
|
|
9962
|
+
// backend only updates the keys it receives.
|
|
9963
|
+
...candidate.actorId ? {
|
|
9964
|
+
actorId: candidate.actorId
|
|
9965
|
+
} : {}
|
|
9365
9966
|
};
|
|
9366
9967
|
await api.patchJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/service-angles/${encodeURIComponent(candidate.id)}`, edits);
|
|
9367
9968
|
const result = await api.postJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/service-angles/select`, {
|
|
@@ -9402,58 +10003,58 @@ __name(registerTaskTools, "registerTaskTools");
|
|
|
9402
10003
|
|
|
9403
10004
|
// src/tools/creatives.ts
|
|
9404
10005
|
import { randomUUID } from "crypto";
|
|
9405
|
-
import { z as
|
|
10006
|
+
import { z as z46 } from "zod";
|
|
9406
10007
|
var CreativeRefs = {
|
|
9407
10008
|
images: ImagesInput,
|
|
9408
10009
|
language: Lang.describe('Idioma del contenido. Ej. "es", "en", "pt". REQUERIDO \u2014 si el usuario no lo dijo, PREG\xDANTALE antes de invocar. NUNCA infieras desde el contexto del chat ni asumas un default.'),
|
|
9409
10010
|
country: Country.describe('Pa\xEDs objetivo ISO. Ej. "CO", "MX", "US", "AR". Define moneda, m\xE9todos de pago y tono. REQUERIDO \u2014 si el usuario no lo dijo, PREG\xDANTALE antes de invocar. NUNCA infieras desde el contexto del chat ni asumas un default.'),
|
|
9410
|
-
price:
|
|
9411
|
-
offer:
|
|
9412
|
-
compareAtPrice:
|
|
9413
|
-
paymentMethods:
|
|
9414
|
-
productContext:
|
|
9415
|
-
productLabel:
|
|
9416
|
-
buyerPersona:
|
|
9417
|
-
isCombo:
|
|
9418
|
-
competitorScanEnabled:
|
|
9419
|
-
brandId:
|
|
9420
|
-
projectId:
|
|
9421
|
-
creativeDirection:
|
|
9422
|
-
headline:
|
|
9423
|
-
subheadline:
|
|
10011
|
+
price: z46.string().min(1).max(120).optional().describe('Precio en formato libre \u2014 ej. "$89.900", "USD 29.99". Opcional pero MUY IMPACTANTE para conversi\xF3n: si el usuario no lo mencion\xF3, PREG\xDANTALE antes de invocar. Si el usuario te dice "t\xFA elige", omite el campo y la IA del backend sugiere uno.'),
|
|
10012
|
+
offer: z46.string().min(1).max(240).optional().describe('Oferta a destacar \u2014 ej. "2x1", "Env\xEDo gratis", "50% OFF". Opcional pero MUY IMPACTANTE: si el usuario no lo mencion\xF3, PREG\xDANTALE antes de invocar (es v\xE1lido que diga "sin oferta"). Si el usuario te dice "t\xFA elige", omite el campo.'),
|
|
10013
|
+
compareAtPrice: z46.string().min(1).max(120).optional().describe('Opcional. Precio anterior o de referencia para mostrar descuento. Ej. "$129.000" cuando price="$89.900".'),
|
|
10014
|
+
paymentMethods: z46.string().min(1).max(240).optional().describe('Opcional. M\xE9todos de pago a mostrar. Ej. "Tarjeta, PSE, Nequi, Contraentrega". Si se omite, la landing no muestra m\xE9todos de pago.'),
|
|
10015
|
+
productContext: z46.string().max(3e3).optional().describe("Opcional. Informacion adicional del producto que la IA debe usar como contexto prioritario: uso real, comprador, restricciones, diferenciadores o direccion creativa. No reemplaza la investigacion de competencia; si hay competencia, guia el primer angulo y los outputs no deben contradecirlo."),
|
|
10016
|
+
productLabel: z46.string().trim().max(80).optional().describe('Opcional. Nombre o tipo del producto en pocas palabras (ej. "collar de oro", "quita pelusas para ropa"). Es AUTORITATIVO: la IA usa esta identidad y no la reemplaza por otra categor\xEDa. Si lo omites, la IA lo detecta desde la imagen. NO salta la extracci\xF3n.'),
|
|
10017
|
+
buyerPersona: z46.string().optional().describe('Opcional. Descripci\xF3n del comprador objetivo. Ej. "Mujeres 35-50, profesionales urbanas". Si se omite, la IA infiere desde el \xE1ngulo y producto.'),
|
|
10018
|
+
isCombo: z46.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
|
|
10019
|
+
competitorScanEnabled: z46.boolean().optional().describe("Opcional. Si true (default), el sistema analiza ads de competencia en Meta Ad Library y enriquece los \xE1ngulos generados con hooks y painPoints validados por el mercado (\u2248$0.20-0.48 extra por task \u2014 var\xEDa con cu\xE1ntas transcripciones de video se capturen). Si false, el pipeline genera \xE1ngulos sin esa se\xF1al \u2014 usa esto SOLO si el usuario ya tiene su propia investigaci\xF3n de mercado o quiere ahorrar costos en productos muy nicho sin presencia en Meta. Default true."),
|
|
10020
|
+
brandId: z46.string().uuid().optional().describe("Opcional. ID de una marca del usuario; si se env\xEDa, el sistema usa su voz, colores y persona para enriquecer el creativo."),
|
|
10021
|
+
projectId: z46.string().uuid().optional().describe("Opcional. ID de un proyecto guiado de dropshipping activo del usuario. Enlaza esta generaci\xF3n con su progreso y recompensas."),
|
|
10022
|
+
creativeDirection: z46.string().max(500).optional().describe('Opcional. Direcci\xF3n creativa en texto libre (colores, mood, estilo). M\xC1XIMA PRIORIDAD: anula marca, colores y estilo por defecto. Aplica SOLO a im\xE1genes y landing. Ej. im\xE1genes: "amarillo, azul y rojo, minimalista"; landing: "elegante, paleta fr\xEDa". Para video usa scriptNotes (ambiente/escenario). M\xE1x 500 caracteres.'),
|
|
10023
|
+
headline: z46.string().max(120).optional().describe("Opcional. Titular EXACTO a renderizar en la imagen (o en la portada de la landing). Se imprime tal cual: la IA no lo reescribe, no lo acorta y no lo traduce. \xDAsalo cuando el copy es de marca y no es negociable. M\xE1x 120 caracteres. Si lo omites, la IA escribe el titular."),
|
|
10024
|
+
subheadline: z46.string().max(160).optional().describe("Opcional. Subt\xEDtulo EXACTO a renderizar, misma regla que headline. Independiente: puedes fijar solo el subt\xEDtulo. M\xE1x 160 caracteres.")
|
|
9424
10025
|
};
|
|
9425
|
-
var VideoInput =
|
|
10026
|
+
var VideoInput = z46.object({
|
|
9426
10027
|
images: CreativeRefs.images,
|
|
9427
|
-
title:
|
|
9428
|
-
primaryAngle:
|
|
10028
|
+
title: z46.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
|
|
10029
|
+
primaryAngle: z46.string().min(1).describe('\xC1ngulo de venta principal (frase del beneficio). Ej. "Reduce arrugas en 14 d\xEDas" o "Para quienes odian las cremas grasosas".'),
|
|
9429
10030
|
language: CreativeRefs.language,
|
|
9430
10031
|
country: CreativeRefs.country,
|
|
9431
|
-
aspectRatio:
|
|
10032
|
+
aspectRatio: z46.enum([
|
|
9432
10033
|
"9:16",
|
|
9433
10034
|
"16:9",
|
|
9434
10035
|
"Auto"
|
|
9435
10036
|
]).describe('Relaci\xF3n de aspecto. "9:16" = vertical (TikTok/Reels \u2014 default recomendado para ads). "16:9" = horizontal (YouTube). "Auto" = decide la IA.'),
|
|
9436
|
-
durationSeconds:
|
|
9437
|
-
|
|
9438
|
-
|
|
10037
|
+
durationSeconds: z46.union([
|
|
10038
|
+
z46.literal(15),
|
|
10039
|
+
z46.literal(30)
|
|
9439
10040
|
]).default(15).describe("Opcional. Duraci\xF3n objetivo: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
|
|
9440
|
-
videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla. Default "auto" (la IA escoge tras analizar el producto). Todos los presets funcionan a 15s y 30s
|
|
10041
|
+
videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla. Default "auto" (la IA escoge tras analizar el producto). Todos los presets funcionan a 15s (single_call) y 30s (Multiescena). Opciones: "auto", "ugc_dynamic" (review con experiencia y prueba), "ugc_how_to" (tutorial con pasos y resultado visible), "ugc_unboxing_chaos" (apertura y primera prueba), "problem_solution" (problema concreto, producto como soluci\xF3n y CTA), "product_showcase" (producto ejecutando una funci\xF3n real), "wearable_showcase" (prenda o accesorio modelado), "cinematic" (TV spot con receta cinematogr\xE1fica), "wild_card" (una regla visual experimental original), "pixar_problem" (problema animado 3D), "pixar_sassy" (producto que habla).'),
|
|
9441
10042
|
videoTier: VideoTierEnum.optional().describe('Opcional. Estilo del video. "basic" = "Realista"; para durationSeconds=30 el sistema selecciona internamente Multiescena. No se expone el tier interno directamente.'),
|
|
9442
10043
|
videoResolution: VideoResolutionEnum.optional().describe('Opcional. Resoluci\xF3n del render. En la web se pregunta "\xBFResoluci\xF3n del video?". "480p" = excelente calidad y m\xE1s econ\xF3mico (RECOMENDADO, default si se omite); "720p" = m\xE1xima nitidez, entre 3% y 15% m\xE1s caro seg\xFAn formato y tier. Si el usuario no menciona resoluci\xF3n, OMITE este campo (el backend usa 480p).'),
|
|
9443
|
-
features:
|
|
10044
|
+
features: z46.array(z46.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
|
|
9444
10045
|
price: CreativeRefs.price,
|
|
9445
10046
|
offer: CreativeRefs.offer,
|
|
9446
10047
|
productContext: CreativeRefs.productContext,
|
|
9447
10048
|
productLabel: CreativeRefs.productLabel,
|
|
9448
|
-
cta:
|
|
9449
|
-
scriptNotes:
|
|
9450
|
-
pauseForVideoScriptReview:
|
|
9451
|
-
pauseForAngleReview:
|
|
10049
|
+
cta: z46.string().optional().describe('Opcional. Call-to-action final del video. Ej. "Compra ahora", "Pide el tuyo hoy", "Link en bio". Si se omite, la IA escribe uno.'),
|
|
10050
|
+
scriptNotes: z46.string().optional().describe('Opcional. Notas/restricciones para el guion (la IA las usa al redactar). Ej. "No mencionar la marca X", "Tono divertido, no cl\xEDnico", "Mencionar 2x1". NO es el guion literal \u2014 para editar el guion palabra-por-palabra, pasa `pauseForVideoScriptReview: true` y usa get_video_script / approve_video_script tras la pausa.'),
|
|
10051
|
+
pauseForVideoScriptReview: z46.boolean().optional().describe("Opcional. Si true, la tarea pausa despu\xE9s de generar el guion y antes de renderizar. \xDAsalo cuando el usuario quiera editar el guion antes de gastar cr\xE9ditos en el video. Tras la pausa: get_video_script para leerlo, approve_video_script para aprobar (con o sin ediciones). Default false."),
|
|
10052
|
+
pauseForAngleReview: z46.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo para que el usuario lo confirme o edite antes del render. Default false."),
|
|
9452
10053
|
competitorScanEnabled: CreativeRefs.competitorScanEnabled,
|
|
9453
10054
|
brandId: CreativeRefs.brandId,
|
|
9454
10055
|
projectId: CreativeRefs.projectId
|
|
9455
10056
|
}).strict();
|
|
9456
|
-
var FreeVideoQualityEnum =
|
|
10057
|
+
var FreeVideoQualityEnum = z46.enum([
|
|
9457
10058
|
"economical",
|
|
9458
10059
|
"high_detail"
|
|
9459
10060
|
]);
|
|
@@ -9475,38 +10076,38 @@ var FREE_VIDEO_DURATIONS_BY_QUALITY = {
|
|
|
9475
10076
|
15
|
|
9476
10077
|
]
|
|
9477
10078
|
};
|
|
9478
|
-
var FreeVideoInput =
|
|
10079
|
+
var FreeVideoInput = z46.object({
|
|
9479
10080
|
images: ImagesInput.optional().describe("Opcional. 1-5 fotos del producto. Om\xEDtelo para generar desde texto puro."),
|
|
9480
|
-
rawPrompt:
|
|
10081
|
+
rawPrompt: z46.string().trim().min(1).max(2e3).describe("Prompt o guion exacto del video. Si activas subt\xEDtulos, este mismo texto se usa como guion de subt\xEDtulos."),
|
|
9481
10082
|
quality: FreeVideoQualityEnum.describe('Calidad del video: "economical" = Econ\xF3mico o "high_detail" = Alto detalle.'),
|
|
9482
|
-
durationSeconds:
|
|
10083
|
+
durationSeconds: z46.number().int().min(1).max(15).describe("Duraci\xF3n exacta. Econ\xF3mico permite 6, 8, 10 o 15s; Alto detalle permite 4, 8, 12 o 15s."),
|
|
9483
10084
|
videoResolution: VideoResolutionEnum.describe("Resoluci\xF3n del video: 480p o 720p."),
|
|
9484
|
-
generateAiMusic:
|
|
9485
|
-
addSubtitles:
|
|
9486
|
-
idempotencyKey:
|
|
10085
|
+
generateAiMusic: z46.boolean().default(false).describe("Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false."),
|
|
10086
|
+
addSubtitles: z46.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
|
|
10087
|
+
idempotencyKey: z46.string().uuid().optional().describe("Opcional. UUID para reintentar exactamente esta misma solicitud sin crear ni cobrar otro video. Reutiliza el mismo valor si el cliente no recibi\xF3 la respuesta.")
|
|
9487
10088
|
}).strict();
|
|
9488
10089
|
var imageStyleList = ImageCreativeStyleEnum.options.join(" | ");
|
|
9489
10090
|
var archetypeList = ImageCreativeArchetypeEnum.options.join(" | ");
|
|
9490
10091
|
var visualTreatmentList = ImageCreativeVisualTreatmentEnum.options.join(" | ");
|
|
9491
10092
|
var aspectRatioList = ImageCreativeAspectRatioEnum.options.join(" | ");
|
|
9492
|
-
var ImageInput =
|
|
10093
|
+
var ImageInput = z46.object({
|
|
9493
10094
|
images: CreativeRefs.images,
|
|
9494
|
-
productTitle:
|
|
9495
|
-
primaryAngle:
|
|
10095
|
+
productTitle: z46.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
|
|
10096
|
+
primaryAngle: z46.string().min(1).describe('\xC1ngulo de venta principal. Ej. "Reduce arrugas en 14 d\xEDas".'),
|
|
9496
10097
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
9497
10098
|
language: CreativeRefs.language,
|
|
9498
10099
|
country: CreativeRefs.country,
|
|
9499
|
-
nVariants:
|
|
9500
|
-
imageModel:
|
|
10100
|
+
nVariants: z46.number().int().min(1).max(6).optional().describe("Opcional. N\xFAmero de variantes a generar (1-6). Default 3."),
|
|
10101
|
+
imageModel: z46.string().optional().describe('Opcional. Modelo de imagen. Ej. "gpt-image-2", "nano-banana-2". Om\xEDtelo para usar el default optimizado por costo/calidad.'),
|
|
9501
10102
|
imageStyle: ImageCreativeStyleEnum.optional().describe(`Opcional. Estilo del shot del producto. Valores: ${imageStyleList}. Si se omite, la IA escoge seg\xFAn el archetype.`),
|
|
9502
10103
|
creativeArchetype: ImageCreativeArchetypeEnum.optional().describe(`Opcional. Arquetipo CRO de la composici\xF3n. Valores: ${archetypeList}. Si se omite, la IA escoge seg\xFAn \xE1ngulo+oferta (ej. "offer_value_prop" cuando hay descuento, "social_proof_trust" cuando el \xE1ngulo es testimonial).`),
|
|
9503
10104
|
visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe(`Opcional. Tratamiento gr\xE1fico. Valores: ${visualTreatmentList} ("performance_product_ad" = dealer-style alto CTR para Meta/TikTok ads; "premium_dtc_carousel" = fotograf\xEDa limpia estilo Allbirds/Glossier). Si se omite, la IA escoge.`),
|
|
9504
|
-
styleMix:
|
|
10105
|
+
styleMix: z46.boolean().optional().describe("Opcional. Si true, las N variantes mezclan distintos imageStyle para A/B test. Si false/omitido, todas comparten estilo. Default false."),
|
|
9505
10106
|
aspectRatio: ImageCreativeAspectRatioEnum.optional().describe(`Opcional. Relaci\xF3n de aspecto. Valores: ${aspectRatioList}. Default "4:5" (vertical Instagram feed).`),
|
|
9506
|
-
generateAdCopy:
|
|
9507
|
-
pauseForAngleReview:
|
|
9508
|
-
features:
|
|
9509
|
-
brandColors:
|
|
10107
|
+
generateAdCopy: z46.boolean().optional().describe("Opcional. Si true, tambi\xE9n genera headline/body/CTA para el ad. Default false."),
|
|
10108
|
+
pauseForAngleReview: z46.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo y antes de las im\xE1genes para que el usuario edite v\xEDa edit_task_draft. Default false."),
|
|
10109
|
+
features: z46.array(z46.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
|
|
10110
|
+
brandColors: z46.array(z46.string()).optional().describe('Opcional. Paleta de marca en hex. Ej. ["#FF6B35", "#004E89"]. Si se omite, la IA elige seg\xFAn el producto.'),
|
|
9510
10111
|
price: CreativeRefs.price,
|
|
9511
10112
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
9512
10113
|
offer: CreativeRefs.offer,
|
|
@@ -9520,16 +10121,16 @@ var ImageInput = z44.object({
|
|
|
9520
10121
|
headline: CreativeRefs.headline,
|
|
9521
10122
|
subheadline: CreativeRefs.subheadline
|
|
9522
10123
|
});
|
|
9523
|
-
var EditImageCreativeInput =
|
|
9524
|
-
taskId:
|
|
9525
|
-
variantIndex:
|
|
9526
|
-
instruction:
|
|
10124
|
+
var EditImageCreativeInput = z46.object({
|
|
10125
|
+
taskId: z46.string().uuid().describe("ID de la tarea de imagen ya finalizada."),
|
|
10126
|
+
variantIndex: z46.number().int().min(0).max(10).describe("\xCDndice de la variante a corregir (0 = primera imagen)."),
|
|
10127
|
+
instruction: z46.string().min(1).max(1e3).describe('Qu\xE9 cambiar en esa imagen. Ej. "quita el badge de env\xEDo gratis", "cambia el fondo a blanco".')
|
|
9527
10128
|
});
|
|
9528
|
-
var LandingInput =
|
|
10129
|
+
var LandingInput = z46.object({
|
|
9529
10130
|
images: CreativeRefs.images,
|
|
9530
10131
|
language: CreativeRefs.language,
|
|
9531
10132
|
country: CreativeRefs.country,
|
|
9532
|
-
salesAngle:
|
|
10133
|
+
salesAngle: z46.string().optional().describe('Opcional. \xC1ngulo de venta principal. Ej. "Reduce arrugas en 14 d\xEDas". Si se omite, la IA lo deduce de la imagen.'),
|
|
9533
10134
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
9534
10135
|
price: CreativeRefs.price,
|
|
9535
10136
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
@@ -9578,9 +10179,8 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
9578
10179
|
throw translateError(e);
|
|
9579
10180
|
}
|
|
9580
10181
|
});
|
|
9581
|
-
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\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) => {
|
|
10182
|
+
server.tool("generate_video_creative", 'Genera UN video creativo independiente. Devuelve un taskId.\n\nREQUERIDOS: images (array de 1-5 fotos), title, primaryAngle, language, country, aspectRatio.\n\nANTES de invocar, PREG\xDANTALE al usuario por price y offer si no los mencion\xF3 \u2014 son los inputs de mayor impacto en conversi\xF3n y nunca debes asumirlos. NO defaultes country/language desde el contexto: preg\xFAntale expl\xEDcitamente. Si el usuario te dice "t\xFA elige" para price/offer, om\xEDtelos.\n\nOTROS OPCIONALES (puedes invocar sin ellos y dejar que la IA elija defaults sensatos): durationSeconds (15 o 30), videoPreset (tipo/narrativa del video), features, cta, scriptNotes (dirige el guion Y el ambiente/escenario/est\xE9tica del video, ej. "en una playa, opulento").\n\nCOMPETENCIA Y GAP REPLICATION: con videoPreset="auto" (default) y competitorScanEnabled=true (default), el sistema analiza anuncios de competencia. Si encuentra un winner de video coherente, ancla el primaryAngle y genera autom\xE1ticamente la narrativa `gap_replication` para copiar su estructura audiovisual. No la selecciones manualmente: es un resultado del an\xE1lisis. Si competitorScanEnabled=false, esta b\xFAsqueda se omite.\n\nESTILO: 15s usa "Realista" (single_call); 30s usa "Multiescena" (multi-toma, aproximadamente 30s). Multiescena se factura por escena, as\xED que avisa el costo antes de activarlo.\n\nGUION: el usuario NO escribe el guion desde cero \u2014 la IA lo redacta. Si quiere REVISARLO/EDITARLO antes de renderizar (igual que en la web), pasa `pauseForVideoScriptReview: true`; cuando la tarea quede pausada, usa get_video_script para leer las escenas, ofr\xE9celas al usuario, y llama approve_video_script con las ediciones. Para dirigir el guion ANTES de generar (sin pausar), usa scriptNotes. Para revisar el \xE1ngulo y decidir duraci\xF3n tras el an\xE1lisis de competencia, pasa `pauseForAngleReview: true` y luego resuelve `angle_review_approval` con `gapDurationSeconds`.', VideoInput.shape, async (input) => {
|
|
9582
10183
|
try {
|
|
9583
|
-
assertVideoPresetDurationCompatibility(input.videoPreset, input.durationSeconds);
|
|
9584
10184
|
const resolved = resolveVideoPreset(input.videoPreset);
|
|
9585
10185
|
const wireTier = resolveWireVideoTier(input.videoTier, input.durationSeconds);
|
|
9586
10186
|
const { videoTier: _publicTier, videoResolution: _vr, projectId, ...rest } = input;
|
|
@@ -9655,8 +10255,8 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
9655
10255
|
__name(registerCreativeTools, "registerCreativeTools");
|
|
9656
10256
|
|
|
9657
10257
|
// src/tools/creative-variants.ts
|
|
9658
|
-
import { z as
|
|
9659
|
-
var LandingSectionKeyEnum =
|
|
10258
|
+
import { z as z47 } from "zod";
|
|
10259
|
+
var LandingSectionKeyEnum = z47.enum([
|
|
9660
10260
|
"problem_hook",
|
|
9661
10261
|
"problem_cost",
|
|
9662
10262
|
"solution_mechanism",
|
|
@@ -9696,24 +10296,24 @@ var LandingSectionKeyEnum = z45.enum([
|
|
|
9696
10296
|
"lifestyle_fit",
|
|
9697
10297
|
"join_lifestyle"
|
|
9698
10298
|
]);
|
|
9699
|
-
var CreativeVariantKindEnum =
|
|
10299
|
+
var CreativeVariantKindEnum = z47.enum([
|
|
9700
10300
|
"video",
|
|
9701
10301
|
"image",
|
|
9702
10302
|
"landing"
|
|
9703
10303
|
]);
|
|
9704
|
-
var EstimateCreativeVariantInputBase =
|
|
9705
|
-
sourceTaskId:
|
|
10304
|
+
var EstimateCreativeVariantInputBase = z47.object({
|
|
10305
|
+
sourceTaskId: z47.string().uuid().describe("ID de la tarea original completada."),
|
|
9706
10306
|
kind: CreativeVariantKindEnum,
|
|
9707
10307
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Solo cambia el preset p\xFAblico; el servidor conserva el modelo/tier del task origen."),
|
|
9708
10308
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video. Resoluci\xF3n del nuevo render."),
|
|
9709
|
-
nVariants:
|
|
10309
|
+
nVariants: z47.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
9710
10310
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si ya conoces la secci\xF3n, evita cotizar la clasificaci\xF3n con IA."),
|
|
9711
|
-
instructions:
|
|
10311
|
+
instructions: z47.string().trim().min(1).max(2e3).optional().describe("Opcional para landing. Si se env\xEDa sin sectionKey, el estimado suma la clasificaci\xF3n pagada con IA.")
|
|
9712
10312
|
}).strict();
|
|
9713
10313
|
var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
9714
10314
|
if (input.kind === "video" && input.nVariants !== void 0) {
|
|
9715
10315
|
ctx.addIssue({
|
|
9716
|
-
code:
|
|
10316
|
+
code: z47.ZodIssueCode.custom,
|
|
9717
10317
|
path: [
|
|
9718
10318
|
"nVariants"
|
|
9719
10319
|
],
|
|
@@ -9723,7 +10323,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9723
10323
|
if (input.kind !== "video") {
|
|
9724
10324
|
if (input.videoPreset !== void 0) {
|
|
9725
10325
|
ctx.addIssue({
|
|
9726
|
-
code:
|
|
10326
|
+
code: z47.ZodIssueCode.custom,
|
|
9727
10327
|
path: [
|
|
9728
10328
|
"videoPreset"
|
|
9729
10329
|
],
|
|
@@ -9732,7 +10332,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9732
10332
|
}
|
|
9733
10333
|
if (input.videoResolution !== void 0) {
|
|
9734
10334
|
ctx.addIssue({
|
|
9735
|
-
code:
|
|
10335
|
+
code: z47.ZodIssueCode.custom,
|
|
9736
10336
|
path: [
|
|
9737
10337
|
"videoResolution"
|
|
9738
10338
|
],
|
|
@@ -9742,7 +10342,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9742
10342
|
}
|
|
9743
10343
|
if (input.kind !== "image" && input.nVariants !== void 0) {
|
|
9744
10344
|
ctx.addIssue({
|
|
9745
|
-
code:
|
|
10345
|
+
code: z47.ZodIssueCode.custom,
|
|
9746
10346
|
path: [
|
|
9747
10347
|
"nVariants"
|
|
9748
10348
|
],
|
|
@@ -9752,7 +10352,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9752
10352
|
if (input.kind !== "landing") {
|
|
9753
10353
|
if (input.sectionKey !== void 0) {
|
|
9754
10354
|
ctx.addIssue({
|
|
9755
|
-
code:
|
|
10355
|
+
code: z47.ZodIssueCode.custom,
|
|
9756
10356
|
path: [
|
|
9757
10357
|
"sectionKey"
|
|
9758
10358
|
],
|
|
@@ -9761,7 +10361,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9761
10361
|
}
|
|
9762
10362
|
if (input.instructions !== void 0) {
|
|
9763
10363
|
ctx.addIssue({
|
|
9764
|
-
code:
|
|
10364
|
+
code: z47.ZodIssueCode.custom,
|
|
9765
10365
|
path: [
|
|
9766
10366
|
"instructions"
|
|
9767
10367
|
],
|
|
@@ -9770,31 +10370,31 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9770
10370
|
}
|
|
9771
10371
|
}
|
|
9772
10372
|
});
|
|
9773
|
-
var CreateCreativeVariantInputBase =
|
|
9774
|
-
sourceTaskId:
|
|
10373
|
+
var CreateCreativeVariantInputBase = z47.object({
|
|
10374
|
+
sourceTaskId: z47.string().uuid().describe("ID de la tarea original completada."),
|
|
9775
10375
|
kind: CreativeVariantKindEnum,
|
|
9776
|
-
confirm:
|
|
9777
|
-
clientRequestId:
|
|
9778
|
-
angle:
|
|
9779
|
-
buyerPersona:
|
|
9780
|
-
scriptNotes:
|
|
9781
|
-
creativeDirection:
|
|
10376
|
+
confirm: z47.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
|
|
10377
|
+
clientRequestId: z47.string().uuid().describe("UUID estable y obligatorio para esta intenci\xF3n. Reutil\xEDzalo ante timeouts o transporte ambiguo. Si la API devuelve status=error, una nueva generaci\xF3n exige otra confirmaci\xF3n y un UUID nuevo."),
|
|
10378
|
+
angle: z47.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
|
|
10379
|
+
buyerPersona: z47.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
|
|
10380
|
+
scriptNotes: z47.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
|
|
10381
|
+
creativeDirection: z47.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
|
|
9782
10382
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Preset p\xFAblico; el servidor resuelve modelo/tier desde el task origen."),
|
|
9783
10383
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video."),
|
|
9784
|
-
generateAiMusic:
|
|
9785
|
-
nVariants:
|
|
10384
|
+
generateAiMusic: z47.boolean().optional().describe("Opcional para video."),
|
|
10385
|
+
nVariants: z47.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
9786
10386
|
imageStyle: ImageCreativeStyleEnum.optional().describe("Opcional para imagen."),
|
|
9787
10387
|
creativeArchetype: ImageCreativeArchetypeEnum.optional().describe("Opcional para imagen."),
|
|
9788
10388
|
visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe("Opcional para imagen."),
|
|
9789
10389
|
aspectRatio: ImageCreativeAspectRatioEnum.optional().describe("Opcional para imagen."),
|
|
9790
|
-
styleMix:
|
|
10390
|
+
styleMix: z47.boolean().optional().describe("Opcional para imagen."),
|
|
9791
10391
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si se omite, se clasifica con IA a partir de instructions."),
|
|
9792
|
-
instructions:
|
|
10392
|
+
instructions: z47.string().trim().min(1).max(2e3).optional().describe("Solicitud libre para una nueva secci\xF3n de landing. La IA la clasifica si no env\xEDas sectionKey.")
|
|
9793
10393
|
}).strict();
|
|
9794
10394
|
var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
9795
10395
|
if ((input.kind === "video" || input.kind === "image") && !input.angle) {
|
|
9796
10396
|
ctx.addIssue({
|
|
9797
|
-
code:
|
|
10397
|
+
code: z47.ZodIssueCode.custom,
|
|
9798
10398
|
path: [
|
|
9799
10399
|
"angle"
|
|
9800
10400
|
],
|
|
@@ -9803,7 +10403,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
9803
10403
|
}
|
|
9804
10404
|
if (input.kind === "landing" && !input.sectionKey && !input.instructions) {
|
|
9805
10405
|
ctx.addIssue({
|
|
9806
|
-
code:
|
|
10406
|
+
code: z47.ZodIssueCode.custom,
|
|
9807
10407
|
path: [
|
|
9808
10408
|
"instructions"
|
|
9809
10409
|
],
|
|
@@ -9849,7 +10449,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
9849
10449
|
for (const field of fieldsByKind[input.kind]) {
|
|
9850
10450
|
if (input[field] !== void 0) {
|
|
9851
10451
|
ctx.addIssue({
|
|
9852
|
-
code:
|
|
10452
|
+
code: z47.ZodIssueCode.custom,
|
|
9853
10453
|
path: [
|
|
9854
10454
|
field
|
|
9855
10455
|
],
|
|
@@ -9966,27 +10566,27 @@ function registerCreativeVariantTools(server, api) {
|
|
|
9966
10566
|
__name(registerCreativeVariantTools, "registerCreativeVariantTools");
|
|
9967
10567
|
|
|
9968
10568
|
// src/tools/script-review.ts
|
|
9969
|
-
import { z as
|
|
9970
|
-
var GetVideoScriptInput =
|
|
9971
|
-
taskId:
|
|
9972
|
-
angleKey:
|
|
10569
|
+
import { z as z48 } from "zod";
|
|
10570
|
+
var GetVideoScriptInput = z48.object({
|
|
10571
|
+
taskId: z48.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
|
|
10572
|
+
angleKey: z48.string().min(1).optional().describe('Clave del \xE1ngulo cuyo guion se quiere leer (formato "angle_0", "angle_1", ...). REQUERIDO para tareas multi-angle: si se omite, el backend resuelve a "angle_0" LITERAL (no "el primer pendiente"), as\xED que en multi-angle donde angle_0 ya est\xE9 aprobado/saltado obtendr\xEDas status="approved" y nunca alcanzar\xEDas el \xE1ngulo realmente pendiente. Para single-angle puede omitirse.')
|
|
9973
10573
|
}).strict();
|
|
9974
10574
|
var VIDEO_SCRIPT_APPROVAL_MAX_SCENES2 = 50;
|
|
9975
10575
|
var VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2 = 2e3;
|
|
9976
|
-
var ApproveSceneEdit =
|
|
9977
|
-
index:
|
|
9978
|
-
dialogue:
|
|
10576
|
+
var ApproveSceneEdit = z48.object({
|
|
10577
|
+
index: z48.number().int().nonnegative().describe("\xCDndice de la escena (0-based, mismo que devuelve get_video_script)."),
|
|
10578
|
+
dialogue: z48.string().min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2).refine((v) => v.trim().length > 0, {
|
|
9979
10579
|
message: "dialogue no puede ser s\xF3lo espacios"
|
|
9980
10580
|
}).describe("Di\xE1logo final de la escena. NO puede estar vac\xEDo ni ser s\xF3lo espacios. Puede ser el original sin cambios o la edici\xF3n del usuario.")
|
|
9981
10581
|
}).strict();
|
|
9982
|
-
var ApproveVideoScriptInput =
|
|
9983
|
-
taskId:
|
|
9984
|
-
angleKey:
|
|
9985
|
-
scenes:
|
|
10582
|
+
var ApproveVideoScriptInput = z48.object({
|
|
10583
|
+
taskId: z48.string().min(1).describe("ID de la tarea."),
|
|
10584
|
+
angleKey: z48.string().trim().min(1).max(120).optional().describe('Mismo angleKey usado en get_video_script. REQUERIDO en multi-angle (si se omite el backend resuelve a "angle_0" literal). En single-angle puede omitirse.'),
|
|
10585
|
+
scenes: z48.array(ApproveSceneEdit).min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_SCENES2).describe("Escenas con los di\xE1logos finales. El backend mergea por `index` \u2014 las escenas NO incluidas conservan el di\xE1logo original. Debe haber entre 1 y 50 entradas. Para cada entrada que mandes con un dialogue distinto del original, el backend valida minWords \u2264 words \u2264 maxWords y rechaza con 400 si te sales del rango.")
|
|
9986
10586
|
}).strict();
|
|
9987
|
-
var RegenerateVideoScriptInput =
|
|
9988
|
-
taskId:
|
|
9989
|
-
angleKey:
|
|
10587
|
+
var RegenerateVideoScriptInput = z48.object({
|
|
10588
|
+
taskId: z48.string().min(1).describe("ID de la tarea."),
|
|
10589
|
+
angleKey: z48.string().min(1).describe('Clave del \xE1ngulo a regenerar. Single-angle: pasa "angle_0". Multi-angle: el mismo que devuelve get_video_script. NO se puede omitir \u2014 el backend lo exige siempre.'),
|
|
9990
10590
|
narrativeStyle: NarrativeStyleEnum.describe('Narrativa para el nuevo guion. Incluye valores hist\xF3ricos porque esta herramienta tambi\xE9n regenera tareas antiguas. REQUERIDO \u2014 el backend NO acepta "mantener la actual"; siempre tienes que elegir una. Si el usuario quiere re-rolear con la misma narrativa, pasa la que ya tiene el \xE1ngulo (la ves en get_video_script o en get_task_status).')
|
|
9991
10591
|
}).strict();
|
|
9992
10592
|
function registerScriptReviewTools(server, api) {
|
|
@@ -10026,9 +10626,9 @@ function registerScriptReviewTools(server, api) {
|
|
|
10026
10626
|
__name(registerScriptReviewTools, "registerScriptReviewTools");
|
|
10027
10627
|
|
|
10028
10628
|
// src/tools/connections.ts
|
|
10029
|
-
import { z as
|
|
10629
|
+
import { z as z49 } from "zod";
|
|
10030
10630
|
function registerConnectionTools(server, api) {
|
|
10031
|
-
server.tool("list_shopify_shops", "Lista las tiendas Shopify conectadas por este usuario. Si la lista est\xE1 vac\xEDa, dile al usuario que conecte una tienda en productmaker.app/integrations antes de intentar publicar.",
|
|
10631
|
+
server.tool("list_shopify_shops", "Lista las tiendas Shopify conectadas por este usuario. Si la lista est\xE1 vac\xEDa, dile al usuario que conecte una tienda en productmaker.app/integrations antes de intentar publicar.", z49.object({}).shape, async () => {
|
|
10032
10632
|
try {
|
|
10033
10633
|
const r = await api.getJson("/v1/shopify/shops");
|
|
10034
10634
|
return asText(r);
|
|
@@ -10036,10 +10636,10 @@ function registerConnectionTools(server, api) {
|
|
|
10036
10636
|
throw translateError(e);
|
|
10037
10637
|
}
|
|
10038
10638
|
});
|
|
10039
|
-
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.',
|
|
10040
|
-
shopDomain:
|
|
10041
|
-
query:
|
|
10042
|
-
taskId:
|
|
10639
|
+
server.tool("list_shopify_products", 'Busca productos en una tienda Shopify conectada para elegir el target de una republicaci\xF3n. Usa el `id` devuelto junto con `publicationMode="existing"` en `publish_to_shopify`. Si `taskId` se proporciona, la respuesta tambi\xE9n indica el producto publicado actualmente para esa tarea y tienda. No modifica Shopify.', z49.object({
|
|
10640
|
+
shopDomain: z49.string().min(3).max(120),
|
|
10641
|
+
query: z49.string().trim().max(120).optional(),
|
|
10642
|
+
taskId: z49.string().uuid().optional()
|
|
10043
10643
|
}).shape, async ({ shopDomain, query, taskId }) => {
|
|
10044
10644
|
try {
|
|
10045
10645
|
const params = new URLSearchParams({
|
|
@@ -10053,7 +10653,7 @@ function registerConnectionTools(server, api) {
|
|
|
10053
10653
|
throw translateError(e);
|
|
10054
10654
|
}
|
|
10055
10655
|
});
|
|
10056
|
-
server.tool("list_meta_ad_accounts", "Lista las cuentas publicitarias de Meta conectadas. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar Meta en productmaker.app/integrations.",
|
|
10656
|
+
server.tool("list_meta_ad_accounts", "Lista las cuentas publicitarias de Meta conectadas. Para publicar, prefiere `list_meta_destinations`, que tambi\xE9n devuelve el portfolio y las p\xE1ginas disponibles. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar Meta en productmaker.app/integrations.", z49.object({}).shape, async () => {
|
|
10057
10657
|
try {
|
|
10058
10658
|
const r = await api.getJson("/v1/facebook/ad-accounts");
|
|
10059
10659
|
return asText(r);
|
|
@@ -10061,7 +10661,15 @@ function registerConnectionTools(server, api) {
|
|
|
10061
10661
|
throw translateError(e);
|
|
10062
10662
|
}
|
|
10063
10663
|
});
|
|
10064
|
-
server.tool("
|
|
10664
|
+
server.tool("list_meta_destinations", "Lista los destinos de publicaci\xF3n de Meta del usuario: Business portfolios, cuentas publicitarias dentro de cada portfolio y Facebook Pages disponibles. Es obligatorio llamar esta herramienta antes de publicar, mostrar las opciones y enviar al publicador el `portfolioId`, `adAccountId` y `pageId` elegidos; nunca elijas silenciosamente la primera p\xE1gina o cuenta. No modifica Meta.", z49.object({}).shape, async () => {
|
|
10665
|
+
try {
|
|
10666
|
+
const r = await api.getJson("/v1/facebook/destinations");
|
|
10667
|
+
return asText(r);
|
|
10668
|
+
} catch (e) {
|
|
10669
|
+
throw translateError(e);
|
|
10670
|
+
}
|
|
10671
|
+
});
|
|
10672
|
+
server.tool("list_tiktok_advertisers", "Lista las cuentas de TikTok Ads conectadas. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar TikTok en productmaker.app/integrations.", z49.object({}).shape, async () => {
|
|
10065
10673
|
try {
|
|
10066
10674
|
const r = await api.getJson("/v1/tiktok/advertisers");
|
|
10067
10675
|
return asText(r);
|
|
@@ -10072,106 +10680,140 @@ function registerConnectionTools(server, api) {
|
|
|
10072
10680
|
}
|
|
10073
10681
|
__name(registerConnectionTools, "registerConnectionTools");
|
|
10074
10682
|
|
|
10683
|
+
// src/tools/actors.ts
|
|
10684
|
+
import { z as z50 } from "zod";
|
|
10685
|
+
function registerActorTools(server, api) {
|
|
10686
|
+
server.tool("list_actors", "Lista los personajes guardados del usuario (personas creadas con IA que pueden protagonizar los videos e im\xE1genes). Devuelve id, nombre, descripci\xF3n, g\xE9nero y voz. Usa el `id` como `actorId` al crear o editar una tarea. Solo aparecen los personajes listos para usar; si la lista est\xE1 vac\xEDa, dile al usuario que cree uno en productmaker.app antes de intentar asignarlo. No modifica nada.", z50.object({}).shape, async () => {
|
|
10687
|
+
try {
|
|
10688
|
+
const rows = await api.getJson("/v1/actors");
|
|
10689
|
+
const actors = (Array.isArray(rows) ? rows : []).filter((actor) => actor.status === "ready").map(({ id, name, description, gender, voiceId }) => ({
|
|
10690
|
+
id,
|
|
10691
|
+
name,
|
|
10692
|
+
description,
|
|
10693
|
+
gender,
|
|
10694
|
+
voiceId: voiceId ?? null
|
|
10695
|
+
}));
|
|
10696
|
+
return asText({
|
|
10697
|
+
actors
|
|
10698
|
+
});
|
|
10699
|
+
} catch (e) {
|
|
10700
|
+
throw translateError(e);
|
|
10701
|
+
}
|
|
10702
|
+
});
|
|
10703
|
+
}
|
|
10704
|
+
__name(registerActorTools, "registerActorTools");
|
|
10705
|
+
|
|
10075
10706
|
// src/tools/publish.ts
|
|
10076
|
-
import { z as
|
|
10077
|
-
var ShopifyInput =
|
|
10078
|
-
taskId:
|
|
10079
|
-
shopDomain:
|
|
10707
|
+
import { z as z51 } from "zod";
|
|
10708
|
+
var ShopifyInput = z51.object({
|
|
10709
|
+
taskId: z51.string().min(1),
|
|
10710
|
+
shopDomain: z51.string().min(1),
|
|
10080
10711
|
/** Override: number of landing images to include. Default = ALL images the
|
|
10081
10712
|
* task generated (the MCP fetches /v1/results first to count). Bounded
|
|
10082
10713
|
* generously here; the backend validates each index against
|
|
10083
10714
|
* landing.images.length and rejects out-of-range. */
|
|
10084
|
-
numLandingImages:
|
|
10085
|
-
variantIndex:
|
|
10715
|
+
numLandingImages: z51.number().int().min(1).max(20).optional(),
|
|
10716
|
+
variantIndex: z51.number().int().min(0).optional(),
|
|
10086
10717
|
/** ASK THE USER before setting: "¿Tu producto tiene variantes (talla, color, etc.)?".
|
|
10087
10718
|
* When true, the landing renders variant-picker pills; when false, it ships
|
|
10088
10719
|
* a single-option product page. Defaults to false. */
|
|
10089
|
-
showVariantPicker:
|
|
10720
|
+
showVariantPicker: z51.boolean().optional(),
|
|
10090
10721
|
/** Default `auto` updates the product previously published for this task and
|
|
10091
10722
|
* shop. Use `existing` with targetProductId to choose another product, or
|
|
10092
10723
|
* `new` only when the user explicitly wants a duplicate/new listing. */
|
|
10093
|
-
publicationMode:
|
|
10724
|
+
publicationMode: z51.enum([
|
|
10094
10725
|
"auto",
|
|
10095
10726
|
"existing",
|
|
10096
10727
|
"new"
|
|
10097
10728
|
]).optional(),
|
|
10098
10729
|
/** Shopify product GID, numeric id, or admin product URL. Required when
|
|
10099
10730
|
* publicationMode is `existing`. */
|
|
10100
|
-
targetProductId:
|
|
10731
|
+
targetProductId: z51.string().trim().min(1).max(300).optional()
|
|
10101
10732
|
});
|
|
10102
|
-
var McpCampaignConfig =
|
|
10733
|
+
var McpCampaignConfig = z51.object({
|
|
10103
10734
|
/** ALWAYS in US dollars, whatever the ad account's currency is. ProductMaker
|
|
10104
10735
|
* converts to the account currency server-side. Do NOT pre-convert: on a COP
|
|
10105
10736
|
* account the user's "60.000 al día" is `dailyBudgetUsd: 15`, not 60000. */
|
|
10106
|
-
dailyBudgetUsd:
|
|
10737
|
+
dailyBudgetUsd: z51.number().positive().optional(),
|
|
10107
10738
|
/** ISO-3166-1 alpha-2 country codes. Meta accepts max 1 (single-country
|
|
10108
10739
|
* ad set). TikTok takes only the first entry. */
|
|
10109
|
-
targetCountries:
|
|
10110
|
-
campaignName:
|
|
10740
|
+
targetCountries: z51.array(z51.string().length(2)).max(1).optional(),
|
|
10741
|
+
campaignName: z51.string().trim().min(1).max(100).optional()
|
|
10111
10742
|
});
|
|
10112
10743
|
var McpAdAccountId = MetaAdAccountId.describe('Meta ad account id. Accepts "act_1234567890" or the bare numeric id "1234567890".');
|
|
10113
|
-
var MetaInput =
|
|
10114
|
-
taskId:
|
|
10744
|
+
var MetaInput = z51.object({
|
|
10745
|
+
taskId: z51.string().min(1),
|
|
10746
|
+
/** Exact Meta Business portfolio selected from `list_meta_destinations`. */
|
|
10747
|
+
portfolioId: z51.string().trim().min(1),
|
|
10748
|
+
/** Exact Facebook Page selected from `list_meta_destinations`. */
|
|
10749
|
+
pageId: z51.string().trim().min(1),
|
|
10115
10750
|
adAccountId: McpAdAccountId,
|
|
10116
|
-
pixelId:
|
|
10117
|
-
landingUrl:
|
|
10751
|
+
pixelId: z51.string().min(1).optional(),
|
|
10752
|
+
landingUrl: z51.string().url().optional(),
|
|
10118
10753
|
/** Select which angles to publish by 0-based index. Omit to publish all
|
|
10119
10754
|
* successful angles. Example: a task with 3 angles → `[0,2]` publishes #0 + #2. */
|
|
10120
|
-
angleIndexes:
|
|
10755
|
+
angleIndexes: z51.array(z51.number().int().nonnegative()).optional(),
|
|
10121
10756
|
campaignConfig: McpCampaignConfig.optional(),
|
|
10122
10757
|
/** Override the ad's primary text (body copy). If omitted, ProductMaker
|
|
10123
10758
|
* uses the auto-generated copy attached to the task. Capped at 500 chars
|
|
10124
10759
|
* (Meta's primary-text limit). */
|
|
10125
|
-
primaryText:
|
|
10760
|
+
primaryText: z51.string().trim().min(1).max(500).optional(),
|
|
10126
10761
|
/** Optional WhatsApp number. When set, the ad button opens a WhatsApp chat
|
|
10127
10762
|
* (wa.me) instead of a landing page. */
|
|
10128
|
-
phoneNumber:
|
|
10763
|
+
phoneNumber: z51.string().min(8).max(32).optional()
|
|
10129
10764
|
});
|
|
10130
|
-
var GroupedTaskSource =
|
|
10131
|
-
type:
|
|
10132
|
-
taskId:
|
|
10133
|
-
angleIndex:
|
|
10134
|
-
videoVariant:
|
|
10765
|
+
var GroupedTaskSource = z51.object({
|
|
10766
|
+
type: z51.literal("task"),
|
|
10767
|
+
taskId: z51.string().min(1),
|
|
10768
|
+
angleIndex: z51.number().int().min(0).optional(),
|
|
10769
|
+
videoVariant: z51.enum([
|
|
10135
10770
|
"with_subtitles",
|
|
10136
10771
|
"without_subtitles"
|
|
10137
10772
|
]).optional(),
|
|
10138
|
-
imageVariantIndex:
|
|
10139
|
-
});
|
|
10140
|
-
var GroupedMediaSource =
|
|
10141
|
-
type:
|
|
10142
|
-
url:
|
|
10143
|
-
path:
|
|
10144
|
-
thumbnailUrl:
|
|
10145
|
-
thumbnailPath:
|
|
10773
|
+
imageVariantIndex: z51.number().int().min(0).optional()
|
|
10774
|
+
});
|
|
10775
|
+
var GroupedMediaSource = z51.object({
|
|
10776
|
+
type: z51.literal("media"),
|
|
10777
|
+
url: z51.string().url().optional().describe("Public HTTPS media URL. For a file on the user laptop, use request_meta_media_upload on the hosted MCP instead."),
|
|
10778
|
+
path: z51.string().min(1).optional().describe('Local file path only when this MCP runs over stdio. On the hosted MCP, call request_meta_media_upload and then use source.type="staged".'),
|
|
10779
|
+
thumbnailUrl: z51.string().url().optional(),
|
|
10780
|
+
thumbnailPath: z51.string().min(1).optional()
|
|
10146
10781
|
}).refine((value) => Boolean(value.url) !== Boolean(value.path), {
|
|
10147
10782
|
message: "media source needs exactly one of `url` or `path`"
|
|
10148
10783
|
}).refine((value) => !(value.thumbnailUrl && value.thumbnailPath), {
|
|
10149
10784
|
message: "media source needs at most one of `thumbnailUrl` or `thumbnailPath`"
|
|
10150
10785
|
});
|
|
10151
|
-
var
|
|
10152
|
-
type:
|
|
10786
|
+
var GroupedStagedMediaSource = z51.object({
|
|
10787
|
+
type: z51.literal("staged"),
|
|
10788
|
+
/** Returned by check_meta_media_upload after the user selects a local file. */
|
|
10789
|
+
stagingAssetId: z51.string().uuid(),
|
|
10790
|
+
thumbnailAssetId: z51.string().uuid().optional()
|
|
10791
|
+
});
|
|
10792
|
+
var GroupedCreative = z51.object({
|
|
10793
|
+
type: z51.enum([
|
|
10153
10794
|
"image",
|
|
10154
10795
|
"video"
|
|
10155
10796
|
]),
|
|
10156
|
-
source:
|
|
10797
|
+
source: z51.union([
|
|
10157
10798
|
GroupedTaskSource,
|
|
10158
|
-
GroupedMediaSource
|
|
10799
|
+
GroupedMediaSource,
|
|
10800
|
+
GroupedStagedMediaSource
|
|
10159
10801
|
]),
|
|
10160
|
-
title:
|
|
10161
|
-
description:
|
|
10802
|
+
title: z51.string().max(120).optional(),
|
|
10803
|
+
description: z51.string().max(500).optional(),
|
|
10162
10804
|
cta: MetaCtaType.optional(),
|
|
10163
|
-
creativeId:
|
|
10805
|
+
creativeId: z51.string().min(1).optional()
|
|
10164
10806
|
});
|
|
10165
|
-
var GroupedCampaignConfig =
|
|
10807
|
+
var GroupedCampaignConfig = z51.object({
|
|
10166
10808
|
campaignType: MetaCampaignType.default("sales"),
|
|
10167
10809
|
optimizationMode: MetaOptimizationMode.default("advantage_plus"),
|
|
10168
10810
|
budgetMode: MetaBudgetMode.default("campaign"),
|
|
10169
|
-
campaignDailyBudget:
|
|
10170
|
-
adSets:
|
|
10171
|
-
tempId:
|
|
10172
|
-
name:
|
|
10173
|
-
dailyBudget:
|
|
10174
|
-
creativeIds:
|
|
10811
|
+
campaignDailyBudget: z51.number().int().positive().optional(),
|
|
10812
|
+
adSets: z51.array(z51.object({
|
|
10813
|
+
tempId: z51.string().min(1).max(80),
|
|
10814
|
+
name: z51.string().min(1).max(100),
|
|
10815
|
+
dailyBudget: z51.number().int().positive().optional(),
|
|
10816
|
+
creativeIds: z51.array(z51.string().min(1)).min(1)
|
|
10175
10817
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_AD_SETS),
|
|
10176
10818
|
/**
|
|
10177
10819
|
* Valid values are 'landing' and 'whatsapp' — there is no 'website'. This
|
|
@@ -10179,22 +10821,22 @@ var GroupedCampaignConfig = z48.object({
|
|
|
10179
10821
|
* opaque `invalid_literal` and made the WhatsApp destination — supported by
|
|
10180
10822
|
* the API since #849 — unreachable from this tool.
|
|
10181
10823
|
*/
|
|
10182
|
-
destination:
|
|
10183
|
-
|
|
10184
|
-
type:
|
|
10185
|
-
landingUrl:
|
|
10824
|
+
destination: z51.discriminatedUnion("type", [
|
|
10825
|
+
z51.object({
|
|
10826
|
+
type: z51.literal("landing"),
|
|
10827
|
+
landingUrl: z51.string().url()
|
|
10186
10828
|
}),
|
|
10187
|
-
|
|
10188
|
-
type:
|
|
10189
|
-
phoneNumber:
|
|
10829
|
+
z51.object({
|
|
10830
|
+
type: z51.literal("whatsapp"),
|
|
10831
|
+
phoneNumber: z51.string().min(8).max(32)
|
|
10190
10832
|
})
|
|
10191
10833
|
]),
|
|
10192
|
-
saveAsDefault:
|
|
10834
|
+
saveAsDefault: z51.boolean().default(false),
|
|
10193
10835
|
budgetPolicy: MetaBudgetPolicy.optional()
|
|
10194
10836
|
}).superRefine((value, ctx) => {
|
|
10195
10837
|
if (value.budgetMode === "campaign" && value.campaignDailyBudget === void 0) {
|
|
10196
10838
|
ctx.addIssue({
|
|
10197
|
-
code:
|
|
10839
|
+
code: z51.ZodIssueCode.custom,
|
|
10198
10840
|
path: [
|
|
10199
10841
|
"campaignDailyBudget"
|
|
10200
10842
|
],
|
|
@@ -10205,7 +10847,7 @@ var GroupedCampaignConfig = z48.object({
|
|
|
10205
10847
|
value.adSets.forEach((adSet, index) => {
|
|
10206
10848
|
if (adSet.dailyBudget === void 0) {
|
|
10207
10849
|
ctx.addIssue({
|
|
10208
|
-
code:
|
|
10850
|
+
code: z51.ZodIssueCode.custom,
|
|
10209
10851
|
path: [
|
|
10210
10852
|
"adSets",
|
|
10211
10853
|
index,
|
|
@@ -10217,22 +10859,26 @@ var GroupedCampaignConfig = z48.object({
|
|
|
10217
10859
|
});
|
|
10218
10860
|
}
|
|
10219
10861
|
});
|
|
10220
|
-
var PublishCreativesToMetaInputBase =
|
|
10221
|
-
creatives:
|
|
10862
|
+
var PublishCreativesToMetaInputBase = z51.object({
|
|
10863
|
+
creatives: z51.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
10864
|
+
/** Exact Meta Business portfolio selected from `list_meta_destinations`. */
|
|
10865
|
+
portfolioId: z51.string().trim().min(1),
|
|
10866
|
+
/** Exact Facebook Page selected from `list_meta_destinations`. */
|
|
10867
|
+
pageId: z51.string().trim().min(1),
|
|
10222
10868
|
adAccountId: McpAdAccountId,
|
|
10223
|
-
pixelId:
|
|
10869
|
+
pixelId: z51.string().min(1).optional(),
|
|
10224
10870
|
/** Required unless `campaignConfig.destination` is whatsapp — see the
|
|
10225
10871
|
* superRefine below. Mirrors the API's `UploadToMetaRequest`. */
|
|
10226
|
-
landingUrl:
|
|
10227
|
-
targetCountry:
|
|
10228
|
-
campaignName:
|
|
10872
|
+
landingUrl: z51.string().url().optional(),
|
|
10873
|
+
targetCountry: z51.string().length(2),
|
|
10874
|
+
campaignName: z51.string().trim().min(1).max(100),
|
|
10229
10875
|
campaignConfig: GroupedCampaignConfig.optional()
|
|
10230
10876
|
});
|
|
10231
10877
|
var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((value, ctx) => {
|
|
10232
10878
|
const destination = value.campaignConfig?.destination;
|
|
10233
10879
|
if (destination?.type !== "whatsapp" && !value.landingUrl) {
|
|
10234
10880
|
ctx.addIssue({
|
|
10235
|
-
code:
|
|
10881
|
+
code: z51.ZodIssueCode.custom,
|
|
10236
10882
|
path: [
|
|
10237
10883
|
"landingUrl"
|
|
10238
10884
|
],
|
|
@@ -10241,7 +10887,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
10241
10887
|
}
|
|
10242
10888
|
if (destination?.type === "landing" && destination.landingUrl !== value.landingUrl) {
|
|
10243
10889
|
ctx.addIssue({
|
|
10244
|
-
code:
|
|
10890
|
+
code: z51.ZodIssueCode.custom,
|
|
10245
10891
|
path: [
|
|
10246
10892
|
"campaignConfig",
|
|
10247
10893
|
"destination",
|
|
@@ -10251,46 +10897,50 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
10251
10897
|
});
|
|
10252
10898
|
}
|
|
10253
10899
|
});
|
|
10254
|
-
var PresignUploadResponseSchema =
|
|
10255
|
-
uploadUrl:
|
|
10900
|
+
var PresignUploadResponseSchema = z51.object({
|
|
10901
|
+
uploadUrl: z51.string().url().refine((value) => {
|
|
10256
10902
|
const url = new URL(value);
|
|
10257
10903
|
return !url.username && !url.password && url.protocol === "https:";
|
|
10258
10904
|
}, "presigned upload URL must be HTTPS without embedded credentials"),
|
|
10259
|
-
publicUrl:
|
|
10905
|
+
publicUrl: z51.string().url().refine((value) => {
|
|
10260
10906
|
const url = new URL(value);
|
|
10261
10907
|
return !url.username && !url.password;
|
|
10262
|
-
}, "public media URL must not contain embedded credentials")
|
|
10908
|
+
}, "public media URL must not contain embedded credentials"),
|
|
10909
|
+
stagingAssetId: z51.string().uuid()
|
|
10263
10910
|
});
|
|
10264
|
-
async function uploadResolvedMedia(api, media) {
|
|
10265
|
-
if (media.url) return media.url;
|
|
10911
|
+
async function uploadResolvedMedia(api, media, assetRole) {
|
|
10266
10912
|
if (!media.buffer) throw new ResolveMediaError("INVALID_INPUT", "resolved media has no bytes");
|
|
10267
10913
|
const presigned = PresignUploadResponseSchema.parse(await api.postJson("/v1/ads/presign-upload", {
|
|
10268
10914
|
contentType: media.mimetype,
|
|
10269
10915
|
fileName: media.filename,
|
|
10270
|
-
fileSize: media.buffer.byteLength
|
|
10916
|
+
fileSize: media.buffer.byteLength,
|
|
10917
|
+
purpose: "meta_staging",
|
|
10918
|
+
assetRole
|
|
10271
10919
|
}));
|
|
10272
10920
|
await api.putBytes(presigned.uploadUrl, media.buffer, media.mimetype);
|
|
10273
|
-
return
|
|
10921
|
+
return {
|
|
10922
|
+
stagingAssetId: presigned.stagingAssetId
|
|
10923
|
+
};
|
|
10274
10924
|
}
|
|
10275
10925
|
__name(uploadResolvedMedia, "uploadResolvedMedia");
|
|
10276
|
-
async function resolveAndUploadMedia(api, ref, expectedKind, transport) {
|
|
10926
|
+
async function resolveAndUploadMedia(api, ref, expectedKind, assetRole, transport) {
|
|
10277
10927
|
const media = await resolveMediaRef(ref, {
|
|
10278
10928
|
transport,
|
|
10279
10929
|
expectedKind
|
|
10280
10930
|
});
|
|
10281
|
-
return uploadResolvedMedia(api, media);
|
|
10931
|
+
return uploadResolvedMedia(api, media, assetRole);
|
|
10282
10932
|
}
|
|
10283
10933
|
__name(resolveAndUploadMedia, "resolveAndUploadMedia");
|
|
10284
|
-
var TikTokInput =
|
|
10285
|
-
taskId:
|
|
10286
|
-
advertiserId:
|
|
10287
|
-
pixelId:
|
|
10288
|
-
landingUrl:
|
|
10289
|
-
angleIndexes:
|
|
10934
|
+
var TikTokInput = z51.object({
|
|
10935
|
+
taskId: z51.string().min(1),
|
|
10936
|
+
advertiserId: z51.string().min(1),
|
|
10937
|
+
pixelId: z51.string().min(1).optional(),
|
|
10938
|
+
landingUrl: z51.string().url().optional(),
|
|
10939
|
+
angleIndexes: z51.array(z51.number().int().nonnegative()).optional(),
|
|
10290
10940
|
campaignConfig: McpCampaignConfig.optional(),
|
|
10291
10941
|
/** Override the ad's description (TikTok's body copy). If omitted,
|
|
10292
10942
|
* ProductMaker uses the auto-generated copy attached to the task. */
|
|
10293
|
-
primaryText:
|
|
10943
|
+
primaryText: z51.string().trim().min(1).max(500).optional()
|
|
10294
10944
|
});
|
|
10295
10945
|
function registerPublishTools(server, api, opts = {}) {
|
|
10296
10946
|
const transport = opts.transport ?? "http";
|
|
@@ -10329,7 +10979,7 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
10329
10979
|
throw translateError(e);
|
|
10330
10980
|
}
|
|
10331
10981
|
});
|
|
10332
|
-
server.tool("publish_to_meta", "Crea una campa\xF1a de Meta Ads (Facebook/Instagram) desde UNA sola tarea.
|
|
10982
|
+
server.tool("publish_to_meta", "Crea una campa\xF1a de Meta Ads (Facebook/Instagram) desde UNA sola tarea. Antes llama `list_meta_destinations`, deja que el usuario elija el `portfolioId` (Business portfolio), el `adAccountId` y el `pageId` exactos, y env\xEDa esos tres valores; no adivines ni uses la primera p\xE1gina. Si quieres juntar creativos de VARIAS tareas usa `publish_creatives_to_meta`. La campa\xF1a se crea SIEMPRE PAUSED y el usuario la activa manualmente. `pixelId` es opcional. `angleIndexes` permite seleccionar \xE1ngulos. `campaignConfig` admite presupuesto en USD, pa\xEDses ISO-2 y nombre. `primaryText` permite reemplazar el copy y `phoneNumber` configura WhatsApp. Despu\xE9s usa `get_meta_campaign_status`.", MetaInput.shape, async ({ taskId, campaignConfig, primaryText, phoneNumber, ...rest }) => {
|
|
10333
10983
|
try {
|
|
10334
10984
|
const body = {
|
|
10335
10985
|
...rest
|
|
@@ -10357,7 +11007,7 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
10357
11007
|
throw translateError(e);
|
|
10358
11008
|
}
|
|
10359
11009
|
});
|
|
10360
|
-
server.tool("publish_creatives_to_meta", "Crea UNA campa\xF1a de Meta Ads
|
|
11010
|
+
server.tool("publish_creatives_to_meta", "Crea UNA campa\xF1a de Meta Ads PAUSED agrupando hasta " + CAMPAIGN_LIMITS.MAX_CREATIVES + ' creativos. Antes llama `list_meta_destinations` y pide al usuario elegir el `portfolioId`, `adAccountId` y `pageId` exactos. Para archivos en la laptop con MCP remoto: llama `request_meta_media_upload`, espera al usuario y consulta `check_meta_media_upload`; por CADA entrada de `assets[]` crea un creativo `source.type="staged"`, con `type` igual a su `mediaKind`. Si la carga fue parcial, publica s\xF3lo los assets que ese estado confirm\xF3; nunca inventes assets ni superes diez creativos. Las rutas locales s\xF3lo funcionan con MCP stdio. Los medios propios se suben temporalmente al CDN de ProductMaker y se eliminan despu\xE9s de publicar correctamente; si falla, quedan s\xF3lo para reintento y el TTL los limpia. Para videos la miniatura es opcional y ProductMaker puede generarla con FFmpeg. `campaignConfig.destination` acepta landing o WhatsApp. No actives la campa\xF1a autom\xE1ticamente.', PublishCreativesToMetaInputBase.shape, async (rawInput) => {
|
|
10361
11011
|
try {
|
|
10362
11012
|
const parsed = PublishCreativesToMetaInput.safeParse(rawInput);
|
|
10363
11013
|
if (!parsed.success) throw invalidInputFromZod(parsed.error);
|
|
@@ -10383,25 +11033,44 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
10383
11033
|
});
|
|
10384
11034
|
continue;
|
|
10385
11035
|
}
|
|
10386
|
-
|
|
11036
|
+
if (creative.source.type === "staged") {
|
|
11037
|
+
creatives.push({
|
|
11038
|
+
type: creative.type,
|
|
11039
|
+
source: {
|
|
11040
|
+
type: "media",
|
|
11041
|
+
mediaAssetId: creative.source.stagingAssetId,
|
|
11042
|
+
...creative.source.thumbnailAssetId ? {
|
|
11043
|
+
thumbnailAssetId: creative.source.thumbnailAssetId
|
|
11044
|
+
} : {}
|
|
11045
|
+
},
|
|
11046
|
+
title: creative.title,
|
|
11047
|
+
description: creative.description,
|
|
11048
|
+
cta: creative.cta,
|
|
11049
|
+
creativeId
|
|
11050
|
+
});
|
|
11051
|
+
continue;
|
|
11052
|
+
}
|
|
11053
|
+
const stagedMedia = await resolveAndUploadMedia(api, {
|
|
10387
11054
|
url: creative.source.url,
|
|
10388
11055
|
path: creative.source.path
|
|
10389
|
-
}, creative.type, transport);
|
|
10390
|
-
let
|
|
11056
|
+
}, creative.type, "media", transport);
|
|
11057
|
+
let stagedThumbnail;
|
|
10391
11058
|
if (creative.type === "video") {
|
|
10392
11059
|
if (creative.source.thumbnailUrl || creative.source.thumbnailPath) {
|
|
10393
|
-
|
|
11060
|
+
stagedThumbnail = await resolveAndUploadMedia(api, {
|
|
10394
11061
|
url: creative.source.thumbnailUrl,
|
|
10395
11062
|
path: creative.source.thumbnailPath
|
|
10396
|
-
}, "image", transport);
|
|
11063
|
+
}, "image", "thumbnail", transport);
|
|
10397
11064
|
}
|
|
10398
11065
|
}
|
|
10399
11066
|
creatives.push({
|
|
10400
11067
|
type: creative.type,
|
|
10401
11068
|
source: {
|
|
10402
11069
|
type: "media",
|
|
10403
|
-
|
|
10404
|
-
|
|
11070
|
+
mediaAssetId: stagedMedia.stagingAssetId,
|
|
11071
|
+
...stagedThumbnail ? {
|
|
11072
|
+
thumbnailAssetId: stagedThumbnail.stagingAssetId
|
|
11073
|
+
} : {}
|
|
10405
11074
|
},
|
|
10406
11075
|
title: creative.title,
|
|
10407
11076
|
description: creative.description,
|
|
@@ -10411,6 +11080,8 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
10411
11080
|
}
|
|
10412
11081
|
const r = await api.postJson("/v1/facebook/campaigns/from-tasks", {
|
|
10413
11082
|
creatives,
|
|
11083
|
+
portfolioId: input.portfolioId,
|
|
11084
|
+
pageId: input.pageId,
|
|
10414
11085
|
adAccountId: input.adAccountId,
|
|
10415
11086
|
pixelId: input.pixelId,
|
|
10416
11087
|
landingUrl: input.landingUrl,
|
|
@@ -10500,14 +11171,146 @@ function toTikTokCampaignConfig(cfg) {
|
|
|
10500
11171
|
}
|
|
10501
11172
|
__name(toTikTokCampaignConfig, "toTikTokCampaignConfig");
|
|
10502
11173
|
|
|
11174
|
+
// src/tools/meta-upload.ts
|
|
11175
|
+
import { z as z52 } from "zod";
|
|
11176
|
+
var MetaUploadSessionInputBase = z52.object({
|
|
11177
|
+
mediaKind: z52.enum([
|
|
11178
|
+
"image",
|
|
11179
|
+
"video",
|
|
11180
|
+
"mixed"
|
|
11181
|
+
]).default("mixed").describe("The kind of file the user will select in the upload page. Use mixed for a normal batch of image and video creatives."),
|
|
11182
|
+
assetRole: z52.enum([
|
|
11183
|
+
"media",
|
|
11184
|
+
"thumbnail"
|
|
11185
|
+
]).default("media").describe("Use media for the creative itself; thumbnail only for an image thumbnail.")
|
|
11186
|
+
});
|
|
11187
|
+
var OpenAiFileReference = z52.object({
|
|
11188
|
+
download_url: z52.string().url(),
|
|
11189
|
+
file_id: z52.string().min(1),
|
|
11190
|
+
mime_type: z52.string().min(1).optional(),
|
|
11191
|
+
file_name: z52.string().min(1).optional()
|
|
11192
|
+
}).strict();
|
|
11193
|
+
var MetaUploadSessionToolInputShape = {
|
|
11194
|
+
...MetaUploadSessionInputBase.shape,
|
|
11195
|
+
files: z52.array(OpenAiFileReference).max(10).optional().describe("Optional OpenAI-hosted file references provided by ChatGPT. They are only metadata for the UI bridge and are never forwarded to ProductMaker.")
|
|
11196
|
+
};
|
|
11197
|
+
var MetaUploadSessionToolInput = z52.object(MetaUploadSessionToolInputShape).superRefine((value, ctx) => {
|
|
11198
|
+
if (value.assetRole === "thumbnail" && value.mediaKind !== "image") {
|
|
11199
|
+
ctx.addIssue({
|
|
11200
|
+
code: z52.ZodIssueCode.custom,
|
|
11201
|
+
path: [
|
|
11202
|
+
"mediaKind"
|
|
11203
|
+
],
|
|
11204
|
+
message: "Meta thumbnails must be images"
|
|
11205
|
+
});
|
|
11206
|
+
}
|
|
11207
|
+
});
|
|
11208
|
+
var MetaUploadSessionResponse2 = z52.object({
|
|
11209
|
+
sessionId: z52.string().uuid(),
|
|
11210
|
+
uploadPageUrl: z52.string().url(),
|
|
11211
|
+
mediaKind: z52.enum([
|
|
11212
|
+
"image",
|
|
11213
|
+
"video",
|
|
11214
|
+
"mixed"
|
|
11215
|
+
]),
|
|
11216
|
+
assetRole: z52.enum([
|
|
11217
|
+
"media",
|
|
11218
|
+
"thumbnail"
|
|
11219
|
+
]),
|
|
11220
|
+
maxFiles: z52.number().int().positive(),
|
|
11221
|
+
expiresAt: z52.string().datetime()
|
|
11222
|
+
});
|
|
11223
|
+
var MetaUploadStatusInput = z52.object({
|
|
11224
|
+
sessionId: z52.string().uuid().describe("The sessionId returned by request_meta_media_upload.")
|
|
11225
|
+
});
|
|
11226
|
+
var MetaUploadStatusResponse = z52.object({
|
|
11227
|
+
sessionId: z52.string().uuid(),
|
|
11228
|
+
status: z52.enum([
|
|
11229
|
+
"pending",
|
|
11230
|
+
"uploaded"
|
|
11231
|
+
]),
|
|
11232
|
+
assets: z52.array(z52.object({
|
|
11233
|
+
stagingAssetId: z52.string().uuid(),
|
|
11234
|
+
mediaKind: z52.enum([
|
|
11235
|
+
"image",
|
|
11236
|
+
"video"
|
|
11237
|
+
])
|
|
11238
|
+
})).max(10).default([]),
|
|
11239
|
+
stagingAssetId: z52.string().uuid().optional()
|
|
11240
|
+
});
|
|
11241
|
+
function uploadSessionText(result) {
|
|
11242
|
+
const mediaLabel = result.mediaKind === "mixed" ? "archivos" : result.mediaKind === "video" ? "video" : "imagen";
|
|
11243
|
+
return {
|
|
11244
|
+
content: [
|
|
11245
|
+
{
|
|
11246
|
+
type: "text",
|
|
11247
|
+
text: [
|
|
11248
|
+
`### Sube tu ${mediaLabel} a ProductMaker`,
|
|
11249
|
+
`[Abrir carga segura](${result.uploadPageUrl})`,
|
|
11250
|
+
`Puedes seleccionar hasta ${result.maxFiles} ${result.mediaKind === "mixed" ? "archivos" : "archivo"}. Cuando termine, escribe **\u201Clisto\u201D** en este chat para que ProductMaker contin\xFAe con Meta.`,
|
|
11251
|
+
`sessionId: \`${result.sessionId}\``,
|
|
11252
|
+
`Este enlace expira: ${result.expiresAt}`
|
|
11253
|
+
].join("\n\n")
|
|
11254
|
+
}
|
|
11255
|
+
]
|
|
11256
|
+
};
|
|
11257
|
+
}
|
|
11258
|
+
__name(uploadSessionText, "uploadSessionText");
|
|
11259
|
+
function registerMetaUploadTools(server, api) {
|
|
11260
|
+
server.registerTool("request_meta_media_upload", {
|
|
11261
|
+
title: "Solicitar carga segura de Meta",
|
|
11262
|
+
description: "Inicia una carga segura desde la laptop del usuario para creativos propios de Meta. Por defecto crea una sesi\xF3n `mixed` de hasta diez archivos; tambi\xE9n admite una sola imagen o video. En ChatGPT puede recibir referencias opcionales de `files`, pero NO las descarga ni las reenv\xEDa: siempre devuelve un enlace de ProductMaker donde el usuario selecciona los archivos. No pidas URL CDN, ruta local, bytes ni base64. Despu\xE9s conserva el `sessionId` y llama `check_meta_media_upload`. Esta herramienta no publica ni consume cr\xE9ditos.",
|
|
11263
|
+
inputSchema: MetaUploadSessionToolInputShape,
|
|
11264
|
+
outputSchema: MetaUploadSessionResponse2,
|
|
11265
|
+
_meta: {
|
|
11266
|
+
ui: {
|
|
11267
|
+
resourceUri: META_UPLOAD_APP_RESOURCE_URI,
|
|
11268
|
+
visibility: [
|
|
11269
|
+
"model",
|
|
11270
|
+
"app"
|
|
11271
|
+
]
|
|
11272
|
+
},
|
|
11273
|
+
"openai/outputTemplate": META_UPLOAD_APP_RESOURCE_URI,
|
|
11274
|
+
"openai/widgetAccessible": true,
|
|
11275
|
+
"openai/fileParams": [
|
|
11276
|
+
"files"
|
|
11277
|
+
],
|
|
11278
|
+
"openai/toolInvocation/invoking": "Preparando carga segura\u2026",
|
|
11279
|
+
"openai/toolInvocation/invoked": "Carga segura lista"
|
|
11280
|
+
}
|
|
11281
|
+
}, async (rawInput) => {
|
|
11282
|
+
try {
|
|
11283
|
+
const input = MetaUploadSessionToolInput.parse(rawInput);
|
|
11284
|
+
const { files: _files, ...sessionInput } = input;
|
|
11285
|
+
const result = MetaUploadSessionResponse2.parse(await api.postJson("/v1/ads/meta-upload-sessions", sessionInput));
|
|
11286
|
+
return {
|
|
11287
|
+
structuredContent: result,
|
|
11288
|
+
...uploadSessionText(result)
|
|
11289
|
+
};
|
|
11290
|
+
} catch (error) {
|
|
11291
|
+
throw translateError(error);
|
|
11292
|
+
}
|
|
11293
|
+
});
|
|
11294
|
+
server.tool("check_meta_media_upload", 'Comprueba si el usuario ya termin\xF3 de subir desde el enlace de ProductMaker. Si status=uploaded, devuelve `assets[]`; crea un creativo `source.type="staged"` por cada asset, usando su `mediaKind` como el tipo del creativo, hasta diez en el batch. Si la carga fue parcial, publica \xFAnicamente los assets que `assets[]` reporta como subidos. Conserva el `stagingAssetId` legado si aparece. Si status=pending, pide al usuario que termine; no publiques todav\xEDa.', MetaUploadStatusInput.shape, async (rawInput) => {
|
|
11295
|
+
try {
|
|
11296
|
+
const input = MetaUploadStatusInput.parse(rawInput);
|
|
11297
|
+
const result = MetaUploadStatusResponse.parse(await api.getJson(`/v1/ads/meta-upload-sessions/${encodeURIComponent(input.sessionId)}`));
|
|
11298
|
+
return asText(result);
|
|
11299
|
+
} catch (error) {
|
|
11300
|
+
throw translateError(error);
|
|
11301
|
+
}
|
|
11302
|
+
});
|
|
11303
|
+
}
|
|
11304
|
+
__name(registerMetaUploadTools, "registerMetaUploadTools");
|
|
11305
|
+
|
|
10503
11306
|
// src/tools/campaigns.ts
|
|
10504
|
-
import { z as
|
|
10505
|
-
var MetaStatusInput =
|
|
10506
|
-
campaignId:
|
|
11307
|
+
import { z as z53 } from "zod";
|
|
11308
|
+
var MetaStatusInput = z53.object({
|
|
11309
|
+
campaignId: z53.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
|
|
10507
11310
|
});
|
|
10508
|
-
var TikTokStatusInput =
|
|
10509
|
-
campaignId:
|
|
10510
|
-
advertiserId:
|
|
11311
|
+
var TikTokStatusInput = z53.object({
|
|
11312
|
+
campaignId: z53.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
|
|
11313
|
+
advertiserId: z53.string().min(1).describe("The same advertiserId used to publish the campaign.")
|
|
10511
11314
|
});
|
|
10512
11315
|
function registerCampaignTools(server, api) {
|
|
10513
11316
|
server.tool("get_meta_campaign_status", "Consulta el estado actual + m\xE9tricas lifetime (spend, impressions, clicks, reach) de una campa\xF1a Meta publicada. Llama esto DESPU\xC9S de `publish_to_meta` para confirmar que la campa\xF1a se cre\xF3 correctamente y para reportar performance al usuario. Es read-only \u2014 no modifica nada en Meta Ads Manager.", MetaStatusInput.shape, async (input) => {
|
|
@@ -10604,10 +11407,10 @@ function renderTikTokStatusMarkdown(r) {
|
|
|
10604
11407
|
__name(renderTikTokStatusMarkdown, "renderTikTokStatusMarkdown");
|
|
10605
11408
|
|
|
10606
11409
|
// ../../packages/product-economics/src/product-costing.ts
|
|
10607
|
-
import { z as
|
|
11410
|
+
import { z as z55 } from "zod";
|
|
10608
11411
|
|
|
10609
11412
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
10610
|
-
import { z as
|
|
11413
|
+
import { z as z54 } from "zod";
|
|
10611
11414
|
|
|
10612
11415
|
// ../../packages/product-economics/src/product-costing-shipping-references.json
|
|
10613
11416
|
var product_costing_shipping_references_default = {
|
|
@@ -10671,21 +11474,21 @@ var product_costing_shipping_references_default = {
|
|
|
10671
11474
|
};
|
|
10672
11475
|
|
|
10673
11476
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
10674
|
-
var ShippingReferenceSchema =
|
|
10675
|
-
reference:
|
|
10676
|
-
min:
|
|
10677
|
-
max:
|
|
10678
|
-
updatedAt:
|
|
10679
|
-
confidence:
|
|
11477
|
+
var ShippingReferenceSchema = z54.object({
|
|
11478
|
+
reference: z54.number().positive(),
|
|
11479
|
+
min: z54.number().nonnegative(),
|
|
11480
|
+
max: z54.number().positive(),
|
|
11481
|
+
updatedAt: z54.string().date(),
|
|
11482
|
+
confidence: z54.enum([
|
|
10680
11483
|
"high",
|
|
10681
11484
|
"medium",
|
|
10682
11485
|
"medium-low"
|
|
10683
11486
|
]),
|
|
10684
|
-
sources:
|
|
11487
|
+
sources: z54.array(z54.string().url().startsWith("https://")).min(1).max(3)
|
|
10685
11488
|
}).superRefine(({ reference, min, max }, context) => {
|
|
10686
11489
|
if (min > reference || reference > max) {
|
|
10687
11490
|
context.addIssue({
|
|
10688
|
-
code:
|
|
11491
|
+
code: z54.ZodIssueCode.custom,
|
|
10689
11492
|
message: "shipping_reference_outside_range",
|
|
10690
11493
|
path: [
|
|
10691
11494
|
"reference"
|
|
@@ -10693,7 +11496,7 @@ var ShippingReferenceSchema = z50.object({
|
|
|
10693
11496
|
});
|
|
10694
11497
|
}
|
|
10695
11498
|
});
|
|
10696
|
-
var SHIPPING_REFERENCES =
|
|
11499
|
+
var SHIPPING_REFERENCES = z54.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
|
|
10697
11500
|
var PRODUCT_COSTING_COUNTRY_CODES = Object.freeze(Object.keys(SHIPPING_REFERENCES));
|
|
10698
11501
|
var SPANISH_LOCALE_COUNTRIES = /* @__PURE__ */ new Set([
|
|
10699
11502
|
"MX",
|
|
@@ -10875,18 +11678,18 @@ function getProductCostingCountryName(country) {
|
|
|
10875
11678
|
__name(getProductCostingCountryName, "getProductCostingCountryName");
|
|
10876
11679
|
|
|
10877
11680
|
// ../../packages/product-economics/src/product-costing.ts
|
|
10878
|
-
var ProductCostingInputSchema =
|
|
10879
|
-
country:
|
|
10880
|
-
supplierCost:
|
|
10881
|
-
shippingCost:
|
|
10882
|
-
returnRate:
|
|
10883
|
-
cancellationRate:
|
|
10884
|
-
adsManagerCpa:
|
|
10885
|
-
desiredMargin:
|
|
11681
|
+
var ProductCostingInputSchema = z55.object({
|
|
11682
|
+
country: z55.string().refine(isProductCostingCountryCode, "unsupported_country"),
|
|
11683
|
+
supplierCost: z55.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
|
|
11684
|
+
shippingCost: z55.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
|
|
11685
|
+
returnRate: z55.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
11686
|
+
cancellationRate: z55.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
11687
|
+
adsManagerCpa: z55.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
|
|
11688
|
+
desiredMargin: z55.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
|
|
10886
11689
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
10887
11690
|
if (returnRate + cancellationRate >= 1) {
|
|
10888
11691
|
context.addIssue({
|
|
10889
|
-
code:
|
|
11692
|
+
code: z55.ZodIssueCode.custom,
|
|
10890
11693
|
message: "delivery_rate_positive",
|
|
10891
11694
|
path: [
|
|
10892
11695
|
"cancellationRate"
|
|
@@ -10953,14 +11756,14 @@ function calculateProductCosting(rawInput) {
|
|
|
10953
11756
|
__name(calculateProductCosting, "calculateProductCosting");
|
|
10954
11757
|
|
|
10955
11758
|
// src/tools/product-costing.ts
|
|
10956
|
-
import { z as
|
|
10957
|
-
var optionalAmount = /* @__PURE__ */ __name((description) =>
|
|
10958
|
-
var optionalPercent = /* @__PURE__ */ __name((description) =>
|
|
10959
|
-
var ProductCostingToolInput =
|
|
10960
|
-
country:
|
|
11759
|
+
import { z as z56 } from "zod";
|
|
11760
|
+
var optionalAmount = /* @__PURE__ */ __name((description) => z56.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
|
|
11761
|
+
var optionalPercent = /* @__PURE__ */ __name((description) => z56.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
|
|
11762
|
+
var ProductCostingToolInput = z56.object({
|
|
11763
|
+
country: z56.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
|
|
10961
11764
|
message: "Pa\xEDs no soportado por ProductMaker."
|
|
10962
11765
|
}).optional().describe("C\xF3digo ISO-2 de cualquiera de los pa\xEDses disponibles en ProductMaker. Omite para Colombia."),
|
|
10963
|
-
supplierCost:
|
|
11766
|
+
supplierCost: z56.number().finite().max(Number.MAX_SAFE_INTEGER).positive().optional().describe("Costo real de una unidad con el proveedor, en la moneda del pa\xEDs. Requerido salvo que uses supplierCostCop para Colombia."),
|
|
10964
11767
|
supplierCostCop: optionalAmount("Alias anterior para costo del proveedor en Colombia. Prefiere supplierCost."),
|
|
10965
11768
|
shippingCost: optionalAmount("Costo de un env\xEDo en la moneda del pa\xEDs. Omite para usar el valor gu\xEDa vigente."),
|
|
10966
11769
|
shippingCostCop: optionalAmount("Alias anterior para flete en Colombia. Prefiere shippingCost."),
|
|
@@ -11092,7 +11895,7 @@ function formatPercent(value) {
|
|
|
11092
11895
|
__name(formatPercent, "formatPercent");
|
|
11093
11896
|
|
|
11094
11897
|
// src/tools/product-research.ts
|
|
11095
|
-
import { z as
|
|
11898
|
+
import { z as z57 } from "zod";
|
|
11096
11899
|
|
|
11097
11900
|
// src/tools/product-research-idempotency.ts
|
|
11098
11901
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -11208,17 +12011,17 @@ var TERMINAL2 = /* @__PURE__ */ new Set([
|
|
|
11208
12011
|
]);
|
|
11209
12012
|
var POLL_INTERVAL_MS = 5e3;
|
|
11210
12013
|
var MAX_WAIT_SECONDS = 120;
|
|
11211
|
-
var FindInput =
|
|
12014
|
+
var FindInput = z57.object({
|
|
11212
12015
|
targetCountry: ResearchTargetCountryEnum.describe("Pa\xEDs objetivo. S\xF3lo CO, MX, PE, EC y CL tienen cobertura de investigaci\xF3n."),
|
|
11213
12016
|
categoryKey: ResearchCategoryEnum.default("auto").describe('Categor\xEDa a investigar. Usa "auto" para que la IA elija seg\xFAn el mercado.'),
|
|
11214
|
-
confirmed:
|
|
11215
|
-
waitSeconds:
|
|
12017
|
+
confirmed: z57.boolean().describe(`Ponlo en true SOLO despu\xE9s de que el usuario haya aceptado el cargo de ${PRICE_CREDITS} cr\xE9ditos. No lo asumas.`),
|
|
12018
|
+
waitSeconds: z57.number().int().min(0).max(MAX_WAIT_SECONDS).optional().describe("Segundos a esperar el reporte antes de devolver el runId. M\xE1ximo 120.")
|
|
11216
12019
|
});
|
|
11217
|
-
var GetInput =
|
|
11218
|
-
runId:
|
|
12020
|
+
var GetInput = z57.object({
|
|
12021
|
+
runId: z57.string().uuid().describe("ID del run devuelto por find_winning_products.")
|
|
11219
12022
|
});
|
|
11220
|
-
var ListInput =
|
|
11221
|
-
limit:
|
|
12023
|
+
var ListInput = z57.object({
|
|
12024
|
+
limit: z57.number().int().min(1).max(20).default(10)
|
|
11222
12025
|
});
|
|
11223
12026
|
function textResult(text) {
|
|
11224
12027
|
return {
|
|
@@ -11310,14 +12113,14 @@ _Esta llamada no cobr\xF3 cr\xE9ditos: se reutiliz\xF3 una investigaci\xF3n reci
|
|
|
11310
12113
|
__name(registerProductResearchTools, "registerProductResearchTools");
|
|
11311
12114
|
|
|
11312
12115
|
// src/tools/dropshipping-projects.ts
|
|
11313
|
-
import { z as
|
|
11314
|
-
var ProjectId =
|
|
11315
|
-
var DropshippingProjectStatus =
|
|
12116
|
+
import { z as z58 } from "zod";
|
|
12117
|
+
var ProjectId = z58.string().uuid();
|
|
12118
|
+
var DropshippingProjectStatus = z58.enum([
|
|
11316
12119
|
"active",
|
|
11317
12120
|
"completed",
|
|
11318
12121
|
"archived"
|
|
11319
12122
|
]);
|
|
11320
|
-
var DropshippingProjectPhase =
|
|
12123
|
+
var DropshippingProjectPhase = z58.enum([
|
|
11321
12124
|
"research",
|
|
11322
12125
|
"costing",
|
|
11323
12126
|
"media",
|
|
@@ -11326,21 +12129,21 @@ var DropshippingProjectPhase = z54.enum([
|
|
|
11326
12129
|
"advertising",
|
|
11327
12130
|
"complete"
|
|
11328
12131
|
]);
|
|
11329
|
-
var DropshippingRewardMilestone =
|
|
12132
|
+
var DropshippingRewardMilestone = z58.enum([
|
|
11330
12133
|
"product_selected",
|
|
11331
12134
|
"costing_saved",
|
|
11332
12135
|
"product_media_ready",
|
|
11333
12136
|
"creatives_completed",
|
|
11334
12137
|
"campaign_created"
|
|
11335
12138
|
]);
|
|
11336
|
-
var TargetCountry =
|
|
12139
|
+
var TargetCountry = z58.enum([
|
|
11337
12140
|
"CO",
|
|
11338
12141
|
"MX",
|
|
11339
12142
|
"PE",
|
|
11340
12143
|
"EC",
|
|
11341
12144
|
"CL"
|
|
11342
12145
|
]);
|
|
11343
|
-
var ResearchCategory =
|
|
12146
|
+
var ResearchCategory = z58.enum([
|
|
11344
12147
|
"auto",
|
|
11345
12148
|
"automotive",
|
|
11346
12149
|
"kitchen",
|
|
@@ -11355,7 +12158,7 @@ var ResearchCategory = z54.enum([
|
|
|
11355
12158
|
"garden",
|
|
11356
12159
|
"cleaning"
|
|
11357
12160
|
]);
|
|
11358
|
-
var HttpUrl3 =
|
|
12161
|
+
var HttpUrl3 = z58.string().url().max(2e3).refine((value) => {
|
|
11359
12162
|
try {
|
|
11360
12163
|
return [
|
|
11361
12164
|
"http:",
|
|
@@ -11365,46 +12168,46 @@ var HttpUrl3 = z54.string().url().max(2e3).refine((value) => {
|
|
|
11365
12168
|
return false;
|
|
11366
12169
|
}
|
|
11367
12170
|
}, "URL must use HTTP(S)");
|
|
11368
|
-
var ResearchCandidateOrigin =
|
|
11369
|
-
type:
|
|
12171
|
+
var ResearchCandidateOrigin = z58.object({
|
|
12172
|
+
type: z58.literal("research_candidate"),
|
|
11370
12173
|
runId: ProjectId,
|
|
11371
|
-
candidateSourceId:
|
|
12174
|
+
candidateSourceId: z58.string().min(1).max(160)
|
|
11372
12175
|
});
|
|
11373
|
-
var TaskOrigin =
|
|
11374
|
-
type:
|
|
12176
|
+
var TaskOrigin = z58.object({
|
|
12177
|
+
type: z58.literal("task"),
|
|
11375
12178
|
taskId: ProjectId
|
|
11376
12179
|
});
|
|
11377
|
-
var ManualOrigin =
|
|
11378
|
-
type:
|
|
12180
|
+
var ManualOrigin = z58.object({
|
|
12181
|
+
type: z58.literal("manual"),
|
|
11379
12182
|
clientRequestId: ProjectId,
|
|
11380
|
-
country:
|
|
12183
|
+
country: z58.string().min(2).max(3).optional()
|
|
11381
12184
|
});
|
|
11382
|
-
var StartProjectInput =
|
|
11383
|
-
origin:
|
|
12185
|
+
var StartProjectInput = z58.object({
|
|
12186
|
+
origin: z58.discriminatedUnion("type", [
|
|
11384
12187
|
ResearchCandidateOrigin,
|
|
11385
12188
|
TaskOrigin,
|
|
11386
12189
|
ManualOrigin
|
|
11387
12190
|
]),
|
|
11388
|
-
productName:
|
|
12191
|
+
productName: z58.string().trim().min(1).max(240).optional(),
|
|
11389
12192
|
productUrl: HttpUrl3.optional(),
|
|
11390
|
-
country:
|
|
11391
|
-
languageCode:
|
|
12193
|
+
country: z58.string().min(2).max(3).optional(),
|
|
12194
|
+
languageCode: z58.string().min(2).max(16).optional()
|
|
11392
12195
|
}).strict();
|
|
11393
|
-
var CostProjectInput =
|
|
12196
|
+
var CostProjectInput = z58.object({
|
|
11394
12197
|
projectId: ProjectId,
|
|
11395
|
-
version:
|
|
11396
|
-
country:
|
|
11397
|
-
supplierCost:
|
|
11398
|
-
shippingCost:
|
|
11399
|
-
returnRate:
|
|
11400
|
-
cancellationRate:
|
|
11401
|
-
adsManagerCpa:
|
|
11402
|
-
desiredMargin:
|
|
11403
|
-
});
|
|
11404
|
-
var GuideInput =
|
|
12198
|
+
version: z58.number().int().positive(),
|
|
12199
|
+
country: z58.string().trim().min(2).max(3),
|
|
12200
|
+
supplierCost: z58.number().finite().positive(),
|
|
12201
|
+
shippingCost: z58.number().finite().nonnegative(),
|
|
12202
|
+
returnRate: z58.number().finite().min(0).lt(1),
|
|
12203
|
+
cancellationRate: z58.number().finite().min(0).lt(1),
|
|
12204
|
+
adsManagerCpa: z58.number().finite().nonnegative(),
|
|
12205
|
+
desiredMargin: z58.number().finite().min(0).lt(1)
|
|
12206
|
+
});
|
|
12207
|
+
var GuideInput = z58.object({
|
|
11405
12208
|
projectId: ProjectId.optional()
|
|
11406
12209
|
}).strict();
|
|
11407
|
-
var DropshippingProjectNextAction =
|
|
12210
|
+
var DropshippingProjectNextAction = z58.discriminatedUnion("kind", [
|
|
11408
12211
|
"cost_product",
|
|
11409
12212
|
"provide_product_media",
|
|
11410
12213
|
"generate_creatives",
|
|
@@ -11418,78 +12221,78 @@ var DropshippingProjectNextAction = z54.discriminatedUnion("kind", [
|
|
|
11418
12221
|
"create_meta_campaign",
|
|
11419
12222
|
"repair_meta_campaign",
|
|
11420
12223
|
"review_paused_campaign"
|
|
11421
|
-
].map((kind) =>
|
|
11422
|
-
kind:
|
|
11423
|
-
href:
|
|
12224
|
+
].map((kind) => z58.object({
|
|
12225
|
+
kind: z58.literal(kind),
|
|
12226
|
+
href: z58.string().min(1)
|
|
11424
12227
|
})));
|
|
11425
|
-
var CostingInputSchema =
|
|
11426
|
-
country:
|
|
11427
|
-
supplierCost:
|
|
11428
|
-
shippingCost:
|
|
11429
|
-
returnRate:
|
|
11430
|
-
cancellationRate:
|
|
11431
|
-
adsManagerCpa:
|
|
11432
|
-
desiredMargin:
|
|
12228
|
+
var CostingInputSchema = z58.object({
|
|
12229
|
+
country: z58.string().min(2).max(3),
|
|
12230
|
+
supplierCost: z58.number().finite().positive(),
|
|
12231
|
+
shippingCost: z58.number().finite().nonnegative(),
|
|
12232
|
+
returnRate: z58.number().finite().min(0).lt(1),
|
|
12233
|
+
cancellationRate: z58.number().finite().min(0).lt(1),
|
|
12234
|
+
adsManagerCpa: z58.number().finite().nonnegative(),
|
|
12235
|
+
desiredMargin: z58.number().finite().min(0).lt(1)
|
|
11433
12236
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
11434
12237
|
if (returnRate + cancellationRate >= 1) context.addIssue({
|
|
11435
|
-
code:
|
|
12238
|
+
code: z58.ZodIssueCode.custom,
|
|
11436
12239
|
path: [
|
|
11437
12240
|
"cancellationRate"
|
|
11438
12241
|
]
|
|
11439
12242
|
});
|
|
11440
12243
|
});
|
|
11441
|
-
var CostingResultSchema =
|
|
11442
|
-
country:
|
|
11443
|
-
deliveryRate:
|
|
11444
|
-
adjustedShippingCost:
|
|
11445
|
-
adjustedCpa:
|
|
11446
|
-
totalCost:
|
|
11447
|
-
suggestedPrice:
|
|
11448
|
-
baseCommercialPrice:
|
|
11449
|
-
minimumSafePrice:
|
|
11450
|
-
commercialPrice:
|
|
11451
|
-
comparisonPrice:
|
|
11452
|
-
estimatedProfit:
|
|
11453
|
-
actualMargin:
|
|
11454
|
-
breakEvenAdsManagerCpa:
|
|
11455
|
-
wasPriceAutoAdjusted:
|
|
11456
|
-
profitabilityStatus:
|
|
11457
|
-
});
|
|
11458
|
-
var RewardProgressSchema =
|
|
11459
|
-
campaignKey:
|
|
11460
|
-
totalCredits:
|
|
11461
|
-
earnedCredits:
|
|
11462
|
-
milestones:
|
|
12244
|
+
var CostingResultSchema = z58.object({
|
|
12245
|
+
country: z58.string().min(2).max(3),
|
|
12246
|
+
deliveryRate: z58.number().finite(),
|
|
12247
|
+
adjustedShippingCost: z58.number().finite(),
|
|
12248
|
+
adjustedCpa: z58.number().finite(),
|
|
12249
|
+
totalCost: z58.number().finite(),
|
|
12250
|
+
suggestedPrice: z58.number().finite(),
|
|
12251
|
+
baseCommercialPrice: z58.number().finite(),
|
|
12252
|
+
minimumSafePrice: z58.number().finite(),
|
|
12253
|
+
commercialPrice: z58.number().finite(),
|
|
12254
|
+
comparisonPrice: z58.number().finite(),
|
|
12255
|
+
estimatedProfit: z58.number().finite(),
|
|
12256
|
+
actualMargin: z58.number().finite(),
|
|
12257
|
+
breakEvenAdsManagerCpa: z58.number().finite(),
|
|
12258
|
+
wasPriceAutoAdjusted: z58.boolean(),
|
|
12259
|
+
profitabilityStatus: z58.literal("healthy")
|
|
12260
|
+
});
|
|
12261
|
+
var RewardProgressSchema = z58.object({
|
|
12262
|
+
campaignKey: z58.literal("dropshipping-launch-v1"),
|
|
12263
|
+
totalCredits: z58.number().int().positive(),
|
|
12264
|
+
earnedCredits: z58.number().int().nonnegative(),
|
|
12265
|
+
milestones: z58.array(z58.object({
|
|
11463
12266
|
milestone: DropshippingRewardMilestone,
|
|
11464
|
-
credits:
|
|
11465
|
-
available:
|
|
11466
|
-
earned:
|
|
12267
|
+
credits: z58.number().int().positive(),
|
|
12268
|
+
available: z58.boolean().optional(),
|
|
12269
|
+
earned: z58.boolean(),
|
|
11467
12270
|
grantId: ProjectId.nullable(),
|
|
11468
|
-
grantedAt:
|
|
12271
|
+
grantedAt: z58.string().datetime().nullable()
|
|
11469
12272
|
})).length(DropshippingRewardMilestone.options.length)
|
|
11470
12273
|
});
|
|
11471
|
-
var ProjectResponseSchema =
|
|
12274
|
+
var ProjectResponseSchema = z58.object({
|
|
11472
12275
|
id: ProjectId,
|
|
11473
12276
|
taskId: ProjectId.nullable(),
|
|
11474
12277
|
status: DropshippingProjectStatus,
|
|
11475
|
-
originKey:
|
|
12278
|
+
originKey: z58.string().min(1),
|
|
11476
12279
|
researchRunId: ProjectId.nullable(),
|
|
11477
|
-
candidateSourceId:
|
|
11478
|
-
productName:
|
|
12280
|
+
candidateSourceId: z58.string().nullable(),
|
|
12281
|
+
productName: z58.string().nullable(),
|
|
11479
12282
|
productUrl: HttpUrl3.nullable(),
|
|
11480
12283
|
referenceImageUrl: HttpUrl3.nullable(),
|
|
11481
|
-
targetCountry:
|
|
11482
|
-
languageCode:
|
|
11483
|
-
hasReferenceImage:
|
|
12284
|
+
targetCountry: z58.string().nullable(),
|
|
12285
|
+
languageCode: z58.string().nullable(),
|
|
12286
|
+
hasReferenceImage: z58.boolean(),
|
|
11484
12287
|
costingInput: CostingInputSchema.nullable(),
|
|
11485
12288
|
costingResult: CostingResultSchema.nullable(),
|
|
11486
|
-
costingVersion:
|
|
11487
|
-
version:
|
|
12289
|
+
costingVersion: z58.number().int().positive().nullable(),
|
|
12290
|
+
version: z58.number().int().positive(),
|
|
11488
12291
|
phase: DropshippingProjectPhase,
|
|
11489
12292
|
nextAction: DropshippingProjectNextAction.nullable(),
|
|
11490
12293
|
rewards: RewardProgressSchema,
|
|
11491
|
-
createdAt:
|
|
11492
|
-
updatedAt:
|
|
12294
|
+
createdAt: z58.string().datetime(),
|
|
12295
|
+
updatedAt: z58.string().datetime()
|
|
11493
12296
|
}).passthrough();
|
|
11494
12297
|
function projectText(raw) {
|
|
11495
12298
|
const project = ProjectResponseSchema.parse(raw);
|
|
@@ -11574,6 +12377,7 @@ function createServer(opts) {
|
|
|
11574
12377
|
}
|
|
11575
12378
|
});
|
|
11576
12379
|
const api = new ApiClient(apiBaseUrl, apiKey);
|
|
12380
|
+
registerMetaUploadAppResource(server);
|
|
11577
12381
|
registerProductCostingTools(server);
|
|
11578
12382
|
registerProductResearchTools(server, api);
|
|
11579
12383
|
registerDropshippingProjectTools(server, api);
|
|
@@ -11587,9 +12391,11 @@ function createServer(opts) {
|
|
|
11587
12391
|
registerCreativeVariantTools(server, api);
|
|
11588
12392
|
registerScriptReviewTools(server, api);
|
|
11589
12393
|
registerConnectionTools(server, api);
|
|
12394
|
+
registerActorTools(server, api);
|
|
11590
12395
|
registerPublishTools(server, api, {
|
|
11591
12396
|
transport
|
|
11592
12397
|
});
|
|
12398
|
+
registerMetaUploadTools(server, api);
|
|
11593
12399
|
registerCampaignTools(server, api);
|
|
11594
12400
|
return server;
|
|
11595
12401
|
}
|