@productmaker/mcp 2.0.0 → 2.2.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/dist/http.js +901 -345
- package/dist/stdio.js +901 -345
- package/package.json +2 -2
package/dist/http.js
CHANGED
|
@@ -76,6 +76,17 @@ var ApiClient = class {
|
|
|
76
76
|
});
|
|
77
77
|
return this.parse(res);
|
|
78
78
|
}
|
|
79
|
+
async putJson(path3, body) {
|
|
80
|
+
const res = await fetch(`${this.baseUrl}${path3}`, {
|
|
81
|
+
method: "PUT",
|
|
82
|
+
headers: {
|
|
83
|
+
...this.authHeaders(),
|
|
84
|
+
"Content-Type": "application/json"
|
|
85
|
+
},
|
|
86
|
+
body: JSON.stringify(body)
|
|
87
|
+
});
|
|
88
|
+
return this.parse(res);
|
|
89
|
+
}
|
|
79
90
|
async deleteJson(path3) {
|
|
80
91
|
const res = await fetch(`${this.baseUrl}${path3}`, {
|
|
81
92
|
method: "DELETE",
|
|
@@ -142,10 +153,10 @@ var ApiClient = class {
|
|
|
142
153
|
};
|
|
143
154
|
|
|
144
155
|
// src/index.ts
|
|
145
|
-
var MCP_VERSION = "2.
|
|
156
|
+
var MCP_VERSION = "2.2.0";
|
|
146
157
|
|
|
147
158
|
// src/tools/tasks.ts
|
|
148
|
-
import { z as
|
|
159
|
+
import { z as z40 } from "zod";
|
|
149
160
|
|
|
150
161
|
// src/resolve-image.ts
|
|
151
162
|
import { lookup as dnsLookup } from "dns/promises";
|
|
@@ -1392,6 +1403,8 @@ var INGEST_ALLOWED_IMAGE_MIME_TYPES = [
|
|
|
1392
1403
|
var PRODUCT_CONTEXT_MAX_CHARS = 3e3;
|
|
1393
1404
|
var INGEST_ALLOWED_IMAGE_MIME_TYPE_SET = new Set(INGEST_ALLOWED_IMAGE_MIME_TYPES);
|
|
1394
1405
|
var IngestOptions = z13.object({
|
|
1406
|
+
/** Optional owner-scoped guided journey context. Never bypasses ingest gates. */
|
|
1407
|
+
dropshippingProjectId: z13.string().uuid().optional(),
|
|
1395
1408
|
languageCode: LanguageCodeSchema.default("es-CO"),
|
|
1396
1409
|
ugcFormat: VideoFormat.default("single_15s"),
|
|
1397
1410
|
country: CountrySchema,
|
|
@@ -1411,11 +1424,12 @@ var IngestOptions = z13.object({
|
|
|
1411
1424
|
* shorter than the generated angle count leaves the remaining positions
|
|
1412
1425
|
* auto-selected. Empty/omitted → all angles auto-select as before. */
|
|
1413
1426
|
angleNarratives: z13.array(NarrativeStyle).max(3).optional(),
|
|
1414
|
-
//
|
|
1415
|
-
//
|
|
1427
|
+
// 480p is the recommended default for every tier (flipped back 2026-07-30 — the
|
|
1428
|
+
// 720p premium is real but small, and users on the entry plan were defaulting
|
|
1429
|
+
// into it while burning half a month of credits on one video).
|
|
1416
1430
|
// This Zod default is what callers that omit the field (MCP/API) actually get,
|
|
1417
|
-
// so it must agree with resolveSingleCallAwareResolution's unset fallback (
|
|
1418
|
-
videoResolution: VideoResolution.default("
|
|
1431
|
+
// so it must agree with resolveSingleCallAwareResolution's unset fallback (480p).
|
|
1432
|
+
videoResolution: VideoResolution.default("480p"),
|
|
1419
1433
|
generateAdCopy: z13.boolean().default(true),
|
|
1420
1434
|
generateAiMusic: z13.boolean().optional(),
|
|
1421
1435
|
voiceId: VoiceId.optional(),
|
|
@@ -1530,9 +1544,9 @@ var IngestResumePayload = z13.object({
|
|
|
1530
1544
|
videoModel: VideoModel.default("veo"),
|
|
1531
1545
|
videoTier: VideoTier.optional(),
|
|
1532
1546
|
narrativeStyle: NarrativeStyle.default("review"),
|
|
1533
|
-
//
|
|
1534
|
-
// with IngestOptions.videoResolution and resolveSingleCallAwareResolution.
|
|
1535
|
-
videoResolution: VideoResolution.default("
|
|
1547
|
+
// 480p is the recommended default for every tier (flipped back 2026-07-30) — keep in
|
|
1548
|
+
// sync with IngestOptions.videoResolution and resolveSingleCallAwareResolution.
|
|
1549
|
+
videoResolution: VideoResolution.default("480p"),
|
|
1536
1550
|
pauseForVideoScriptReview: z13.boolean().optional(),
|
|
1537
1551
|
generateAiMusic: z13.boolean().optional(),
|
|
1538
1552
|
scriptNotes: z13.string().max(2e3).optional(),
|
|
@@ -4991,16 +5005,37 @@ var DEFAULT_PRICING = {
|
|
|
4991
5005
|
ourCostCents: 0.15,
|
|
4992
5006
|
userPriceCents: 0.3
|
|
4993
5007
|
},
|
|
4994
|
-
// Video QA —
|
|
4995
|
-
//
|
|
4996
|
-
//
|
|
4997
|
-
// (
|
|
4998
|
-
//
|
|
4999
|
-
//
|
|
5008
|
+
// Video QA — anatomy. THREE keys, and they do NOT share a unit. Getting this wrong
|
|
5009
|
+
// over-charges by ~42x, which is exactly what a first pass at the 2026-07-30 change did.
|
|
5010
|
+
//
|
|
5011
|
+
// (a) The arm-scan's per-FRAME counting sweep. STILL LIVE on the whole-video concat path
|
|
5012
|
+
// (`scanClipArms` at apps/api/src/workers/core/video-concat-core.ts), which bills
|
|
5013
|
+
// units:1 PER SAMPLED FRAME, up to ARM_SCAN_FRAME_CEILING. Rate must stay per-frame.
|
|
5014
|
+
// Sized above the 30d prod average (10303 rows => 0.132c user/frame).
|
|
5000
5015
|
"openrouter:video-anatomy-arm-scan": {
|
|
5001
5016
|
ourCostCents: 0.1,
|
|
5002
5017
|
userPriceCents: 0.2
|
|
5003
5018
|
},
|
|
5019
|
+
// (b) The frame JUDGE that replaced the sweep on the `analyzeVideoClipDJ` path: ALL frames
|
|
5020
|
+
// in ONE multi-image call, so a unit is one call. MEASURED, not estimated —
|
|
5021
|
+
// usage.cost on a real 16-frame judgement at 768px is $0.0417 => 4.17c our. The table is
|
|
5022
|
+
// the FALLBACK used when OpenRouter omits usage.cost, so it must sit at or above that.
|
|
5023
|
+
// Billed PER FRAME (units = frames in the call), like the sweep and for the same reason:
|
|
5024
|
+
// the cost scales with IMAGES and QA_FRAME_JUDGE_FRAMES is env-overridable, so a per-CALL
|
|
5025
|
+
// unit would freeze the fallback charge and the credit-gate quote at the cap the rate
|
|
5026
|
+
// happened to be measured at. Measured $0.0417 over 16 frames => 0.26c/frame; priced at
|
|
5027
|
+
// 0.32c for headroom (n=1 on one clip, and the 1.9k reasoning-token share moves with
|
|
5028
|
+
// QA_DETECTOR_EFFORT). Re-derive from 30d of BillingEntry once this key has volume.
|
|
5029
|
+
"openrouter:video-qa-anatomy-frame-judge": {
|
|
5030
|
+
ourCostCents: 0.32,
|
|
5031
|
+
userPriceCents: 0.64
|
|
5032
|
+
},
|
|
5033
|
+
// (c) Tracing confirmation of a frames-only finding: 3 stills + a 3s 480p slice, one call,
|
|
5034
|
+
// no 16-image prompt — materially cheaper than the judgement it verifies.
|
|
5035
|
+
"openrouter:video-qa-anatomy-tracing-confirm": {
|
|
5036
|
+
ourCostCents: 1.5,
|
|
5037
|
+
userPriceCents: 3
|
|
5038
|
+
},
|
|
5004
5039
|
// Physics confirmation — skeptical second opinion on D+J floating-object flags
|
|
5005
5040
|
// (Gemini 3 Flash Preview, ~3 frames per flagged moment, 1 call). Aligned with other vision-QA keys.
|
|
5006
5041
|
"openrouter:video-physics-confirmation": {
|
|
@@ -6546,17 +6581,216 @@ var RegenerateEditRequestSchema = z36.object({
|
|
|
6546
6581
|
comment: z36.string().min(1).max(2e3)
|
|
6547
6582
|
});
|
|
6548
6583
|
|
|
6549
|
-
// ../../packages/types/src/
|
|
6584
|
+
// ../../packages/types/src/dropshipping-project.ts
|
|
6550
6585
|
import { z as z37 } from "zod";
|
|
6551
|
-
var
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6586
|
+
var DropshippingProjectStatusSchema = z37.enum([
|
|
6587
|
+
"active",
|
|
6588
|
+
"completed",
|
|
6589
|
+
"archived"
|
|
6590
|
+
]);
|
|
6591
|
+
var DROPSHIPPING_LAUNCH_REWARD_CAMPAIGN = "dropshipping-launch-v1";
|
|
6592
|
+
var DropshippingRewardMilestoneSchema = z37.enum([
|
|
6593
|
+
"product_selected",
|
|
6594
|
+
"costing_saved",
|
|
6595
|
+
"product_media_ready",
|
|
6596
|
+
"creatives_completed",
|
|
6597
|
+
"campaign_created"
|
|
6598
|
+
]);
|
|
6599
|
+
var DROPSHIPPING_LAUNCH_REWARD_CREDITS = {
|
|
6600
|
+
product_selected: 50,
|
|
6601
|
+
costing_saved: 50,
|
|
6602
|
+
product_media_ready: 50,
|
|
6603
|
+
creatives_completed: 150,
|
|
6604
|
+
campaign_created: 200
|
|
6605
|
+
};
|
|
6606
|
+
var DROPSHIPPING_LAUNCH_REWARD_TOTAL_CREDITS = Object.values(DROPSHIPPING_LAUNCH_REWARD_CREDITS).reduce((sum, credits) => sum + credits, 0);
|
|
6607
|
+
var DropshippingProjectRewardMilestoneStateSchema = z37.object({
|
|
6608
|
+
milestone: DropshippingRewardMilestoneSchema,
|
|
6609
|
+
credits: z37.number().int().positive(),
|
|
6610
|
+
available: z37.boolean().optional(),
|
|
6611
|
+
earned: z37.boolean(),
|
|
6612
|
+
grantId: z37.string().uuid().nullable(),
|
|
6613
|
+
grantedAt: z37.string().datetime().nullable()
|
|
6614
|
+
});
|
|
6615
|
+
var DropshippingProjectRewardProgressSchema = z37.object({
|
|
6616
|
+
campaignKey: z37.literal(DROPSHIPPING_LAUNCH_REWARD_CAMPAIGN),
|
|
6617
|
+
totalCredits: z37.number().int().positive(),
|
|
6618
|
+
earnedCredits: z37.number().int().nonnegative(),
|
|
6619
|
+
milestones: z37.array(DropshippingProjectRewardMilestoneStateSchema).length(DropshippingRewardMilestoneSchema.options.length)
|
|
6620
|
+
});
|
|
6621
|
+
var DropshippingProjectPhaseSchema = z37.enum([
|
|
6622
|
+
"research",
|
|
6623
|
+
"costing",
|
|
6624
|
+
"media",
|
|
6625
|
+
"generating",
|
|
6626
|
+
"storefront",
|
|
6627
|
+
"advertising",
|
|
6628
|
+
"complete"
|
|
6629
|
+
]);
|
|
6630
|
+
var DropshippingProjectCostingInputSchema = z37.object({
|
|
6631
|
+
country: z37.string().min(2).max(3),
|
|
6632
|
+
supplierCost: z37.number().finite().positive(),
|
|
6633
|
+
shippingCost: z37.number().finite().nonnegative(),
|
|
6634
|
+
returnRate: z37.number().finite().min(0).lt(1),
|
|
6635
|
+
cancellationRate: z37.number().finite().min(0).lt(1),
|
|
6636
|
+
adsManagerCpa: z37.number().finite().nonnegative(),
|
|
6637
|
+
desiredMargin: z37.number().finite().min(0).lt(1)
|
|
6638
|
+
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
6639
|
+
if (returnRate + cancellationRate >= 1) {
|
|
6640
|
+
context.addIssue({
|
|
6641
|
+
code: z37.ZodIssueCode.custom,
|
|
6642
|
+
path: [
|
|
6643
|
+
"cancellationRate"
|
|
6644
|
+
],
|
|
6645
|
+
message: "delivery_rate_positive"
|
|
6646
|
+
});
|
|
6647
|
+
}
|
|
6648
|
+
});
|
|
6649
|
+
var DropshippingProjectCostingResultSchema = z37.object({
|
|
6650
|
+
country: z37.string().min(2).max(3),
|
|
6651
|
+
deliveryRate: z37.number().finite(),
|
|
6652
|
+
adjustedShippingCost: z37.number().finite(),
|
|
6653
|
+
adjustedCpa: z37.number().finite(),
|
|
6654
|
+
totalCost: z37.number().finite(),
|
|
6655
|
+
suggestedPrice: z37.number().finite(),
|
|
6656
|
+
baseCommercialPrice: z37.number().finite(),
|
|
6657
|
+
minimumSafePrice: z37.number().finite(),
|
|
6658
|
+
commercialPrice: z37.number().finite(),
|
|
6659
|
+
comparisonPrice: z37.number().finite(),
|
|
6660
|
+
estimatedProfit: z37.number().finite(),
|
|
6661
|
+
actualMargin: z37.number().finite(),
|
|
6662
|
+
breakEvenAdsManagerCpa: z37.number().finite(),
|
|
6663
|
+
wasPriceAutoAdjusted: z37.boolean(),
|
|
6664
|
+
profitabilityStatus: z37.literal("healthy")
|
|
6665
|
+
});
|
|
6666
|
+
var HttpUrlSchema = z37.string().url().max(2e3).refine((value) => {
|
|
6667
|
+
try {
|
|
6668
|
+
return [
|
|
6669
|
+
"http:",
|
|
6670
|
+
"https:"
|
|
6671
|
+
].includes(new URL(value).protocol);
|
|
6672
|
+
} catch {
|
|
6673
|
+
return false;
|
|
6674
|
+
}
|
|
6675
|
+
}, "Product URL must use HTTP(S)");
|
|
6676
|
+
var ResearchCandidateOriginSchema = z37.object({
|
|
6677
|
+
type: z37.literal("research_candidate"),
|
|
6678
|
+
runId: z37.string().uuid(),
|
|
6679
|
+
candidateSourceId: z37.string().min(1).max(160)
|
|
6680
|
+
});
|
|
6681
|
+
var TaskOriginSchema = z37.object({
|
|
6682
|
+
type: z37.literal("task"),
|
|
6683
|
+
taskId: z37.string().uuid()
|
|
6684
|
+
});
|
|
6685
|
+
var ManualOriginSchema = z37.object({
|
|
6686
|
+
type: z37.literal("manual"),
|
|
6687
|
+
clientRequestId: z37.string().uuid(),
|
|
6688
|
+
country: z37.string().min(2).max(3).optional()
|
|
6689
|
+
});
|
|
6690
|
+
var CreateDropshippingProjectRequestSchema = z37.object({
|
|
6691
|
+
origin: z37.discriminatedUnion("type", [
|
|
6692
|
+
ResearchCandidateOriginSchema,
|
|
6693
|
+
TaskOriginSchema,
|
|
6694
|
+
ManualOriginSchema
|
|
6695
|
+
]),
|
|
6696
|
+
productName: z37.string().trim().min(1).max(240).optional(),
|
|
6697
|
+
productUrl: HttpUrlSchema.optional(),
|
|
6698
|
+
country: z37.string().min(2).max(3).optional(),
|
|
6699
|
+
languageCode: z37.string().min(2).max(16).optional()
|
|
6700
|
+
}).strict();
|
|
6701
|
+
var SaveDropshippingProjectCostingRequestSchema = z37.object({
|
|
6702
|
+
input: DropshippingProjectCostingInputSchema,
|
|
6703
|
+
version: z37.number().int().positive()
|
|
6704
|
+
}).strict();
|
|
6705
|
+
var DropshippingProjectNextActionSchema = z37.discriminatedUnion("kind", [
|
|
6706
|
+
z37.object({
|
|
6707
|
+
kind: z37.literal("cost_product"),
|
|
6708
|
+
href: z37.string().min(1)
|
|
6709
|
+
}),
|
|
6710
|
+
z37.object({
|
|
6711
|
+
kind: z37.literal("provide_product_media"),
|
|
6712
|
+
href: z37.string().min(1)
|
|
6713
|
+
}),
|
|
6714
|
+
z37.object({
|
|
6715
|
+
kind: z37.literal("generate_creatives"),
|
|
6716
|
+
href: z37.string().min(1)
|
|
6717
|
+
}),
|
|
6718
|
+
z37.object({
|
|
6719
|
+
kind: z37.literal("wait_for_creatives"),
|
|
6720
|
+
href: z37.string().min(1)
|
|
6721
|
+
}),
|
|
6722
|
+
z37.object({
|
|
6723
|
+
kind: z37.literal("repair_creatives"),
|
|
6724
|
+
href: z37.string().min(1)
|
|
6725
|
+
}),
|
|
6726
|
+
z37.object({
|
|
6727
|
+
kind: z37.literal("complete_creative_set"),
|
|
6728
|
+
href: z37.string().min(1)
|
|
6729
|
+
}),
|
|
6730
|
+
z37.object({
|
|
6731
|
+
kind: z37.literal("connect_shopify"),
|
|
6732
|
+
href: z37.string().min(1)
|
|
6733
|
+
}),
|
|
6734
|
+
z37.object({
|
|
6735
|
+
kind: z37.literal("publish_storefront"),
|
|
6736
|
+
href: z37.string().min(1)
|
|
6737
|
+
}),
|
|
6738
|
+
z37.object({
|
|
6739
|
+
kind: z37.literal("wait_for_storefront"),
|
|
6740
|
+
href: z37.string().min(1)
|
|
6741
|
+
}),
|
|
6742
|
+
z37.object({
|
|
6743
|
+
kind: z37.literal("connect_meta"),
|
|
6744
|
+
href: z37.string().min(1)
|
|
6745
|
+
}),
|
|
6746
|
+
z37.object({
|
|
6747
|
+
kind: z37.literal("create_meta_campaign"),
|
|
6748
|
+
href: z37.string().min(1)
|
|
6749
|
+
}),
|
|
6750
|
+
z37.object({
|
|
6751
|
+
kind: z37.literal("repair_meta_campaign"),
|
|
6752
|
+
href: z37.string().min(1)
|
|
6753
|
+
}),
|
|
6754
|
+
z37.object({
|
|
6755
|
+
kind: z37.literal("review_paused_campaign"),
|
|
6756
|
+
href: z37.string().min(1)
|
|
6757
|
+
})
|
|
6758
|
+
]);
|
|
6759
|
+
var DropshippingProjectResponseSchema = z37.object({
|
|
6760
|
+
id: z37.string().uuid(),
|
|
6761
|
+
taskId: z37.string().uuid().nullable(),
|
|
6762
|
+
status: DropshippingProjectStatusSchema,
|
|
6763
|
+
originKey: z37.string().min(1),
|
|
6764
|
+
researchRunId: z37.string().uuid().nullable(),
|
|
6765
|
+
candidateSourceId: z37.string().nullable(),
|
|
6766
|
+
productName: z37.string().nullable(),
|
|
6767
|
+
productUrl: HttpUrlSchema.nullable(),
|
|
6768
|
+
referenceImageUrl: z37.string().url().nullable(),
|
|
6769
|
+
hasReferenceImage: z37.boolean(),
|
|
6770
|
+
targetCountry: z37.string().nullable(),
|
|
6771
|
+
languageCode: z37.string().nullable(),
|
|
6772
|
+
costingInput: DropshippingProjectCostingInputSchema.nullable(),
|
|
6773
|
+
costingResult: DropshippingProjectCostingResultSchema.nullable(),
|
|
6774
|
+
costingVersion: z37.number().int().positive().nullable(),
|
|
6775
|
+
version: z37.number().int().positive(),
|
|
6776
|
+
phase: DropshippingProjectPhaseSchema,
|
|
6777
|
+
nextAction: DropshippingProjectNextActionSchema.nullable(),
|
|
6778
|
+
rewards: DropshippingProjectRewardProgressSchema,
|
|
6779
|
+
createdAt: z37.string().datetime(),
|
|
6780
|
+
updatedAt: z37.string().datetime()
|
|
6781
|
+
});
|
|
6782
|
+
|
|
6783
|
+
// ../../packages/types/src/scene-subject-contract.ts
|
|
6784
|
+
import { z as z38 } from "zod";
|
|
6785
|
+
var SceneSubjectContractSchema = z38.object({
|
|
6786
|
+
maxPeople: z38.number().int().min(1).max(10),
|
|
6787
|
+
minPeople: z38.number().int().min(0).max(10),
|
|
6788
|
+
minDistinctPeople: z38.number().int().min(0).max(10),
|
|
6789
|
+
maxHands: z38.number().int().min(0).max(20)
|
|
6556
6790
|
}).superRefine((value, ctx) => {
|
|
6557
6791
|
if (value.minPeople > value.maxPeople) {
|
|
6558
6792
|
ctx.addIssue({
|
|
6559
|
-
code:
|
|
6793
|
+
code: z38.ZodIssueCode.custom,
|
|
6560
6794
|
path: [
|
|
6561
6795
|
"minPeople"
|
|
6562
6796
|
],
|
|
@@ -6565,7 +6799,7 @@ var SceneSubjectContractSchema = z37.object({
|
|
|
6565
6799
|
}
|
|
6566
6800
|
if (value.minDistinctPeople > value.maxPeople) {
|
|
6567
6801
|
ctx.addIssue({
|
|
6568
|
-
code:
|
|
6802
|
+
code: z38.ZodIssueCode.custom,
|
|
6569
6803
|
path: [
|
|
6570
6804
|
"minDistinctPeople"
|
|
6571
6805
|
],
|
|
@@ -6574,7 +6808,7 @@ var SceneSubjectContractSchema = z37.object({
|
|
|
6574
6808
|
}
|
|
6575
6809
|
if (value.minDistinctPeople > value.minPeople) {
|
|
6576
6810
|
ctx.addIssue({
|
|
6577
|
-
code:
|
|
6811
|
+
code: z38.ZodIssueCode.custom,
|
|
6578
6812
|
path: [
|
|
6579
6813
|
"minDistinctPeople"
|
|
6580
6814
|
],
|
|
@@ -6590,6 +6824,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
6590
6824
|
confirmation: "credit_consuming",
|
|
6591
6825
|
schemaHash: "bab794a9700a8d3f42b97f444a776e7ed4dcd833038c52588f951b50970ff3fa"
|
|
6592
6826
|
},
|
|
6827
|
+
{
|
|
6828
|
+
name: "cost_dropshipping_project",
|
|
6829
|
+
confirmation: "state_change",
|
|
6830
|
+
schemaHash: "746797b5400b170ba3a0704d432afff635794f0a7ae0b9da07b26d49e3b82242"
|
|
6831
|
+
},
|
|
6593
6832
|
{
|
|
6594
6833
|
name: "cost_product",
|
|
6595
6834
|
confirmation: "none",
|
|
@@ -6603,7 +6842,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
6603
6842
|
{
|
|
6604
6843
|
name: "create_product_task",
|
|
6605
6844
|
confirmation: "credit_consuming",
|
|
6606
|
-
schemaHash: "
|
|
6845
|
+
schemaHash: "5b19f7c6dfdc55b0c472df045e24fe73aa134624a141b63342bfbd3bbb0d043b"
|
|
6607
6846
|
},
|
|
6608
6847
|
{
|
|
6609
6848
|
name: "edit_task_draft",
|
|
@@ -6628,17 +6867,17 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
6628
6867
|
{
|
|
6629
6868
|
name: "generate_image_creatives",
|
|
6630
6869
|
confirmation: "credit_consuming",
|
|
6631
|
-
schemaHash: "
|
|
6870
|
+
schemaHash: "82b520e8001e9c57ab0a89d9d66ea5dfc92e6306eed302edfe1005cee5025fbf"
|
|
6632
6871
|
},
|
|
6633
6872
|
{
|
|
6634
6873
|
name: "generate_landing",
|
|
6635
6874
|
confirmation: "credit_consuming",
|
|
6636
|
-
schemaHash: "
|
|
6875
|
+
schemaHash: "23f0c56dc719235e51d16fd61a9703b64fa6d300356ffbf6b21d703324b0bb18"
|
|
6637
6876
|
},
|
|
6638
6877
|
{
|
|
6639
6878
|
name: "generate_video_creative",
|
|
6640
6879
|
confirmation: "credit_consuming",
|
|
6641
|
-
schemaHash: "
|
|
6880
|
+
schemaHash: "79d3ec9310885b482d62c94a27e9c039833c0619d9332a593fe7d32a60ae1388"
|
|
6642
6881
|
},
|
|
6643
6882
|
{
|
|
6644
6883
|
name: "get_meta_campaign_status",
|
|
@@ -6665,6 +6904,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
6665
6904
|
confirmation: "none",
|
|
6666
6905
|
schemaHash: "c96e7042368ba6f241773d26d4a443894b092801d1c81054f3798865138edfba"
|
|
6667
6906
|
},
|
|
6907
|
+
{
|
|
6908
|
+
name: "guide_dropshipping_launch",
|
|
6909
|
+
confirmation: "none",
|
|
6910
|
+
schemaHash: "0e7a1cb5ad5c6681473df5689336edcd7a63b8d68853359982e44bfd98a65db8"
|
|
6911
|
+
},
|
|
6668
6912
|
{
|
|
6669
6913
|
name: "list_meta_ad_accounts",
|
|
6670
6914
|
confirmation: "none",
|
|
@@ -6724,6 +6968,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
6724
6968
|
name: "resolve_pending_action",
|
|
6725
6969
|
confirmation: "credit_consuming",
|
|
6726
6970
|
schemaHash: "3a9f33a26fce63b20bece3a528f8eb16e917dabc8a9ec5f04e746bad84757aa3"
|
|
6971
|
+
},
|
|
6972
|
+
{
|
|
6973
|
+
name: "start_dropshipping_project",
|
|
6974
|
+
confirmation: "state_change",
|
|
6975
|
+
schemaHash: "2b621702e8da8bb894d392060e5e4f677a8dc26f454e991dbb929053715a4f8b"
|
|
6727
6976
|
}
|
|
6728
6977
|
];
|
|
6729
6978
|
var PRODUCTMAKER_MCP_CAPABILITY_NAMES = PRODUCTMAKER_MCP_CAPABILITIES.map((capability) => capability.name);
|
|
@@ -6967,10 +7216,10 @@ async function resolveMediaRef(ref, opts) {
|
|
|
6967
7216
|
__name(resolveMediaRef, "resolveMediaRef");
|
|
6968
7217
|
|
|
6969
7218
|
// src/tools/shared.ts
|
|
6970
|
-
import { z as
|
|
6971
|
-
var Lang =
|
|
6972
|
-
var Country =
|
|
6973
|
-
var LandingStrategyEnum =
|
|
7219
|
+
import { z as z39 } from "zod";
|
|
7220
|
+
var Lang = z39.string().min(2).max(10);
|
|
7221
|
+
var Country = z39.string().min(2).max(3);
|
|
7222
|
+
var LandingStrategyEnum = z39.enum([
|
|
6974
7223
|
"auto",
|
|
6975
7224
|
"classic",
|
|
6976
7225
|
"pain_solution",
|
|
@@ -6979,12 +7228,12 @@ var LandingStrategyEnum = z38.enum([
|
|
|
6979
7228
|
"offer_value",
|
|
6980
7229
|
"lifestyle_identity"
|
|
6981
7230
|
]);
|
|
6982
|
-
var LandingImageFormatEnum =
|
|
7231
|
+
var LandingImageFormatEnum = z39.enum([
|
|
6983
7232
|
"9:16",
|
|
6984
7233
|
"4:5",
|
|
6985
7234
|
"1:1"
|
|
6986
7235
|
]);
|
|
6987
|
-
var VideoPresetEnum =
|
|
7236
|
+
var VideoPresetEnum = z39.enum([
|
|
6988
7237
|
"auto",
|
|
6989
7238
|
"ugc_dynamic",
|
|
6990
7239
|
"ugc_how_to",
|
|
@@ -6996,7 +7245,7 @@ var VideoPresetEnum = z38.enum([
|
|
|
6996
7245
|
"pixar_problem",
|
|
6997
7246
|
"pixar_sassy"
|
|
6998
7247
|
]);
|
|
6999
|
-
var TaskCreativeVariantVideoPresetEnum =
|
|
7248
|
+
var TaskCreativeVariantVideoPresetEnum = z39.enum([
|
|
7000
7249
|
"auto",
|
|
7001
7250
|
"ugc_dynamic",
|
|
7002
7251
|
"ugc_how_to",
|
|
@@ -7061,7 +7310,7 @@ function resolveVideoPreset(preset) {
|
|
|
7061
7310
|
return VIDEO_PRESET_RESOLUTION[preset] ?? null;
|
|
7062
7311
|
}
|
|
7063
7312
|
__name(resolveVideoPreset, "resolveVideoPreset");
|
|
7064
|
-
var VideoTierEnum =
|
|
7313
|
+
var VideoTierEnum = z39.enum([
|
|
7065
7314
|
"basic"
|
|
7066
7315
|
]);
|
|
7067
7316
|
function resolveVideoTier(_tier) {
|
|
@@ -7076,17 +7325,17 @@ function resolveVideoFormatForDuration(durationSeconds) {
|
|
|
7076
7325
|
return durationSeconds === 30 ? "double_30s" : "single_15s";
|
|
7077
7326
|
}
|
|
7078
7327
|
__name(resolveVideoFormatForDuration, "resolveVideoFormatForDuration");
|
|
7079
|
-
var VideoResolutionEnum =
|
|
7328
|
+
var VideoResolutionEnum = z39.enum([
|
|
7080
7329
|
"480p",
|
|
7081
7330
|
"720p"
|
|
7082
7331
|
]);
|
|
7083
|
-
var VideoStyleEnum =
|
|
7332
|
+
var VideoStyleEnum = z39.enum([
|
|
7084
7333
|
"ugc",
|
|
7085
7334
|
"cartoon_3d",
|
|
7086
7335
|
"product_only",
|
|
7087
7336
|
"ai_dynamic"
|
|
7088
7337
|
]);
|
|
7089
|
-
var NarrativeStyleEnum =
|
|
7338
|
+
var NarrativeStyleEnum = z39.enum([
|
|
7090
7339
|
"review",
|
|
7091
7340
|
"ugc_how_to",
|
|
7092
7341
|
"problem_product_cta",
|
|
@@ -7103,7 +7352,7 @@ var NarrativeStyleEnum = z38.enum([
|
|
|
7103
7352
|
"ugc_skeptic_confession",
|
|
7104
7353
|
"ugc_demo_review"
|
|
7105
7354
|
]);
|
|
7106
|
-
var PublicNarrativeStyleEnum =
|
|
7355
|
+
var PublicNarrativeStyleEnum = z39.enum([
|
|
7107
7356
|
"review",
|
|
7108
7357
|
"ugc_how_to",
|
|
7109
7358
|
"ugc_unboxing_chaos",
|
|
@@ -7114,7 +7363,7 @@ var PublicNarrativeStyleEnum = z38.enum([
|
|
|
7114
7363
|
"animated_problem_pixar",
|
|
7115
7364
|
"sassy_product_pixar"
|
|
7116
7365
|
]);
|
|
7117
|
-
var VideoModelEnum =
|
|
7366
|
+
var VideoModelEnum = z39.enum([
|
|
7118
7367
|
"veo",
|
|
7119
7368
|
"wan",
|
|
7120
7369
|
"bytedance",
|
|
@@ -7122,25 +7371,25 @@ var VideoModelEnum = z38.enum([
|
|
|
7122
7371
|
"grok",
|
|
7123
7372
|
"kling"
|
|
7124
7373
|
]);
|
|
7125
|
-
var ProductReferenceUserChoiceEnum =
|
|
7374
|
+
var ProductReferenceUserChoiceEnum = z39.enum([
|
|
7126
7375
|
"same_product",
|
|
7127
7376
|
"variants",
|
|
7128
7377
|
"different_products",
|
|
7129
7378
|
"combo_distinct"
|
|
7130
7379
|
]);
|
|
7131
|
-
var ProductReferenceCandidateGroupingEnum =
|
|
7380
|
+
var ProductReferenceCandidateGroupingEnum = z39.enum([
|
|
7132
7381
|
"same_product_same_variant",
|
|
7133
7382
|
"same_product_different_variants",
|
|
7134
7383
|
"different_products",
|
|
7135
7384
|
"unclear"
|
|
7136
7385
|
]);
|
|
7137
|
-
var GapDurationChoiceMirror =
|
|
7138
|
-
angleIndex:
|
|
7139
|
-
recommendedSec:
|
|
7140
|
-
|
|
7141
|
-
|
|
7386
|
+
var GapDurationChoiceMirror = z39.object({
|
|
7387
|
+
angleIndex: z39.number().int().nonnegative(),
|
|
7388
|
+
recommendedSec: z39.union([
|
|
7389
|
+
z39.literal(15),
|
|
7390
|
+
z39.literal(30)
|
|
7142
7391
|
]),
|
|
7143
|
-
estimatedDeltaCredits:
|
|
7392
|
+
estimatedDeltaCredits: z39.number().int().nonnegative().optional()
|
|
7144
7393
|
});
|
|
7145
7394
|
function buildSelectedImageUrlsHash(urls) {
|
|
7146
7395
|
const input = [
|
|
@@ -7154,13 +7403,13 @@ function buildSelectedImageUrlsHash(urls) {
|
|
|
7154
7403
|
return `fnv1a:${hash.toString(16).padStart(8, "0")}:${urls.length}`;
|
|
7155
7404
|
}
|
|
7156
7405
|
__name(buildSelectedImageUrlsHash, "buildSelectedImageUrlsHash");
|
|
7157
|
-
var ImageCreativeStyleEnum =
|
|
7406
|
+
var ImageCreativeStyleEnum = z39.enum([
|
|
7158
7407
|
"studio",
|
|
7159
7408
|
"floating",
|
|
7160
7409
|
"ingredients",
|
|
7161
7410
|
"in_use"
|
|
7162
7411
|
]);
|
|
7163
|
-
var ImageCreativeArchetypeEnum =
|
|
7412
|
+
var ImageCreativeArchetypeEnum = z39.enum([
|
|
7164
7413
|
"direct_response_product_ad",
|
|
7165
7414
|
"hero_outcome",
|
|
7166
7415
|
"offer_value_prop",
|
|
@@ -7169,23 +7418,23 @@ var ImageCreativeArchetypeEnum = z38.enum([
|
|
|
7169
7418
|
"problem_solution",
|
|
7170
7419
|
"lifestyle_identity"
|
|
7171
7420
|
]);
|
|
7172
|
-
var ImageCreativeVisualTreatmentEnum =
|
|
7421
|
+
var ImageCreativeVisualTreatmentEnum = z39.enum([
|
|
7173
7422
|
"performance_product_ad",
|
|
7174
7423
|
"premium_dtc_carousel"
|
|
7175
7424
|
]);
|
|
7176
|
-
var ImageCreativeAspectRatioEnum =
|
|
7425
|
+
var ImageCreativeAspectRatioEnum = z39.enum([
|
|
7177
7426
|
"1:1",
|
|
7178
7427
|
"4:5",
|
|
7179
7428
|
"9:16",
|
|
7180
7429
|
"16:9"
|
|
7181
7430
|
]);
|
|
7182
|
-
var ImageRefSchema =
|
|
7183
|
-
url:
|
|
7184
|
-
path:
|
|
7431
|
+
var ImageRefSchema = z39.object({
|
|
7432
|
+
url: z39.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.'),
|
|
7433
|
+
path: z39.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`.')
|
|
7185
7434
|
}).refine((v) => Boolean(v.url) !== Boolean(v.path), {
|
|
7186
7435
|
message: "each image entry must have exactly one of `url` or `path`"
|
|
7187
7436
|
});
|
|
7188
|
-
var ImagesInput =
|
|
7437
|
+
var ImagesInput = z39.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.');
|
|
7189
7438
|
function asText(value) {
|
|
7190
7439
|
return {
|
|
7191
7440
|
content: [
|
|
@@ -7238,14 +7487,14 @@ async function submitCreative(api, opts) {
|
|
|
7238
7487
|
});
|
|
7239
7488
|
}
|
|
7240
7489
|
__name(submitCreative, "submitCreative");
|
|
7241
|
-
var ResearchTargetCountryEnum =
|
|
7490
|
+
var ResearchTargetCountryEnum = z39.enum([
|
|
7242
7491
|
"CO",
|
|
7243
7492
|
"MX",
|
|
7244
7493
|
"PE",
|
|
7245
7494
|
"EC",
|
|
7246
7495
|
"CL"
|
|
7247
7496
|
]);
|
|
7248
|
-
var ResearchCategoryEnum =
|
|
7497
|
+
var ResearchCategoryEnum = z39.enum([
|
|
7249
7498
|
"auto",
|
|
7250
7499
|
"automotive",
|
|
7251
7500
|
"kitchen",
|
|
@@ -7291,6 +7540,15 @@ var McpErrorCode = {
|
|
|
7291
7540
|
TASK_NOT_READY: "TASK_NOT_READY",
|
|
7292
7541
|
CREATIVE_VARIANT_FAILED: "CREATIVE_VARIANT_FAILED",
|
|
7293
7542
|
CREATIVE_VARIANT_DISPATCH_FAILED: "CREATIVE_VARIANT_DISPATCH_FAILED",
|
|
7543
|
+
// Guided dropshipping project / research
|
|
7544
|
+
DROPSHIPPING_PROJECT_NOT_FOUND: "DROPSHIPPING_PROJECT_NOT_FOUND",
|
|
7545
|
+
DROPSHIPPING_PROJECT_CANDIDATE_NOT_READY: "DROPSHIPPING_PROJECT_CANDIDATE_NOT_READY",
|
|
7546
|
+
DROPSHIPPING_PROJECT_VERSION_CONFLICT: "DROPSHIPPING_PROJECT_VERSION_CONFLICT",
|
|
7547
|
+
DROPSHIPPING_PROJECT_MEDIA_REQUIRED: "DROPSHIPPING_PROJECT_MEDIA_REQUIRED",
|
|
7548
|
+
DROPSHIPPING_PROJECT_DISABLED: "DROPSHIPPING_PROJECT_DISABLED",
|
|
7549
|
+
KALODATA_NOT_CONFIGURED: "KALODATA_NOT_CONFIGURED",
|
|
7550
|
+
META_AD_LIBRARY_NOT_CONFIGURED: "META_AD_LIBRARY_NOT_CONFIGURED",
|
|
7551
|
+
PRODUCT_RESEARCH_DISABLED: "PRODUCT_RESEARCH_DISABLED",
|
|
7294
7552
|
PRODUCT_RESEARCH_IDEMPOTENCY_CONFLICT: "PRODUCT_RESEARCH_IDEMPOTENCY_CONFLICT",
|
|
7295
7553
|
// Connection presence
|
|
7296
7554
|
NO_SHOPIFY_SHOPS: "NO_SHOPIFY_SHOPS",
|
|
@@ -7301,6 +7559,7 @@ var McpErrorCode = {
|
|
|
7301
7559
|
CAMPAIGN_MISSING_LANDING_URL: "CAMPAIGN_MISSING_LANDING_URL",
|
|
7302
7560
|
CAMPAIGN_NO_CREATIVES: "CAMPAIGN_NO_CREATIVES",
|
|
7303
7561
|
CAMPAIGN_MISSING_THUMBNAILS: "CAMPAIGN_MISSING_THUMBNAILS",
|
|
7562
|
+
CAMPAIGN_RECONCILIATION_REQUIRED: "CAMPAIGN_RECONCILIATION_REQUIRED",
|
|
7304
7563
|
// Shopify
|
|
7305
7564
|
SHOPIFY_NOT_CONNECTED: "SHOPIFY_NOT_CONNECTED",
|
|
7306
7565
|
SHOPIFY_TOKEN_EXPIRED: "SHOPIFY_TOKEN_EXPIRED",
|
|
@@ -7405,6 +7664,7 @@ var CODE_MESSAGES = {
|
|
|
7405
7664
|
CAMPAIGN_MISSING_LANDING_URL: "Missing landing URL for the campaign. Publish to Shopify first or pass `landingUrl` explicitly.",
|
|
7406
7665
|
CAMPAIGN_NO_CREATIVES: "The task has no creatives (video/image) ready for the campaign. Wait for generation to finish.",
|
|
7407
7666
|
CAMPAIGN_MISSING_THUMBNAILS: "Missing thumbnails for the videos. Verify that image/video generation has finished.",
|
|
7667
|
+
CAMPAIGN_RECONCILIATION_REQUIRED: "A campaign may already have been created. Do not retry creation; reopen the project and reconcile it or contact support.",
|
|
7408
7668
|
// Connections
|
|
7409
7669
|
NO_SHOPIFY_SHOPS: "No Shopify stores connected. Connect one at https://productmaker.app/integrations.",
|
|
7410
7670
|
NO_META_AD_ACCOUNTS: "No Meta Ads accounts connected. Connect Facebook at https://productmaker.app/integrations.",
|
|
@@ -7413,6 +7673,14 @@ var CODE_MESSAGES = {
|
|
|
7413
7673
|
TASK_NOT_READY: "The task is not ready for this action yet. Wait for it to finish and retry.",
|
|
7414
7674
|
CREATIVE_VARIANT_FAILED: "The previous creative variant attempt failed. Ask for confirmation before starting a new paid attempt with a new clientRequestId.",
|
|
7415
7675
|
CREATIVE_VARIANT_DISPATCH_FAILED: "The creative variant could not be queued. Retry as a new paid attempt with a new clientRequestId.",
|
|
7676
|
+
DROPSHIPPING_PROJECT_NOT_FOUND: "Project not found or it does not belong to this user.",
|
|
7677
|
+
DROPSHIPPING_PROJECT_CANDIDATE_NOT_READY: "That research candidate is no longer available. Return to the current research results and choose a fresh candidate.",
|
|
7678
|
+
DROPSHIPPING_PROJECT_VERSION_CONFLICT: "This project changed in another tab. Read it again, review the newer values, and retry with the fresh version.",
|
|
7679
|
+
DROPSHIPPING_PROJECT_MEDIA_REQUIRED: "This project needs a product photo before creating content. Upload one from the guided ingest step.",
|
|
7680
|
+
DROPSHIPPING_PROJECT_DISABLED: "Guided dropshipping projects are temporarily unavailable. Try again later.",
|
|
7681
|
+
KALODATA_NOT_CONFIGURED: "Product research is temporarily unavailable because the trend-data provider is not configured.",
|
|
7682
|
+
META_AD_LIBRARY_NOT_CONFIGURED: "Product research is temporarily unavailable because Meta Ad Library is not configured.",
|
|
7683
|
+
PRODUCT_RESEARCH_DISABLED: "Product research is temporarily unavailable. Try again later.",
|
|
7416
7684
|
PRODUCT_RESEARCH_IDEMPOTENCY_CONFLICT: "A product research run with the same country and category is already running. Check it with `list_product_research_runs` instead of starting a new paid run.",
|
|
7417
7685
|
// API key
|
|
7418
7686
|
INVALID_API_KEY: "Invalid API key. Check your PM_API_KEY in the MCP client configuration.",
|
|
@@ -7449,6 +7717,11 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
|
|
|
7449
7717
|
McpErrorCode.CAMPAIGN_NOT_FOUND,
|
|
7450
7718
|
McpErrorCode.CAMPAIGN_TASK_NOT_FOUND,
|
|
7451
7719
|
McpErrorCode.TASK_NOT_FOUND,
|
|
7720
|
+
McpErrorCode.DROPSHIPPING_PROJECT_NOT_FOUND,
|
|
7721
|
+
McpErrorCode.DROPSHIPPING_PROJECT_DISABLED,
|
|
7722
|
+
McpErrorCode.DROPSHIPPING_PROJECT_CANDIDATE_NOT_READY,
|
|
7723
|
+
McpErrorCode.DROPSHIPPING_PROJECT_VERSION_CONFLICT,
|
|
7724
|
+
McpErrorCode.DROPSHIPPING_PROJECT_MEDIA_REQUIRED,
|
|
7452
7725
|
McpErrorCode.META_TASK_NOT_FOUND,
|
|
7453
7726
|
McpErrorCode.META_INVALID_AD_ACCOUNT,
|
|
7454
7727
|
McpErrorCode.META_PIXEL_NOT_FOUND,
|
|
@@ -7457,6 +7730,7 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
|
|
|
7457
7730
|
McpErrorCode.TIKTOK_ADVERTISER_REQUIRED,
|
|
7458
7731
|
McpErrorCode.TIKTOK_PIXEL_NOT_FOUND,
|
|
7459
7732
|
McpErrorCode.CAMPAIGN_MISSING_LANDING_URL,
|
|
7733
|
+
McpErrorCode.CAMPAIGN_RECONCILIATION_REQUIRED,
|
|
7460
7734
|
McpErrorCode.META_NO_LANDING_URL,
|
|
7461
7735
|
McpErrorCode.META_DAILY_BUDGET_TOO_HIGH,
|
|
7462
7736
|
McpErrorCode.META_CAMPAIGN_CONFIG_INVALID,
|
|
@@ -7494,6 +7768,14 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
|
|
|
7494
7768
|
var PASSTHROUGH_CODES = /* @__PURE__ */ new Set([
|
|
7495
7769
|
McpErrorCode.INSUFFICIENT_CREDITS,
|
|
7496
7770
|
McpErrorCode.TASK_QUOTA_EXCEEDED,
|
|
7771
|
+
McpErrorCode.DROPSHIPPING_PROJECT_NOT_FOUND,
|
|
7772
|
+
McpErrorCode.DROPSHIPPING_PROJECT_DISABLED,
|
|
7773
|
+
McpErrorCode.DROPSHIPPING_PROJECT_CANDIDATE_NOT_READY,
|
|
7774
|
+
McpErrorCode.DROPSHIPPING_PROJECT_VERSION_CONFLICT,
|
|
7775
|
+
McpErrorCode.DROPSHIPPING_PROJECT_MEDIA_REQUIRED,
|
|
7776
|
+
McpErrorCode.KALODATA_NOT_CONFIGURED,
|
|
7777
|
+
McpErrorCode.META_AD_LIBRARY_NOT_CONFIGURED,
|
|
7778
|
+
McpErrorCode.PRODUCT_RESEARCH_DISABLED,
|
|
7497
7779
|
McpErrorCode.CREATIVE_VARIANT_DISPATCH_FAILED,
|
|
7498
7780
|
McpErrorCode.PRODUCT_RESEARCH_IDEMPOTENCY_CONFLICT,
|
|
7499
7781
|
// Shopify
|
|
@@ -7548,6 +7830,7 @@ var PASSTHROUGH_CODES = /* @__PURE__ */ new Set([
|
|
|
7548
7830
|
McpErrorCode.CAMPAIGN_MISSING_LANDING_URL,
|
|
7549
7831
|
McpErrorCode.CAMPAIGN_NO_CREATIVES,
|
|
7550
7832
|
McpErrorCode.CAMPAIGN_MISSING_THUMBNAILS,
|
|
7833
|
+
McpErrorCode.CAMPAIGN_RECONCILIATION_REQUIRED,
|
|
7551
7834
|
// Connections
|
|
7552
7835
|
McpErrorCode.NO_SHOPIFY_SHOPS,
|
|
7553
7836
|
McpErrorCode.NO_META_AD_ACCOUNTS,
|
|
@@ -7858,66 +8141,67 @@ function renderOutputsMarkdown(outputs) {
|
|
|
7858
8141
|
__name(renderOutputsMarkdown, "renderOutputsMarkdown");
|
|
7859
8142
|
|
|
7860
8143
|
// src/tools/tasks.ts
|
|
7861
|
-
var CreateProductTaskInput =
|
|
7862
|
-
images: ImagesInput,
|
|
7863
|
-
|
|
8144
|
+
var CreateProductTaskInput = z40.object({
|
|
8145
|
+
images: ImagesInput.optional().describe("1\u20135 fotos del mismo producto. Omitir \xFAnicamente cuando projectId apunta a un proyecto propio con imagen guardada."),
|
|
8146
|
+
projectId: z40.string().uuid().optional().describe("Opcional. Proyecto guiado propio. Permite continuar sin volver a subir la imagen guardada; nunca evita cr\xE9ditos, cuotas ni confirmaciones."),
|
|
8147
|
+
productUrl: z40.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."),
|
|
7864
8148
|
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.'),
|
|
7865
8149
|
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.'),
|
|
7866
|
-
price:
|
|
7867
|
-
compareAtPrice:
|
|
7868
|
-
offer:
|
|
7869
|
-
paymentMethods:
|
|
7870
|
-
productContext:
|
|
7871
|
-
productLabel:
|
|
8150
|
+
price: z40.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.'),
|
|
8151
|
+
compareAtPrice: z40.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".'),
|
|
8152
|
+
offer: z40.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.'),
|
|
8153
|
+
paymentMethods: z40.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.'),
|
|
8154
|
+
productContext: z40.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."),
|
|
8155
|
+
productLabel: z40.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.'),
|
|
7872
8156
|
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). Los presets funcionan a 15s y 30s. 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), "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.'),
|
|
7873
|
-
durationSeconds:
|
|
7874
|
-
|
|
7875
|
-
|
|
8157
|
+
durationSeconds: z40.union([
|
|
8158
|
+
z40.literal(15),
|
|
8159
|
+
z40.literal(30)
|
|
7876
8160
|
]).default(15).describe("Opcional. Duraci\xF3n objetivo del video: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
|
|
7877
|
-
pauseForAngleReview:
|
|
8161
|
+
pauseForAngleReview: z40.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."),
|
|
7878
8162
|
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.'),
|
|
7879
|
-
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
|
|
7880
|
-
multiAngleVideos:
|
|
8163
|
+
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).'),
|
|
8164
|
+
multiAngleVideos: z40.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."),
|
|
7881
8165
|
angleNarratives: PublicNarrativeStyleEnum.array().max(3).optional().describe('Opcional. Narrativa POR \xC1NGULO (posicional). Valores actuales: "review", "ugc_how_to", "ugc_unboxing_chaos", "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.'),
|
|
7882
|
-
outputs:
|
|
8166
|
+
outputs: z40.array(z40.enum([
|
|
7883
8167
|
"landing",
|
|
7884
8168
|
"video",
|
|
7885
8169
|
"image"
|
|
7886
8170
|
])).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.'),
|
|
7887
|
-
competitorScanEnabled:
|
|
7888
|
-
brandId:
|
|
7889
|
-
creativeDirection:
|
|
8171
|
+
competitorScanEnabled: z40.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."),
|
|
8172
|
+
brandId: z40.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."),
|
|
8173
|
+
creativeDirection: z40.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.'),
|
|
7890
8174
|
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".'),
|
|
7891
8175
|
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".')
|
|
7892
8176
|
}).strict();
|
|
7893
|
-
var GetTaskStatusInput =
|
|
7894
|
-
taskId:
|
|
7895
|
-
waitSeconds:
|
|
7896
|
-
});
|
|
7897
|
-
var ListTasksInput =
|
|
7898
|
-
limit:
|
|
7899
|
-
offset:
|
|
7900
|
-
search:
|
|
7901
|
-
});
|
|
7902
|
-
var EditTaskDraftInput =
|
|
7903
|
-
taskId:
|
|
7904
|
-
angleIndex:
|
|
7905
|
-
angle:
|
|
7906
|
-
angle:
|
|
7907
|
-
buyerPersona:
|
|
7908
|
-
actor:
|
|
7909
|
-
gender:
|
|
8177
|
+
var GetTaskStatusInput = z40.object({
|
|
8178
|
+
taskId: z40.string().min(1),
|
|
8179
|
+
waitSeconds: z40.number().int().min(0).max(120).optional()
|
|
8180
|
+
});
|
|
8181
|
+
var ListTasksInput = z40.object({
|
|
8182
|
+
limit: z40.number().int().min(1).max(50).optional(),
|
|
8183
|
+
offset: z40.number().int().min(0).optional(),
|
|
8184
|
+
search: z40.string().optional()
|
|
8185
|
+
});
|
|
8186
|
+
var EditTaskDraftInput = z40.object({
|
|
8187
|
+
taskId: z40.string().min(1),
|
|
8188
|
+
angleIndex: z40.number().int().min(0),
|
|
8189
|
+
angle: z40.object({
|
|
8190
|
+
angle: z40.string().min(1).max(480),
|
|
8191
|
+
buyerPersona: z40.string().min(1).max(360),
|
|
8192
|
+
actor: z40.string().min(1).max(360).optional(),
|
|
8193
|
+
gender: z40.enum([
|
|
7910
8194
|
"female",
|
|
7911
8195
|
"male"
|
|
7912
8196
|
]).optional(),
|
|
7913
|
-
scriptNotes:
|
|
8197
|
+
scriptNotes: z40.string().max(2e3).optional()
|
|
7914
8198
|
})
|
|
7915
8199
|
});
|
|
7916
|
-
var PendingActionPayload =
|
|
7917
|
-
|
|
7918
|
-
kind:
|
|
8200
|
+
var PendingActionPayload = z40.discriminatedUnion("kind", [
|
|
8201
|
+
z40.object({
|
|
8202
|
+
kind: z40.literal("product_reference_confirmation"),
|
|
7919
8203
|
userChoice: ProductReferenceUserChoiceEnum,
|
|
7920
|
-
confirmedProductNames:
|
|
8204
|
+
confirmedProductNames: z40.array(z40.string().min(1).max(120)).min(2).max(8).optional(),
|
|
7921
8205
|
// SCOPED multi-scene opt-in — the ONLY way STANDARD ("Multiescena") is
|
|
7922
8206
|
// reachable via MCP. Valid ONLY together with userChoice='variants' (same
|
|
7923
8207
|
// product in several colors): renders one shot per color and stitches them,
|
|
@@ -7928,62 +8212,62 @@ var PendingActionPayload = z39.discriminatedUnion("kind", [
|
|
|
7928
8212
|
// safe because multi-scene IS the intended render for a color variant set,
|
|
7929
8213
|
// not a blind quality upsell. Maps to `videoTier: 'standard'` on the
|
|
7930
8214
|
// confirm call; the backend re-gates it (variants + variant_set only).
|
|
7931
|
-
useMultiSceneShowcase:
|
|
8215
|
+
useMultiSceneShowcase: z40.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".')
|
|
7932
8216
|
}),
|
|
7933
|
-
|
|
7934
|
-
kind:
|
|
8217
|
+
z40.object({
|
|
8218
|
+
kind: z40.literal("angle_review_approval"),
|
|
7935
8219
|
// All edits are optional — empty action body just approves the
|
|
7936
8220
|
// backend-generated draft as-is. Field shapes mirror the angle-review
|
|
7937
8221
|
// request contracts in @packages/types/wizard.ts; the handler routes to
|
|
7938
8222
|
// the dedicated video/image/autopilot endpoint based on StatusResponse.kind.
|
|
7939
|
-
customAngle:
|
|
8223
|
+
customAngle: z40.string().min(1).max(480).optional(),
|
|
7940
8224
|
// Keep the shared MCP action within the strictest downstream contract:
|
|
7941
8225
|
// autopilot/image resume endpoints cap these fields at 1500 characters.
|
|
7942
|
-
painPoints:
|
|
7943
|
-
benefits:
|
|
7944
|
-
features:
|
|
7945
|
-
gapDurationSeconds:
|
|
7946
|
-
|
|
7947
|
-
|
|
8226
|
+
painPoints: z40.string().max(1500).optional(),
|
|
8227
|
+
benefits: z40.string().max(1500).optional(),
|
|
8228
|
+
features: z40.array(z40.string().min(1).max(300)).max(10).optional(),
|
|
8229
|
+
gapDurationSeconds: z40.union([
|
|
8230
|
+
z40.literal(15),
|
|
8231
|
+
z40.literal(30)
|
|
7948
8232
|
]).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.")
|
|
7949
8233
|
})
|
|
7950
8234
|
]);
|
|
7951
|
-
var ResolvePendingActionInput =
|
|
7952
|
-
taskId:
|
|
8235
|
+
var ResolvePendingActionInput = z40.object({
|
|
8236
|
+
taskId: z40.string().min(1),
|
|
7953
8237
|
action: PendingActionPayload
|
|
7954
8238
|
}).strict();
|
|
7955
8239
|
function resolveVariantConfirmVideoTier(action) {
|
|
7956
8240
|
return action.useMultiSceneShowcase && action.userChoice === "variants" ? "standard" : void 0;
|
|
7957
8241
|
}
|
|
7958
8242
|
__name(resolveVariantConfirmVideoTier, "resolveVariantConfirmVideoTier");
|
|
7959
|
-
var StatusForResolveSchema =
|
|
8243
|
+
var StatusForResolveSchema = z40.object({
|
|
7960
8244
|
// Metadata used to select the backend resume contract. Older API responses
|
|
7961
8245
|
// may omit these fields, so the resolver keeps the generic fallback.
|
|
7962
|
-
kind:
|
|
8246
|
+
kind: z40.enum([
|
|
7963
8247
|
"autopilot",
|
|
7964
8248
|
"video",
|
|
7965
8249
|
"image",
|
|
7966
8250
|
"landing",
|
|
7967
8251
|
"mix"
|
|
7968
8252
|
]).optional(),
|
|
7969
|
-
selectedOutputs:
|
|
8253
|
+
selectedOutputs: z40.array(z40.enum([
|
|
7970
8254
|
"landing",
|
|
7971
8255
|
"video",
|
|
7972
8256
|
"image"
|
|
7973
8257
|
])).optional(),
|
|
7974
|
-
videoAngleCount:
|
|
7975
|
-
ugcFormat:
|
|
8258
|
+
videoAngleCount: z40.number().int().min(1).max(3).optional(),
|
|
8259
|
+
ugcFormat: z40.enum([
|
|
7976
8260
|
"single_8s",
|
|
7977
8261
|
"single_15s",
|
|
7978
8262
|
"double_30s"
|
|
7979
8263
|
]).optional(),
|
|
7980
|
-
videoStyle:
|
|
8264
|
+
videoStyle: z40.enum([
|
|
7981
8265
|
"ugc",
|
|
7982
8266
|
"cartoon_3d",
|
|
7983
8267
|
"product_only",
|
|
7984
8268
|
"ai_dynamic"
|
|
7985
8269
|
]).optional(),
|
|
7986
|
-
videoModel:
|
|
8270
|
+
videoModel: z40.enum([
|
|
7987
8271
|
"veo",
|
|
7988
8272
|
"wan",
|
|
7989
8273
|
"bytedance",
|
|
@@ -7991,26 +8275,26 @@ var StatusForResolveSchema = z39.object({
|
|
|
7991
8275
|
"grok",
|
|
7992
8276
|
"kling"
|
|
7993
8277
|
]).optional(),
|
|
7994
|
-
videoTier:
|
|
8278
|
+
videoTier: z40.enum([
|
|
7995
8279
|
"legacy",
|
|
7996
8280
|
"standard",
|
|
7997
8281
|
"pro",
|
|
7998
8282
|
"single_call"
|
|
7999
8283
|
]).optional(),
|
|
8000
|
-
videoResolution:
|
|
8284
|
+
videoResolution: z40.enum([
|
|
8001
8285
|
"480p",
|
|
8002
8286
|
"720p"
|
|
8003
8287
|
]).optional(),
|
|
8004
8288
|
narrativeStyle: NarrativeStyleEnum.optional(),
|
|
8005
|
-
pendingUserAction:
|
|
8006
|
-
kind:
|
|
8007
|
-
candidateGrouping:
|
|
8008
|
-
selectedImageUrls:
|
|
8009
|
-
proposedProductNames:
|
|
8010
|
-
customAngleDraft:
|
|
8011
|
-
painPointsDraft:
|
|
8012
|
-
benefitsDraft:
|
|
8013
|
-
featuresDraft:
|
|
8289
|
+
pendingUserAction: z40.object({
|
|
8290
|
+
kind: z40.string(),
|
|
8291
|
+
candidateGrouping: z40.string().optional(),
|
|
8292
|
+
selectedImageUrls: z40.array(z40.string()).optional(),
|
|
8293
|
+
proposedProductNames: z40.array(z40.string()).optional(),
|
|
8294
|
+
customAngleDraft: z40.string().optional(),
|
|
8295
|
+
painPointsDraft: z40.string().optional(),
|
|
8296
|
+
benefitsDraft: z40.string().optional(),
|
|
8297
|
+
featuresDraft: z40.array(z40.string()).optional(),
|
|
8014
8298
|
// Recommendation consumed only when the caller explicitly sends
|
|
8015
8299
|
// gapDurationSeconds: 30 in the angle approval action.
|
|
8016
8300
|
gapDurationChoice: GapDurationChoiceMirror.optional()
|
|
@@ -8051,11 +8335,14 @@ function resolveStatusTaskKind(status) {
|
|
|
8051
8335
|
}
|
|
8052
8336
|
__name(resolveStatusTaskKind, "resolveStatusTaskKind");
|
|
8053
8337
|
function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http" } = {}) {
|
|
8054
|
-
server.tool("create_product_task",
|
|
8338
|
+
server.tool("create_product_task", "Crea una tarea de producto desde fotos o desde un proyecto guiado propio. Por defecto genera los 3 outputs (landing + video + image creatives) EN UN MISMO TASK. Devuelve un taskId.\n\nINPUTS REQUERIDOS: images (1-5 fotos del MISMO producto; la primera es la hero) O projectId si el proyecto ya tiene una imagen guardada; adem\xE1s language y country.\n\nANTES de invocar, PREG\xDANTALE al usuario por price, offer y paymentMethods si no los mencion\xF3 \u2014 son inputs de alto impacto en la landing y nunca debes asumirlos. Si hay descuento, pregunta tambi\xE9n compareAtPrice. NO defaultes country/language desde el contexto del chat: preg\xFAntale expl\xEDcitamente, incluso si est\xE1 en LATAM.\n\nSi env\xEDas projectId, debe ser el proyecto propio que el usuario acaba de costear; el backend resolver\xE1 la imagen privada y conservar\xE1 todos los controles de cr\xE9ditos/confirmaci\xF3n. Si el usuario adjunta images junto con projectId, esas fotos reemplazan la referencia solo para este task.\n\nANTES de invocar, DILE CLARAMENTE qu\xE9 outputs vas a generar y que `create_product_task` los genera juntos en un mismo task.\n\nUsa SOLO valores declarados en los enums.", CreateProductTaskInput.shape, async (input) => {
|
|
8055
8339
|
try {
|
|
8056
|
-
|
|
8340
|
+
if (!input.images && !input.projectId) {
|
|
8341
|
+
throw new Error("images es requerido salvo que env\xEDes projectId.");
|
|
8342
|
+
}
|
|
8343
|
+
const imgs = input.images ? await resolveImages(input.images, {
|
|
8057
8344
|
transport
|
|
8058
|
-
});
|
|
8345
|
+
}) : [];
|
|
8059
8346
|
const parts = imgs.map((img, i) => ({
|
|
8060
8347
|
...img,
|
|
8061
8348
|
filename: `image-${i}.png`
|
|
@@ -8066,6 +8353,7 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
|
|
|
8066
8353
|
const fields = flatten({
|
|
8067
8354
|
"options.languageCode": input.language,
|
|
8068
8355
|
"options.country": input.country,
|
|
8356
|
+
"options.dropshippingProjectId": input.projectId,
|
|
8069
8357
|
"options.price": input.price,
|
|
8070
8358
|
"options.compareAtPrice": input.compareAtPrice,
|
|
8071
8359
|
"options.offer": input.offer,
|
|
@@ -8332,54 +8620,56 @@ __name(registerTaskTools, "registerTaskTools");
|
|
|
8332
8620
|
|
|
8333
8621
|
// src/tools/creatives.ts
|
|
8334
8622
|
import { randomUUID } from "crypto";
|
|
8335
|
-
import { z as
|
|
8623
|
+
import { z as z41 } from "zod";
|
|
8336
8624
|
var CreativeRefs = {
|
|
8337
8625
|
images: ImagesInput,
|
|
8338
8626
|
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.'),
|
|
8339
8627
|
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.'),
|
|
8340
|
-
price:
|
|
8341
|
-
offer:
|
|
8342
|
-
compareAtPrice:
|
|
8343
|
-
paymentMethods:
|
|
8344
|
-
productContext:
|
|
8345
|
-
productLabel:
|
|
8346
|
-
buyerPersona:
|
|
8347
|
-
isCombo:
|
|
8348
|
-
competitorScanEnabled:
|
|
8349
|
-
brandId:
|
|
8350
|
-
|
|
8628
|
+
price: z41.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.'),
|
|
8629
|
+
offer: z41.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.'),
|
|
8630
|
+
compareAtPrice: z41.string().min(1).max(120).optional().describe('Opcional. Precio anterior o de referencia para mostrar descuento. Ej. "$129.000" cuando price="$89.900".'),
|
|
8631
|
+
paymentMethods: z41.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.'),
|
|
8632
|
+
productContext: z41.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."),
|
|
8633
|
+
productLabel: z41.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.'),
|
|
8634
|
+
buyerPersona: z41.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.'),
|
|
8635
|
+
isCombo: z41.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
|
|
8636
|
+
competitorScanEnabled: z41.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."),
|
|
8637
|
+
brandId: z41.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."),
|
|
8638
|
+
projectId: z41.string().uuid().optional().describe("Opcional. ID de un proyecto guiado de dropshipping activo del usuario. Enlaza esta generaci\xF3n con su progreso y recompensas."),
|
|
8639
|
+
creativeDirection: z41.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.')
|
|
8351
8640
|
};
|
|
8352
|
-
var VideoInput =
|
|
8641
|
+
var VideoInput = z41.object({
|
|
8353
8642
|
images: CreativeRefs.images,
|
|
8354
|
-
title:
|
|
8355
|
-
primaryAngle:
|
|
8643
|
+
title: z41.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
|
|
8644
|
+
primaryAngle: z41.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".'),
|
|
8356
8645
|
language: CreativeRefs.language,
|
|
8357
8646
|
country: CreativeRefs.country,
|
|
8358
|
-
aspectRatio:
|
|
8647
|
+
aspectRatio: z41.enum([
|
|
8359
8648
|
"9:16",
|
|
8360
8649
|
"16:9",
|
|
8361
8650
|
"Auto"
|
|
8362
8651
|
]).describe('Relaci\xF3n de aspecto. "9:16" = vertical (TikTok/Reels \u2014 default recomendado para ads). "16:9" = horizontal (YouTube). "Auto" = decide la IA.'),
|
|
8363
|
-
durationSeconds:
|
|
8364
|
-
|
|
8365
|
-
|
|
8652
|
+
durationSeconds: z41.union([
|
|
8653
|
+
z41.literal(15),
|
|
8654
|
+
z41.literal(30)
|
|
8366
8655
|
]).default(15).describe("Opcional. Duraci\xF3n objetivo: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
|
|
8367
8656
|
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). Los presets funcionan a 15s y 30s. 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), "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).'),
|
|
8368
8657
|
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.'),
|
|
8369
|
-
videoResolution: VideoResolutionEnum.optional().describe('Opcional. Resoluci\xF3n del render. En la web se pregunta "\xBFResoluci\xF3n del video?". "
|
|
8370
|
-
features:
|
|
8658
|
+
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).'),
|
|
8659
|
+
features: z41.array(z41.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
|
|
8371
8660
|
price: CreativeRefs.price,
|
|
8372
8661
|
offer: CreativeRefs.offer,
|
|
8373
8662
|
productContext: CreativeRefs.productContext,
|
|
8374
8663
|
productLabel: CreativeRefs.productLabel,
|
|
8375
|
-
cta:
|
|
8376
|
-
scriptNotes:
|
|
8377
|
-
pauseForVideoScriptReview:
|
|
8378
|
-
pauseForAngleReview:
|
|
8664
|
+
cta: z41.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.'),
|
|
8665
|
+
scriptNotes: z41.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.'),
|
|
8666
|
+
pauseForVideoScriptReview: z41.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."),
|
|
8667
|
+
pauseForAngleReview: z41.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."),
|
|
8379
8668
|
competitorScanEnabled: CreativeRefs.competitorScanEnabled,
|
|
8380
|
-
brandId: CreativeRefs.brandId
|
|
8669
|
+
brandId: CreativeRefs.brandId,
|
|
8670
|
+
projectId: CreativeRefs.projectId
|
|
8381
8671
|
}).strict();
|
|
8382
|
-
var FreeVideoQualityEnum =
|
|
8672
|
+
var FreeVideoQualityEnum = z41.enum([
|
|
8383
8673
|
"economical",
|
|
8384
8674
|
"high_detail"
|
|
8385
8675
|
]);
|
|
@@ -8401,38 +8691,38 @@ var FREE_VIDEO_DURATIONS_BY_QUALITY = {
|
|
|
8401
8691
|
15
|
|
8402
8692
|
]
|
|
8403
8693
|
};
|
|
8404
|
-
var FreeVideoInput =
|
|
8694
|
+
var FreeVideoInput = z41.object({
|
|
8405
8695
|
images: ImagesInput.optional().describe("Opcional. 1-5 fotos del producto. Om\xEDtelo para generar desde texto puro."),
|
|
8406
|
-
rawPrompt:
|
|
8696
|
+
rawPrompt: z41.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."),
|
|
8407
8697
|
quality: FreeVideoQualityEnum.describe('Calidad del video: "economical" = Econ\xF3mico o "high_detail" = Alto detalle.'),
|
|
8408
|
-
durationSeconds:
|
|
8698
|
+
durationSeconds: z41.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."),
|
|
8409
8699
|
videoResolution: VideoResolutionEnum.describe("Resoluci\xF3n del video: 480p o 720p."),
|
|
8410
|
-
generateAiMusic:
|
|
8411
|
-
addSubtitles:
|
|
8412
|
-
idempotencyKey:
|
|
8700
|
+
generateAiMusic: z41.boolean().default(false).describe("Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false."),
|
|
8701
|
+
addSubtitles: z41.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
|
|
8702
|
+
idempotencyKey: z41.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.")
|
|
8413
8703
|
}).strict();
|
|
8414
8704
|
var imageStyleList = ImageCreativeStyleEnum.options.join(" | ");
|
|
8415
8705
|
var archetypeList = ImageCreativeArchetypeEnum.options.join(" | ");
|
|
8416
8706
|
var visualTreatmentList = ImageCreativeVisualTreatmentEnum.options.join(" | ");
|
|
8417
8707
|
var aspectRatioList = ImageCreativeAspectRatioEnum.options.join(" | ");
|
|
8418
|
-
var ImageInput =
|
|
8708
|
+
var ImageInput = z41.object({
|
|
8419
8709
|
images: CreativeRefs.images,
|
|
8420
|
-
productTitle:
|
|
8421
|
-
primaryAngle:
|
|
8710
|
+
productTitle: z41.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
|
|
8711
|
+
primaryAngle: z41.string().min(1).describe('\xC1ngulo de venta principal. Ej. "Reduce arrugas en 14 d\xEDas".'),
|
|
8422
8712
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
8423
8713
|
language: CreativeRefs.language,
|
|
8424
8714
|
country: CreativeRefs.country,
|
|
8425
|
-
nVariants:
|
|
8426
|
-
imageModel:
|
|
8715
|
+
nVariants: z41.number().int().min(1).max(6).optional().describe("Opcional. N\xFAmero de variantes a generar (1-6). Default 3."),
|
|
8716
|
+
imageModel: z41.string().optional().describe('Opcional. Modelo de imagen. Ej. "gpt-image-2", "nano-banana-2". Om\xEDtelo para usar el default optimizado por costo/calidad.'),
|
|
8427
8717
|
imageStyle: ImageCreativeStyleEnum.optional().describe(`Opcional. Estilo del shot del producto. Valores: ${imageStyleList}. Si se omite, la IA escoge seg\xFAn el archetype.`),
|
|
8428
8718
|
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).`),
|
|
8429
8719
|
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.`),
|
|
8430
|
-
styleMix:
|
|
8720
|
+
styleMix: z41.boolean().optional().describe("Opcional. Si true, las N variantes mezclan distintos imageStyle para A/B test. Si false/omitido, todas comparten estilo. Default false."),
|
|
8431
8721
|
aspectRatio: ImageCreativeAspectRatioEnum.optional().describe(`Opcional. Relaci\xF3n de aspecto. Valores: ${aspectRatioList}. Default "4:5" (vertical Instagram feed).`),
|
|
8432
|
-
generateAdCopy:
|
|
8433
|
-
pauseForAngleReview:
|
|
8434
|
-
features:
|
|
8435
|
-
brandColors:
|
|
8722
|
+
generateAdCopy: z41.boolean().optional().describe("Opcional. Si true, tambi\xE9n genera headline/body/CTA para el ad. Default false."),
|
|
8723
|
+
pauseForAngleReview: z41.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."),
|
|
8724
|
+
features: z41.array(z41.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
|
|
8725
|
+
brandColors: z41.array(z41.string()).optional().describe('Opcional. Paleta de marca en hex. Ej. ["#FF6B35", "#004E89"]. Si se omite, la IA elige seg\xFAn el producto.'),
|
|
8436
8726
|
price: CreativeRefs.price,
|
|
8437
8727
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
8438
8728
|
offer: CreativeRefs.offer,
|
|
@@ -8441,13 +8731,14 @@ var ImageInput = z40.object({
|
|
|
8441
8731
|
isCombo: CreativeRefs.isCombo,
|
|
8442
8732
|
competitorScanEnabled: CreativeRefs.competitorScanEnabled,
|
|
8443
8733
|
brandId: CreativeRefs.brandId,
|
|
8734
|
+
projectId: CreativeRefs.projectId,
|
|
8444
8735
|
creativeDirection: CreativeRefs.creativeDirection
|
|
8445
8736
|
});
|
|
8446
|
-
var LandingInput =
|
|
8737
|
+
var LandingInput = z41.object({
|
|
8447
8738
|
images: CreativeRefs.images,
|
|
8448
8739
|
language: CreativeRefs.language,
|
|
8449
8740
|
country: CreativeRefs.country,
|
|
8450
|
-
salesAngle:
|
|
8741
|
+
salesAngle: z41.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.'),
|
|
8451
8742
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
8452
8743
|
price: CreativeRefs.price,
|
|
8453
8744
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
@@ -8458,6 +8749,7 @@ var LandingInput = z40.object({
|
|
|
8458
8749
|
isCombo: CreativeRefs.isCombo,
|
|
8459
8750
|
competitorScanEnabled: CreativeRefs.competitorScanEnabled,
|
|
8460
8751
|
brandId: CreativeRefs.brandId,
|
|
8752
|
+
projectId: CreativeRefs.projectId,
|
|
8461
8753
|
creativeDirection: CreativeRefs.creativeDirection,
|
|
8462
8754
|
landingStrategy: LandingStrategyEnum.default("classic").describe('Opcional. Estructura de la landing cl\xE1sica: "classic" (estructura original probada y recomendada; default del primer rollout), "auto" (la IA decide; experimental), "pain_solution" (problema\u2192soluci\xF3n), "social_proof" (Landing Viral: UGC, WhatsApp, Instagram y evidencia social), "benefits_clarity" (beneficios claros), "offer_value" (promoci\xF3n/valor) o "lifestyle_identity" (estilo de vida). Default "classic".'),
|
|
8463
8755
|
landingImageFormat: LandingImageFormatEnum.default("9:16").describe('Opcional. Formato/relaci\xF3n de aspecto de las im\xE1genes de la landing cl\xE1sica: "9:16" (vertical, por defecto), "4:5" (vertical compacto para feed) o "1:1" (cuadrado). Default "9:16".')
|
|
@@ -8497,7 +8789,7 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
8497
8789
|
try {
|
|
8498
8790
|
const resolved = resolveVideoPreset(input.videoPreset);
|
|
8499
8791
|
const wireTier = resolveWireVideoTier(input.videoTier, input.durationSeconds);
|
|
8500
|
-
const { videoTier: _publicTier, videoResolution: _vr, ...rest } = input;
|
|
8792
|
+
const { videoTier: _publicTier, videoResolution: _vr, projectId, ...rest } = input;
|
|
8501
8793
|
const wirePayload = {
|
|
8502
8794
|
...rest
|
|
8503
8795
|
};
|
|
@@ -8507,6 +8799,7 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
8507
8799
|
}
|
|
8508
8800
|
wirePayload.videoTier = wireTier;
|
|
8509
8801
|
if (input.videoResolution) wirePayload.videoResolution = input.videoResolution;
|
|
8802
|
+
if (projectId) wirePayload.dropshippingProjectId = projectId;
|
|
8510
8803
|
return await submitCreative(api, {
|
|
8511
8804
|
path: "/v1/creatives/videos",
|
|
8512
8805
|
estimatedSeconds: 300,
|
|
@@ -8519,10 +8812,16 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
8519
8812
|
});
|
|
8520
8813
|
server.tool("generate_image_creatives", 'Genera N variantes de imagen creativa (ads est\xE1ticos). Devuelve un taskId.\n\nREQUERIDOS: images (array de 1-5 fotos), productTitle, primaryAngle, language, country.\n\nANTES de invocar, PREG\xDANTALE al usuario:\n- price y offer (se renderizan en la imagen \u2014 nunca los asumas; si dice "t\xFA elige", om\xEDtelos).\n- compareAtPrice si hay descuento (ej. "$129.000" para tachar).\n- creativeArchetype y visualTreatment si quiere un look espec\xEDfico (performance vs premium DTC). Si no le importa, om\xEDtelos.\n\nNO defaultes country/language desde el contexto: preg\xFAntale expl\xEDcitamente.\n\nOTROS OPCIONALES: buyerPersona, nVariants (default 3), imageModel, imageStyle, styleMix, aspectRatio (default 4:5), generateAdCopy, pauseForAngleReview, features, brandColors, isCombo, creativeDirection.', ImageInput.shape, async (input) => {
|
|
8521
8814
|
try {
|
|
8815
|
+
const { projectId, ...rest } = input;
|
|
8522
8816
|
return await submitCreative(api, {
|
|
8523
8817
|
path: "/v1/creatives/images",
|
|
8524
8818
|
estimatedSeconds: 60,
|
|
8525
|
-
input
|
|
8819
|
+
input: {
|
|
8820
|
+
...rest,
|
|
8821
|
+
...projectId ? {
|
|
8822
|
+
dropshippingProjectId: projectId
|
|
8823
|
+
} : {}
|
|
8824
|
+
},
|
|
8526
8825
|
transport
|
|
8527
8826
|
});
|
|
8528
8827
|
} catch (e) {
|
|
@@ -8531,10 +8830,16 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
8531
8830
|
});
|
|
8532
8831
|
server.tool("generate_landing", 'Genera una landing page cl\xE1sica basada en im\xE1genes, lista para Shopify. Devuelve un taskId.\n\nREQUERIDOS: images (array de 1-5 fotos), language, country.\n\nANTES de invocar, PREG\xDANTALE al usuario por price, offer y paymentMethods si no los mencion\xF3 \u2014 son los inputs de mayor impacto en conversi\xF3n y nunca debes asumirlos. Si hay descuento, pregunta tambi\xE9n compareAtPrice. NO defaultes country/language desde el contexto: preg\xFAntale expl\xEDcitamente. Si el usuario te dice "t\xFA elige" para esos campos, om\xEDtelos.\n\nOTROS OPCIONALES (puedes invocar sin ellos y dejar que la IA elija defaults): salesAngle, buyerPersona, isCombo, creativeDirection, landingStrategy.', LandingInput.shape, async (input) => {
|
|
8533
8832
|
try {
|
|
8833
|
+
const { projectId, ...rest } = input;
|
|
8534
8834
|
return await submitCreative(api, {
|
|
8535
8835
|
path: "/v1/creatives/landing",
|
|
8536
8836
|
estimatedSeconds: 60,
|
|
8537
|
-
input
|
|
8837
|
+
input: {
|
|
8838
|
+
...rest,
|
|
8839
|
+
...projectId ? {
|
|
8840
|
+
dropshippingProjectId: projectId
|
|
8841
|
+
} : {}
|
|
8842
|
+
},
|
|
8538
8843
|
transport
|
|
8539
8844
|
});
|
|
8540
8845
|
} catch (e) {
|
|
@@ -8545,8 +8850,8 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
8545
8850
|
__name(registerCreativeTools, "registerCreativeTools");
|
|
8546
8851
|
|
|
8547
8852
|
// src/tools/creative-variants.ts
|
|
8548
|
-
import { z as
|
|
8549
|
-
var LandingSectionKeyEnum =
|
|
8853
|
+
import { z as z42 } from "zod";
|
|
8854
|
+
var LandingSectionKeyEnum = z42.enum([
|
|
8550
8855
|
"problem_hook",
|
|
8551
8856
|
"problem_cost",
|
|
8552
8857
|
"solution_mechanism",
|
|
@@ -8586,24 +8891,24 @@ var LandingSectionKeyEnum = z41.enum([
|
|
|
8586
8891
|
"lifestyle_fit",
|
|
8587
8892
|
"join_lifestyle"
|
|
8588
8893
|
]);
|
|
8589
|
-
var CreativeVariantKindEnum =
|
|
8894
|
+
var CreativeVariantKindEnum = z42.enum([
|
|
8590
8895
|
"video",
|
|
8591
8896
|
"image",
|
|
8592
8897
|
"landing"
|
|
8593
8898
|
]);
|
|
8594
|
-
var EstimateCreativeVariantInputBase =
|
|
8595
|
-
sourceTaskId:
|
|
8899
|
+
var EstimateCreativeVariantInputBase = z42.object({
|
|
8900
|
+
sourceTaskId: z42.string().uuid().describe("ID de la tarea original completada."),
|
|
8596
8901
|
kind: CreativeVariantKindEnum,
|
|
8597
8902
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Solo cambia el preset p\xFAblico; el servidor conserva el modelo/tier del task origen."),
|
|
8598
8903
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video. Resoluci\xF3n del nuevo render."),
|
|
8599
|
-
nVariants:
|
|
8904
|
+
nVariants: z42.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
8600
8905
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si ya conoces la secci\xF3n, evita cotizar la clasificaci\xF3n con IA."),
|
|
8601
|
-
instructions:
|
|
8906
|
+
instructions: z42.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.")
|
|
8602
8907
|
}).strict();
|
|
8603
8908
|
var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
8604
8909
|
if (input.kind === "video" && input.nVariants !== void 0) {
|
|
8605
8910
|
ctx.addIssue({
|
|
8606
|
-
code:
|
|
8911
|
+
code: z42.ZodIssueCode.custom,
|
|
8607
8912
|
path: [
|
|
8608
8913
|
"nVariants"
|
|
8609
8914
|
],
|
|
@@ -8613,7 +8918,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
8613
8918
|
if (input.kind !== "video") {
|
|
8614
8919
|
if (input.videoPreset !== void 0) {
|
|
8615
8920
|
ctx.addIssue({
|
|
8616
|
-
code:
|
|
8921
|
+
code: z42.ZodIssueCode.custom,
|
|
8617
8922
|
path: [
|
|
8618
8923
|
"videoPreset"
|
|
8619
8924
|
],
|
|
@@ -8622,7 +8927,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
8622
8927
|
}
|
|
8623
8928
|
if (input.videoResolution !== void 0) {
|
|
8624
8929
|
ctx.addIssue({
|
|
8625
|
-
code:
|
|
8930
|
+
code: z42.ZodIssueCode.custom,
|
|
8626
8931
|
path: [
|
|
8627
8932
|
"videoResolution"
|
|
8628
8933
|
],
|
|
@@ -8632,7 +8937,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
8632
8937
|
}
|
|
8633
8938
|
if (input.kind !== "image" && input.nVariants !== void 0) {
|
|
8634
8939
|
ctx.addIssue({
|
|
8635
|
-
code:
|
|
8940
|
+
code: z42.ZodIssueCode.custom,
|
|
8636
8941
|
path: [
|
|
8637
8942
|
"nVariants"
|
|
8638
8943
|
],
|
|
@@ -8642,7 +8947,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
8642
8947
|
if (input.kind !== "landing") {
|
|
8643
8948
|
if (input.sectionKey !== void 0) {
|
|
8644
8949
|
ctx.addIssue({
|
|
8645
|
-
code:
|
|
8950
|
+
code: z42.ZodIssueCode.custom,
|
|
8646
8951
|
path: [
|
|
8647
8952
|
"sectionKey"
|
|
8648
8953
|
],
|
|
@@ -8651,7 +8956,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
8651
8956
|
}
|
|
8652
8957
|
if (input.instructions !== void 0) {
|
|
8653
8958
|
ctx.addIssue({
|
|
8654
|
-
code:
|
|
8959
|
+
code: z42.ZodIssueCode.custom,
|
|
8655
8960
|
path: [
|
|
8656
8961
|
"instructions"
|
|
8657
8962
|
],
|
|
@@ -8660,31 +8965,31 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
8660
8965
|
}
|
|
8661
8966
|
}
|
|
8662
8967
|
});
|
|
8663
|
-
var CreateCreativeVariantInputBase =
|
|
8664
|
-
sourceTaskId:
|
|
8968
|
+
var CreateCreativeVariantInputBase = z42.object({
|
|
8969
|
+
sourceTaskId: z42.string().uuid().describe("ID de la tarea original completada."),
|
|
8665
8970
|
kind: CreativeVariantKindEnum,
|
|
8666
|
-
confirm:
|
|
8667
|
-
clientRequestId:
|
|
8668
|
-
angle:
|
|
8669
|
-
buyerPersona:
|
|
8670
|
-
scriptNotes:
|
|
8671
|
-
creativeDirection:
|
|
8971
|
+
confirm: z42.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
|
|
8972
|
+
clientRequestId: z42.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."),
|
|
8973
|
+
angle: z42.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
|
|
8974
|
+
buyerPersona: z42.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
|
|
8975
|
+
scriptNotes: z42.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
|
|
8976
|
+
creativeDirection: z42.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
|
|
8672
8977
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Preset p\xFAblico; el servidor resuelve modelo/tier desde el task origen."),
|
|
8673
8978
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video."),
|
|
8674
|
-
generateAiMusic:
|
|
8675
|
-
nVariants:
|
|
8979
|
+
generateAiMusic: z42.boolean().optional().describe("Opcional para video."),
|
|
8980
|
+
nVariants: z42.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
8676
8981
|
imageStyle: ImageCreativeStyleEnum.optional().describe("Opcional para imagen."),
|
|
8677
8982
|
creativeArchetype: ImageCreativeArchetypeEnum.optional().describe("Opcional para imagen."),
|
|
8678
8983
|
visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe("Opcional para imagen."),
|
|
8679
8984
|
aspectRatio: ImageCreativeAspectRatioEnum.optional().describe("Opcional para imagen."),
|
|
8680
|
-
styleMix:
|
|
8985
|
+
styleMix: z42.boolean().optional().describe("Opcional para imagen."),
|
|
8681
8986
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si se omite, se clasifica con IA a partir de instructions."),
|
|
8682
|
-
instructions:
|
|
8987
|
+
instructions: z42.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.")
|
|
8683
8988
|
}).strict();
|
|
8684
8989
|
var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
8685
8990
|
if ((input.kind === "video" || input.kind === "image") && !input.angle) {
|
|
8686
8991
|
ctx.addIssue({
|
|
8687
|
-
code:
|
|
8992
|
+
code: z42.ZodIssueCode.custom,
|
|
8688
8993
|
path: [
|
|
8689
8994
|
"angle"
|
|
8690
8995
|
],
|
|
@@ -8693,7 +8998,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
8693
8998
|
}
|
|
8694
8999
|
if (input.kind === "landing" && !input.sectionKey && !input.instructions) {
|
|
8695
9000
|
ctx.addIssue({
|
|
8696
|
-
code:
|
|
9001
|
+
code: z42.ZodIssueCode.custom,
|
|
8697
9002
|
path: [
|
|
8698
9003
|
"instructions"
|
|
8699
9004
|
],
|
|
@@ -8739,7 +9044,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
8739
9044
|
for (const field of fieldsByKind[input.kind]) {
|
|
8740
9045
|
if (input[field] !== void 0) {
|
|
8741
9046
|
ctx.addIssue({
|
|
8742
|
-
code:
|
|
9047
|
+
code: z42.ZodIssueCode.custom,
|
|
8743
9048
|
path: [
|
|
8744
9049
|
field
|
|
8745
9050
|
],
|
|
@@ -8856,27 +9161,27 @@ function registerCreativeVariantTools(server, api) {
|
|
|
8856
9161
|
__name(registerCreativeVariantTools, "registerCreativeVariantTools");
|
|
8857
9162
|
|
|
8858
9163
|
// src/tools/script-review.ts
|
|
8859
|
-
import { z as
|
|
8860
|
-
var GetVideoScriptInput =
|
|
8861
|
-
taskId:
|
|
8862
|
-
angleKey:
|
|
9164
|
+
import { z as z43 } from "zod";
|
|
9165
|
+
var GetVideoScriptInput = z43.object({
|
|
9166
|
+
taskId: z43.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
|
|
9167
|
+
angleKey: z43.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.')
|
|
8863
9168
|
}).strict();
|
|
8864
9169
|
var VIDEO_SCRIPT_APPROVAL_MAX_SCENES2 = 50;
|
|
8865
9170
|
var VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2 = 2e3;
|
|
8866
|
-
var ApproveSceneEdit =
|
|
8867
|
-
index:
|
|
8868
|
-
dialogue:
|
|
9171
|
+
var ApproveSceneEdit = z43.object({
|
|
9172
|
+
index: z43.number().int().nonnegative().describe("\xCDndice de la escena (0-based, mismo que devuelve get_video_script)."),
|
|
9173
|
+
dialogue: z43.string().min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2).refine((v) => v.trim().length > 0, {
|
|
8869
9174
|
message: "dialogue no puede ser s\xF3lo espacios"
|
|
8870
9175
|
}).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.")
|
|
8871
9176
|
}).strict();
|
|
8872
|
-
var ApproveVideoScriptInput =
|
|
8873
|
-
taskId:
|
|
8874
|
-
angleKey:
|
|
8875
|
-
scenes:
|
|
9177
|
+
var ApproveVideoScriptInput = z43.object({
|
|
9178
|
+
taskId: z43.string().min(1).describe("ID de la tarea."),
|
|
9179
|
+
angleKey: z43.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.'),
|
|
9180
|
+
scenes: z43.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.")
|
|
8876
9181
|
}).strict();
|
|
8877
|
-
var RegenerateVideoScriptInput =
|
|
8878
|
-
taskId:
|
|
8879
|
-
angleKey:
|
|
9182
|
+
var RegenerateVideoScriptInput = z43.object({
|
|
9183
|
+
taskId: z43.string().min(1).describe("ID de la tarea."),
|
|
9184
|
+
angleKey: z43.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.'),
|
|
8880
9185
|
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).')
|
|
8881
9186
|
}).strict();
|
|
8882
9187
|
function registerScriptReviewTools(server, api) {
|
|
@@ -8916,9 +9221,9 @@ function registerScriptReviewTools(server, api) {
|
|
|
8916
9221
|
__name(registerScriptReviewTools, "registerScriptReviewTools");
|
|
8917
9222
|
|
|
8918
9223
|
// src/tools/connections.ts
|
|
8919
|
-
import { z as
|
|
9224
|
+
import { z as z44 } from "zod";
|
|
8920
9225
|
function registerConnectionTools(server, api) {
|
|
8921
|
-
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.",
|
|
9226
|
+
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.", z44.object({}).shape, async () => {
|
|
8922
9227
|
try {
|
|
8923
9228
|
const r = await api.getJson("/v1/shopify/shops");
|
|
8924
9229
|
return asText(r);
|
|
@@ -8926,10 +9231,10 @@ function registerConnectionTools(server, api) {
|
|
|
8926
9231
|
throw translateError(e);
|
|
8927
9232
|
}
|
|
8928
9233
|
});
|
|
8929
|
-
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.',
|
|
8930
|
-
shopDomain:
|
|
8931
|
-
query:
|
|
8932
|
-
taskId:
|
|
9234
|
+
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.', z44.object({
|
|
9235
|
+
shopDomain: z44.string().min(3).max(120),
|
|
9236
|
+
query: z44.string().trim().max(120).optional(),
|
|
9237
|
+
taskId: z44.string().uuid().optional()
|
|
8933
9238
|
}).shape, async ({ shopDomain, query, taskId }) => {
|
|
8934
9239
|
try {
|
|
8935
9240
|
const params = new URLSearchParams({
|
|
@@ -8943,7 +9248,7 @@ function registerConnectionTools(server, api) {
|
|
|
8943
9248
|
throw translateError(e);
|
|
8944
9249
|
}
|
|
8945
9250
|
});
|
|
8946
|
-
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.",
|
|
9251
|
+
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.", z44.object({}).shape, async () => {
|
|
8947
9252
|
try {
|
|
8948
9253
|
const r = await api.getJson("/v1/facebook/ad-accounts");
|
|
8949
9254
|
return asText(r);
|
|
@@ -8951,7 +9256,7 @@ function registerConnectionTools(server, api) {
|
|
|
8951
9256
|
throw translateError(e);
|
|
8952
9257
|
}
|
|
8953
9258
|
});
|
|
8954
|
-
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.",
|
|
9259
|
+
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.", z44.object({}).shape, async () => {
|
|
8955
9260
|
try {
|
|
8956
9261
|
const r = await api.getJson("/v1/tiktok/advertisers");
|
|
8957
9262
|
return asText(r);
|
|
@@ -8963,105 +9268,105 @@ function registerConnectionTools(server, api) {
|
|
|
8963
9268
|
__name(registerConnectionTools, "registerConnectionTools");
|
|
8964
9269
|
|
|
8965
9270
|
// src/tools/publish.ts
|
|
8966
|
-
import { z as
|
|
8967
|
-
var ShopifyInput =
|
|
8968
|
-
taskId:
|
|
8969
|
-
shopDomain:
|
|
9271
|
+
import { z as z45 } from "zod";
|
|
9272
|
+
var ShopifyInput = z45.object({
|
|
9273
|
+
taskId: z45.string().min(1),
|
|
9274
|
+
shopDomain: z45.string().min(1),
|
|
8970
9275
|
/** Override: number of landing images to include. Default = ALL images the
|
|
8971
9276
|
* task generated (the MCP fetches /v1/results first to count). Bounded
|
|
8972
9277
|
* generously here; the backend validates each index against
|
|
8973
9278
|
* landing.images.length and rejects out-of-range. */
|
|
8974
|
-
numLandingImages:
|
|
8975
|
-
variantIndex:
|
|
9279
|
+
numLandingImages: z45.number().int().min(1).max(20).optional(),
|
|
9280
|
+
variantIndex: z45.number().int().min(0).optional(),
|
|
8976
9281
|
/** ASK THE USER before setting: "¿Tu producto tiene variantes (talla, color, etc.)?".
|
|
8977
9282
|
* When true, the landing renders variant-picker pills; when false, it ships
|
|
8978
9283
|
* a single-option product page. Defaults to false. */
|
|
8979
|
-
showVariantPicker:
|
|
9284
|
+
showVariantPicker: z45.boolean().optional(),
|
|
8980
9285
|
/** Default `auto` updates the product previously published for this task and
|
|
8981
9286
|
* shop. Use `existing` with targetProductId to choose another product, or
|
|
8982
9287
|
* `new` only when the user explicitly wants a duplicate/new listing. */
|
|
8983
|
-
publicationMode:
|
|
9288
|
+
publicationMode: z45.enum([
|
|
8984
9289
|
"auto",
|
|
8985
9290
|
"existing",
|
|
8986
9291
|
"new"
|
|
8987
9292
|
]).optional(),
|
|
8988
9293
|
/** Shopify product GID, numeric id, or admin product URL. Required when
|
|
8989
9294
|
* publicationMode is `existing`. */
|
|
8990
|
-
targetProductId:
|
|
9295
|
+
targetProductId: z45.string().trim().min(1).max(300).optional()
|
|
8991
9296
|
});
|
|
8992
|
-
var McpCampaignConfig =
|
|
9297
|
+
var McpCampaignConfig = z45.object({
|
|
8993
9298
|
/** ALWAYS in US dollars, whatever the ad account's currency is. ProductMaker
|
|
8994
9299
|
* converts to the account currency server-side. Do NOT pre-convert: on a COP
|
|
8995
9300
|
* account the user's "60.000 al día" is `dailyBudgetUsd: 15`, not 60000. */
|
|
8996
|
-
dailyBudgetUsd:
|
|
9301
|
+
dailyBudgetUsd: z45.number().positive().optional(),
|
|
8997
9302
|
/** ISO-3166-1 alpha-2 country codes. Meta accepts max 1 (single-country
|
|
8998
9303
|
* ad set). TikTok takes only the first entry. */
|
|
8999
|
-
targetCountries:
|
|
9000
|
-
campaignName:
|
|
9304
|
+
targetCountries: z45.array(z45.string().length(2)).max(1).optional(),
|
|
9305
|
+
campaignName: z45.string().trim().min(1).max(100).optional()
|
|
9001
9306
|
});
|
|
9002
9307
|
var McpAdAccountId = MetaAdAccountId.describe('Meta ad account id. Accepts "act_1234567890" or the bare numeric id "1234567890".');
|
|
9003
|
-
var MetaInput =
|
|
9004
|
-
taskId:
|
|
9308
|
+
var MetaInput = z45.object({
|
|
9309
|
+
taskId: z45.string().min(1),
|
|
9005
9310
|
adAccountId: McpAdAccountId,
|
|
9006
|
-
pixelId:
|
|
9007
|
-
landingUrl:
|
|
9311
|
+
pixelId: z45.string().min(1).optional(),
|
|
9312
|
+
landingUrl: z45.string().url().optional(),
|
|
9008
9313
|
/** Select which angles to publish by 0-based index. Omit to publish all
|
|
9009
9314
|
* successful angles. Example: a task with 3 angles → `[0,2]` publishes #0 + #2. */
|
|
9010
|
-
angleIndexes:
|
|
9315
|
+
angleIndexes: z45.array(z45.number().int().nonnegative()).optional(),
|
|
9011
9316
|
campaignConfig: McpCampaignConfig.optional(),
|
|
9012
9317
|
/** Override the ad's primary text (body copy). If omitted, ProductMaker
|
|
9013
9318
|
* uses the auto-generated copy attached to the task. Capped at 500 chars
|
|
9014
9319
|
* (Meta's primary-text limit). */
|
|
9015
|
-
primaryText:
|
|
9320
|
+
primaryText: z45.string().trim().min(1).max(500).optional(),
|
|
9016
9321
|
/** Optional WhatsApp number. When set, the ad button opens a WhatsApp chat
|
|
9017
9322
|
* (wa.me) instead of a landing page. */
|
|
9018
|
-
phoneNumber:
|
|
9323
|
+
phoneNumber: z45.string().min(8).max(32).optional()
|
|
9019
9324
|
});
|
|
9020
|
-
var GroupedTaskSource =
|
|
9021
|
-
type:
|
|
9022
|
-
taskId:
|
|
9023
|
-
angleIndex:
|
|
9024
|
-
videoVariant:
|
|
9325
|
+
var GroupedTaskSource = z45.object({
|
|
9326
|
+
type: z45.literal("task"),
|
|
9327
|
+
taskId: z45.string().min(1),
|
|
9328
|
+
angleIndex: z45.number().int().min(0).optional(),
|
|
9329
|
+
videoVariant: z45.enum([
|
|
9025
9330
|
"with_subtitles",
|
|
9026
9331
|
"without_subtitles"
|
|
9027
9332
|
]).optional(),
|
|
9028
|
-
imageVariantIndex:
|
|
9029
|
-
});
|
|
9030
|
-
var GroupedMediaSource =
|
|
9031
|
-
type:
|
|
9032
|
-
url:
|
|
9033
|
-
path:
|
|
9034
|
-
thumbnailUrl:
|
|
9035
|
-
thumbnailPath:
|
|
9333
|
+
imageVariantIndex: z45.number().int().min(0).optional()
|
|
9334
|
+
});
|
|
9335
|
+
var GroupedMediaSource = z45.object({
|
|
9336
|
+
type: z45.literal("media"),
|
|
9337
|
+
url: z45.string().url().optional(),
|
|
9338
|
+
path: z45.string().min(1).optional(),
|
|
9339
|
+
thumbnailUrl: z45.string().url().optional(),
|
|
9340
|
+
thumbnailPath: z45.string().min(1).optional()
|
|
9036
9341
|
}).refine((value) => Boolean(value.url) !== Boolean(value.path), {
|
|
9037
9342
|
message: "media source needs exactly one of `url` or `path`"
|
|
9038
9343
|
}).refine((value) => !(value.thumbnailUrl && value.thumbnailPath), {
|
|
9039
9344
|
message: "media source needs at most one of `thumbnailUrl` or `thumbnailPath`"
|
|
9040
9345
|
});
|
|
9041
|
-
var GroupedCreative =
|
|
9042
|
-
type:
|
|
9346
|
+
var GroupedCreative = z45.object({
|
|
9347
|
+
type: z45.enum([
|
|
9043
9348
|
"image",
|
|
9044
9349
|
"video"
|
|
9045
9350
|
]),
|
|
9046
|
-
source:
|
|
9351
|
+
source: z45.union([
|
|
9047
9352
|
GroupedTaskSource,
|
|
9048
9353
|
GroupedMediaSource
|
|
9049
9354
|
]),
|
|
9050
|
-
title:
|
|
9051
|
-
description:
|
|
9355
|
+
title: z45.string().max(120).optional(),
|
|
9356
|
+
description: z45.string().max(500).optional(),
|
|
9052
9357
|
cta: MetaCtaType.optional(),
|
|
9053
|
-
creativeId:
|
|
9358
|
+
creativeId: z45.string().min(1).optional()
|
|
9054
9359
|
});
|
|
9055
|
-
var GroupedCampaignConfig =
|
|
9360
|
+
var GroupedCampaignConfig = z45.object({
|
|
9056
9361
|
campaignType: MetaCampaignType.default("sales"),
|
|
9057
9362
|
optimizationMode: MetaOptimizationMode.default("advantage_plus"),
|
|
9058
9363
|
budgetMode: MetaBudgetMode.default("campaign"),
|
|
9059
|
-
campaignDailyBudget:
|
|
9060
|
-
adSets:
|
|
9061
|
-
tempId:
|
|
9062
|
-
name:
|
|
9063
|
-
dailyBudget:
|
|
9064
|
-
creativeIds:
|
|
9364
|
+
campaignDailyBudget: z45.number().int().positive().optional(),
|
|
9365
|
+
adSets: z45.array(z45.object({
|
|
9366
|
+
tempId: z45.string().min(1).max(80),
|
|
9367
|
+
name: z45.string().min(1).max(100),
|
|
9368
|
+
dailyBudget: z45.number().int().positive().optional(),
|
|
9369
|
+
creativeIds: z45.array(z45.string().min(1)).min(1)
|
|
9065
9370
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_AD_SETS),
|
|
9066
9371
|
/**
|
|
9067
9372
|
* Valid values are 'landing' and 'whatsapp' — there is no 'website'. This
|
|
@@ -9069,22 +9374,22 @@ var GroupedCampaignConfig = z44.object({
|
|
|
9069
9374
|
* opaque `invalid_literal` and made the WhatsApp destination — supported by
|
|
9070
9375
|
* the API since #849 — unreachable from this tool.
|
|
9071
9376
|
*/
|
|
9072
|
-
destination:
|
|
9073
|
-
|
|
9074
|
-
type:
|
|
9075
|
-
landingUrl:
|
|
9377
|
+
destination: z45.discriminatedUnion("type", [
|
|
9378
|
+
z45.object({
|
|
9379
|
+
type: z45.literal("landing"),
|
|
9380
|
+
landingUrl: z45.string().url()
|
|
9076
9381
|
}),
|
|
9077
|
-
|
|
9078
|
-
type:
|
|
9079
|
-
phoneNumber:
|
|
9382
|
+
z45.object({
|
|
9383
|
+
type: z45.literal("whatsapp"),
|
|
9384
|
+
phoneNumber: z45.string().min(8).max(32)
|
|
9080
9385
|
})
|
|
9081
9386
|
]),
|
|
9082
|
-
saveAsDefault:
|
|
9387
|
+
saveAsDefault: z45.boolean().default(false),
|
|
9083
9388
|
budgetPolicy: MetaBudgetPolicy.optional()
|
|
9084
9389
|
}).superRefine((value, ctx) => {
|
|
9085
9390
|
if (value.budgetMode === "campaign" && value.campaignDailyBudget === void 0) {
|
|
9086
9391
|
ctx.addIssue({
|
|
9087
|
-
code:
|
|
9392
|
+
code: z45.ZodIssueCode.custom,
|
|
9088
9393
|
path: [
|
|
9089
9394
|
"campaignDailyBudget"
|
|
9090
9395
|
],
|
|
@@ -9095,7 +9400,7 @@ var GroupedCampaignConfig = z44.object({
|
|
|
9095
9400
|
value.adSets.forEach((adSet, index) => {
|
|
9096
9401
|
if (adSet.dailyBudget === void 0) {
|
|
9097
9402
|
ctx.addIssue({
|
|
9098
|
-
code:
|
|
9403
|
+
code: z45.ZodIssueCode.custom,
|
|
9099
9404
|
path: [
|
|
9100
9405
|
"adSets",
|
|
9101
9406
|
index,
|
|
@@ -9107,22 +9412,22 @@ var GroupedCampaignConfig = z44.object({
|
|
|
9107
9412
|
});
|
|
9108
9413
|
}
|
|
9109
9414
|
});
|
|
9110
|
-
var PublishCreativesToMetaInputBase =
|
|
9111
|
-
creatives:
|
|
9415
|
+
var PublishCreativesToMetaInputBase = z45.object({
|
|
9416
|
+
creatives: z45.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
9112
9417
|
adAccountId: McpAdAccountId,
|
|
9113
|
-
pixelId:
|
|
9418
|
+
pixelId: z45.string().min(1).optional(),
|
|
9114
9419
|
/** Required unless `campaignConfig.destination` is whatsapp — see the
|
|
9115
9420
|
* superRefine below. Mirrors the API's `UploadToMetaRequest`. */
|
|
9116
|
-
landingUrl:
|
|
9117
|
-
targetCountry:
|
|
9118
|
-
campaignName:
|
|
9421
|
+
landingUrl: z45.string().url().optional(),
|
|
9422
|
+
targetCountry: z45.string().length(2),
|
|
9423
|
+
campaignName: z45.string().trim().min(1).max(100),
|
|
9119
9424
|
campaignConfig: GroupedCampaignConfig.optional()
|
|
9120
9425
|
});
|
|
9121
9426
|
var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((value, ctx) => {
|
|
9122
9427
|
const destination = value.campaignConfig?.destination;
|
|
9123
9428
|
if (destination?.type !== "whatsapp" && !value.landingUrl) {
|
|
9124
9429
|
ctx.addIssue({
|
|
9125
|
-
code:
|
|
9430
|
+
code: z45.ZodIssueCode.custom,
|
|
9126
9431
|
path: [
|
|
9127
9432
|
"landingUrl"
|
|
9128
9433
|
],
|
|
@@ -9131,7 +9436,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
9131
9436
|
}
|
|
9132
9437
|
if (destination?.type === "landing" && destination.landingUrl !== value.landingUrl) {
|
|
9133
9438
|
ctx.addIssue({
|
|
9134
|
-
code:
|
|
9439
|
+
code: z45.ZodIssueCode.custom,
|
|
9135
9440
|
path: [
|
|
9136
9441
|
"campaignConfig",
|
|
9137
9442
|
"destination",
|
|
@@ -9141,12 +9446,12 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
9141
9446
|
});
|
|
9142
9447
|
}
|
|
9143
9448
|
});
|
|
9144
|
-
var PresignUploadResponseSchema =
|
|
9145
|
-
uploadUrl:
|
|
9449
|
+
var PresignUploadResponseSchema = z45.object({
|
|
9450
|
+
uploadUrl: z45.string().url().refine((value) => {
|
|
9146
9451
|
const url = new URL(value);
|
|
9147
9452
|
return !url.username && !url.password && url.protocol === "https:";
|
|
9148
9453
|
}, "presigned upload URL must be HTTPS without embedded credentials"),
|
|
9149
|
-
publicUrl:
|
|
9454
|
+
publicUrl: z45.string().url().refine((value) => {
|
|
9150
9455
|
const url = new URL(value);
|
|
9151
9456
|
return !url.username && !url.password;
|
|
9152
9457
|
}, "public media URL must not contain embedded credentials")
|
|
@@ -9171,16 +9476,16 @@ async function resolveAndUploadMedia(api, ref, expectedKind, transport) {
|
|
|
9171
9476
|
return uploadResolvedMedia(api, media);
|
|
9172
9477
|
}
|
|
9173
9478
|
__name(resolveAndUploadMedia, "resolveAndUploadMedia");
|
|
9174
|
-
var TikTokInput =
|
|
9175
|
-
taskId:
|
|
9176
|
-
advertiserId:
|
|
9177
|
-
pixelId:
|
|
9178
|
-
landingUrl:
|
|
9179
|
-
angleIndexes:
|
|
9479
|
+
var TikTokInput = z45.object({
|
|
9480
|
+
taskId: z45.string().min(1),
|
|
9481
|
+
advertiserId: z45.string().min(1),
|
|
9482
|
+
pixelId: z45.string().min(1).optional(),
|
|
9483
|
+
landingUrl: z45.string().url().optional(),
|
|
9484
|
+
angleIndexes: z45.array(z45.number().int().nonnegative()).optional(),
|
|
9180
9485
|
campaignConfig: McpCampaignConfig.optional(),
|
|
9181
9486
|
/** Override the ad's description (TikTok's body copy). If omitted,
|
|
9182
9487
|
* ProductMaker uses the auto-generated copy attached to the task. */
|
|
9183
|
-
primaryText:
|
|
9488
|
+
primaryText: z45.string().trim().min(1).max(500).optional()
|
|
9184
9489
|
});
|
|
9185
9490
|
function registerPublishTools(server, api, opts = {}) {
|
|
9186
9491
|
const transport = opts.transport ?? "http";
|
|
@@ -9391,13 +9696,13 @@ function toTikTokCampaignConfig(cfg) {
|
|
|
9391
9696
|
__name(toTikTokCampaignConfig, "toTikTokCampaignConfig");
|
|
9392
9697
|
|
|
9393
9698
|
// src/tools/campaigns.ts
|
|
9394
|
-
import { z as
|
|
9395
|
-
var MetaStatusInput =
|
|
9396
|
-
campaignId:
|
|
9699
|
+
import { z as z46 } from "zod";
|
|
9700
|
+
var MetaStatusInput = z46.object({
|
|
9701
|
+
campaignId: z46.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
|
|
9397
9702
|
});
|
|
9398
|
-
var TikTokStatusInput =
|
|
9399
|
-
campaignId:
|
|
9400
|
-
advertiserId:
|
|
9703
|
+
var TikTokStatusInput = z46.object({
|
|
9704
|
+
campaignId: z46.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
|
|
9705
|
+
advertiserId: z46.string().min(1).describe("The same advertiserId used to publish the campaign.")
|
|
9401
9706
|
});
|
|
9402
9707
|
function registerCampaignTools(server, api) {
|
|
9403
9708
|
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) => {
|
|
@@ -9494,10 +9799,10 @@ function renderTikTokStatusMarkdown(r) {
|
|
|
9494
9799
|
__name(renderTikTokStatusMarkdown, "renderTikTokStatusMarkdown");
|
|
9495
9800
|
|
|
9496
9801
|
// ../../packages/product-economics/src/product-costing.ts
|
|
9497
|
-
import { z as
|
|
9802
|
+
import { z as z48 } from "zod";
|
|
9498
9803
|
|
|
9499
9804
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
9500
|
-
import { z as
|
|
9805
|
+
import { z as z47 } from "zod";
|
|
9501
9806
|
|
|
9502
9807
|
// ../../packages/product-economics/src/product-costing-shipping-references.json
|
|
9503
9808
|
var product_costing_shipping_references_default = {
|
|
@@ -9561,21 +9866,21 @@ var product_costing_shipping_references_default = {
|
|
|
9561
9866
|
};
|
|
9562
9867
|
|
|
9563
9868
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
9564
|
-
var ShippingReferenceSchema =
|
|
9565
|
-
reference:
|
|
9566
|
-
min:
|
|
9567
|
-
max:
|
|
9568
|
-
updatedAt:
|
|
9569
|
-
confidence:
|
|
9869
|
+
var ShippingReferenceSchema = z47.object({
|
|
9870
|
+
reference: z47.number().positive(),
|
|
9871
|
+
min: z47.number().nonnegative(),
|
|
9872
|
+
max: z47.number().positive(),
|
|
9873
|
+
updatedAt: z47.string().date(),
|
|
9874
|
+
confidence: z47.enum([
|
|
9570
9875
|
"high",
|
|
9571
9876
|
"medium",
|
|
9572
9877
|
"medium-low"
|
|
9573
9878
|
]),
|
|
9574
|
-
sources:
|
|
9879
|
+
sources: z47.array(z47.string().url().startsWith("https://")).min(1).max(3)
|
|
9575
9880
|
}).superRefine(({ reference, min, max }, context) => {
|
|
9576
9881
|
if (min > reference || reference > max) {
|
|
9577
9882
|
context.addIssue({
|
|
9578
|
-
code:
|
|
9883
|
+
code: z47.ZodIssueCode.custom,
|
|
9579
9884
|
message: "shipping_reference_outside_range",
|
|
9580
9885
|
path: [
|
|
9581
9886
|
"reference"
|
|
@@ -9583,7 +9888,7 @@ var ShippingReferenceSchema = z46.object({
|
|
|
9583
9888
|
});
|
|
9584
9889
|
}
|
|
9585
9890
|
});
|
|
9586
|
-
var SHIPPING_REFERENCES =
|
|
9891
|
+
var SHIPPING_REFERENCES = z47.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
|
|
9587
9892
|
var PRODUCT_COSTING_COUNTRY_CODES = Object.freeze(Object.keys(SHIPPING_REFERENCES));
|
|
9588
9893
|
var SPANISH_LOCALE_COUNTRIES = /* @__PURE__ */ new Set([
|
|
9589
9894
|
"MX",
|
|
@@ -9765,18 +10070,18 @@ function getProductCostingCountryName(country) {
|
|
|
9765
10070
|
__name(getProductCostingCountryName, "getProductCostingCountryName");
|
|
9766
10071
|
|
|
9767
10072
|
// ../../packages/product-economics/src/product-costing.ts
|
|
9768
|
-
var ProductCostingInputSchema =
|
|
9769
|
-
country:
|
|
9770
|
-
supplierCost:
|
|
9771
|
-
shippingCost:
|
|
9772
|
-
returnRate:
|
|
9773
|
-
cancellationRate:
|
|
9774
|
-
adsManagerCpa:
|
|
9775
|
-
desiredMargin:
|
|
10073
|
+
var ProductCostingInputSchema = z48.object({
|
|
10074
|
+
country: z48.string().refine(isProductCostingCountryCode, "unsupported_country"),
|
|
10075
|
+
supplierCost: z48.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
|
|
10076
|
+
shippingCost: z48.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
|
|
10077
|
+
returnRate: z48.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
10078
|
+
cancellationRate: z48.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
10079
|
+
adsManagerCpa: z48.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
|
|
10080
|
+
desiredMargin: z48.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
|
|
9776
10081
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
9777
10082
|
if (returnRate + cancellationRate >= 1) {
|
|
9778
10083
|
context.addIssue({
|
|
9779
|
-
code:
|
|
10084
|
+
code: z48.ZodIssueCode.custom,
|
|
9780
10085
|
message: "delivery_rate_positive",
|
|
9781
10086
|
path: [
|
|
9782
10087
|
"cancellationRate"
|
|
@@ -9843,14 +10148,14 @@ function calculateProductCosting(rawInput) {
|
|
|
9843
10148
|
__name(calculateProductCosting, "calculateProductCosting");
|
|
9844
10149
|
|
|
9845
10150
|
// src/tools/product-costing.ts
|
|
9846
|
-
import { z as
|
|
9847
|
-
var optionalAmount = /* @__PURE__ */ __name((description) =>
|
|
9848
|
-
var optionalPercent = /* @__PURE__ */ __name((description) =>
|
|
9849
|
-
var ProductCostingToolInput =
|
|
9850
|
-
country:
|
|
10151
|
+
import { z as z49 } from "zod";
|
|
10152
|
+
var optionalAmount = /* @__PURE__ */ __name((description) => z49.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
|
|
10153
|
+
var optionalPercent = /* @__PURE__ */ __name((description) => z49.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
|
|
10154
|
+
var ProductCostingToolInput = z49.object({
|
|
10155
|
+
country: z49.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
|
|
9851
10156
|
message: "Pa\xEDs no soportado por ProductMaker."
|
|
9852
10157
|
}).optional().describe("C\xF3digo ISO-2 de cualquiera de los pa\xEDses disponibles en ProductMaker. Omite para Colombia."),
|
|
9853
|
-
supplierCost:
|
|
10158
|
+
supplierCost: z49.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."),
|
|
9854
10159
|
supplierCostCop: optionalAmount("Alias anterior para costo del proveedor en Colombia. Prefiere supplierCost."),
|
|
9855
10160
|
shippingCost: optionalAmount("Costo de un env\xEDo en la moneda del pa\xEDs. Omite para usar el valor gu\xEDa vigente."),
|
|
9856
10161
|
shippingCostCop: optionalAmount("Alias anterior para flete en Colombia. Prefiere shippingCost."),
|
|
@@ -9982,7 +10287,7 @@ function formatPercent(value) {
|
|
|
9982
10287
|
__name(formatPercent, "formatPercent");
|
|
9983
10288
|
|
|
9984
10289
|
// src/tools/product-research.ts
|
|
9985
|
-
import { z as
|
|
10290
|
+
import { z as z50 } from "zod";
|
|
9986
10291
|
|
|
9987
10292
|
// src/tools/product-research-idempotency.ts
|
|
9988
10293
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -10098,17 +10403,17 @@ var TERMINAL2 = /* @__PURE__ */ new Set([
|
|
|
10098
10403
|
]);
|
|
10099
10404
|
var POLL_INTERVAL_MS = 5e3;
|
|
10100
10405
|
var MAX_WAIT_SECONDS = 120;
|
|
10101
|
-
var FindInput =
|
|
10406
|
+
var FindInput = z50.object({
|
|
10102
10407
|
targetCountry: ResearchTargetCountryEnum.describe("Pa\xEDs objetivo. S\xF3lo CO, MX, PE, EC y CL tienen cobertura de investigaci\xF3n."),
|
|
10103
10408
|
categoryKey: ResearchCategoryEnum.default("auto").describe('Categor\xEDa a investigar. Usa "auto" para que la IA elija seg\xFAn el mercado.'),
|
|
10104
|
-
confirmed:
|
|
10105
|
-
waitSeconds:
|
|
10409
|
+
confirmed: z50.boolean().describe(`Ponlo en true SOLO despu\xE9s de que el usuario haya aceptado el cargo de ${PRICE_CREDITS} cr\xE9ditos. No lo asumas.`),
|
|
10410
|
+
waitSeconds: z50.number().int().min(0).max(MAX_WAIT_SECONDS).optional().describe("Segundos a esperar el reporte antes de devolver el runId. M\xE1ximo 120.")
|
|
10106
10411
|
});
|
|
10107
|
-
var GetInput =
|
|
10108
|
-
runId:
|
|
10412
|
+
var GetInput = z50.object({
|
|
10413
|
+
runId: z50.string().uuid().describe("ID del run devuelto por find_winning_products.")
|
|
10109
10414
|
});
|
|
10110
|
-
var ListInput =
|
|
10111
|
-
limit:
|
|
10415
|
+
var ListInput = z50.object({
|
|
10416
|
+
limit: z50.number().int().min(1).max(20).default(10)
|
|
10112
10417
|
});
|
|
10113
10418
|
function textResult(text) {
|
|
10114
10419
|
return {
|
|
@@ -10199,6 +10504,256 @@ _Esta llamada no cobr\xF3 cr\xE9ditos: se reutiliz\xF3 una investigaci\xF3n reci
|
|
|
10199
10504
|
}
|
|
10200
10505
|
__name(registerProductResearchTools, "registerProductResearchTools");
|
|
10201
10506
|
|
|
10507
|
+
// src/tools/dropshipping-projects.ts
|
|
10508
|
+
import { z as z51 } from "zod";
|
|
10509
|
+
var ProjectId = z51.string().uuid();
|
|
10510
|
+
var DropshippingProjectStatus = z51.enum([
|
|
10511
|
+
"active",
|
|
10512
|
+
"completed",
|
|
10513
|
+
"archived"
|
|
10514
|
+
]);
|
|
10515
|
+
var DropshippingProjectPhase = z51.enum([
|
|
10516
|
+
"research",
|
|
10517
|
+
"costing",
|
|
10518
|
+
"media",
|
|
10519
|
+
"generating",
|
|
10520
|
+
"storefront",
|
|
10521
|
+
"advertising",
|
|
10522
|
+
"complete"
|
|
10523
|
+
]);
|
|
10524
|
+
var DropshippingRewardMilestone = z51.enum([
|
|
10525
|
+
"product_selected",
|
|
10526
|
+
"costing_saved",
|
|
10527
|
+
"product_media_ready",
|
|
10528
|
+
"creatives_completed",
|
|
10529
|
+
"campaign_created"
|
|
10530
|
+
]);
|
|
10531
|
+
var TargetCountry = z51.enum([
|
|
10532
|
+
"CO",
|
|
10533
|
+
"MX",
|
|
10534
|
+
"PE",
|
|
10535
|
+
"EC",
|
|
10536
|
+
"CL"
|
|
10537
|
+
]);
|
|
10538
|
+
var ResearchCategory = z51.enum([
|
|
10539
|
+
"auto",
|
|
10540
|
+
"automotive",
|
|
10541
|
+
"kitchen",
|
|
10542
|
+
"pets",
|
|
10543
|
+
"home_organization",
|
|
10544
|
+
"beauty",
|
|
10545
|
+
"wellness",
|
|
10546
|
+
"fitness",
|
|
10547
|
+
"baby",
|
|
10548
|
+
"tech",
|
|
10549
|
+
"tools",
|
|
10550
|
+
"garden",
|
|
10551
|
+
"cleaning"
|
|
10552
|
+
]);
|
|
10553
|
+
var HttpUrl3 = z51.string().url().max(2e3).refine((value) => {
|
|
10554
|
+
try {
|
|
10555
|
+
return [
|
|
10556
|
+
"http:",
|
|
10557
|
+
"https:"
|
|
10558
|
+
].includes(new URL(value).protocol);
|
|
10559
|
+
} catch {
|
|
10560
|
+
return false;
|
|
10561
|
+
}
|
|
10562
|
+
}, "URL must use HTTP(S)");
|
|
10563
|
+
var ResearchCandidateOrigin = z51.object({
|
|
10564
|
+
type: z51.literal("research_candidate"),
|
|
10565
|
+
runId: ProjectId,
|
|
10566
|
+
candidateSourceId: z51.string().min(1).max(160)
|
|
10567
|
+
});
|
|
10568
|
+
var TaskOrigin = z51.object({
|
|
10569
|
+
type: z51.literal("task"),
|
|
10570
|
+
taskId: ProjectId
|
|
10571
|
+
});
|
|
10572
|
+
var ManualOrigin = z51.object({
|
|
10573
|
+
type: z51.literal("manual"),
|
|
10574
|
+
clientRequestId: ProjectId,
|
|
10575
|
+
country: z51.string().min(2).max(3).optional()
|
|
10576
|
+
});
|
|
10577
|
+
var StartProjectInput = z51.object({
|
|
10578
|
+
origin: z51.discriminatedUnion("type", [
|
|
10579
|
+
ResearchCandidateOrigin,
|
|
10580
|
+
TaskOrigin,
|
|
10581
|
+
ManualOrigin
|
|
10582
|
+
]),
|
|
10583
|
+
productName: z51.string().trim().min(1).max(240).optional(),
|
|
10584
|
+
productUrl: HttpUrl3.optional(),
|
|
10585
|
+
country: z51.string().min(2).max(3).optional(),
|
|
10586
|
+
languageCode: z51.string().min(2).max(16).optional()
|
|
10587
|
+
}).strict();
|
|
10588
|
+
var CostProjectInput = z51.object({
|
|
10589
|
+
projectId: ProjectId,
|
|
10590
|
+
version: z51.number().int().positive(),
|
|
10591
|
+
country: z51.string().trim().min(2).max(3),
|
|
10592
|
+
supplierCost: z51.number().finite().positive(),
|
|
10593
|
+
shippingCost: z51.number().finite().nonnegative(),
|
|
10594
|
+
returnRate: z51.number().finite().min(0).lt(1),
|
|
10595
|
+
cancellationRate: z51.number().finite().min(0).lt(1),
|
|
10596
|
+
adsManagerCpa: z51.number().finite().nonnegative(),
|
|
10597
|
+
desiredMargin: z51.number().finite().min(0).lt(1)
|
|
10598
|
+
});
|
|
10599
|
+
var GuideInput = z51.object({
|
|
10600
|
+
projectId: ProjectId.optional()
|
|
10601
|
+
}).strict();
|
|
10602
|
+
var DropshippingProjectNextAction = z51.discriminatedUnion("kind", [
|
|
10603
|
+
"cost_product",
|
|
10604
|
+
"provide_product_media",
|
|
10605
|
+
"generate_creatives",
|
|
10606
|
+
"wait_for_creatives",
|
|
10607
|
+
"repair_creatives",
|
|
10608
|
+
"complete_creative_set",
|
|
10609
|
+
"connect_shopify",
|
|
10610
|
+
"publish_storefront",
|
|
10611
|
+
"wait_for_storefront",
|
|
10612
|
+
"connect_meta",
|
|
10613
|
+
"create_meta_campaign",
|
|
10614
|
+
"repair_meta_campaign",
|
|
10615
|
+
"review_paused_campaign"
|
|
10616
|
+
].map((kind) => z51.object({
|
|
10617
|
+
kind: z51.literal(kind),
|
|
10618
|
+
href: z51.string().min(1)
|
|
10619
|
+
})));
|
|
10620
|
+
var CostingInputSchema = z51.object({
|
|
10621
|
+
country: z51.string().min(2).max(3),
|
|
10622
|
+
supplierCost: z51.number().finite().positive(),
|
|
10623
|
+
shippingCost: z51.number().finite().nonnegative(),
|
|
10624
|
+
returnRate: z51.number().finite().min(0).lt(1),
|
|
10625
|
+
cancellationRate: z51.number().finite().min(0).lt(1),
|
|
10626
|
+
adsManagerCpa: z51.number().finite().nonnegative(),
|
|
10627
|
+
desiredMargin: z51.number().finite().min(0).lt(1)
|
|
10628
|
+
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
10629
|
+
if (returnRate + cancellationRate >= 1) context.addIssue({
|
|
10630
|
+
code: z51.ZodIssueCode.custom,
|
|
10631
|
+
path: [
|
|
10632
|
+
"cancellationRate"
|
|
10633
|
+
]
|
|
10634
|
+
});
|
|
10635
|
+
});
|
|
10636
|
+
var CostingResultSchema = z51.object({
|
|
10637
|
+
country: z51.string().min(2).max(3),
|
|
10638
|
+
deliveryRate: z51.number().finite(),
|
|
10639
|
+
adjustedShippingCost: z51.number().finite(),
|
|
10640
|
+
adjustedCpa: z51.number().finite(),
|
|
10641
|
+
totalCost: z51.number().finite(),
|
|
10642
|
+
suggestedPrice: z51.number().finite(),
|
|
10643
|
+
baseCommercialPrice: z51.number().finite(),
|
|
10644
|
+
minimumSafePrice: z51.number().finite(),
|
|
10645
|
+
commercialPrice: z51.number().finite(),
|
|
10646
|
+
comparisonPrice: z51.number().finite(),
|
|
10647
|
+
estimatedProfit: z51.number().finite(),
|
|
10648
|
+
actualMargin: z51.number().finite(),
|
|
10649
|
+
breakEvenAdsManagerCpa: z51.number().finite(),
|
|
10650
|
+
wasPriceAutoAdjusted: z51.boolean(),
|
|
10651
|
+
profitabilityStatus: z51.literal("healthy")
|
|
10652
|
+
});
|
|
10653
|
+
var RewardProgressSchema = z51.object({
|
|
10654
|
+
campaignKey: z51.literal("dropshipping-launch-v1"),
|
|
10655
|
+
totalCredits: z51.number().int().positive(),
|
|
10656
|
+
earnedCredits: z51.number().int().nonnegative(),
|
|
10657
|
+
milestones: z51.array(z51.object({
|
|
10658
|
+
milestone: DropshippingRewardMilestone,
|
|
10659
|
+
credits: z51.number().int().positive(),
|
|
10660
|
+
available: z51.boolean().optional(),
|
|
10661
|
+
earned: z51.boolean(),
|
|
10662
|
+
grantId: ProjectId.nullable(),
|
|
10663
|
+
grantedAt: z51.string().datetime().nullable()
|
|
10664
|
+
})).length(DropshippingRewardMilestone.options.length)
|
|
10665
|
+
});
|
|
10666
|
+
var ProjectResponseSchema = z51.object({
|
|
10667
|
+
id: ProjectId,
|
|
10668
|
+
taskId: ProjectId.nullable(),
|
|
10669
|
+
status: DropshippingProjectStatus,
|
|
10670
|
+
originKey: z51.string().min(1),
|
|
10671
|
+
researchRunId: ProjectId.nullable(),
|
|
10672
|
+
candidateSourceId: z51.string().nullable(),
|
|
10673
|
+
productName: z51.string().nullable(),
|
|
10674
|
+
productUrl: HttpUrl3.nullable(),
|
|
10675
|
+
referenceImageUrl: HttpUrl3.nullable(),
|
|
10676
|
+
targetCountry: z51.string().nullable(),
|
|
10677
|
+
languageCode: z51.string().nullable(),
|
|
10678
|
+
hasReferenceImage: z51.boolean(),
|
|
10679
|
+
costingInput: CostingInputSchema.nullable(),
|
|
10680
|
+
costingResult: CostingResultSchema.nullable(),
|
|
10681
|
+
costingVersion: z51.number().int().positive().nullable(),
|
|
10682
|
+
version: z51.number().int().positive(),
|
|
10683
|
+
phase: DropshippingProjectPhase,
|
|
10684
|
+
nextAction: DropshippingProjectNextAction.nullable(),
|
|
10685
|
+
rewards: RewardProgressSchema,
|
|
10686
|
+
createdAt: z51.string().datetime(),
|
|
10687
|
+
updatedAt: z51.string().datetime()
|
|
10688
|
+
}).passthrough();
|
|
10689
|
+
function projectText(raw) {
|
|
10690
|
+
const project = ProjectResponseSchema.parse(raw);
|
|
10691
|
+
const next = project.nextAction ? `${project.nextAction.kind} (${project.nextAction.href})` : "ninguna";
|
|
10692
|
+
return {
|
|
10693
|
+
content: [
|
|
10694
|
+
{
|
|
10695
|
+
type: "text",
|
|
10696
|
+
text: JSON.stringify({
|
|
10697
|
+
projectId: project.id,
|
|
10698
|
+
productName: project.productName,
|
|
10699
|
+
phase: project.phase,
|
|
10700
|
+
nextAction: project.nextAction,
|
|
10701
|
+
hasReferenceImage: project.hasReferenceImage,
|
|
10702
|
+
costingSaved: Boolean(project.costingResult),
|
|
10703
|
+
rewardProgress: {
|
|
10704
|
+
earnedCredits: project.rewards.earnedCredits,
|
|
10705
|
+
totalCredits: project.rewards.totalCredits,
|
|
10706
|
+
milestones: project.rewards.milestones
|
|
10707
|
+
},
|
|
10708
|
+
version: project.version,
|
|
10709
|
+
message: `Siguiente paso: ${next}. Relee este proyecto despu\xE9s de cada mutaci\xF3n.`
|
|
10710
|
+
})
|
|
10711
|
+
}
|
|
10712
|
+
]
|
|
10713
|
+
};
|
|
10714
|
+
}
|
|
10715
|
+
__name(projectText, "projectText");
|
|
10716
|
+
function registerDropshippingProjectTools(server, api) {
|
|
10717
|
+
server.tool("start_dropshipping_project", "Inicia o reanuda un proyecto opcional de dropshipping. \xDAsalo cuando el usuario quiera seguir un producto hasta publicarlo; para una operaci\xF3n puntual usa el tool directo. La creaci\xF3n es idempotente y no cobra cr\xE9ditos.", StartProjectInput.shape, async (input) => {
|
|
10718
|
+
try {
|
|
10719
|
+
return projectText(await api.postJson("/v1/dropshipping-projects", input));
|
|
10720
|
+
} catch (error) {
|
|
10721
|
+
throw translateError(error);
|
|
10722
|
+
}
|
|
10723
|
+
});
|
|
10724
|
+
server.tool("cost_dropshipping_project", "Guarda el costeo de un proyecto guiado. Escribe solo inputs; el backend recalcula el resultado can\xF3nico y controla la versi\xF3n. Pide al usuario el costo real del proveedor: nunca lo inventes. No crea tarea ni publica nada.", CostProjectInput.shape, async (input) => {
|
|
10725
|
+
try {
|
|
10726
|
+
if (input.returnRate + input.cancellationRate >= 1) {
|
|
10727
|
+
throw new Error("returnRate + cancellationRate debe ser menor que 1.");
|
|
10728
|
+
}
|
|
10729
|
+
const { projectId, version, ...costingInput } = input;
|
|
10730
|
+
return projectText(await api.putJson(`/v1/dropshipping-projects/${encodeURIComponent(projectId)}/costing`, {
|
|
10731
|
+
version,
|
|
10732
|
+
input: costingInput
|
|
10733
|
+
}));
|
|
10734
|
+
} catch (error) {
|
|
10735
|
+
throw translateError(error);
|
|
10736
|
+
}
|
|
10737
|
+
});
|
|
10738
|
+
server.tool("guide_dropshipping_launch", "Gu\xEDa read-only del lanzamiento. Sin projectId explica cu\xE1ndo usar un flujo directo versus uno guiado; con projectId devuelve la fase y la \xFAnica nextAction calculada por ProductMaker. Despu\xE9s de cada mutaci\xF3n vuelve a llamar este tool o lee el proyecto. Nunca activa Meta.", GuideInput.shape, async (input) => {
|
|
10739
|
+
try {
|
|
10740
|
+
if (!input.projectId) {
|
|
10741
|
+
return asText({
|
|
10742
|
+
mode: "choose",
|
|
10743
|
+
guided: "Usa start_dropshipping_project cuando el usuario quiera encontrar un producto y dejarlo listo para publicar.",
|
|
10744
|
+
direct: "En modo directo usa cost_product, create_product_task, publish_to_shopify o publish_to_meta cuando pidi\xF3 una operaci\xF3n espec\xEDfica.",
|
|
10745
|
+
confirmation: "Antes de acciones pagadas o externas, muestra el estimado y pide confirmaci\xF3n.",
|
|
10746
|
+
safety: "Las campa\xF1as Meta quedan PAUSED; el usuario las activa manualmente en Ads Manager."
|
|
10747
|
+
});
|
|
10748
|
+
}
|
|
10749
|
+
return projectText(await api.getJson(`/v1/dropshipping-projects/${encodeURIComponent(input.projectId)}`));
|
|
10750
|
+
} catch (error) {
|
|
10751
|
+
throw translateError(error);
|
|
10752
|
+
}
|
|
10753
|
+
});
|
|
10754
|
+
}
|
|
10755
|
+
__name(registerDropshippingProjectTools, "registerDropshippingProjectTools");
|
|
10756
|
+
|
|
10202
10757
|
// src/server.ts
|
|
10203
10758
|
function createServer(opts) {
|
|
10204
10759
|
const { apiKey, apiBaseUrl, proxyBaseUrl = null, transport = "http" } = opts;
|
|
@@ -10216,6 +10771,7 @@ function createServer(opts) {
|
|
|
10216
10771
|
const api = new ApiClient(apiBaseUrl, apiKey);
|
|
10217
10772
|
registerProductCostingTools(server);
|
|
10218
10773
|
registerProductResearchTools(server, api);
|
|
10774
|
+
registerDropshippingProjectTools(server, api);
|
|
10219
10775
|
registerTaskTools(server, api, {
|
|
10220
10776
|
proxyBaseUrl,
|
|
10221
10777
|
transport
|