@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/stdio.js
CHANGED
|
@@ -73,6 +73,17 @@ var ApiClient = class {
|
|
|
73
73
|
});
|
|
74
74
|
return this.parse(res);
|
|
75
75
|
}
|
|
76
|
+
async putJson(path3, body) {
|
|
77
|
+
const res = await fetch(`${this.baseUrl}${path3}`, {
|
|
78
|
+
method: "PUT",
|
|
79
|
+
headers: {
|
|
80
|
+
...this.authHeaders(),
|
|
81
|
+
"Content-Type": "application/json"
|
|
82
|
+
},
|
|
83
|
+
body: JSON.stringify(body)
|
|
84
|
+
});
|
|
85
|
+
return this.parse(res);
|
|
86
|
+
}
|
|
76
87
|
async deleteJson(path3) {
|
|
77
88
|
const res = await fetch(`${this.baseUrl}${path3}`, {
|
|
78
89
|
method: "DELETE",
|
|
@@ -139,10 +150,10 @@ var ApiClient = class {
|
|
|
139
150
|
};
|
|
140
151
|
|
|
141
152
|
// src/index.ts
|
|
142
|
-
var MCP_VERSION = "2.
|
|
153
|
+
var MCP_VERSION = "2.2.0";
|
|
143
154
|
|
|
144
155
|
// src/tools/tasks.ts
|
|
145
|
-
import { z as
|
|
156
|
+
import { z as z40 } from "zod";
|
|
146
157
|
|
|
147
158
|
// src/resolve-image.ts
|
|
148
159
|
import { lookup as dnsLookup } from "dns/promises";
|
|
@@ -1389,6 +1400,8 @@ var INGEST_ALLOWED_IMAGE_MIME_TYPES = [
|
|
|
1389
1400
|
var PRODUCT_CONTEXT_MAX_CHARS = 3e3;
|
|
1390
1401
|
var INGEST_ALLOWED_IMAGE_MIME_TYPE_SET = new Set(INGEST_ALLOWED_IMAGE_MIME_TYPES);
|
|
1391
1402
|
var IngestOptions = z13.object({
|
|
1403
|
+
/** Optional owner-scoped guided journey context. Never bypasses ingest gates. */
|
|
1404
|
+
dropshippingProjectId: z13.string().uuid().optional(),
|
|
1392
1405
|
languageCode: LanguageCodeSchema.default("es-CO"),
|
|
1393
1406
|
ugcFormat: VideoFormat.default("single_15s"),
|
|
1394
1407
|
country: CountrySchema,
|
|
@@ -1408,11 +1421,12 @@ var IngestOptions = z13.object({
|
|
|
1408
1421
|
* shorter than the generated angle count leaves the remaining positions
|
|
1409
1422
|
* auto-selected. Empty/omitted → all angles auto-select as before. */
|
|
1410
1423
|
angleNarratives: z13.array(NarrativeStyle).max(3).optional(),
|
|
1411
|
-
//
|
|
1412
|
-
//
|
|
1424
|
+
// 480p is the recommended default for every tier (flipped back 2026-07-30 — the
|
|
1425
|
+
// 720p premium is real but small, and users on the entry plan were defaulting
|
|
1426
|
+
// into it while burning half a month of credits on one video).
|
|
1413
1427
|
// This Zod default is what callers that omit the field (MCP/API) actually get,
|
|
1414
|
-
// so it must agree with resolveSingleCallAwareResolution's unset fallback (
|
|
1415
|
-
videoResolution: VideoResolution.default("
|
|
1428
|
+
// so it must agree with resolveSingleCallAwareResolution's unset fallback (480p).
|
|
1429
|
+
videoResolution: VideoResolution.default("480p"),
|
|
1416
1430
|
generateAdCopy: z13.boolean().default(true),
|
|
1417
1431
|
generateAiMusic: z13.boolean().optional(),
|
|
1418
1432
|
voiceId: VoiceId.optional(),
|
|
@@ -1527,9 +1541,9 @@ var IngestResumePayload = z13.object({
|
|
|
1527
1541
|
videoModel: VideoModel.default("veo"),
|
|
1528
1542
|
videoTier: VideoTier.optional(),
|
|
1529
1543
|
narrativeStyle: NarrativeStyle.default("review"),
|
|
1530
|
-
//
|
|
1531
|
-
// with IngestOptions.videoResolution and resolveSingleCallAwareResolution.
|
|
1532
|
-
videoResolution: VideoResolution.default("
|
|
1544
|
+
// 480p is the recommended default for every tier (flipped back 2026-07-30) — keep in
|
|
1545
|
+
// sync with IngestOptions.videoResolution and resolveSingleCallAwareResolution.
|
|
1546
|
+
videoResolution: VideoResolution.default("480p"),
|
|
1533
1547
|
pauseForVideoScriptReview: z13.boolean().optional(),
|
|
1534
1548
|
generateAiMusic: z13.boolean().optional(),
|
|
1535
1549
|
scriptNotes: z13.string().max(2e3).optional(),
|
|
@@ -4988,16 +5002,37 @@ var DEFAULT_PRICING = {
|
|
|
4988
5002
|
ourCostCents: 0.15,
|
|
4989
5003
|
userPriceCents: 0.3
|
|
4990
5004
|
},
|
|
4991
|
-
// Video QA —
|
|
4992
|
-
//
|
|
4993
|
-
//
|
|
4994
|
-
// (
|
|
4995
|
-
//
|
|
4996
|
-
//
|
|
5005
|
+
// Video QA — anatomy. THREE keys, and they do NOT share a unit. Getting this wrong
|
|
5006
|
+
// over-charges by ~42x, which is exactly what a first pass at the 2026-07-30 change did.
|
|
5007
|
+
//
|
|
5008
|
+
// (a) The arm-scan's per-FRAME counting sweep. STILL LIVE on the whole-video concat path
|
|
5009
|
+
// (`scanClipArms` at apps/api/src/workers/core/video-concat-core.ts), which bills
|
|
5010
|
+
// units:1 PER SAMPLED FRAME, up to ARM_SCAN_FRAME_CEILING. Rate must stay per-frame.
|
|
5011
|
+
// Sized above the 30d prod average (10303 rows => 0.132c user/frame).
|
|
4997
5012
|
"openrouter:video-anatomy-arm-scan": {
|
|
4998
5013
|
ourCostCents: 0.1,
|
|
4999
5014
|
userPriceCents: 0.2
|
|
5000
5015
|
},
|
|
5016
|
+
// (b) The frame JUDGE that replaced the sweep on the `analyzeVideoClipDJ` path: ALL frames
|
|
5017
|
+
// in ONE multi-image call, so a unit is one call. MEASURED, not estimated —
|
|
5018
|
+
// usage.cost on a real 16-frame judgement at 768px is $0.0417 => 4.17c our. The table is
|
|
5019
|
+
// the FALLBACK used when OpenRouter omits usage.cost, so it must sit at or above that.
|
|
5020
|
+
// Billed PER FRAME (units = frames in the call), like the sweep and for the same reason:
|
|
5021
|
+
// the cost scales with IMAGES and QA_FRAME_JUDGE_FRAMES is env-overridable, so a per-CALL
|
|
5022
|
+
// unit would freeze the fallback charge and the credit-gate quote at the cap the rate
|
|
5023
|
+
// happened to be measured at. Measured $0.0417 over 16 frames => 0.26c/frame; priced at
|
|
5024
|
+
// 0.32c for headroom (n=1 on one clip, and the 1.9k reasoning-token share moves with
|
|
5025
|
+
// QA_DETECTOR_EFFORT). Re-derive from 30d of BillingEntry once this key has volume.
|
|
5026
|
+
"openrouter:video-qa-anatomy-frame-judge": {
|
|
5027
|
+
ourCostCents: 0.32,
|
|
5028
|
+
userPriceCents: 0.64
|
|
5029
|
+
},
|
|
5030
|
+
// (c) Tracing confirmation of a frames-only finding: 3 stills + a 3s 480p slice, one call,
|
|
5031
|
+
// no 16-image prompt — materially cheaper than the judgement it verifies.
|
|
5032
|
+
"openrouter:video-qa-anatomy-tracing-confirm": {
|
|
5033
|
+
ourCostCents: 1.5,
|
|
5034
|
+
userPriceCents: 3
|
|
5035
|
+
},
|
|
5001
5036
|
// Physics confirmation — skeptical second opinion on D+J floating-object flags
|
|
5002
5037
|
// (Gemini 3 Flash Preview, ~3 frames per flagged moment, 1 call). Aligned with other vision-QA keys.
|
|
5003
5038
|
"openrouter:video-physics-confirmation": {
|
|
@@ -6543,17 +6578,216 @@ var RegenerateEditRequestSchema = z36.object({
|
|
|
6543
6578
|
comment: z36.string().min(1).max(2e3)
|
|
6544
6579
|
});
|
|
6545
6580
|
|
|
6546
|
-
// ../../packages/types/src/
|
|
6581
|
+
// ../../packages/types/src/dropshipping-project.ts
|
|
6547
6582
|
import { z as z37 } from "zod";
|
|
6548
|
-
var
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6583
|
+
var DropshippingProjectStatusSchema = z37.enum([
|
|
6584
|
+
"active",
|
|
6585
|
+
"completed",
|
|
6586
|
+
"archived"
|
|
6587
|
+
]);
|
|
6588
|
+
var DROPSHIPPING_LAUNCH_REWARD_CAMPAIGN = "dropshipping-launch-v1";
|
|
6589
|
+
var DropshippingRewardMilestoneSchema = z37.enum([
|
|
6590
|
+
"product_selected",
|
|
6591
|
+
"costing_saved",
|
|
6592
|
+
"product_media_ready",
|
|
6593
|
+
"creatives_completed",
|
|
6594
|
+
"campaign_created"
|
|
6595
|
+
]);
|
|
6596
|
+
var DROPSHIPPING_LAUNCH_REWARD_CREDITS = {
|
|
6597
|
+
product_selected: 50,
|
|
6598
|
+
costing_saved: 50,
|
|
6599
|
+
product_media_ready: 50,
|
|
6600
|
+
creatives_completed: 150,
|
|
6601
|
+
campaign_created: 200
|
|
6602
|
+
};
|
|
6603
|
+
var DROPSHIPPING_LAUNCH_REWARD_TOTAL_CREDITS = Object.values(DROPSHIPPING_LAUNCH_REWARD_CREDITS).reduce((sum, credits) => sum + credits, 0);
|
|
6604
|
+
var DropshippingProjectRewardMilestoneStateSchema = z37.object({
|
|
6605
|
+
milestone: DropshippingRewardMilestoneSchema,
|
|
6606
|
+
credits: z37.number().int().positive(),
|
|
6607
|
+
available: z37.boolean().optional(),
|
|
6608
|
+
earned: z37.boolean(),
|
|
6609
|
+
grantId: z37.string().uuid().nullable(),
|
|
6610
|
+
grantedAt: z37.string().datetime().nullable()
|
|
6611
|
+
});
|
|
6612
|
+
var DropshippingProjectRewardProgressSchema = z37.object({
|
|
6613
|
+
campaignKey: z37.literal(DROPSHIPPING_LAUNCH_REWARD_CAMPAIGN),
|
|
6614
|
+
totalCredits: z37.number().int().positive(),
|
|
6615
|
+
earnedCredits: z37.number().int().nonnegative(),
|
|
6616
|
+
milestones: z37.array(DropshippingProjectRewardMilestoneStateSchema).length(DropshippingRewardMilestoneSchema.options.length)
|
|
6617
|
+
});
|
|
6618
|
+
var DropshippingProjectPhaseSchema = z37.enum([
|
|
6619
|
+
"research",
|
|
6620
|
+
"costing",
|
|
6621
|
+
"media",
|
|
6622
|
+
"generating",
|
|
6623
|
+
"storefront",
|
|
6624
|
+
"advertising",
|
|
6625
|
+
"complete"
|
|
6626
|
+
]);
|
|
6627
|
+
var DropshippingProjectCostingInputSchema = z37.object({
|
|
6628
|
+
country: z37.string().min(2).max(3),
|
|
6629
|
+
supplierCost: z37.number().finite().positive(),
|
|
6630
|
+
shippingCost: z37.number().finite().nonnegative(),
|
|
6631
|
+
returnRate: z37.number().finite().min(0).lt(1),
|
|
6632
|
+
cancellationRate: z37.number().finite().min(0).lt(1),
|
|
6633
|
+
adsManagerCpa: z37.number().finite().nonnegative(),
|
|
6634
|
+
desiredMargin: z37.number().finite().min(0).lt(1)
|
|
6635
|
+
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
6636
|
+
if (returnRate + cancellationRate >= 1) {
|
|
6637
|
+
context.addIssue({
|
|
6638
|
+
code: z37.ZodIssueCode.custom,
|
|
6639
|
+
path: [
|
|
6640
|
+
"cancellationRate"
|
|
6641
|
+
],
|
|
6642
|
+
message: "delivery_rate_positive"
|
|
6643
|
+
});
|
|
6644
|
+
}
|
|
6645
|
+
});
|
|
6646
|
+
var DropshippingProjectCostingResultSchema = z37.object({
|
|
6647
|
+
country: z37.string().min(2).max(3),
|
|
6648
|
+
deliveryRate: z37.number().finite(),
|
|
6649
|
+
adjustedShippingCost: z37.number().finite(),
|
|
6650
|
+
adjustedCpa: z37.number().finite(),
|
|
6651
|
+
totalCost: z37.number().finite(),
|
|
6652
|
+
suggestedPrice: z37.number().finite(),
|
|
6653
|
+
baseCommercialPrice: z37.number().finite(),
|
|
6654
|
+
minimumSafePrice: z37.number().finite(),
|
|
6655
|
+
commercialPrice: z37.number().finite(),
|
|
6656
|
+
comparisonPrice: z37.number().finite(),
|
|
6657
|
+
estimatedProfit: z37.number().finite(),
|
|
6658
|
+
actualMargin: z37.number().finite(),
|
|
6659
|
+
breakEvenAdsManagerCpa: z37.number().finite(),
|
|
6660
|
+
wasPriceAutoAdjusted: z37.boolean(),
|
|
6661
|
+
profitabilityStatus: z37.literal("healthy")
|
|
6662
|
+
});
|
|
6663
|
+
var HttpUrlSchema = z37.string().url().max(2e3).refine((value) => {
|
|
6664
|
+
try {
|
|
6665
|
+
return [
|
|
6666
|
+
"http:",
|
|
6667
|
+
"https:"
|
|
6668
|
+
].includes(new URL(value).protocol);
|
|
6669
|
+
} catch {
|
|
6670
|
+
return false;
|
|
6671
|
+
}
|
|
6672
|
+
}, "Product URL must use HTTP(S)");
|
|
6673
|
+
var ResearchCandidateOriginSchema = z37.object({
|
|
6674
|
+
type: z37.literal("research_candidate"),
|
|
6675
|
+
runId: z37.string().uuid(),
|
|
6676
|
+
candidateSourceId: z37.string().min(1).max(160)
|
|
6677
|
+
});
|
|
6678
|
+
var TaskOriginSchema = z37.object({
|
|
6679
|
+
type: z37.literal("task"),
|
|
6680
|
+
taskId: z37.string().uuid()
|
|
6681
|
+
});
|
|
6682
|
+
var ManualOriginSchema = z37.object({
|
|
6683
|
+
type: z37.literal("manual"),
|
|
6684
|
+
clientRequestId: z37.string().uuid(),
|
|
6685
|
+
country: z37.string().min(2).max(3).optional()
|
|
6686
|
+
});
|
|
6687
|
+
var CreateDropshippingProjectRequestSchema = z37.object({
|
|
6688
|
+
origin: z37.discriminatedUnion("type", [
|
|
6689
|
+
ResearchCandidateOriginSchema,
|
|
6690
|
+
TaskOriginSchema,
|
|
6691
|
+
ManualOriginSchema
|
|
6692
|
+
]),
|
|
6693
|
+
productName: z37.string().trim().min(1).max(240).optional(),
|
|
6694
|
+
productUrl: HttpUrlSchema.optional(),
|
|
6695
|
+
country: z37.string().min(2).max(3).optional(),
|
|
6696
|
+
languageCode: z37.string().min(2).max(16).optional()
|
|
6697
|
+
}).strict();
|
|
6698
|
+
var SaveDropshippingProjectCostingRequestSchema = z37.object({
|
|
6699
|
+
input: DropshippingProjectCostingInputSchema,
|
|
6700
|
+
version: z37.number().int().positive()
|
|
6701
|
+
}).strict();
|
|
6702
|
+
var DropshippingProjectNextActionSchema = z37.discriminatedUnion("kind", [
|
|
6703
|
+
z37.object({
|
|
6704
|
+
kind: z37.literal("cost_product"),
|
|
6705
|
+
href: z37.string().min(1)
|
|
6706
|
+
}),
|
|
6707
|
+
z37.object({
|
|
6708
|
+
kind: z37.literal("provide_product_media"),
|
|
6709
|
+
href: z37.string().min(1)
|
|
6710
|
+
}),
|
|
6711
|
+
z37.object({
|
|
6712
|
+
kind: z37.literal("generate_creatives"),
|
|
6713
|
+
href: z37.string().min(1)
|
|
6714
|
+
}),
|
|
6715
|
+
z37.object({
|
|
6716
|
+
kind: z37.literal("wait_for_creatives"),
|
|
6717
|
+
href: z37.string().min(1)
|
|
6718
|
+
}),
|
|
6719
|
+
z37.object({
|
|
6720
|
+
kind: z37.literal("repair_creatives"),
|
|
6721
|
+
href: z37.string().min(1)
|
|
6722
|
+
}),
|
|
6723
|
+
z37.object({
|
|
6724
|
+
kind: z37.literal("complete_creative_set"),
|
|
6725
|
+
href: z37.string().min(1)
|
|
6726
|
+
}),
|
|
6727
|
+
z37.object({
|
|
6728
|
+
kind: z37.literal("connect_shopify"),
|
|
6729
|
+
href: z37.string().min(1)
|
|
6730
|
+
}),
|
|
6731
|
+
z37.object({
|
|
6732
|
+
kind: z37.literal("publish_storefront"),
|
|
6733
|
+
href: z37.string().min(1)
|
|
6734
|
+
}),
|
|
6735
|
+
z37.object({
|
|
6736
|
+
kind: z37.literal("wait_for_storefront"),
|
|
6737
|
+
href: z37.string().min(1)
|
|
6738
|
+
}),
|
|
6739
|
+
z37.object({
|
|
6740
|
+
kind: z37.literal("connect_meta"),
|
|
6741
|
+
href: z37.string().min(1)
|
|
6742
|
+
}),
|
|
6743
|
+
z37.object({
|
|
6744
|
+
kind: z37.literal("create_meta_campaign"),
|
|
6745
|
+
href: z37.string().min(1)
|
|
6746
|
+
}),
|
|
6747
|
+
z37.object({
|
|
6748
|
+
kind: z37.literal("repair_meta_campaign"),
|
|
6749
|
+
href: z37.string().min(1)
|
|
6750
|
+
}),
|
|
6751
|
+
z37.object({
|
|
6752
|
+
kind: z37.literal("review_paused_campaign"),
|
|
6753
|
+
href: z37.string().min(1)
|
|
6754
|
+
})
|
|
6755
|
+
]);
|
|
6756
|
+
var DropshippingProjectResponseSchema = z37.object({
|
|
6757
|
+
id: z37.string().uuid(),
|
|
6758
|
+
taskId: z37.string().uuid().nullable(),
|
|
6759
|
+
status: DropshippingProjectStatusSchema,
|
|
6760
|
+
originKey: z37.string().min(1),
|
|
6761
|
+
researchRunId: z37.string().uuid().nullable(),
|
|
6762
|
+
candidateSourceId: z37.string().nullable(),
|
|
6763
|
+
productName: z37.string().nullable(),
|
|
6764
|
+
productUrl: HttpUrlSchema.nullable(),
|
|
6765
|
+
referenceImageUrl: z37.string().url().nullable(),
|
|
6766
|
+
hasReferenceImage: z37.boolean(),
|
|
6767
|
+
targetCountry: z37.string().nullable(),
|
|
6768
|
+
languageCode: z37.string().nullable(),
|
|
6769
|
+
costingInput: DropshippingProjectCostingInputSchema.nullable(),
|
|
6770
|
+
costingResult: DropshippingProjectCostingResultSchema.nullable(),
|
|
6771
|
+
costingVersion: z37.number().int().positive().nullable(),
|
|
6772
|
+
version: z37.number().int().positive(),
|
|
6773
|
+
phase: DropshippingProjectPhaseSchema,
|
|
6774
|
+
nextAction: DropshippingProjectNextActionSchema.nullable(),
|
|
6775
|
+
rewards: DropshippingProjectRewardProgressSchema,
|
|
6776
|
+
createdAt: z37.string().datetime(),
|
|
6777
|
+
updatedAt: z37.string().datetime()
|
|
6778
|
+
});
|
|
6779
|
+
|
|
6780
|
+
// ../../packages/types/src/scene-subject-contract.ts
|
|
6781
|
+
import { z as z38 } from "zod";
|
|
6782
|
+
var SceneSubjectContractSchema = z38.object({
|
|
6783
|
+
maxPeople: z38.number().int().min(1).max(10),
|
|
6784
|
+
minPeople: z38.number().int().min(0).max(10),
|
|
6785
|
+
minDistinctPeople: z38.number().int().min(0).max(10),
|
|
6786
|
+
maxHands: z38.number().int().min(0).max(20)
|
|
6553
6787
|
}).superRefine((value, ctx) => {
|
|
6554
6788
|
if (value.minPeople > value.maxPeople) {
|
|
6555
6789
|
ctx.addIssue({
|
|
6556
|
-
code:
|
|
6790
|
+
code: z38.ZodIssueCode.custom,
|
|
6557
6791
|
path: [
|
|
6558
6792
|
"minPeople"
|
|
6559
6793
|
],
|
|
@@ -6562,7 +6796,7 @@ var SceneSubjectContractSchema = z37.object({
|
|
|
6562
6796
|
}
|
|
6563
6797
|
if (value.minDistinctPeople > value.maxPeople) {
|
|
6564
6798
|
ctx.addIssue({
|
|
6565
|
-
code:
|
|
6799
|
+
code: z38.ZodIssueCode.custom,
|
|
6566
6800
|
path: [
|
|
6567
6801
|
"minDistinctPeople"
|
|
6568
6802
|
],
|
|
@@ -6571,7 +6805,7 @@ var SceneSubjectContractSchema = z37.object({
|
|
|
6571
6805
|
}
|
|
6572
6806
|
if (value.minDistinctPeople > value.minPeople) {
|
|
6573
6807
|
ctx.addIssue({
|
|
6574
|
-
code:
|
|
6808
|
+
code: z38.ZodIssueCode.custom,
|
|
6575
6809
|
path: [
|
|
6576
6810
|
"minDistinctPeople"
|
|
6577
6811
|
],
|
|
@@ -6587,6 +6821,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
6587
6821
|
confirmation: "credit_consuming",
|
|
6588
6822
|
schemaHash: "bab794a9700a8d3f42b97f444a776e7ed4dcd833038c52588f951b50970ff3fa"
|
|
6589
6823
|
},
|
|
6824
|
+
{
|
|
6825
|
+
name: "cost_dropshipping_project",
|
|
6826
|
+
confirmation: "state_change",
|
|
6827
|
+
schemaHash: "746797b5400b170ba3a0704d432afff635794f0a7ae0b9da07b26d49e3b82242"
|
|
6828
|
+
},
|
|
6590
6829
|
{
|
|
6591
6830
|
name: "cost_product",
|
|
6592
6831
|
confirmation: "none",
|
|
@@ -6600,7 +6839,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
6600
6839
|
{
|
|
6601
6840
|
name: "create_product_task",
|
|
6602
6841
|
confirmation: "credit_consuming",
|
|
6603
|
-
schemaHash: "
|
|
6842
|
+
schemaHash: "5b19f7c6dfdc55b0c472df045e24fe73aa134624a141b63342bfbd3bbb0d043b"
|
|
6604
6843
|
},
|
|
6605
6844
|
{
|
|
6606
6845
|
name: "edit_task_draft",
|
|
@@ -6625,17 +6864,17 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
6625
6864
|
{
|
|
6626
6865
|
name: "generate_image_creatives",
|
|
6627
6866
|
confirmation: "credit_consuming",
|
|
6628
|
-
schemaHash: "
|
|
6867
|
+
schemaHash: "82b520e8001e9c57ab0a89d9d66ea5dfc92e6306eed302edfe1005cee5025fbf"
|
|
6629
6868
|
},
|
|
6630
6869
|
{
|
|
6631
6870
|
name: "generate_landing",
|
|
6632
6871
|
confirmation: "credit_consuming",
|
|
6633
|
-
schemaHash: "
|
|
6872
|
+
schemaHash: "23f0c56dc719235e51d16fd61a9703b64fa6d300356ffbf6b21d703324b0bb18"
|
|
6634
6873
|
},
|
|
6635
6874
|
{
|
|
6636
6875
|
name: "generate_video_creative",
|
|
6637
6876
|
confirmation: "credit_consuming",
|
|
6638
|
-
schemaHash: "
|
|
6877
|
+
schemaHash: "79d3ec9310885b482d62c94a27e9c039833c0619d9332a593fe7d32a60ae1388"
|
|
6639
6878
|
},
|
|
6640
6879
|
{
|
|
6641
6880
|
name: "get_meta_campaign_status",
|
|
@@ -6662,6 +6901,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
6662
6901
|
confirmation: "none",
|
|
6663
6902
|
schemaHash: "c96e7042368ba6f241773d26d4a443894b092801d1c81054f3798865138edfba"
|
|
6664
6903
|
},
|
|
6904
|
+
{
|
|
6905
|
+
name: "guide_dropshipping_launch",
|
|
6906
|
+
confirmation: "none",
|
|
6907
|
+
schemaHash: "0e7a1cb5ad5c6681473df5689336edcd7a63b8d68853359982e44bfd98a65db8"
|
|
6908
|
+
},
|
|
6665
6909
|
{
|
|
6666
6910
|
name: "list_meta_ad_accounts",
|
|
6667
6911
|
confirmation: "none",
|
|
@@ -6721,6 +6965,11 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
6721
6965
|
name: "resolve_pending_action",
|
|
6722
6966
|
confirmation: "credit_consuming",
|
|
6723
6967
|
schemaHash: "3a9f33a26fce63b20bece3a528f8eb16e917dabc8a9ec5f04e746bad84757aa3"
|
|
6968
|
+
},
|
|
6969
|
+
{
|
|
6970
|
+
name: "start_dropshipping_project",
|
|
6971
|
+
confirmation: "state_change",
|
|
6972
|
+
schemaHash: "2b621702e8da8bb894d392060e5e4f677a8dc26f454e991dbb929053715a4f8b"
|
|
6724
6973
|
}
|
|
6725
6974
|
];
|
|
6726
6975
|
var PRODUCTMAKER_MCP_CAPABILITY_NAMES = PRODUCTMAKER_MCP_CAPABILITIES.map((capability) => capability.name);
|
|
@@ -6964,10 +7213,10 @@ async function resolveMediaRef(ref, opts) {
|
|
|
6964
7213
|
__name(resolveMediaRef, "resolveMediaRef");
|
|
6965
7214
|
|
|
6966
7215
|
// src/tools/shared.ts
|
|
6967
|
-
import { z as
|
|
6968
|
-
var Lang =
|
|
6969
|
-
var Country =
|
|
6970
|
-
var LandingStrategyEnum =
|
|
7216
|
+
import { z as z39 } from "zod";
|
|
7217
|
+
var Lang = z39.string().min(2).max(10);
|
|
7218
|
+
var Country = z39.string().min(2).max(3);
|
|
7219
|
+
var LandingStrategyEnum = z39.enum([
|
|
6971
7220
|
"auto",
|
|
6972
7221
|
"classic",
|
|
6973
7222
|
"pain_solution",
|
|
@@ -6976,12 +7225,12 @@ var LandingStrategyEnum = z38.enum([
|
|
|
6976
7225
|
"offer_value",
|
|
6977
7226
|
"lifestyle_identity"
|
|
6978
7227
|
]);
|
|
6979
|
-
var LandingImageFormatEnum =
|
|
7228
|
+
var LandingImageFormatEnum = z39.enum([
|
|
6980
7229
|
"9:16",
|
|
6981
7230
|
"4:5",
|
|
6982
7231
|
"1:1"
|
|
6983
7232
|
]);
|
|
6984
|
-
var VideoPresetEnum =
|
|
7233
|
+
var VideoPresetEnum = z39.enum([
|
|
6985
7234
|
"auto",
|
|
6986
7235
|
"ugc_dynamic",
|
|
6987
7236
|
"ugc_how_to",
|
|
@@ -6993,7 +7242,7 @@ var VideoPresetEnum = z38.enum([
|
|
|
6993
7242
|
"pixar_problem",
|
|
6994
7243
|
"pixar_sassy"
|
|
6995
7244
|
]);
|
|
6996
|
-
var TaskCreativeVariantVideoPresetEnum =
|
|
7245
|
+
var TaskCreativeVariantVideoPresetEnum = z39.enum([
|
|
6997
7246
|
"auto",
|
|
6998
7247
|
"ugc_dynamic",
|
|
6999
7248
|
"ugc_how_to",
|
|
@@ -7058,7 +7307,7 @@ function resolveVideoPreset(preset) {
|
|
|
7058
7307
|
return VIDEO_PRESET_RESOLUTION[preset] ?? null;
|
|
7059
7308
|
}
|
|
7060
7309
|
__name(resolveVideoPreset, "resolveVideoPreset");
|
|
7061
|
-
var VideoTierEnum =
|
|
7310
|
+
var VideoTierEnum = z39.enum([
|
|
7062
7311
|
"basic"
|
|
7063
7312
|
]);
|
|
7064
7313
|
function resolveVideoTier(_tier) {
|
|
@@ -7073,17 +7322,17 @@ function resolveVideoFormatForDuration(durationSeconds) {
|
|
|
7073
7322
|
return durationSeconds === 30 ? "double_30s" : "single_15s";
|
|
7074
7323
|
}
|
|
7075
7324
|
__name(resolveVideoFormatForDuration, "resolveVideoFormatForDuration");
|
|
7076
|
-
var VideoResolutionEnum =
|
|
7325
|
+
var VideoResolutionEnum = z39.enum([
|
|
7077
7326
|
"480p",
|
|
7078
7327
|
"720p"
|
|
7079
7328
|
]);
|
|
7080
|
-
var VideoStyleEnum =
|
|
7329
|
+
var VideoStyleEnum = z39.enum([
|
|
7081
7330
|
"ugc",
|
|
7082
7331
|
"cartoon_3d",
|
|
7083
7332
|
"product_only",
|
|
7084
7333
|
"ai_dynamic"
|
|
7085
7334
|
]);
|
|
7086
|
-
var NarrativeStyleEnum =
|
|
7335
|
+
var NarrativeStyleEnum = z39.enum([
|
|
7087
7336
|
"review",
|
|
7088
7337
|
"ugc_how_to",
|
|
7089
7338
|
"problem_product_cta",
|
|
@@ -7100,7 +7349,7 @@ var NarrativeStyleEnum = z38.enum([
|
|
|
7100
7349
|
"ugc_skeptic_confession",
|
|
7101
7350
|
"ugc_demo_review"
|
|
7102
7351
|
]);
|
|
7103
|
-
var PublicNarrativeStyleEnum =
|
|
7352
|
+
var PublicNarrativeStyleEnum = z39.enum([
|
|
7104
7353
|
"review",
|
|
7105
7354
|
"ugc_how_to",
|
|
7106
7355
|
"ugc_unboxing_chaos",
|
|
@@ -7111,7 +7360,7 @@ var PublicNarrativeStyleEnum = z38.enum([
|
|
|
7111
7360
|
"animated_problem_pixar",
|
|
7112
7361
|
"sassy_product_pixar"
|
|
7113
7362
|
]);
|
|
7114
|
-
var VideoModelEnum =
|
|
7363
|
+
var VideoModelEnum = z39.enum([
|
|
7115
7364
|
"veo",
|
|
7116
7365
|
"wan",
|
|
7117
7366
|
"bytedance",
|
|
@@ -7119,25 +7368,25 @@ var VideoModelEnum = z38.enum([
|
|
|
7119
7368
|
"grok",
|
|
7120
7369
|
"kling"
|
|
7121
7370
|
]);
|
|
7122
|
-
var ProductReferenceUserChoiceEnum =
|
|
7371
|
+
var ProductReferenceUserChoiceEnum = z39.enum([
|
|
7123
7372
|
"same_product",
|
|
7124
7373
|
"variants",
|
|
7125
7374
|
"different_products",
|
|
7126
7375
|
"combo_distinct"
|
|
7127
7376
|
]);
|
|
7128
|
-
var ProductReferenceCandidateGroupingEnum =
|
|
7377
|
+
var ProductReferenceCandidateGroupingEnum = z39.enum([
|
|
7129
7378
|
"same_product_same_variant",
|
|
7130
7379
|
"same_product_different_variants",
|
|
7131
7380
|
"different_products",
|
|
7132
7381
|
"unclear"
|
|
7133
7382
|
]);
|
|
7134
|
-
var GapDurationChoiceMirror =
|
|
7135
|
-
angleIndex:
|
|
7136
|
-
recommendedSec:
|
|
7137
|
-
|
|
7138
|
-
|
|
7383
|
+
var GapDurationChoiceMirror = z39.object({
|
|
7384
|
+
angleIndex: z39.number().int().nonnegative(),
|
|
7385
|
+
recommendedSec: z39.union([
|
|
7386
|
+
z39.literal(15),
|
|
7387
|
+
z39.literal(30)
|
|
7139
7388
|
]),
|
|
7140
|
-
estimatedDeltaCredits:
|
|
7389
|
+
estimatedDeltaCredits: z39.number().int().nonnegative().optional()
|
|
7141
7390
|
});
|
|
7142
7391
|
function buildSelectedImageUrlsHash(urls) {
|
|
7143
7392
|
const input = [
|
|
@@ -7151,13 +7400,13 @@ function buildSelectedImageUrlsHash(urls) {
|
|
|
7151
7400
|
return `fnv1a:${hash.toString(16).padStart(8, "0")}:${urls.length}`;
|
|
7152
7401
|
}
|
|
7153
7402
|
__name(buildSelectedImageUrlsHash, "buildSelectedImageUrlsHash");
|
|
7154
|
-
var ImageCreativeStyleEnum =
|
|
7403
|
+
var ImageCreativeStyleEnum = z39.enum([
|
|
7155
7404
|
"studio",
|
|
7156
7405
|
"floating",
|
|
7157
7406
|
"ingredients",
|
|
7158
7407
|
"in_use"
|
|
7159
7408
|
]);
|
|
7160
|
-
var ImageCreativeArchetypeEnum =
|
|
7409
|
+
var ImageCreativeArchetypeEnum = z39.enum([
|
|
7161
7410
|
"direct_response_product_ad",
|
|
7162
7411
|
"hero_outcome",
|
|
7163
7412
|
"offer_value_prop",
|
|
@@ -7166,23 +7415,23 @@ var ImageCreativeArchetypeEnum = z38.enum([
|
|
|
7166
7415
|
"problem_solution",
|
|
7167
7416
|
"lifestyle_identity"
|
|
7168
7417
|
]);
|
|
7169
|
-
var ImageCreativeVisualTreatmentEnum =
|
|
7418
|
+
var ImageCreativeVisualTreatmentEnum = z39.enum([
|
|
7170
7419
|
"performance_product_ad",
|
|
7171
7420
|
"premium_dtc_carousel"
|
|
7172
7421
|
]);
|
|
7173
|
-
var ImageCreativeAspectRatioEnum =
|
|
7422
|
+
var ImageCreativeAspectRatioEnum = z39.enum([
|
|
7174
7423
|
"1:1",
|
|
7175
7424
|
"4:5",
|
|
7176
7425
|
"9:16",
|
|
7177
7426
|
"16:9"
|
|
7178
7427
|
]);
|
|
7179
|
-
var ImageRefSchema =
|
|
7180
|
-
url:
|
|
7181
|
-
path:
|
|
7428
|
+
var ImageRefSchema = z39.object({
|
|
7429
|
+
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.'),
|
|
7430
|
+
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`.')
|
|
7182
7431
|
}).refine((v) => Boolean(v.url) !== Boolean(v.path), {
|
|
7183
7432
|
message: "each image entry must have exactly one of `url` or `path`"
|
|
7184
7433
|
});
|
|
7185
|
-
var ImagesInput =
|
|
7434
|
+
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.');
|
|
7186
7435
|
function asText(value) {
|
|
7187
7436
|
return {
|
|
7188
7437
|
content: [
|
|
@@ -7235,14 +7484,14 @@ async function submitCreative(api, opts) {
|
|
|
7235
7484
|
});
|
|
7236
7485
|
}
|
|
7237
7486
|
__name(submitCreative, "submitCreative");
|
|
7238
|
-
var ResearchTargetCountryEnum =
|
|
7487
|
+
var ResearchTargetCountryEnum = z39.enum([
|
|
7239
7488
|
"CO",
|
|
7240
7489
|
"MX",
|
|
7241
7490
|
"PE",
|
|
7242
7491
|
"EC",
|
|
7243
7492
|
"CL"
|
|
7244
7493
|
]);
|
|
7245
|
-
var ResearchCategoryEnum =
|
|
7494
|
+
var ResearchCategoryEnum = z39.enum([
|
|
7246
7495
|
"auto",
|
|
7247
7496
|
"automotive",
|
|
7248
7497
|
"kitchen",
|
|
@@ -7288,6 +7537,15 @@ var McpErrorCode = {
|
|
|
7288
7537
|
TASK_NOT_READY: "TASK_NOT_READY",
|
|
7289
7538
|
CREATIVE_VARIANT_FAILED: "CREATIVE_VARIANT_FAILED",
|
|
7290
7539
|
CREATIVE_VARIANT_DISPATCH_FAILED: "CREATIVE_VARIANT_DISPATCH_FAILED",
|
|
7540
|
+
// Guided dropshipping project / research
|
|
7541
|
+
DROPSHIPPING_PROJECT_NOT_FOUND: "DROPSHIPPING_PROJECT_NOT_FOUND",
|
|
7542
|
+
DROPSHIPPING_PROJECT_CANDIDATE_NOT_READY: "DROPSHIPPING_PROJECT_CANDIDATE_NOT_READY",
|
|
7543
|
+
DROPSHIPPING_PROJECT_VERSION_CONFLICT: "DROPSHIPPING_PROJECT_VERSION_CONFLICT",
|
|
7544
|
+
DROPSHIPPING_PROJECT_MEDIA_REQUIRED: "DROPSHIPPING_PROJECT_MEDIA_REQUIRED",
|
|
7545
|
+
DROPSHIPPING_PROJECT_DISABLED: "DROPSHIPPING_PROJECT_DISABLED",
|
|
7546
|
+
KALODATA_NOT_CONFIGURED: "KALODATA_NOT_CONFIGURED",
|
|
7547
|
+
META_AD_LIBRARY_NOT_CONFIGURED: "META_AD_LIBRARY_NOT_CONFIGURED",
|
|
7548
|
+
PRODUCT_RESEARCH_DISABLED: "PRODUCT_RESEARCH_DISABLED",
|
|
7291
7549
|
PRODUCT_RESEARCH_IDEMPOTENCY_CONFLICT: "PRODUCT_RESEARCH_IDEMPOTENCY_CONFLICT",
|
|
7292
7550
|
// Connection presence
|
|
7293
7551
|
NO_SHOPIFY_SHOPS: "NO_SHOPIFY_SHOPS",
|
|
@@ -7298,6 +7556,7 @@ var McpErrorCode = {
|
|
|
7298
7556
|
CAMPAIGN_MISSING_LANDING_URL: "CAMPAIGN_MISSING_LANDING_URL",
|
|
7299
7557
|
CAMPAIGN_NO_CREATIVES: "CAMPAIGN_NO_CREATIVES",
|
|
7300
7558
|
CAMPAIGN_MISSING_THUMBNAILS: "CAMPAIGN_MISSING_THUMBNAILS",
|
|
7559
|
+
CAMPAIGN_RECONCILIATION_REQUIRED: "CAMPAIGN_RECONCILIATION_REQUIRED",
|
|
7301
7560
|
// Shopify
|
|
7302
7561
|
SHOPIFY_NOT_CONNECTED: "SHOPIFY_NOT_CONNECTED",
|
|
7303
7562
|
SHOPIFY_TOKEN_EXPIRED: "SHOPIFY_TOKEN_EXPIRED",
|
|
@@ -7402,6 +7661,7 @@ var CODE_MESSAGES = {
|
|
|
7402
7661
|
CAMPAIGN_MISSING_LANDING_URL: "Missing landing URL for the campaign. Publish to Shopify first or pass `landingUrl` explicitly.",
|
|
7403
7662
|
CAMPAIGN_NO_CREATIVES: "The task has no creatives (video/image) ready for the campaign. Wait for generation to finish.",
|
|
7404
7663
|
CAMPAIGN_MISSING_THUMBNAILS: "Missing thumbnails for the videos. Verify that image/video generation has finished.",
|
|
7664
|
+
CAMPAIGN_RECONCILIATION_REQUIRED: "A campaign may already have been created. Do not retry creation; reopen the project and reconcile it or contact support.",
|
|
7405
7665
|
// Connections
|
|
7406
7666
|
NO_SHOPIFY_SHOPS: "No Shopify stores connected. Connect one at https://productmaker.app/integrations.",
|
|
7407
7667
|
NO_META_AD_ACCOUNTS: "No Meta Ads accounts connected. Connect Facebook at https://productmaker.app/integrations.",
|
|
@@ -7410,6 +7670,14 @@ var CODE_MESSAGES = {
|
|
|
7410
7670
|
TASK_NOT_READY: "The task is not ready for this action yet. Wait for it to finish and retry.",
|
|
7411
7671
|
CREATIVE_VARIANT_FAILED: "The previous creative variant attempt failed. Ask for confirmation before starting a new paid attempt with a new clientRequestId.",
|
|
7412
7672
|
CREATIVE_VARIANT_DISPATCH_FAILED: "The creative variant could not be queued. Retry as a new paid attempt with a new clientRequestId.",
|
|
7673
|
+
DROPSHIPPING_PROJECT_NOT_FOUND: "Project not found or it does not belong to this user.",
|
|
7674
|
+
DROPSHIPPING_PROJECT_CANDIDATE_NOT_READY: "That research candidate is no longer available. Return to the current research results and choose a fresh candidate.",
|
|
7675
|
+
DROPSHIPPING_PROJECT_VERSION_CONFLICT: "This project changed in another tab. Read it again, review the newer values, and retry with the fresh version.",
|
|
7676
|
+
DROPSHIPPING_PROJECT_MEDIA_REQUIRED: "This project needs a product photo before creating content. Upload one from the guided ingest step.",
|
|
7677
|
+
DROPSHIPPING_PROJECT_DISABLED: "Guided dropshipping projects are temporarily unavailable. Try again later.",
|
|
7678
|
+
KALODATA_NOT_CONFIGURED: "Product research is temporarily unavailable because the trend-data provider is not configured.",
|
|
7679
|
+
META_AD_LIBRARY_NOT_CONFIGURED: "Product research is temporarily unavailable because Meta Ad Library is not configured.",
|
|
7680
|
+
PRODUCT_RESEARCH_DISABLED: "Product research is temporarily unavailable. Try again later.",
|
|
7413
7681
|
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.",
|
|
7414
7682
|
// API key
|
|
7415
7683
|
INVALID_API_KEY: "Invalid API key. Check your PM_API_KEY in the MCP client configuration.",
|
|
@@ -7446,6 +7714,11 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
|
|
|
7446
7714
|
McpErrorCode.CAMPAIGN_NOT_FOUND,
|
|
7447
7715
|
McpErrorCode.CAMPAIGN_TASK_NOT_FOUND,
|
|
7448
7716
|
McpErrorCode.TASK_NOT_FOUND,
|
|
7717
|
+
McpErrorCode.DROPSHIPPING_PROJECT_NOT_FOUND,
|
|
7718
|
+
McpErrorCode.DROPSHIPPING_PROJECT_DISABLED,
|
|
7719
|
+
McpErrorCode.DROPSHIPPING_PROJECT_CANDIDATE_NOT_READY,
|
|
7720
|
+
McpErrorCode.DROPSHIPPING_PROJECT_VERSION_CONFLICT,
|
|
7721
|
+
McpErrorCode.DROPSHIPPING_PROJECT_MEDIA_REQUIRED,
|
|
7449
7722
|
McpErrorCode.META_TASK_NOT_FOUND,
|
|
7450
7723
|
McpErrorCode.META_INVALID_AD_ACCOUNT,
|
|
7451
7724
|
McpErrorCode.META_PIXEL_NOT_FOUND,
|
|
@@ -7454,6 +7727,7 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
|
|
|
7454
7727
|
McpErrorCode.TIKTOK_ADVERTISER_REQUIRED,
|
|
7455
7728
|
McpErrorCode.TIKTOK_PIXEL_NOT_FOUND,
|
|
7456
7729
|
McpErrorCode.CAMPAIGN_MISSING_LANDING_URL,
|
|
7730
|
+
McpErrorCode.CAMPAIGN_RECONCILIATION_REQUIRED,
|
|
7457
7731
|
McpErrorCode.META_NO_LANDING_URL,
|
|
7458
7732
|
McpErrorCode.META_DAILY_BUDGET_TOO_HIGH,
|
|
7459
7733
|
McpErrorCode.META_CAMPAIGN_CONFIG_INVALID,
|
|
@@ -7491,6 +7765,14 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
|
|
|
7491
7765
|
var PASSTHROUGH_CODES = /* @__PURE__ */ new Set([
|
|
7492
7766
|
McpErrorCode.INSUFFICIENT_CREDITS,
|
|
7493
7767
|
McpErrorCode.TASK_QUOTA_EXCEEDED,
|
|
7768
|
+
McpErrorCode.DROPSHIPPING_PROJECT_NOT_FOUND,
|
|
7769
|
+
McpErrorCode.DROPSHIPPING_PROJECT_DISABLED,
|
|
7770
|
+
McpErrorCode.DROPSHIPPING_PROJECT_CANDIDATE_NOT_READY,
|
|
7771
|
+
McpErrorCode.DROPSHIPPING_PROJECT_VERSION_CONFLICT,
|
|
7772
|
+
McpErrorCode.DROPSHIPPING_PROJECT_MEDIA_REQUIRED,
|
|
7773
|
+
McpErrorCode.KALODATA_NOT_CONFIGURED,
|
|
7774
|
+
McpErrorCode.META_AD_LIBRARY_NOT_CONFIGURED,
|
|
7775
|
+
McpErrorCode.PRODUCT_RESEARCH_DISABLED,
|
|
7494
7776
|
McpErrorCode.CREATIVE_VARIANT_DISPATCH_FAILED,
|
|
7495
7777
|
McpErrorCode.PRODUCT_RESEARCH_IDEMPOTENCY_CONFLICT,
|
|
7496
7778
|
// Shopify
|
|
@@ -7545,6 +7827,7 @@ var PASSTHROUGH_CODES = /* @__PURE__ */ new Set([
|
|
|
7545
7827
|
McpErrorCode.CAMPAIGN_MISSING_LANDING_URL,
|
|
7546
7828
|
McpErrorCode.CAMPAIGN_NO_CREATIVES,
|
|
7547
7829
|
McpErrorCode.CAMPAIGN_MISSING_THUMBNAILS,
|
|
7830
|
+
McpErrorCode.CAMPAIGN_RECONCILIATION_REQUIRED,
|
|
7548
7831
|
// Connections
|
|
7549
7832
|
McpErrorCode.NO_SHOPIFY_SHOPS,
|
|
7550
7833
|
McpErrorCode.NO_META_AD_ACCOUNTS,
|
|
@@ -7814,66 +8097,67 @@ function renderOutputsMarkdown(outputs) {
|
|
|
7814
8097
|
__name(renderOutputsMarkdown, "renderOutputsMarkdown");
|
|
7815
8098
|
|
|
7816
8099
|
// src/tools/tasks.ts
|
|
7817
|
-
var CreateProductTaskInput =
|
|
7818
|
-
images: ImagesInput,
|
|
7819
|
-
|
|
8100
|
+
var CreateProductTaskInput = z40.object({
|
|
8101
|
+
images: ImagesInput.optional().describe("1\u20135 fotos del mismo producto. Omitir \xFAnicamente cuando projectId apunta a un proyecto propio con imagen guardada."),
|
|
8102
|
+
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."),
|
|
8103
|
+
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."),
|
|
7820
8104
|
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.'),
|
|
7821
8105
|
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.'),
|
|
7822
|
-
price:
|
|
7823
|
-
compareAtPrice:
|
|
7824
|
-
offer:
|
|
7825
|
-
paymentMethods:
|
|
7826
|
-
productContext:
|
|
7827
|
-
productLabel:
|
|
8106
|
+
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.'),
|
|
8107
|
+
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".'),
|
|
8108
|
+
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.'),
|
|
8109
|
+
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.'),
|
|
8110
|
+
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."),
|
|
8111
|
+
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.'),
|
|
7828
8112
|
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.'),
|
|
7829
|
-
durationSeconds:
|
|
7830
|
-
|
|
7831
|
-
|
|
8113
|
+
durationSeconds: z40.union([
|
|
8114
|
+
z40.literal(15),
|
|
8115
|
+
z40.literal(30)
|
|
7832
8116
|
]).default(15).describe("Opcional. Duraci\xF3n objetivo del video: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
|
|
7833
|
-
pauseForAngleReview:
|
|
8117
|
+
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."),
|
|
7834
8118
|
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.'),
|
|
7835
|
-
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
|
|
7836
|
-
multiAngleVideos:
|
|
8119
|
+
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).'),
|
|
8120
|
+
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."),
|
|
7837
8121
|
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.'),
|
|
7838
|
-
outputs:
|
|
8122
|
+
outputs: z40.array(z40.enum([
|
|
7839
8123
|
"landing",
|
|
7840
8124
|
"video",
|
|
7841
8125
|
"image"
|
|
7842
8126
|
])).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.'),
|
|
7843
|
-
competitorScanEnabled:
|
|
7844
|
-
brandId:
|
|
7845
|
-
creativeDirection:
|
|
8127
|
+
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."),
|
|
8128
|
+
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."),
|
|
8129
|
+
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.'),
|
|
7846
8130
|
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".'),
|
|
7847
8131
|
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".')
|
|
7848
8132
|
}).strict();
|
|
7849
|
-
var GetTaskStatusInput =
|
|
7850
|
-
taskId:
|
|
7851
|
-
waitSeconds:
|
|
7852
|
-
});
|
|
7853
|
-
var ListTasksInput =
|
|
7854
|
-
limit:
|
|
7855
|
-
offset:
|
|
7856
|
-
search:
|
|
7857
|
-
});
|
|
7858
|
-
var EditTaskDraftInput =
|
|
7859
|
-
taskId:
|
|
7860
|
-
angleIndex:
|
|
7861
|
-
angle:
|
|
7862
|
-
angle:
|
|
7863
|
-
buyerPersona:
|
|
7864
|
-
actor:
|
|
7865
|
-
gender:
|
|
8133
|
+
var GetTaskStatusInput = z40.object({
|
|
8134
|
+
taskId: z40.string().min(1),
|
|
8135
|
+
waitSeconds: z40.number().int().min(0).max(120).optional()
|
|
8136
|
+
});
|
|
8137
|
+
var ListTasksInput = z40.object({
|
|
8138
|
+
limit: z40.number().int().min(1).max(50).optional(),
|
|
8139
|
+
offset: z40.number().int().min(0).optional(),
|
|
8140
|
+
search: z40.string().optional()
|
|
8141
|
+
});
|
|
8142
|
+
var EditTaskDraftInput = z40.object({
|
|
8143
|
+
taskId: z40.string().min(1),
|
|
8144
|
+
angleIndex: z40.number().int().min(0),
|
|
8145
|
+
angle: z40.object({
|
|
8146
|
+
angle: z40.string().min(1).max(480),
|
|
8147
|
+
buyerPersona: z40.string().min(1).max(360),
|
|
8148
|
+
actor: z40.string().min(1).max(360).optional(),
|
|
8149
|
+
gender: z40.enum([
|
|
7866
8150
|
"female",
|
|
7867
8151
|
"male"
|
|
7868
8152
|
]).optional(),
|
|
7869
|
-
scriptNotes:
|
|
8153
|
+
scriptNotes: z40.string().max(2e3).optional()
|
|
7870
8154
|
})
|
|
7871
8155
|
});
|
|
7872
|
-
var PendingActionPayload =
|
|
7873
|
-
|
|
7874
|
-
kind:
|
|
8156
|
+
var PendingActionPayload = z40.discriminatedUnion("kind", [
|
|
8157
|
+
z40.object({
|
|
8158
|
+
kind: z40.literal("product_reference_confirmation"),
|
|
7875
8159
|
userChoice: ProductReferenceUserChoiceEnum,
|
|
7876
|
-
confirmedProductNames:
|
|
8160
|
+
confirmedProductNames: z40.array(z40.string().min(1).max(120)).min(2).max(8).optional(),
|
|
7877
8161
|
// SCOPED multi-scene opt-in — the ONLY way STANDARD ("Multiescena") is
|
|
7878
8162
|
// reachable via MCP. Valid ONLY together with userChoice='variants' (same
|
|
7879
8163
|
// product in several colors): renders one shot per color and stitches them,
|
|
@@ -7884,62 +8168,62 @@ var PendingActionPayload = z39.discriminatedUnion("kind", [
|
|
|
7884
8168
|
// safe because multi-scene IS the intended render for a color variant set,
|
|
7885
8169
|
// not a blind quality upsell. Maps to `videoTier: 'standard'` on the
|
|
7886
8170
|
// confirm call; the backend re-gates it (variants + variant_set only).
|
|
7887
|
-
useMultiSceneShowcase:
|
|
8171
|
+
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".')
|
|
7888
8172
|
}),
|
|
7889
|
-
|
|
7890
|
-
kind:
|
|
8173
|
+
z40.object({
|
|
8174
|
+
kind: z40.literal("angle_review_approval"),
|
|
7891
8175
|
// All edits are optional — empty action body just approves the
|
|
7892
8176
|
// backend-generated draft as-is. Field shapes mirror the angle-review
|
|
7893
8177
|
// request contracts in @packages/types/wizard.ts; the handler routes to
|
|
7894
8178
|
// the dedicated video/image/autopilot endpoint based on StatusResponse.kind.
|
|
7895
|
-
customAngle:
|
|
8179
|
+
customAngle: z40.string().min(1).max(480).optional(),
|
|
7896
8180
|
// Keep the shared MCP action within the strictest downstream contract:
|
|
7897
8181
|
// autopilot/image resume endpoints cap these fields at 1500 characters.
|
|
7898
|
-
painPoints:
|
|
7899
|
-
benefits:
|
|
7900
|
-
features:
|
|
7901
|
-
gapDurationSeconds:
|
|
7902
|
-
|
|
7903
|
-
|
|
8182
|
+
painPoints: z40.string().max(1500).optional(),
|
|
8183
|
+
benefits: z40.string().max(1500).optional(),
|
|
8184
|
+
features: z40.array(z40.string().min(1).max(300)).max(10).optional(),
|
|
8185
|
+
gapDurationSeconds: z40.union([
|
|
8186
|
+
z40.literal(15),
|
|
8187
|
+
z40.literal(30)
|
|
7904
8188
|
]).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.")
|
|
7905
8189
|
})
|
|
7906
8190
|
]);
|
|
7907
|
-
var ResolvePendingActionInput =
|
|
7908
|
-
taskId:
|
|
8191
|
+
var ResolvePendingActionInput = z40.object({
|
|
8192
|
+
taskId: z40.string().min(1),
|
|
7909
8193
|
action: PendingActionPayload
|
|
7910
8194
|
}).strict();
|
|
7911
8195
|
function resolveVariantConfirmVideoTier(action) {
|
|
7912
8196
|
return action.useMultiSceneShowcase && action.userChoice === "variants" ? "standard" : void 0;
|
|
7913
8197
|
}
|
|
7914
8198
|
__name(resolveVariantConfirmVideoTier, "resolveVariantConfirmVideoTier");
|
|
7915
|
-
var StatusForResolveSchema =
|
|
8199
|
+
var StatusForResolveSchema = z40.object({
|
|
7916
8200
|
// Metadata used to select the backend resume contract. Older API responses
|
|
7917
8201
|
// may omit these fields, so the resolver keeps the generic fallback.
|
|
7918
|
-
kind:
|
|
8202
|
+
kind: z40.enum([
|
|
7919
8203
|
"autopilot",
|
|
7920
8204
|
"video",
|
|
7921
8205
|
"image",
|
|
7922
8206
|
"landing",
|
|
7923
8207
|
"mix"
|
|
7924
8208
|
]).optional(),
|
|
7925
|
-
selectedOutputs:
|
|
8209
|
+
selectedOutputs: z40.array(z40.enum([
|
|
7926
8210
|
"landing",
|
|
7927
8211
|
"video",
|
|
7928
8212
|
"image"
|
|
7929
8213
|
])).optional(),
|
|
7930
|
-
videoAngleCount:
|
|
7931
|
-
ugcFormat:
|
|
8214
|
+
videoAngleCount: z40.number().int().min(1).max(3).optional(),
|
|
8215
|
+
ugcFormat: z40.enum([
|
|
7932
8216
|
"single_8s",
|
|
7933
8217
|
"single_15s",
|
|
7934
8218
|
"double_30s"
|
|
7935
8219
|
]).optional(),
|
|
7936
|
-
videoStyle:
|
|
8220
|
+
videoStyle: z40.enum([
|
|
7937
8221
|
"ugc",
|
|
7938
8222
|
"cartoon_3d",
|
|
7939
8223
|
"product_only",
|
|
7940
8224
|
"ai_dynamic"
|
|
7941
8225
|
]).optional(),
|
|
7942
|
-
videoModel:
|
|
8226
|
+
videoModel: z40.enum([
|
|
7943
8227
|
"veo",
|
|
7944
8228
|
"wan",
|
|
7945
8229
|
"bytedance",
|
|
@@ -7947,26 +8231,26 @@ var StatusForResolveSchema = z39.object({
|
|
|
7947
8231
|
"grok",
|
|
7948
8232
|
"kling"
|
|
7949
8233
|
]).optional(),
|
|
7950
|
-
videoTier:
|
|
8234
|
+
videoTier: z40.enum([
|
|
7951
8235
|
"legacy",
|
|
7952
8236
|
"standard",
|
|
7953
8237
|
"pro",
|
|
7954
8238
|
"single_call"
|
|
7955
8239
|
]).optional(),
|
|
7956
|
-
videoResolution:
|
|
8240
|
+
videoResolution: z40.enum([
|
|
7957
8241
|
"480p",
|
|
7958
8242
|
"720p"
|
|
7959
8243
|
]).optional(),
|
|
7960
8244
|
narrativeStyle: NarrativeStyleEnum.optional(),
|
|
7961
|
-
pendingUserAction:
|
|
7962
|
-
kind:
|
|
7963
|
-
candidateGrouping:
|
|
7964
|
-
selectedImageUrls:
|
|
7965
|
-
proposedProductNames:
|
|
7966
|
-
customAngleDraft:
|
|
7967
|
-
painPointsDraft:
|
|
7968
|
-
benefitsDraft:
|
|
7969
|
-
featuresDraft:
|
|
8245
|
+
pendingUserAction: z40.object({
|
|
8246
|
+
kind: z40.string(),
|
|
8247
|
+
candidateGrouping: z40.string().optional(),
|
|
8248
|
+
selectedImageUrls: z40.array(z40.string()).optional(),
|
|
8249
|
+
proposedProductNames: z40.array(z40.string()).optional(),
|
|
8250
|
+
customAngleDraft: z40.string().optional(),
|
|
8251
|
+
painPointsDraft: z40.string().optional(),
|
|
8252
|
+
benefitsDraft: z40.string().optional(),
|
|
8253
|
+
featuresDraft: z40.array(z40.string()).optional(),
|
|
7970
8254
|
// Recommendation consumed only when the caller explicitly sends
|
|
7971
8255
|
// gapDurationSeconds: 30 in the angle approval action.
|
|
7972
8256
|
gapDurationChoice: GapDurationChoiceMirror.optional()
|
|
@@ -8007,11 +8291,14 @@ function resolveStatusTaskKind(status) {
|
|
|
8007
8291
|
}
|
|
8008
8292
|
__name(resolveStatusTaskKind, "resolveStatusTaskKind");
|
|
8009
8293
|
function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http" } = {}) {
|
|
8010
|
-
server.tool("create_product_task",
|
|
8294
|
+
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) => {
|
|
8011
8295
|
try {
|
|
8012
|
-
|
|
8296
|
+
if (!input.images && !input.projectId) {
|
|
8297
|
+
throw new Error("images es requerido salvo que env\xEDes projectId.");
|
|
8298
|
+
}
|
|
8299
|
+
const imgs = input.images ? await resolveImages(input.images, {
|
|
8013
8300
|
transport
|
|
8014
|
-
});
|
|
8301
|
+
}) : [];
|
|
8015
8302
|
const parts = imgs.map((img, i) => ({
|
|
8016
8303
|
...img,
|
|
8017
8304
|
filename: `image-${i}.png`
|
|
@@ -8022,6 +8309,7 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
|
|
|
8022
8309
|
const fields = flatten({
|
|
8023
8310
|
"options.languageCode": input.language,
|
|
8024
8311
|
"options.country": input.country,
|
|
8312
|
+
"options.dropshippingProjectId": input.projectId,
|
|
8025
8313
|
"options.price": input.price,
|
|
8026
8314
|
"options.compareAtPrice": input.compareAtPrice,
|
|
8027
8315
|
"options.offer": input.offer,
|
|
@@ -8288,54 +8576,56 @@ __name(registerTaskTools, "registerTaskTools");
|
|
|
8288
8576
|
|
|
8289
8577
|
// src/tools/creatives.ts
|
|
8290
8578
|
import { randomUUID } from "crypto";
|
|
8291
|
-
import { z as
|
|
8579
|
+
import { z as z41 } from "zod";
|
|
8292
8580
|
var CreativeRefs = {
|
|
8293
8581
|
images: ImagesInput,
|
|
8294
8582
|
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.'),
|
|
8295
8583
|
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.'),
|
|
8296
|
-
price:
|
|
8297
|
-
offer:
|
|
8298
|
-
compareAtPrice:
|
|
8299
|
-
paymentMethods:
|
|
8300
|
-
productContext:
|
|
8301
|
-
productLabel:
|
|
8302
|
-
buyerPersona:
|
|
8303
|
-
isCombo:
|
|
8304
|
-
competitorScanEnabled:
|
|
8305
|
-
brandId:
|
|
8306
|
-
|
|
8584
|
+
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.'),
|
|
8585
|
+
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.'),
|
|
8586
|
+
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".'),
|
|
8587
|
+
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.'),
|
|
8588
|
+
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."),
|
|
8589
|
+
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.'),
|
|
8590
|
+
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.'),
|
|
8591
|
+
isCombo: z41.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
|
|
8592
|
+
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."),
|
|
8593
|
+
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."),
|
|
8594
|
+
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."),
|
|
8595
|
+
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.')
|
|
8307
8596
|
};
|
|
8308
|
-
var VideoInput =
|
|
8597
|
+
var VideoInput = z41.object({
|
|
8309
8598
|
images: CreativeRefs.images,
|
|
8310
|
-
title:
|
|
8311
|
-
primaryAngle:
|
|
8599
|
+
title: z41.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
|
|
8600
|
+
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".'),
|
|
8312
8601
|
language: CreativeRefs.language,
|
|
8313
8602
|
country: CreativeRefs.country,
|
|
8314
|
-
aspectRatio:
|
|
8603
|
+
aspectRatio: z41.enum([
|
|
8315
8604
|
"9:16",
|
|
8316
8605
|
"16:9",
|
|
8317
8606
|
"Auto"
|
|
8318
8607
|
]).describe('Relaci\xF3n de aspecto. "9:16" = vertical (TikTok/Reels \u2014 default recomendado para ads). "16:9" = horizontal (YouTube). "Auto" = decide la IA.'),
|
|
8319
|
-
durationSeconds:
|
|
8320
|
-
|
|
8321
|
-
|
|
8608
|
+
durationSeconds: z41.union([
|
|
8609
|
+
z41.literal(15),
|
|
8610
|
+
z41.literal(30)
|
|
8322
8611
|
]).default(15).describe("Opcional. Duraci\xF3n objetivo: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
|
|
8323
8612
|
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).'),
|
|
8324
8613
|
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.'),
|
|
8325
|
-
videoResolution: VideoResolutionEnum.optional().describe('Opcional. Resoluci\xF3n del render. En la web se pregunta "\xBFResoluci\xF3n del video?". "
|
|
8326
|
-
features:
|
|
8614
|
+
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).'),
|
|
8615
|
+
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"].'),
|
|
8327
8616
|
price: CreativeRefs.price,
|
|
8328
8617
|
offer: CreativeRefs.offer,
|
|
8329
8618
|
productContext: CreativeRefs.productContext,
|
|
8330
8619
|
productLabel: CreativeRefs.productLabel,
|
|
8331
|
-
cta:
|
|
8332
|
-
scriptNotes:
|
|
8333
|
-
pauseForVideoScriptReview:
|
|
8334
|
-
pauseForAngleReview:
|
|
8620
|
+
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.'),
|
|
8621
|
+
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.'),
|
|
8622
|
+
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."),
|
|
8623
|
+
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."),
|
|
8335
8624
|
competitorScanEnabled: CreativeRefs.competitorScanEnabled,
|
|
8336
|
-
brandId: CreativeRefs.brandId
|
|
8625
|
+
brandId: CreativeRefs.brandId,
|
|
8626
|
+
projectId: CreativeRefs.projectId
|
|
8337
8627
|
}).strict();
|
|
8338
|
-
var FreeVideoQualityEnum =
|
|
8628
|
+
var FreeVideoQualityEnum = z41.enum([
|
|
8339
8629
|
"economical",
|
|
8340
8630
|
"high_detail"
|
|
8341
8631
|
]);
|
|
@@ -8357,38 +8647,38 @@ var FREE_VIDEO_DURATIONS_BY_QUALITY = {
|
|
|
8357
8647
|
15
|
|
8358
8648
|
]
|
|
8359
8649
|
};
|
|
8360
|
-
var FreeVideoInput =
|
|
8650
|
+
var FreeVideoInput = z41.object({
|
|
8361
8651
|
images: ImagesInput.optional().describe("Opcional. 1-5 fotos del producto. Om\xEDtelo para generar desde texto puro."),
|
|
8362
|
-
rawPrompt:
|
|
8652
|
+
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."),
|
|
8363
8653
|
quality: FreeVideoQualityEnum.describe('Calidad del video: "economical" = Econ\xF3mico o "high_detail" = Alto detalle.'),
|
|
8364
|
-
durationSeconds:
|
|
8654
|
+
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."),
|
|
8365
8655
|
videoResolution: VideoResolutionEnum.describe("Resoluci\xF3n del video: 480p o 720p."),
|
|
8366
|
-
generateAiMusic:
|
|
8367
|
-
addSubtitles:
|
|
8368
|
-
idempotencyKey:
|
|
8656
|
+
generateAiMusic: z41.boolean().default(false).describe("Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false."),
|
|
8657
|
+
addSubtitles: z41.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
|
|
8658
|
+
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.")
|
|
8369
8659
|
}).strict();
|
|
8370
8660
|
var imageStyleList = ImageCreativeStyleEnum.options.join(" | ");
|
|
8371
8661
|
var archetypeList = ImageCreativeArchetypeEnum.options.join(" | ");
|
|
8372
8662
|
var visualTreatmentList = ImageCreativeVisualTreatmentEnum.options.join(" | ");
|
|
8373
8663
|
var aspectRatioList = ImageCreativeAspectRatioEnum.options.join(" | ");
|
|
8374
|
-
var ImageInput =
|
|
8664
|
+
var ImageInput = z41.object({
|
|
8375
8665
|
images: CreativeRefs.images,
|
|
8376
|
-
productTitle:
|
|
8377
|
-
primaryAngle:
|
|
8666
|
+
productTitle: z41.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
|
|
8667
|
+
primaryAngle: z41.string().min(1).describe('\xC1ngulo de venta principal. Ej. "Reduce arrugas en 14 d\xEDas".'),
|
|
8378
8668
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
8379
8669
|
language: CreativeRefs.language,
|
|
8380
8670
|
country: CreativeRefs.country,
|
|
8381
|
-
nVariants:
|
|
8382
|
-
imageModel:
|
|
8671
|
+
nVariants: z41.number().int().min(1).max(6).optional().describe("Opcional. N\xFAmero de variantes a generar (1-6). Default 3."),
|
|
8672
|
+
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.'),
|
|
8383
8673
|
imageStyle: ImageCreativeStyleEnum.optional().describe(`Opcional. Estilo del shot del producto. Valores: ${imageStyleList}. Si se omite, la IA escoge seg\xFAn el archetype.`),
|
|
8384
8674
|
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).`),
|
|
8385
8675
|
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.`),
|
|
8386
|
-
styleMix:
|
|
8676
|
+
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."),
|
|
8387
8677
|
aspectRatio: ImageCreativeAspectRatioEnum.optional().describe(`Opcional. Relaci\xF3n de aspecto. Valores: ${aspectRatioList}. Default "4:5" (vertical Instagram feed).`),
|
|
8388
|
-
generateAdCopy:
|
|
8389
|
-
pauseForAngleReview:
|
|
8390
|
-
features:
|
|
8391
|
-
brandColors:
|
|
8678
|
+
generateAdCopy: z41.boolean().optional().describe("Opcional. Si true, tambi\xE9n genera headline/body/CTA para el ad. Default false."),
|
|
8679
|
+
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."),
|
|
8680
|
+
features: z41.array(z41.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
|
|
8681
|
+
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.'),
|
|
8392
8682
|
price: CreativeRefs.price,
|
|
8393
8683
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
8394
8684
|
offer: CreativeRefs.offer,
|
|
@@ -8397,13 +8687,14 @@ var ImageInput = z40.object({
|
|
|
8397
8687
|
isCombo: CreativeRefs.isCombo,
|
|
8398
8688
|
competitorScanEnabled: CreativeRefs.competitorScanEnabled,
|
|
8399
8689
|
brandId: CreativeRefs.brandId,
|
|
8690
|
+
projectId: CreativeRefs.projectId,
|
|
8400
8691
|
creativeDirection: CreativeRefs.creativeDirection
|
|
8401
8692
|
});
|
|
8402
|
-
var LandingInput =
|
|
8693
|
+
var LandingInput = z41.object({
|
|
8403
8694
|
images: CreativeRefs.images,
|
|
8404
8695
|
language: CreativeRefs.language,
|
|
8405
8696
|
country: CreativeRefs.country,
|
|
8406
|
-
salesAngle:
|
|
8697
|
+
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.'),
|
|
8407
8698
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
8408
8699
|
price: CreativeRefs.price,
|
|
8409
8700
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
@@ -8414,6 +8705,7 @@ var LandingInput = z40.object({
|
|
|
8414
8705
|
isCombo: CreativeRefs.isCombo,
|
|
8415
8706
|
competitorScanEnabled: CreativeRefs.competitorScanEnabled,
|
|
8416
8707
|
brandId: CreativeRefs.brandId,
|
|
8708
|
+
projectId: CreativeRefs.projectId,
|
|
8417
8709
|
creativeDirection: CreativeRefs.creativeDirection,
|
|
8418
8710
|
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".'),
|
|
8419
8711
|
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".')
|
|
@@ -8453,7 +8745,7 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
8453
8745
|
try {
|
|
8454
8746
|
const resolved = resolveVideoPreset(input.videoPreset);
|
|
8455
8747
|
const wireTier = resolveWireVideoTier(input.videoTier, input.durationSeconds);
|
|
8456
|
-
const { videoTier: _publicTier, videoResolution: _vr, ...rest } = input;
|
|
8748
|
+
const { videoTier: _publicTier, videoResolution: _vr, projectId, ...rest } = input;
|
|
8457
8749
|
const wirePayload = {
|
|
8458
8750
|
...rest
|
|
8459
8751
|
};
|
|
@@ -8463,6 +8755,7 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
8463
8755
|
}
|
|
8464
8756
|
wirePayload.videoTier = wireTier;
|
|
8465
8757
|
if (input.videoResolution) wirePayload.videoResolution = input.videoResolution;
|
|
8758
|
+
if (projectId) wirePayload.dropshippingProjectId = projectId;
|
|
8466
8759
|
return await submitCreative(api, {
|
|
8467
8760
|
path: "/v1/creatives/videos",
|
|
8468
8761
|
estimatedSeconds: 300,
|
|
@@ -8475,10 +8768,16 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
8475
8768
|
});
|
|
8476
8769
|
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) => {
|
|
8477
8770
|
try {
|
|
8771
|
+
const { projectId, ...rest } = input;
|
|
8478
8772
|
return await submitCreative(api, {
|
|
8479
8773
|
path: "/v1/creatives/images",
|
|
8480
8774
|
estimatedSeconds: 60,
|
|
8481
|
-
input
|
|
8775
|
+
input: {
|
|
8776
|
+
...rest,
|
|
8777
|
+
...projectId ? {
|
|
8778
|
+
dropshippingProjectId: projectId
|
|
8779
|
+
} : {}
|
|
8780
|
+
},
|
|
8482
8781
|
transport
|
|
8483
8782
|
});
|
|
8484
8783
|
} catch (e) {
|
|
@@ -8487,10 +8786,16 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
8487
8786
|
});
|
|
8488
8787
|
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) => {
|
|
8489
8788
|
try {
|
|
8789
|
+
const { projectId, ...rest } = input;
|
|
8490
8790
|
return await submitCreative(api, {
|
|
8491
8791
|
path: "/v1/creatives/landing",
|
|
8492
8792
|
estimatedSeconds: 60,
|
|
8493
|
-
input
|
|
8793
|
+
input: {
|
|
8794
|
+
...rest,
|
|
8795
|
+
...projectId ? {
|
|
8796
|
+
dropshippingProjectId: projectId
|
|
8797
|
+
} : {}
|
|
8798
|
+
},
|
|
8494
8799
|
transport
|
|
8495
8800
|
});
|
|
8496
8801
|
} catch (e) {
|
|
@@ -8501,8 +8806,8 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
8501
8806
|
__name(registerCreativeTools, "registerCreativeTools");
|
|
8502
8807
|
|
|
8503
8808
|
// src/tools/creative-variants.ts
|
|
8504
|
-
import { z as
|
|
8505
|
-
var LandingSectionKeyEnum =
|
|
8809
|
+
import { z as z42 } from "zod";
|
|
8810
|
+
var LandingSectionKeyEnum = z42.enum([
|
|
8506
8811
|
"problem_hook",
|
|
8507
8812
|
"problem_cost",
|
|
8508
8813
|
"solution_mechanism",
|
|
@@ -8542,24 +8847,24 @@ var LandingSectionKeyEnum = z41.enum([
|
|
|
8542
8847
|
"lifestyle_fit",
|
|
8543
8848
|
"join_lifestyle"
|
|
8544
8849
|
]);
|
|
8545
|
-
var CreativeVariantKindEnum =
|
|
8850
|
+
var CreativeVariantKindEnum = z42.enum([
|
|
8546
8851
|
"video",
|
|
8547
8852
|
"image",
|
|
8548
8853
|
"landing"
|
|
8549
8854
|
]);
|
|
8550
|
-
var EstimateCreativeVariantInputBase =
|
|
8551
|
-
sourceTaskId:
|
|
8855
|
+
var EstimateCreativeVariantInputBase = z42.object({
|
|
8856
|
+
sourceTaskId: z42.string().uuid().describe("ID de la tarea original completada."),
|
|
8552
8857
|
kind: CreativeVariantKindEnum,
|
|
8553
8858
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Solo cambia el preset p\xFAblico; el servidor conserva el modelo/tier del task origen."),
|
|
8554
8859
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video. Resoluci\xF3n del nuevo render."),
|
|
8555
|
-
nVariants:
|
|
8860
|
+
nVariants: z42.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
8556
8861
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si ya conoces la secci\xF3n, evita cotizar la clasificaci\xF3n con IA."),
|
|
8557
|
-
instructions:
|
|
8862
|
+
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.")
|
|
8558
8863
|
}).strict();
|
|
8559
8864
|
var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
8560
8865
|
if (input.kind === "video" && input.nVariants !== void 0) {
|
|
8561
8866
|
ctx.addIssue({
|
|
8562
|
-
code:
|
|
8867
|
+
code: z42.ZodIssueCode.custom,
|
|
8563
8868
|
path: [
|
|
8564
8869
|
"nVariants"
|
|
8565
8870
|
],
|
|
@@ -8569,7 +8874,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
8569
8874
|
if (input.kind !== "video") {
|
|
8570
8875
|
if (input.videoPreset !== void 0) {
|
|
8571
8876
|
ctx.addIssue({
|
|
8572
|
-
code:
|
|
8877
|
+
code: z42.ZodIssueCode.custom,
|
|
8573
8878
|
path: [
|
|
8574
8879
|
"videoPreset"
|
|
8575
8880
|
],
|
|
@@ -8578,7 +8883,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
8578
8883
|
}
|
|
8579
8884
|
if (input.videoResolution !== void 0) {
|
|
8580
8885
|
ctx.addIssue({
|
|
8581
|
-
code:
|
|
8886
|
+
code: z42.ZodIssueCode.custom,
|
|
8582
8887
|
path: [
|
|
8583
8888
|
"videoResolution"
|
|
8584
8889
|
],
|
|
@@ -8588,7 +8893,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
8588
8893
|
}
|
|
8589
8894
|
if (input.kind !== "image" && input.nVariants !== void 0) {
|
|
8590
8895
|
ctx.addIssue({
|
|
8591
|
-
code:
|
|
8896
|
+
code: z42.ZodIssueCode.custom,
|
|
8592
8897
|
path: [
|
|
8593
8898
|
"nVariants"
|
|
8594
8899
|
],
|
|
@@ -8598,7 +8903,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
8598
8903
|
if (input.kind !== "landing") {
|
|
8599
8904
|
if (input.sectionKey !== void 0) {
|
|
8600
8905
|
ctx.addIssue({
|
|
8601
|
-
code:
|
|
8906
|
+
code: z42.ZodIssueCode.custom,
|
|
8602
8907
|
path: [
|
|
8603
8908
|
"sectionKey"
|
|
8604
8909
|
],
|
|
@@ -8607,7 +8912,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
8607
8912
|
}
|
|
8608
8913
|
if (input.instructions !== void 0) {
|
|
8609
8914
|
ctx.addIssue({
|
|
8610
|
-
code:
|
|
8915
|
+
code: z42.ZodIssueCode.custom,
|
|
8611
8916
|
path: [
|
|
8612
8917
|
"instructions"
|
|
8613
8918
|
],
|
|
@@ -8616,31 +8921,31 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
8616
8921
|
}
|
|
8617
8922
|
}
|
|
8618
8923
|
});
|
|
8619
|
-
var CreateCreativeVariantInputBase =
|
|
8620
|
-
sourceTaskId:
|
|
8924
|
+
var CreateCreativeVariantInputBase = z42.object({
|
|
8925
|
+
sourceTaskId: z42.string().uuid().describe("ID de la tarea original completada."),
|
|
8621
8926
|
kind: CreativeVariantKindEnum,
|
|
8622
|
-
confirm:
|
|
8623
|
-
clientRequestId:
|
|
8624
|
-
angle:
|
|
8625
|
-
buyerPersona:
|
|
8626
|
-
scriptNotes:
|
|
8627
|
-
creativeDirection:
|
|
8927
|
+
confirm: z42.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
|
|
8928
|
+
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."),
|
|
8929
|
+
angle: z42.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
|
|
8930
|
+
buyerPersona: z42.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
|
|
8931
|
+
scriptNotes: z42.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
|
|
8932
|
+
creativeDirection: z42.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
|
|
8628
8933
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Preset p\xFAblico; el servidor resuelve modelo/tier desde el task origen."),
|
|
8629
8934
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video."),
|
|
8630
|
-
generateAiMusic:
|
|
8631
|
-
nVariants:
|
|
8935
|
+
generateAiMusic: z42.boolean().optional().describe("Opcional para video."),
|
|
8936
|
+
nVariants: z42.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
8632
8937
|
imageStyle: ImageCreativeStyleEnum.optional().describe("Opcional para imagen."),
|
|
8633
8938
|
creativeArchetype: ImageCreativeArchetypeEnum.optional().describe("Opcional para imagen."),
|
|
8634
8939
|
visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe("Opcional para imagen."),
|
|
8635
8940
|
aspectRatio: ImageCreativeAspectRatioEnum.optional().describe("Opcional para imagen."),
|
|
8636
|
-
styleMix:
|
|
8941
|
+
styleMix: z42.boolean().optional().describe("Opcional para imagen."),
|
|
8637
8942
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si se omite, se clasifica con IA a partir de instructions."),
|
|
8638
|
-
instructions:
|
|
8943
|
+
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.")
|
|
8639
8944
|
}).strict();
|
|
8640
8945
|
var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
8641
8946
|
if ((input.kind === "video" || input.kind === "image") && !input.angle) {
|
|
8642
8947
|
ctx.addIssue({
|
|
8643
|
-
code:
|
|
8948
|
+
code: z42.ZodIssueCode.custom,
|
|
8644
8949
|
path: [
|
|
8645
8950
|
"angle"
|
|
8646
8951
|
],
|
|
@@ -8649,7 +8954,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
8649
8954
|
}
|
|
8650
8955
|
if (input.kind === "landing" && !input.sectionKey && !input.instructions) {
|
|
8651
8956
|
ctx.addIssue({
|
|
8652
|
-
code:
|
|
8957
|
+
code: z42.ZodIssueCode.custom,
|
|
8653
8958
|
path: [
|
|
8654
8959
|
"instructions"
|
|
8655
8960
|
],
|
|
@@ -8695,7 +9000,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
8695
9000
|
for (const field of fieldsByKind[input.kind]) {
|
|
8696
9001
|
if (input[field] !== void 0) {
|
|
8697
9002
|
ctx.addIssue({
|
|
8698
|
-
code:
|
|
9003
|
+
code: z42.ZodIssueCode.custom,
|
|
8699
9004
|
path: [
|
|
8700
9005
|
field
|
|
8701
9006
|
],
|
|
@@ -8812,27 +9117,27 @@ function registerCreativeVariantTools(server, api) {
|
|
|
8812
9117
|
__name(registerCreativeVariantTools, "registerCreativeVariantTools");
|
|
8813
9118
|
|
|
8814
9119
|
// src/tools/script-review.ts
|
|
8815
|
-
import { z as
|
|
8816
|
-
var GetVideoScriptInput =
|
|
8817
|
-
taskId:
|
|
8818
|
-
angleKey:
|
|
9120
|
+
import { z as z43 } from "zod";
|
|
9121
|
+
var GetVideoScriptInput = z43.object({
|
|
9122
|
+
taskId: z43.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
|
|
9123
|
+
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.')
|
|
8819
9124
|
}).strict();
|
|
8820
9125
|
var VIDEO_SCRIPT_APPROVAL_MAX_SCENES2 = 50;
|
|
8821
9126
|
var VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2 = 2e3;
|
|
8822
|
-
var ApproveSceneEdit =
|
|
8823
|
-
index:
|
|
8824
|
-
dialogue:
|
|
9127
|
+
var ApproveSceneEdit = z43.object({
|
|
9128
|
+
index: z43.number().int().nonnegative().describe("\xCDndice de la escena (0-based, mismo que devuelve get_video_script)."),
|
|
9129
|
+
dialogue: z43.string().min(1).max(VIDEO_SCRIPT_APPROVAL_MAX_DIALOGUE_CHARS2).refine((v) => v.trim().length > 0, {
|
|
8825
9130
|
message: "dialogue no puede ser s\xF3lo espacios"
|
|
8826
9131
|
}).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.")
|
|
8827
9132
|
}).strict();
|
|
8828
|
-
var ApproveVideoScriptInput =
|
|
8829
|
-
taskId:
|
|
8830
|
-
angleKey:
|
|
8831
|
-
scenes:
|
|
9133
|
+
var ApproveVideoScriptInput = z43.object({
|
|
9134
|
+
taskId: z43.string().min(1).describe("ID de la tarea."),
|
|
9135
|
+
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.'),
|
|
9136
|
+
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.")
|
|
8832
9137
|
}).strict();
|
|
8833
|
-
var RegenerateVideoScriptInput =
|
|
8834
|
-
taskId:
|
|
8835
|
-
angleKey:
|
|
9138
|
+
var RegenerateVideoScriptInput = z43.object({
|
|
9139
|
+
taskId: z43.string().min(1).describe("ID de la tarea."),
|
|
9140
|
+
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.'),
|
|
8836
9141
|
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).')
|
|
8837
9142
|
}).strict();
|
|
8838
9143
|
function registerScriptReviewTools(server, api) {
|
|
@@ -8872,9 +9177,9 @@ function registerScriptReviewTools(server, api) {
|
|
|
8872
9177
|
__name(registerScriptReviewTools, "registerScriptReviewTools");
|
|
8873
9178
|
|
|
8874
9179
|
// src/tools/connections.ts
|
|
8875
|
-
import { z as
|
|
9180
|
+
import { z as z44 } from "zod";
|
|
8876
9181
|
function registerConnectionTools(server, api) {
|
|
8877
|
-
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.",
|
|
9182
|
+
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 () => {
|
|
8878
9183
|
try {
|
|
8879
9184
|
const r = await api.getJson("/v1/shopify/shops");
|
|
8880
9185
|
return asText(r);
|
|
@@ -8882,10 +9187,10 @@ function registerConnectionTools(server, api) {
|
|
|
8882
9187
|
throw translateError(e);
|
|
8883
9188
|
}
|
|
8884
9189
|
});
|
|
8885
|
-
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.',
|
|
8886
|
-
shopDomain:
|
|
8887
|
-
query:
|
|
8888
|
-
taskId:
|
|
9190
|
+
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({
|
|
9191
|
+
shopDomain: z44.string().min(3).max(120),
|
|
9192
|
+
query: z44.string().trim().max(120).optional(),
|
|
9193
|
+
taskId: z44.string().uuid().optional()
|
|
8889
9194
|
}).shape, async ({ shopDomain, query, taskId }) => {
|
|
8890
9195
|
try {
|
|
8891
9196
|
const params = new URLSearchParams({
|
|
@@ -8899,7 +9204,7 @@ function registerConnectionTools(server, api) {
|
|
|
8899
9204
|
throw translateError(e);
|
|
8900
9205
|
}
|
|
8901
9206
|
});
|
|
8902
|
-
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.",
|
|
9207
|
+
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 () => {
|
|
8903
9208
|
try {
|
|
8904
9209
|
const r = await api.getJson("/v1/facebook/ad-accounts");
|
|
8905
9210
|
return asText(r);
|
|
@@ -8907,7 +9212,7 @@ function registerConnectionTools(server, api) {
|
|
|
8907
9212
|
throw translateError(e);
|
|
8908
9213
|
}
|
|
8909
9214
|
});
|
|
8910
|
-
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.",
|
|
9215
|
+
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 () => {
|
|
8911
9216
|
try {
|
|
8912
9217
|
const r = await api.getJson("/v1/tiktok/advertisers");
|
|
8913
9218
|
return asText(r);
|
|
@@ -8919,105 +9224,105 @@ function registerConnectionTools(server, api) {
|
|
|
8919
9224
|
__name(registerConnectionTools, "registerConnectionTools");
|
|
8920
9225
|
|
|
8921
9226
|
// src/tools/publish.ts
|
|
8922
|
-
import { z as
|
|
8923
|
-
var ShopifyInput =
|
|
8924
|
-
taskId:
|
|
8925
|
-
shopDomain:
|
|
9227
|
+
import { z as z45 } from "zod";
|
|
9228
|
+
var ShopifyInput = z45.object({
|
|
9229
|
+
taskId: z45.string().min(1),
|
|
9230
|
+
shopDomain: z45.string().min(1),
|
|
8926
9231
|
/** Override: number of landing images to include. Default = ALL images the
|
|
8927
9232
|
* task generated (the MCP fetches /v1/results first to count). Bounded
|
|
8928
9233
|
* generously here; the backend validates each index against
|
|
8929
9234
|
* landing.images.length and rejects out-of-range. */
|
|
8930
|
-
numLandingImages:
|
|
8931
|
-
variantIndex:
|
|
9235
|
+
numLandingImages: z45.number().int().min(1).max(20).optional(),
|
|
9236
|
+
variantIndex: z45.number().int().min(0).optional(),
|
|
8932
9237
|
/** ASK THE USER before setting: "¿Tu producto tiene variantes (talla, color, etc.)?".
|
|
8933
9238
|
* When true, the landing renders variant-picker pills; when false, it ships
|
|
8934
9239
|
* a single-option product page. Defaults to false. */
|
|
8935
|
-
showVariantPicker:
|
|
9240
|
+
showVariantPicker: z45.boolean().optional(),
|
|
8936
9241
|
/** Default `auto` updates the product previously published for this task and
|
|
8937
9242
|
* shop. Use `existing` with targetProductId to choose another product, or
|
|
8938
9243
|
* `new` only when the user explicitly wants a duplicate/new listing. */
|
|
8939
|
-
publicationMode:
|
|
9244
|
+
publicationMode: z45.enum([
|
|
8940
9245
|
"auto",
|
|
8941
9246
|
"existing",
|
|
8942
9247
|
"new"
|
|
8943
9248
|
]).optional(),
|
|
8944
9249
|
/** Shopify product GID, numeric id, or admin product URL. Required when
|
|
8945
9250
|
* publicationMode is `existing`. */
|
|
8946
|
-
targetProductId:
|
|
9251
|
+
targetProductId: z45.string().trim().min(1).max(300).optional()
|
|
8947
9252
|
});
|
|
8948
|
-
var McpCampaignConfig =
|
|
9253
|
+
var McpCampaignConfig = z45.object({
|
|
8949
9254
|
/** ALWAYS in US dollars, whatever the ad account's currency is. ProductMaker
|
|
8950
9255
|
* converts to the account currency server-side. Do NOT pre-convert: on a COP
|
|
8951
9256
|
* account the user's "60.000 al día" is `dailyBudgetUsd: 15`, not 60000. */
|
|
8952
|
-
dailyBudgetUsd:
|
|
9257
|
+
dailyBudgetUsd: z45.number().positive().optional(),
|
|
8953
9258
|
/** ISO-3166-1 alpha-2 country codes. Meta accepts max 1 (single-country
|
|
8954
9259
|
* ad set). TikTok takes only the first entry. */
|
|
8955
|
-
targetCountries:
|
|
8956
|
-
campaignName:
|
|
9260
|
+
targetCountries: z45.array(z45.string().length(2)).max(1).optional(),
|
|
9261
|
+
campaignName: z45.string().trim().min(1).max(100).optional()
|
|
8957
9262
|
});
|
|
8958
9263
|
var McpAdAccountId = MetaAdAccountId.describe('Meta ad account id. Accepts "act_1234567890" or the bare numeric id "1234567890".');
|
|
8959
|
-
var MetaInput =
|
|
8960
|
-
taskId:
|
|
9264
|
+
var MetaInput = z45.object({
|
|
9265
|
+
taskId: z45.string().min(1),
|
|
8961
9266
|
adAccountId: McpAdAccountId,
|
|
8962
|
-
pixelId:
|
|
8963
|
-
landingUrl:
|
|
9267
|
+
pixelId: z45.string().min(1).optional(),
|
|
9268
|
+
landingUrl: z45.string().url().optional(),
|
|
8964
9269
|
/** Select which angles to publish by 0-based index. Omit to publish all
|
|
8965
9270
|
* successful angles. Example: a task with 3 angles → `[0,2]` publishes #0 + #2. */
|
|
8966
|
-
angleIndexes:
|
|
9271
|
+
angleIndexes: z45.array(z45.number().int().nonnegative()).optional(),
|
|
8967
9272
|
campaignConfig: McpCampaignConfig.optional(),
|
|
8968
9273
|
/** Override the ad's primary text (body copy). If omitted, ProductMaker
|
|
8969
9274
|
* uses the auto-generated copy attached to the task. Capped at 500 chars
|
|
8970
9275
|
* (Meta's primary-text limit). */
|
|
8971
|
-
primaryText:
|
|
9276
|
+
primaryText: z45.string().trim().min(1).max(500).optional(),
|
|
8972
9277
|
/** Optional WhatsApp number. When set, the ad button opens a WhatsApp chat
|
|
8973
9278
|
* (wa.me) instead of a landing page. */
|
|
8974
|
-
phoneNumber:
|
|
9279
|
+
phoneNumber: z45.string().min(8).max(32).optional()
|
|
8975
9280
|
});
|
|
8976
|
-
var GroupedTaskSource =
|
|
8977
|
-
type:
|
|
8978
|
-
taskId:
|
|
8979
|
-
angleIndex:
|
|
8980
|
-
videoVariant:
|
|
9281
|
+
var GroupedTaskSource = z45.object({
|
|
9282
|
+
type: z45.literal("task"),
|
|
9283
|
+
taskId: z45.string().min(1),
|
|
9284
|
+
angleIndex: z45.number().int().min(0).optional(),
|
|
9285
|
+
videoVariant: z45.enum([
|
|
8981
9286
|
"with_subtitles",
|
|
8982
9287
|
"without_subtitles"
|
|
8983
9288
|
]).optional(),
|
|
8984
|
-
imageVariantIndex:
|
|
8985
|
-
});
|
|
8986
|
-
var GroupedMediaSource =
|
|
8987
|
-
type:
|
|
8988
|
-
url:
|
|
8989
|
-
path:
|
|
8990
|
-
thumbnailUrl:
|
|
8991
|
-
thumbnailPath:
|
|
9289
|
+
imageVariantIndex: z45.number().int().min(0).optional()
|
|
9290
|
+
});
|
|
9291
|
+
var GroupedMediaSource = z45.object({
|
|
9292
|
+
type: z45.literal("media"),
|
|
9293
|
+
url: z45.string().url().optional(),
|
|
9294
|
+
path: z45.string().min(1).optional(),
|
|
9295
|
+
thumbnailUrl: z45.string().url().optional(),
|
|
9296
|
+
thumbnailPath: z45.string().min(1).optional()
|
|
8992
9297
|
}).refine((value) => Boolean(value.url) !== Boolean(value.path), {
|
|
8993
9298
|
message: "media source needs exactly one of `url` or `path`"
|
|
8994
9299
|
}).refine((value) => !(value.thumbnailUrl && value.thumbnailPath), {
|
|
8995
9300
|
message: "media source needs at most one of `thumbnailUrl` or `thumbnailPath`"
|
|
8996
9301
|
});
|
|
8997
|
-
var GroupedCreative =
|
|
8998
|
-
type:
|
|
9302
|
+
var GroupedCreative = z45.object({
|
|
9303
|
+
type: z45.enum([
|
|
8999
9304
|
"image",
|
|
9000
9305
|
"video"
|
|
9001
9306
|
]),
|
|
9002
|
-
source:
|
|
9307
|
+
source: z45.union([
|
|
9003
9308
|
GroupedTaskSource,
|
|
9004
9309
|
GroupedMediaSource
|
|
9005
9310
|
]),
|
|
9006
|
-
title:
|
|
9007
|
-
description:
|
|
9311
|
+
title: z45.string().max(120).optional(),
|
|
9312
|
+
description: z45.string().max(500).optional(),
|
|
9008
9313
|
cta: MetaCtaType.optional(),
|
|
9009
|
-
creativeId:
|
|
9314
|
+
creativeId: z45.string().min(1).optional()
|
|
9010
9315
|
});
|
|
9011
|
-
var GroupedCampaignConfig =
|
|
9316
|
+
var GroupedCampaignConfig = z45.object({
|
|
9012
9317
|
campaignType: MetaCampaignType.default("sales"),
|
|
9013
9318
|
optimizationMode: MetaOptimizationMode.default("advantage_plus"),
|
|
9014
9319
|
budgetMode: MetaBudgetMode.default("campaign"),
|
|
9015
|
-
campaignDailyBudget:
|
|
9016
|
-
adSets:
|
|
9017
|
-
tempId:
|
|
9018
|
-
name:
|
|
9019
|
-
dailyBudget:
|
|
9020
|
-
creativeIds:
|
|
9320
|
+
campaignDailyBudget: z45.number().int().positive().optional(),
|
|
9321
|
+
adSets: z45.array(z45.object({
|
|
9322
|
+
tempId: z45.string().min(1).max(80),
|
|
9323
|
+
name: z45.string().min(1).max(100),
|
|
9324
|
+
dailyBudget: z45.number().int().positive().optional(),
|
|
9325
|
+
creativeIds: z45.array(z45.string().min(1)).min(1)
|
|
9021
9326
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_AD_SETS),
|
|
9022
9327
|
/**
|
|
9023
9328
|
* Valid values are 'landing' and 'whatsapp' — there is no 'website'. This
|
|
@@ -9025,22 +9330,22 @@ var GroupedCampaignConfig = z44.object({
|
|
|
9025
9330
|
* opaque `invalid_literal` and made the WhatsApp destination — supported by
|
|
9026
9331
|
* the API since #849 — unreachable from this tool.
|
|
9027
9332
|
*/
|
|
9028
|
-
destination:
|
|
9029
|
-
|
|
9030
|
-
type:
|
|
9031
|
-
landingUrl:
|
|
9333
|
+
destination: z45.discriminatedUnion("type", [
|
|
9334
|
+
z45.object({
|
|
9335
|
+
type: z45.literal("landing"),
|
|
9336
|
+
landingUrl: z45.string().url()
|
|
9032
9337
|
}),
|
|
9033
|
-
|
|
9034
|
-
type:
|
|
9035
|
-
phoneNumber:
|
|
9338
|
+
z45.object({
|
|
9339
|
+
type: z45.literal("whatsapp"),
|
|
9340
|
+
phoneNumber: z45.string().min(8).max(32)
|
|
9036
9341
|
})
|
|
9037
9342
|
]),
|
|
9038
|
-
saveAsDefault:
|
|
9343
|
+
saveAsDefault: z45.boolean().default(false),
|
|
9039
9344
|
budgetPolicy: MetaBudgetPolicy.optional()
|
|
9040
9345
|
}).superRefine((value, ctx) => {
|
|
9041
9346
|
if (value.budgetMode === "campaign" && value.campaignDailyBudget === void 0) {
|
|
9042
9347
|
ctx.addIssue({
|
|
9043
|
-
code:
|
|
9348
|
+
code: z45.ZodIssueCode.custom,
|
|
9044
9349
|
path: [
|
|
9045
9350
|
"campaignDailyBudget"
|
|
9046
9351
|
],
|
|
@@ -9051,7 +9356,7 @@ var GroupedCampaignConfig = z44.object({
|
|
|
9051
9356
|
value.adSets.forEach((adSet, index) => {
|
|
9052
9357
|
if (adSet.dailyBudget === void 0) {
|
|
9053
9358
|
ctx.addIssue({
|
|
9054
|
-
code:
|
|
9359
|
+
code: z45.ZodIssueCode.custom,
|
|
9055
9360
|
path: [
|
|
9056
9361
|
"adSets",
|
|
9057
9362
|
index,
|
|
@@ -9063,22 +9368,22 @@ var GroupedCampaignConfig = z44.object({
|
|
|
9063
9368
|
});
|
|
9064
9369
|
}
|
|
9065
9370
|
});
|
|
9066
|
-
var PublishCreativesToMetaInputBase =
|
|
9067
|
-
creatives:
|
|
9371
|
+
var PublishCreativesToMetaInputBase = z45.object({
|
|
9372
|
+
creatives: z45.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
9068
9373
|
adAccountId: McpAdAccountId,
|
|
9069
|
-
pixelId:
|
|
9374
|
+
pixelId: z45.string().min(1).optional(),
|
|
9070
9375
|
/** Required unless `campaignConfig.destination` is whatsapp — see the
|
|
9071
9376
|
* superRefine below. Mirrors the API's `UploadToMetaRequest`. */
|
|
9072
|
-
landingUrl:
|
|
9073
|
-
targetCountry:
|
|
9074
|
-
campaignName:
|
|
9377
|
+
landingUrl: z45.string().url().optional(),
|
|
9378
|
+
targetCountry: z45.string().length(2),
|
|
9379
|
+
campaignName: z45.string().trim().min(1).max(100),
|
|
9075
9380
|
campaignConfig: GroupedCampaignConfig.optional()
|
|
9076
9381
|
});
|
|
9077
9382
|
var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((value, ctx) => {
|
|
9078
9383
|
const destination = value.campaignConfig?.destination;
|
|
9079
9384
|
if (destination?.type !== "whatsapp" && !value.landingUrl) {
|
|
9080
9385
|
ctx.addIssue({
|
|
9081
|
-
code:
|
|
9386
|
+
code: z45.ZodIssueCode.custom,
|
|
9082
9387
|
path: [
|
|
9083
9388
|
"landingUrl"
|
|
9084
9389
|
],
|
|
@@ -9087,7 +9392,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
9087
9392
|
}
|
|
9088
9393
|
if (destination?.type === "landing" && destination.landingUrl !== value.landingUrl) {
|
|
9089
9394
|
ctx.addIssue({
|
|
9090
|
-
code:
|
|
9395
|
+
code: z45.ZodIssueCode.custom,
|
|
9091
9396
|
path: [
|
|
9092
9397
|
"campaignConfig",
|
|
9093
9398
|
"destination",
|
|
@@ -9097,12 +9402,12 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
9097
9402
|
});
|
|
9098
9403
|
}
|
|
9099
9404
|
});
|
|
9100
|
-
var PresignUploadResponseSchema =
|
|
9101
|
-
uploadUrl:
|
|
9405
|
+
var PresignUploadResponseSchema = z45.object({
|
|
9406
|
+
uploadUrl: z45.string().url().refine((value) => {
|
|
9102
9407
|
const url = new URL(value);
|
|
9103
9408
|
return !url.username && !url.password && url.protocol === "https:";
|
|
9104
9409
|
}, "presigned upload URL must be HTTPS without embedded credentials"),
|
|
9105
|
-
publicUrl:
|
|
9410
|
+
publicUrl: z45.string().url().refine((value) => {
|
|
9106
9411
|
const url = new URL(value);
|
|
9107
9412
|
return !url.username && !url.password;
|
|
9108
9413
|
}, "public media URL must not contain embedded credentials")
|
|
@@ -9127,16 +9432,16 @@ async function resolveAndUploadMedia(api, ref, expectedKind, transport) {
|
|
|
9127
9432
|
return uploadResolvedMedia(api, media);
|
|
9128
9433
|
}
|
|
9129
9434
|
__name(resolveAndUploadMedia, "resolveAndUploadMedia");
|
|
9130
|
-
var TikTokInput =
|
|
9131
|
-
taskId:
|
|
9132
|
-
advertiserId:
|
|
9133
|
-
pixelId:
|
|
9134
|
-
landingUrl:
|
|
9135
|
-
angleIndexes:
|
|
9435
|
+
var TikTokInput = z45.object({
|
|
9436
|
+
taskId: z45.string().min(1),
|
|
9437
|
+
advertiserId: z45.string().min(1),
|
|
9438
|
+
pixelId: z45.string().min(1).optional(),
|
|
9439
|
+
landingUrl: z45.string().url().optional(),
|
|
9440
|
+
angleIndexes: z45.array(z45.number().int().nonnegative()).optional(),
|
|
9136
9441
|
campaignConfig: McpCampaignConfig.optional(),
|
|
9137
9442
|
/** Override the ad's description (TikTok's body copy). If omitted,
|
|
9138
9443
|
* ProductMaker uses the auto-generated copy attached to the task. */
|
|
9139
|
-
primaryText:
|
|
9444
|
+
primaryText: z45.string().trim().min(1).max(500).optional()
|
|
9140
9445
|
});
|
|
9141
9446
|
function registerPublishTools(server, api, opts = {}) {
|
|
9142
9447
|
const transport = opts.transport ?? "http";
|
|
@@ -9347,13 +9652,13 @@ function toTikTokCampaignConfig(cfg) {
|
|
|
9347
9652
|
__name(toTikTokCampaignConfig, "toTikTokCampaignConfig");
|
|
9348
9653
|
|
|
9349
9654
|
// src/tools/campaigns.ts
|
|
9350
|
-
import { z as
|
|
9351
|
-
var MetaStatusInput =
|
|
9352
|
-
campaignId:
|
|
9655
|
+
import { z as z46 } from "zod";
|
|
9656
|
+
var MetaStatusInput = z46.object({
|
|
9657
|
+
campaignId: z46.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
|
|
9353
9658
|
});
|
|
9354
|
-
var TikTokStatusInput =
|
|
9355
|
-
campaignId:
|
|
9356
|
-
advertiserId:
|
|
9659
|
+
var TikTokStatusInput = z46.object({
|
|
9660
|
+
campaignId: z46.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
|
|
9661
|
+
advertiserId: z46.string().min(1).describe("The same advertiserId used to publish the campaign.")
|
|
9357
9662
|
});
|
|
9358
9663
|
function registerCampaignTools(server, api) {
|
|
9359
9664
|
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) => {
|
|
@@ -9450,10 +9755,10 @@ function renderTikTokStatusMarkdown(r) {
|
|
|
9450
9755
|
__name(renderTikTokStatusMarkdown, "renderTikTokStatusMarkdown");
|
|
9451
9756
|
|
|
9452
9757
|
// ../../packages/product-economics/src/product-costing.ts
|
|
9453
|
-
import { z as
|
|
9758
|
+
import { z as z48 } from "zod";
|
|
9454
9759
|
|
|
9455
9760
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
9456
|
-
import { z as
|
|
9761
|
+
import { z as z47 } from "zod";
|
|
9457
9762
|
|
|
9458
9763
|
// ../../packages/product-economics/src/product-costing-shipping-references.json
|
|
9459
9764
|
var product_costing_shipping_references_default = {
|
|
@@ -9517,21 +9822,21 @@ var product_costing_shipping_references_default = {
|
|
|
9517
9822
|
};
|
|
9518
9823
|
|
|
9519
9824
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
9520
|
-
var ShippingReferenceSchema =
|
|
9521
|
-
reference:
|
|
9522
|
-
min:
|
|
9523
|
-
max:
|
|
9524
|
-
updatedAt:
|
|
9525
|
-
confidence:
|
|
9825
|
+
var ShippingReferenceSchema = z47.object({
|
|
9826
|
+
reference: z47.number().positive(),
|
|
9827
|
+
min: z47.number().nonnegative(),
|
|
9828
|
+
max: z47.number().positive(),
|
|
9829
|
+
updatedAt: z47.string().date(),
|
|
9830
|
+
confidence: z47.enum([
|
|
9526
9831
|
"high",
|
|
9527
9832
|
"medium",
|
|
9528
9833
|
"medium-low"
|
|
9529
9834
|
]),
|
|
9530
|
-
sources:
|
|
9835
|
+
sources: z47.array(z47.string().url().startsWith("https://")).min(1).max(3)
|
|
9531
9836
|
}).superRefine(({ reference, min, max }, context) => {
|
|
9532
9837
|
if (min > reference || reference > max) {
|
|
9533
9838
|
context.addIssue({
|
|
9534
|
-
code:
|
|
9839
|
+
code: z47.ZodIssueCode.custom,
|
|
9535
9840
|
message: "shipping_reference_outside_range",
|
|
9536
9841
|
path: [
|
|
9537
9842
|
"reference"
|
|
@@ -9539,7 +9844,7 @@ var ShippingReferenceSchema = z46.object({
|
|
|
9539
9844
|
});
|
|
9540
9845
|
}
|
|
9541
9846
|
});
|
|
9542
|
-
var SHIPPING_REFERENCES =
|
|
9847
|
+
var SHIPPING_REFERENCES = z47.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
|
|
9543
9848
|
var PRODUCT_COSTING_COUNTRY_CODES = Object.freeze(Object.keys(SHIPPING_REFERENCES));
|
|
9544
9849
|
var SPANISH_LOCALE_COUNTRIES = /* @__PURE__ */ new Set([
|
|
9545
9850
|
"MX",
|
|
@@ -9721,18 +10026,18 @@ function getProductCostingCountryName(country) {
|
|
|
9721
10026
|
__name(getProductCostingCountryName, "getProductCostingCountryName");
|
|
9722
10027
|
|
|
9723
10028
|
// ../../packages/product-economics/src/product-costing.ts
|
|
9724
|
-
var ProductCostingInputSchema =
|
|
9725
|
-
country:
|
|
9726
|
-
supplierCost:
|
|
9727
|
-
shippingCost:
|
|
9728
|
-
returnRate:
|
|
9729
|
-
cancellationRate:
|
|
9730
|
-
adsManagerCpa:
|
|
9731
|
-
desiredMargin:
|
|
10029
|
+
var ProductCostingInputSchema = z48.object({
|
|
10030
|
+
country: z48.string().refine(isProductCostingCountryCode, "unsupported_country"),
|
|
10031
|
+
supplierCost: z48.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
|
|
10032
|
+
shippingCost: z48.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
|
|
10033
|
+
returnRate: z48.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
10034
|
+
cancellationRate: z48.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
10035
|
+
adsManagerCpa: z48.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
|
|
10036
|
+
desiredMargin: z48.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
|
|
9732
10037
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
9733
10038
|
if (returnRate + cancellationRate >= 1) {
|
|
9734
10039
|
context.addIssue({
|
|
9735
|
-
code:
|
|
10040
|
+
code: z48.ZodIssueCode.custom,
|
|
9736
10041
|
message: "delivery_rate_positive",
|
|
9737
10042
|
path: [
|
|
9738
10043
|
"cancellationRate"
|
|
@@ -9799,14 +10104,14 @@ function calculateProductCosting(rawInput) {
|
|
|
9799
10104
|
__name(calculateProductCosting, "calculateProductCosting");
|
|
9800
10105
|
|
|
9801
10106
|
// src/tools/product-costing.ts
|
|
9802
|
-
import { z as
|
|
9803
|
-
var optionalAmount = /* @__PURE__ */ __name((description) =>
|
|
9804
|
-
var optionalPercent = /* @__PURE__ */ __name((description) =>
|
|
9805
|
-
var ProductCostingToolInput =
|
|
9806
|
-
country:
|
|
10107
|
+
import { z as z49 } from "zod";
|
|
10108
|
+
var optionalAmount = /* @__PURE__ */ __name((description) => z49.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
|
|
10109
|
+
var optionalPercent = /* @__PURE__ */ __name((description) => z49.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
|
|
10110
|
+
var ProductCostingToolInput = z49.object({
|
|
10111
|
+
country: z49.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
|
|
9807
10112
|
message: "Pa\xEDs no soportado por ProductMaker."
|
|
9808
10113
|
}).optional().describe("C\xF3digo ISO-2 de cualquiera de los pa\xEDses disponibles en ProductMaker. Omite para Colombia."),
|
|
9809
|
-
supplierCost:
|
|
10114
|
+
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."),
|
|
9810
10115
|
supplierCostCop: optionalAmount("Alias anterior para costo del proveedor en Colombia. Prefiere supplierCost."),
|
|
9811
10116
|
shippingCost: optionalAmount("Costo de un env\xEDo en la moneda del pa\xEDs. Omite para usar el valor gu\xEDa vigente."),
|
|
9812
10117
|
shippingCostCop: optionalAmount("Alias anterior para flete en Colombia. Prefiere shippingCost."),
|
|
@@ -9938,7 +10243,7 @@ function formatPercent(value) {
|
|
|
9938
10243
|
__name(formatPercent, "formatPercent");
|
|
9939
10244
|
|
|
9940
10245
|
// src/tools/product-research.ts
|
|
9941
|
-
import { z as
|
|
10246
|
+
import { z as z50 } from "zod";
|
|
9942
10247
|
|
|
9943
10248
|
// src/tools/product-research-idempotency.ts
|
|
9944
10249
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -10054,17 +10359,17 @@ var TERMINAL2 = /* @__PURE__ */ new Set([
|
|
|
10054
10359
|
]);
|
|
10055
10360
|
var POLL_INTERVAL_MS = 5e3;
|
|
10056
10361
|
var MAX_WAIT_SECONDS = 120;
|
|
10057
|
-
var FindInput =
|
|
10362
|
+
var FindInput = z50.object({
|
|
10058
10363
|
targetCountry: ResearchTargetCountryEnum.describe("Pa\xEDs objetivo. S\xF3lo CO, MX, PE, EC y CL tienen cobertura de investigaci\xF3n."),
|
|
10059
10364
|
categoryKey: ResearchCategoryEnum.default("auto").describe('Categor\xEDa a investigar. Usa "auto" para que la IA elija seg\xFAn el mercado.'),
|
|
10060
|
-
confirmed:
|
|
10061
|
-
waitSeconds:
|
|
10365
|
+
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.`),
|
|
10366
|
+
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.")
|
|
10062
10367
|
});
|
|
10063
|
-
var GetInput =
|
|
10064
|
-
runId:
|
|
10368
|
+
var GetInput = z50.object({
|
|
10369
|
+
runId: z50.string().uuid().describe("ID del run devuelto por find_winning_products.")
|
|
10065
10370
|
});
|
|
10066
|
-
var ListInput =
|
|
10067
|
-
limit:
|
|
10371
|
+
var ListInput = z50.object({
|
|
10372
|
+
limit: z50.number().int().min(1).max(20).default(10)
|
|
10068
10373
|
});
|
|
10069
10374
|
function textResult(text) {
|
|
10070
10375
|
return {
|
|
@@ -10155,6 +10460,256 @@ _Esta llamada no cobr\xF3 cr\xE9ditos: se reutiliz\xF3 una investigaci\xF3n reci
|
|
|
10155
10460
|
}
|
|
10156
10461
|
__name(registerProductResearchTools, "registerProductResearchTools");
|
|
10157
10462
|
|
|
10463
|
+
// src/tools/dropshipping-projects.ts
|
|
10464
|
+
import { z as z51 } from "zod";
|
|
10465
|
+
var ProjectId = z51.string().uuid();
|
|
10466
|
+
var DropshippingProjectStatus = z51.enum([
|
|
10467
|
+
"active",
|
|
10468
|
+
"completed",
|
|
10469
|
+
"archived"
|
|
10470
|
+
]);
|
|
10471
|
+
var DropshippingProjectPhase = z51.enum([
|
|
10472
|
+
"research",
|
|
10473
|
+
"costing",
|
|
10474
|
+
"media",
|
|
10475
|
+
"generating",
|
|
10476
|
+
"storefront",
|
|
10477
|
+
"advertising",
|
|
10478
|
+
"complete"
|
|
10479
|
+
]);
|
|
10480
|
+
var DropshippingRewardMilestone = z51.enum([
|
|
10481
|
+
"product_selected",
|
|
10482
|
+
"costing_saved",
|
|
10483
|
+
"product_media_ready",
|
|
10484
|
+
"creatives_completed",
|
|
10485
|
+
"campaign_created"
|
|
10486
|
+
]);
|
|
10487
|
+
var TargetCountry = z51.enum([
|
|
10488
|
+
"CO",
|
|
10489
|
+
"MX",
|
|
10490
|
+
"PE",
|
|
10491
|
+
"EC",
|
|
10492
|
+
"CL"
|
|
10493
|
+
]);
|
|
10494
|
+
var ResearchCategory = z51.enum([
|
|
10495
|
+
"auto",
|
|
10496
|
+
"automotive",
|
|
10497
|
+
"kitchen",
|
|
10498
|
+
"pets",
|
|
10499
|
+
"home_organization",
|
|
10500
|
+
"beauty",
|
|
10501
|
+
"wellness",
|
|
10502
|
+
"fitness",
|
|
10503
|
+
"baby",
|
|
10504
|
+
"tech",
|
|
10505
|
+
"tools",
|
|
10506
|
+
"garden",
|
|
10507
|
+
"cleaning"
|
|
10508
|
+
]);
|
|
10509
|
+
var HttpUrl3 = z51.string().url().max(2e3).refine((value) => {
|
|
10510
|
+
try {
|
|
10511
|
+
return [
|
|
10512
|
+
"http:",
|
|
10513
|
+
"https:"
|
|
10514
|
+
].includes(new URL(value).protocol);
|
|
10515
|
+
} catch {
|
|
10516
|
+
return false;
|
|
10517
|
+
}
|
|
10518
|
+
}, "URL must use HTTP(S)");
|
|
10519
|
+
var ResearchCandidateOrigin = z51.object({
|
|
10520
|
+
type: z51.literal("research_candidate"),
|
|
10521
|
+
runId: ProjectId,
|
|
10522
|
+
candidateSourceId: z51.string().min(1).max(160)
|
|
10523
|
+
});
|
|
10524
|
+
var TaskOrigin = z51.object({
|
|
10525
|
+
type: z51.literal("task"),
|
|
10526
|
+
taskId: ProjectId
|
|
10527
|
+
});
|
|
10528
|
+
var ManualOrigin = z51.object({
|
|
10529
|
+
type: z51.literal("manual"),
|
|
10530
|
+
clientRequestId: ProjectId,
|
|
10531
|
+
country: z51.string().min(2).max(3).optional()
|
|
10532
|
+
});
|
|
10533
|
+
var StartProjectInput = z51.object({
|
|
10534
|
+
origin: z51.discriminatedUnion("type", [
|
|
10535
|
+
ResearchCandidateOrigin,
|
|
10536
|
+
TaskOrigin,
|
|
10537
|
+
ManualOrigin
|
|
10538
|
+
]),
|
|
10539
|
+
productName: z51.string().trim().min(1).max(240).optional(),
|
|
10540
|
+
productUrl: HttpUrl3.optional(),
|
|
10541
|
+
country: z51.string().min(2).max(3).optional(),
|
|
10542
|
+
languageCode: z51.string().min(2).max(16).optional()
|
|
10543
|
+
}).strict();
|
|
10544
|
+
var CostProjectInput = z51.object({
|
|
10545
|
+
projectId: ProjectId,
|
|
10546
|
+
version: z51.number().int().positive(),
|
|
10547
|
+
country: z51.string().trim().min(2).max(3),
|
|
10548
|
+
supplierCost: z51.number().finite().positive(),
|
|
10549
|
+
shippingCost: z51.number().finite().nonnegative(),
|
|
10550
|
+
returnRate: z51.number().finite().min(0).lt(1),
|
|
10551
|
+
cancellationRate: z51.number().finite().min(0).lt(1),
|
|
10552
|
+
adsManagerCpa: z51.number().finite().nonnegative(),
|
|
10553
|
+
desiredMargin: z51.number().finite().min(0).lt(1)
|
|
10554
|
+
});
|
|
10555
|
+
var GuideInput = z51.object({
|
|
10556
|
+
projectId: ProjectId.optional()
|
|
10557
|
+
}).strict();
|
|
10558
|
+
var DropshippingProjectNextAction = z51.discriminatedUnion("kind", [
|
|
10559
|
+
"cost_product",
|
|
10560
|
+
"provide_product_media",
|
|
10561
|
+
"generate_creatives",
|
|
10562
|
+
"wait_for_creatives",
|
|
10563
|
+
"repair_creatives",
|
|
10564
|
+
"complete_creative_set",
|
|
10565
|
+
"connect_shopify",
|
|
10566
|
+
"publish_storefront",
|
|
10567
|
+
"wait_for_storefront",
|
|
10568
|
+
"connect_meta",
|
|
10569
|
+
"create_meta_campaign",
|
|
10570
|
+
"repair_meta_campaign",
|
|
10571
|
+
"review_paused_campaign"
|
|
10572
|
+
].map((kind) => z51.object({
|
|
10573
|
+
kind: z51.literal(kind),
|
|
10574
|
+
href: z51.string().min(1)
|
|
10575
|
+
})));
|
|
10576
|
+
var CostingInputSchema = z51.object({
|
|
10577
|
+
country: z51.string().min(2).max(3),
|
|
10578
|
+
supplierCost: z51.number().finite().positive(),
|
|
10579
|
+
shippingCost: z51.number().finite().nonnegative(),
|
|
10580
|
+
returnRate: z51.number().finite().min(0).lt(1),
|
|
10581
|
+
cancellationRate: z51.number().finite().min(0).lt(1),
|
|
10582
|
+
adsManagerCpa: z51.number().finite().nonnegative(),
|
|
10583
|
+
desiredMargin: z51.number().finite().min(0).lt(1)
|
|
10584
|
+
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
10585
|
+
if (returnRate + cancellationRate >= 1) context.addIssue({
|
|
10586
|
+
code: z51.ZodIssueCode.custom,
|
|
10587
|
+
path: [
|
|
10588
|
+
"cancellationRate"
|
|
10589
|
+
]
|
|
10590
|
+
});
|
|
10591
|
+
});
|
|
10592
|
+
var CostingResultSchema = z51.object({
|
|
10593
|
+
country: z51.string().min(2).max(3),
|
|
10594
|
+
deliveryRate: z51.number().finite(),
|
|
10595
|
+
adjustedShippingCost: z51.number().finite(),
|
|
10596
|
+
adjustedCpa: z51.number().finite(),
|
|
10597
|
+
totalCost: z51.number().finite(),
|
|
10598
|
+
suggestedPrice: z51.number().finite(),
|
|
10599
|
+
baseCommercialPrice: z51.number().finite(),
|
|
10600
|
+
minimumSafePrice: z51.number().finite(),
|
|
10601
|
+
commercialPrice: z51.number().finite(),
|
|
10602
|
+
comparisonPrice: z51.number().finite(),
|
|
10603
|
+
estimatedProfit: z51.number().finite(),
|
|
10604
|
+
actualMargin: z51.number().finite(),
|
|
10605
|
+
breakEvenAdsManagerCpa: z51.number().finite(),
|
|
10606
|
+
wasPriceAutoAdjusted: z51.boolean(),
|
|
10607
|
+
profitabilityStatus: z51.literal("healthy")
|
|
10608
|
+
});
|
|
10609
|
+
var RewardProgressSchema = z51.object({
|
|
10610
|
+
campaignKey: z51.literal("dropshipping-launch-v1"),
|
|
10611
|
+
totalCredits: z51.number().int().positive(),
|
|
10612
|
+
earnedCredits: z51.number().int().nonnegative(),
|
|
10613
|
+
milestones: z51.array(z51.object({
|
|
10614
|
+
milestone: DropshippingRewardMilestone,
|
|
10615
|
+
credits: z51.number().int().positive(),
|
|
10616
|
+
available: z51.boolean().optional(),
|
|
10617
|
+
earned: z51.boolean(),
|
|
10618
|
+
grantId: ProjectId.nullable(),
|
|
10619
|
+
grantedAt: z51.string().datetime().nullable()
|
|
10620
|
+
})).length(DropshippingRewardMilestone.options.length)
|
|
10621
|
+
});
|
|
10622
|
+
var ProjectResponseSchema = z51.object({
|
|
10623
|
+
id: ProjectId,
|
|
10624
|
+
taskId: ProjectId.nullable(),
|
|
10625
|
+
status: DropshippingProjectStatus,
|
|
10626
|
+
originKey: z51.string().min(1),
|
|
10627
|
+
researchRunId: ProjectId.nullable(),
|
|
10628
|
+
candidateSourceId: z51.string().nullable(),
|
|
10629
|
+
productName: z51.string().nullable(),
|
|
10630
|
+
productUrl: HttpUrl3.nullable(),
|
|
10631
|
+
referenceImageUrl: HttpUrl3.nullable(),
|
|
10632
|
+
targetCountry: z51.string().nullable(),
|
|
10633
|
+
languageCode: z51.string().nullable(),
|
|
10634
|
+
hasReferenceImage: z51.boolean(),
|
|
10635
|
+
costingInput: CostingInputSchema.nullable(),
|
|
10636
|
+
costingResult: CostingResultSchema.nullable(),
|
|
10637
|
+
costingVersion: z51.number().int().positive().nullable(),
|
|
10638
|
+
version: z51.number().int().positive(),
|
|
10639
|
+
phase: DropshippingProjectPhase,
|
|
10640
|
+
nextAction: DropshippingProjectNextAction.nullable(),
|
|
10641
|
+
rewards: RewardProgressSchema,
|
|
10642
|
+
createdAt: z51.string().datetime(),
|
|
10643
|
+
updatedAt: z51.string().datetime()
|
|
10644
|
+
}).passthrough();
|
|
10645
|
+
function projectText(raw) {
|
|
10646
|
+
const project = ProjectResponseSchema.parse(raw);
|
|
10647
|
+
const next = project.nextAction ? `${project.nextAction.kind} (${project.nextAction.href})` : "ninguna";
|
|
10648
|
+
return {
|
|
10649
|
+
content: [
|
|
10650
|
+
{
|
|
10651
|
+
type: "text",
|
|
10652
|
+
text: JSON.stringify({
|
|
10653
|
+
projectId: project.id,
|
|
10654
|
+
productName: project.productName,
|
|
10655
|
+
phase: project.phase,
|
|
10656
|
+
nextAction: project.nextAction,
|
|
10657
|
+
hasReferenceImage: project.hasReferenceImage,
|
|
10658
|
+
costingSaved: Boolean(project.costingResult),
|
|
10659
|
+
rewardProgress: {
|
|
10660
|
+
earnedCredits: project.rewards.earnedCredits,
|
|
10661
|
+
totalCredits: project.rewards.totalCredits,
|
|
10662
|
+
milestones: project.rewards.milestones
|
|
10663
|
+
},
|
|
10664
|
+
version: project.version,
|
|
10665
|
+
message: `Siguiente paso: ${next}. Relee este proyecto despu\xE9s de cada mutaci\xF3n.`
|
|
10666
|
+
})
|
|
10667
|
+
}
|
|
10668
|
+
]
|
|
10669
|
+
};
|
|
10670
|
+
}
|
|
10671
|
+
__name(projectText, "projectText");
|
|
10672
|
+
function registerDropshippingProjectTools(server, api) {
|
|
10673
|
+
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) => {
|
|
10674
|
+
try {
|
|
10675
|
+
return projectText(await api.postJson("/v1/dropshipping-projects", input));
|
|
10676
|
+
} catch (error) {
|
|
10677
|
+
throw translateError(error);
|
|
10678
|
+
}
|
|
10679
|
+
});
|
|
10680
|
+
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) => {
|
|
10681
|
+
try {
|
|
10682
|
+
if (input.returnRate + input.cancellationRate >= 1) {
|
|
10683
|
+
throw new Error("returnRate + cancellationRate debe ser menor que 1.");
|
|
10684
|
+
}
|
|
10685
|
+
const { projectId, version, ...costingInput } = input;
|
|
10686
|
+
return projectText(await api.putJson(`/v1/dropshipping-projects/${encodeURIComponent(projectId)}/costing`, {
|
|
10687
|
+
version,
|
|
10688
|
+
input: costingInput
|
|
10689
|
+
}));
|
|
10690
|
+
} catch (error) {
|
|
10691
|
+
throw translateError(error);
|
|
10692
|
+
}
|
|
10693
|
+
});
|
|
10694
|
+
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) => {
|
|
10695
|
+
try {
|
|
10696
|
+
if (!input.projectId) {
|
|
10697
|
+
return asText({
|
|
10698
|
+
mode: "choose",
|
|
10699
|
+
guided: "Usa start_dropshipping_project cuando el usuario quiera encontrar un producto y dejarlo listo para publicar.",
|
|
10700
|
+
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.",
|
|
10701
|
+
confirmation: "Antes de acciones pagadas o externas, muestra el estimado y pide confirmaci\xF3n.",
|
|
10702
|
+
safety: "Las campa\xF1as Meta quedan PAUSED; el usuario las activa manualmente en Ads Manager."
|
|
10703
|
+
});
|
|
10704
|
+
}
|
|
10705
|
+
return projectText(await api.getJson(`/v1/dropshipping-projects/${encodeURIComponent(input.projectId)}`));
|
|
10706
|
+
} catch (error) {
|
|
10707
|
+
throw translateError(error);
|
|
10708
|
+
}
|
|
10709
|
+
});
|
|
10710
|
+
}
|
|
10711
|
+
__name(registerDropshippingProjectTools, "registerDropshippingProjectTools");
|
|
10712
|
+
|
|
10158
10713
|
// src/server.ts
|
|
10159
10714
|
function createServer(opts) {
|
|
10160
10715
|
const { apiKey, apiBaseUrl, proxyBaseUrl = null, transport = "http" } = opts;
|
|
@@ -10172,6 +10727,7 @@ function createServer(opts) {
|
|
|
10172
10727
|
const api = new ApiClient(apiBaseUrl, apiKey);
|
|
10173
10728
|
registerProductCostingTools(server);
|
|
10174
10729
|
registerProductResearchTools(server, api);
|
|
10730
|
+
registerDropshippingProjectTools(server, api);
|
|
10175
10731
|
registerTaskTools(server, api, {
|
|
10176
10732
|
proxyBaseUrl,
|
|
10177
10733
|
transport
|