@productmaker/mcp 2.3.2 → 2.4.0
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 +3 -0
- package/dist/http.js +620 -440
- package/dist/stdio.js +620 -440
- package/package.json +3 -3
package/dist/stdio.js
CHANGED
|
@@ -150,10 +150,10 @@ var ApiClient = class {
|
|
|
150
150
|
};
|
|
151
151
|
|
|
152
152
|
// src/index.ts
|
|
153
|
-
var MCP_VERSION = "2.
|
|
153
|
+
var MCP_VERSION = "2.4.0";
|
|
154
154
|
|
|
155
155
|
// src/tools/tasks.ts
|
|
156
|
-
import { z as
|
|
156
|
+
import { z as z44 } from "zod";
|
|
157
157
|
|
|
158
158
|
// src/resolve-image.ts
|
|
159
159
|
import { lookup as dnsLookup } from "dns/promises";
|
|
@@ -1042,6 +1042,8 @@ var Angle = z6.object({
|
|
|
1042
1042
|
buyerPersona: z6.string().min(1).max(360),
|
|
1043
1043
|
/** Character description for video (role + appearance + attitude). Drives voice selection and Frame0Spec. */
|
|
1044
1044
|
actor: z6.string().min(1).max(360).optional(),
|
|
1045
|
+
/** Saved custom actor (Actor.id) resolved for this angle. Its approved photo anchors the identity. */
|
|
1046
|
+
actorId: z6.string().optional(),
|
|
1045
1047
|
// Binary only: voice catalog has no non-binary voices.
|
|
1046
1048
|
// See packages/angles/src/index.ts + getAngles coercion in angle.repository.ts.
|
|
1047
1049
|
gender: z6.enum([
|
|
@@ -1632,6 +1634,8 @@ var IngestOptions = z14.object({
|
|
|
1632
1634
|
generateAdCopy: z14.boolean().default(true),
|
|
1633
1635
|
generateAiMusic: z14.boolean().optional(),
|
|
1634
1636
|
voiceId: VoiceId.optional(),
|
|
1637
|
+
/** Global saved custom actor (Actor.id). Default for all angles. */
|
|
1638
|
+
actorId: z14.string().optional(),
|
|
1635
1639
|
isCombo: z14.boolean().default(false),
|
|
1636
1640
|
productContext: z14.string().transform((value) => value.trim()).pipe(z14.string().max(PRODUCT_CONTEXT_MAX_CHARS)).optional(),
|
|
1637
1641
|
creativeDirection: z14.string().transform((value) => value.trim()).pipe(z14.string().max(500)).optional(),
|
|
@@ -1646,7 +1650,7 @@ var IngestOptions = z14.object({
|
|
|
1646
1650
|
"landing",
|
|
1647
1651
|
"video",
|
|
1648
1652
|
"image"
|
|
1649
|
-
])).min(
|
|
1653
|
+
])).min(1).max(3).optional(),
|
|
1650
1654
|
// Landing page output: 'classic' = image-based (default), 'code' = structured
|
|
1651
1655
|
// code landing rendered by the theme app extension. Absent ⇒ classic.
|
|
1652
1656
|
landingMode: LandingModeSchema.optional(),
|
|
@@ -1697,7 +1701,13 @@ var VideoOptionsOverride = z14.object({
|
|
|
1697
1701
|
videoResolution: VideoResolution.optional(),
|
|
1698
1702
|
voiceId: VoiceId.optional(),
|
|
1699
1703
|
/** Actor/character description (role + appearance + attitude). Drives voice and video character. */
|
|
1700
|
-
actor: z14.string().max(360).optional()
|
|
1704
|
+
actor: z14.string().max(360).optional(),
|
|
1705
|
+
/** Saved custom actor (Actor.id). Its approved photo anchors the identity.
|
|
1706
|
+
* `null` is an explicit REMOVAL (the user cleared the picker) and must be
|
|
1707
|
+
* sent as such: an omitted key means "not mentioned", which every writer
|
|
1708
|
+
* reads as "leave the stored selection alone". Same contract as
|
|
1709
|
+
* service-angles.controller.ts. */
|
|
1710
|
+
actorId: z14.string().nullable().optional()
|
|
1701
1711
|
});
|
|
1702
1712
|
var AngleVideoOptions = VideoOptionsOverride.extend({
|
|
1703
1713
|
angleIndex: z14.number().int().nonnegative(),
|
|
@@ -1708,7 +1718,11 @@ var AngleVideoOptions = VideoOptionsOverride.extend({
|
|
|
1708
1718
|
/** Per-angle buyer persona override (describes the target audience) */
|
|
1709
1719
|
buyerPersona: z14.string().max(360).optional(),
|
|
1710
1720
|
/** Per-angle actor/character description (role + appearance + attitude). Drives voice and video character. */
|
|
1711
|
-
actor: z14.string().max(360).optional()
|
|
1721
|
+
actor: z14.string().max(360).optional(),
|
|
1722
|
+
/** Per-angle saved custom actor (Actor.id). Overrides the global actorId.
|
|
1723
|
+
* `null` = the user removed the personaje on this angle; it must NOT fall
|
|
1724
|
+
* back to the global default. Omitted = not mentioned. */
|
|
1725
|
+
actorId: z14.string().nullable().optional()
|
|
1712
1726
|
});
|
|
1713
1727
|
var RapidScriptBlueprintShotForApproval = z14.object({
|
|
1714
1728
|
id: z14.string().min(1),
|
|
@@ -1771,6 +1785,8 @@ var IngestResumePayload = z14.object({
|
|
|
1771
1785
|
voiceId: VoiceId.optional(),
|
|
1772
1786
|
/** Global actor/character description (role + appearance + attitude). Default for all angles. */
|
|
1773
1787
|
actor: z14.string().max(360).optional(),
|
|
1788
|
+
/** Global saved custom actor (Actor.id). Default for all angles. */
|
|
1789
|
+
actorId: z14.string().optional(),
|
|
1774
1790
|
/** Custom angle override for landing/creative (uses this instead of first angle) */
|
|
1775
1791
|
customAngleOverride: z14.string().min(1).max(480).optional(),
|
|
1776
1792
|
/** Pain points to emphasize in landings, creatives, and videos */
|
|
@@ -2469,6 +2485,8 @@ var RefineAngleResponse = z20.object({
|
|
|
2469
2485
|
painPoints: z20.array(z20.string()).optional(),
|
|
2470
2486
|
benefits: z20.array(z20.string()).optional(),
|
|
2471
2487
|
actor: z20.string().optional(),
|
|
2488
|
+
/** Saved custom actor (Actor.id) carried through the refine round-trip. */
|
|
2489
|
+
actorId: z20.string().optional(),
|
|
2472
2490
|
buyerPersona: z20.string().optional()
|
|
2473
2491
|
});
|
|
2474
2492
|
|
|
@@ -2848,7 +2866,9 @@ var PendingUserActionSchema = z23.discriminatedUnion("kind", [
|
|
|
2848
2866
|
id: z23.string().uuid(),
|
|
2849
2867
|
content: z23.string(),
|
|
2850
2868
|
creativeDirection: z23.string(),
|
|
2851
|
-
actor: z23.string()
|
|
2869
|
+
actor: z23.string(),
|
|
2870
|
+
/** Saved custom actor (Actor.id) backing this angle, when one was chosen. */
|
|
2871
|
+
actorId: z23.string().optional()
|
|
2852
2872
|
})).min(1).max(3)
|
|
2853
2873
|
}),
|
|
2854
2874
|
z23.object({
|
|
@@ -3001,7 +3021,8 @@ var CostEstimationType = z24.enum([
|
|
|
3001
3021
|
"landing-regenerate",
|
|
3002
3022
|
"landing-creative",
|
|
3003
3023
|
"campaign-copy",
|
|
3004
|
-
"service-creative"
|
|
3024
|
+
"service-creative",
|
|
3025
|
+
"actor-grid"
|
|
3005
3026
|
]);
|
|
3006
3027
|
var IngestEstimationOptions = z24.object({
|
|
3007
3028
|
/** Number of product references that the extract stage will clean. */
|
|
@@ -3102,6 +3123,8 @@ var VideoCreativeEstimationOptions = z24.object({
|
|
|
3102
3123
|
requiresAngles: z24.boolean().optional(),
|
|
3103
3124
|
/** When false, omit the Meta/Whisper competitor-scan ceiling from the estimate. */
|
|
3104
3125
|
competitorScanEnabled: z24.boolean().optional(),
|
|
3126
|
+
/** Quote the competitor scan even when product extraction is skipped (direct video auto path). */
|
|
3127
|
+
requiresCompetitorScan: z24.boolean().optional(),
|
|
3105
3128
|
generateAiMusic: z24.boolean().optional(),
|
|
3106
3129
|
/** Free mode burns one Whisper pass over the final clip when enabled. */
|
|
3107
3130
|
addSubtitles: z24.boolean().optional()
|
|
@@ -3170,6 +3193,7 @@ var ServiceCreativeEstimationOptions = z24.object({
|
|
|
3170
3193
|
mediaAssetCount: z24.number().int().min(0).max(8).optional(),
|
|
3171
3194
|
uploadedVideoDurationSeconds: z24.number().min(0).max(240).optional()
|
|
3172
3195
|
});
|
|
3196
|
+
var ActorGridEstimationOptions = z24.object({}).strict();
|
|
3173
3197
|
var CostEstimationRequest = z24.discriminatedUnion("type", [
|
|
3174
3198
|
z24.object({
|
|
3175
3199
|
type: z24.literal("ingest"),
|
|
@@ -3206,6 +3230,10 @@ var CostEstimationRequest = z24.discriminatedUnion("type", [
|
|
|
3206
3230
|
z24.object({
|
|
3207
3231
|
type: z24.literal("service-creative"),
|
|
3208
3232
|
options: ServiceCreativeEstimationOptions
|
|
3233
|
+
}),
|
|
3234
|
+
z24.object({
|
|
3235
|
+
type: z24.literal("actor-grid"),
|
|
3236
|
+
options: ActorGridEstimationOptions
|
|
3209
3237
|
})
|
|
3210
3238
|
]);
|
|
3211
3239
|
var CostEstimationResponse = z24.object({
|
|
@@ -3696,10 +3724,15 @@ var PresignUploadRequest = z31.object({
|
|
|
3696
3724
|
fileName: z31.string().min(1).max(255),
|
|
3697
3725
|
/** File size in bytes (validated against MAX_FILE_SIZE_BYTES) */
|
|
3698
3726
|
fileSize: z31.number().int().positive().max(CAMPAIGN_LIMITS.MAX_FILE_SIZE_BYTES),
|
|
3699
|
-
/**
|
|
3727
|
+
/**
|
|
3728
|
+
* Service uploads receive a user-bound asset ticket; ads keep the legacy path.
|
|
3729
|
+
* `actor` uploads the identity photo of a custom actor and is stored under
|
|
3730
|
+
* `actors/<userId>/…` so `AssetAccessGuard` can resolve its owner.
|
|
3731
|
+
*/
|
|
3700
3732
|
purpose: z31.enum([
|
|
3701
3733
|
"ads",
|
|
3702
|
-
"service"
|
|
3734
|
+
"service",
|
|
3735
|
+
"actor"
|
|
3703
3736
|
]).optional()
|
|
3704
3737
|
});
|
|
3705
3738
|
var PresignUploadResponse = z31.object({
|
|
@@ -4692,6 +4725,13 @@ var DEFAULT_PRICING = {
|
|
|
4692
4725
|
ourCostCents: 3,
|
|
4693
4726
|
userPriceCents: 6
|
|
4694
4727
|
},
|
|
4728
|
+
// Custom actor identity photos (gpt-image-2 @ 1K, 4 candidates per attempt).
|
|
4729
|
+
// Recorded once per PRODUCED candidate. Without this row billingService.record
|
|
4730
|
+
// logs "No pricing rule found; skipping billing record" and the whole grid is free.
|
|
4731
|
+
"kie:image-generation:actor": {
|
|
4732
|
+
ourCostCents: 3,
|
|
4733
|
+
userPriceCents: 6
|
|
4734
|
+
},
|
|
4695
4735
|
"kie:image-generation:seedream-5-lite-text-to-image": {
|
|
4696
4736
|
ourCostCents: 3,
|
|
4697
4737
|
userPriceCents: 6
|
|
@@ -5628,6 +5668,32 @@ var DEFAULT_PRICING = {
|
|
|
5628
5668
|
ourCostCents: 1.5,
|
|
5629
5669
|
userPriceCents: 3
|
|
5630
5670
|
},
|
|
5671
|
+
// Frame0 per-person anatomy gate. Priced PER PANEL, like the inventory above and for the same
|
|
5672
|
+
// reason: it is one vision call per rendered panel, so quoting it per VIDEO would under-quote
|
|
5673
|
+
// by the panel count (~10x). Same single-image, short-answer shape as the inventory, so it
|
|
5674
|
+
// carries the same rate until measured otherwise.
|
|
5675
|
+
"openrouter:rapid-panel-anatomy-gate": {
|
|
5676
|
+
ourCostCents: 1.5,
|
|
5677
|
+
userPriceCents: 3
|
|
5678
|
+
},
|
|
5679
|
+
// Plan hand-budget audit: one TEXT call per shot (no image), so materially cheaper than the
|
|
5680
|
+
// vision passes above. Billed at the call site, therefore it MUST be quoted — an unquoted paid
|
|
5681
|
+
// call is the failure this repo already has on record.
|
|
5682
|
+
"openrouter:plan-hand-budget-audit": {
|
|
5683
|
+
ourCostCents: 0.3,
|
|
5684
|
+
userPriceCents: 0.6
|
|
5685
|
+
},
|
|
5686
|
+
// Hand-budget REPAIR: the rewrite that fires only on a shot whose brief demands more hand jobs
|
|
5687
|
+
// than its cast has hands. One TEXT call, same shape as the audit above but with the brief on
|
|
5688
|
+
// both sides (prompt in, prose out), so twice the rate rather than the audit's.
|
|
5689
|
+
//
|
|
5690
|
+
// Kept on its OWN key rather than folded into the audit for the reason the frame-inventory delta
|
|
5691
|
+
// is: the audit runs on every shot and the repair on a minority, so one blended rate would price
|
|
5692
|
+
// both wrong and neither line would be auditable against the ledger.
|
|
5693
|
+
"openrouter:rapid-hand-budget-repair": {
|
|
5694
|
+
ourCostCents: 0.6,
|
|
5695
|
+
userPriceCents: 1.2
|
|
5696
|
+
},
|
|
5631
5697
|
// Unit = one shot-action flatten, once per video, quoted ONLY when frame inventories are on.
|
|
5632
5698
|
// Carrying ~10 inventories as text raised that call from a 1.7078¢ production median to a
|
|
5633
5699
|
// measured 3.1481¢ / 3.4551¢. This is the difference, kept on its own key so the base rate above
|
|
@@ -7058,6 +7124,7 @@ var MAX_SEGMENT_SECONDS = 3;
|
|
|
7058
7124
|
var VideoEditStatusSchema = z38.enum([
|
|
7059
7125
|
"pending",
|
|
7060
7126
|
"processing",
|
|
7127
|
+
"assembling",
|
|
7061
7128
|
"ready",
|
|
7062
7129
|
"accepted",
|
|
7063
7130
|
"discarded",
|
|
@@ -7085,6 +7152,13 @@ var VideoEditModeSchema = z38.enum([
|
|
|
7085
7152
|
"wan_edit",
|
|
7086
7153
|
"regenerate"
|
|
7087
7154
|
]);
|
|
7155
|
+
var VideoRegenerationEngineSchema = z38.enum([
|
|
7156
|
+
"free_text",
|
|
7157
|
+
"free_image",
|
|
7158
|
+
"single_call",
|
|
7159
|
+
"multi_shot"
|
|
7160
|
+
]);
|
|
7161
|
+
var VideoRegenerationRouteFingerprintSchema = z38.string().trim().min(1).max(128);
|
|
7088
7162
|
var RegenerateEditRequestSchema = z38.object({
|
|
7089
7163
|
angleKey: z38.string().min(1),
|
|
7090
7164
|
comment: z38.string().min(1).max(2e3)
|
|
@@ -7481,7 +7555,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7481
7555
|
{
|
|
7482
7556
|
name: "create_product_task",
|
|
7483
7557
|
confirmation: "credit_consuming",
|
|
7484
|
-
schemaHash: "
|
|
7558
|
+
schemaHash: "50845af40b29f479592ca5e545d9c1af1fe631998802827affccb4641fc90c18"
|
|
7485
7559
|
},
|
|
7486
7560
|
{
|
|
7487
7561
|
name: "create_service_video",
|
|
@@ -7496,7 +7570,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7496
7570
|
{
|
|
7497
7571
|
name: "edit_task_draft",
|
|
7498
7572
|
confirmation: "state_change",
|
|
7499
|
-
schemaHash: "
|
|
7573
|
+
schemaHash: "1c9343bdce51aac36ec6254557c8a904037746523168527f012b4c5b6361e98e"
|
|
7500
7574
|
},
|
|
7501
7575
|
{
|
|
7502
7576
|
name: "estimate_creative_variant",
|
|
@@ -7558,6 +7632,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7558
7632
|
confirmation: "none",
|
|
7559
7633
|
schemaHash: "0e7a1cb5ad5c6681473df5689336edcd7a63b8d68853359982e44bfd98a65db8"
|
|
7560
7634
|
},
|
|
7635
|
+
{
|
|
7636
|
+
name: "list_actors",
|
|
7637
|
+
confirmation: "none",
|
|
7638
|
+
schemaHash: "7a014b717a77aac971ea0ab5c0ff47bb13e6cae7ef9442d9dedb15ce36381b15"
|
|
7639
|
+
},
|
|
7561
7640
|
{
|
|
7562
7641
|
name: "list_meta_ad_accounts",
|
|
7563
7642
|
confirmation: "none",
|
|
@@ -7626,6 +7705,68 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7626
7705
|
];
|
|
7627
7706
|
var PRODUCTMAKER_MCP_CAPABILITY_NAMES = PRODUCTMAKER_MCP_CAPABILITIES.map((capability) => capability.name);
|
|
7628
7707
|
|
|
7708
|
+
// ../../packages/types/src/actor.ts
|
|
7709
|
+
import { z as z42 } from "zod";
|
|
7710
|
+
var ActorStatusSchema = z42.enum([
|
|
7711
|
+
"draft",
|
|
7712
|
+
"generating",
|
|
7713
|
+
"ready",
|
|
7714
|
+
"failed"
|
|
7715
|
+
]);
|
|
7716
|
+
var ACTOR_NAME_MAX_CHARS = 60;
|
|
7717
|
+
var ACTOR_DESCRIPTION_MAX_CHARS = 600;
|
|
7718
|
+
var ACTOR_CANDIDATE_COUNT = 4;
|
|
7719
|
+
var ActorGenderSchema = z42.enum([
|
|
7720
|
+
"female",
|
|
7721
|
+
"male",
|
|
7722
|
+
"non_binary"
|
|
7723
|
+
]);
|
|
7724
|
+
var ActorSchema = z42.object({
|
|
7725
|
+
id: z42.string(),
|
|
7726
|
+
name: z42.string().min(1).max(ACTOR_NAME_MAX_CHARS),
|
|
7727
|
+
description: z42.string().max(ACTOR_DESCRIPTION_MAX_CHARS),
|
|
7728
|
+
gender: ActorGenderSchema,
|
|
7729
|
+
voiceId: z42.string().nullable().optional(),
|
|
7730
|
+
brandId: z42.string().nullable().optional(),
|
|
7731
|
+
status: ActorStatusSchema,
|
|
7732
|
+
/**
|
|
7733
|
+
* A candidate-grid run is in flight RIGHT NOW.
|
|
7734
|
+
*
|
|
7735
|
+
* Separate from `status` because the two answer different questions:
|
|
7736
|
+
* `status` is the PUBLISHED state (a personaje being regenerated stays
|
|
7737
|
+
* `ready`, so it keeps serving the renders that already reference it), and
|
|
7738
|
+
* this flag is the live one the wizard polls. It is derived from a lock
|
|
7739
|
+
* timestamp with a TTL, so an orphaned run reads as `false` instead of
|
|
7740
|
+
* pinning the personaje forever.
|
|
7741
|
+
*/
|
|
7742
|
+
generating: z42.boolean().optional(),
|
|
7743
|
+
photoUrl: z42.string().url().nullable().optional(),
|
|
7744
|
+
avatarUrl: z42.string().url().nullable().optional(),
|
|
7745
|
+
candidateUrls: z42.array(z42.string().url()).optional(),
|
|
7746
|
+
/** Indices of candidateUrls that failed the anatomy check (Task 6). */
|
|
7747
|
+
flaggedCandidateIndices: z42.array(z42.number().int()).optional()
|
|
7748
|
+
});
|
|
7749
|
+
var CreateActorRequest = z42.object({
|
|
7750
|
+
name: z42.string().min(1).max(ACTOR_NAME_MAX_CHARS),
|
|
7751
|
+
description: z42.string().max(ACTOR_DESCRIPTION_MAX_CHARS).default(""),
|
|
7752
|
+
gender: ActorGenderSchema,
|
|
7753
|
+
voiceId: z42.string().optional(),
|
|
7754
|
+
brandId: z42.string().optional(),
|
|
7755
|
+
sourcePhotoKey: z42.string().optional()
|
|
7756
|
+
}).refine((v) => v.description.trim().length > 0 || !!v.sourcePhotoKey, {
|
|
7757
|
+
message: "Se necesita una descripci\xF3n o una foto"
|
|
7758
|
+
});
|
|
7759
|
+
var UpdateActorRequest = z42.object({
|
|
7760
|
+
name: z42.string().min(1).max(ACTOR_NAME_MAX_CHARS).optional(),
|
|
7761
|
+
description: z42.string().max(ACTOR_DESCRIPTION_MAX_CHARS).optional(),
|
|
7762
|
+
gender: ActorGenderSchema.optional(),
|
|
7763
|
+
voiceId: z42.string().nullable().optional(),
|
|
7764
|
+
brandId: z42.string().nullable().optional()
|
|
7765
|
+
});
|
|
7766
|
+
var ApproveActorRequest = z42.object({
|
|
7767
|
+
candidateIndex: z42.number().int().min(0).max(ACTOR_CANDIDATE_COUNT - 1)
|
|
7768
|
+
});
|
|
7769
|
+
|
|
7629
7770
|
// src/resolve-media.ts
|
|
7630
7771
|
var MAX_MEDIA_BYTES = CAMPAIGN_LIMITS.MAX_FILE_SIZE_BYTES;
|
|
7631
7772
|
var MEDIA_FORMATS = [
|
|
@@ -7865,10 +8006,10 @@ async function resolveMediaRef(ref, opts) {
|
|
|
7865
8006
|
__name(resolveMediaRef, "resolveMediaRef");
|
|
7866
8007
|
|
|
7867
8008
|
// src/tools/shared.ts
|
|
7868
|
-
import { z as
|
|
7869
|
-
var Lang =
|
|
7870
|
-
var Country =
|
|
7871
|
-
var LandingStrategyEnum =
|
|
8009
|
+
import { z as z43 } from "zod";
|
|
8010
|
+
var Lang = z43.string().min(2).max(10);
|
|
8011
|
+
var Country = z43.string().min(2).max(3);
|
|
8012
|
+
var LandingStrategyEnum = z43.enum([
|
|
7872
8013
|
"auto",
|
|
7873
8014
|
"classic",
|
|
7874
8015
|
"pain_solution",
|
|
@@ -7877,12 +8018,12 @@ var LandingStrategyEnum = z42.enum([
|
|
|
7877
8018
|
"offer_value",
|
|
7878
8019
|
"lifestyle_identity"
|
|
7879
8020
|
]);
|
|
7880
|
-
var LandingImageFormatEnum =
|
|
8021
|
+
var LandingImageFormatEnum = z43.enum([
|
|
7881
8022
|
"9:16",
|
|
7882
8023
|
"4:5",
|
|
7883
8024
|
"1:1"
|
|
7884
8025
|
]);
|
|
7885
|
-
var VideoPresetEnum =
|
|
8026
|
+
var VideoPresetEnum = z43.enum([
|
|
7886
8027
|
"auto",
|
|
7887
8028
|
"ugc_dynamic",
|
|
7888
8029
|
"ugc_how_to",
|
|
@@ -7895,7 +8036,7 @@ var VideoPresetEnum = z42.enum([
|
|
|
7895
8036
|
"pixar_problem",
|
|
7896
8037
|
"pixar_sassy"
|
|
7897
8038
|
]);
|
|
7898
|
-
var TaskCreativeVariantVideoPresetEnum =
|
|
8039
|
+
var TaskCreativeVariantVideoPresetEnum = z43.enum([
|
|
7899
8040
|
"auto",
|
|
7900
8041
|
"ugc_dynamic",
|
|
7901
8042
|
"ugc_how_to",
|
|
@@ -7966,7 +8107,7 @@ function resolveVideoPreset(preset) {
|
|
|
7966
8107
|
return VIDEO_PRESET_RESOLUTION[preset] ?? null;
|
|
7967
8108
|
}
|
|
7968
8109
|
__name(resolveVideoPreset, "resolveVideoPreset");
|
|
7969
|
-
var VideoTierEnum =
|
|
8110
|
+
var VideoTierEnum = z43.enum([
|
|
7970
8111
|
"basic"
|
|
7971
8112
|
]);
|
|
7972
8113
|
function resolveVideoTier(_tier) {
|
|
@@ -7987,17 +8128,17 @@ function resolveVideoFormatForDuration(durationSeconds) {
|
|
|
7987
8128
|
return durationSeconds === 30 ? "double_30s" : "single_15s";
|
|
7988
8129
|
}
|
|
7989
8130
|
__name(resolveVideoFormatForDuration, "resolveVideoFormatForDuration");
|
|
7990
|
-
var VideoResolutionEnum =
|
|
8131
|
+
var VideoResolutionEnum = z43.enum([
|
|
7991
8132
|
"480p",
|
|
7992
8133
|
"720p"
|
|
7993
8134
|
]);
|
|
7994
|
-
var VideoStyleEnum =
|
|
8135
|
+
var VideoStyleEnum = z43.enum([
|
|
7995
8136
|
"ugc",
|
|
7996
8137
|
"cartoon_3d",
|
|
7997
8138
|
"product_only",
|
|
7998
8139
|
"ai_dynamic"
|
|
7999
8140
|
]);
|
|
8000
|
-
var NarrativeStyleEnum =
|
|
8141
|
+
var NarrativeStyleEnum = z43.enum([
|
|
8001
8142
|
"review",
|
|
8002
8143
|
"ugc_how_to",
|
|
8003
8144
|
"problem_product_cta",
|
|
@@ -8014,7 +8155,7 @@ var NarrativeStyleEnum = z42.enum([
|
|
|
8014
8155
|
"ugc_skeptic_confession",
|
|
8015
8156
|
"ugc_demo_review"
|
|
8016
8157
|
]);
|
|
8017
|
-
var PublicNarrativeStyleEnum =
|
|
8158
|
+
var PublicNarrativeStyleEnum = z43.enum([
|
|
8018
8159
|
"review",
|
|
8019
8160
|
"ugc_how_to",
|
|
8020
8161
|
"ugc_unboxing_chaos",
|
|
@@ -8026,7 +8167,7 @@ var PublicNarrativeStyleEnum = z42.enum([
|
|
|
8026
8167
|
"animated_problem_pixar",
|
|
8027
8168
|
"sassy_product_pixar"
|
|
8028
8169
|
]);
|
|
8029
|
-
var VideoModelEnum =
|
|
8170
|
+
var VideoModelEnum = z43.enum([
|
|
8030
8171
|
"veo",
|
|
8031
8172
|
"wan",
|
|
8032
8173
|
"bytedance",
|
|
@@ -8034,25 +8175,25 @@ var VideoModelEnum = z42.enum([
|
|
|
8034
8175
|
"grok",
|
|
8035
8176
|
"kling"
|
|
8036
8177
|
]);
|
|
8037
|
-
var ProductReferenceUserChoiceEnum =
|
|
8178
|
+
var ProductReferenceUserChoiceEnum = z43.enum([
|
|
8038
8179
|
"same_product",
|
|
8039
8180
|
"variants",
|
|
8040
8181
|
"different_products",
|
|
8041
8182
|
"combo_distinct"
|
|
8042
8183
|
]);
|
|
8043
|
-
var ProductReferenceCandidateGroupingEnum =
|
|
8184
|
+
var ProductReferenceCandidateGroupingEnum = z43.enum([
|
|
8044
8185
|
"same_product_same_variant",
|
|
8045
8186
|
"same_product_different_variants",
|
|
8046
8187
|
"different_products",
|
|
8047
8188
|
"unclear"
|
|
8048
8189
|
]);
|
|
8049
|
-
var GapDurationChoiceMirror =
|
|
8050
|
-
angleIndex:
|
|
8051
|
-
recommendedSec:
|
|
8052
|
-
|
|
8053
|
-
|
|
8190
|
+
var GapDurationChoiceMirror = z43.object({
|
|
8191
|
+
angleIndex: z43.number().int().nonnegative(),
|
|
8192
|
+
recommendedSec: z43.union([
|
|
8193
|
+
z43.literal(15),
|
|
8194
|
+
z43.literal(30)
|
|
8054
8195
|
]),
|
|
8055
|
-
estimatedDeltaCredits:
|
|
8196
|
+
estimatedDeltaCredits: z43.number().int().nonnegative().optional()
|
|
8056
8197
|
});
|
|
8057
8198
|
function buildSelectedImageUrlsHash(urls) {
|
|
8058
8199
|
const input = [
|
|
@@ -8066,13 +8207,13 @@ function buildSelectedImageUrlsHash(urls) {
|
|
|
8066
8207
|
return `fnv1a:${hash.toString(16).padStart(8, "0")}:${urls.length}`;
|
|
8067
8208
|
}
|
|
8068
8209
|
__name(buildSelectedImageUrlsHash, "buildSelectedImageUrlsHash");
|
|
8069
|
-
var ImageCreativeStyleEnum =
|
|
8210
|
+
var ImageCreativeStyleEnum = z43.enum([
|
|
8070
8211
|
"studio",
|
|
8071
8212
|
"floating",
|
|
8072
8213
|
"ingredients",
|
|
8073
8214
|
"in_use"
|
|
8074
8215
|
]);
|
|
8075
|
-
var ImageCreativeArchetypeEnum =
|
|
8216
|
+
var ImageCreativeArchetypeEnum = z43.enum([
|
|
8076
8217
|
"direct_response_product_ad",
|
|
8077
8218
|
"hero_outcome",
|
|
8078
8219
|
"offer_value_prop",
|
|
@@ -8081,23 +8222,23 @@ var ImageCreativeArchetypeEnum = z42.enum([
|
|
|
8081
8222
|
"problem_solution",
|
|
8082
8223
|
"lifestyle_identity"
|
|
8083
8224
|
]);
|
|
8084
|
-
var ImageCreativeVisualTreatmentEnum =
|
|
8225
|
+
var ImageCreativeVisualTreatmentEnum = z43.enum([
|
|
8085
8226
|
"performance_product_ad",
|
|
8086
8227
|
"premium_dtc_carousel"
|
|
8087
8228
|
]);
|
|
8088
|
-
var ImageCreativeAspectRatioEnum =
|
|
8229
|
+
var ImageCreativeAspectRatioEnum = z43.enum([
|
|
8089
8230
|
"1:1",
|
|
8090
8231
|
"4:5",
|
|
8091
8232
|
"9:16",
|
|
8092
8233
|
"16:9"
|
|
8093
8234
|
]);
|
|
8094
|
-
var ImageRefSchema =
|
|
8095
|
-
url:
|
|
8096
|
-
path:
|
|
8235
|
+
var ImageRefSchema = z43.object({
|
|
8236
|
+
url: z43.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.'),
|
|
8237
|
+
path: z43.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
8238
|
}).refine((v) => Boolean(v.url) !== Boolean(v.path), {
|
|
8098
8239
|
message: "each image entry must have exactly one of `url` or `path`"
|
|
8099
8240
|
});
|
|
8100
|
-
var ImagesInput =
|
|
8241
|
+
var ImagesInput = z43.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
8242
|
function asText(value) {
|
|
8102
8243
|
return {
|
|
8103
8244
|
content: [
|
|
@@ -8150,14 +8291,14 @@ async function submitCreative(api, opts) {
|
|
|
8150
8291
|
});
|
|
8151
8292
|
}
|
|
8152
8293
|
__name(submitCreative, "submitCreative");
|
|
8153
|
-
var ResearchTargetCountryEnum =
|
|
8294
|
+
var ResearchTargetCountryEnum = z43.enum([
|
|
8154
8295
|
"CO",
|
|
8155
8296
|
"MX",
|
|
8156
8297
|
"PE",
|
|
8157
8298
|
"EC",
|
|
8158
8299
|
"CL"
|
|
8159
8300
|
]);
|
|
8160
|
-
var ResearchCategoryEnum =
|
|
8301
|
+
var ResearchCategoryEnum = z43.enum([
|
|
8161
8302
|
"auto",
|
|
8162
8303
|
"automotive",
|
|
8163
8304
|
"kitchen",
|
|
@@ -8769,52 +8910,53 @@ function renderOutputsMarkdown(outputs) {
|
|
|
8769
8910
|
__name(renderOutputsMarkdown, "renderOutputsMarkdown");
|
|
8770
8911
|
|
|
8771
8912
|
// src/tools/tasks.ts
|
|
8772
|
-
var CreateProductTaskInput =
|
|
8913
|
+
var CreateProductTaskInput = z44.object({
|
|
8773
8914
|
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:
|
|
8915
|
+
projectId: z44.string().uuid().optional().describe("Opcional. Proyecto guiado propio. Permite continuar sin volver a subir la imagen guardada; nunca evita cr\xE9ditos, cuotas ni confirmaciones."),
|
|
8916
|
+
productUrl: z44.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
8917
|
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
8918
|
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:
|
|
8919
|
+
price: z44.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.'),
|
|
8920
|
+
compareAtPrice: z44.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".'),
|
|
8921
|
+
offer: z44.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.'),
|
|
8922
|
+
paymentMethods: z44.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.'),
|
|
8923
|
+
productContext: z44.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."),
|
|
8924
|
+
productLabel: z44.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.'),
|
|
8784
8925
|
videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla. Default "auto" (la IA escoge tras analizar el producto). Todos los presets funcionan a 15s y 30s, excepto "problem_solution", que requiere 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.'),
|
|
8785
|
-
durationSeconds:
|
|
8786
|
-
|
|
8787
|
-
|
|
8926
|
+
durationSeconds: z44.union([
|
|
8927
|
+
z44.literal(15),
|
|
8928
|
+
z44.literal(30)
|
|
8788
8929
|
]).default(15).describe("Opcional. Duraci\xF3n objetivo del video: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
|
|
8789
|
-
pauseForAngleReview:
|
|
8930
|
+
pauseForAngleReview: z44.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
8931
|
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
8932
|
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:
|
|
8933
|
+
multiAngleVideos: z44.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
8934
|
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:
|
|
8935
|
+
outputs: z44.array(z44.enum([
|
|
8795
8936
|
"landing",
|
|
8796
8937
|
"video",
|
|
8797
8938
|
"image"
|
|
8798
8939
|
])).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
|
-
|
|
8940
|
+
competitorScanEnabled: z44.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."),
|
|
8941
|
+
brandId: z44.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."),
|
|
8942
|
+
actorId: z44.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."),
|
|
8943
|
+
creativeDirection: z44.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.'),
|
|
8944
|
+
headline: z44.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."),
|
|
8945
|
+
subheadline: z44.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
8946
|
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
8947
|
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
8948
|
}).strict();
|
|
8807
|
-
var CreateServiceVideoInput =
|
|
8808
|
-
landingUrl:
|
|
8809
|
-
additionalContext:
|
|
8949
|
+
var CreateServiceVideoInput = z44.object({
|
|
8950
|
+
landingUrl: z44.string().url().describe("URL p\xFAblica de la landing del servicio. Es suficiente para generar el video; los medios son opcionales en la web."),
|
|
8951
|
+
additionalContext: z44.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
8952
|
language: Lang.describe("Idioma del video: es, en o pt."),
|
|
8811
|
-
voiceId:
|
|
8812
|
-
brandId:
|
|
8813
|
-
reviewMode:
|
|
8953
|
+
voiceId: z44.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.'),
|
|
8954
|
+
brandId: z44.string().uuid().optional().describe("Marca guardada del usuario. Aplica voz, mensajes, paleta y persona predeterminada."),
|
|
8955
|
+
reviewMode: z44.enum([
|
|
8814
8956
|
"automatic",
|
|
8815
8957
|
"guided"
|
|
8816
8958
|
]).default("automatic").describe("automatic = la IA elige concepto y guion sin pausas; guided = el usuario aprueba concepto y storyboard."),
|
|
8817
|
-
videoPreset:
|
|
8959
|
+
videoPreset: z44.enum([
|
|
8818
8960
|
"auto",
|
|
8819
8961
|
"ugc_dynamic",
|
|
8820
8962
|
"ugc_how_to",
|
|
@@ -8822,34 +8964,35 @@ var CreateServiceVideoInput = z43.object({
|
|
|
8822
8964
|
"wild_card"
|
|
8823
8965
|
]).default("auto")
|
|
8824
8966
|
}).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
|
-
|
|
8967
|
+
var GetTaskStatusInput = z44.object({
|
|
8968
|
+
taskId: z44.string().min(1),
|
|
8969
|
+
waitSeconds: z44.number().int().min(0).max(120).optional()
|
|
8970
|
+
});
|
|
8971
|
+
var ListTasksInput = z44.object({
|
|
8972
|
+
limit: z44.number().int().min(1).max(50).optional(),
|
|
8973
|
+
offset: z44.number().int().min(0).optional(),
|
|
8974
|
+
search: z44.string().optional()
|
|
8975
|
+
});
|
|
8976
|
+
var EditTaskDraftInput = z44.object({
|
|
8977
|
+
taskId: z44.string().min(1),
|
|
8978
|
+
angleIndex: z44.number().int().min(0),
|
|
8979
|
+
angle: z44.object({
|
|
8980
|
+
angle: z44.string().min(1).max(480),
|
|
8981
|
+
buyerPersona: z44.string().min(1).max(360),
|
|
8982
|
+
actor: z44.string().min(1).max(360).optional(),
|
|
8983
|
+
actorId: z44.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."),
|
|
8984
|
+
gender: z44.enum([
|
|
8842
8985
|
"female",
|
|
8843
8986
|
"male"
|
|
8844
8987
|
]).optional(),
|
|
8845
|
-
scriptNotes:
|
|
8988
|
+
scriptNotes: z44.string().max(2e3).optional()
|
|
8846
8989
|
})
|
|
8847
8990
|
});
|
|
8848
|
-
var PendingActionPayload =
|
|
8849
|
-
|
|
8850
|
-
kind:
|
|
8991
|
+
var PendingActionPayload = z44.discriminatedUnion("kind", [
|
|
8992
|
+
z44.object({
|
|
8993
|
+
kind: z44.literal("product_reference_confirmation"),
|
|
8851
8994
|
userChoice: ProductReferenceUserChoiceEnum,
|
|
8852
|
-
confirmedProductNames:
|
|
8995
|
+
confirmedProductNames: z44.array(z44.string().min(1).max(120)).min(2).max(8).optional(),
|
|
8853
8996
|
// SCOPED multi-scene opt-in — the ONLY way STANDARD ("Multiescena") is
|
|
8854
8997
|
// reachable via MCP. Valid ONLY together with userChoice='variants' (same
|
|
8855
8998
|
// product in several colors): renders one shot per color and stitches them,
|
|
@@ -8860,50 +9003,50 @@ var PendingActionPayload = z43.discriminatedUnion("kind", [
|
|
|
8860
9003
|
// safe because multi-scene IS the intended render for a color variant set,
|
|
8861
9004
|
// not a blind quality upsell. Maps to `videoTier: 'standard'` on the
|
|
8862
9005
|
// confirm call; the backend re-gates it (variants + variant_set only).
|
|
8863
|
-
useMultiSceneShowcase:
|
|
9006
|
+
useMultiSceneShowcase: z44.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
9007
|
}),
|
|
8865
|
-
|
|
8866
|
-
kind:
|
|
9008
|
+
z44.object({
|
|
9009
|
+
kind: z44.literal("angle_review_approval"),
|
|
8867
9010
|
// All edits are optional — empty action body just approves the
|
|
8868
9011
|
// backend-generated draft as-is. Field shapes mirror the angle-review
|
|
8869
9012
|
// request contracts in @packages/types/wizard.ts; the handler routes to
|
|
8870
9013
|
// the dedicated video/image/autopilot endpoint based on StatusResponse.kind.
|
|
8871
|
-
customAngle:
|
|
9014
|
+
customAngle: z44.string().min(1).max(480).optional(),
|
|
8872
9015
|
// Keep the shared MCP action within the strictest downstream contract:
|
|
8873
9016
|
// autopilot/image resume endpoints cap these fields at 1500 characters.
|
|
8874
|
-
painPoints:
|
|
8875
|
-
benefits:
|
|
8876
|
-
features:
|
|
8877
|
-
gapDurationSeconds:
|
|
8878
|
-
|
|
8879
|
-
|
|
9017
|
+
painPoints: z44.string().max(1500).optional(),
|
|
9018
|
+
benefits: z44.string().max(1500).optional(),
|
|
9019
|
+
features: z44.array(z44.string().min(1).max(300)).max(10).optional(),
|
|
9020
|
+
gapDurationSeconds: z44.union([
|
|
9021
|
+
z44.literal(15),
|
|
9022
|
+
z44.literal(30)
|
|
8880
9023
|
]).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
9024
|
}),
|
|
8882
|
-
|
|
8883
|
-
kind:
|
|
8884
|
-
angleId:
|
|
8885
|
-
content:
|
|
8886
|
-
creativeDirection:
|
|
8887
|
-
actor:
|
|
9025
|
+
z44.object({
|
|
9026
|
+
kind: z44.literal("service_angle_approval"),
|
|
9027
|
+
angleId: z44.string().uuid(),
|
|
9028
|
+
content: z44.string().min(1).max(480).optional(),
|
|
9029
|
+
creativeDirection: z44.string().min(1).max(1500).optional(),
|
|
9030
|
+
actor: z44.string().min(1).max(360).optional()
|
|
8888
9031
|
}),
|
|
8889
|
-
|
|
8890
|
-
kind:
|
|
8891
|
-
voiceovers:
|
|
8892
|
-
visualDirections:
|
|
9032
|
+
z44.object({
|
|
9033
|
+
kind: z44.literal("service_script_approval"),
|
|
9034
|
+
voiceovers: z44.record(z44.string(), z44.string().min(1).max(600)).optional(),
|
|
9035
|
+
visualDirections: z44.record(z44.string(), z44.string().min(1).max(1200)).optional()
|
|
8893
9036
|
})
|
|
8894
9037
|
]);
|
|
8895
|
-
var ResolvePendingActionInput =
|
|
8896
|
-
taskId:
|
|
9038
|
+
var ResolvePendingActionInput = z44.object({
|
|
9039
|
+
taskId: z44.string().min(1),
|
|
8897
9040
|
action: PendingActionPayload
|
|
8898
9041
|
}).strict();
|
|
8899
9042
|
function resolveVariantConfirmVideoTier(action) {
|
|
8900
9043
|
return action.useMultiSceneShowcase && action.userChoice === "variants" ? "standard" : void 0;
|
|
8901
9044
|
}
|
|
8902
9045
|
__name(resolveVariantConfirmVideoTier, "resolveVariantConfirmVideoTier");
|
|
8903
|
-
var StatusForResolveSchema =
|
|
9046
|
+
var StatusForResolveSchema = z44.object({
|
|
8904
9047
|
// Metadata used to select the backend resume contract. Older API responses
|
|
8905
9048
|
// may omit these fields, so the resolver keeps the generic fallback.
|
|
8906
|
-
kind:
|
|
9049
|
+
kind: z44.enum([
|
|
8907
9050
|
"autopilot",
|
|
8908
9051
|
"video",
|
|
8909
9052
|
"image",
|
|
@@ -8911,24 +9054,24 @@ var StatusForResolveSchema = z43.object({
|
|
|
8911
9054
|
"mix",
|
|
8912
9055
|
"service"
|
|
8913
9056
|
]).optional(),
|
|
8914
|
-
selectedOutputs:
|
|
9057
|
+
selectedOutputs: z44.array(z44.enum([
|
|
8915
9058
|
"landing",
|
|
8916
9059
|
"video",
|
|
8917
9060
|
"image"
|
|
8918
9061
|
])).optional(),
|
|
8919
|
-
videoAngleCount:
|
|
8920
|
-
ugcFormat:
|
|
9062
|
+
videoAngleCount: z44.number().int().min(1).max(3).optional(),
|
|
9063
|
+
ugcFormat: z44.enum([
|
|
8921
9064
|
"single_8s",
|
|
8922
9065
|
"single_15s",
|
|
8923
9066
|
"double_30s"
|
|
8924
9067
|
]).optional(),
|
|
8925
|
-
videoStyle:
|
|
9068
|
+
videoStyle: z44.enum([
|
|
8926
9069
|
"ugc",
|
|
8927
9070
|
"cartoon_3d",
|
|
8928
9071
|
"product_only",
|
|
8929
9072
|
"ai_dynamic"
|
|
8930
9073
|
]).optional(),
|
|
8931
|
-
videoModel:
|
|
9074
|
+
videoModel: z44.enum([
|
|
8932
9075
|
"veo",
|
|
8933
9076
|
"wan",
|
|
8934
9077
|
"bytedance",
|
|
@@ -8936,39 +9079,45 @@ var StatusForResolveSchema = z43.object({
|
|
|
8936
9079
|
"grok",
|
|
8937
9080
|
"kling"
|
|
8938
9081
|
]).optional(),
|
|
8939
|
-
videoTier:
|
|
9082
|
+
videoTier: z44.enum([
|
|
8940
9083
|
"legacy",
|
|
8941
9084
|
"standard",
|
|
8942
9085
|
"pro",
|
|
8943
9086
|
"single_call"
|
|
8944
9087
|
]).optional(),
|
|
8945
|
-
videoResolution:
|
|
9088
|
+
videoResolution: z44.enum([
|
|
8946
9089
|
"480p",
|
|
8947
9090
|
"720p"
|
|
8948
9091
|
]).optional(),
|
|
8949
9092
|
narrativeStyle: NarrativeStyleEnum.optional(),
|
|
8950
|
-
pendingUserAction:
|
|
8951
|
-
kind:
|
|
8952
|
-
candidateGrouping:
|
|
8953
|
-
selectedImageUrls:
|
|
8954
|
-
proposedProductNames:
|
|
8955
|
-
customAngleDraft:
|
|
8956
|
-
painPointsDraft:
|
|
8957
|
-
benefitsDraft:
|
|
8958
|
-
featuresDraft:
|
|
9093
|
+
pendingUserAction: z44.object({
|
|
9094
|
+
kind: z44.string(),
|
|
9095
|
+
candidateGrouping: z44.string().optional(),
|
|
9096
|
+
selectedImageUrls: z44.array(z44.string()).optional(),
|
|
9097
|
+
proposedProductNames: z44.array(z44.string()).optional(),
|
|
9098
|
+
customAngleDraft: z44.string().optional(),
|
|
9099
|
+
painPointsDraft: z44.string().optional(),
|
|
9100
|
+
benefitsDraft: z44.string().optional(),
|
|
9101
|
+
featuresDraft: z44.array(z44.string()).optional(),
|
|
8959
9102
|
// Recommendation consumed only when the caller explicitly sends
|
|
8960
9103
|
// gapDurationSeconds: 30 in the angle approval action.
|
|
8961
9104
|
gapDurationChoice: GapDurationChoiceMirror.optional(),
|
|
8962
|
-
|
|
8963
|
-
|
|
8964
|
-
|
|
8965
|
-
|
|
8966
|
-
|
|
8967
|
-
|
|
8968
|
-
|
|
8969
|
-
|
|
8970
|
-
|
|
8971
|
-
|
|
9105
|
+
// `.passthrough()` + an explicit `actorId`: this object is a MIRROR of the
|
|
9106
|
+
// backend's pending service angle, and a bare z.object() STRIPS unknown
|
|
9107
|
+
// keys. `actorId` (the saved custom actor the picker stored) was silently
|
|
9108
|
+
// dropped here, so the approval handler below could not echo it back on the
|
|
9109
|
+
// PATCH — the selection looked accepted and reached no render.
|
|
9110
|
+
angles: z44.array(z44.object({
|
|
9111
|
+
id: z44.string().uuid(),
|
|
9112
|
+
content: z44.string(),
|
|
9113
|
+
creativeDirection: z44.string(),
|
|
9114
|
+
actor: z44.string(),
|
|
9115
|
+
actorId: z44.string().nullable().optional()
|
|
9116
|
+
}).passthrough()).optional(),
|
|
9117
|
+
panels: z44.array(z44.object({
|
|
9118
|
+
beatId: z44.string(),
|
|
9119
|
+
voiceover: z44.string(),
|
|
9120
|
+
visualDirection: z44.string()
|
|
8972
9121
|
}).passthrough()).optional()
|
|
8973
9122
|
}).optional()
|
|
8974
9123
|
}).passthrough();
|
|
@@ -9044,6 +9193,7 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
|
|
|
9044
9193
|
"options.selectedOutputs": input.outputs,
|
|
9045
9194
|
"options.competitorScanEnabled": input.competitorScanEnabled,
|
|
9046
9195
|
"options.brandId": input.brandId,
|
|
9196
|
+
"options.actorId": input.actorId,
|
|
9047
9197
|
"options.creativeDirection": input.creativeDirection,
|
|
9048
9198
|
"options.headline": input.headline,
|
|
9049
9199
|
"options.subheadline": input.subheadline,
|
|
@@ -9185,7 +9335,7 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
|
|
|
9185
9335
|
throw translateError(e);
|
|
9186
9336
|
}
|
|
9187
9337
|
});
|
|
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) => {
|
|
9338
|
+
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
9339
|
try {
|
|
9190
9340
|
const angle = await api.patchJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/angles/${input.angleIndex}`, input.angle ?? {});
|
|
9191
9341
|
return asText({
|
|
@@ -9361,7 +9511,13 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
|
|
|
9361
9511
|
const edits = {
|
|
9362
9512
|
content: action.content ?? candidate.content,
|
|
9363
9513
|
creativeDirection: action.creativeDirection ?? candidate.creativeDirection,
|
|
9364
|
-
actor: action.actor ?? candidate.actor
|
|
9514
|
+
actor: action.actor ?? candidate.actor,
|
|
9515
|
+
// Echo the persisted saved actor. Omitted when absent so the PATCH
|
|
9516
|
+
// never writes a null over a selection made elsewhere — the
|
|
9517
|
+
// backend only updates the keys it receives.
|
|
9518
|
+
...candidate.actorId ? {
|
|
9519
|
+
actorId: candidate.actorId
|
|
9520
|
+
} : {}
|
|
9365
9521
|
};
|
|
9366
9522
|
await api.patchJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/service-angles/${encodeURIComponent(candidate.id)}`, edits);
|
|
9367
9523
|
const result = await api.postJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/service-angles/select`, {
|
|
@@ -9402,58 +9558,58 @@ __name(registerTaskTools, "registerTaskTools");
|
|
|
9402
9558
|
|
|
9403
9559
|
// src/tools/creatives.ts
|
|
9404
9560
|
import { randomUUID } from "crypto";
|
|
9405
|
-
import { z as
|
|
9561
|
+
import { z as z45 } from "zod";
|
|
9406
9562
|
var CreativeRefs = {
|
|
9407
9563
|
images: ImagesInput,
|
|
9408
9564
|
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
9565
|
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:
|
|
9566
|
+
price: z45.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.'),
|
|
9567
|
+
offer: z45.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.'),
|
|
9568
|
+
compareAtPrice: z45.string().min(1).max(120).optional().describe('Opcional. Precio anterior o de referencia para mostrar descuento. Ej. "$129.000" cuando price="$89.900".'),
|
|
9569
|
+
paymentMethods: z45.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.'),
|
|
9570
|
+
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."),
|
|
9571
|
+
productLabel: z45.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.'),
|
|
9572
|
+
buyerPersona: z45.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.'),
|
|
9573
|
+
isCombo: z45.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
|
|
9574
|
+
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."),
|
|
9575
|
+
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 el creativo."),
|
|
9576
|
+
projectId: z45.string().uuid().optional().describe("Opcional. ID de un proyecto guiado de dropshipping activo del usuario. Enlaza esta generaci\xF3n con su progreso y recompensas."),
|
|
9577
|
+
creativeDirection: z45.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.'),
|
|
9578
|
+
headline: z45.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."),
|
|
9579
|
+
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.")
|
|
9424
9580
|
};
|
|
9425
|
-
var VideoInput =
|
|
9581
|
+
var VideoInput = z45.object({
|
|
9426
9582
|
images: CreativeRefs.images,
|
|
9427
|
-
title:
|
|
9428
|
-
primaryAngle:
|
|
9583
|
+
title: z45.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
|
|
9584
|
+
primaryAngle: z45.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
9585
|
language: CreativeRefs.language,
|
|
9430
9586
|
country: CreativeRefs.country,
|
|
9431
|
-
aspectRatio:
|
|
9587
|
+
aspectRatio: z45.enum([
|
|
9432
9588
|
"9:16",
|
|
9433
9589
|
"16:9",
|
|
9434
9590
|
"Auto"
|
|
9435
9591
|
]).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
|
-
|
|
9592
|
+
durationSeconds: z45.union([
|
|
9593
|
+
z45.literal(15),
|
|
9594
|
+
z45.literal(30)
|
|
9439
9595
|
]).default(15).describe("Opcional. Duraci\xF3n objetivo: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
|
|
9440
9596
|
videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla. Default "auto" (la IA escoge tras analizar el producto). Todos los presets funcionan a 15s y 30s, excepto "problem_solution", que requiere 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
9597
|
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
9598
|
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:
|
|
9599
|
+
features: z45.array(z45.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
|
|
9444
9600
|
price: CreativeRefs.price,
|
|
9445
9601
|
offer: CreativeRefs.offer,
|
|
9446
9602
|
productContext: CreativeRefs.productContext,
|
|
9447
9603
|
productLabel: CreativeRefs.productLabel,
|
|
9448
|
-
cta:
|
|
9449
|
-
scriptNotes:
|
|
9450
|
-
pauseForVideoScriptReview:
|
|
9451
|
-
pauseForAngleReview:
|
|
9604
|
+
cta: z45.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.'),
|
|
9605
|
+
scriptNotes: z45.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.'),
|
|
9606
|
+
pauseForVideoScriptReview: z45.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."),
|
|
9607
|
+
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. Default false."),
|
|
9452
9608
|
competitorScanEnabled: CreativeRefs.competitorScanEnabled,
|
|
9453
9609
|
brandId: CreativeRefs.brandId,
|
|
9454
9610
|
projectId: CreativeRefs.projectId
|
|
9455
9611
|
}).strict();
|
|
9456
|
-
var FreeVideoQualityEnum =
|
|
9612
|
+
var FreeVideoQualityEnum = z45.enum([
|
|
9457
9613
|
"economical",
|
|
9458
9614
|
"high_detail"
|
|
9459
9615
|
]);
|
|
@@ -9475,38 +9631,38 @@ var FREE_VIDEO_DURATIONS_BY_QUALITY = {
|
|
|
9475
9631
|
15
|
|
9476
9632
|
]
|
|
9477
9633
|
};
|
|
9478
|
-
var FreeVideoInput =
|
|
9634
|
+
var FreeVideoInput = z45.object({
|
|
9479
9635
|
images: ImagesInput.optional().describe("Opcional. 1-5 fotos del producto. Om\xEDtelo para generar desde texto puro."),
|
|
9480
|
-
rawPrompt:
|
|
9636
|
+
rawPrompt: z45.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
9637
|
quality: FreeVideoQualityEnum.describe('Calidad del video: "economical" = Econ\xF3mico o "high_detail" = Alto detalle.'),
|
|
9482
|
-
durationSeconds:
|
|
9638
|
+
durationSeconds: z45.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
9639
|
videoResolution: VideoResolutionEnum.describe("Resoluci\xF3n del video: 480p o 720p."),
|
|
9484
|
-
generateAiMusic:
|
|
9485
|
-
addSubtitles:
|
|
9486
|
-
idempotencyKey:
|
|
9640
|
+
generateAiMusic: z45.boolean().default(false).describe("Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false."),
|
|
9641
|
+
addSubtitles: z45.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
|
|
9642
|
+
idempotencyKey: z45.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
9643
|
}).strict();
|
|
9488
9644
|
var imageStyleList = ImageCreativeStyleEnum.options.join(" | ");
|
|
9489
9645
|
var archetypeList = ImageCreativeArchetypeEnum.options.join(" | ");
|
|
9490
9646
|
var visualTreatmentList = ImageCreativeVisualTreatmentEnum.options.join(" | ");
|
|
9491
9647
|
var aspectRatioList = ImageCreativeAspectRatioEnum.options.join(" | ");
|
|
9492
|
-
var ImageInput =
|
|
9648
|
+
var ImageInput = z45.object({
|
|
9493
9649
|
images: CreativeRefs.images,
|
|
9494
|
-
productTitle:
|
|
9495
|
-
primaryAngle:
|
|
9650
|
+
productTitle: z45.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
|
|
9651
|
+
primaryAngle: z45.string().min(1).describe('\xC1ngulo de venta principal. Ej. "Reduce arrugas en 14 d\xEDas".'),
|
|
9496
9652
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
9497
9653
|
language: CreativeRefs.language,
|
|
9498
9654
|
country: CreativeRefs.country,
|
|
9499
|
-
nVariants:
|
|
9500
|
-
imageModel:
|
|
9655
|
+
nVariants: z45.number().int().min(1).max(6).optional().describe("Opcional. N\xFAmero de variantes a generar (1-6). Default 3."),
|
|
9656
|
+
imageModel: z45.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
9657
|
imageStyle: ImageCreativeStyleEnum.optional().describe(`Opcional. Estilo del shot del producto. Valores: ${imageStyleList}. Si se omite, la IA escoge seg\xFAn el archetype.`),
|
|
9502
9658
|
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
9659
|
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:
|
|
9660
|
+
styleMix: z45.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
9661
|
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:
|
|
9662
|
+
generateAdCopy: z45.boolean().optional().describe("Opcional. Si true, tambi\xE9n genera headline/body/CTA para el ad. Default false."),
|
|
9663
|
+
pauseForAngleReview: z45.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."),
|
|
9664
|
+
features: z45.array(z45.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
|
|
9665
|
+
brandColors: z45.array(z45.string()).optional().describe('Opcional. Paleta de marca en hex. Ej. ["#FF6B35", "#004E89"]. Si se omite, la IA elige seg\xFAn el producto.'),
|
|
9510
9666
|
price: CreativeRefs.price,
|
|
9511
9667
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
9512
9668
|
offer: CreativeRefs.offer,
|
|
@@ -9520,16 +9676,16 @@ var ImageInput = z44.object({
|
|
|
9520
9676
|
headline: CreativeRefs.headline,
|
|
9521
9677
|
subheadline: CreativeRefs.subheadline
|
|
9522
9678
|
});
|
|
9523
|
-
var EditImageCreativeInput =
|
|
9524
|
-
taskId:
|
|
9525
|
-
variantIndex:
|
|
9526
|
-
instruction:
|
|
9679
|
+
var EditImageCreativeInput = z45.object({
|
|
9680
|
+
taskId: z45.string().uuid().describe("ID de la tarea de imagen ya finalizada."),
|
|
9681
|
+
variantIndex: z45.number().int().min(0).max(10).describe("\xCDndice de la variante a corregir (0 = primera imagen)."),
|
|
9682
|
+
instruction: z45.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
9683
|
});
|
|
9528
|
-
var LandingInput =
|
|
9684
|
+
var LandingInput = z45.object({
|
|
9529
9685
|
images: CreativeRefs.images,
|
|
9530
9686
|
language: CreativeRefs.language,
|
|
9531
9687
|
country: CreativeRefs.country,
|
|
9532
|
-
salesAngle:
|
|
9688
|
+
salesAngle: z45.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
9689
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
9534
9690
|
price: CreativeRefs.price,
|
|
9535
9691
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
@@ -9578,7 +9734,7 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
9578
9734
|
throw translateError(e);
|
|
9579
9735
|
}
|
|
9580
9736
|
});
|
|
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) => {
|
|
9737
|
+
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
9738
|
try {
|
|
9583
9739
|
assertVideoPresetDurationCompatibility(input.videoPreset, input.durationSeconds);
|
|
9584
9740
|
const resolved = resolveVideoPreset(input.videoPreset);
|
|
@@ -9655,8 +9811,8 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
9655
9811
|
__name(registerCreativeTools, "registerCreativeTools");
|
|
9656
9812
|
|
|
9657
9813
|
// src/tools/creative-variants.ts
|
|
9658
|
-
import { z as
|
|
9659
|
-
var LandingSectionKeyEnum =
|
|
9814
|
+
import { z as z46 } from "zod";
|
|
9815
|
+
var LandingSectionKeyEnum = z46.enum([
|
|
9660
9816
|
"problem_hook",
|
|
9661
9817
|
"problem_cost",
|
|
9662
9818
|
"solution_mechanism",
|
|
@@ -9696,24 +9852,24 @@ var LandingSectionKeyEnum = z45.enum([
|
|
|
9696
9852
|
"lifestyle_fit",
|
|
9697
9853
|
"join_lifestyle"
|
|
9698
9854
|
]);
|
|
9699
|
-
var CreativeVariantKindEnum =
|
|
9855
|
+
var CreativeVariantKindEnum = z46.enum([
|
|
9700
9856
|
"video",
|
|
9701
9857
|
"image",
|
|
9702
9858
|
"landing"
|
|
9703
9859
|
]);
|
|
9704
|
-
var EstimateCreativeVariantInputBase =
|
|
9705
|
-
sourceTaskId:
|
|
9860
|
+
var EstimateCreativeVariantInputBase = z46.object({
|
|
9861
|
+
sourceTaskId: z46.string().uuid().describe("ID de la tarea original completada."),
|
|
9706
9862
|
kind: CreativeVariantKindEnum,
|
|
9707
9863
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Solo cambia el preset p\xFAblico; el servidor conserva el modelo/tier del task origen."),
|
|
9708
9864
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video. Resoluci\xF3n del nuevo render."),
|
|
9709
|
-
nVariants:
|
|
9865
|
+
nVariants: z46.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
9710
9866
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si ya conoces la secci\xF3n, evita cotizar la clasificaci\xF3n con IA."),
|
|
9711
|
-
instructions:
|
|
9867
|
+
instructions: z46.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
9868
|
}).strict();
|
|
9713
9869
|
var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
9714
9870
|
if (input.kind === "video" && input.nVariants !== void 0) {
|
|
9715
9871
|
ctx.addIssue({
|
|
9716
|
-
code:
|
|
9872
|
+
code: z46.ZodIssueCode.custom,
|
|
9717
9873
|
path: [
|
|
9718
9874
|
"nVariants"
|
|
9719
9875
|
],
|
|
@@ -9723,7 +9879,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9723
9879
|
if (input.kind !== "video") {
|
|
9724
9880
|
if (input.videoPreset !== void 0) {
|
|
9725
9881
|
ctx.addIssue({
|
|
9726
|
-
code:
|
|
9882
|
+
code: z46.ZodIssueCode.custom,
|
|
9727
9883
|
path: [
|
|
9728
9884
|
"videoPreset"
|
|
9729
9885
|
],
|
|
@@ -9732,7 +9888,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9732
9888
|
}
|
|
9733
9889
|
if (input.videoResolution !== void 0) {
|
|
9734
9890
|
ctx.addIssue({
|
|
9735
|
-
code:
|
|
9891
|
+
code: z46.ZodIssueCode.custom,
|
|
9736
9892
|
path: [
|
|
9737
9893
|
"videoResolution"
|
|
9738
9894
|
],
|
|
@@ -9742,7 +9898,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9742
9898
|
}
|
|
9743
9899
|
if (input.kind !== "image" && input.nVariants !== void 0) {
|
|
9744
9900
|
ctx.addIssue({
|
|
9745
|
-
code:
|
|
9901
|
+
code: z46.ZodIssueCode.custom,
|
|
9746
9902
|
path: [
|
|
9747
9903
|
"nVariants"
|
|
9748
9904
|
],
|
|
@@ -9752,7 +9908,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9752
9908
|
if (input.kind !== "landing") {
|
|
9753
9909
|
if (input.sectionKey !== void 0) {
|
|
9754
9910
|
ctx.addIssue({
|
|
9755
|
-
code:
|
|
9911
|
+
code: z46.ZodIssueCode.custom,
|
|
9756
9912
|
path: [
|
|
9757
9913
|
"sectionKey"
|
|
9758
9914
|
],
|
|
@@ -9761,7 +9917,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9761
9917
|
}
|
|
9762
9918
|
if (input.instructions !== void 0) {
|
|
9763
9919
|
ctx.addIssue({
|
|
9764
|
-
code:
|
|
9920
|
+
code: z46.ZodIssueCode.custom,
|
|
9765
9921
|
path: [
|
|
9766
9922
|
"instructions"
|
|
9767
9923
|
],
|
|
@@ -9770,31 +9926,31 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9770
9926
|
}
|
|
9771
9927
|
}
|
|
9772
9928
|
});
|
|
9773
|
-
var CreateCreativeVariantInputBase =
|
|
9774
|
-
sourceTaskId:
|
|
9929
|
+
var CreateCreativeVariantInputBase = z46.object({
|
|
9930
|
+
sourceTaskId: z46.string().uuid().describe("ID de la tarea original completada."),
|
|
9775
9931
|
kind: CreativeVariantKindEnum,
|
|
9776
|
-
confirm:
|
|
9777
|
-
clientRequestId:
|
|
9778
|
-
angle:
|
|
9779
|
-
buyerPersona:
|
|
9780
|
-
scriptNotes:
|
|
9781
|
-
creativeDirection:
|
|
9932
|
+
confirm: z46.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
|
|
9933
|
+
clientRequestId: z46.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."),
|
|
9934
|
+
angle: z46.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
|
|
9935
|
+
buyerPersona: z46.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
|
|
9936
|
+
scriptNotes: z46.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
|
|
9937
|
+
creativeDirection: z46.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
|
|
9782
9938
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Preset p\xFAblico; el servidor resuelve modelo/tier desde el task origen."),
|
|
9783
9939
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video."),
|
|
9784
|
-
generateAiMusic:
|
|
9785
|
-
nVariants:
|
|
9940
|
+
generateAiMusic: z46.boolean().optional().describe("Opcional para video."),
|
|
9941
|
+
nVariants: z46.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
9786
9942
|
imageStyle: ImageCreativeStyleEnum.optional().describe("Opcional para imagen."),
|
|
9787
9943
|
creativeArchetype: ImageCreativeArchetypeEnum.optional().describe("Opcional para imagen."),
|
|
9788
9944
|
visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe("Opcional para imagen."),
|
|
9789
9945
|
aspectRatio: ImageCreativeAspectRatioEnum.optional().describe("Opcional para imagen."),
|
|
9790
|
-
styleMix:
|
|
9946
|
+
styleMix: z46.boolean().optional().describe("Opcional para imagen."),
|
|
9791
9947
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si se omite, se clasifica con IA a partir de instructions."),
|
|
9792
|
-
instructions:
|
|
9948
|
+
instructions: z46.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
9949
|
}).strict();
|
|
9794
9950
|
var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
9795
9951
|
if ((input.kind === "video" || input.kind === "image") && !input.angle) {
|
|
9796
9952
|
ctx.addIssue({
|
|
9797
|
-
code:
|
|
9953
|
+
code: z46.ZodIssueCode.custom,
|
|
9798
9954
|
path: [
|
|
9799
9955
|
"angle"
|
|
9800
9956
|
],
|
|
@@ -9803,7 +9959,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
9803
9959
|
}
|
|
9804
9960
|
if (input.kind === "landing" && !input.sectionKey && !input.instructions) {
|
|
9805
9961
|
ctx.addIssue({
|
|
9806
|
-
code:
|
|
9962
|
+
code: z46.ZodIssueCode.custom,
|
|
9807
9963
|
path: [
|
|
9808
9964
|
"instructions"
|
|
9809
9965
|
],
|
|
@@ -9849,7 +10005,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
9849
10005
|
for (const field of fieldsByKind[input.kind]) {
|
|
9850
10006
|
if (input[field] !== void 0) {
|
|
9851
10007
|
ctx.addIssue({
|
|
9852
|
-
code:
|
|
10008
|
+
code: z46.ZodIssueCode.custom,
|
|
9853
10009
|
path: [
|
|
9854
10010
|
field
|
|
9855
10011
|
],
|
|
@@ -9966,27 +10122,27 @@ function registerCreativeVariantTools(server, api) {
|
|
|
9966
10122
|
__name(registerCreativeVariantTools, "registerCreativeVariantTools");
|
|
9967
10123
|
|
|
9968
10124
|
// src/tools/script-review.ts
|
|
9969
|
-
import { z as
|
|
9970
|
-
var GetVideoScriptInput =
|
|
9971
|
-
taskId:
|
|
9972
|
-
angleKey:
|
|
10125
|
+
import { z as z47 } from "zod";
|
|
10126
|
+
var GetVideoScriptInput = z47.object({
|
|
10127
|
+
taskId: z47.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
|
|
10128
|
+
angleKey: z47.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
10129
|
}).strict();
|
|
9974
10130
|
var VIDEO_SCRIPT_APPROVAL_MAX_SCENES2 = 50;
|
|
9975
10131
|
var VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2 = 2e3;
|
|
9976
|
-
var ApproveSceneEdit =
|
|
9977
|
-
index:
|
|
9978
|
-
dialogue:
|
|
10132
|
+
var ApproveSceneEdit = z47.object({
|
|
10133
|
+
index: z47.number().int().nonnegative().describe("\xCDndice de la escena (0-based, mismo que devuelve get_video_script)."),
|
|
10134
|
+
dialogue: z47.string().min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2).refine((v) => v.trim().length > 0, {
|
|
9979
10135
|
message: "dialogue no puede ser s\xF3lo espacios"
|
|
9980
10136
|
}).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
10137
|
}).strict();
|
|
9982
|
-
var ApproveVideoScriptInput =
|
|
9983
|
-
taskId:
|
|
9984
|
-
angleKey:
|
|
9985
|
-
scenes:
|
|
10138
|
+
var ApproveVideoScriptInput = z47.object({
|
|
10139
|
+
taskId: z47.string().min(1).describe("ID de la tarea."),
|
|
10140
|
+
angleKey: z47.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.'),
|
|
10141
|
+
scenes: z47.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
10142
|
}).strict();
|
|
9987
|
-
var RegenerateVideoScriptInput =
|
|
9988
|
-
taskId:
|
|
9989
|
-
angleKey:
|
|
10143
|
+
var RegenerateVideoScriptInput = z47.object({
|
|
10144
|
+
taskId: z47.string().min(1).describe("ID de la tarea."),
|
|
10145
|
+
angleKey: z47.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
10146
|
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
10147
|
}).strict();
|
|
9992
10148
|
function registerScriptReviewTools(server, api) {
|
|
@@ -10026,9 +10182,9 @@ function registerScriptReviewTools(server, api) {
|
|
|
10026
10182
|
__name(registerScriptReviewTools, "registerScriptReviewTools");
|
|
10027
10183
|
|
|
10028
10184
|
// src/tools/connections.ts
|
|
10029
|
-
import { z as
|
|
10185
|
+
import { z as z48 } from "zod";
|
|
10030
10186
|
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.",
|
|
10187
|
+
server.tool("list_shopify_shops", "Lista las tiendas Shopify conectadas por este usuario. Si la lista est\xE1 vac\xEDa, dile al usuario que conecte una tienda en productmaker.app/integrations antes de intentar publicar.", z48.object({}).shape, async () => {
|
|
10032
10188
|
try {
|
|
10033
10189
|
const r = await api.getJson("/v1/shopify/shops");
|
|
10034
10190
|
return asText(r);
|
|
@@ -10036,10 +10192,10 @@ function registerConnectionTools(server, api) {
|
|
|
10036
10192
|
throw translateError(e);
|
|
10037
10193
|
}
|
|
10038
10194
|
});
|
|
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:
|
|
10195
|
+
server.tool("list_shopify_products", 'Busca productos en una tienda Shopify conectada para elegir el target de una republicaci\xF3n. Usa el `id` devuelto junto con `publicationMode="existing"` en `publish_to_shopify`. Si `taskId` se proporciona, la respuesta tambi\xE9n indica el producto publicado actualmente para esa tarea y tienda. No modifica Shopify.', z48.object({
|
|
10196
|
+
shopDomain: z48.string().min(3).max(120),
|
|
10197
|
+
query: z48.string().trim().max(120).optional(),
|
|
10198
|
+
taskId: z48.string().uuid().optional()
|
|
10043
10199
|
}).shape, async ({ shopDomain, query, taskId }) => {
|
|
10044
10200
|
try {
|
|
10045
10201
|
const params = new URLSearchParams({
|
|
@@ -10053,7 +10209,7 @@ function registerConnectionTools(server, api) {
|
|
|
10053
10209
|
throw translateError(e);
|
|
10054
10210
|
}
|
|
10055
10211
|
});
|
|
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.",
|
|
10212
|
+
server.tool("list_meta_ad_accounts", "Lista las cuentas publicitarias de Meta conectadas. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar Meta en productmaker.app/integrations.", z48.object({}).shape, async () => {
|
|
10057
10213
|
try {
|
|
10058
10214
|
const r = await api.getJson("/v1/facebook/ad-accounts");
|
|
10059
10215
|
return asText(r);
|
|
@@ -10061,7 +10217,7 @@ function registerConnectionTools(server, api) {
|
|
|
10061
10217
|
throw translateError(e);
|
|
10062
10218
|
}
|
|
10063
10219
|
});
|
|
10064
|
-
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.",
|
|
10220
|
+
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.", z48.object({}).shape, async () => {
|
|
10065
10221
|
try {
|
|
10066
10222
|
const r = await api.getJson("/v1/tiktok/advertisers");
|
|
10067
10223
|
return asText(r);
|
|
@@ -10072,106 +10228,129 @@ function registerConnectionTools(server, api) {
|
|
|
10072
10228
|
}
|
|
10073
10229
|
__name(registerConnectionTools, "registerConnectionTools");
|
|
10074
10230
|
|
|
10231
|
+
// src/tools/actors.ts
|
|
10232
|
+
import { z as z49 } from "zod";
|
|
10233
|
+
function registerActorTools(server, api) {
|
|
10234
|
+
server.tool("list_actors", "Lista los personajes guardados del usuario (personas creadas con IA que pueden protagonizar los videos e im\xE1genes). Devuelve id, nombre, descripci\xF3n, g\xE9nero y voz. Usa el `id` como `actorId` al crear o editar una tarea. Solo aparecen los personajes listos para usar; si la lista est\xE1 vac\xEDa, dile al usuario que cree uno en productmaker.app antes de intentar asignarlo. No modifica nada.", z49.object({}).shape, async () => {
|
|
10235
|
+
try {
|
|
10236
|
+
const rows = await api.getJson("/v1/actors");
|
|
10237
|
+
const actors = (Array.isArray(rows) ? rows : []).filter((actor) => actor.status === "ready").map(({ id, name, description, gender, voiceId }) => ({
|
|
10238
|
+
id,
|
|
10239
|
+
name,
|
|
10240
|
+
description,
|
|
10241
|
+
gender,
|
|
10242
|
+
voiceId: voiceId ?? null
|
|
10243
|
+
}));
|
|
10244
|
+
return asText({
|
|
10245
|
+
actors
|
|
10246
|
+
});
|
|
10247
|
+
} catch (e) {
|
|
10248
|
+
throw translateError(e);
|
|
10249
|
+
}
|
|
10250
|
+
});
|
|
10251
|
+
}
|
|
10252
|
+
__name(registerActorTools, "registerActorTools");
|
|
10253
|
+
|
|
10075
10254
|
// src/tools/publish.ts
|
|
10076
|
-
import { z as
|
|
10077
|
-
var ShopifyInput =
|
|
10078
|
-
taskId:
|
|
10079
|
-
shopDomain:
|
|
10255
|
+
import { z as z50 } from "zod";
|
|
10256
|
+
var ShopifyInput = z50.object({
|
|
10257
|
+
taskId: z50.string().min(1),
|
|
10258
|
+
shopDomain: z50.string().min(1),
|
|
10080
10259
|
/** Override: number of landing images to include. Default = ALL images the
|
|
10081
10260
|
* task generated (the MCP fetches /v1/results first to count). Bounded
|
|
10082
10261
|
* generously here; the backend validates each index against
|
|
10083
10262
|
* landing.images.length and rejects out-of-range. */
|
|
10084
|
-
numLandingImages:
|
|
10085
|
-
variantIndex:
|
|
10263
|
+
numLandingImages: z50.number().int().min(1).max(20).optional(),
|
|
10264
|
+
variantIndex: z50.number().int().min(0).optional(),
|
|
10086
10265
|
/** ASK THE USER before setting: "¿Tu producto tiene variantes (talla, color, etc.)?".
|
|
10087
10266
|
* When true, the landing renders variant-picker pills; when false, it ships
|
|
10088
10267
|
* a single-option product page. Defaults to false. */
|
|
10089
|
-
showVariantPicker:
|
|
10268
|
+
showVariantPicker: z50.boolean().optional(),
|
|
10090
10269
|
/** Default `auto` updates the product previously published for this task and
|
|
10091
10270
|
* shop. Use `existing` with targetProductId to choose another product, or
|
|
10092
10271
|
* `new` only when the user explicitly wants a duplicate/new listing. */
|
|
10093
|
-
publicationMode:
|
|
10272
|
+
publicationMode: z50.enum([
|
|
10094
10273
|
"auto",
|
|
10095
10274
|
"existing",
|
|
10096
10275
|
"new"
|
|
10097
10276
|
]).optional(),
|
|
10098
10277
|
/** Shopify product GID, numeric id, or admin product URL. Required when
|
|
10099
10278
|
* publicationMode is `existing`. */
|
|
10100
|
-
targetProductId:
|
|
10279
|
+
targetProductId: z50.string().trim().min(1).max(300).optional()
|
|
10101
10280
|
});
|
|
10102
|
-
var McpCampaignConfig =
|
|
10281
|
+
var McpCampaignConfig = z50.object({
|
|
10103
10282
|
/** ALWAYS in US dollars, whatever the ad account's currency is. ProductMaker
|
|
10104
10283
|
* converts to the account currency server-side. Do NOT pre-convert: on a COP
|
|
10105
10284
|
* account the user's "60.000 al día" is `dailyBudgetUsd: 15`, not 60000. */
|
|
10106
|
-
dailyBudgetUsd:
|
|
10285
|
+
dailyBudgetUsd: z50.number().positive().optional(),
|
|
10107
10286
|
/** ISO-3166-1 alpha-2 country codes. Meta accepts max 1 (single-country
|
|
10108
10287
|
* ad set). TikTok takes only the first entry. */
|
|
10109
|
-
targetCountries:
|
|
10110
|
-
campaignName:
|
|
10288
|
+
targetCountries: z50.array(z50.string().length(2)).max(1).optional(),
|
|
10289
|
+
campaignName: z50.string().trim().min(1).max(100).optional()
|
|
10111
10290
|
});
|
|
10112
10291
|
var McpAdAccountId = MetaAdAccountId.describe('Meta ad account id. Accepts "act_1234567890" or the bare numeric id "1234567890".');
|
|
10113
|
-
var MetaInput =
|
|
10114
|
-
taskId:
|
|
10292
|
+
var MetaInput = z50.object({
|
|
10293
|
+
taskId: z50.string().min(1),
|
|
10115
10294
|
adAccountId: McpAdAccountId,
|
|
10116
|
-
pixelId:
|
|
10117
|
-
landingUrl:
|
|
10295
|
+
pixelId: z50.string().min(1).optional(),
|
|
10296
|
+
landingUrl: z50.string().url().optional(),
|
|
10118
10297
|
/** Select which angles to publish by 0-based index. Omit to publish all
|
|
10119
10298
|
* successful angles. Example: a task with 3 angles → `[0,2]` publishes #0 + #2. */
|
|
10120
|
-
angleIndexes:
|
|
10299
|
+
angleIndexes: z50.array(z50.number().int().nonnegative()).optional(),
|
|
10121
10300
|
campaignConfig: McpCampaignConfig.optional(),
|
|
10122
10301
|
/** Override the ad's primary text (body copy). If omitted, ProductMaker
|
|
10123
10302
|
* uses the auto-generated copy attached to the task. Capped at 500 chars
|
|
10124
10303
|
* (Meta's primary-text limit). */
|
|
10125
|
-
primaryText:
|
|
10304
|
+
primaryText: z50.string().trim().min(1).max(500).optional(),
|
|
10126
10305
|
/** Optional WhatsApp number. When set, the ad button opens a WhatsApp chat
|
|
10127
10306
|
* (wa.me) instead of a landing page. */
|
|
10128
|
-
phoneNumber:
|
|
10307
|
+
phoneNumber: z50.string().min(8).max(32).optional()
|
|
10129
10308
|
});
|
|
10130
|
-
var GroupedTaskSource =
|
|
10131
|
-
type:
|
|
10132
|
-
taskId:
|
|
10133
|
-
angleIndex:
|
|
10134
|
-
videoVariant:
|
|
10309
|
+
var GroupedTaskSource = z50.object({
|
|
10310
|
+
type: z50.literal("task"),
|
|
10311
|
+
taskId: z50.string().min(1),
|
|
10312
|
+
angleIndex: z50.number().int().min(0).optional(),
|
|
10313
|
+
videoVariant: z50.enum([
|
|
10135
10314
|
"with_subtitles",
|
|
10136
10315
|
"without_subtitles"
|
|
10137
10316
|
]).optional(),
|
|
10138
|
-
imageVariantIndex:
|
|
10139
|
-
});
|
|
10140
|
-
var GroupedMediaSource =
|
|
10141
|
-
type:
|
|
10142
|
-
url:
|
|
10143
|
-
path:
|
|
10144
|
-
thumbnailUrl:
|
|
10145
|
-
thumbnailPath:
|
|
10317
|
+
imageVariantIndex: z50.number().int().min(0).optional()
|
|
10318
|
+
});
|
|
10319
|
+
var GroupedMediaSource = z50.object({
|
|
10320
|
+
type: z50.literal("media"),
|
|
10321
|
+
url: z50.string().url().optional(),
|
|
10322
|
+
path: z50.string().min(1).optional(),
|
|
10323
|
+
thumbnailUrl: z50.string().url().optional(),
|
|
10324
|
+
thumbnailPath: z50.string().min(1).optional()
|
|
10146
10325
|
}).refine((value) => Boolean(value.url) !== Boolean(value.path), {
|
|
10147
10326
|
message: "media source needs exactly one of `url` or `path`"
|
|
10148
10327
|
}).refine((value) => !(value.thumbnailUrl && value.thumbnailPath), {
|
|
10149
10328
|
message: "media source needs at most one of `thumbnailUrl` or `thumbnailPath`"
|
|
10150
10329
|
});
|
|
10151
|
-
var GroupedCreative =
|
|
10152
|
-
type:
|
|
10330
|
+
var GroupedCreative = z50.object({
|
|
10331
|
+
type: z50.enum([
|
|
10153
10332
|
"image",
|
|
10154
10333
|
"video"
|
|
10155
10334
|
]),
|
|
10156
|
-
source:
|
|
10335
|
+
source: z50.union([
|
|
10157
10336
|
GroupedTaskSource,
|
|
10158
10337
|
GroupedMediaSource
|
|
10159
10338
|
]),
|
|
10160
|
-
title:
|
|
10161
|
-
description:
|
|
10339
|
+
title: z50.string().max(120).optional(),
|
|
10340
|
+
description: z50.string().max(500).optional(),
|
|
10162
10341
|
cta: MetaCtaType.optional(),
|
|
10163
|
-
creativeId:
|
|
10342
|
+
creativeId: z50.string().min(1).optional()
|
|
10164
10343
|
});
|
|
10165
|
-
var GroupedCampaignConfig =
|
|
10344
|
+
var GroupedCampaignConfig = z50.object({
|
|
10166
10345
|
campaignType: MetaCampaignType.default("sales"),
|
|
10167
10346
|
optimizationMode: MetaOptimizationMode.default("advantage_plus"),
|
|
10168
10347
|
budgetMode: MetaBudgetMode.default("campaign"),
|
|
10169
|
-
campaignDailyBudget:
|
|
10170
|
-
adSets:
|
|
10171
|
-
tempId:
|
|
10172
|
-
name:
|
|
10173
|
-
dailyBudget:
|
|
10174
|
-
creativeIds:
|
|
10348
|
+
campaignDailyBudget: z50.number().int().positive().optional(),
|
|
10349
|
+
adSets: z50.array(z50.object({
|
|
10350
|
+
tempId: z50.string().min(1).max(80),
|
|
10351
|
+
name: z50.string().min(1).max(100),
|
|
10352
|
+
dailyBudget: z50.number().int().positive().optional(),
|
|
10353
|
+
creativeIds: z50.array(z50.string().min(1)).min(1)
|
|
10175
10354
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_AD_SETS),
|
|
10176
10355
|
/**
|
|
10177
10356
|
* Valid values are 'landing' and 'whatsapp' — there is no 'website'. This
|
|
@@ -10179,22 +10358,22 @@ var GroupedCampaignConfig = z48.object({
|
|
|
10179
10358
|
* opaque `invalid_literal` and made the WhatsApp destination — supported by
|
|
10180
10359
|
* the API since #849 — unreachable from this tool.
|
|
10181
10360
|
*/
|
|
10182
|
-
destination:
|
|
10183
|
-
|
|
10184
|
-
type:
|
|
10185
|
-
landingUrl:
|
|
10361
|
+
destination: z50.discriminatedUnion("type", [
|
|
10362
|
+
z50.object({
|
|
10363
|
+
type: z50.literal("landing"),
|
|
10364
|
+
landingUrl: z50.string().url()
|
|
10186
10365
|
}),
|
|
10187
|
-
|
|
10188
|
-
type:
|
|
10189
|
-
phoneNumber:
|
|
10366
|
+
z50.object({
|
|
10367
|
+
type: z50.literal("whatsapp"),
|
|
10368
|
+
phoneNumber: z50.string().min(8).max(32)
|
|
10190
10369
|
})
|
|
10191
10370
|
]),
|
|
10192
|
-
saveAsDefault:
|
|
10371
|
+
saveAsDefault: z50.boolean().default(false),
|
|
10193
10372
|
budgetPolicy: MetaBudgetPolicy.optional()
|
|
10194
10373
|
}).superRefine((value, ctx) => {
|
|
10195
10374
|
if (value.budgetMode === "campaign" && value.campaignDailyBudget === void 0) {
|
|
10196
10375
|
ctx.addIssue({
|
|
10197
|
-
code:
|
|
10376
|
+
code: z50.ZodIssueCode.custom,
|
|
10198
10377
|
path: [
|
|
10199
10378
|
"campaignDailyBudget"
|
|
10200
10379
|
],
|
|
@@ -10205,7 +10384,7 @@ var GroupedCampaignConfig = z48.object({
|
|
|
10205
10384
|
value.adSets.forEach((adSet, index) => {
|
|
10206
10385
|
if (adSet.dailyBudget === void 0) {
|
|
10207
10386
|
ctx.addIssue({
|
|
10208
|
-
code:
|
|
10387
|
+
code: z50.ZodIssueCode.custom,
|
|
10209
10388
|
path: [
|
|
10210
10389
|
"adSets",
|
|
10211
10390
|
index,
|
|
@@ -10217,22 +10396,22 @@ var GroupedCampaignConfig = z48.object({
|
|
|
10217
10396
|
});
|
|
10218
10397
|
}
|
|
10219
10398
|
});
|
|
10220
|
-
var PublishCreativesToMetaInputBase =
|
|
10221
|
-
creatives:
|
|
10399
|
+
var PublishCreativesToMetaInputBase = z50.object({
|
|
10400
|
+
creatives: z50.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
10222
10401
|
adAccountId: McpAdAccountId,
|
|
10223
|
-
pixelId:
|
|
10402
|
+
pixelId: z50.string().min(1).optional(),
|
|
10224
10403
|
/** Required unless `campaignConfig.destination` is whatsapp — see the
|
|
10225
10404
|
* superRefine below. Mirrors the API's `UploadToMetaRequest`. */
|
|
10226
|
-
landingUrl:
|
|
10227
|
-
targetCountry:
|
|
10228
|
-
campaignName:
|
|
10405
|
+
landingUrl: z50.string().url().optional(),
|
|
10406
|
+
targetCountry: z50.string().length(2),
|
|
10407
|
+
campaignName: z50.string().trim().min(1).max(100),
|
|
10229
10408
|
campaignConfig: GroupedCampaignConfig.optional()
|
|
10230
10409
|
});
|
|
10231
10410
|
var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((value, ctx) => {
|
|
10232
10411
|
const destination = value.campaignConfig?.destination;
|
|
10233
10412
|
if (destination?.type !== "whatsapp" && !value.landingUrl) {
|
|
10234
10413
|
ctx.addIssue({
|
|
10235
|
-
code:
|
|
10414
|
+
code: z50.ZodIssueCode.custom,
|
|
10236
10415
|
path: [
|
|
10237
10416
|
"landingUrl"
|
|
10238
10417
|
],
|
|
@@ -10241,7 +10420,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
10241
10420
|
}
|
|
10242
10421
|
if (destination?.type === "landing" && destination.landingUrl !== value.landingUrl) {
|
|
10243
10422
|
ctx.addIssue({
|
|
10244
|
-
code:
|
|
10423
|
+
code: z50.ZodIssueCode.custom,
|
|
10245
10424
|
path: [
|
|
10246
10425
|
"campaignConfig",
|
|
10247
10426
|
"destination",
|
|
@@ -10251,12 +10430,12 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
10251
10430
|
});
|
|
10252
10431
|
}
|
|
10253
10432
|
});
|
|
10254
|
-
var PresignUploadResponseSchema =
|
|
10255
|
-
uploadUrl:
|
|
10433
|
+
var PresignUploadResponseSchema = z50.object({
|
|
10434
|
+
uploadUrl: z50.string().url().refine((value) => {
|
|
10256
10435
|
const url = new URL(value);
|
|
10257
10436
|
return !url.username && !url.password && url.protocol === "https:";
|
|
10258
10437
|
}, "presigned upload URL must be HTTPS without embedded credentials"),
|
|
10259
|
-
publicUrl:
|
|
10438
|
+
publicUrl: z50.string().url().refine((value) => {
|
|
10260
10439
|
const url = new URL(value);
|
|
10261
10440
|
return !url.username && !url.password;
|
|
10262
10441
|
}, "public media URL must not contain embedded credentials")
|
|
@@ -10281,16 +10460,16 @@ async function resolveAndUploadMedia(api, ref, expectedKind, transport) {
|
|
|
10281
10460
|
return uploadResolvedMedia(api, media);
|
|
10282
10461
|
}
|
|
10283
10462
|
__name(resolveAndUploadMedia, "resolveAndUploadMedia");
|
|
10284
|
-
var TikTokInput =
|
|
10285
|
-
taskId:
|
|
10286
|
-
advertiserId:
|
|
10287
|
-
pixelId:
|
|
10288
|
-
landingUrl:
|
|
10289
|
-
angleIndexes:
|
|
10463
|
+
var TikTokInput = z50.object({
|
|
10464
|
+
taskId: z50.string().min(1),
|
|
10465
|
+
advertiserId: z50.string().min(1),
|
|
10466
|
+
pixelId: z50.string().min(1).optional(),
|
|
10467
|
+
landingUrl: z50.string().url().optional(),
|
|
10468
|
+
angleIndexes: z50.array(z50.number().int().nonnegative()).optional(),
|
|
10290
10469
|
campaignConfig: McpCampaignConfig.optional(),
|
|
10291
10470
|
/** Override the ad's description (TikTok's body copy). If omitted,
|
|
10292
10471
|
* ProductMaker uses the auto-generated copy attached to the task. */
|
|
10293
|
-
primaryText:
|
|
10472
|
+
primaryText: z50.string().trim().min(1).max(500).optional()
|
|
10294
10473
|
});
|
|
10295
10474
|
function registerPublishTools(server, api, opts = {}) {
|
|
10296
10475
|
const transport = opts.transport ?? "http";
|
|
@@ -10501,13 +10680,13 @@ function toTikTokCampaignConfig(cfg) {
|
|
|
10501
10680
|
__name(toTikTokCampaignConfig, "toTikTokCampaignConfig");
|
|
10502
10681
|
|
|
10503
10682
|
// src/tools/campaigns.ts
|
|
10504
|
-
import { z as
|
|
10505
|
-
var MetaStatusInput =
|
|
10506
|
-
campaignId:
|
|
10683
|
+
import { z as z51 } from "zod";
|
|
10684
|
+
var MetaStatusInput = z51.object({
|
|
10685
|
+
campaignId: z51.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
|
|
10507
10686
|
});
|
|
10508
|
-
var TikTokStatusInput =
|
|
10509
|
-
campaignId:
|
|
10510
|
-
advertiserId:
|
|
10687
|
+
var TikTokStatusInput = z51.object({
|
|
10688
|
+
campaignId: z51.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
|
|
10689
|
+
advertiserId: z51.string().min(1).describe("The same advertiserId used to publish the campaign.")
|
|
10511
10690
|
});
|
|
10512
10691
|
function registerCampaignTools(server, api) {
|
|
10513
10692
|
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 +10783,10 @@ function renderTikTokStatusMarkdown(r) {
|
|
|
10604
10783
|
__name(renderTikTokStatusMarkdown, "renderTikTokStatusMarkdown");
|
|
10605
10784
|
|
|
10606
10785
|
// ../../packages/product-economics/src/product-costing.ts
|
|
10607
|
-
import { z as
|
|
10786
|
+
import { z as z53 } from "zod";
|
|
10608
10787
|
|
|
10609
10788
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
10610
|
-
import { z as
|
|
10789
|
+
import { z as z52 } from "zod";
|
|
10611
10790
|
|
|
10612
10791
|
// ../../packages/product-economics/src/product-costing-shipping-references.json
|
|
10613
10792
|
var product_costing_shipping_references_default = {
|
|
@@ -10671,21 +10850,21 @@ var product_costing_shipping_references_default = {
|
|
|
10671
10850
|
};
|
|
10672
10851
|
|
|
10673
10852
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
10674
|
-
var ShippingReferenceSchema =
|
|
10675
|
-
reference:
|
|
10676
|
-
min:
|
|
10677
|
-
max:
|
|
10678
|
-
updatedAt:
|
|
10679
|
-
confidence:
|
|
10853
|
+
var ShippingReferenceSchema = z52.object({
|
|
10854
|
+
reference: z52.number().positive(),
|
|
10855
|
+
min: z52.number().nonnegative(),
|
|
10856
|
+
max: z52.number().positive(),
|
|
10857
|
+
updatedAt: z52.string().date(),
|
|
10858
|
+
confidence: z52.enum([
|
|
10680
10859
|
"high",
|
|
10681
10860
|
"medium",
|
|
10682
10861
|
"medium-low"
|
|
10683
10862
|
]),
|
|
10684
|
-
sources:
|
|
10863
|
+
sources: z52.array(z52.string().url().startsWith("https://")).min(1).max(3)
|
|
10685
10864
|
}).superRefine(({ reference, min, max }, context) => {
|
|
10686
10865
|
if (min > reference || reference > max) {
|
|
10687
10866
|
context.addIssue({
|
|
10688
|
-
code:
|
|
10867
|
+
code: z52.ZodIssueCode.custom,
|
|
10689
10868
|
message: "shipping_reference_outside_range",
|
|
10690
10869
|
path: [
|
|
10691
10870
|
"reference"
|
|
@@ -10693,7 +10872,7 @@ var ShippingReferenceSchema = z50.object({
|
|
|
10693
10872
|
});
|
|
10694
10873
|
}
|
|
10695
10874
|
});
|
|
10696
|
-
var SHIPPING_REFERENCES =
|
|
10875
|
+
var SHIPPING_REFERENCES = z52.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
|
|
10697
10876
|
var PRODUCT_COSTING_COUNTRY_CODES = Object.freeze(Object.keys(SHIPPING_REFERENCES));
|
|
10698
10877
|
var SPANISH_LOCALE_COUNTRIES = /* @__PURE__ */ new Set([
|
|
10699
10878
|
"MX",
|
|
@@ -10875,18 +11054,18 @@ function getProductCostingCountryName(country) {
|
|
|
10875
11054
|
__name(getProductCostingCountryName, "getProductCostingCountryName");
|
|
10876
11055
|
|
|
10877
11056
|
// ../../packages/product-economics/src/product-costing.ts
|
|
10878
|
-
var ProductCostingInputSchema =
|
|
10879
|
-
country:
|
|
10880
|
-
supplierCost:
|
|
10881
|
-
shippingCost:
|
|
10882
|
-
returnRate:
|
|
10883
|
-
cancellationRate:
|
|
10884
|
-
adsManagerCpa:
|
|
10885
|
-
desiredMargin:
|
|
11057
|
+
var ProductCostingInputSchema = z53.object({
|
|
11058
|
+
country: z53.string().refine(isProductCostingCountryCode, "unsupported_country"),
|
|
11059
|
+
supplierCost: z53.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
|
|
11060
|
+
shippingCost: z53.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
|
|
11061
|
+
returnRate: z53.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
11062
|
+
cancellationRate: z53.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
11063
|
+
adsManagerCpa: z53.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
|
|
11064
|
+
desiredMargin: z53.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
|
|
10886
11065
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
10887
11066
|
if (returnRate + cancellationRate >= 1) {
|
|
10888
11067
|
context.addIssue({
|
|
10889
|
-
code:
|
|
11068
|
+
code: z53.ZodIssueCode.custom,
|
|
10890
11069
|
message: "delivery_rate_positive",
|
|
10891
11070
|
path: [
|
|
10892
11071
|
"cancellationRate"
|
|
@@ -10953,14 +11132,14 @@ function calculateProductCosting(rawInput) {
|
|
|
10953
11132
|
__name(calculateProductCosting, "calculateProductCosting");
|
|
10954
11133
|
|
|
10955
11134
|
// 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:
|
|
11135
|
+
import { z as z54 } from "zod";
|
|
11136
|
+
var optionalAmount = /* @__PURE__ */ __name((description) => z54.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
|
|
11137
|
+
var optionalPercent = /* @__PURE__ */ __name((description) => z54.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
|
|
11138
|
+
var ProductCostingToolInput = z54.object({
|
|
11139
|
+
country: z54.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
|
|
10961
11140
|
message: "Pa\xEDs no soportado por ProductMaker."
|
|
10962
11141
|
}).optional().describe("C\xF3digo ISO-2 de cualquiera de los pa\xEDses disponibles en ProductMaker. Omite para Colombia."),
|
|
10963
|
-
supplierCost:
|
|
11142
|
+
supplierCost: z54.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
11143
|
supplierCostCop: optionalAmount("Alias anterior para costo del proveedor en Colombia. Prefiere supplierCost."),
|
|
10965
11144
|
shippingCost: optionalAmount("Costo de un env\xEDo en la moneda del pa\xEDs. Omite para usar el valor gu\xEDa vigente."),
|
|
10966
11145
|
shippingCostCop: optionalAmount("Alias anterior para flete en Colombia. Prefiere shippingCost."),
|
|
@@ -11092,7 +11271,7 @@ function formatPercent(value) {
|
|
|
11092
11271
|
__name(formatPercent, "formatPercent");
|
|
11093
11272
|
|
|
11094
11273
|
// src/tools/product-research.ts
|
|
11095
|
-
import { z as
|
|
11274
|
+
import { z as z55 } from "zod";
|
|
11096
11275
|
|
|
11097
11276
|
// src/tools/product-research-idempotency.ts
|
|
11098
11277
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -11208,17 +11387,17 @@ var TERMINAL2 = /* @__PURE__ */ new Set([
|
|
|
11208
11387
|
]);
|
|
11209
11388
|
var POLL_INTERVAL_MS = 5e3;
|
|
11210
11389
|
var MAX_WAIT_SECONDS = 120;
|
|
11211
|
-
var FindInput =
|
|
11390
|
+
var FindInput = z55.object({
|
|
11212
11391
|
targetCountry: ResearchTargetCountryEnum.describe("Pa\xEDs objetivo. S\xF3lo CO, MX, PE, EC y CL tienen cobertura de investigaci\xF3n."),
|
|
11213
11392
|
categoryKey: ResearchCategoryEnum.default("auto").describe('Categor\xEDa a investigar. Usa "auto" para que la IA elija seg\xFAn el mercado.'),
|
|
11214
|
-
confirmed:
|
|
11215
|
-
waitSeconds:
|
|
11393
|
+
confirmed: z55.boolean().describe(`Ponlo en true SOLO despu\xE9s de que el usuario haya aceptado el cargo de ${PRICE_CREDITS} cr\xE9ditos. No lo asumas.`),
|
|
11394
|
+
waitSeconds: z55.number().int().min(0).max(MAX_WAIT_SECONDS).optional().describe("Segundos a esperar el reporte antes de devolver el runId. M\xE1ximo 120.")
|
|
11216
11395
|
});
|
|
11217
|
-
var GetInput =
|
|
11218
|
-
runId:
|
|
11396
|
+
var GetInput = z55.object({
|
|
11397
|
+
runId: z55.string().uuid().describe("ID del run devuelto por find_winning_products.")
|
|
11219
11398
|
});
|
|
11220
|
-
var ListInput =
|
|
11221
|
-
limit:
|
|
11399
|
+
var ListInput = z55.object({
|
|
11400
|
+
limit: z55.number().int().min(1).max(20).default(10)
|
|
11222
11401
|
});
|
|
11223
11402
|
function textResult(text) {
|
|
11224
11403
|
return {
|
|
@@ -11310,14 +11489,14 @@ _Esta llamada no cobr\xF3 cr\xE9ditos: se reutiliz\xF3 una investigaci\xF3n reci
|
|
|
11310
11489
|
__name(registerProductResearchTools, "registerProductResearchTools");
|
|
11311
11490
|
|
|
11312
11491
|
// src/tools/dropshipping-projects.ts
|
|
11313
|
-
import { z as
|
|
11314
|
-
var ProjectId =
|
|
11315
|
-
var DropshippingProjectStatus =
|
|
11492
|
+
import { z as z56 } from "zod";
|
|
11493
|
+
var ProjectId = z56.string().uuid();
|
|
11494
|
+
var DropshippingProjectStatus = z56.enum([
|
|
11316
11495
|
"active",
|
|
11317
11496
|
"completed",
|
|
11318
11497
|
"archived"
|
|
11319
11498
|
]);
|
|
11320
|
-
var DropshippingProjectPhase =
|
|
11499
|
+
var DropshippingProjectPhase = z56.enum([
|
|
11321
11500
|
"research",
|
|
11322
11501
|
"costing",
|
|
11323
11502
|
"media",
|
|
@@ -11326,21 +11505,21 @@ var DropshippingProjectPhase = z54.enum([
|
|
|
11326
11505
|
"advertising",
|
|
11327
11506
|
"complete"
|
|
11328
11507
|
]);
|
|
11329
|
-
var DropshippingRewardMilestone =
|
|
11508
|
+
var DropshippingRewardMilestone = z56.enum([
|
|
11330
11509
|
"product_selected",
|
|
11331
11510
|
"costing_saved",
|
|
11332
11511
|
"product_media_ready",
|
|
11333
11512
|
"creatives_completed",
|
|
11334
11513
|
"campaign_created"
|
|
11335
11514
|
]);
|
|
11336
|
-
var TargetCountry =
|
|
11515
|
+
var TargetCountry = z56.enum([
|
|
11337
11516
|
"CO",
|
|
11338
11517
|
"MX",
|
|
11339
11518
|
"PE",
|
|
11340
11519
|
"EC",
|
|
11341
11520
|
"CL"
|
|
11342
11521
|
]);
|
|
11343
|
-
var ResearchCategory =
|
|
11522
|
+
var ResearchCategory = z56.enum([
|
|
11344
11523
|
"auto",
|
|
11345
11524
|
"automotive",
|
|
11346
11525
|
"kitchen",
|
|
@@ -11355,7 +11534,7 @@ var ResearchCategory = z54.enum([
|
|
|
11355
11534
|
"garden",
|
|
11356
11535
|
"cleaning"
|
|
11357
11536
|
]);
|
|
11358
|
-
var HttpUrl3 =
|
|
11537
|
+
var HttpUrl3 = z56.string().url().max(2e3).refine((value) => {
|
|
11359
11538
|
try {
|
|
11360
11539
|
return [
|
|
11361
11540
|
"http:",
|
|
@@ -11365,46 +11544,46 @@ var HttpUrl3 = z54.string().url().max(2e3).refine((value) => {
|
|
|
11365
11544
|
return false;
|
|
11366
11545
|
}
|
|
11367
11546
|
}, "URL must use HTTP(S)");
|
|
11368
|
-
var ResearchCandidateOrigin =
|
|
11369
|
-
type:
|
|
11547
|
+
var ResearchCandidateOrigin = z56.object({
|
|
11548
|
+
type: z56.literal("research_candidate"),
|
|
11370
11549
|
runId: ProjectId,
|
|
11371
|
-
candidateSourceId:
|
|
11550
|
+
candidateSourceId: z56.string().min(1).max(160)
|
|
11372
11551
|
});
|
|
11373
|
-
var TaskOrigin =
|
|
11374
|
-
type:
|
|
11552
|
+
var TaskOrigin = z56.object({
|
|
11553
|
+
type: z56.literal("task"),
|
|
11375
11554
|
taskId: ProjectId
|
|
11376
11555
|
});
|
|
11377
|
-
var ManualOrigin =
|
|
11378
|
-
type:
|
|
11556
|
+
var ManualOrigin = z56.object({
|
|
11557
|
+
type: z56.literal("manual"),
|
|
11379
11558
|
clientRequestId: ProjectId,
|
|
11380
|
-
country:
|
|
11559
|
+
country: z56.string().min(2).max(3).optional()
|
|
11381
11560
|
});
|
|
11382
|
-
var StartProjectInput =
|
|
11383
|
-
origin:
|
|
11561
|
+
var StartProjectInput = z56.object({
|
|
11562
|
+
origin: z56.discriminatedUnion("type", [
|
|
11384
11563
|
ResearchCandidateOrigin,
|
|
11385
11564
|
TaskOrigin,
|
|
11386
11565
|
ManualOrigin
|
|
11387
11566
|
]),
|
|
11388
|
-
productName:
|
|
11567
|
+
productName: z56.string().trim().min(1).max(240).optional(),
|
|
11389
11568
|
productUrl: HttpUrl3.optional(),
|
|
11390
|
-
country:
|
|
11391
|
-
languageCode:
|
|
11569
|
+
country: z56.string().min(2).max(3).optional(),
|
|
11570
|
+
languageCode: z56.string().min(2).max(16).optional()
|
|
11392
11571
|
}).strict();
|
|
11393
|
-
var CostProjectInput =
|
|
11572
|
+
var CostProjectInput = z56.object({
|
|
11394
11573
|
projectId: ProjectId,
|
|
11395
|
-
version:
|
|
11396
|
-
country:
|
|
11397
|
-
supplierCost:
|
|
11398
|
-
shippingCost:
|
|
11399
|
-
returnRate:
|
|
11400
|
-
cancellationRate:
|
|
11401
|
-
adsManagerCpa:
|
|
11402
|
-
desiredMargin:
|
|
11403
|
-
});
|
|
11404
|
-
var GuideInput =
|
|
11574
|
+
version: z56.number().int().positive(),
|
|
11575
|
+
country: z56.string().trim().min(2).max(3),
|
|
11576
|
+
supplierCost: z56.number().finite().positive(),
|
|
11577
|
+
shippingCost: z56.number().finite().nonnegative(),
|
|
11578
|
+
returnRate: z56.number().finite().min(0).lt(1),
|
|
11579
|
+
cancellationRate: z56.number().finite().min(0).lt(1),
|
|
11580
|
+
adsManagerCpa: z56.number().finite().nonnegative(),
|
|
11581
|
+
desiredMargin: z56.number().finite().min(0).lt(1)
|
|
11582
|
+
});
|
|
11583
|
+
var GuideInput = z56.object({
|
|
11405
11584
|
projectId: ProjectId.optional()
|
|
11406
11585
|
}).strict();
|
|
11407
|
-
var DropshippingProjectNextAction =
|
|
11586
|
+
var DropshippingProjectNextAction = z56.discriminatedUnion("kind", [
|
|
11408
11587
|
"cost_product",
|
|
11409
11588
|
"provide_product_media",
|
|
11410
11589
|
"generate_creatives",
|
|
@@ -11418,78 +11597,78 @@ var DropshippingProjectNextAction = z54.discriminatedUnion("kind", [
|
|
|
11418
11597
|
"create_meta_campaign",
|
|
11419
11598
|
"repair_meta_campaign",
|
|
11420
11599
|
"review_paused_campaign"
|
|
11421
|
-
].map((kind) =>
|
|
11422
|
-
kind:
|
|
11423
|
-
href:
|
|
11600
|
+
].map((kind) => z56.object({
|
|
11601
|
+
kind: z56.literal(kind),
|
|
11602
|
+
href: z56.string().min(1)
|
|
11424
11603
|
})));
|
|
11425
|
-
var CostingInputSchema =
|
|
11426
|
-
country:
|
|
11427
|
-
supplierCost:
|
|
11428
|
-
shippingCost:
|
|
11429
|
-
returnRate:
|
|
11430
|
-
cancellationRate:
|
|
11431
|
-
adsManagerCpa:
|
|
11432
|
-
desiredMargin:
|
|
11604
|
+
var CostingInputSchema = z56.object({
|
|
11605
|
+
country: z56.string().min(2).max(3),
|
|
11606
|
+
supplierCost: z56.number().finite().positive(),
|
|
11607
|
+
shippingCost: z56.number().finite().nonnegative(),
|
|
11608
|
+
returnRate: z56.number().finite().min(0).lt(1),
|
|
11609
|
+
cancellationRate: z56.number().finite().min(0).lt(1),
|
|
11610
|
+
adsManagerCpa: z56.number().finite().nonnegative(),
|
|
11611
|
+
desiredMargin: z56.number().finite().min(0).lt(1)
|
|
11433
11612
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
11434
11613
|
if (returnRate + cancellationRate >= 1) context.addIssue({
|
|
11435
|
-
code:
|
|
11614
|
+
code: z56.ZodIssueCode.custom,
|
|
11436
11615
|
path: [
|
|
11437
11616
|
"cancellationRate"
|
|
11438
11617
|
]
|
|
11439
11618
|
});
|
|
11440
11619
|
});
|
|
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:
|
|
11620
|
+
var CostingResultSchema = z56.object({
|
|
11621
|
+
country: z56.string().min(2).max(3),
|
|
11622
|
+
deliveryRate: z56.number().finite(),
|
|
11623
|
+
adjustedShippingCost: z56.number().finite(),
|
|
11624
|
+
adjustedCpa: z56.number().finite(),
|
|
11625
|
+
totalCost: z56.number().finite(),
|
|
11626
|
+
suggestedPrice: z56.number().finite(),
|
|
11627
|
+
baseCommercialPrice: z56.number().finite(),
|
|
11628
|
+
minimumSafePrice: z56.number().finite(),
|
|
11629
|
+
commercialPrice: z56.number().finite(),
|
|
11630
|
+
comparisonPrice: z56.number().finite(),
|
|
11631
|
+
estimatedProfit: z56.number().finite(),
|
|
11632
|
+
actualMargin: z56.number().finite(),
|
|
11633
|
+
breakEvenAdsManagerCpa: z56.number().finite(),
|
|
11634
|
+
wasPriceAutoAdjusted: z56.boolean(),
|
|
11635
|
+
profitabilityStatus: z56.literal("healthy")
|
|
11636
|
+
});
|
|
11637
|
+
var RewardProgressSchema = z56.object({
|
|
11638
|
+
campaignKey: z56.literal("dropshipping-launch-v1"),
|
|
11639
|
+
totalCredits: z56.number().int().positive(),
|
|
11640
|
+
earnedCredits: z56.number().int().nonnegative(),
|
|
11641
|
+
milestones: z56.array(z56.object({
|
|
11463
11642
|
milestone: DropshippingRewardMilestone,
|
|
11464
|
-
credits:
|
|
11465
|
-
available:
|
|
11466
|
-
earned:
|
|
11643
|
+
credits: z56.number().int().positive(),
|
|
11644
|
+
available: z56.boolean().optional(),
|
|
11645
|
+
earned: z56.boolean(),
|
|
11467
11646
|
grantId: ProjectId.nullable(),
|
|
11468
|
-
grantedAt:
|
|
11647
|
+
grantedAt: z56.string().datetime().nullable()
|
|
11469
11648
|
})).length(DropshippingRewardMilestone.options.length)
|
|
11470
11649
|
});
|
|
11471
|
-
var ProjectResponseSchema =
|
|
11650
|
+
var ProjectResponseSchema = z56.object({
|
|
11472
11651
|
id: ProjectId,
|
|
11473
11652
|
taskId: ProjectId.nullable(),
|
|
11474
11653
|
status: DropshippingProjectStatus,
|
|
11475
|
-
originKey:
|
|
11654
|
+
originKey: z56.string().min(1),
|
|
11476
11655
|
researchRunId: ProjectId.nullable(),
|
|
11477
|
-
candidateSourceId:
|
|
11478
|
-
productName:
|
|
11656
|
+
candidateSourceId: z56.string().nullable(),
|
|
11657
|
+
productName: z56.string().nullable(),
|
|
11479
11658
|
productUrl: HttpUrl3.nullable(),
|
|
11480
11659
|
referenceImageUrl: HttpUrl3.nullable(),
|
|
11481
|
-
targetCountry:
|
|
11482
|
-
languageCode:
|
|
11483
|
-
hasReferenceImage:
|
|
11660
|
+
targetCountry: z56.string().nullable(),
|
|
11661
|
+
languageCode: z56.string().nullable(),
|
|
11662
|
+
hasReferenceImage: z56.boolean(),
|
|
11484
11663
|
costingInput: CostingInputSchema.nullable(),
|
|
11485
11664
|
costingResult: CostingResultSchema.nullable(),
|
|
11486
|
-
costingVersion:
|
|
11487
|
-
version:
|
|
11665
|
+
costingVersion: z56.number().int().positive().nullable(),
|
|
11666
|
+
version: z56.number().int().positive(),
|
|
11488
11667
|
phase: DropshippingProjectPhase,
|
|
11489
11668
|
nextAction: DropshippingProjectNextAction.nullable(),
|
|
11490
11669
|
rewards: RewardProgressSchema,
|
|
11491
|
-
createdAt:
|
|
11492
|
-
updatedAt:
|
|
11670
|
+
createdAt: z56.string().datetime(),
|
|
11671
|
+
updatedAt: z56.string().datetime()
|
|
11493
11672
|
}).passthrough();
|
|
11494
11673
|
function projectText(raw) {
|
|
11495
11674
|
const project = ProjectResponseSchema.parse(raw);
|
|
@@ -11587,6 +11766,7 @@ function createServer(opts) {
|
|
|
11587
11766
|
registerCreativeVariantTools(server, api);
|
|
11588
11767
|
registerScriptReviewTools(server, api);
|
|
11589
11768
|
registerConnectionTools(server, api);
|
|
11769
|
+
registerActorTools(server, api);
|
|
11590
11770
|
registerPublishTools(server, api, {
|
|
11591
11771
|
transport
|
|
11592
11772
|
});
|