@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/http.js
CHANGED
|
@@ -153,10 +153,10 @@ var ApiClient = class {
|
|
|
153
153
|
};
|
|
154
154
|
|
|
155
155
|
// src/index.ts
|
|
156
|
-
var MCP_VERSION = "2.
|
|
156
|
+
var MCP_VERSION = "2.4.0";
|
|
157
157
|
|
|
158
158
|
// src/tools/tasks.ts
|
|
159
|
-
import { z as
|
|
159
|
+
import { z as z44 } from "zod";
|
|
160
160
|
|
|
161
161
|
// src/resolve-image.ts
|
|
162
162
|
import { lookup as dnsLookup } from "dns/promises";
|
|
@@ -1045,6 +1045,8 @@ var Angle = z6.object({
|
|
|
1045
1045
|
buyerPersona: z6.string().min(1).max(360),
|
|
1046
1046
|
/** Character description for video (role + appearance + attitude). Drives voice selection and Frame0Spec. */
|
|
1047
1047
|
actor: z6.string().min(1).max(360).optional(),
|
|
1048
|
+
/** Saved custom actor (Actor.id) resolved for this angle. Its approved photo anchors the identity. */
|
|
1049
|
+
actorId: z6.string().optional(),
|
|
1048
1050
|
// Binary only: voice catalog has no non-binary voices.
|
|
1049
1051
|
// See packages/angles/src/index.ts + getAngles coercion in angle.repository.ts.
|
|
1050
1052
|
gender: z6.enum([
|
|
@@ -1635,6 +1637,8 @@ var IngestOptions = z14.object({
|
|
|
1635
1637
|
generateAdCopy: z14.boolean().default(true),
|
|
1636
1638
|
generateAiMusic: z14.boolean().optional(),
|
|
1637
1639
|
voiceId: VoiceId.optional(),
|
|
1640
|
+
/** Global saved custom actor (Actor.id). Default for all angles. */
|
|
1641
|
+
actorId: z14.string().optional(),
|
|
1638
1642
|
isCombo: z14.boolean().default(false),
|
|
1639
1643
|
productContext: z14.string().transform((value) => value.trim()).pipe(z14.string().max(PRODUCT_CONTEXT_MAX_CHARS)).optional(),
|
|
1640
1644
|
creativeDirection: z14.string().transform((value) => value.trim()).pipe(z14.string().max(500)).optional(),
|
|
@@ -1649,7 +1653,7 @@ var IngestOptions = z14.object({
|
|
|
1649
1653
|
"landing",
|
|
1650
1654
|
"video",
|
|
1651
1655
|
"image"
|
|
1652
|
-
])).min(
|
|
1656
|
+
])).min(1).max(3).optional(),
|
|
1653
1657
|
// Landing page output: 'classic' = image-based (default), 'code' = structured
|
|
1654
1658
|
// code landing rendered by the theme app extension. Absent ⇒ classic.
|
|
1655
1659
|
landingMode: LandingModeSchema.optional(),
|
|
@@ -1700,7 +1704,13 @@ var VideoOptionsOverride = z14.object({
|
|
|
1700
1704
|
videoResolution: VideoResolution.optional(),
|
|
1701
1705
|
voiceId: VoiceId.optional(),
|
|
1702
1706
|
/** Actor/character description (role + appearance + attitude). Drives voice and video character. */
|
|
1703
|
-
actor: z14.string().max(360).optional()
|
|
1707
|
+
actor: z14.string().max(360).optional(),
|
|
1708
|
+
/** Saved custom actor (Actor.id). Its approved photo anchors the identity.
|
|
1709
|
+
* `null` is an explicit REMOVAL (the user cleared the picker) and must be
|
|
1710
|
+
* sent as such: an omitted key means "not mentioned", which every writer
|
|
1711
|
+
* reads as "leave the stored selection alone". Same contract as
|
|
1712
|
+
* service-angles.controller.ts. */
|
|
1713
|
+
actorId: z14.string().nullable().optional()
|
|
1704
1714
|
});
|
|
1705
1715
|
var AngleVideoOptions = VideoOptionsOverride.extend({
|
|
1706
1716
|
angleIndex: z14.number().int().nonnegative(),
|
|
@@ -1711,7 +1721,11 @@ var AngleVideoOptions = VideoOptionsOverride.extend({
|
|
|
1711
1721
|
/** Per-angle buyer persona override (describes the target audience) */
|
|
1712
1722
|
buyerPersona: z14.string().max(360).optional(),
|
|
1713
1723
|
/** Per-angle actor/character description (role + appearance + attitude). Drives voice and video character. */
|
|
1714
|
-
actor: z14.string().max(360).optional()
|
|
1724
|
+
actor: z14.string().max(360).optional(),
|
|
1725
|
+
/** Per-angle saved custom actor (Actor.id). Overrides the global actorId.
|
|
1726
|
+
* `null` = the user removed the personaje on this angle; it must NOT fall
|
|
1727
|
+
* back to the global default. Omitted = not mentioned. */
|
|
1728
|
+
actorId: z14.string().nullable().optional()
|
|
1715
1729
|
});
|
|
1716
1730
|
var RapidScriptBlueprintShotForApproval = z14.object({
|
|
1717
1731
|
id: z14.string().min(1),
|
|
@@ -1774,6 +1788,8 @@ var IngestResumePayload = z14.object({
|
|
|
1774
1788
|
voiceId: VoiceId.optional(),
|
|
1775
1789
|
/** Global actor/character description (role + appearance + attitude). Default for all angles. */
|
|
1776
1790
|
actor: z14.string().max(360).optional(),
|
|
1791
|
+
/** Global saved custom actor (Actor.id). Default for all angles. */
|
|
1792
|
+
actorId: z14.string().optional(),
|
|
1777
1793
|
/** Custom angle override for landing/creative (uses this instead of first angle) */
|
|
1778
1794
|
customAngleOverride: z14.string().min(1).max(480).optional(),
|
|
1779
1795
|
/** Pain points to emphasize in landings, creatives, and videos */
|
|
@@ -2472,6 +2488,8 @@ var RefineAngleResponse = z20.object({
|
|
|
2472
2488
|
painPoints: z20.array(z20.string()).optional(),
|
|
2473
2489
|
benefits: z20.array(z20.string()).optional(),
|
|
2474
2490
|
actor: z20.string().optional(),
|
|
2491
|
+
/** Saved custom actor (Actor.id) carried through the refine round-trip. */
|
|
2492
|
+
actorId: z20.string().optional(),
|
|
2475
2493
|
buyerPersona: z20.string().optional()
|
|
2476
2494
|
});
|
|
2477
2495
|
|
|
@@ -2851,7 +2869,9 @@ var PendingUserActionSchema = z23.discriminatedUnion("kind", [
|
|
|
2851
2869
|
id: z23.string().uuid(),
|
|
2852
2870
|
content: z23.string(),
|
|
2853
2871
|
creativeDirection: z23.string(),
|
|
2854
|
-
actor: z23.string()
|
|
2872
|
+
actor: z23.string(),
|
|
2873
|
+
/** Saved custom actor (Actor.id) backing this angle, when one was chosen. */
|
|
2874
|
+
actorId: z23.string().optional()
|
|
2855
2875
|
})).min(1).max(3)
|
|
2856
2876
|
}),
|
|
2857
2877
|
z23.object({
|
|
@@ -3004,7 +3024,8 @@ var CostEstimationType = z24.enum([
|
|
|
3004
3024
|
"landing-regenerate",
|
|
3005
3025
|
"landing-creative",
|
|
3006
3026
|
"campaign-copy",
|
|
3007
|
-
"service-creative"
|
|
3027
|
+
"service-creative",
|
|
3028
|
+
"actor-grid"
|
|
3008
3029
|
]);
|
|
3009
3030
|
var IngestEstimationOptions = z24.object({
|
|
3010
3031
|
/** Number of product references that the extract stage will clean. */
|
|
@@ -3105,6 +3126,8 @@ var VideoCreativeEstimationOptions = z24.object({
|
|
|
3105
3126
|
requiresAngles: z24.boolean().optional(),
|
|
3106
3127
|
/** When false, omit the Meta/Whisper competitor-scan ceiling from the estimate. */
|
|
3107
3128
|
competitorScanEnabled: z24.boolean().optional(),
|
|
3129
|
+
/** Quote the competitor scan even when product extraction is skipped (direct video auto path). */
|
|
3130
|
+
requiresCompetitorScan: z24.boolean().optional(),
|
|
3108
3131
|
generateAiMusic: z24.boolean().optional(),
|
|
3109
3132
|
/** Free mode burns one Whisper pass over the final clip when enabled. */
|
|
3110
3133
|
addSubtitles: z24.boolean().optional()
|
|
@@ -3173,6 +3196,7 @@ var ServiceCreativeEstimationOptions = z24.object({
|
|
|
3173
3196
|
mediaAssetCount: z24.number().int().min(0).max(8).optional(),
|
|
3174
3197
|
uploadedVideoDurationSeconds: z24.number().min(0).max(240).optional()
|
|
3175
3198
|
});
|
|
3199
|
+
var ActorGridEstimationOptions = z24.object({}).strict();
|
|
3176
3200
|
var CostEstimationRequest = z24.discriminatedUnion("type", [
|
|
3177
3201
|
z24.object({
|
|
3178
3202
|
type: z24.literal("ingest"),
|
|
@@ -3209,6 +3233,10 @@ var CostEstimationRequest = z24.discriminatedUnion("type", [
|
|
|
3209
3233
|
z24.object({
|
|
3210
3234
|
type: z24.literal("service-creative"),
|
|
3211
3235
|
options: ServiceCreativeEstimationOptions
|
|
3236
|
+
}),
|
|
3237
|
+
z24.object({
|
|
3238
|
+
type: z24.literal("actor-grid"),
|
|
3239
|
+
options: ActorGridEstimationOptions
|
|
3212
3240
|
})
|
|
3213
3241
|
]);
|
|
3214
3242
|
var CostEstimationResponse = z24.object({
|
|
@@ -3699,10 +3727,15 @@ var PresignUploadRequest = z31.object({
|
|
|
3699
3727
|
fileName: z31.string().min(1).max(255),
|
|
3700
3728
|
/** File size in bytes (validated against MAX_FILE_SIZE_BYTES) */
|
|
3701
3729
|
fileSize: z31.number().int().positive().max(CAMPAIGN_LIMITS.MAX_FILE_SIZE_BYTES),
|
|
3702
|
-
/**
|
|
3730
|
+
/**
|
|
3731
|
+
* Service uploads receive a user-bound asset ticket; ads keep the legacy path.
|
|
3732
|
+
* `actor` uploads the identity photo of a custom actor and is stored under
|
|
3733
|
+
* `actors/<userId>/…` so `AssetAccessGuard` can resolve its owner.
|
|
3734
|
+
*/
|
|
3703
3735
|
purpose: z31.enum([
|
|
3704
3736
|
"ads",
|
|
3705
|
-
"service"
|
|
3737
|
+
"service",
|
|
3738
|
+
"actor"
|
|
3706
3739
|
]).optional()
|
|
3707
3740
|
});
|
|
3708
3741
|
var PresignUploadResponse = z31.object({
|
|
@@ -4695,6 +4728,13 @@ var DEFAULT_PRICING = {
|
|
|
4695
4728
|
ourCostCents: 3,
|
|
4696
4729
|
userPriceCents: 6
|
|
4697
4730
|
},
|
|
4731
|
+
// Custom actor identity photos (gpt-image-2 @ 1K, 4 candidates per attempt).
|
|
4732
|
+
// Recorded once per PRODUCED candidate. Without this row billingService.record
|
|
4733
|
+
// logs "No pricing rule found; skipping billing record" and the whole grid is free.
|
|
4734
|
+
"kie:image-generation:actor": {
|
|
4735
|
+
ourCostCents: 3,
|
|
4736
|
+
userPriceCents: 6
|
|
4737
|
+
},
|
|
4698
4738
|
"kie:image-generation:seedream-5-lite-text-to-image": {
|
|
4699
4739
|
ourCostCents: 3,
|
|
4700
4740
|
userPriceCents: 6
|
|
@@ -5631,6 +5671,32 @@ var DEFAULT_PRICING = {
|
|
|
5631
5671
|
ourCostCents: 1.5,
|
|
5632
5672
|
userPriceCents: 3
|
|
5633
5673
|
},
|
|
5674
|
+
// Frame0 per-person anatomy gate. Priced PER PANEL, like the inventory above and for the same
|
|
5675
|
+
// reason: it is one vision call per rendered panel, so quoting it per VIDEO would under-quote
|
|
5676
|
+
// by the panel count (~10x). Same single-image, short-answer shape as the inventory, so it
|
|
5677
|
+
// carries the same rate until measured otherwise.
|
|
5678
|
+
"openrouter:rapid-panel-anatomy-gate": {
|
|
5679
|
+
ourCostCents: 1.5,
|
|
5680
|
+
userPriceCents: 3
|
|
5681
|
+
},
|
|
5682
|
+
// Plan hand-budget audit: one TEXT call per shot (no image), so materially cheaper than the
|
|
5683
|
+
// vision passes above. Billed at the call site, therefore it MUST be quoted — an unquoted paid
|
|
5684
|
+
// call is the failure this repo already has on record.
|
|
5685
|
+
"openrouter:plan-hand-budget-audit": {
|
|
5686
|
+
ourCostCents: 0.3,
|
|
5687
|
+
userPriceCents: 0.6
|
|
5688
|
+
},
|
|
5689
|
+
// Hand-budget REPAIR: the rewrite that fires only on a shot whose brief demands more hand jobs
|
|
5690
|
+
// than its cast has hands. One TEXT call, same shape as the audit above but with the brief on
|
|
5691
|
+
// both sides (prompt in, prose out), so twice the rate rather than the audit's.
|
|
5692
|
+
//
|
|
5693
|
+
// Kept on its OWN key rather than folded into the audit for the reason the frame-inventory delta
|
|
5694
|
+
// is: the audit runs on every shot and the repair on a minority, so one blended rate would price
|
|
5695
|
+
// both wrong and neither line would be auditable against the ledger.
|
|
5696
|
+
"openrouter:rapid-hand-budget-repair": {
|
|
5697
|
+
ourCostCents: 0.6,
|
|
5698
|
+
userPriceCents: 1.2
|
|
5699
|
+
},
|
|
5634
5700
|
// Unit = one shot-action flatten, once per video, quoted ONLY when frame inventories are on.
|
|
5635
5701
|
// Carrying ~10 inventories as text raised that call from a 1.7078¢ production median to a
|
|
5636
5702
|
// measured 3.1481¢ / 3.4551¢. This is the difference, kept on its own key so the base rate above
|
|
@@ -7061,6 +7127,7 @@ var MAX_SEGMENT_SECONDS = 3;
|
|
|
7061
7127
|
var VideoEditStatusSchema = z38.enum([
|
|
7062
7128
|
"pending",
|
|
7063
7129
|
"processing",
|
|
7130
|
+
"assembling",
|
|
7064
7131
|
"ready",
|
|
7065
7132
|
"accepted",
|
|
7066
7133
|
"discarded",
|
|
@@ -7088,6 +7155,13 @@ var VideoEditModeSchema = z38.enum([
|
|
|
7088
7155
|
"wan_edit",
|
|
7089
7156
|
"regenerate"
|
|
7090
7157
|
]);
|
|
7158
|
+
var VideoRegenerationEngineSchema = z38.enum([
|
|
7159
|
+
"free_text",
|
|
7160
|
+
"free_image",
|
|
7161
|
+
"single_call",
|
|
7162
|
+
"multi_shot"
|
|
7163
|
+
]);
|
|
7164
|
+
var VideoRegenerationRouteFingerprintSchema = z38.string().trim().min(1).max(128);
|
|
7091
7165
|
var RegenerateEditRequestSchema = z38.object({
|
|
7092
7166
|
angleKey: z38.string().min(1),
|
|
7093
7167
|
comment: z38.string().min(1).max(2e3)
|
|
@@ -7484,7 +7558,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7484
7558
|
{
|
|
7485
7559
|
name: "create_product_task",
|
|
7486
7560
|
confirmation: "credit_consuming",
|
|
7487
|
-
schemaHash: "
|
|
7561
|
+
schemaHash: "50845af40b29f479592ca5e545d9c1af1fe631998802827affccb4641fc90c18"
|
|
7488
7562
|
},
|
|
7489
7563
|
{
|
|
7490
7564
|
name: "create_service_video",
|
|
@@ -7499,7 +7573,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7499
7573
|
{
|
|
7500
7574
|
name: "edit_task_draft",
|
|
7501
7575
|
confirmation: "state_change",
|
|
7502
|
-
schemaHash: "
|
|
7576
|
+
schemaHash: "1c9343bdce51aac36ec6254557c8a904037746523168527f012b4c5b6361e98e"
|
|
7503
7577
|
},
|
|
7504
7578
|
{
|
|
7505
7579
|
name: "estimate_creative_variant",
|
|
@@ -7561,6 +7635,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7561
7635
|
confirmation: "none",
|
|
7562
7636
|
schemaHash: "0e7a1cb5ad5c6681473df5689336edcd7a63b8d68853359982e44bfd98a65db8"
|
|
7563
7637
|
},
|
|
7638
|
+
{
|
|
7639
|
+
name: "list_actors",
|
|
7640
|
+
confirmation: "none",
|
|
7641
|
+
schemaHash: "7a014b717a77aac971ea0ab5c0ff47bb13e6cae7ef9442d9dedb15ce36381b15"
|
|
7642
|
+
},
|
|
7564
7643
|
{
|
|
7565
7644
|
name: "list_meta_ad_accounts",
|
|
7566
7645
|
confirmation: "none",
|
|
@@ -7629,6 +7708,68 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
7629
7708
|
];
|
|
7630
7709
|
var PRODUCTMAKER_MCP_CAPABILITY_NAMES = PRODUCTMAKER_MCP_CAPABILITIES.map((capability) => capability.name);
|
|
7631
7710
|
|
|
7711
|
+
// ../../packages/types/src/actor.ts
|
|
7712
|
+
import { z as z42 } from "zod";
|
|
7713
|
+
var ActorStatusSchema = z42.enum([
|
|
7714
|
+
"draft",
|
|
7715
|
+
"generating",
|
|
7716
|
+
"ready",
|
|
7717
|
+
"failed"
|
|
7718
|
+
]);
|
|
7719
|
+
var ACTOR_NAME_MAX_CHARS = 60;
|
|
7720
|
+
var ACTOR_DESCRIPTION_MAX_CHARS = 600;
|
|
7721
|
+
var ACTOR_CANDIDATE_COUNT = 4;
|
|
7722
|
+
var ActorGenderSchema = z42.enum([
|
|
7723
|
+
"female",
|
|
7724
|
+
"male",
|
|
7725
|
+
"non_binary"
|
|
7726
|
+
]);
|
|
7727
|
+
var ActorSchema = z42.object({
|
|
7728
|
+
id: z42.string(),
|
|
7729
|
+
name: z42.string().min(1).max(ACTOR_NAME_MAX_CHARS),
|
|
7730
|
+
description: z42.string().max(ACTOR_DESCRIPTION_MAX_CHARS),
|
|
7731
|
+
gender: ActorGenderSchema,
|
|
7732
|
+
voiceId: z42.string().nullable().optional(),
|
|
7733
|
+
brandId: z42.string().nullable().optional(),
|
|
7734
|
+
status: ActorStatusSchema,
|
|
7735
|
+
/**
|
|
7736
|
+
* A candidate-grid run is in flight RIGHT NOW.
|
|
7737
|
+
*
|
|
7738
|
+
* Separate from `status` because the two answer different questions:
|
|
7739
|
+
* `status` is the PUBLISHED state (a personaje being regenerated stays
|
|
7740
|
+
* `ready`, so it keeps serving the renders that already reference it), and
|
|
7741
|
+
* this flag is the live one the wizard polls. It is derived from a lock
|
|
7742
|
+
* timestamp with a TTL, so an orphaned run reads as `false` instead of
|
|
7743
|
+
* pinning the personaje forever.
|
|
7744
|
+
*/
|
|
7745
|
+
generating: z42.boolean().optional(),
|
|
7746
|
+
photoUrl: z42.string().url().nullable().optional(),
|
|
7747
|
+
avatarUrl: z42.string().url().nullable().optional(),
|
|
7748
|
+
candidateUrls: z42.array(z42.string().url()).optional(),
|
|
7749
|
+
/** Indices of candidateUrls that failed the anatomy check (Task 6). */
|
|
7750
|
+
flaggedCandidateIndices: z42.array(z42.number().int()).optional()
|
|
7751
|
+
});
|
|
7752
|
+
var CreateActorRequest = z42.object({
|
|
7753
|
+
name: z42.string().min(1).max(ACTOR_NAME_MAX_CHARS),
|
|
7754
|
+
description: z42.string().max(ACTOR_DESCRIPTION_MAX_CHARS).default(""),
|
|
7755
|
+
gender: ActorGenderSchema,
|
|
7756
|
+
voiceId: z42.string().optional(),
|
|
7757
|
+
brandId: z42.string().optional(),
|
|
7758
|
+
sourcePhotoKey: z42.string().optional()
|
|
7759
|
+
}).refine((v) => v.description.trim().length > 0 || !!v.sourcePhotoKey, {
|
|
7760
|
+
message: "Se necesita una descripci\xF3n o una foto"
|
|
7761
|
+
});
|
|
7762
|
+
var UpdateActorRequest = z42.object({
|
|
7763
|
+
name: z42.string().min(1).max(ACTOR_NAME_MAX_CHARS).optional(),
|
|
7764
|
+
description: z42.string().max(ACTOR_DESCRIPTION_MAX_CHARS).optional(),
|
|
7765
|
+
gender: ActorGenderSchema.optional(),
|
|
7766
|
+
voiceId: z42.string().nullable().optional(),
|
|
7767
|
+
brandId: z42.string().nullable().optional()
|
|
7768
|
+
});
|
|
7769
|
+
var ApproveActorRequest = z42.object({
|
|
7770
|
+
candidateIndex: z42.number().int().min(0).max(ACTOR_CANDIDATE_COUNT - 1)
|
|
7771
|
+
});
|
|
7772
|
+
|
|
7632
7773
|
// src/resolve-media.ts
|
|
7633
7774
|
var MAX_MEDIA_BYTES = CAMPAIGN_LIMITS.MAX_FILE_SIZE_BYTES;
|
|
7634
7775
|
var MEDIA_FORMATS = [
|
|
@@ -7868,10 +8009,10 @@ async function resolveMediaRef(ref, opts) {
|
|
|
7868
8009
|
__name(resolveMediaRef, "resolveMediaRef");
|
|
7869
8010
|
|
|
7870
8011
|
// src/tools/shared.ts
|
|
7871
|
-
import { z as
|
|
7872
|
-
var Lang =
|
|
7873
|
-
var Country =
|
|
7874
|
-
var LandingStrategyEnum =
|
|
8012
|
+
import { z as z43 } from "zod";
|
|
8013
|
+
var Lang = z43.string().min(2).max(10);
|
|
8014
|
+
var Country = z43.string().min(2).max(3);
|
|
8015
|
+
var LandingStrategyEnum = z43.enum([
|
|
7875
8016
|
"auto",
|
|
7876
8017
|
"classic",
|
|
7877
8018
|
"pain_solution",
|
|
@@ -7880,12 +8021,12 @@ var LandingStrategyEnum = z42.enum([
|
|
|
7880
8021
|
"offer_value",
|
|
7881
8022
|
"lifestyle_identity"
|
|
7882
8023
|
]);
|
|
7883
|
-
var LandingImageFormatEnum =
|
|
8024
|
+
var LandingImageFormatEnum = z43.enum([
|
|
7884
8025
|
"9:16",
|
|
7885
8026
|
"4:5",
|
|
7886
8027
|
"1:1"
|
|
7887
8028
|
]);
|
|
7888
|
-
var VideoPresetEnum =
|
|
8029
|
+
var VideoPresetEnum = z43.enum([
|
|
7889
8030
|
"auto",
|
|
7890
8031
|
"ugc_dynamic",
|
|
7891
8032
|
"ugc_how_to",
|
|
@@ -7898,7 +8039,7 @@ var VideoPresetEnum = z42.enum([
|
|
|
7898
8039
|
"pixar_problem",
|
|
7899
8040
|
"pixar_sassy"
|
|
7900
8041
|
]);
|
|
7901
|
-
var TaskCreativeVariantVideoPresetEnum =
|
|
8042
|
+
var TaskCreativeVariantVideoPresetEnum = z43.enum([
|
|
7902
8043
|
"auto",
|
|
7903
8044
|
"ugc_dynamic",
|
|
7904
8045
|
"ugc_how_to",
|
|
@@ -7969,7 +8110,7 @@ function resolveVideoPreset(preset) {
|
|
|
7969
8110
|
return VIDEO_PRESET_RESOLUTION[preset] ?? null;
|
|
7970
8111
|
}
|
|
7971
8112
|
__name(resolveVideoPreset, "resolveVideoPreset");
|
|
7972
|
-
var VideoTierEnum =
|
|
8113
|
+
var VideoTierEnum = z43.enum([
|
|
7973
8114
|
"basic"
|
|
7974
8115
|
]);
|
|
7975
8116
|
function resolveVideoTier(_tier) {
|
|
@@ -7990,17 +8131,17 @@ function resolveVideoFormatForDuration(durationSeconds) {
|
|
|
7990
8131
|
return durationSeconds === 30 ? "double_30s" : "single_15s";
|
|
7991
8132
|
}
|
|
7992
8133
|
__name(resolveVideoFormatForDuration, "resolveVideoFormatForDuration");
|
|
7993
|
-
var VideoResolutionEnum =
|
|
8134
|
+
var VideoResolutionEnum = z43.enum([
|
|
7994
8135
|
"480p",
|
|
7995
8136
|
"720p"
|
|
7996
8137
|
]);
|
|
7997
|
-
var VideoStyleEnum =
|
|
8138
|
+
var VideoStyleEnum = z43.enum([
|
|
7998
8139
|
"ugc",
|
|
7999
8140
|
"cartoon_3d",
|
|
8000
8141
|
"product_only",
|
|
8001
8142
|
"ai_dynamic"
|
|
8002
8143
|
]);
|
|
8003
|
-
var NarrativeStyleEnum =
|
|
8144
|
+
var NarrativeStyleEnum = z43.enum([
|
|
8004
8145
|
"review",
|
|
8005
8146
|
"ugc_how_to",
|
|
8006
8147
|
"problem_product_cta",
|
|
@@ -8017,7 +8158,7 @@ var NarrativeStyleEnum = z42.enum([
|
|
|
8017
8158
|
"ugc_skeptic_confession",
|
|
8018
8159
|
"ugc_demo_review"
|
|
8019
8160
|
]);
|
|
8020
|
-
var PublicNarrativeStyleEnum =
|
|
8161
|
+
var PublicNarrativeStyleEnum = z43.enum([
|
|
8021
8162
|
"review",
|
|
8022
8163
|
"ugc_how_to",
|
|
8023
8164
|
"ugc_unboxing_chaos",
|
|
@@ -8029,7 +8170,7 @@ var PublicNarrativeStyleEnum = z42.enum([
|
|
|
8029
8170
|
"animated_problem_pixar",
|
|
8030
8171
|
"sassy_product_pixar"
|
|
8031
8172
|
]);
|
|
8032
|
-
var VideoModelEnum =
|
|
8173
|
+
var VideoModelEnum = z43.enum([
|
|
8033
8174
|
"veo",
|
|
8034
8175
|
"wan",
|
|
8035
8176
|
"bytedance",
|
|
@@ -8037,25 +8178,25 @@ var VideoModelEnum = z42.enum([
|
|
|
8037
8178
|
"grok",
|
|
8038
8179
|
"kling"
|
|
8039
8180
|
]);
|
|
8040
|
-
var ProductReferenceUserChoiceEnum =
|
|
8181
|
+
var ProductReferenceUserChoiceEnum = z43.enum([
|
|
8041
8182
|
"same_product",
|
|
8042
8183
|
"variants",
|
|
8043
8184
|
"different_products",
|
|
8044
8185
|
"combo_distinct"
|
|
8045
8186
|
]);
|
|
8046
|
-
var ProductReferenceCandidateGroupingEnum =
|
|
8187
|
+
var ProductReferenceCandidateGroupingEnum = z43.enum([
|
|
8047
8188
|
"same_product_same_variant",
|
|
8048
8189
|
"same_product_different_variants",
|
|
8049
8190
|
"different_products",
|
|
8050
8191
|
"unclear"
|
|
8051
8192
|
]);
|
|
8052
|
-
var GapDurationChoiceMirror =
|
|
8053
|
-
angleIndex:
|
|
8054
|
-
recommendedSec:
|
|
8055
|
-
|
|
8056
|
-
|
|
8193
|
+
var GapDurationChoiceMirror = z43.object({
|
|
8194
|
+
angleIndex: z43.number().int().nonnegative(),
|
|
8195
|
+
recommendedSec: z43.union([
|
|
8196
|
+
z43.literal(15),
|
|
8197
|
+
z43.literal(30)
|
|
8057
8198
|
]),
|
|
8058
|
-
estimatedDeltaCredits:
|
|
8199
|
+
estimatedDeltaCredits: z43.number().int().nonnegative().optional()
|
|
8059
8200
|
});
|
|
8060
8201
|
function buildSelectedImageUrlsHash(urls) {
|
|
8061
8202
|
const input = [
|
|
@@ -8069,13 +8210,13 @@ function buildSelectedImageUrlsHash(urls) {
|
|
|
8069
8210
|
return `fnv1a:${hash.toString(16).padStart(8, "0")}:${urls.length}`;
|
|
8070
8211
|
}
|
|
8071
8212
|
__name(buildSelectedImageUrlsHash, "buildSelectedImageUrlsHash");
|
|
8072
|
-
var ImageCreativeStyleEnum =
|
|
8213
|
+
var ImageCreativeStyleEnum = z43.enum([
|
|
8073
8214
|
"studio",
|
|
8074
8215
|
"floating",
|
|
8075
8216
|
"ingredients",
|
|
8076
8217
|
"in_use"
|
|
8077
8218
|
]);
|
|
8078
|
-
var ImageCreativeArchetypeEnum =
|
|
8219
|
+
var ImageCreativeArchetypeEnum = z43.enum([
|
|
8079
8220
|
"direct_response_product_ad",
|
|
8080
8221
|
"hero_outcome",
|
|
8081
8222
|
"offer_value_prop",
|
|
@@ -8084,23 +8225,23 @@ var ImageCreativeArchetypeEnum = z42.enum([
|
|
|
8084
8225
|
"problem_solution",
|
|
8085
8226
|
"lifestyle_identity"
|
|
8086
8227
|
]);
|
|
8087
|
-
var ImageCreativeVisualTreatmentEnum =
|
|
8228
|
+
var ImageCreativeVisualTreatmentEnum = z43.enum([
|
|
8088
8229
|
"performance_product_ad",
|
|
8089
8230
|
"premium_dtc_carousel"
|
|
8090
8231
|
]);
|
|
8091
|
-
var ImageCreativeAspectRatioEnum =
|
|
8232
|
+
var ImageCreativeAspectRatioEnum = z43.enum([
|
|
8092
8233
|
"1:1",
|
|
8093
8234
|
"4:5",
|
|
8094
8235
|
"9:16",
|
|
8095
8236
|
"16:9"
|
|
8096
8237
|
]);
|
|
8097
|
-
var ImageRefSchema =
|
|
8098
|
-
url:
|
|
8099
|
-
path:
|
|
8238
|
+
var ImageRefSchema = z43.object({
|
|
8239
|
+
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.'),
|
|
8240
|
+
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`.')
|
|
8100
8241
|
}).refine((v) => Boolean(v.url) !== Boolean(v.path), {
|
|
8101
8242
|
message: "each image entry must have exactly one of `url` or `path`"
|
|
8102
8243
|
});
|
|
8103
|
-
var ImagesInput =
|
|
8244
|
+
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.');
|
|
8104
8245
|
function asText(value) {
|
|
8105
8246
|
return {
|
|
8106
8247
|
content: [
|
|
@@ -8153,14 +8294,14 @@ async function submitCreative(api, opts) {
|
|
|
8153
8294
|
});
|
|
8154
8295
|
}
|
|
8155
8296
|
__name(submitCreative, "submitCreative");
|
|
8156
|
-
var ResearchTargetCountryEnum =
|
|
8297
|
+
var ResearchTargetCountryEnum = z43.enum([
|
|
8157
8298
|
"CO",
|
|
8158
8299
|
"MX",
|
|
8159
8300
|
"PE",
|
|
8160
8301
|
"EC",
|
|
8161
8302
|
"CL"
|
|
8162
8303
|
]);
|
|
8163
|
-
var ResearchCategoryEnum =
|
|
8304
|
+
var ResearchCategoryEnum = z43.enum([
|
|
8164
8305
|
"auto",
|
|
8165
8306
|
"automotive",
|
|
8166
8307
|
"kitchen",
|
|
@@ -8813,52 +8954,53 @@ function renderOutputsMarkdown(outputs) {
|
|
|
8813
8954
|
__name(renderOutputsMarkdown, "renderOutputsMarkdown");
|
|
8814
8955
|
|
|
8815
8956
|
// src/tools/tasks.ts
|
|
8816
|
-
var CreateProductTaskInput =
|
|
8957
|
+
var CreateProductTaskInput = z44.object({
|
|
8817
8958
|
images: ImagesInput.optional().describe("1\u20135 fotos del mismo producto. Omitir \xFAnicamente cuando projectId apunta a un proyecto propio con imagen guardada."),
|
|
8818
|
-
projectId:
|
|
8819
|
-
productUrl:
|
|
8959
|
+
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."),
|
|
8960
|
+
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."),
|
|
8820
8961
|
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.'),
|
|
8821
8962
|
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.'),
|
|
8822
|
-
price:
|
|
8823
|
-
compareAtPrice:
|
|
8824
|
-
offer:
|
|
8825
|
-
paymentMethods:
|
|
8826
|
-
productContext:
|
|
8827
|
-
productLabel:
|
|
8963
|
+
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.'),
|
|
8964
|
+
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".'),
|
|
8965
|
+
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.'),
|
|
8966
|
+
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.'),
|
|
8967
|
+
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."),
|
|
8968
|
+
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.'),
|
|
8828
8969
|
videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla. Default "auto" (la IA escoge tras analizar el producto). Todos los presets funcionan a 15s y 30s, 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.'),
|
|
8829
|
-
durationSeconds:
|
|
8830
|
-
|
|
8831
|
-
|
|
8970
|
+
durationSeconds: z44.union([
|
|
8971
|
+
z44.literal(15),
|
|
8972
|
+
z44.literal(30)
|
|
8832
8973
|
]).default(15).describe("Opcional. Duraci\xF3n objetivo del video: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
|
|
8833
|
-
pauseForAngleReview:
|
|
8974
|
+
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."),
|
|
8834
8975
|
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.'),
|
|
8835
8976
|
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).'),
|
|
8836
|
-
multiAngleVideos:
|
|
8977
|
+
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."),
|
|
8837
8978
|
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.'),
|
|
8838
|
-
outputs:
|
|
8979
|
+
outputs: z44.array(z44.enum([
|
|
8839
8980
|
"landing",
|
|
8840
8981
|
"video",
|
|
8841
8982
|
"image"
|
|
8842
8983
|
])).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.'),
|
|
8843
|
-
competitorScanEnabled:
|
|
8844
|
-
brandId:
|
|
8845
|
-
|
|
8846
|
-
|
|
8847
|
-
|
|
8984
|
+
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."),
|
|
8985
|
+
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."),
|
|
8986
|
+
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."),
|
|
8987
|
+
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.'),
|
|
8988
|
+
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."),
|
|
8989
|
+
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."),
|
|
8848
8990
|
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".'),
|
|
8849
8991
|
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".')
|
|
8850
8992
|
}).strict();
|
|
8851
|
-
var CreateServiceVideoInput =
|
|
8852
|
-
landingUrl:
|
|
8853
|
-
additionalContext:
|
|
8993
|
+
var CreateServiceVideoInput = z44.object({
|
|
8994
|
+
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."),
|
|
8995
|
+
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.'),
|
|
8854
8996
|
language: Lang.describe("Idioma del video: es, en o pt."),
|
|
8855
|
-
voiceId:
|
|
8856
|
-
brandId:
|
|
8857
|
-
reviewMode:
|
|
8997
|
+
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.'),
|
|
8998
|
+
brandId: z44.string().uuid().optional().describe("Marca guardada del usuario. Aplica voz, mensajes, paleta y persona predeterminada."),
|
|
8999
|
+
reviewMode: z44.enum([
|
|
8858
9000
|
"automatic",
|
|
8859
9001
|
"guided"
|
|
8860
9002
|
]).default("automatic").describe("automatic = la IA elige concepto y guion sin pausas; guided = el usuario aprueba concepto y storyboard."),
|
|
8861
|
-
videoPreset:
|
|
9003
|
+
videoPreset: z44.enum([
|
|
8862
9004
|
"auto",
|
|
8863
9005
|
"ugc_dynamic",
|
|
8864
9006
|
"ugc_how_to",
|
|
@@ -8866,34 +9008,35 @@ var CreateServiceVideoInput = z43.object({
|
|
|
8866
9008
|
"wild_card"
|
|
8867
9009
|
]).default("auto")
|
|
8868
9010
|
}).strict();
|
|
8869
|
-
var GetTaskStatusInput =
|
|
8870
|
-
taskId:
|
|
8871
|
-
waitSeconds:
|
|
8872
|
-
});
|
|
8873
|
-
var ListTasksInput =
|
|
8874
|
-
limit:
|
|
8875
|
-
offset:
|
|
8876
|
-
search:
|
|
8877
|
-
});
|
|
8878
|
-
var EditTaskDraftInput =
|
|
8879
|
-
taskId:
|
|
8880
|
-
angleIndex:
|
|
8881
|
-
angle:
|
|
8882
|
-
angle:
|
|
8883
|
-
buyerPersona:
|
|
8884
|
-
actor:
|
|
8885
|
-
|
|
9011
|
+
var GetTaskStatusInput = z44.object({
|
|
9012
|
+
taskId: z44.string().min(1),
|
|
9013
|
+
waitSeconds: z44.number().int().min(0).max(120).optional()
|
|
9014
|
+
});
|
|
9015
|
+
var ListTasksInput = z44.object({
|
|
9016
|
+
limit: z44.number().int().min(1).max(50).optional(),
|
|
9017
|
+
offset: z44.number().int().min(0).optional(),
|
|
9018
|
+
search: z44.string().optional()
|
|
9019
|
+
});
|
|
9020
|
+
var EditTaskDraftInput = z44.object({
|
|
9021
|
+
taskId: z44.string().min(1),
|
|
9022
|
+
angleIndex: z44.number().int().min(0),
|
|
9023
|
+
angle: z44.object({
|
|
9024
|
+
angle: z44.string().min(1).max(480),
|
|
9025
|
+
buyerPersona: z44.string().min(1).max(360),
|
|
9026
|
+
actor: z44.string().min(1).max(360).optional(),
|
|
9027
|
+
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."),
|
|
9028
|
+
gender: z44.enum([
|
|
8886
9029
|
"female",
|
|
8887
9030
|
"male"
|
|
8888
9031
|
]).optional(),
|
|
8889
|
-
scriptNotes:
|
|
9032
|
+
scriptNotes: z44.string().max(2e3).optional()
|
|
8890
9033
|
})
|
|
8891
9034
|
});
|
|
8892
|
-
var PendingActionPayload =
|
|
8893
|
-
|
|
8894
|
-
kind:
|
|
9035
|
+
var PendingActionPayload = z44.discriminatedUnion("kind", [
|
|
9036
|
+
z44.object({
|
|
9037
|
+
kind: z44.literal("product_reference_confirmation"),
|
|
8895
9038
|
userChoice: ProductReferenceUserChoiceEnum,
|
|
8896
|
-
confirmedProductNames:
|
|
9039
|
+
confirmedProductNames: z44.array(z44.string().min(1).max(120)).min(2).max(8).optional(),
|
|
8897
9040
|
// SCOPED multi-scene opt-in — the ONLY way STANDARD ("Multiescena") is
|
|
8898
9041
|
// reachable via MCP. Valid ONLY together with userChoice='variants' (same
|
|
8899
9042
|
// product in several colors): renders one shot per color and stitches them,
|
|
@@ -8904,50 +9047,50 @@ var PendingActionPayload = z43.discriminatedUnion("kind", [
|
|
|
8904
9047
|
// safe because multi-scene IS the intended render for a color variant set,
|
|
8905
9048
|
// not a blind quality upsell. Maps to `videoTier: 'standard'` on the
|
|
8906
9049
|
// confirm call; the backend re-gates it (variants + variant_set only).
|
|
8907
|
-
useMultiSceneShowcase:
|
|
9050
|
+
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".')
|
|
8908
9051
|
}),
|
|
8909
|
-
|
|
8910
|
-
kind:
|
|
9052
|
+
z44.object({
|
|
9053
|
+
kind: z44.literal("angle_review_approval"),
|
|
8911
9054
|
// All edits are optional — empty action body just approves the
|
|
8912
9055
|
// backend-generated draft as-is. Field shapes mirror the angle-review
|
|
8913
9056
|
// request contracts in @packages/types/wizard.ts; the handler routes to
|
|
8914
9057
|
// the dedicated video/image/autopilot endpoint based on StatusResponse.kind.
|
|
8915
|
-
customAngle:
|
|
9058
|
+
customAngle: z44.string().min(1).max(480).optional(),
|
|
8916
9059
|
// Keep the shared MCP action within the strictest downstream contract:
|
|
8917
9060
|
// autopilot/image resume endpoints cap these fields at 1500 characters.
|
|
8918
|
-
painPoints:
|
|
8919
|
-
benefits:
|
|
8920
|
-
features:
|
|
8921
|
-
gapDurationSeconds:
|
|
8922
|
-
|
|
8923
|
-
|
|
9061
|
+
painPoints: z44.string().max(1500).optional(),
|
|
9062
|
+
benefits: z44.string().max(1500).optional(),
|
|
9063
|
+
features: z44.array(z44.string().min(1).max(300)).max(10).optional(),
|
|
9064
|
+
gapDurationSeconds: z44.union([
|
|
9065
|
+
z44.literal(15),
|
|
9066
|
+
z44.literal(30)
|
|
8924
9067
|
]).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.")
|
|
8925
9068
|
}),
|
|
8926
|
-
|
|
8927
|
-
kind:
|
|
8928
|
-
angleId:
|
|
8929
|
-
content:
|
|
8930
|
-
creativeDirection:
|
|
8931
|
-
actor:
|
|
9069
|
+
z44.object({
|
|
9070
|
+
kind: z44.literal("service_angle_approval"),
|
|
9071
|
+
angleId: z44.string().uuid(),
|
|
9072
|
+
content: z44.string().min(1).max(480).optional(),
|
|
9073
|
+
creativeDirection: z44.string().min(1).max(1500).optional(),
|
|
9074
|
+
actor: z44.string().min(1).max(360).optional()
|
|
8932
9075
|
}),
|
|
8933
|
-
|
|
8934
|
-
kind:
|
|
8935
|
-
voiceovers:
|
|
8936
|
-
visualDirections:
|
|
9076
|
+
z44.object({
|
|
9077
|
+
kind: z44.literal("service_script_approval"),
|
|
9078
|
+
voiceovers: z44.record(z44.string(), z44.string().min(1).max(600)).optional(),
|
|
9079
|
+
visualDirections: z44.record(z44.string(), z44.string().min(1).max(1200)).optional()
|
|
8937
9080
|
})
|
|
8938
9081
|
]);
|
|
8939
|
-
var ResolvePendingActionInput =
|
|
8940
|
-
taskId:
|
|
9082
|
+
var ResolvePendingActionInput = z44.object({
|
|
9083
|
+
taskId: z44.string().min(1),
|
|
8941
9084
|
action: PendingActionPayload
|
|
8942
9085
|
}).strict();
|
|
8943
9086
|
function resolveVariantConfirmVideoTier(action) {
|
|
8944
9087
|
return action.useMultiSceneShowcase && action.userChoice === "variants" ? "standard" : void 0;
|
|
8945
9088
|
}
|
|
8946
9089
|
__name(resolveVariantConfirmVideoTier, "resolveVariantConfirmVideoTier");
|
|
8947
|
-
var StatusForResolveSchema =
|
|
9090
|
+
var StatusForResolveSchema = z44.object({
|
|
8948
9091
|
// Metadata used to select the backend resume contract. Older API responses
|
|
8949
9092
|
// may omit these fields, so the resolver keeps the generic fallback.
|
|
8950
|
-
kind:
|
|
9093
|
+
kind: z44.enum([
|
|
8951
9094
|
"autopilot",
|
|
8952
9095
|
"video",
|
|
8953
9096
|
"image",
|
|
@@ -8955,24 +9098,24 @@ var StatusForResolveSchema = z43.object({
|
|
|
8955
9098
|
"mix",
|
|
8956
9099
|
"service"
|
|
8957
9100
|
]).optional(),
|
|
8958
|
-
selectedOutputs:
|
|
9101
|
+
selectedOutputs: z44.array(z44.enum([
|
|
8959
9102
|
"landing",
|
|
8960
9103
|
"video",
|
|
8961
9104
|
"image"
|
|
8962
9105
|
])).optional(),
|
|
8963
|
-
videoAngleCount:
|
|
8964
|
-
ugcFormat:
|
|
9106
|
+
videoAngleCount: z44.number().int().min(1).max(3).optional(),
|
|
9107
|
+
ugcFormat: z44.enum([
|
|
8965
9108
|
"single_8s",
|
|
8966
9109
|
"single_15s",
|
|
8967
9110
|
"double_30s"
|
|
8968
9111
|
]).optional(),
|
|
8969
|
-
videoStyle:
|
|
9112
|
+
videoStyle: z44.enum([
|
|
8970
9113
|
"ugc",
|
|
8971
9114
|
"cartoon_3d",
|
|
8972
9115
|
"product_only",
|
|
8973
9116
|
"ai_dynamic"
|
|
8974
9117
|
]).optional(),
|
|
8975
|
-
videoModel:
|
|
9118
|
+
videoModel: z44.enum([
|
|
8976
9119
|
"veo",
|
|
8977
9120
|
"wan",
|
|
8978
9121
|
"bytedance",
|
|
@@ -8980,39 +9123,45 @@ var StatusForResolveSchema = z43.object({
|
|
|
8980
9123
|
"grok",
|
|
8981
9124
|
"kling"
|
|
8982
9125
|
]).optional(),
|
|
8983
|
-
videoTier:
|
|
9126
|
+
videoTier: z44.enum([
|
|
8984
9127
|
"legacy",
|
|
8985
9128
|
"standard",
|
|
8986
9129
|
"pro",
|
|
8987
9130
|
"single_call"
|
|
8988
9131
|
]).optional(),
|
|
8989
|
-
videoResolution:
|
|
9132
|
+
videoResolution: z44.enum([
|
|
8990
9133
|
"480p",
|
|
8991
9134
|
"720p"
|
|
8992
9135
|
]).optional(),
|
|
8993
9136
|
narrativeStyle: NarrativeStyleEnum.optional(),
|
|
8994
|
-
pendingUserAction:
|
|
8995
|
-
kind:
|
|
8996
|
-
candidateGrouping:
|
|
8997
|
-
selectedImageUrls:
|
|
8998
|
-
proposedProductNames:
|
|
8999
|
-
customAngleDraft:
|
|
9000
|
-
painPointsDraft:
|
|
9001
|
-
benefitsDraft:
|
|
9002
|
-
featuresDraft:
|
|
9137
|
+
pendingUserAction: z44.object({
|
|
9138
|
+
kind: z44.string(),
|
|
9139
|
+
candidateGrouping: z44.string().optional(),
|
|
9140
|
+
selectedImageUrls: z44.array(z44.string()).optional(),
|
|
9141
|
+
proposedProductNames: z44.array(z44.string()).optional(),
|
|
9142
|
+
customAngleDraft: z44.string().optional(),
|
|
9143
|
+
painPointsDraft: z44.string().optional(),
|
|
9144
|
+
benefitsDraft: z44.string().optional(),
|
|
9145
|
+
featuresDraft: z44.array(z44.string()).optional(),
|
|
9003
9146
|
// Recommendation consumed only when the caller explicitly sends
|
|
9004
9147
|
// gapDurationSeconds: 30 in the angle approval action.
|
|
9005
9148
|
gapDurationChoice: GapDurationChoiceMirror.optional(),
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
|
|
9012
|
-
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
|
|
9149
|
+
// `.passthrough()` + an explicit `actorId`: this object is a MIRROR of the
|
|
9150
|
+
// backend's pending service angle, and a bare z.object() STRIPS unknown
|
|
9151
|
+
// keys. `actorId` (the saved custom actor the picker stored) was silently
|
|
9152
|
+
// dropped here, so the approval handler below could not echo it back on the
|
|
9153
|
+
// PATCH — the selection looked accepted and reached no render.
|
|
9154
|
+
angles: z44.array(z44.object({
|
|
9155
|
+
id: z44.string().uuid(),
|
|
9156
|
+
content: z44.string(),
|
|
9157
|
+
creativeDirection: z44.string(),
|
|
9158
|
+
actor: z44.string(),
|
|
9159
|
+
actorId: z44.string().nullable().optional()
|
|
9160
|
+
}).passthrough()).optional(),
|
|
9161
|
+
panels: z44.array(z44.object({
|
|
9162
|
+
beatId: z44.string(),
|
|
9163
|
+
voiceover: z44.string(),
|
|
9164
|
+
visualDirection: z44.string()
|
|
9016
9165
|
}).passthrough()).optional()
|
|
9017
9166
|
}).optional()
|
|
9018
9167
|
}).passthrough();
|
|
@@ -9088,6 +9237,7 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
|
|
|
9088
9237
|
"options.selectedOutputs": input.outputs,
|
|
9089
9238
|
"options.competitorScanEnabled": input.competitorScanEnabled,
|
|
9090
9239
|
"options.brandId": input.brandId,
|
|
9240
|
+
"options.actorId": input.actorId,
|
|
9091
9241
|
"options.creativeDirection": input.creativeDirection,
|
|
9092
9242
|
"options.headline": input.headline,
|
|
9093
9243
|
"options.subheadline": input.subheadline,
|
|
@@ -9229,7 +9379,7 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
|
|
|
9229
9379
|
throw translateError(e);
|
|
9230
9380
|
}
|
|
9231
9381
|
});
|
|
9232
|
-
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) => {
|
|
9382
|
+
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) => {
|
|
9233
9383
|
try {
|
|
9234
9384
|
const angle = await api.patchJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/angles/${input.angleIndex}`, input.angle ?? {});
|
|
9235
9385
|
return asText({
|
|
@@ -9405,7 +9555,13 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
|
|
|
9405
9555
|
const edits = {
|
|
9406
9556
|
content: action.content ?? candidate.content,
|
|
9407
9557
|
creativeDirection: action.creativeDirection ?? candidate.creativeDirection,
|
|
9408
|
-
actor: action.actor ?? candidate.actor
|
|
9558
|
+
actor: action.actor ?? candidate.actor,
|
|
9559
|
+
// Echo the persisted saved actor. Omitted when absent so the PATCH
|
|
9560
|
+
// never writes a null over a selection made elsewhere — the
|
|
9561
|
+
// backend only updates the keys it receives.
|
|
9562
|
+
...candidate.actorId ? {
|
|
9563
|
+
actorId: candidate.actorId
|
|
9564
|
+
} : {}
|
|
9409
9565
|
};
|
|
9410
9566
|
await api.patchJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/service-angles/${encodeURIComponent(candidate.id)}`, edits);
|
|
9411
9567
|
const result = await api.postJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/service-angles/select`, {
|
|
@@ -9446,58 +9602,58 @@ __name(registerTaskTools, "registerTaskTools");
|
|
|
9446
9602
|
|
|
9447
9603
|
// src/tools/creatives.ts
|
|
9448
9604
|
import { randomUUID } from "crypto";
|
|
9449
|
-
import { z as
|
|
9605
|
+
import { z as z45 } from "zod";
|
|
9450
9606
|
var CreativeRefs = {
|
|
9451
9607
|
images: ImagesInput,
|
|
9452
9608
|
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.'),
|
|
9453
9609
|
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.'),
|
|
9454
|
-
price:
|
|
9455
|
-
offer:
|
|
9456
|
-
compareAtPrice:
|
|
9457
|
-
paymentMethods:
|
|
9458
|
-
productContext:
|
|
9459
|
-
productLabel:
|
|
9460
|
-
buyerPersona:
|
|
9461
|
-
isCombo:
|
|
9462
|
-
competitorScanEnabled:
|
|
9463
|
-
brandId:
|
|
9464
|
-
projectId:
|
|
9465
|
-
creativeDirection:
|
|
9466
|
-
headline:
|
|
9467
|
-
subheadline:
|
|
9610
|
+
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.'),
|
|
9611
|
+
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.'),
|
|
9612
|
+
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".'),
|
|
9613
|
+
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.'),
|
|
9614
|
+
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."),
|
|
9615
|
+
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.'),
|
|
9616
|
+
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.'),
|
|
9617
|
+
isCombo: z45.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
|
|
9618
|
+
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."),
|
|
9619
|
+
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."),
|
|
9620
|
+
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."),
|
|
9621
|
+
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.'),
|
|
9622
|
+
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."),
|
|
9623
|
+
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.")
|
|
9468
9624
|
};
|
|
9469
|
-
var VideoInput =
|
|
9625
|
+
var VideoInput = z45.object({
|
|
9470
9626
|
images: CreativeRefs.images,
|
|
9471
|
-
title:
|
|
9472
|
-
primaryAngle:
|
|
9627
|
+
title: z45.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
|
|
9628
|
+
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".'),
|
|
9473
9629
|
language: CreativeRefs.language,
|
|
9474
9630
|
country: CreativeRefs.country,
|
|
9475
|
-
aspectRatio:
|
|
9631
|
+
aspectRatio: z45.enum([
|
|
9476
9632
|
"9:16",
|
|
9477
9633
|
"16:9",
|
|
9478
9634
|
"Auto"
|
|
9479
9635
|
]).describe('Relaci\xF3n de aspecto. "9:16" = vertical (TikTok/Reels \u2014 default recomendado para ads). "16:9" = horizontal (YouTube). "Auto" = decide la IA.'),
|
|
9480
|
-
durationSeconds:
|
|
9481
|
-
|
|
9482
|
-
|
|
9636
|
+
durationSeconds: z45.union([
|
|
9637
|
+
z45.literal(15),
|
|
9638
|
+
z45.literal(30)
|
|
9483
9639
|
]).default(15).describe("Opcional. Duraci\xF3n objetivo: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
|
|
9484
9640
|
videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla. Default "auto" (la IA escoge tras analizar el producto). Todos los presets funcionan a 15s y 30s, 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).'),
|
|
9485
9641
|
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.'),
|
|
9486
9642
|
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).'),
|
|
9487
|
-
features:
|
|
9643
|
+
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"].'),
|
|
9488
9644
|
price: CreativeRefs.price,
|
|
9489
9645
|
offer: CreativeRefs.offer,
|
|
9490
9646
|
productContext: CreativeRefs.productContext,
|
|
9491
9647
|
productLabel: CreativeRefs.productLabel,
|
|
9492
|
-
cta:
|
|
9493
|
-
scriptNotes:
|
|
9494
|
-
pauseForVideoScriptReview:
|
|
9495
|
-
pauseForAngleReview:
|
|
9648
|
+
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.'),
|
|
9649
|
+
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.'),
|
|
9650
|
+
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."),
|
|
9651
|
+
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."),
|
|
9496
9652
|
competitorScanEnabled: CreativeRefs.competitorScanEnabled,
|
|
9497
9653
|
brandId: CreativeRefs.brandId,
|
|
9498
9654
|
projectId: CreativeRefs.projectId
|
|
9499
9655
|
}).strict();
|
|
9500
|
-
var FreeVideoQualityEnum =
|
|
9656
|
+
var FreeVideoQualityEnum = z45.enum([
|
|
9501
9657
|
"economical",
|
|
9502
9658
|
"high_detail"
|
|
9503
9659
|
]);
|
|
@@ -9519,38 +9675,38 @@ var FREE_VIDEO_DURATIONS_BY_QUALITY = {
|
|
|
9519
9675
|
15
|
|
9520
9676
|
]
|
|
9521
9677
|
};
|
|
9522
|
-
var FreeVideoInput =
|
|
9678
|
+
var FreeVideoInput = z45.object({
|
|
9523
9679
|
images: ImagesInput.optional().describe("Opcional. 1-5 fotos del producto. Om\xEDtelo para generar desde texto puro."),
|
|
9524
|
-
rawPrompt:
|
|
9680
|
+
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."),
|
|
9525
9681
|
quality: FreeVideoQualityEnum.describe('Calidad del video: "economical" = Econ\xF3mico o "high_detail" = Alto detalle.'),
|
|
9526
|
-
durationSeconds:
|
|
9682
|
+
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."),
|
|
9527
9683
|
videoResolution: VideoResolutionEnum.describe("Resoluci\xF3n del video: 480p o 720p."),
|
|
9528
|
-
generateAiMusic:
|
|
9529
|
-
addSubtitles:
|
|
9530
|
-
idempotencyKey:
|
|
9684
|
+
generateAiMusic: z45.boolean().default(false).describe("Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false."),
|
|
9685
|
+
addSubtitles: z45.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
|
|
9686
|
+
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.")
|
|
9531
9687
|
}).strict();
|
|
9532
9688
|
var imageStyleList = ImageCreativeStyleEnum.options.join(" | ");
|
|
9533
9689
|
var archetypeList = ImageCreativeArchetypeEnum.options.join(" | ");
|
|
9534
9690
|
var visualTreatmentList = ImageCreativeVisualTreatmentEnum.options.join(" | ");
|
|
9535
9691
|
var aspectRatioList = ImageCreativeAspectRatioEnum.options.join(" | ");
|
|
9536
|
-
var ImageInput =
|
|
9692
|
+
var ImageInput = z45.object({
|
|
9537
9693
|
images: CreativeRefs.images,
|
|
9538
|
-
productTitle:
|
|
9539
|
-
primaryAngle:
|
|
9694
|
+
productTitle: z45.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
|
|
9695
|
+
primaryAngle: z45.string().min(1).describe('\xC1ngulo de venta principal. Ej. "Reduce arrugas en 14 d\xEDas".'),
|
|
9540
9696
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
9541
9697
|
language: CreativeRefs.language,
|
|
9542
9698
|
country: CreativeRefs.country,
|
|
9543
|
-
nVariants:
|
|
9544
|
-
imageModel:
|
|
9699
|
+
nVariants: z45.number().int().min(1).max(6).optional().describe("Opcional. N\xFAmero de variantes a generar (1-6). Default 3."),
|
|
9700
|
+
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.'),
|
|
9545
9701
|
imageStyle: ImageCreativeStyleEnum.optional().describe(`Opcional. Estilo del shot del producto. Valores: ${imageStyleList}. Si se omite, la IA escoge seg\xFAn el archetype.`),
|
|
9546
9702
|
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).`),
|
|
9547
9703
|
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.`),
|
|
9548
|
-
styleMix:
|
|
9704
|
+
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."),
|
|
9549
9705
|
aspectRatio: ImageCreativeAspectRatioEnum.optional().describe(`Opcional. Relaci\xF3n de aspecto. Valores: ${aspectRatioList}. Default "4:5" (vertical Instagram feed).`),
|
|
9550
|
-
generateAdCopy:
|
|
9551
|
-
pauseForAngleReview:
|
|
9552
|
-
features:
|
|
9553
|
-
brandColors:
|
|
9706
|
+
generateAdCopy: z45.boolean().optional().describe("Opcional. Si true, tambi\xE9n genera headline/body/CTA para el ad. Default false."),
|
|
9707
|
+
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."),
|
|
9708
|
+
features: z45.array(z45.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
|
|
9709
|
+
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.'),
|
|
9554
9710
|
price: CreativeRefs.price,
|
|
9555
9711
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
9556
9712
|
offer: CreativeRefs.offer,
|
|
@@ -9564,16 +9720,16 @@ var ImageInput = z44.object({
|
|
|
9564
9720
|
headline: CreativeRefs.headline,
|
|
9565
9721
|
subheadline: CreativeRefs.subheadline
|
|
9566
9722
|
});
|
|
9567
|
-
var EditImageCreativeInput =
|
|
9568
|
-
taskId:
|
|
9569
|
-
variantIndex:
|
|
9570
|
-
instruction:
|
|
9723
|
+
var EditImageCreativeInput = z45.object({
|
|
9724
|
+
taskId: z45.string().uuid().describe("ID de la tarea de imagen ya finalizada."),
|
|
9725
|
+
variantIndex: z45.number().int().min(0).max(10).describe("\xCDndice de la variante a corregir (0 = primera imagen)."),
|
|
9726
|
+
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".')
|
|
9571
9727
|
});
|
|
9572
|
-
var LandingInput =
|
|
9728
|
+
var LandingInput = z45.object({
|
|
9573
9729
|
images: CreativeRefs.images,
|
|
9574
9730
|
language: CreativeRefs.language,
|
|
9575
9731
|
country: CreativeRefs.country,
|
|
9576
|
-
salesAngle:
|
|
9732
|
+
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.'),
|
|
9577
9733
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
9578
9734
|
price: CreativeRefs.price,
|
|
9579
9735
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
@@ -9622,7 +9778,7 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
9622
9778
|
throw translateError(e);
|
|
9623
9779
|
}
|
|
9624
9780
|
});
|
|
9625
|
-
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) => {
|
|
9781
|
+
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) => {
|
|
9626
9782
|
try {
|
|
9627
9783
|
assertVideoPresetDurationCompatibility(input.videoPreset, input.durationSeconds);
|
|
9628
9784
|
const resolved = resolveVideoPreset(input.videoPreset);
|
|
@@ -9699,8 +9855,8 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
9699
9855
|
__name(registerCreativeTools, "registerCreativeTools");
|
|
9700
9856
|
|
|
9701
9857
|
// src/tools/creative-variants.ts
|
|
9702
|
-
import { z as
|
|
9703
|
-
var LandingSectionKeyEnum =
|
|
9858
|
+
import { z as z46 } from "zod";
|
|
9859
|
+
var LandingSectionKeyEnum = z46.enum([
|
|
9704
9860
|
"problem_hook",
|
|
9705
9861
|
"problem_cost",
|
|
9706
9862
|
"solution_mechanism",
|
|
@@ -9740,24 +9896,24 @@ var LandingSectionKeyEnum = z45.enum([
|
|
|
9740
9896
|
"lifestyle_fit",
|
|
9741
9897
|
"join_lifestyle"
|
|
9742
9898
|
]);
|
|
9743
|
-
var CreativeVariantKindEnum =
|
|
9899
|
+
var CreativeVariantKindEnum = z46.enum([
|
|
9744
9900
|
"video",
|
|
9745
9901
|
"image",
|
|
9746
9902
|
"landing"
|
|
9747
9903
|
]);
|
|
9748
|
-
var EstimateCreativeVariantInputBase =
|
|
9749
|
-
sourceTaskId:
|
|
9904
|
+
var EstimateCreativeVariantInputBase = z46.object({
|
|
9905
|
+
sourceTaskId: z46.string().uuid().describe("ID de la tarea original completada."),
|
|
9750
9906
|
kind: CreativeVariantKindEnum,
|
|
9751
9907
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Solo cambia el preset p\xFAblico; el servidor conserva el modelo/tier del task origen."),
|
|
9752
9908
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video. Resoluci\xF3n del nuevo render."),
|
|
9753
|
-
nVariants:
|
|
9909
|
+
nVariants: z46.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
9754
9910
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si ya conoces la secci\xF3n, evita cotizar la clasificaci\xF3n con IA."),
|
|
9755
|
-
instructions:
|
|
9911
|
+
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.")
|
|
9756
9912
|
}).strict();
|
|
9757
9913
|
var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
9758
9914
|
if (input.kind === "video" && input.nVariants !== void 0) {
|
|
9759
9915
|
ctx.addIssue({
|
|
9760
|
-
code:
|
|
9916
|
+
code: z46.ZodIssueCode.custom,
|
|
9761
9917
|
path: [
|
|
9762
9918
|
"nVariants"
|
|
9763
9919
|
],
|
|
@@ -9767,7 +9923,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9767
9923
|
if (input.kind !== "video") {
|
|
9768
9924
|
if (input.videoPreset !== void 0) {
|
|
9769
9925
|
ctx.addIssue({
|
|
9770
|
-
code:
|
|
9926
|
+
code: z46.ZodIssueCode.custom,
|
|
9771
9927
|
path: [
|
|
9772
9928
|
"videoPreset"
|
|
9773
9929
|
],
|
|
@@ -9776,7 +9932,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9776
9932
|
}
|
|
9777
9933
|
if (input.videoResolution !== void 0) {
|
|
9778
9934
|
ctx.addIssue({
|
|
9779
|
-
code:
|
|
9935
|
+
code: z46.ZodIssueCode.custom,
|
|
9780
9936
|
path: [
|
|
9781
9937
|
"videoResolution"
|
|
9782
9938
|
],
|
|
@@ -9786,7 +9942,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9786
9942
|
}
|
|
9787
9943
|
if (input.kind !== "image" && input.nVariants !== void 0) {
|
|
9788
9944
|
ctx.addIssue({
|
|
9789
|
-
code:
|
|
9945
|
+
code: z46.ZodIssueCode.custom,
|
|
9790
9946
|
path: [
|
|
9791
9947
|
"nVariants"
|
|
9792
9948
|
],
|
|
@@ -9796,7 +9952,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9796
9952
|
if (input.kind !== "landing") {
|
|
9797
9953
|
if (input.sectionKey !== void 0) {
|
|
9798
9954
|
ctx.addIssue({
|
|
9799
|
-
code:
|
|
9955
|
+
code: z46.ZodIssueCode.custom,
|
|
9800
9956
|
path: [
|
|
9801
9957
|
"sectionKey"
|
|
9802
9958
|
],
|
|
@@ -9805,7 +9961,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9805
9961
|
}
|
|
9806
9962
|
if (input.instructions !== void 0) {
|
|
9807
9963
|
ctx.addIssue({
|
|
9808
|
-
code:
|
|
9964
|
+
code: z46.ZodIssueCode.custom,
|
|
9809
9965
|
path: [
|
|
9810
9966
|
"instructions"
|
|
9811
9967
|
],
|
|
@@ -9814,31 +9970,31 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
9814
9970
|
}
|
|
9815
9971
|
}
|
|
9816
9972
|
});
|
|
9817
|
-
var CreateCreativeVariantInputBase =
|
|
9818
|
-
sourceTaskId:
|
|
9973
|
+
var CreateCreativeVariantInputBase = z46.object({
|
|
9974
|
+
sourceTaskId: z46.string().uuid().describe("ID de la tarea original completada."),
|
|
9819
9975
|
kind: CreativeVariantKindEnum,
|
|
9820
|
-
confirm:
|
|
9821
|
-
clientRequestId:
|
|
9822
|
-
angle:
|
|
9823
|
-
buyerPersona:
|
|
9824
|
-
scriptNotes:
|
|
9825
|
-
creativeDirection:
|
|
9976
|
+
confirm: z46.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
|
|
9977
|
+
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."),
|
|
9978
|
+
angle: z46.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
|
|
9979
|
+
buyerPersona: z46.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
|
|
9980
|
+
scriptNotes: z46.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
|
|
9981
|
+
creativeDirection: z46.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
|
|
9826
9982
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Preset p\xFAblico; el servidor resuelve modelo/tier desde el task origen."),
|
|
9827
9983
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video."),
|
|
9828
|
-
generateAiMusic:
|
|
9829
|
-
nVariants:
|
|
9984
|
+
generateAiMusic: z46.boolean().optional().describe("Opcional para video."),
|
|
9985
|
+
nVariants: z46.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
9830
9986
|
imageStyle: ImageCreativeStyleEnum.optional().describe("Opcional para imagen."),
|
|
9831
9987
|
creativeArchetype: ImageCreativeArchetypeEnum.optional().describe("Opcional para imagen."),
|
|
9832
9988
|
visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe("Opcional para imagen."),
|
|
9833
9989
|
aspectRatio: ImageCreativeAspectRatioEnum.optional().describe("Opcional para imagen."),
|
|
9834
|
-
styleMix:
|
|
9990
|
+
styleMix: z46.boolean().optional().describe("Opcional para imagen."),
|
|
9835
9991
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si se omite, se clasifica con IA a partir de instructions."),
|
|
9836
|
-
instructions:
|
|
9992
|
+
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.")
|
|
9837
9993
|
}).strict();
|
|
9838
9994
|
var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
9839
9995
|
if ((input.kind === "video" || input.kind === "image") && !input.angle) {
|
|
9840
9996
|
ctx.addIssue({
|
|
9841
|
-
code:
|
|
9997
|
+
code: z46.ZodIssueCode.custom,
|
|
9842
9998
|
path: [
|
|
9843
9999
|
"angle"
|
|
9844
10000
|
],
|
|
@@ -9847,7 +10003,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
9847
10003
|
}
|
|
9848
10004
|
if (input.kind === "landing" && !input.sectionKey && !input.instructions) {
|
|
9849
10005
|
ctx.addIssue({
|
|
9850
|
-
code:
|
|
10006
|
+
code: z46.ZodIssueCode.custom,
|
|
9851
10007
|
path: [
|
|
9852
10008
|
"instructions"
|
|
9853
10009
|
],
|
|
@@ -9893,7 +10049,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
9893
10049
|
for (const field of fieldsByKind[input.kind]) {
|
|
9894
10050
|
if (input[field] !== void 0) {
|
|
9895
10051
|
ctx.addIssue({
|
|
9896
|
-
code:
|
|
10052
|
+
code: z46.ZodIssueCode.custom,
|
|
9897
10053
|
path: [
|
|
9898
10054
|
field
|
|
9899
10055
|
],
|
|
@@ -10010,27 +10166,27 @@ function registerCreativeVariantTools(server, api) {
|
|
|
10010
10166
|
__name(registerCreativeVariantTools, "registerCreativeVariantTools");
|
|
10011
10167
|
|
|
10012
10168
|
// src/tools/script-review.ts
|
|
10013
|
-
import { z as
|
|
10014
|
-
var GetVideoScriptInput =
|
|
10015
|
-
taskId:
|
|
10016
|
-
angleKey:
|
|
10169
|
+
import { z as z47 } from "zod";
|
|
10170
|
+
var GetVideoScriptInput = z47.object({
|
|
10171
|
+
taskId: z47.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
|
|
10172
|
+
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.')
|
|
10017
10173
|
}).strict();
|
|
10018
10174
|
var VIDEO_SCRIPT_APPROVAL_MAX_SCENES2 = 50;
|
|
10019
10175
|
var VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2 = 2e3;
|
|
10020
|
-
var ApproveSceneEdit =
|
|
10021
|
-
index:
|
|
10022
|
-
dialogue:
|
|
10176
|
+
var ApproveSceneEdit = z47.object({
|
|
10177
|
+
index: z47.number().int().nonnegative().describe("\xCDndice de la escena (0-based, mismo que devuelve get_video_script)."),
|
|
10178
|
+
dialogue: z47.string().min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2).refine((v) => v.trim().length > 0, {
|
|
10023
10179
|
message: "dialogue no puede ser s\xF3lo espacios"
|
|
10024
10180
|
}).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.")
|
|
10025
10181
|
}).strict();
|
|
10026
|
-
var ApproveVideoScriptInput =
|
|
10027
|
-
taskId:
|
|
10028
|
-
angleKey:
|
|
10029
|
-
scenes:
|
|
10182
|
+
var ApproveVideoScriptInput = z47.object({
|
|
10183
|
+
taskId: z47.string().min(1).describe("ID de la tarea."),
|
|
10184
|
+
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.'),
|
|
10185
|
+
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.")
|
|
10030
10186
|
}).strict();
|
|
10031
|
-
var RegenerateVideoScriptInput =
|
|
10032
|
-
taskId:
|
|
10033
|
-
angleKey:
|
|
10187
|
+
var RegenerateVideoScriptInput = z47.object({
|
|
10188
|
+
taskId: z47.string().min(1).describe("ID de la tarea."),
|
|
10189
|
+
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.'),
|
|
10034
10190
|
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).')
|
|
10035
10191
|
}).strict();
|
|
10036
10192
|
function registerScriptReviewTools(server, api) {
|
|
@@ -10070,9 +10226,9 @@ function registerScriptReviewTools(server, api) {
|
|
|
10070
10226
|
__name(registerScriptReviewTools, "registerScriptReviewTools");
|
|
10071
10227
|
|
|
10072
10228
|
// src/tools/connections.ts
|
|
10073
|
-
import { z as
|
|
10229
|
+
import { z as z48 } from "zod";
|
|
10074
10230
|
function registerConnectionTools(server, api) {
|
|
10075
|
-
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.",
|
|
10231
|
+
server.tool("list_shopify_shops", "Lista las tiendas Shopify conectadas por este usuario. Si la lista est\xE1 vac\xEDa, dile al usuario que conecte una tienda en productmaker.app/integrations antes de intentar publicar.", z48.object({}).shape, async () => {
|
|
10076
10232
|
try {
|
|
10077
10233
|
const r = await api.getJson("/v1/shopify/shops");
|
|
10078
10234
|
return asText(r);
|
|
@@ -10080,10 +10236,10 @@ function registerConnectionTools(server, api) {
|
|
|
10080
10236
|
throw translateError(e);
|
|
10081
10237
|
}
|
|
10082
10238
|
});
|
|
10083
|
-
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.',
|
|
10084
|
-
shopDomain:
|
|
10085
|
-
query:
|
|
10086
|
-
taskId:
|
|
10239
|
+
server.tool("list_shopify_products", 'Busca productos en una tienda Shopify conectada para elegir el target de una republicaci\xF3n. Usa el `id` devuelto junto con `publicationMode="existing"` en `publish_to_shopify`. Si `taskId` se proporciona, la respuesta tambi\xE9n indica el producto publicado actualmente para esa tarea y tienda. No modifica Shopify.', z48.object({
|
|
10240
|
+
shopDomain: z48.string().min(3).max(120),
|
|
10241
|
+
query: z48.string().trim().max(120).optional(),
|
|
10242
|
+
taskId: z48.string().uuid().optional()
|
|
10087
10243
|
}).shape, async ({ shopDomain, query, taskId }) => {
|
|
10088
10244
|
try {
|
|
10089
10245
|
const params = new URLSearchParams({
|
|
@@ -10097,7 +10253,7 @@ function registerConnectionTools(server, api) {
|
|
|
10097
10253
|
throw translateError(e);
|
|
10098
10254
|
}
|
|
10099
10255
|
});
|
|
10100
|
-
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.",
|
|
10256
|
+
server.tool("list_meta_ad_accounts", "Lista las cuentas publicitarias de Meta conectadas. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar Meta en productmaker.app/integrations.", z48.object({}).shape, async () => {
|
|
10101
10257
|
try {
|
|
10102
10258
|
const r = await api.getJson("/v1/facebook/ad-accounts");
|
|
10103
10259
|
return asText(r);
|
|
@@ -10105,7 +10261,7 @@ function registerConnectionTools(server, api) {
|
|
|
10105
10261
|
throw translateError(e);
|
|
10106
10262
|
}
|
|
10107
10263
|
});
|
|
10108
|
-
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.",
|
|
10264
|
+
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 () => {
|
|
10109
10265
|
try {
|
|
10110
10266
|
const r = await api.getJson("/v1/tiktok/advertisers");
|
|
10111
10267
|
return asText(r);
|
|
@@ -10116,106 +10272,129 @@ function registerConnectionTools(server, api) {
|
|
|
10116
10272
|
}
|
|
10117
10273
|
__name(registerConnectionTools, "registerConnectionTools");
|
|
10118
10274
|
|
|
10275
|
+
// src/tools/actors.ts
|
|
10276
|
+
import { z as z49 } from "zod";
|
|
10277
|
+
function registerActorTools(server, api) {
|
|
10278
|
+
server.tool("list_actors", "Lista los personajes guardados del usuario (personas creadas con IA que pueden protagonizar los videos e im\xE1genes). Devuelve id, nombre, descripci\xF3n, g\xE9nero y voz. Usa el `id` como `actorId` al crear o editar una tarea. Solo aparecen los personajes listos para usar; si la lista est\xE1 vac\xEDa, dile al usuario que cree uno en productmaker.app antes de intentar asignarlo. No modifica nada.", z49.object({}).shape, async () => {
|
|
10279
|
+
try {
|
|
10280
|
+
const rows = await api.getJson("/v1/actors");
|
|
10281
|
+
const actors = (Array.isArray(rows) ? rows : []).filter((actor) => actor.status === "ready").map(({ id, name, description, gender, voiceId }) => ({
|
|
10282
|
+
id,
|
|
10283
|
+
name,
|
|
10284
|
+
description,
|
|
10285
|
+
gender,
|
|
10286
|
+
voiceId: voiceId ?? null
|
|
10287
|
+
}));
|
|
10288
|
+
return asText({
|
|
10289
|
+
actors
|
|
10290
|
+
});
|
|
10291
|
+
} catch (e) {
|
|
10292
|
+
throw translateError(e);
|
|
10293
|
+
}
|
|
10294
|
+
});
|
|
10295
|
+
}
|
|
10296
|
+
__name(registerActorTools, "registerActorTools");
|
|
10297
|
+
|
|
10119
10298
|
// src/tools/publish.ts
|
|
10120
|
-
import { z as
|
|
10121
|
-
var ShopifyInput =
|
|
10122
|
-
taskId:
|
|
10123
|
-
shopDomain:
|
|
10299
|
+
import { z as z50 } from "zod";
|
|
10300
|
+
var ShopifyInput = z50.object({
|
|
10301
|
+
taskId: z50.string().min(1),
|
|
10302
|
+
shopDomain: z50.string().min(1),
|
|
10124
10303
|
/** Override: number of landing images to include. Default = ALL images the
|
|
10125
10304
|
* task generated (the MCP fetches /v1/results first to count). Bounded
|
|
10126
10305
|
* generously here; the backend validates each index against
|
|
10127
10306
|
* landing.images.length and rejects out-of-range. */
|
|
10128
|
-
numLandingImages:
|
|
10129
|
-
variantIndex:
|
|
10307
|
+
numLandingImages: z50.number().int().min(1).max(20).optional(),
|
|
10308
|
+
variantIndex: z50.number().int().min(0).optional(),
|
|
10130
10309
|
/** ASK THE USER before setting: "¿Tu producto tiene variantes (talla, color, etc.)?".
|
|
10131
10310
|
* When true, the landing renders variant-picker pills; when false, it ships
|
|
10132
10311
|
* a single-option product page. Defaults to false. */
|
|
10133
|
-
showVariantPicker:
|
|
10312
|
+
showVariantPicker: z50.boolean().optional(),
|
|
10134
10313
|
/** Default `auto` updates the product previously published for this task and
|
|
10135
10314
|
* shop. Use `existing` with targetProductId to choose another product, or
|
|
10136
10315
|
* `new` only when the user explicitly wants a duplicate/new listing. */
|
|
10137
|
-
publicationMode:
|
|
10316
|
+
publicationMode: z50.enum([
|
|
10138
10317
|
"auto",
|
|
10139
10318
|
"existing",
|
|
10140
10319
|
"new"
|
|
10141
10320
|
]).optional(),
|
|
10142
10321
|
/** Shopify product GID, numeric id, or admin product URL. Required when
|
|
10143
10322
|
* publicationMode is `existing`. */
|
|
10144
|
-
targetProductId:
|
|
10323
|
+
targetProductId: z50.string().trim().min(1).max(300).optional()
|
|
10145
10324
|
});
|
|
10146
|
-
var McpCampaignConfig =
|
|
10325
|
+
var McpCampaignConfig = z50.object({
|
|
10147
10326
|
/** ALWAYS in US dollars, whatever the ad account's currency is. ProductMaker
|
|
10148
10327
|
* converts to the account currency server-side. Do NOT pre-convert: on a COP
|
|
10149
10328
|
* account the user's "60.000 al día" is `dailyBudgetUsd: 15`, not 60000. */
|
|
10150
|
-
dailyBudgetUsd:
|
|
10329
|
+
dailyBudgetUsd: z50.number().positive().optional(),
|
|
10151
10330
|
/** ISO-3166-1 alpha-2 country codes. Meta accepts max 1 (single-country
|
|
10152
10331
|
* ad set). TikTok takes only the first entry. */
|
|
10153
|
-
targetCountries:
|
|
10154
|
-
campaignName:
|
|
10332
|
+
targetCountries: z50.array(z50.string().length(2)).max(1).optional(),
|
|
10333
|
+
campaignName: z50.string().trim().min(1).max(100).optional()
|
|
10155
10334
|
});
|
|
10156
10335
|
var McpAdAccountId = MetaAdAccountId.describe('Meta ad account id. Accepts "act_1234567890" or the bare numeric id "1234567890".');
|
|
10157
|
-
var MetaInput =
|
|
10158
|
-
taskId:
|
|
10336
|
+
var MetaInput = z50.object({
|
|
10337
|
+
taskId: z50.string().min(1),
|
|
10159
10338
|
adAccountId: McpAdAccountId,
|
|
10160
|
-
pixelId:
|
|
10161
|
-
landingUrl:
|
|
10339
|
+
pixelId: z50.string().min(1).optional(),
|
|
10340
|
+
landingUrl: z50.string().url().optional(),
|
|
10162
10341
|
/** Select which angles to publish by 0-based index. Omit to publish all
|
|
10163
10342
|
* successful angles. Example: a task with 3 angles → `[0,2]` publishes #0 + #2. */
|
|
10164
|
-
angleIndexes:
|
|
10343
|
+
angleIndexes: z50.array(z50.number().int().nonnegative()).optional(),
|
|
10165
10344
|
campaignConfig: McpCampaignConfig.optional(),
|
|
10166
10345
|
/** Override the ad's primary text (body copy). If omitted, ProductMaker
|
|
10167
10346
|
* uses the auto-generated copy attached to the task. Capped at 500 chars
|
|
10168
10347
|
* (Meta's primary-text limit). */
|
|
10169
|
-
primaryText:
|
|
10348
|
+
primaryText: z50.string().trim().min(1).max(500).optional(),
|
|
10170
10349
|
/** Optional WhatsApp number. When set, the ad button opens a WhatsApp chat
|
|
10171
10350
|
* (wa.me) instead of a landing page. */
|
|
10172
|
-
phoneNumber:
|
|
10351
|
+
phoneNumber: z50.string().min(8).max(32).optional()
|
|
10173
10352
|
});
|
|
10174
|
-
var GroupedTaskSource =
|
|
10175
|
-
type:
|
|
10176
|
-
taskId:
|
|
10177
|
-
angleIndex:
|
|
10178
|
-
videoVariant:
|
|
10353
|
+
var GroupedTaskSource = z50.object({
|
|
10354
|
+
type: z50.literal("task"),
|
|
10355
|
+
taskId: z50.string().min(1),
|
|
10356
|
+
angleIndex: z50.number().int().min(0).optional(),
|
|
10357
|
+
videoVariant: z50.enum([
|
|
10179
10358
|
"with_subtitles",
|
|
10180
10359
|
"without_subtitles"
|
|
10181
10360
|
]).optional(),
|
|
10182
|
-
imageVariantIndex:
|
|
10183
|
-
});
|
|
10184
|
-
var GroupedMediaSource =
|
|
10185
|
-
type:
|
|
10186
|
-
url:
|
|
10187
|
-
path:
|
|
10188
|
-
thumbnailUrl:
|
|
10189
|
-
thumbnailPath:
|
|
10361
|
+
imageVariantIndex: z50.number().int().min(0).optional()
|
|
10362
|
+
});
|
|
10363
|
+
var GroupedMediaSource = z50.object({
|
|
10364
|
+
type: z50.literal("media"),
|
|
10365
|
+
url: z50.string().url().optional(),
|
|
10366
|
+
path: z50.string().min(1).optional(),
|
|
10367
|
+
thumbnailUrl: z50.string().url().optional(),
|
|
10368
|
+
thumbnailPath: z50.string().min(1).optional()
|
|
10190
10369
|
}).refine((value) => Boolean(value.url) !== Boolean(value.path), {
|
|
10191
10370
|
message: "media source needs exactly one of `url` or `path`"
|
|
10192
10371
|
}).refine((value) => !(value.thumbnailUrl && value.thumbnailPath), {
|
|
10193
10372
|
message: "media source needs at most one of `thumbnailUrl` or `thumbnailPath`"
|
|
10194
10373
|
});
|
|
10195
|
-
var GroupedCreative =
|
|
10196
|
-
type:
|
|
10374
|
+
var GroupedCreative = z50.object({
|
|
10375
|
+
type: z50.enum([
|
|
10197
10376
|
"image",
|
|
10198
10377
|
"video"
|
|
10199
10378
|
]),
|
|
10200
|
-
source:
|
|
10379
|
+
source: z50.union([
|
|
10201
10380
|
GroupedTaskSource,
|
|
10202
10381
|
GroupedMediaSource
|
|
10203
10382
|
]),
|
|
10204
|
-
title:
|
|
10205
|
-
description:
|
|
10383
|
+
title: z50.string().max(120).optional(),
|
|
10384
|
+
description: z50.string().max(500).optional(),
|
|
10206
10385
|
cta: MetaCtaType.optional(),
|
|
10207
|
-
creativeId:
|
|
10386
|
+
creativeId: z50.string().min(1).optional()
|
|
10208
10387
|
});
|
|
10209
|
-
var GroupedCampaignConfig =
|
|
10388
|
+
var GroupedCampaignConfig = z50.object({
|
|
10210
10389
|
campaignType: MetaCampaignType.default("sales"),
|
|
10211
10390
|
optimizationMode: MetaOptimizationMode.default("advantage_plus"),
|
|
10212
10391
|
budgetMode: MetaBudgetMode.default("campaign"),
|
|
10213
|
-
campaignDailyBudget:
|
|
10214
|
-
adSets:
|
|
10215
|
-
tempId:
|
|
10216
|
-
name:
|
|
10217
|
-
dailyBudget:
|
|
10218
|
-
creativeIds:
|
|
10392
|
+
campaignDailyBudget: z50.number().int().positive().optional(),
|
|
10393
|
+
adSets: z50.array(z50.object({
|
|
10394
|
+
tempId: z50.string().min(1).max(80),
|
|
10395
|
+
name: z50.string().min(1).max(100),
|
|
10396
|
+
dailyBudget: z50.number().int().positive().optional(),
|
|
10397
|
+
creativeIds: z50.array(z50.string().min(1)).min(1)
|
|
10219
10398
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_AD_SETS),
|
|
10220
10399
|
/**
|
|
10221
10400
|
* Valid values are 'landing' and 'whatsapp' — there is no 'website'. This
|
|
@@ -10223,22 +10402,22 @@ var GroupedCampaignConfig = z48.object({
|
|
|
10223
10402
|
* opaque `invalid_literal` and made the WhatsApp destination — supported by
|
|
10224
10403
|
* the API since #849 — unreachable from this tool.
|
|
10225
10404
|
*/
|
|
10226
|
-
destination:
|
|
10227
|
-
|
|
10228
|
-
type:
|
|
10229
|
-
landingUrl:
|
|
10405
|
+
destination: z50.discriminatedUnion("type", [
|
|
10406
|
+
z50.object({
|
|
10407
|
+
type: z50.literal("landing"),
|
|
10408
|
+
landingUrl: z50.string().url()
|
|
10230
10409
|
}),
|
|
10231
|
-
|
|
10232
|
-
type:
|
|
10233
|
-
phoneNumber:
|
|
10410
|
+
z50.object({
|
|
10411
|
+
type: z50.literal("whatsapp"),
|
|
10412
|
+
phoneNumber: z50.string().min(8).max(32)
|
|
10234
10413
|
})
|
|
10235
10414
|
]),
|
|
10236
|
-
saveAsDefault:
|
|
10415
|
+
saveAsDefault: z50.boolean().default(false),
|
|
10237
10416
|
budgetPolicy: MetaBudgetPolicy.optional()
|
|
10238
10417
|
}).superRefine((value, ctx) => {
|
|
10239
10418
|
if (value.budgetMode === "campaign" && value.campaignDailyBudget === void 0) {
|
|
10240
10419
|
ctx.addIssue({
|
|
10241
|
-
code:
|
|
10420
|
+
code: z50.ZodIssueCode.custom,
|
|
10242
10421
|
path: [
|
|
10243
10422
|
"campaignDailyBudget"
|
|
10244
10423
|
],
|
|
@@ -10249,7 +10428,7 @@ var GroupedCampaignConfig = z48.object({
|
|
|
10249
10428
|
value.adSets.forEach((adSet, index) => {
|
|
10250
10429
|
if (adSet.dailyBudget === void 0) {
|
|
10251
10430
|
ctx.addIssue({
|
|
10252
|
-
code:
|
|
10431
|
+
code: z50.ZodIssueCode.custom,
|
|
10253
10432
|
path: [
|
|
10254
10433
|
"adSets",
|
|
10255
10434
|
index,
|
|
@@ -10261,22 +10440,22 @@ var GroupedCampaignConfig = z48.object({
|
|
|
10261
10440
|
});
|
|
10262
10441
|
}
|
|
10263
10442
|
});
|
|
10264
|
-
var PublishCreativesToMetaInputBase =
|
|
10265
|
-
creatives:
|
|
10443
|
+
var PublishCreativesToMetaInputBase = z50.object({
|
|
10444
|
+
creatives: z50.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
10266
10445
|
adAccountId: McpAdAccountId,
|
|
10267
|
-
pixelId:
|
|
10446
|
+
pixelId: z50.string().min(1).optional(),
|
|
10268
10447
|
/** Required unless `campaignConfig.destination` is whatsapp — see the
|
|
10269
10448
|
* superRefine below. Mirrors the API's `UploadToMetaRequest`. */
|
|
10270
|
-
landingUrl:
|
|
10271
|
-
targetCountry:
|
|
10272
|
-
campaignName:
|
|
10449
|
+
landingUrl: z50.string().url().optional(),
|
|
10450
|
+
targetCountry: z50.string().length(2),
|
|
10451
|
+
campaignName: z50.string().trim().min(1).max(100),
|
|
10273
10452
|
campaignConfig: GroupedCampaignConfig.optional()
|
|
10274
10453
|
});
|
|
10275
10454
|
var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((value, ctx) => {
|
|
10276
10455
|
const destination = value.campaignConfig?.destination;
|
|
10277
10456
|
if (destination?.type !== "whatsapp" && !value.landingUrl) {
|
|
10278
10457
|
ctx.addIssue({
|
|
10279
|
-
code:
|
|
10458
|
+
code: z50.ZodIssueCode.custom,
|
|
10280
10459
|
path: [
|
|
10281
10460
|
"landingUrl"
|
|
10282
10461
|
],
|
|
@@ -10285,7 +10464,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
10285
10464
|
}
|
|
10286
10465
|
if (destination?.type === "landing" && destination.landingUrl !== value.landingUrl) {
|
|
10287
10466
|
ctx.addIssue({
|
|
10288
|
-
code:
|
|
10467
|
+
code: z50.ZodIssueCode.custom,
|
|
10289
10468
|
path: [
|
|
10290
10469
|
"campaignConfig",
|
|
10291
10470
|
"destination",
|
|
@@ -10295,12 +10474,12 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
10295
10474
|
});
|
|
10296
10475
|
}
|
|
10297
10476
|
});
|
|
10298
|
-
var PresignUploadResponseSchema =
|
|
10299
|
-
uploadUrl:
|
|
10477
|
+
var PresignUploadResponseSchema = z50.object({
|
|
10478
|
+
uploadUrl: z50.string().url().refine((value) => {
|
|
10300
10479
|
const url = new URL(value);
|
|
10301
10480
|
return !url.username && !url.password && url.protocol === "https:";
|
|
10302
10481
|
}, "presigned upload URL must be HTTPS without embedded credentials"),
|
|
10303
|
-
publicUrl:
|
|
10482
|
+
publicUrl: z50.string().url().refine((value) => {
|
|
10304
10483
|
const url = new URL(value);
|
|
10305
10484
|
return !url.username && !url.password;
|
|
10306
10485
|
}, "public media URL must not contain embedded credentials")
|
|
@@ -10325,16 +10504,16 @@ async function resolveAndUploadMedia(api, ref, expectedKind, transport) {
|
|
|
10325
10504
|
return uploadResolvedMedia(api, media);
|
|
10326
10505
|
}
|
|
10327
10506
|
__name(resolveAndUploadMedia, "resolveAndUploadMedia");
|
|
10328
|
-
var TikTokInput =
|
|
10329
|
-
taskId:
|
|
10330
|
-
advertiserId:
|
|
10331
|
-
pixelId:
|
|
10332
|
-
landingUrl:
|
|
10333
|
-
angleIndexes:
|
|
10507
|
+
var TikTokInput = z50.object({
|
|
10508
|
+
taskId: z50.string().min(1),
|
|
10509
|
+
advertiserId: z50.string().min(1),
|
|
10510
|
+
pixelId: z50.string().min(1).optional(),
|
|
10511
|
+
landingUrl: z50.string().url().optional(),
|
|
10512
|
+
angleIndexes: z50.array(z50.number().int().nonnegative()).optional(),
|
|
10334
10513
|
campaignConfig: McpCampaignConfig.optional(),
|
|
10335
10514
|
/** Override the ad's description (TikTok's body copy). If omitted,
|
|
10336
10515
|
* ProductMaker uses the auto-generated copy attached to the task. */
|
|
10337
|
-
primaryText:
|
|
10516
|
+
primaryText: z50.string().trim().min(1).max(500).optional()
|
|
10338
10517
|
});
|
|
10339
10518
|
function registerPublishTools(server, api, opts = {}) {
|
|
10340
10519
|
const transport = opts.transport ?? "http";
|
|
@@ -10545,13 +10724,13 @@ function toTikTokCampaignConfig(cfg) {
|
|
|
10545
10724
|
__name(toTikTokCampaignConfig, "toTikTokCampaignConfig");
|
|
10546
10725
|
|
|
10547
10726
|
// src/tools/campaigns.ts
|
|
10548
|
-
import { z as
|
|
10549
|
-
var MetaStatusInput =
|
|
10550
|
-
campaignId:
|
|
10727
|
+
import { z as z51 } from "zod";
|
|
10728
|
+
var MetaStatusInput = z51.object({
|
|
10729
|
+
campaignId: z51.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
|
|
10551
10730
|
});
|
|
10552
|
-
var TikTokStatusInput =
|
|
10553
|
-
campaignId:
|
|
10554
|
-
advertiserId:
|
|
10731
|
+
var TikTokStatusInput = z51.object({
|
|
10732
|
+
campaignId: z51.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
|
|
10733
|
+
advertiserId: z51.string().min(1).describe("The same advertiserId used to publish the campaign.")
|
|
10555
10734
|
});
|
|
10556
10735
|
function registerCampaignTools(server, api) {
|
|
10557
10736
|
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) => {
|
|
@@ -10648,10 +10827,10 @@ function renderTikTokStatusMarkdown(r) {
|
|
|
10648
10827
|
__name(renderTikTokStatusMarkdown, "renderTikTokStatusMarkdown");
|
|
10649
10828
|
|
|
10650
10829
|
// ../../packages/product-economics/src/product-costing.ts
|
|
10651
|
-
import { z as
|
|
10830
|
+
import { z as z53 } from "zod";
|
|
10652
10831
|
|
|
10653
10832
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
10654
|
-
import { z as
|
|
10833
|
+
import { z as z52 } from "zod";
|
|
10655
10834
|
|
|
10656
10835
|
// ../../packages/product-economics/src/product-costing-shipping-references.json
|
|
10657
10836
|
var product_costing_shipping_references_default = {
|
|
@@ -10715,21 +10894,21 @@ var product_costing_shipping_references_default = {
|
|
|
10715
10894
|
};
|
|
10716
10895
|
|
|
10717
10896
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
10718
|
-
var ShippingReferenceSchema =
|
|
10719
|
-
reference:
|
|
10720
|
-
min:
|
|
10721
|
-
max:
|
|
10722
|
-
updatedAt:
|
|
10723
|
-
confidence:
|
|
10897
|
+
var ShippingReferenceSchema = z52.object({
|
|
10898
|
+
reference: z52.number().positive(),
|
|
10899
|
+
min: z52.number().nonnegative(),
|
|
10900
|
+
max: z52.number().positive(),
|
|
10901
|
+
updatedAt: z52.string().date(),
|
|
10902
|
+
confidence: z52.enum([
|
|
10724
10903
|
"high",
|
|
10725
10904
|
"medium",
|
|
10726
10905
|
"medium-low"
|
|
10727
10906
|
]),
|
|
10728
|
-
sources:
|
|
10907
|
+
sources: z52.array(z52.string().url().startsWith("https://")).min(1).max(3)
|
|
10729
10908
|
}).superRefine(({ reference, min, max }, context) => {
|
|
10730
10909
|
if (min > reference || reference > max) {
|
|
10731
10910
|
context.addIssue({
|
|
10732
|
-
code:
|
|
10911
|
+
code: z52.ZodIssueCode.custom,
|
|
10733
10912
|
message: "shipping_reference_outside_range",
|
|
10734
10913
|
path: [
|
|
10735
10914
|
"reference"
|
|
@@ -10737,7 +10916,7 @@ var ShippingReferenceSchema = z50.object({
|
|
|
10737
10916
|
});
|
|
10738
10917
|
}
|
|
10739
10918
|
});
|
|
10740
|
-
var SHIPPING_REFERENCES =
|
|
10919
|
+
var SHIPPING_REFERENCES = z52.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
|
|
10741
10920
|
var PRODUCT_COSTING_COUNTRY_CODES = Object.freeze(Object.keys(SHIPPING_REFERENCES));
|
|
10742
10921
|
var SPANISH_LOCALE_COUNTRIES = /* @__PURE__ */ new Set([
|
|
10743
10922
|
"MX",
|
|
@@ -10919,18 +11098,18 @@ function getProductCostingCountryName(country) {
|
|
|
10919
11098
|
__name(getProductCostingCountryName, "getProductCostingCountryName");
|
|
10920
11099
|
|
|
10921
11100
|
// ../../packages/product-economics/src/product-costing.ts
|
|
10922
|
-
var ProductCostingInputSchema =
|
|
10923
|
-
country:
|
|
10924
|
-
supplierCost:
|
|
10925
|
-
shippingCost:
|
|
10926
|
-
returnRate:
|
|
10927
|
-
cancellationRate:
|
|
10928
|
-
adsManagerCpa:
|
|
10929
|
-
desiredMargin:
|
|
11101
|
+
var ProductCostingInputSchema = z53.object({
|
|
11102
|
+
country: z53.string().refine(isProductCostingCountryCode, "unsupported_country"),
|
|
11103
|
+
supplierCost: z53.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
|
|
11104
|
+
shippingCost: z53.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
|
|
11105
|
+
returnRate: z53.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
11106
|
+
cancellationRate: z53.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
11107
|
+
adsManagerCpa: z53.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
|
|
11108
|
+
desiredMargin: z53.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
|
|
10930
11109
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
10931
11110
|
if (returnRate + cancellationRate >= 1) {
|
|
10932
11111
|
context.addIssue({
|
|
10933
|
-
code:
|
|
11112
|
+
code: z53.ZodIssueCode.custom,
|
|
10934
11113
|
message: "delivery_rate_positive",
|
|
10935
11114
|
path: [
|
|
10936
11115
|
"cancellationRate"
|
|
@@ -10997,14 +11176,14 @@ function calculateProductCosting(rawInput) {
|
|
|
10997
11176
|
__name(calculateProductCosting, "calculateProductCosting");
|
|
10998
11177
|
|
|
10999
11178
|
// src/tools/product-costing.ts
|
|
11000
|
-
import { z as
|
|
11001
|
-
var optionalAmount = /* @__PURE__ */ __name((description) =>
|
|
11002
|
-
var optionalPercent = /* @__PURE__ */ __name((description) =>
|
|
11003
|
-
var ProductCostingToolInput =
|
|
11004
|
-
country:
|
|
11179
|
+
import { z as z54 } from "zod";
|
|
11180
|
+
var optionalAmount = /* @__PURE__ */ __name((description) => z54.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
|
|
11181
|
+
var optionalPercent = /* @__PURE__ */ __name((description) => z54.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
|
|
11182
|
+
var ProductCostingToolInput = z54.object({
|
|
11183
|
+
country: z54.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
|
|
11005
11184
|
message: "Pa\xEDs no soportado por ProductMaker."
|
|
11006
11185
|
}).optional().describe("C\xF3digo ISO-2 de cualquiera de los pa\xEDses disponibles en ProductMaker. Omite para Colombia."),
|
|
11007
|
-
supplierCost:
|
|
11186
|
+
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."),
|
|
11008
11187
|
supplierCostCop: optionalAmount("Alias anterior para costo del proveedor en Colombia. Prefiere supplierCost."),
|
|
11009
11188
|
shippingCost: optionalAmount("Costo de un env\xEDo en la moneda del pa\xEDs. Omite para usar el valor gu\xEDa vigente."),
|
|
11010
11189
|
shippingCostCop: optionalAmount("Alias anterior para flete en Colombia. Prefiere shippingCost."),
|
|
@@ -11136,7 +11315,7 @@ function formatPercent(value) {
|
|
|
11136
11315
|
__name(formatPercent, "formatPercent");
|
|
11137
11316
|
|
|
11138
11317
|
// src/tools/product-research.ts
|
|
11139
|
-
import { z as
|
|
11318
|
+
import { z as z55 } from "zod";
|
|
11140
11319
|
|
|
11141
11320
|
// src/tools/product-research-idempotency.ts
|
|
11142
11321
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -11252,17 +11431,17 @@ var TERMINAL2 = /* @__PURE__ */ new Set([
|
|
|
11252
11431
|
]);
|
|
11253
11432
|
var POLL_INTERVAL_MS = 5e3;
|
|
11254
11433
|
var MAX_WAIT_SECONDS = 120;
|
|
11255
|
-
var FindInput =
|
|
11434
|
+
var FindInput = z55.object({
|
|
11256
11435
|
targetCountry: ResearchTargetCountryEnum.describe("Pa\xEDs objetivo. S\xF3lo CO, MX, PE, EC y CL tienen cobertura de investigaci\xF3n."),
|
|
11257
11436
|
categoryKey: ResearchCategoryEnum.default("auto").describe('Categor\xEDa a investigar. Usa "auto" para que la IA elija seg\xFAn el mercado.'),
|
|
11258
|
-
confirmed:
|
|
11259
|
-
waitSeconds:
|
|
11437
|
+
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.`),
|
|
11438
|
+
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.")
|
|
11260
11439
|
});
|
|
11261
|
-
var GetInput =
|
|
11262
|
-
runId:
|
|
11440
|
+
var GetInput = z55.object({
|
|
11441
|
+
runId: z55.string().uuid().describe("ID del run devuelto por find_winning_products.")
|
|
11263
11442
|
});
|
|
11264
|
-
var ListInput =
|
|
11265
|
-
limit:
|
|
11443
|
+
var ListInput = z55.object({
|
|
11444
|
+
limit: z55.number().int().min(1).max(20).default(10)
|
|
11266
11445
|
});
|
|
11267
11446
|
function textResult(text) {
|
|
11268
11447
|
return {
|
|
@@ -11354,14 +11533,14 @@ _Esta llamada no cobr\xF3 cr\xE9ditos: se reutiliz\xF3 una investigaci\xF3n reci
|
|
|
11354
11533
|
__name(registerProductResearchTools, "registerProductResearchTools");
|
|
11355
11534
|
|
|
11356
11535
|
// src/tools/dropshipping-projects.ts
|
|
11357
|
-
import { z as
|
|
11358
|
-
var ProjectId =
|
|
11359
|
-
var DropshippingProjectStatus =
|
|
11536
|
+
import { z as z56 } from "zod";
|
|
11537
|
+
var ProjectId = z56.string().uuid();
|
|
11538
|
+
var DropshippingProjectStatus = z56.enum([
|
|
11360
11539
|
"active",
|
|
11361
11540
|
"completed",
|
|
11362
11541
|
"archived"
|
|
11363
11542
|
]);
|
|
11364
|
-
var DropshippingProjectPhase =
|
|
11543
|
+
var DropshippingProjectPhase = z56.enum([
|
|
11365
11544
|
"research",
|
|
11366
11545
|
"costing",
|
|
11367
11546
|
"media",
|
|
@@ -11370,21 +11549,21 @@ var DropshippingProjectPhase = z54.enum([
|
|
|
11370
11549
|
"advertising",
|
|
11371
11550
|
"complete"
|
|
11372
11551
|
]);
|
|
11373
|
-
var DropshippingRewardMilestone =
|
|
11552
|
+
var DropshippingRewardMilestone = z56.enum([
|
|
11374
11553
|
"product_selected",
|
|
11375
11554
|
"costing_saved",
|
|
11376
11555
|
"product_media_ready",
|
|
11377
11556
|
"creatives_completed",
|
|
11378
11557
|
"campaign_created"
|
|
11379
11558
|
]);
|
|
11380
|
-
var TargetCountry =
|
|
11559
|
+
var TargetCountry = z56.enum([
|
|
11381
11560
|
"CO",
|
|
11382
11561
|
"MX",
|
|
11383
11562
|
"PE",
|
|
11384
11563
|
"EC",
|
|
11385
11564
|
"CL"
|
|
11386
11565
|
]);
|
|
11387
|
-
var ResearchCategory =
|
|
11566
|
+
var ResearchCategory = z56.enum([
|
|
11388
11567
|
"auto",
|
|
11389
11568
|
"automotive",
|
|
11390
11569
|
"kitchen",
|
|
@@ -11399,7 +11578,7 @@ var ResearchCategory = z54.enum([
|
|
|
11399
11578
|
"garden",
|
|
11400
11579
|
"cleaning"
|
|
11401
11580
|
]);
|
|
11402
|
-
var HttpUrl3 =
|
|
11581
|
+
var HttpUrl3 = z56.string().url().max(2e3).refine((value) => {
|
|
11403
11582
|
try {
|
|
11404
11583
|
return [
|
|
11405
11584
|
"http:",
|
|
@@ -11409,46 +11588,46 @@ var HttpUrl3 = z54.string().url().max(2e3).refine((value) => {
|
|
|
11409
11588
|
return false;
|
|
11410
11589
|
}
|
|
11411
11590
|
}, "URL must use HTTP(S)");
|
|
11412
|
-
var ResearchCandidateOrigin =
|
|
11413
|
-
type:
|
|
11591
|
+
var ResearchCandidateOrigin = z56.object({
|
|
11592
|
+
type: z56.literal("research_candidate"),
|
|
11414
11593
|
runId: ProjectId,
|
|
11415
|
-
candidateSourceId:
|
|
11594
|
+
candidateSourceId: z56.string().min(1).max(160)
|
|
11416
11595
|
});
|
|
11417
|
-
var TaskOrigin =
|
|
11418
|
-
type:
|
|
11596
|
+
var TaskOrigin = z56.object({
|
|
11597
|
+
type: z56.literal("task"),
|
|
11419
11598
|
taskId: ProjectId
|
|
11420
11599
|
});
|
|
11421
|
-
var ManualOrigin =
|
|
11422
|
-
type:
|
|
11600
|
+
var ManualOrigin = z56.object({
|
|
11601
|
+
type: z56.literal("manual"),
|
|
11423
11602
|
clientRequestId: ProjectId,
|
|
11424
|
-
country:
|
|
11603
|
+
country: z56.string().min(2).max(3).optional()
|
|
11425
11604
|
});
|
|
11426
|
-
var StartProjectInput =
|
|
11427
|
-
origin:
|
|
11605
|
+
var StartProjectInput = z56.object({
|
|
11606
|
+
origin: z56.discriminatedUnion("type", [
|
|
11428
11607
|
ResearchCandidateOrigin,
|
|
11429
11608
|
TaskOrigin,
|
|
11430
11609
|
ManualOrigin
|
|
11431
11610
|
]),
|
|
11432
|
-
productName:
|
|
11611
|
+
productName: z56.string().trim().min(1).max(240).optional(),
|
|
11433
11612
|
productUrl: HttpUrl3.optional(),
|
|
11434
|
-
country:
|
|
11435
|
-
languageCode:
|
|
11613
|
+
country: z56.string().min(2).max(3).optional(),
|
|
11614
|
+
languageCode: z56.string().min(2).max(16).optional()
|
|
11436
11615
|
}).strict();
|
|
11437
|
-
var CostProjectInput =
|
|
11616
|
+
var CostProjectInput = z56.object({
|
|
11438
11617
|
projectId: ProjectId,
|
|
11439
|
-
version:
|
|
11440
|
-
country:
|
|
11441
|
-
supplierCost:
|
|
11442
|
-
shippingCost:
|
|
11443
|
-
returnRate:
|
|
11444
|
-
cancellationRate:
|
|
11445
|
-
adsManagerCpa:
|
|
11446
|
-
desiredMargin:
|
|
11447
|
-
});
|
|
11448
|
-
var GuideInput =
|
|
11618
|
+
version: z56.number().int().positive(),
|
|
11619
|
+
country: z56.string().trim().min(2).max(3),
|
|
11620
|
+
supplierCost: z56.number().finite().positive(),
|
|
11621
|
+
shippingCost: z56.number().finite().nonnegative(),
|
|
11622
|
+
returnRate: z56.number().finite().min(0).lt(1),
|
|
11623
|
+
cancellationRate: z56.number().finite().min(0).lt(1),
|
|
11624
|
+
adsManagerCpa: z56.number().finite().nonnegative(),
|
|
11625
|
+
desiredMargin: z56.number().finite().min(0).lt(1)
|
|
11626
|
+
});
|
|
11627
|
+
var GuideInput = z56.object({
|
|
11449
11628
|
projectId: ProjectId.optional()
|
|
11450
11629
|
}).strict();
|
|
11451
|
-
var DropshippingProjectNextAction =
|
|
11630
|
+
var DropshippingProjectNextAction = z56.discriminatedUnion("kind", [
|
|
11452
11631
|
"cost_product",
|
|
11453
11632
|
"provide_product_media",
|
|
11454
11633
|
"generate_creatives",
|
|
@@ -11462,78 +11641,78 @@ var DropshippingProjectNextAction = z54.discriminatedUnion("kind", [
|
|
|
11462
11641
|
"create_meta_campaign",
|
|
11463
11642
|
"repair_meta_campaign",
|
|
11464
11643
|
"review_paused_campaign"
|
|
11465
|
-
].map((kind) =>
|
|
11466
|
-
kind:
|
|
11467
|
-
href:
|
|
11644
|
+
].map((kind) => z56.object({
|
|
11645
|
+
kind: z56.literal(kind),
|
|
11646
|
+
href: z56.string().min(1)
|
|
11468
11647
|
})));
|
|
11469
|
-
var CostingInputSchema =
|
|
11470
|
-
country:
|
|
11471
|
-
supplierCost:
|
|
11472
|
-
shippingCost:
|
|
11473
|
-
returnRate:
|
|
11474
|
-
cancellationRate:
|
|
11475
|
-
adsManagerCpa:
|
|
11476
|
-
desiredMargin:
|
|
11648
|
+
var CostingInputSchema = z56.object({
|
|
11649
|
+
country: z56.string().min(2).max(3),
|
|
11650
|
+
supplierCost: z56.number().finite().positive(),
|
|
11651
|
+
shippingCost: z56.number().finite().nonnegative(),
|
|
11652
|
+
returnRate: z56.number().finite().min(0).lt(1),
|
|
11653
|
+
cancellationRate: z56.number().finite().min(0).lt(1),
|
|
11654
|
+
adsManagerCpa: z56.number().finite().nonnegative(),
|
|
11655
|
+
desiredMargin: z56.number().finite().min(0).lt(1)
|
|
11477
11656
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
11478
11657
|
if (returnRate + cancellationRate >= 1) context.addIssue({
|
|
11479
|
-
code:
|
|
11658
|
+
code: z56.ZodIssueCode.custom,
|
|
11480
11659
|
path: [
|
|
11481
11660
|
"cancellationRate"
|
|
11482
11661
|
]
|
|
11483
11662
|
});
|
|
11484
11663
|
});
|
|
11485
|
-
var CostingResultSchema =
|
|
11486
|
-
country:
|
|
11487
|
-
deliveryRate:
|
|
11488
|
-
adjustedShippingCost:
|
|
11489
|
-
adjustedCpa:
|
|
11490
|
-
totalCost:
|
|
11491
|
-
suggestedPrice:
|
|
11492
|
-
baseCommercialPrice:
|
|
11493
|
-
minimumSafePrice:
|
|
11494
|
-
commercialPrice:
|
|
11495
|
-
comparisonPrice:
|
|
11496
|
-
estimatedProfit:
|
|
11497
|
-
actualMargin:
|
|
11498
|
-
breakEvenAdsManagerCpa:
|
|
11499
|
-
wasPriceAutoAdjusted:
|
|
11500
|
-
profitabilityStatus:
|
|
11501
|
-
});
|
|
11502
|
-
var RewardProgressSchema =
|
|
11503
|
-
campaignKey:
|
|
11504
|
-
totalCredits:
|
|
11505
|
-
earnedCredits:
|
|
11506
|
-
milestones:
|
|
11664
|
+
var CostingResultSchema = z56.object({
|
|
11665
|
+
country: z56.string().min(2).max(3),
|
|
11666
|
+
deliveryRate: z56.number().finite(),
|
|
11667
|
+
adjustedShippingCost: z56.number().finite(),
|
|
11668
|
+
adjustedCpa: z56.number().finite(),
|
|
11669
|
+
totalCost: z56.number().finite(),
|
|
11670
|
+
suggestedPrice: z56.number().finite(),
|
|
11671
|
+
baseCommercialPrice: z56.number().finite(),
|
|
11672
|
+
minimumSafePrice: z56.number().finite(),
|
|
11673
|
+
commercialPrice: z56.number().finite(),
|
|
11674
|
+
comparisonPrice: z56.number().finite(),
|
|
11675
|
+
estimatedProfit: z56.number().finite(),
|
|
11676
|
+
actualMargin: z56.number().finite(),
|
|
11677
|
+
breakEvenAdsManagerCpa: z56.number().finite(),
|
|
11678
|
+
wasPriceAutoAdjusted: z56.boolean(),
|
|
11679
|
+
profitabilityStatus: z56.literal("healthy")
|
|
11680
|
+
});
|
|
11681
|
+
var RewardProgressSchema = z56.object({
|
|
11682
|
+
campaignKey: z56.literal("dropshipping-launch-v1"),
|
|
11683
|
+
totalCredits: z56.number().int().positive(),
|
|
11684
|
+
earnedCredits: z56.number().int().nonnegative(),
|
|
11685
|
+
milestones: z56.array(z56.object({
|
|
11507
11686
|
milestone: DropshippingRewardMilestone,
|
|
11508
|
-
credits:
|
|
11509
|
-
available:
|
|
11510
|
-
earned:
|
|
11687
|
+
credits: z56.number().int().positive(),
|
|
11688
|
+
available: z56.boolean().optional(),
|
|
11689
|
+
earned: z56.boolean(),
|
|
11511
11690
|
grantId: ProjectId.nullable(),
|
|
11512
|
-
grantedAt:
|
|
11691
|
+
grantedAt: z56.string().datetime().nullable()
|
|
11513
11692
|
})).length(DropshippingRewardMilestone.options.length)
|
|
11514
11693
|
});
|
|
11515
|
-
var ProjectResponseSchema =
|
|
11694
|
+
var ProjectResponseSchema = z56.object({
|
|
11516
11695
|
id: ProjectId,
|
|
11517
11696
|
taskId: ProjectId.nullable(),
|
|
11518
11697
|
status: DropshippingProjectStatus,
|
|
11519
|
-
originKey:
|
|
11698
|
+
originKey: z56.string().min(1),
|
|
11520
11699
|
researchRunId: ProjectId.nullable(),
|
|
11521
|
-
candidateSourceId:
|
|
11522
|
-
productName:
|
|
11700
|
+
candidateSourceId: z56.string().nullable(),
|
|
11701
|
+
productName: z56.string().nullable(),
|
|
11523
11702
|
productUrl: HttpUrl3.nullable(),
|
|
11524
11703
|
referenceImageUrl: HttpUrl3.nullable(),
|
|
11525
|
-
targetCountry:
|
|
11526
|
-
languageCode:
|
|
11527
|
-
hasReferenceImage:
|
|
11704
|
+
targetCountry: z56.string().nullable(),
|
|
11705
|
+
languageCode: z56.string().nullable(),
|
|
11706
|
+
hasReferenceImage: z56.boolean(),
|
|
11528
11707
|
costingInput: CostingInputSchema.nullable(),
|
|
11529
11708
|
costingResult: CostingResultSchema.nullable(),
|
|
11530
|
-
costingVersion:
|
|
11531
|
-
version:
|
|
11709
|
+
costingVersion: z56.number().int().positive().nullable(),
|
|
11710
|
+
version: z56.number().int().positive(),
|
|
11532
11711
|
phase: DropshippingProjectPhase,
|
|
11533
11712
|
nextAction: DropshippingProjectNextAction.nullable(),
|
|
11534
11713
|
rewards: RewardProgressSchema,
|
|
11535
|
-
createdAt:
|
|
11536
|
-
updatedAt:
|
|
11714
|
+
createdAt: z56.string().datetime(),
|
|
11715
|
+
updatedAt: z56.string().datetime()
|
|
11537
11716
|
}).passthrough();
|
|
11538
11717
|
function projectText(raw) {
|
|
11539
11718
|
const project = ProjectResponseSchema.parse(raw);
|
|
@@ -11631,6 +11810,7 @@ function createServer(opts) {
|
|
|
11631
11810
|
registerCreativeVariantTools(server, api);
|
|
11632
11811
|
registerScriptReviewTools(server, api);
|
|
11633
11812
|
registerConnectionTools(server, api);
|
|
11813
|
+
registerActorTools(server, api);
|
|
11634
11814
|
registerPublishTools(server, api, {
|
|
11635
11815
|
transport
|
|
11636
11816
|
});
|